Reputation: 3440
I previously asked a question about a delphi and a C/C++ DLL.
I have now another question about a record / struct. The DLL should be able to dynamically CHANGE the VALUE of the pointer vars from the MainAPP.
My delphi MAINAPP has the following record:
type MyRec = record
MyInteger : Pointer;
MyWideString : pwidechar;
MyString : pchar;
MyBool : Pointer
end;
type
TMyFunc = function ( p : pointer ): pointer; stdcall;
procedure test;
var
MyFunction : TMyFunc;
TheRecord : MyRec;
AnInteger : Integer;
AWideString : WideString;
AString : String;
ABool : Bool;
begin
AnInteger := 1234;
AWideString := 'hello';
AString := 'hello2';
ABool := TRUE;
TheRecord.MyInteger := @AnInteger;
TheRecord.MyWideString := pwidechar(AWideString);
TheRecord.AString := pchar(AString);
TheRecord.ABool := @ABool;
[...]
@MyFunction := GetProcAddress...
[...]
MyFunction (@TheRecord); // now the DLL should be able to change the values dynamically.
MessageBoxW (0, pwidechar(AWideString), '', 0); // Show the results how the DLL changed the String to...
end;
C/C++ Code (just example)
typedef struct _TestStruct{
void *TheInteger; // Pointer to Integer
wchar_t *TheWideString; // Pointer to WideString
char *TheAnsiString; // Pointer to AnsiString
bool *TheBool // Pointer to Bool
}TestStruct;
__declspec(dllexport) PVOID __stdcall MyExportedFunc (TestStruct *PTestStruct)
{
MessageBoxW(0 ,PTestStruct->TheWideString, L"Debug" , 0); // We read the value.
PTestStruct->TheWideString = L"Let me change the value here.";
return 0;
}
For some reasons it crashes etc. What do I do wrong?
Thanks for help.
Upvotes: 0
Views: 4524
Reputation: 597345
You are mismanaging the string fields. PWideChar
and PChar
are not the same thing as "pointer to WideString" and "pointer to AnsiString". Delphi has PWideString
(WideString*
) and PAnsiString
(AnsiString*
) types for that purpose instead. You should also use Delphi's PInteger
(int*
) and PBoolean
(bool*
) types instead of Pointer
(void*
). Delphi and C++ are both type-safe languages. Stay away from untyped pointers when possible, your code will be better for it.
type
PMyRec = ^MyRec;
MyRec = record
MyInteger : PInteger;
MyWideString : PWideString;
MyAnsiString : PAnsiString;
MyBool : PBoolean;
end;
TMyFunc = function ( p : PMyRec ): Integer; stdcall;
procedure test;
var
MyFunction : TMyFunc;
TheRecord : MyRec;
AnInteger : Integer;
AWideString : WideString;
AAnsiString : AnsiString;
ABool : Bool;
begin
AnInteger := 1234;
AWideString := 'hello';
AAnsiString := 'hello2';
ABool := TRUE;
TheRecord.MyInteger := @AnInteger;
TheRecord.MyWideString := @AWideString;
TheRecord.MyAnsiString := @AAnsiString;
TheRecord.MyBool := @ABool;
[...]
@MyFunction := GetProcAddress...
[...]
MyFunction (@TheRecord);
MessageBoxW (0, PWideChar(AWideString), '', 0);
end;
.
typedef struct _MyRec
{
int *MyInteger; // Pointer to Integer
WideString *MyWideString; // Pointer to WideString
AnsiString *MyAnsiString; // Pointer to AnsiString
bool *MyBool; // Pointer to Bool
} MyRec, *PMyRec;
__declspec(dllexport) int __stdcall MyExportedFunc (PMyRec PRec)
{
MessageBoxW(NULL, PRec->MyWideString->c_bstr(), L"Debug" , 0);
*(PRec->MyWideString) = L"Let me change the value here.";
return 0;
}
With that said, it can be very dangerous to manipulate AnsiString
(and UnicodeString
) values across a DLL boundary like this, especially if the EXE and DLL are written in different versions of Delphi/C++Builder due to RTL differences, memory manager differences, payload layout differences, etc. WideString
is OK to pass around, though, because it's memory and layout are controlled by the OS, not the RTL.
Upvotes: 1
Reputation: 36092
This probably isn't the cause of the crash at the point where the C++ code assigns to the TheWideString
pointer, but I do see a problem of expectations...
I notice that you're putting the address of the string data that the Delphi AWideString
variable points to into the MyWideString
field of the record. You pass the record to the C++ function, which assigns a new pointer value to the TheWideString/MyWideString
field of the record. When execution returns to the Delphi code, you output the contents of the AWideString
variable.
Your comments indicate that you expect the contents of the AWideString variable to be changed by the C++ function, but that's not what will happen.
The C++ function changes the field in the structure. It does nothing to the memory location that the field previously pointed to. The data that AWideString points to will not be affected by the C++ function.
If the C++ code copied data into the address contained in the field, then it would overwrite the string data that AWideString
points to. Since AWideString
is a Delphi managed string, and the C++ function would be copying more data to that string memory area than the original string had allocated space for, copying data in the C++ function would write past the end of the Delphi allocated string buffer and probably corrupt the Delphi heap. A crash may occur some time later. So it's a good that you're only assigning the pointer to the field, not copying the data! ;>
To see what the C++ function changed, your Delphi code should output the contents of the MyWideString
field of the record after the call to the C++ function.
Upvotes: 2
Reputation: 425
Synchronize fields order in the structures. You can break memory heap using wrong pointers. Also, check alignment both in Delphi and C++.
Upvotes: 1