?
Solved

C++ struct to Delphi

Posted on 2004-04-20
11
Medium Priority
?
1,911 Views
Last Modified: 2012-08-13
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.
0
Comment
Question by:GiulianoB
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 4
  • 3
  • 2
  • +2
11 Comments
 

Author Comment

by:GiulianoB
ID: 10874817
For the second part, I am thinking of making a wrapper DLL that will export the functions in STDCALL format. Would that work ?
0
 
LVL 26

Expert Comment

by:Russell Libby
ID: 10875091

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

0
 
LVL 3

Expert Comment

by:MikProg
ID: 10875417
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
0
Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
LVL 20

Expert Comment

by:Madshi
ID: 10875610
Sounds to me like Russell earned the points this time...   :-)
0
 
LVL 11

Expert Comment

by:robert_marquardt
ID: 10875685
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.
0
 
LVL 20

Expert Comment

by:Madshi
ID: 10875715
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?
0
 

Author Comment

by:GiulianoB
ID: 10876799
I'm not home at the moment but i will try when I get home. Thanks
0
 

Author Comment

by:GiulianoB
ID: 10876874
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 ?
0
 
LVL 26

Accepted Solution

by:
Russell Libby earned 700 total points
ID: 10878504

I would bet on packed record as well, as it is typically the norm (especially nowadays). But then again, looking at a library that exports functions as __fastcall doesn't leave me with a lot of confidence in that bet ;-)

Regading the bool issue: Yes, it could be 1 byte. The bool data type is not part of the standard ANSI C, and it was usually the case to typedef it as an int. It IS part of the C++ standard, but not all compilers are standard in the implementation of such things. So, this is just another case where context means everything. I will offer this though...

Looking at the declaration again, it would appear that this is not the bool data type from the windows header, as the windows header type define is "BOOL"
(and C/C++ is case sensitive). So, there is a very good chance that the compiler's native data type was used.

Anyways, it would have been nice to see a "bool" field (with size comments) in the second struct listing.

Also, I made a mistake with my statement on Delphi's passing. Delphi passes up to 3 values in the registers, but the registers are:

EAX, EDX, ECX // not EAX, EBX, ECX

My apologies on that (It was late, and I was tired...). You still need to pad the function 2 places, as suggested. For _fastcall declarations with 2 params, the declaration would be

function foo(Filler: Integer; Param2: datatype; Param1: datatype);

Please note the reverse order of param1 and param2 in order to get them into their correct registers. In regards to calling _fastcall functions with more than 2 parameters.... well, let's just say that I personally would not want to touch it.

----------

Russell


0
 

Author Comment

by:GiulianoB
ID: 10881843
I haven't tested it all but I understand a lot better and that's what I wanted to accomplish. Thanks!
0
 
LVL 26

Expert Comment

by:Russell Libby
ID: 10882520
Let us know if you run into any issues, and I (and hopefully others) will try to assist.

Russell

0

Featured Post

Free Tool: Path Explorer

An intuitive utility to help find the CSS path to UI elements on a webpage. These paths are used frequently in a variety of front-end development and QA automation tasks.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

This article explains how to create forms/units independent of other forms/units object names in a delphi project. Have you ever created a form for user input in a Delphi project and then had the need to have that same form in a other Delphi proj…
Have you ever had your Delphi form/application just hanging while waiting for data to load? This is the article to read if you want to learn some things about adding threads for data loading in the background. First, I'll setup a general applica…
Have you created a query with information for a calendar? ... and then, abra-cadabra, the calendar is done?! I am going to show you how to make that happen. Visualize your data!  ... really see it To use the code to create a calendar from a q…
In this video, Percona Solutions Engineer Barrett Chambers discusses some of the basic syntax differences between MySQL and MongoDB. To learn more check out our webinar on MongoDB administration for MySQL DBA: https://www.percona.com/resources/we…
Suggested Courses

765 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question