Home All Groups Group Topic Archive Search About

Using function with PChar data type

Author
22 May 2009 12:25 PM
Andrius B.
Hi all.

I would like to find out how to solve the problem using DLL functions
(written in Delphi)  in VB.NET


The DLL contains a function
GetStatus (username: PChar; password: PChar;  var status : PChar):integer;
it takes to input parameters (username and password) and returns 1 if
everything goes OK (status is return in variable "status" as string, like
"valid" or "expired"), and 0 if an exception occurs ("status" contains empty
string).

In VB this function is declared as
Declare Auto Function GetStatus Lib "mydll.dll" (ByVal username As IntPtr,
ByVal password As IntPtr, ByRef status As IntPtr) As Integer

The problem is how to use this function in a code.
As far as I know, the code shoul look smth like this:

Dim username as string="user1"
Dim password as string="pass1"
Dim status as New String(vbnullchar,255)

Dim userptr As New IntPtr
userptr = Marshal.StringToCoTaskMemAnsi(username  & vbNullChar)
Dim passptr As New IntPtr
passptr = Marshal.StringToCoTaskMemAnsi(password & vbNullChar)
Dim statusptr As New IntPtr
statusptr = Marshal.StringToCoTaskMemAnsi(status  & vbNullChar)

dim result as integer = GetStatus(userptr, passptr, statusptr)
status=marshal.PtrToStringAnsi(statusptr)


Is the code right or should be modified?

Thanks in advance.

Author
22 May 2009 4:44 PM
Mike
Andrius B. wrote:
Show quoteHide quote
> Hi all.
>
> I would like to find out how to solve the problem using DLL functions
> (written in Delphi)  in VB.NET
>
>
> The DLL contains a function
> GetStatus (username: PChar; password: PChar;  var status : PChar):integer;
> it takes to input parameters (username and password) and returns 1 if
> everything goes OK (status is return in variable "status" as string, like
> "valid" or "expired"), and 0 if an exception occurs ("status" contains empty
> string).
>
> In VB this function is declared as
> Declare Auto Function GetStatus Lib "mydll.dll" (ByVal username As IntPtr,
> ByVal password As IntPtr, ByRef status As IntPtr) As Integer

Try this

   Declare Auto Function GetStatus Lib "mydll.dll" _
     ( _
       ByVal username As String, _
       ByVal password As String, _
       ByVal status   As IntPtr
     ) as Integer


Usage:

   Dim h As IntPtr = Marshal.AllocHGlobal(255)
   dim res as integer = GetStatus("user1","pass1",h)
   Dim status as string = Marshal.PtrToStringAnsi(h)
   Marshal.FreeHGlobal(h)

You could wrap it (wrapper after wrapper after wrapper after wrapper
<g>) with another function using the import as a primitive

   Declare Auto Function primGetStatus _
     Lib "mydll.dll" Alias "GetStatus" _
     ( _
       ByVal username As String, _
       ByVal password As String, _
       ByVal status   As IntPtr
     ) as Integer

   Function GetStatus
     ( _
       ByVal username As String, _
       ByVal password As String, _
       ByRef status   As String
     ) as Integer

      Dim h As IntPtr = Marshal.AllocHGlobal(255)
      dim res as integer = primGetStatus(username,password,h)
      status = Marshal.PtrToStringAnsi(h)
      Marshal.FreeHGlobal(h)
      return res
   End Function

--
Author
22 May 2009 5:02 PM
Mike
Mike wrote:

Show quoteHide quote
> Try this
>
>   Declare Auto Function GetStatus Lib "mydll.dll" _
>     ( _
>       ByVal username As String, _
>       ByVal password As String, _
>       ByVal status   As IntPtr
>     ) as Integer
>
>
> Usage:
>
>   Dim h As IntPtr = Marshal.AllocHGlobal(255)
>   dim res as integer = GetStatus("user1","pass1",h)
>   Dim status as string = Marshal.PtrToStringAnsi(h)
>   Marshal.FreeHGlobal(h)
>
> You could wrap it (wrapper after wrapper after wrapper after wrapper
> <g>) with another function using the import as a primitive
>
>   Declare Auto Function primGetStatus _
>     Lib "mydll.dll" Alias "GetStatus" _
>     ( _
>       ByVal username As String, _
>       ByVal password As String, _
>       ByVal status   As IntPtr
>     ) as Integer
>
>   Function GetStatus
>     ( _
>       ByVal username As String, _
>       ByVal password As String, _
>       ByRef status   As String
>     ) as Integer
>
>      Dim h As IntPtr = Marshal.AllocHGlobal(255)
>      dim res as integer = primGetStatus(username,password,h)
>      status = Marshal.PtrToStringAnsi(h)
>      Marshal.FreeHGlobal(h)
>      return res
>   End Function

Note I added the continuation "_" when posting this so it looks clean.
I missed one after the status parameter.  You will catch that via compile.

I can't wait for VB.NET to evolve beyond needing _ for continuation
lines. I think I saw somewhere they deprecated it as part of the next
2010 version. :-)

--
Author
22 May 2009 6:03 PM
Andrius B.
Thanks Mike.
I have tried your code (exactly as you wrote), but the same exception
occurs:
"Attempted to read or write protected memory. This is often an indication
that other memory is corrupt."
Is this because of bad variable declaration or bad calling convention?





Show quoteHide quote
"Mike" <unkn***@unknown.tv> wrote in message
news:%23juNnyv2JHA.5816@TK2MSFTNGP02.phx.gbl...
> Andrius B. wrote:
>> Hi all.
>>
>> I would like to find out how to solve the problem using DLL functions
>> (written in Delphi)  in VB.NET
>>
>>
>> The DLL contains a function
>> GetStatus (username: PChar; password: PChar;  var status :
>> PChar):integer;
>> it takes to input parameters (username and password) and returns 1 if
>> everything goes OK (status is return in variable "status" as string, like
>> "valid" or "expired"), and 0 if an exception occurs ("status" contains
>> empty string).
>>
>> In VB this function is declared as
>> Declare Auto Function GetStatus Lib "mydll.dll" (ByVal username As
>> IntPtr, ByVal password As IntPtr, ByRef status As IntPtr) As Integer
>
> Try this
>
>   Declare Auto Function GetStatus Lib "mydll.dll" _
>     ( _
>       ByVal username As String, _
>       ByVal password As String, _
>       ByVal status   As IntPtr
>     ) as Integer
>
>
> Usage:
>
>   Dim h As IntPtr = Marshal.AllocHGlobal(255)
>   dim res as integer = GetStatus("user1","pass1",h)
>   Dim status as string = Marshal.PtrToStringAnsi(h)
>   Marshal.FreeHGlobal(h)
>
> You could wrap it (wrapper after wrapper after wrapper after wrapper <g>)
> with another function using the import as a primitive
>
>   Declare Auto Function primGetStatus _
>     Lib "mydll.dll" Alias "GetStatus" _
>     ( _
>       ByVal username As String, _
>       ByVal password As String, _
>       ByVal status   As IntPtr
>     ) as Integer
>
>   Function GetStatus
>     ( _
>       ByVal username As String, _
>       ByVal password As String, _
>       ByRef status   As String
>     ) as Integer
>
>      Dim h As IntPtr = Marshal.AllocHGlobal(255)
>      dim res as integer = primGetStatus(username,password,h)
>      status = Marshal.PtrToStringAnsi(h)
>      Marshal.FreeHGlobal(h)
>      return res
>   End Function
>
> --
Author
22 May 2009 7:13 PM
Mike
hmmm,

All I can think of is

   1) That Status is overflowing?
   2) Your delphi code is using SHORT strings vs HUGE strings?
   2) Are you using a STDCALL in the DLL exporting?

How is the delphi DLL code compiled?

Your original prototype (delphi to c/c++ to VB.NET)

    x : pchar     --> const char *x        --> byval x as string
    var x : pchar --> char *x or char **x  --> byref as string

the problem is that in general, when return an OUT on a string
pointer, it is generalized helped with a size parameter

    preallocate string of max_size
    GetString(string, max_size)

Thats the case in all languages.

But your function is assuming a fix size of 255, so you need to
preallocate that space.

Since the function is going  to set the status in unmanaged memory,
you need to pass it a pointer to unmanaged memory

    Dim hSpace As IntPtr = Marshal.AllocHGlobal(255)

and have the unmanaged (native) function write to it:

    GetStatus(User,Pass,hSpace)

then you want to type cast the return space of bytes to a string:

    status = Marshal.PtrToStringAnsi(hSpace)

So I don't see whats would be wrong based on your initial prototype
other that there is something else going on.

It all depends on whats going on in the native function.  Maybe you
need to get it 254?

I have lots of similar SDK function that output strings so I know the
signature I provide works for similar function.

Try it with KERNEL32 functions like GetComputerName() which has Win32
prototype:

   BOOL GetComputerName(LPTSTR lpbuffer, LPDWORD lpnsize)

in VB.NET

   Declare Auto Function primGetComputerName _
       Lib "kernel32.dll" Alias "GetComputerName" _
       ( ByVal name As IntPtr, ByRef namesize As Integer) As Boolean

  Function GetComputerName( _
       ByRef name As String, ByRef namesize As Integer) As Boolean
     name = ""
     Dim h As IntPtr = Marshal.AllocHGlobal(namesize)
     dim res as boolean = primGetComputerName(h,namesize)
     if res then name = Marshal.PtrToStringAuto(h)
     Marshal.FreeHGlobal(h)
     return res
   End Function

Usage:

   dim cn as string = ""
   dim dw as integer = 255
   if GetComputerName(cn,dw) then
      Console.Writeline("-Computer Name {0} dw: {1} ",cn,dw)
   end if

That function is similar to yours, the different is that a size is
passed.

I haven't touched delphi since 3.0 when they first introduced HUGE
strings but I still have products and a SDK based on it.

I did a quick DLL creation test here and you will get the same
exception if you use a StrCopy() versus a direct assignment, buts its
also wrong too.

// File: D:\Local\wcsdk\wcserver\dotnet\Sandbox\testpasdll.pas
{$H-,O+,A-}
Library MYDLL;

uses
      sysutils,
      windows;

function GetStatus (username: PChar;
                     password: PChar;
                     var status : PChar):integer; stdcall;
begin
     //strcopy(status,'success');
     status := 'success';
     result := 1;
end;

///////////////////////////////////////////// EXPORT LIST
Exports
GetStatus index 1;
end.

Play with the above until you get it write :-) I ran out of the time
to figure it out.

--





Andrius B. wrote:
Show quoteHide quote
> Thanks Mike.
> I have tried your code (exactly as you wrote), but the same exception
> occurs:
> "Attempted to read or write protected memory. This is often an indication
> that other memory is corrupt."
> Is this because of bad variable declaration or bad calling convention?
>
>
>
>
>
> "Mike" <unkn***@unknown.tv> wrote in message
> news:%23juNnyv2JHA.5816@TK2MSFTNGP02.phx.gbl...
>> Andrius B. wrote:
>>> Hi all.
>>>
>>> I would like to find out how to solve the problem using DLL functions
>>> (written in Delphi)  in VB.NET
>>>
>>>
>>> The DLL contains a function
>>> GetStatus (username: PChar; password: PChar;  var status :
>>> PChar):integer;
>>> it takes to input parameters (username and password) and returns 1 if
>>> everything goes OK (status is return in variable "status" as string, like
>>> "valid" or "expired"), and 0 if an exception occurs ("status" contains
>>> empty string).
>>>
>>> In VB this function is declared as
>>> Declare Auto Function GetStatus Lib "mydll.dll" (ByVal username As
>>> IntPtr, ByVal password As IntPtr, ByRef status As IntPtr) As Integer
>> Try this
>>
>>   Declare Auto Function GetStatus Lib "mydll.dll" _
>>     ( _
>>       ByVal username As String, _
>>       ByVal password As String, _
>>       ByVal status   As IntPtr
>>     ) as Integer
>>
>>
>> Usage:
>>
>>   Dim h As IntPtr = Marshal.AllocHGlobal(255)
>>   dim res as integer = GetStatus("user1","pass1",h)
>>   Dim status as string = Marshal.PtrToStringAnsi(h)
>>   Marshal.FreeHGlobal(h)
>>
>> You could wrap it (wrapper after wrapper after wrapper after wrapper <g>)
>> with another function using the import as a primitive
>>
>>   Declare Auto Function primGetStatus _
>>     Lib "mydll.dll" Alias "GetStatus" _
>>     ( _
>>       ByVal username As String, _
>>       ByVal password As String, _
>>       ByVal status   As IntPtr
>>     ) as Integer
>>
>>   Function GetStatus
>>     ( _
>>       ByVal username As String, _
>>       ByVal password As String, _
>>       ByRef status   As String
>>     ) as Integer
>>
>>      Dim h As IntPtr = Marshal.AllocHGlobal(255)
>>      dim res as integer = primGetStatus(username,password,h)
>>      status = Marshal.PtrToStringAnsi(h)
>>      Marshal.FreeHGlobal(h)
>>      return res
>>   End Function
>>
>> --
>
>
Author
22 May 2009 7:20 PM
Mike
Forgot to point out, maybe it will matter by the compiler directive

{$H-}

may make a big difference.  Try {$H+} as well.  If I recall, it makes
a short STRING into a HUGE string.   A short again, is the zero index
byte length format but its limite to 255  characters. The {$H+} makes
all  those string type into C like pointer strings.

--


Mike wrote:
Show quoteHide quote
> hmmm,
>
> All I can think of is
>
>   1) That Status is overflowing?
>   2) Your delphi code is using SHORT strings vs HUGE strings?
>   2) Are you using a STDCALL in the DLL exporting?
>
> How is the delphi DLL code compiled?
>
> Your original prototype (delphi to c/c++ to VB.NET)
>
>    x : pchar     --> const char *x        --> byval x as string
>    var x : pchar --> char *x or char **x  --> byref as string
>
> the problem is that in general, when return an OUT on a string
> pointer, it is generalized helped with a size parameter
>
>    preallocate string of max_size
>    GetString(string, max_size)
>
> Thats the case in all languages.
>
> But your function is assuming a fix size of 255, so you need to
> preallocate that space.
>
> Since the function is going  to set the status in unmanaged memory, you
> need to pass it a pointer to unmanaged memory
>
>    Dim hSpace As IntPtr = Marshal.AllocHGlobal(255)
>
> and have the unmanaged (native) function write to it:
>
>    GetStatus(User,Pass,hSpace)
>
> then you want to type cast the return space of bytes to a string:
>
>    status = Marshal.PtrToStringAnsi(hSpace)
>
> So I don't see whats would be wrong based on your initial prototype
> other that there is something else going on.
>
> It all depends on whats going on in the native function.  Maybe you need
> to get it 254?
>
> I have lots of similar SDK function that output strings so I know the
> signature I provide works for similar function.
>
> Try it with KERNEL32 functions like GetComputerName() which has Win32
> prototype:
>
>   BOOL GetComputerName(LPTSTR lpbuffer, LPDWORD lpnsize)
>
> in VB.NET
>
>   Declare Auto Function primGetComputerName _
>       Lib "kernel32.dll" Alias "GetComputerName" _
>       ( ByVal name As IntPtr, ByRef namesize As Integer) As Boolean
>
>  Function GetComputerName( _
>       ByRef name As String, ByRef namesize As Integer) As Boolean
>     name = ""
>     Dim h As IntPtr = Marshal.AllocHGlobal(namesize)
>     dim res as boolean = primGetComputerName(h,namesize)
>     if res then name = Marshal.PtrToStringAuto(h)
>     Marshal.FreeHGlobal(h)
>     return res
>   End Function
>
> Usage:
>
>   dim cn as string = ""
>   dim dw as integer = 255
>   if GetComputerName(cn,dw) then
>      Console.Writeline("-Computer Name {0} dw: {1} ",cn,dw)
>   end if
>
> That function is similar to yours, the different is that a size is passed.
>
> I haven't touched delphi since 3.0 when they first introduced HUGE
> strings but I still have products and a SDK based on it.
>
> I did a quick DLL creation test here and you will get the same exception
> if you use a StrCopy() versus a direct assignment, buts its also wrong too.
>
> // File: D:\Local\wcsdk\wcserver\dotnet\Sandbox\testpasdll.pas
> {$H-,O+,A-}
> Library MYDLL;
>
> uses
>      sysutils,
>      windows;
>
> function GetStatus (username: PChar;
>                     password: PChar;
>                     var status : PChar):integer; stdcall;
> begin
>     //strcopy(status,'success');
>     status := 'success';
>     result := 1;
> end;
>
> ///////////////////////////////////////////// EXPORT LIST
> Exports
> GetStatus index 1;
> end.
>
> Play with the above until you get it write :-) I ran out of the time to
> figure it out.
>
> --
>
>
>
>
>
> Andrius B. wrote:
>> Thanks Mike.
>> I have tried your code (exactly as you wrote), but the same exception
>> occurs:
>> "Attempted to read or write protected memory. This is often an
>> indication that other memory is corrupt."
>> Is this because of bad variable declaration or bad calling convention?
>>
>>
>>
>>
>>
>> "Mike" <unkn***@unknown.tv> wrote in message
>> news:%23juNnyv2JHA.5816@TK2MSFTNGP02.phx.gbl...
>>> Andrius B. wrote:
>>>> Hi all.
>>>>
>>>> I would like to find out how to solve the problem using DLL
>>>> functions (written in Delphi)  in VB.NET
>>>>
>>>>
>>>> The DLL contains a function
>>>> GetStatus (username: PChar; password: PChar;  var status :
>>>> PChar):integer;
>>>> it takes to input parameters (username and password) and returns 1
>>>> if everything goes OK (status is return in variable "status" as
>>>> string, like "valid" or "expired"), and 0 if an exception occurs
>>>> ("status" contains empty string).
>>>>
>>>> In VB this function is declared as
>>>> Declare Auto Function GetStatus Lib "mydll.dll" (ByVal username As
>>>> IntPtr, ByVal password As IntPtr, ByRef status As IntPtr) As Integer
>>> Try this
>>>
>>>   Declare Auto Function GetStatus Lib "mydll.dll" _
>>>     ( _
>>>       ByVal username As String, _
>>>       ByVal password As String, _
>>>       ByVal status   As IntPtr
>>>     ) as Integer
>>>
>>>
>>> Usage:
>>>
>>>   Dim h As IntPtr = Marshal.AllocHGlobal(255)
>>>   dim res as integer = GetStatus("user1","pass1",h)
>>>   Dim status as string = Marshal.PtrToStringAnsi(h)
>>>   Marshal.FreeHGlobal(h)
>>>
>>> You could wrap it (wrapper after wrapper after wrapper after wrapper
>>> <g>) with another function using the import as a primitive
>>>
>>>   Declare Auto Function primGetStatus _
>>>     Lib "mydll.dll" Alias "GetStatus" _
>>>     ( _
>>>       ByVal username As String, _
>>>       ByVal password As String, _
>>>       ByVal status   As IntPtr
>>>     ) as Integer
>>>
>>>   Function GetStatus
>>>     ( _
>>>       ByVal username As String, _
>>>       ByVal password As String, _
>>>       ByRef status   As String
>>>     ) as Integer
>>>
>>>      Dim h As IntPtr = Marshal.AllocHGlobal(255)
>>>      dim res as integer = primGetStatus(username,password,h)
>>>      status = Marshal.PtrToStringAnsi(h)
>>>      Marshal.FreeHGlobal(h)
>>>      return res
>>>   End Function
>>>
>>> --
>>
>>
Author
23 May 2009 2:37 AM
Mike
Mike wrote:
> Forgot to point out, maybe it will matter by the compiler directive
>
> {$H-}
>
> may make a big difference.  Try {$H+} as well.  If I recall, it makes a
> short STRING into a HUGE string.   A short again, is the zero index byte
> length format but its limite to 255  characters. The {$H+} makes all 
> those string type into C like pointer strings.
>
> --

Andrius,

You might have figured it out by now, if you did, pass on the info :-)

I tried to figure out why I could not get basic delphi pascal DLL to
marshal correctly.   But what I did was to make sure it worked as a C dll.

    ---------------- cut/paste to mydll.c ------------
    // file: mydll.c
    // compiled like so: cl mydll.c /LD /DLL

    #include <stdio.h>
    #include <windows.h>

   __declspec( dllexport ) int __stdcall GetStatus(char *status)
   {
      strcpy(status,"success from mydll.dll");
      return 1;
   }
   ------------------- cut here ------------------------

This worked calling it from C applet, VB.NET, even a delphi applet

For VB.NET, the import just like I gave you, worked fine:

     ' import mydll.c
     Declare Auto Function cdll_GetStatus Lib "mydll.dll"
       (ByVal status As IntPtr) As Integer

     ' wrapper
     Function GetStatus(ByRef status As String) As Integer
         Dim h As IntPtr = Marshal.AllocHGlobal(255)
         Dim res As Integer = cdll_GetStatus(h)
         status = Marshal.PtrToStringAnsi(h)
         Marshal.FreeHGlobal(h)
         Return res
     End Function

Well, a Delphi DLL should work the same:

// File : testpasdll.pas
{$H-,O-,A-}
Library testpasdll;

uses
    sysutils,windows;

function GetStatus(var status : pchar): integer; stdcall;
begin
     StrPlCopy(status,'success from pascal dll',255);
     result := 1;
end;
//////////////////////////////////// EXPORT LIST
Exports
     GetStatus index 1;
end.

Its been so long that I have worked with the finer details of delphi,
but I am sure it is related to getting to the mixed string stuff in
delphi similar to how VB6 had BSTR string and fixed strings mixing.

Bu I just have not figured why the above does not marshal correctly.
It is functionally the same as the c version.   But I will say, that
generally, it recommended when passing strings with have a size with
it, similar to other WIN32 functions that has string by reference
pointer with a size to copy to the pointer and the pointer is
allocated in the caller side with the function simply filling in.

But if you don't have control of the DLL, then you have to prototype
it the why it is with var status : pchar.

I'll be interested to see what you find out to get it right.

--
Author
23 May 2009 6:56 PM
Andrius B.
Hi,

Thanks for your trying to help.
The problem is, that this DLL with that function was made not by me, but
another programmer; I needed this function in order to improve my app
written in VB.NET, bay adding some new function. So, the author of DLL just
sent me the brief description of the function, and that's all.
After this weekend I'll try to connect with him and ask additional questions
about specifications for using it in VB.

If I find out smth I will write here.

Thanks again.

Andrius





Show quoteHide quote
"Mike" <unkn***@unknown.tv> wrote in message
news:OMaDj902JHA.3544@TK2MSFTNGP04.phx.gbl...
> Mike wrote:
>> Forgot to point out, maybe it will matter by the compiler directive
>>
>> {$H-}
>>
>> may make a big difference.  Try {$H+} as well.  If I recall, it makes a
>> short STRING into a HUGE string.   A short again, is the zero index byte
>> length format but its limite to 255  characters. The {$H+} makes all
>> those string type into C like pointer strings.
>>
>> --
>
> Andrius,
>
> You might have figured it out by now, if you did, pass on the info :-)
>
> I tried to figure out why I could not get basic delphi pascal DLL to
> marshal correctly.   But what I did was to make sure it worked as a C dll.
>
>    ---------------- cut/paste to mydll.c ------------
>    // file: mydll.c
>    // compiled like so: cl mydll.c /LD /DLL
>
>    #include <stdio.h>
>    #include <windows.h>
>
>   __declspec( dllexport ) int __stdcall GetStatus(char *status)
>   {
>      strcpy(status,"success from mydll.dll");
>      return 1;
>   }
>   ------------------- cut here ------------------------
>
> This worked calling it from C applet, VB.NET, even a delphi applet
>
> For VB.NET, the import just like I gave you, worked fine:
>
>     ' import mydll.c
>     Declare Auto Function cdll_GetStatus Lib "mydll.dll"
>       (ByVal status As IntPtr) As Integer
>
>     ' wrapper
>     Function GetStatus(ByRef status As String) As Integer
>         Dim h As IntPtr = Marshal.AllocHGlobal(255)
>         Dim res As Integer = cdll_GetStatus(h)
>         status = Marshal.PtrToStringAnsi(h)
>         Marshal.FreeHGlobal(h)
>         Return res
>     End Function
>
> Well, a Delphi DLL should work the same:
>
> // File : testpasdll.pas
> {$H-,O-,A-}
> Library testpasdll;
>
> uses
>    sysutils,windows;
>
> function GetStatus(var status : pchar): integer; stdcall;
> begin
>     StrPlCopy(status,'success from pascal dll',255);
>     result := 1;
> end;
> //////////////////////////////////// EXPORT LIST
> Exports
>     GetStatus index 1;
> end.
>
> Its been so long that I have worked with the finer details of delphi, but
> I am sure it is related to getting to the mixed string stuff in delphi
> similar to how VB6 had BSTR string and fixed strings mixing.
>
> Bu I just have not figured why the above does not marshal correctly. It is
> functionally the same as the c version.   But I will say, that generally,
> it recommended when passing strings with have a size with it, similar to
> other WIN32 functions that has string by reference pointer with a size to
> copy to the pointer and the pointer is allocated in the caller side with
> the function simply filling in.
>
> But if you don't have control of the DLL, then you have to prototype it
> the why it is with var status : pchar.
>
> I'll be interested to see what you find out to get it right.
>
> --
>
Author
24 May 2009 6:55 AM
Mike
Hi Andrius,

I was able to finally get the Delphi DLL to import and marshall
correctly.  Try this:

    Declare Ansi Function GetStatus Lib "mydll.dll" _
       (Byval user as string, _
        byval pwd as string, _
        ByRef status as String) as Boolean

The issue was the AUTO. Changing it to ANSI made it marshall correctly
without requiring wrapping IntPtr.

Now, to use it you MUST initialize the status string:

    dim s as string = ""    ' <<--- MUST INITIALIZE
    GetStatus("mike","xxxx", s)
    writeline("- GetStatus()     ==> [{0}] ",s)

Try it.

==


Andrius B. wrote:
Show quoteHide quote
> Hi,
>
> Thanks for your trying to help.
> The problem is, that this DLL with that function was made not by me, but
> another programmer; I needed this function in order to improve my app
> written in VB.NET, bay adding some new function. So, the author of DLL just
> sent me the brief description of the function, and that's all.
> After this weekend I'll try to connect with him and ask additional questions
> about specifications for using it in VB.
>
> If I find out smth I will write here.
>
> Thanks again.
>
> Andrius
>
>
>
>
>
> "Mike" <unkn***@unknown.tv> wrote in message
> news:OMaDj902JHA.3544@TK2MSFTNGP04.phx.gbl...
>> Mike wrote:
>>> Forgot to point out, maybe it will matter by the compiler directive
>>>
>>> {$H-}
>>>
>>> may make a big difference.  Try {$H+} as well.  If I recall, it makes a
>>> short STRING into a HUGE string.   A short again, is the zero index byte
>>> length format but its limite to 255  characters. The {$H+} makes all
>>> those string type into C like pointer strings.
>>>
>>> --
>> Andrius,
>>
>> You might have figured it out by now, if you did, pass on the info :-)
>>
>> I tried to figure out why I could not get basic delphi pascal DLL to
>> marshal correctly.   But what I did was to make sure it worked as a C dll.
>>
>>    ---------------- cut/paste to mydll.c ------------
>>    // file: mydll.c
>>    // compiled like so: cl mydll.c /LD /DLL
>>
>>    #include <stdio.h>
>>    #include <windows.h>
>>
>>   __declspec( dllexport ) int __stdcall GetStatus(char *status)
>>   {
>>      strcpy(status,"success from mydll.dll");
>>      return 1;
>>   }
>>   ------------------- cut here ------------------------
>>
>> This worked calling it from C applet, VB.NET, even a delphi applet
>>
>> For VB.NET, the import just like I gave you, worked fine:
>>
>>     ' import mydll.c
>>     Declare Auto Function cdll_GetStatus Lib "mydll.dll"
>>       (ByVal status As IntPtr) As Integer
>>
>>     ' wrapper
>>     Function GetStatus(ByRef status As String) As Integer
>>         Dim h As IntPtr = Marshal.AllocHGlobal(255)
>>         Dim res As Integer = cdll_GetStatus(h)
>>         status = Marshal.PtrToStringAnsi(h)
>>         Marshal.FreeHGlobal(h)
>>         Return res
>>     End Function
>>
>> Well, a Delphi DLL should work the same:
>>
>> // File : testpasdll.pas
>> {$H-,O-,A-}
>> Library testpasdll;
>>
>> uses
>>    sysutils,windows;
>>
>> function GetStatus(var status : pchar): integer; stdcall;
>> begin
>>     StrPlCopy(status,'success from pascal dll',255);
>>     result := 1;
>> end;
>> //////////////////////////////////// EXPORT LIST
>> Exports
>>     GetStatus index 1;
>> end.
>>
>> Its been so long that I have worked with the finer details of delphi, but
>> I am sure it is related to getting to the mixed string stuff in delphi
>> similar to how VB6 had BSTR string and fixed strings mixing.
>>
>> Bu I just have not figured why the above does not marshal correctly. It is
>> functionally the same as the c version.   But I will say, that generally,
>> it recommended when passing strings with have a size with it, similar to
>> other WIN32 functions that has string by reference pointer with a size to
>> copy to the pointer and the pointer is allocated in the caller side with
>> the function simply filling in.
>>
>> But if you don't have control of the DLL, then you have to prototype it
>> the why it is with var status : pchar.
>>
>> I'll be interested to see what you find out to get it right.
>>
>> --
>>
>
>
Author
24 May 2009 7:50 AM
Mike
A followup Andrius <g>

The code below worked with .NET

    dim s as string = ""    ' <<--- MUST INITIALIZE
    GetStatus("mike","xxxx", s)

without allocating space, because as I understand it, in this the
DELPHI DLL I was using was just copying to a null terminated string
pointer marshaled by .NET

But I was leery with the initializing and not allocating space for it.

So I went back to one my C/C++ dll functions where I have server
functions that outputting string and here I use DLLImportAttribute.

Seeing how I can use Declare ANSI I changed my .NET signature to use
Declare ANSI to see if that worked:

   Declare Ansi Function GetConnectedServer Lib "wcsrv2.dll" _
      (ByVal name As string, _
       ByVal namesize As Integer) As Boolean

Here, I needed to allocate the string space in this wrapper function:

    Function GetConnectedServer() As String
      const namesize as integer = 255
      dim name as string = space(namesize)
      if GetConnectedServer(name,namesize) then
         Dim wsp() As Char = {Chr(32), Chr(0)}
         return name.tostring.trim(wsp)
      end if
      return ""
    End Function

So I would not trust pchar string without allocating space.

Finally, I had seen many references to StringBuilder being useful for
marshalling and that works very nicely:

   Declare Ansi Function GetConnectedServer Lib "wcsrv2.dll" _
      (ByVal name As StringBuilder, _
       ByVal namesize As Integer) As Boolean

    Function GetConnectedServer() As String
      const namesize as integer = 255
      Dim name As New StringBuilder(namesize)
      if GetConnectedServer(name,namesize) then
         return name.tostring
      end if
      return ""
    End Function

Probably using StringBuilder for your GetStatus() makes sense/

--

Mike wrote:
Show quoteHide quote
> Hi Andrius,
>
> I was able to finally get the Delphi DLL to import and marshall
> correctly.  Try this:
>
>    Declare Ansi Function GetStatus Lib "mydll.dll" _
>       (Byval user as string, _
>        byval pwd as string, _
>        ByRef status as String) as Boolean
>
> The issue was the AUTO. Changing it to ANSI made it marshall correctly
> without requiring wrapping IntPtr.
>
> Now, to use it you MUST initialize the status string:
>
>    dim s as string = ""    ' <<--- MUST INITIALIZE
>    GetStatus("mike","xxxx", s)
>    writeline("- GetStatus()     ==> [{0}] ",s)
>
> Try it.
>
> ==
>
>
> Andrius B. wrote:
>> Hi,
>>
>> Thanks for your trying to help.
>> The problem is, that this DLL with that function was made not by me,
>> but another programmer; I needed this function in order to improve my
>> app written in VB.NET, bay adding some new function. So, the author of
>> DLL just sent me the brief description of the function, and that's all.
>> After this weekend I'll try to connect with him and ask additional
>> questions about specifications for using it in VB.
>>
>> If I find out smth I will write here.
>>
>> Thanks again.
>>
>> Andrius
>>
>>
>>
>>
>>
>> "Mike" <unkn***@unknown.tv> wrote in message
>> news:OMaDj902JHA.3544@TK2MSFTNGP04.phx.gbl...
>>> Mike wrote:
>>>> Forgot to point out, maybe it will matter by the compiler directive
>>>>
>>>> {$H-}
>>>>
>>>> may make a big difference.  Try {$H+} as well.  If I recall, it
>>>> makes a short STRING into a HUGE string.   A short again, is the
>>>> zero index byte length format but its limite to 255  characters. The
>>>> {$H+} makes all those string type into C like pointer strings.
>>>>
>>>> --
>>> Andrius,
>>>
>>> You might have figured it out by now, if you did, pass on the info :-)
>>>
>>> I tried to figure out why I could not get basic delphi pascal DLL to
>>> marshal correctly.   But what I did was to make sure it worked as a C
>>> dll.
>>>
>>>    ---------------- cut/paste to mydll.c ------------
>>>    // file: mydll.c
>>>    // compiled like so: cl mydll.c /LD /DLL
>>>
>>>    #include <stdio.h>
>>>    #include <windows.h>
>>>
>>>   __declspec( dllexport ) int __stdcall GetStatus(char *status)
>>>   {
>>>      strcpy(status,"success from mydll.dll");
>>>      return 1;
>>>   }
>>>   ------------------- cut here ------------------------
>>>
>>> This worked calling it from C applet, VB.NET, even a delphi applet
>>>
>>> For VB.NET, the import just like I gave you, worked fine:
>>>
>>>     ' import mydll.c
>>>     Declare Auto Function cdll_GetStatus Lib "mydll.dll"
>>>       (ByVal status As IntPtr) As Integer
>>>
>>>     ' wrapper
>>>     Function GetStatus(ByRef status As String) As Integer
>>>         Dim h As IntPtr = Marshal.AllocHGlobal(255)
>>>         Dim res As Integer = cdll_GetStatus(h)
>>>         status = Marshal.PtrToStringAnsi(h)
>>>         Marshal.FreeHGlobal(h)
>>>         Return res
>>>     End Function
>>>
>>> Well, a Delphi DLL should work the same:
>>>
>>> // File : testpasdll.pas
>>> {$H-,O-,A-}
>>> Library testpasdll;
>>>
>>> uses
>>>    sysutils,windows;
>>>
>>> function GetStatus(var status : pchar): integer; stdcall;
>>> begin
>>>     StrPlCopy(status,'success from pascal dll',255);
>>>     result := 1;
>>> end;
>>> //////////////////////////////////// EXPORT LIST
>>> Exports
>>>     GetStatus index 1;
>>> end.
>>>
>>> Its been so long that I have worked with the finer details of delphi,
>>> but I am sure it is related to getting to the mixed string stuff in
>>> delphi similar to how VB6 had BSTR string and fixed strings mixing.
>>>
>>> Bu I just have not figured why the above does not marshal correctly.
>>> It is functionally the same as the c version.   But I will say, that
>>> generally, it recommended when passing strings with have a size with
>>> it, similar to other WIN32 functions that has string by reference
>>> pointer with a size to copy to the pointer and the pointer is
>>> allocated in the caller side with the function simply filling in.
>>>
>>> But if you don't have control of the DLL, then you have to prototype
>>> it the why it is with var status : pchar.
>>>
>>> I'll be interested to see what you find out to get it right.
>>>
>>> --
>>>
>>
>>
Author
29 May 2009 2:58 PM
Andrius B.
Hi.

The problem was solved when the programmer, who is the author of that DLL,
changed the function's export convention to "stdcall".

And the good declaration in vb.net is:
Declare Auto Function GetStatus Lib "mydll.dll" (ByVal username As String,
ByVal password As String, ByRef status As String) As Integer




Show quoteHide quote
"Andrius B." <andriu***@mail.lt> wrote in message
news:e$yLcht2JHA.1380@TK2MSFTNGP05.phx.gbl...
> Hi all.
>
> I would like to find out how to solve the problem using DLL functions
> (written in Delphi)  in VB.NET
>
>
> The DLL contains a function
> GetStatus (username: PChar; password: PChar;  var status : PChar):integer;
> it takes to input parameters (username and password) and returns 1 if
> everything goes OK (status is return in variable "status" as string, like
> "valid" or "expired"), and 0 if an exception occurs ("status" contains
> empty string).
>
> In VB this function is declared as
> Declare Auto Function GetStatus Lib "mydll.dll" (ByVal username As IntPtr,
> ByVal password As IntPtr, ByRef status As IntPtr) As Integer
>
> The problem is how to use this function in a code.
> As far as I know, the code shoul look smth like this:
>
> Dim username as string="user1"
> Dim password as string="pass1"
> Dim status as New String(vbnullchar,255)
>
> Dim userptr As New IntPtr
> userptr = Marshal.StringToCoTaskMemAnsi(username  & vbNullChar)
> Dim passptr As New IntPtr
> passptr = Marshal.StringToCoTaskMemAnsi(password & vbNullChar)
> Dim statusptr As New IntPtr
> statusptr = Marshal.StringToCoTaskMemAnsi(status  & vbNullChar)
>
> dim result as integer = GetStatus(userptr, passptr, statusptr)
> status=marshal.PtrToStringAnsi(statusptr)
>
>
> Is the code right or should be modified?
>
> Thanks in advance.
>