I am trying to port a simple cpp example to Delphi. It calls a function from a x64 DLL:
// ... header
typedef struct dfuDeviceInfo
{
char usbIndex[10]; /**< USB index. */
int busNumber; /**< Bus number. */
int addressNumber; /**< Address number. */
char productId[100]; /**< Product number. */
char serialNumber[100]; /**< Serial number. */
unsigned int dfuVersion; /**< DFU version. */
}dfuDeviceInfo;
int getDfuDeviceList(dfuDeviceInfo** dfuList,int iPID, int iVID);
// ... in main()
dfuDeviceInfo *dfuList;
int getDfuListNb = getDfuDeviceList(&dfuList, 0xdf11, 0x0483);
In Delphi I ported to:
type dfuDeviceInfo = record
usbIndex: array [0..9] of ansichar; //**< USB index. */
busNumber: integer; //**< Bus number. */
addressNumber: integer; //**< Address number. */
productId: array [0..99] of ansichar; //**< Product number. */
serialNumber: array [0..99] of ansichar; //**< Serial number. */
dfuVersion: longword; //**< DFU version. */
end;
type pdfuDeviceInfo = ^dfuDeviceInfo;
var
getDfuDeviceList: function(var dfuList: pointer; iPID: integer; iVID: integer): integer; cdecl = nil;
and a quick test, setting up the DLL function, on demand and calling it:
procedure TfrmMain.Button1Click(Sender: TObject);
var
DLLHandle: THandle;
dfuList: array of dfuDeviceInfo;
getDfuListNb: integer;
begin
DLLHandle := LoadLibrary('THE.DLL');
getDfuDeviceList := GetProcAddress(DLLHandle, 'getDfuDeviceList');
FreeLibrary(DLLHandle);
SetLength(dfuList,99);
getDfuListNb := getDfuDeviceList(pdfuDeviceInfo(dfuList), $DF11, $0483);
end;
I checked with DLL Viewer and aparently the GetProcAddress() is pointing to the correct address in the DLL, however when the function is called a ACCESS_VIOLATION exception occurs. What could be causing the issue?
Gabriel
CodePudding user response:
Your translation is (mostly) correct, although you should double-check the alignment of the record to make sure it matches the same alignment in the C code.
But, more importantly, why are you unloading the DLL from memory before calling the function? Don't do that. You have to leave the DLL loaded until you are done using it.
And also, don't use a Delphi-style dynamic array to receive the DLL's output pointer. Use a raw pointer instead. Delphi-style dynamic arrays and C -style dynamic arrays are not compatible with each other. The only reason the DLL function would take a dfuDeviceInfo**
double-pointer is so that it can allocate its own memory for the array and then return the pointer to you (and in fact, the C code you have shown is expecting the function to do exactly that). If the function were expecting you to allocate the array, then it would have taken a dfuDeviceInfo*
single-pointer instead.
With that said, try this:
type
dfuDeviceInfo = record
usbIndex: array [0..9] of AnsiChar; //**< USB index. */
busNumber: integer; //**< Bus number. */
addressNumber: integer; //**< Address number. */
productId: array [0..99] of AnsiChar; //**< Product number. */
serialNumber: array [0..99] of AnsiChar; //**< Serial number. */
dfuVersion: UInt32; //**< DFU version. */
end;
pdfuDeviceInfo = ^dfuDeviceInfo;
var
getDfuDeviceList: function(var dfuList: pdfuDeviceInfo; iPID: integer; iVID: integer): integer; cdecl = nil;
procedure TfrmMain.Button1Click(Sender: TObject);
var
DLLHandle: THandle;
dfuList: pdfuDeviceInfo;
getDfuListNb: integer;
begin
DLLHandle := LoadLibrary('THE.DLL');
if DLLHandle = 0 then RaiseLastOSError;
try
getDfuDeviceList := GetProcAddress(DLLHandle, 'getDfuDeviceList');
if not Assigned(getDfuDeviceList) then RaiseLastOSError;
dfuList := nil;
getDfuListNb := getDfuDeviceList(dfuList, $DF11, $0483);
try
// use dfuList and getDfuListNb as needed...
finally
// free dfuList according to the DLL's requirements,
// ie by passing it to another function that the
// DLL exports for this purpose, unless the memory
// was allocated by an OS memory function, in which
// case simply call the appropriate OS memory freeing
// function directly, which should be outlined in
// the DLL's documentation...
end;
finally
FreeLibrary(DLLHandle);
end;
end;