开发者

P/Invoke problem marshalling parameter

It seems I have yet another problem in the understanding of marshalling to C++ DLL.

Here is the def of the C++ function & struct :

#define SIZE_PLATE         (28l)
#define SIZE_HJT           (15l)
#define SIZE_DATE          (10)

typedef struct _tyrfdePlate
{
    TCharA PlateID[SIZE_PLATE];
    TInt32 NetworkID;
    TInt32 CityID;
    TCharA DateS[SIZE_DATE];
    TCharA DateE[SIZE_DATE];
    TInt32 Width;
    TInt32 Height;
    TBool  Light;
    TBool  Roll;
    TCharA CycleID[4]; 
    TInt16 OrHjt1;
    TCharA HJTID1[SIZE_HJT];
    TInt16 OrHjt2;
    TCharA HJTID2[SIZE_HJT];
    TInt16 OrHjt3;
    TCharA HJTID3[SIZE_HJT];
    TInt16 OrHjt4;
    TCharA HJTID4[SIZE_HJT];
} tyrfdePlate;

TInt32 __stdcall tyrfdeSetResults(TInt32 TargetNbr, const TInt32* pTargets, TInt32 PlateNbr, const tyrfdePlate* pPlates);

This is what I made in C# based on a previous question I asked:

[StructLayout(LayoutKind.Sequential, Size = 138), Serializable]
public struct Plate
{
    [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 28)]
    public string PlateID;
    public int NetworkID;
    public int CityID;
    [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 10)]
    public string DateS;
    [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 10)]
    public string DateE;
    public int Width;
    public int Height;
    public bool Light;
    public bool Roll;
    [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 4)]
    public string CycleID;
    public short OrHJT1;
    [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 15)]
    public string HJTID1;
    public short OrHJT2;
    [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 15)]
    public string HJTID2;
    public short OrHJT3;
    [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 15)]
    public string HJTID3;
    public short OrHJT4;
    [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 15)]
    public string HJTID4;
}

[DllImport("tyrfde.dll", EntryPoint = "tyrfdeSetResults")]
public static extern int SetResults(int targetNbr, [MarshalAs(UnmanagedType.LPArray)] int[] targetIds, int plateNbr, [MarshalAs(UnmanagedType.LPArray)] Plate[] plates);

Here is an example of the call:

List<Plate> plates = new List<Plate>();
plates.Add(new Plate() { PlateID = "56013208", NetworkID = 992038, CityID = 60010, DateS = "01012009", DateE = "31122010", Width = 400, Height = 300, Light = false, Roll = false, CycleID = "0", OrHJT1 = 2, HJTID1 = "579026356", OrHJT2 = 2, HJTID2 = "579026377", OrHJT3 = 2, HJTID3 = "58571903", OrHJT4 = 0, HJTID4 = "0" });
int[] targets = new int[]{1,2,11,12,130};

int result = SetResults(5, targets, 1, plates.ToArray());

Note that I also tried with native Array instead of generic list with same result.

So basically I'm redoing a test app made in C++ with the same Data. Unfortunately, the function return me -1 which means an error occurred, but the C++ app returns 23. So I guessed something is wrong with either my struct and/or the parameter开发者_运维问答 I pass. Probably the int[]. I've tried to let the default marshal with ref, but didn't work. Any ideas?

EDIT

Since the type definition seem to be very important here is the def :

typedef void           TVoid;
typedef bool           TBool;
typedef char           TCharA; // character         8
typedef TCharA         TChar;  // character         8
typedef wchar_t        TCharW; // character        16
typedef signed   char  TInt08; // integer   signed  8
typedef unsigned char  TUnt08; // integer unsigned  8
typedef signed   short TInt16; // integer   signed 16
typedef unsigned short TUnt16; // integer unsigned 16
typedef signed   long  TInt32; // integer   signed 32
typedef unsigned long  TUnt32; // integer unsigned 32
typedef float          TFlt32; // float 32
typedef double         TFlt64; // float 64


The Size attribute you gave in the [StructLayout] attribute is a good hint. Verify that with this snippet:

        int len = Marshal.SizeOf(typeof(Plate));
        System.Diagnostics.Debug.Assert(len == 138);

The only way you are going to get passed this assertion is when you replace "bool" with "byte" (So TBool = 1 byte) and use a packing of 1:

[StructLayout(LayoutKind.Sequential, Pack=1), Serializable]
public struct Plate {
  //...
}

If that still doesn't work then you'll really have to debug the unmanaged code.


What you really want is a way to debug this. The easiest way is to write your own dll that consumes this data type and see what happens to the struct on the other side.

I suspect that your real problem is structure alignment and how that's working out. What I see in your code is a bunch of elements with odd sizes (15, 28, 10). Chances are the target system has gone and aligned the structure elements on at least 2 byte, if not 4 byte boundaries. Nonethless you should check.

You can also save yourself some time by writing the C that consumes the actual struct and outputs the results of a bunch of offsetof() invocations on the struct elements.

You should be methodical in your approach instead of shotgun, and part of the methodology is to have measurements and feedback. This will give you both.


1) How many bytes is a TBool? 1? 4?

2) You can remove the StructLayout(LayoutKind.Sequential, Size = 138) attribute because it's Sequential by default and the Size can be determined by the runtime.

3) You can drop the [MarshalAs(UnmanagedType.LPArray)] attribute. The marshaller knows how to marshal arrays, but note, by default arrays are Marshalled as [IN], so if the c++ code is going to edit the contents of the arrays, you need to use the [IN,OUT] attribute.


How about the const TInt32* pTargets argument in the dll. I don't know how it's used, but that suggests a pointer to a single TInt32 instance, not an array of TInt32. Granted, it depends on how it's used in the code.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜