Link to home
Start Free TrialLog in
Avatar of Jana618
Jana618

asked on

union type in C header conversion

Hi, I'm trying to convert a c header file, and I'm getting access violations when using a union type.
Can anyone help me out?

  union Pair (
    unsigned short LPr ;
    struct (
     unsigned char ID ;
     unsigned char ID ;
    );
  );                                          
SOLUTION
Avatar of Lee_Nover
Lee_Nover

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
ASKER CERTIFIED SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of Lee_Nover
Lee_Nover

my bad on the ShortInt .. sorry
about 'packed' .. they usualy are packed so I wrote it like that
anyway tnx for correcting me :)
Avatar of Jana618

ASKER

type
  IDRec = record
    ID1: Byte;
    ID2: Byte;
  end;
  Pair = record
    case Integer of
      0: (LPr: SmallInt);
      1: (IDs: IDRec);
  end;

This seems to be it, except for a problem I've run into trying to use the type...
I call a function that should fill an array:

type PairArray = array of Pair;
type PPairArray = ^PairArray;

function GetPairs(var pPairs: PPairArray; var pMaxPairs: Integer): Integer;

from C delclaration:
int WINAPI GetPairs(Pair *pPairs, int *pMaxPairs );

where the Integer result indicates success/error of the function. When called the function returns the integer value for success, and pMaxPairs is returning the expected number of pairs for the test (2, which is changed from it's input value  by the function), but when I try to list the byte values of pairs in the array, values are all = 0 (I'm expecting the 2 pair of array values: 9 & 1; and  18 & 1 for the test)

I'm using this just to display the contents if the array:
ListBox1.Items.Add('ID1 = ' + IntToStr(Pairs[I].IDs.ID1) + 'ID2 = ' + IntToStr(Pairs[I].IDs.ID1));

use "var pPairs: PairArray" - function GetPairs(var pPairs: PairArray; var pMaxPairs: Integer): Integer;
that's the delphi way .. or a c-way like: GetPairs(pPairs: PPairArray; pMaxPairs: PInteger): Integer;

I prefer the first method
Avatar of Jana618

ASKER

If I call:
function GetPairs(var Pairs: PairArray; var MaxPairs: Integer): Integer;
Although the function GetPairs doesn't generate errors, and the value of MaxPairs changes to the expected value in the test, any calls to access values in the array after the function call cause access violations, and an access violation is generated when the function that the GetPairs call is in returns (even if the values in the array are not accessed in the function).

If I call:
function GetPairs(var pPairs: PPairArray; var MaxPairs: Integer): Integer;
then after the GetPairs call I have an array containing all LPr, ID1 and ID2 values = 0 (up to the length of the array = 32, btw).

I call the function using:
  SetLength(Pairs,32);
  MaxPairs := 32;
  New(PairsPtr);
  PairsPtr^ := Pairs;
  Value := GetPairs(PairsPtr, MaxPairs);
  ListBox1.Items.Add('Pairs: ' + IntToStr(MaxPairs));  
  for i := 0 to MaxPairs do
  begin
    ListBox1.Items.Add('ID1 = ' + IntToStr(Pairs[I].IDs.ID1) + 'ID2
                               = ' + IntToStr(Pairs[I].IDs.ID2));
  end;  

  This corresponds to the C example I have:

  Pair Prs[32];
  int Num;
  if(GetPairs(Prs, &(Num = 32)) == success_value)
  {
      printf("Pairs:\n");
      for(i=0; i<Num; i++)
      {
            printf("0x%02x -> 0x%02x\n", Prs[i].ID1, Prs[i].ID2);
       }
  }


function GetPairs(var pPairs: PairArray; var pMaxPairs: Integer): Integer;

var
  pp: PairArray;
  mp, lres: Integer;
begin
  mp:=32;
  SetLength(pp, mp);
  lres:=GetPairs(pp, mp);




example with pointers:

function GetPairs(pPairs: PPairArray; pMaxPairs: PInteger): Integer;

var
  pp: PPairArray;
  mp, lres: Integer;
begin
  mp:=32;
  GetMem(pp, SizeOf(Pair) * mp);
  lres:=GetPairs(pp, @mp);
  // ofcourse don't forget to call
  FreeMem(pp);




both should work
Avatar of Jana618

ASKER

much tighter code, but it does the same thing....
Passing in the array causes the two access violations, and passing in the pointer to array doesn't cause errors, but the array is filled entirely with 0's.
FreeMem won't compile, btw, constant expression expected.
ofcourse we missed an important thing: calling convention ! :)
if nothing is specified in the dll then it's cdecl ... seeing WINAPI I assume it's stdcall
just append stdcall; at the end of the function declaration
function GetPairs(var pPairs: PairArray; var pMaxPairs: Integer): Integer; stdcall;
Avatar of Jana618

ASKER

sorry Lee, I do have stdcall on the function declaration. I've also tried with cdecl, and that gives the same result. It may be that the fault is not with the type def or function call. tomorrow I'll try running C and Java test programs.... unless we can think of anything else to try.
Cheers
humz ... try with packed records :)
If packed does not work:
Can you write a working C program calling the function?
If so then write one to show the value of sizeof(struct Pair).
With that we can determine the real packing of the structure and set it in Delphi with a compiler option.
Avatar of Jana618

ASKER

Many thanks to you both...  it needed:

type
  TIDRec = packed record
    ID1: Byte;
    ID2: Byte;
  end;
  TPair = packed record
    case Integer of
      0: (Pr: SmallInt);
      1: (IDs: TIDRec);
  end;
type TPrArray = array of TPair;

function GetPairs(pPairs: TPrArray; var pMaxPairs: Integer) : Integer; stdcall;
function GetPairs(pPairs: TPrArray; var pMaxPairs: Integer) : Integer; stdcall;

This looks suspicious.
An "array of" parameter in Delphi adds an invisible extra parameter for the size of the array.
Avatar of Jana618

ASKER

The description of the method in the C header file states that the integer pMaxPairs holds the max size of the array on input, and the number of pairs retreived on output (rather than size of the array), so it's behaving as expected for the test cases that I've done so far... sorry if that wasn't very clear before.
would you still say it's suspicious in that case?
ha ! so I was right about the structs being packed :-D
Please give the C declaration of the function GetPairs.
From the description your Pascal conversion is wrong.
 union Pair (
    unsigned short LPr ;
    struct (
     unsigned char ID ;
     unsigned char ID ;
    );
  );  

from C delclaration:
int WINAPI GetPairs(Pair *pPairs, int *pMaxPairs );


so delphi version should have "var Pair" param

function GetPairs(var pPairs: TPrArray; var pMaxPairs: Integer) : Integer; stdcall;
Avatar of Jana618

ASKER

C declaration:
/* get an array of pairs
on entry, set *pMaxPairs to the size of the array pPairs you have allocated
return values
0 = success, *pMaxPairs will be the # elements returned
-1= array too small; *pMaxPairs will be updated to the # slots that are needed
*/
int WINAPI GetPairs(Pair *pPairs, int *pMaxPairs);
typedef int (WINAPI * _fndef_GetPairs)(Pair *pPairs, int *pMaxPairs);

I've defined both static and dynamic linking in the import unit, and I'm using the module loader from http://www.delphi-jedi.org/APILIBRARY:329523 (I've had some help doing another header conversion before - thanks Robert!)

I find that if I use a var parameter for pPairs:
function GetPairs(var pPairs: TPrArray; var pMaxPairs: Integer) : Integer; stdcall;
I get an access violation,
but:
function GetPairs(pPairs: TPrArray; var pMaxPairs: Integer) : Integer; stdcall;
Gets the expected results...
The correct declaration is:

type
  PPair = ^TPair;
  TPair = packed record
    case Integer of
      0: (Pr: SmallInt);
      1: (IDs: TIDRec);
  end;

function GetPairs(Pairs: PPairs; var MaxPairs: Integer): Integer; stdcall;

Usage:

var
  Pairs: array [0..100] of TPairs;
  Num: Integer;
begin
  // tell how many entries are in the array
  Num := 100;
  GetPairs(@Pairs[0], Num);
  // Num probably now containing the number of pairs copied to Pairs array.

Oops:
   Num := 101;