c# P/Invoke fails on API method

P

philipqiu

hi



I just wrote a experiment code that build a DLL that export a method
(ToOut),whic I am trying to call from c#. Right now it return uncorrect
result .

Here is the unmanaged code :

typedef struct flagtest1
{
WORD KK;
}test, *ptest;

typedef struct Out
{
struct
{
DWORD BW: 1;
DWORD Gray: 1;
DWORD Color: 1;

} mode;
ptest pSource[2];


}myOut, * pmyOut;

void ToOut( pmyOut* myouttest)
{

(*myouttest)->mode.BW=1;
(*myouttest)->mode.Color=2;
(*myouttest)->mode.Gray=3;
(*myouttest)->pSource[0] = new test;
(*myouttest)->pSource[1] = new test;
(*myouttest)->pSource[0]->KK=4;
(*myouttest)->pSource[0]->KK=5;
}

Now , here is how I tried the same thing from c#;

[StructLayout(LayoutKind.Explicit,Size=3)]
public class _mode
{
[FieldOffset(0)]
public int BW;

[FieldOffset(1)]
public int Gray;

[FieldOffset(2)]
public int color;
}

[StructLayout(LayoutKind.Sequential)]
public class test
{
public System.UInt16 kk;
}

[StructLayout(LayoutKind.Sequential)]
public class myout
{

public _mode mode;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public IntPtr[] ptest; //=
Marshal.AllocHGlobal(Marshal.SizeOf(typeof(test)));

}

public class Program
{
[DllImport("try.dll")]
public static extern void ToOut( out IntPtr test);

public static void Main(string[] args)
{

IntPtr zzz= Marshal.AllocHGlobal(Marshal.SizeOf(typeof(myout)));
ToOut(out zzz);
myout myzzz = (myout)Marshal.PtrToStructure(zzz, typeof(myout));
/// here the object myzz is uncorrect. the

///members: mode.bw ;mode.gray ;
mode.color and ptest display a random number;



System.Console.WriteLine("{0}",myzzz.mode.BW.ToString());
Marshal.DestroyStructure(zzz, typeof(myout));
Marshal.FreeHGlobal(zzz);
}
}
}

Can anyone suggest a solution?

Thanks
 
W

Willy Denoyette [MVP]

philipqiu said:
hi



I just wrote a experiment code that build a DLL that export a method
(ToOut),whic I am trying to call from c#. Right now it return uncorrect
result .

Here is the unmanaged code :

typedef struct flagtest1
{
WORD KK;
}test, *ptest;

typedef struct Out
{
struct
{
DWORD BW: 1;
DWORD Gray: 1;
DWORD Color: 1;

} mode;
ptest pSource[2];


}myOut, * pmyOut;

void ToOut( pmyOut* myouttest)
{

(*myouttest)->mode.BW=1;
(*myouttest)->mode.Color=2;
(*myouttest)->mode.Gray=3;
(*myouttest)->pSource[0] = new test;
(*myouttest)->pSource[1] = new test;
(*myouttest)->pSource[0]->KK=4;
(*myouttest)->pSource[0]->KK=5;
}

Now , here is how I tried the same thing from c#;

[StructLayout(LayoutKind.Explicit,Size=3)]
public class _mode
{
[FieldOffset(0)]
public int BW;

[FieldOffset(1)]
public int Gray;

[FieldOffset(2)]
public int color;
}

[StructLayout(LayoutKind.Sequential)]
public class test
{
public System.UInt16 kk;
}

[StructLayout(LayoutKind.Sequential)]
public class myout
{

public _mode mode;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public IntPtr[] ptest; //=
Marshal.AllocHGlobal(Marshal.SizeOf(typeof(test)));

}

public class Program
{
[DllImport("try.dll")]
public static extern void ToOut( out IntPtr test);

public static void Main(string[] args)
{

IntPtr zzz=
Marshal.AllocHGlobal(Marshal.SizeOf(typeof(myout)));
ToOut(out zzz);
myout myzzz = (myout)Marshal.PtrToStructure(zzz,
typeof(myout)); /// here the object myzz is uncorrect. the

///members: mode.bw ;mode.gray ; mode.color and ptest display a random
number;



System.Console.WriteLine("{0}",myzzz.mode.BW.ToString());
Marshal.DestroyStructure(zzz, typeof(myout));
Marshal.FreeHGlobal(zzz);
}
}
}

Can anyone suggest a solution?

Thanks


Several things are fundamentaly wrong with this code.
First at the C side....
1) When you allocate memory from the unmanaged heap (AllocHGlobal), you are
responsible for clearing that memory before use.
That means you need to use something like :
SecureZeroMemory(*myouttest, sizeof(*myouttest));
in your C code before filling the structure.
2) Bitfields in C are not layd-out as you may think. Your struct mode is one
DWORD (4 bytes long) in C, and your BW, Color and Gray fields are one bit
each. You are moving 2 and 3 to Color and Gray respectively, but they are
one single bit, so the result is not really what you might expect. You can
only move 0 or 1 to single bit fields....

Second your C# code.
Here your _mode declares an overlapping construct of three int's. The first
int start at byte displacement 0, the second at displacement 1 and the third
at 2. That means that your class is 6 bytes in total, not really what your C
code is expecting from this struct.
What you should do is declare this field in myOut as an int (1), or
redeclare _mode as an enum (2).

1)
[StructLayout(LayoutKind.Sequential)]
public class myout
{
public int mode;
...

2)
[Flags]
public enum _mode
{
BW = 0x01,
Gray = 0x02,
Color = 0x04
}

[StructLayout(LayoutKind.Sequential)]
public class myout
{
public _mode mode;
...

Willy.
 
P

philipqiu

Thanks
Willy Denoyette said:
philipqiu said:
hi



I just wrote a experiment code that build a DLL that export a method
(ToOut),whic I am trying to call from c#. Right now it return uncorrect
result .

Here is the unmanaged code :

typedef struct flagtest1
{
WORD KK;
}test, *ptest;

typedef struct Out
{
struct
{
DWORD BW: 1;
DWORD Gray: 1;
DWORD Color: 1;

} mode;
ptest pSource[2];


}myOut, * pmyOut;

void ToOut( pmyOut* myouttest)
{

(*myouttest)->mode.BW=1;
(*myouttest)->mode.Color=2;
(*myouttest)->mode.Gray=3;
(*myouttest)->pSource[0] = new test;
(*myouttest)->pSource[1] = new test;
(*myouttest)->pSource[0]->KK=4;
(*myouttest)->pSource[0]->KK=5;
}

Now , here is how I tried the same thing from c#;

[StructLayout(LayoutKind.Explicit,Size=3)]
public class _mode
{
[FieldOffset(0)]
public int BW;

[FieldOffset(1)]
public int Gray;

[FieldOffset(2)]
public int color;
}

[StructLayout(LayoutKind.Sequential)]
public class test
{
public System.UInt16 kk;
}

[StructLayout(LayoutKind.Sequential)]
public class myout
{

public _mode mode;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public IntPtr[] ptest; //=
Marshal.AllocHGlobal(Marshal.SizeOf(typeof(test)));

}

public class Program
{
[DllImport("try.dll")]
public static extern void ToOut( out IntPtr test);

public static void Main(string[] args)
{

IntPtr zzz=
Marshal.AllocHGlobal(Marshal.SizeOf(typeof(myout)));
ToOut(out zzz);
myout myzzz = (myout)Marshal.PtrToStructure(zzz,
typeof(myout)); /// here the object myzz is uncorrect. the

///members: mode.bw ;mode.gray ; mode.color and ptest display a random
number;



System.Console.WriteLine("{0}",myzzz.mode.BW.ToString());
Marshal.DestroyStructure(zzz, typeof(myout));
Marshal.FreeHGlobal(zzz);
}
}
}

Can anyone suggest a solution?

Thanks


Several things are fundamentaly wrong with this code.
First at the C side....
1) When you allocate memory from the unmanaged heap (AllocHGlobal), you
are responsible for clearing that memory before use.
That means you need to use something like :
SecureZeroMemory(*myouttest, sizeof(*myouttest));
in your C code before filling the structure.
2) Bitfields in C are not layd-out as you may think. Your struct mode is
one DWORD (4 bytes long) in C, and your BW, Color and Gray fields are one
bit each. You are moving 2 and 3 to Color and Gray respectively, but they
are one single bit, so the result is not really what you might expect. You
can only move 0 or 1 to single bit fields....

Second your C# code.
Here your _mode declares an overlapping construct of three int's. The
first int start at byte displacement 0, the second at displacement 1 and
the third at 2. That means that your class is 6 bytes in total, not really
what your C code is expecting from this struct.
What you should do is declare this field in myOut as an int (1), or
redeclare _mode as an enum (2).

1)
[StructLayout(LayoutKind.Sequential)]
public class myout
{
public int mode;
...

2)
[Flags]
public enum _mode
{
BW = 0x01,
Gray = 0x02,
Color = 0x04
}

[StructLayout(LayoutKind.Sequential)]
public class myout
{
public _mode mode;
...

Willy.
 
P

philipqiu

hi

The unmanaged code is as follows:
typedef struct tagstructtest1
{
DWORD dwon;
}structtest1, * pstructtest1 ;

typedef struct tagstructtest
{
DWORD dwone;
structtest1 two;
}structtest, *pstructtest ;

void teststruct( pstructtest pmytest)
{
pmytest = new structtest;
ZeroMemory(pmytest, sizeof(pmytest));
pmytest->dwone=999;
pmytest->two.dwon=123456;
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top