-1

I am trying to execute imported function in Delphi 2007:

function getOfficialCardNumber(): WideString ; cdecl; external 'idcapp.dll';

but after few second (in debug configuration) it stops here:

77BD37D6 C6055562C17700   mov byte ptr [$77c16255],$00

and says this:

Debug Output: Heap block at 027D4FF8 modified at 027D5009 past requested size of 9  Process PreglediZaDom.exe (23672)

Mentioned function was written in C++ (Visual Studio)

I have tried changing WideString to PWideString and others, but no success. Does anyone know what can be a problem?

In similar application, but written in VB6 they gave me this function to convert strings:

Public Function pisBstrToString(bstr) As String

    Dim bArray()    As Integer

    Dim longArray() As Long

    Dim str         As String

    Dim i           As Integer

    bArray = pisStringToBArray(bstr)
    longArray = pisBArrayToLongArray(bArray)

    str = ""

    For i = 1 To UBound(longArray)

        If longArray(i) > 0 And longArray(i) < 256 Then
            str = str & Chr(longArray(i))
        Else
            str = str & pisLongToUnichar(longArray(i))
        End If

    Next i

    pisBstrToString = str

End Function

I know now that function written in C++ was exported like this:

BSTR _stdcall getOfficialCardNumber()
trincot
  • 317,000
  • 35
  • 244
  • 286
Dejan Dozet
  • 948
  • 10
  • 26
  • Perhaps the translation is wrong? How is the function declared in the C or C++ header? Or how is the function declared in VB6? – Rudy Velthuis Oct 27 '17 at 17:28
  • I don't have C++ header (dll was built by someone else). Public Declare Function getOfficialCardNumber Lib "C:\WINDOWS\system32\idcapp.dll" () As String is declaration in VB6 – Dejan Dozet Oct 27 '17 at 17:35
  • I doubt VB6 can handle `cdecl`, so it should probably be `stdcall`. And the result is probably a `WideString` indeed. – Rudy Velthuis Oct 27 '17 at 17:46
  • It has been a long time since I interfaced with VB6, so the following may be wrong: instead of WideString, try PWideChar. And be sure to free the result with SysFreeString. – Rudy Velthuis Oct 27 '17 at 17:56
  • FWIW, if people write a DLL (no matter in which language), they should provide a C or C++ header for it. My view on writing DLLs: http://www.rvelthuis.de/articles/articles-dlls.html – Rudy Velthuis Oct 27 '17 at 17:59
  • Oh, no, no, please read above text: "I am trying to execute imported function in Delphi 2007:". I only mention that it was working without problems in EXE built in VB6, but Delphi is my concern now. Also I tried with PWideChar too and no luck, code just hangs a while and goes in cpu tab with "mov byte ptr" thing – Dejan Dozet Oct 27 '17 at 18:03
  • Ok, I will ask them for C++ header, thanks – Dejan Dozet Oct 27 '17 at 18:05
  • Did you change the calling convention to stdcall? – Rudy Velthuis Oct 27 '17 at 18:50
  • FWIW, going by the VB6 declaration, it is probably WideString or PWideChar. But the problem seems to happen earlier, so perhaps the DLL needs some setup function call first. – Rudy Velthuis Oct 27 '17 at 18:52
  • please forget about VB6, we are speaking here about Delphi 2007 application, I already called 3 more functions (before the problematic one) with cdecl and they worked as well, but I will change now to stdcall – Dejan Dozet Oct 27 '17 at 18:56
  • I know that you are not interfacing with VB6, but the only declarations I see is the one for Delphi (which could be wrong) and the one for VB6 (which works). So I try to use the VB6 declaration to find out what the proper Delphi declaration should be. It might also help if you post a little more of the VB6 code (just edit your question) that works. – Rudy Velthuis Oct 27 '17 at 18:59

1 Answers1

1

VB6 does not support cdecl, only stdcall. So right there, you are using the wrong calling convention in your Delphi code, causing the call stack to be mismanaged.

Delphi's WideString type is a wrapper for a COM BSTR string, which VB6 does use for its own strings - but NOT for a String return value of a external DLL function! Even if VB6 expected/accepted a BSTR as output, the DLL function would return the BSTR pointer in the EAX cpu register, like most other return types. However, Delphi does not use EAX when handling WideString as a return type. It is passed as a hidden var parameter instead. See Why can a WideString not be used as a function return value for interop? for more details on that.

If the DLL function returns a char* pointer, you will have to declare the function return value as PAnsiChar:

function getOfficialCardNumber(): PAnsiChar; stdcall; external 'idcapp.dll';

Otherwise, if it returns a wchar_t* pointer 1, you will have to declare the function return value as PWideChar instead:

function getOfficialCardNumber(): PWideChar; stdcall; external 'idcapp.dll';

Either way, you have another issue to deal with - who owns the memory being pointed at, and how should it be freed? Delphi can't directly free the memory returned by a DLL, so how is the memory allocated?

  • If statically within the DLL, it doesn't need to be freed at all.

  • If dynamically, which memory manager allocated it?

    • If it is a memory manager that is internal to the DLL, then the DLL must export a separate function to receive the pointer when you are done using it so it can be freed using the same memory manager.

    • If it is an OS-provided memory API (LocalAlloc()/GlobalAlloc(), IMalloc.Alloc()imalloc/CoTaskMemAlloc(), etc), then the Delphi code can directly call the corresponding memory-freeing function (LocalFree()/GlobalFree(), IMalloc.Free()/CoTaskMemFree(), etc).


1: if it happens to actually be a BSTR pointer, you will need to pass the returned pointer to SysFreeString() when you are done using it.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • in this dll there is a procedure called login: procedure login(uName, pwd: WideString); cdecl; external 'idcapp.dll'; which if I declare like this: procedure login(uName, pwd: String); cdecl; external 'idcapp.dll'; it will not work, so parameters have to be of WideString type, so I thought that return parameter should be WideString also – Dejan Dozet Oct 27 '17 at 18:21
  • And yes, it stops in EAX register – Dejan Dozet Oct 27 '17 at 18:25
  • `String` <> `WideString` in Delphi. You can't switch them when interop is involved. If the DLL function requires a `WideString`, pass it a `WideString`. But are you sure it is not actually expecting a `PWideChar` instead? Is the DLL written in Delphi, or something else? – Remy Lebeau Oct 27 '17 at 18:25
  • dll is written in C++ and I don't know anything about what they use as return type, I will ask them for a header tomorrow – Dejan Dozet Oct 27 '17 at 18:29
  • In that case, unless `login()` is declared as accepting a `BSTR` parameter, you should not be declaring it as `WideString` in Delphi. Use `PAnsiChar` for `char*` or `PWideChar` for `wchar_t*` instead. In the latter case, you can use a `WideString` variable, just type-cast it to `PWideChar` when passing it – Remy Lebeau Oct 27 '17 at 18:49