簡體   English   中英

C++ DLL to Delphi:如何將指向數據的指針從 C++ DLL 傳遞給 Delphi 程序?

[英]C++ DLL to Delphi: How to pass a pointer to data from C++ DLL to Delphi program?

我想將數據存儲在 DLL 中並將其引用和大小傳遞給 Delphi 程序。

我創建了一個 C++ DLL 項目和一個 Delphi 應用程序項目。 然后我通過LoadLibraryGetProcAddress顯式加載 DLL。

但是,我無法使用返回的引用訪問數據。

以下是源代碼:

C++ DLL 被調用函數:

void __stdcall exportClass::getDataReference(char* data, int* bufferLength)
{   
    // char* charArray: is member of exportClass
    charArray = "Pass this Array of Char By Reference";
    data = charArray;
    bufferLength = sizeof(charArray) / sizeof(charArray[0]);
}

德爾福調用者:

procedure callDllFunction();
var
  dynamicCharArray : array of AnsiChar;
  pData: PAnsiChar;
  length: Integer;
  pInt: ^Integer;
begin
  pData := @dynamicCharArray[0];
  length := 10;
  pInt := @length;
  explicitDllLoaderClass.callGetDataReference(pData, pInt);
  SetLength(dynamicCharArray, length);
end;

這段代碼有很多錯誤。

在 C++ 方面;

  • data參數是通過value傳遞,所以重新分配它的值只會在函數本地發生,根本不會影響調用者。 您需要通過引用/指針傳遞參數。 您正在嘗試使用bufferLength參數執行此操作,但您沒有正確為其分配值。

  • char*上使用sizeof()不會讓您獲得所指向的字符串數據的正確長度。 請參見查找指針指向的字符串的大小

在德爾福方面:

  • 在獲取指向其有效負載的指針之前,您沒有為動態數組分配任何內存。

  • 您的 C++ 代碼是作為非靜態類方法實現的,但您的 Delphi 代碼並未考慮該方法的隱式this參數(即 Delphi 類方法中的Self參數)。 它需要一個對象來調用該方法。

由於編譯器的差異,跨 DLL 邊界公開對 C++ 對象的直接訪問不是一個好主意。 您應該將 DLL 函數重新實現為平面 C 樣式函數。 您仍然可以使用exportClass ,但僅在 DLL 內部使用,並且必須在 Delphi 端抽象訪問它。

話雖如此,請嘗試更像這樣的事情:

// export this
void* __stdcall getExportClassObj()
{
    return new exportClass;
}

// export this
void __stdcall freeExportClassObj(void *obj)
{
    delete (exportClass*) obj;
}

// export this
void __stdcall getDataReference(void *obj, char* &data, int &bufferLength)
{
    ((exportClass*)obj)->getDataReference(data, bufferLength);
}

// do not export this
void __stdcall exportClass::getDataReference(char* &data, int &bufferLength)
{
    // char* charArray: is member of exportClass
    charArray = "Pass this Array of Char By Reference";
    data = charArray;
    bufferLength = strlen(charArray);
}
type
  GetExportClassObjFunc = function: Pointer; stdcall;
  FreeExportClassObjProc = procedure(obj: Pointer); stdcall;
  GetDataReferenceProc = procedure(obj: Pointer; var data: PAnsiChar; var bufferLength: Integer); stdcall;

getExportClassObj: GetExportClassObjFunc;
freeExportClassObj: FreeExportClassObjProc;
getDataReference: GetDataReferenceProc;

...

procedure callDllFunction();
var
  dynamicCharArray : array of AnsiChar;
  pData: PAnsiChar;
  length: Integer;
  Obj; pointer;
begin
  Obj := getExportClassObj();
  try
    getDataReference(Obj, pData, length);
    // use pData up to length chars as needed...
    SetLength(dynamicCharArray, length);
    Move(pData^, PAnsiChar(dynamicCharArray)^, length);
    ...
  finally
    freeDataReference(Obj);
  end;
end;

您應該從 DLL 中導出 C 函數。 並決定您是否希望 DLL 函數將數據復制到傳遞的緩沖區指針中,或者只是返回指向數據的指針。

我構建了一個示例,顯示 DLL 復制數據。 正如您在問題中所做的那樣,我使用了動態數組。 DLL 內部的函數接收數據復制位置的指針(即Delphi 動態數組)和動態數組的長度。 DLL 內部的函數復制一些數據,直到指定的最大長度,並返回實際長度。

我還使用 Ansi char 來傳遞數據,因為您似乎喜歡這樣。 也可以通過Unicode。

DLL源代碼:

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

extern "C" __declspec(dllexport) void __stdcall getDataReference(char* data, int* bufferLength)
{
    char charArray[] = "Pass this Array of Char By Reference";
    if ((data == NULL) || (bufferLength == NULL) || (*bufferLength <= 0))
        return;
    #pragma warning(suppress : 4996)
    strncpy(data, charArray, *bufferLength);
    *bufferLength = sizeof(charArray) / sizeof(charArray[0]);
}

以及使用該 DLL 的簡單控制台模式 Delphi 應用程序:

program CallMyDll;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  Winapi.Windows,
  System.SysUtils;

type
    TGetDataReference = procedure (Data         : PAnsiChar;
                                   BufferLength : PInteger); stdcall;

var
    DllHandle        : THandle;
    GetDataReference : TGetDataReference;

procedure callDllFunction();
var
    DynamicCharArray : array of AnsiChar;
    DataLength       : Integer;
    S                : String;
begin
    DataLength := 1000;
    SetLength(DynamicCharArray, DataLength);
    GetDataReference(PAnsiChar(DynamicCharArray), @DataLength);
    SetLength(DynamicCharArray, DataLength);
    S := String(PAnsiChar(DynamicCharArray));
    WriteLn(S);
end;

begin
    try
        DllHandle := LoadLibrary('D:\FPiette\Cpp\MyDll\Debug\MyDll.dll');
        if DllHandle = 0 then
            raise Exception.Create('DLL not found');
        @GetDataReference := GetProcAddress(DllHandle, '_getDataReference@8');
        if @getDataReference = nil then
            raise Exception.Create('getDataReference not found');
        callDllFunction();
    except
        on E: Exception do
            Writeln(E.ClassName, ': ', E.Message);
    end;
    WriteLn('Hit RETURN...');
    ReadLn;
end.

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM