GiulianoB
asked on
C++ struct to Delphi
This question is a bit extended..
I am trying to use an exported function of another DLL that looks like this:
void __fastcall PrintMessage(ptMsg * Message)
I need to convert this structure to a Delphi structure. I can convert some of it but i get stuck in the boolean and the string parts:
struct ptMsg
{
BYTE Unk0;
BYTE id;
BYTE Unk1;
BYTE type;
bool overhead;
byte Unk2[3];
WORD Unk3;
char PlayerName[0x10];
char Message[542]
};
I need the new structure in delphi to have the same number of bytes since it will be passed into the function
(I can probably figure the part below myself but any help is appreciated and will be awarded pts)
The beginning of the function looks like this in ASM (used madshi's madAsm)
6fb1e120 sub_6fb1e120: ; function entry point
6fb1e120 sub esp, $dd8
6fb1e126 push ebx
6fb1e127 push ebp
6fb1e128 mov ebp, ecx
6fb1e12a push esi
6fb1e12b push edi
6fb1e12c mov al, [ebp+3]
6fb1e12f test al, al
6fb1e131 jnz loc_6fb1e143
6fb1e131
6fb1e133 mov ecx, [ebp+4]
6fb1e136 call -$6d61b ($6fab0b20)
6fb1e136
6fb1e13b test eax, eax
6fb1e13d jz loc_6fb1e656
6fb1e13d
Since it uses fastcall, I think I will have to change the function a bit from
procedure PrintMessage(mMessage: ptMsg)
so that it sends the parameters correctly.
I am trying to use an exported function of another DLL that looks like this:
void __fastcall PrintMessage(ptMsg * Message)
I need to convert this structure to a Delphi structure. I can convert some of it but i get stuck in the boolean and the string parts:
struct ptMsg
{
BYTE Unk0;
BYTE id;
BYTE Unk1;
BYTE type;
bool overhead;
byte Unk2[3];
WORD Unk3;
char PlayerName[0x10];
char Message[542]
};
I need the new structure in delphi to have the same number of bytes since it will be passed into the function
(I can probably figure the part below myself but any help is appreciated and will be awarded pts)
The beginning of the function looks like this in ASM (used madshi's madAsm)
6fb1e120 sub_6fb1e120: ; function entry point
6fb1e120 sub esp, $dd8
6fb1e126 push ebx
6fb1e127 push ebp
6fb1e128 mov ebp, ecx
6fb1e12a push esi
6fb1e12b push edi
6fb1e12c mov al, [ebp+3]
6fb1e12f test al, al
6fb1e131 jnz loc_6fb1e143
6fb1e131
6fb1e133 mov ecx, [ebp+4]
6fb1e136 call -$6d61b ($6fab0b20)
6fb1e136
6fb1e13b test eax, eax
6fb1e13d jz loc_6fb1e656
6fb1e13d
Since it uses fastcall, I think I will have to change the function a bit from
procedure PrintMessage(mMessage: ptMsg)
so that it sends the parameters correctly.
The struct part I can help you with (to a point)...
type
// You need to figure out the byte alignment the dll was compiled with...
// eg, this may need to be: ptMsg = record
lpPtMsg = ^ptMsg;
ptMsg = packed record
Unk0: Byte;
id: Byte;
Unk1: Byte;
_type: Byte;
overhead: BOOL; // Defined in Windows.pas (4 byte long bool)
Unk2: Array [0..2] of Byte;
Unk3: Word;
PlayerName: Array [0..15] of Char;
Message: Array [0..541] of Char;
end;
Problem is, its impossible to tell the byte alignment of the structure (no C\C++ source context to go from). Where it would end up getting messed up is after the Unk2 field, which is 3 bytes long (if not packed). Unless you know for sure, this will be a trial and error situation.
Regarding the fastcall declaration...
This is kind of a combination of register PLUS stdcall calling convention:
(I quote)
The first two parameters are passed in ECX and EDX, with the remainder passed on the stack as in __stdcall. Again, the callee cleans the stack. Function names are decorated by a leading @-sign and a trailing @-sign followed by the number of bytes of parameters taken by the function (including the register parameters).
Anyways, there is no calling convention in Delphi that "quite" fits this. I offer this suggestion (I take no credit for it, as Madshi has mentioned this in another question somewhat similar to this; I only agree with his suggestion as well).
Pad the function with 2 integer params before the Message param, thus making the
Message param come in as ECX (Delphi passes in EAX, EBX, ECX...) . Leave the function calling convention alone (it will be register by default). As the register function cleans up after the call, you **should** be ok.
Example:
procedure PrintMessage(Fill1, Fill2: Integer; Message: lpPtMsg); external 'dllname';
--------
Regarding your other suggestion. A wrapper would work as well, as long as the wrapper language understood the __fastcall calling convention. (meaning, it needs to be done in C/C++). This would be the safest way IMHO...
Hope this helps,
Russell
Windows.pas in Delphi source contains {ALIGN ON} compiler directive.
Delphi help says:
In the {$A8} or {$A+} state, fields in record types that are declared without the packed modifier and fields in class structures are aligned on quad word boundaries.
If you owner C++ & Delphi code then making same alignment in both sources same used in windows dll's is good idea
Delphi help says:
In the {$A8} or {$A+} state, fields in record types that are declared without the packed modifier and fields in class structures are aligned on quad word boundaries.
If you owner C++ & Delphi code then making same alignment in both sources same used in windows dll's is good idea
Sounds to me like Russell earned the points this time... :-)
I would bet on packed record. Unk3 being the first and only candidate for alignment seems to indicate that packing was considered when the record was designed.
I'm just wondering. The alignment would be perfect if the "bool" would only be 1 byte long. Is that possible? Does a one byte bool exist in C++ or is it always 4 bytes?
ASKER
I'm not home at the moment but i will try when I get home. Thanks
ASKER
I have this also btw:
it's the same structure but diff variable names.. this has the sizes
struct ChatMsg {
BYTE Identifier; // 0x00
BYTE MessageType; // 0x01
BYTE Unk1; // 0x02
BYTE Style; // 0x03
DWORD UnitId; // 0x04
BYTE Color; // 0x08
BYTE Unk2; // 0x09
char Name[0x10]; // 0x0A
char Message[0x100]; // 0x1A
};
also will the method to add 2 fill integers work on any fastcall or does it have to have a certain amt of parameters ?
it's the same structure but diff variable names.. this has the sizes
struct ChatMsg {
BYTE Identifier; // 0x00
BYTE MessageType; // 0x01
BYTE Unk1; // 0x02
BYTE Style; // 0x03
DWORD UnitId; // 0x04
BYTE Color; // 0x08
BYTE Unk2; // 0x09
char Name[0x10]; // 0x0A
char Message[0x100]; // 0x1A
};
also will the method to add 2 fill integers work on any fastcall or does it have to have a certain amt of parameters ?
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
I haven't tested it all but I understand a lot better and that's what I wanted to accomplish. Thanks!
Let us know if you run into any issues, and I (and hopefully others) will try to assist.
Russell
Russell
ASKER