How can I return a PChar from a DLL function to a VB6 application without risking crashes or memory leaks?

前端 未结 7 1489
花落未央
花落未央 2020-12-19 05:37

I have to create a DLL which is used by a VB6 application. This DLL has to provide several functions, some of them must return strings.

This is the VB6 declaration:<

相关标签:
7条回答
  • 2020-12-19 05:55

    You cannot return a PChar as a function result, but you can pass an additional PChar parameter and copy the string you want to return to this PChar. Note, that VB must allocate that string to the required size before passing it to the dll. Also in VB that parameter must be declared as byval param as string AND it must be passed with byval:

      param = "aaaaaaaaaaaaaaaaaaaa" ' reserve 20 characters
      call myproc(byval param)
    

    The additional byval in the call will do the compiler magic of converting a VB string to a PChar and back.

    (I hope I remember this is correctly, it has been quite a while since I was forced to use VB.)

    0 讨论(0)
  • 2020-12-19 05:58

    You need to craft a BSTR instead. VB6 strings are actually BSTRs. Call SysAllocString() on the Delphi side and return the BSTR to the VB6 side. The VB6 side will have to call SysFreeString() to free the string - it will do it automatically.

    If PChar corresponds to an ANSI string (your case) you have to manually convert it to Unicode - use MultiByteToWideChar() for that. See this answer for how to better use SysAllocStringLen() and MultiByteToWideChar() together.

    0 讨论(0)
  • 2020-12-19 05:58

    Use the Windows API to allocate the memory that the PChar pointer points into. Then, the VB app can deallocate the memory after use, using the Windows API, too.

    0 讨论(0)
  • 2020-12-19 06:09

    If you don't want to risk crashes or memory leaks, then craft your API using the Windows API as a model. There, the API functions generally don't allocate their own memory. Instead, the caller passes a buffer and tells the API how big the buffer is. The API fills the buffer up to that limit. See the GetWindowText function, for example. Functions don't return pointers, unless they're pointers to things the caller already provided. Instead, the caller provides everything itself, and the function just uses whatever it's given. You almost never see an output buffer parameter that isn't accompanied by another parameter telling the buffer's size.

    A further enhancement you can make to that technique is to allow the function to tell the caller how big the buffer needs to be. When the input pointer is a null pointer, then the function can return how many bytes the caller needs to provide. The caller will call the function twice.

    You don't need to derive your API from scratch. Use already-working APIs as examples for how to expose your own.

    0 讨论(0)
  • 2020-12-19 06:16

    Combining Sharptooth and Lars D's answer; aren't widestrings already allocated via windows and BSTR?

    0 讨论(0)
  • 2020-12-19 06:17

    I would say that whoever allocates the memory must also free it in this case. You will run into problems with other scenarios. So the most safe and clean way would be:

    1. The DLL allocates memory (because it knows how much) and returns the PChar to caller
    2. After the caller is done with it, it calls FreePointer back to the DLL
    3. DLL frees the memory in the FreePointer exported function

    The setup would be like this:

    unit DLL;
    
    interface
    
    uses
      SysUtils;
    
    function Execute(const Params: PChar): PChar; stdcall;
    procedure FreePointer(const P: PChar); stdcall;
    
    exports Execute;
    exports FreePointer;
    
    implementation
    
    function Execute(const Params: PChar): PChar; stdcall;
    var
      Size: Cardinal;
    begin
      Size := Calculate the size;
      GetMem(Result, Size);
    
      ...do something to fill the buffer
    end;
    
    procedure FreePointer(const P: PChar); stdcall;
    begin
      FreeMem(P);
    end;
    
    end.
    
    0 讨论(0)
提交回复
热议问题