您的位置:首页 > 编程语言 > C#

使用C#.net调用非托管DLL遇到的问题

2007-05-20 22:47 706 查看
2006年4月曾遇到一个托管封送的问题,一直没有解决,现在想起来可能是托管封送时数据类型不对错误导致的,现描述如下:
Using VisualStudio.net2003 framework1.1
C#是托管(Manage)类型的,在C#中调用标准C或C++编译的非托管(Unmanage)Dll时必须重新声明(Declare)有关接口(Interface)和结构(struct)。

现有非托管C结构声明如下:

typedef struct struTagValue{

_TCHAR TagName[C_FULL_TAGNAME_LEN];

longTimestamp;

longTagState;

BYTETagType;

union {

floatrval;

longlval;

BOOLbval;

_TCHAR sval[C_STRINGVALUE_LEN];

};

}TAGVAL, *LPTAGVAL;

 

头文件中有如下定义:

define _TCHAR char;

const DWORD C_FULL_TAGNAME_LEN  =     80;

const DWORD C_STRINGVALUE_LEN     =     128;

 

非托管调用接口声明如下

extern "C" BOOL PASCAL EXPORT Agda_GetNextTagValue(HRECORDSET hRecordset, TAGVAL * pTagVal, BOOL bRemoved=TRUE);

 

C#中重新封送结构的声明:

由于C#没有union,而且对象类型不能和非对象类型对齐,但union中所有变量相对偏移量(offset)是相同的,都是0,所以封送成两个结构,函数调用时也重载两个函数,不同参数分别对应不同的结构。

结构封送声明如下:

[[StructLayout(LayoutKind.Sequential)]

public struct TAGVAL

{

[ MarshalAs( UnmanagedType.ByValTStr, SizeConst=C_FULL_TAGNAME_LEN)]

public string TagName;

public System.Int32 Timestamp;

public System.Int32 TagState;

public System.Byte TagType;

[ StructLayout( LayoutKind.Explicit ,Size=C_STRINGVALUE_LEN )]

public struct strcurrvalue

{

[ FieldOffset( 0 )]

public float rval;

[ FieldOffset( 0 )]

public int lval;

[ FieldOffset( 0 )]

public bool bval;

};

public strcurrvalue currvalue;

};

 

[StructLayout(LayoutKind.Sequential)]

public struct TAGVAL_S

{

[ MarshalAs( UnmanagedType.ByValTStr, SizeConst=C_FULL_TAGNAME_LEN)]

public string TagName;

public System.Int32 Timestamp;

public System.Int32 TagState;

public System.Byte TagType;

[ StructLayout( LayoutKind.Sequential )]

public struct strcurrvalue

{

[ MarshalAs( UnmanagedType.ByValTStr, SizeConst=C_STRINGVALUE_LEN)]

             public string sval;

};

public strcurrvalue currvalue;

};

 

调用接口重载成两个,封送声明如下:

重载接口一:

[DllImport("AgilorAPI.dll")]

public static extern bool Agda_GetNextTagValue(System.Int32 hRecordset,ref TAGVAL pTagVal,bool bRemoved);

重载接口二:

[DllImport("AgilorAPI.dll")]

public static extern bool Agda_GetNextTagValue(System.Int32 hRecordset,ref TAGVAL_S pTagVal,bool bRemoved);

 

结构和接口封送没有任何问题,都是按MSDN说明来封送的,但通过调用接口返回的TAGVAL的currvalue 的lval值却跟实际值有偏差。以下是返回值和实际值的情况:

返回值

实际值

1

177

0

0

117249

117413

-255

-243

72449

72565

 

 

通过调用接口返回currvalue其它类型的值,例如bool类型的bval或float 类型的rval时,返回值和实际值却是正确的。只有返回int32类型的lval才出现以上错误。

百思不得其解......

一个星期后,我把所有数字都转换成二进制表示形式:

返回值

实际值

0000 0000 0000 0000 0000 0000 0000 0001

0000 0000 0000 0000 0000 0000 1011 0001

0000 0000 0000 0000 0000 0000 0000 0000

0000 0000 0000 0000 0000 0000 0000 0000

0000 0000 0000 0001 1100 1010 0000 0001

0000 0000 0000 0001 1100 1010 1010 0101

1111 1111 1111 1111 1111 1111 0000 0001

1111 1111 1111 1111 1111 1111 0000 1101

0000 0000 0000 0001 0001 1011 0000 0001

0000 0000 0000 0001 0001 1011 0111 0101

 

 

我们可以看到,真实值和实际值只有最后一个字节是不同的,前面三个字节都相同。还有什么规律呢?

进一步我们可以发现,无论为正数还是负数,返回值最后一个字节的值都是1即0000 0001,而当真实值为0,即0000 0000时,返回值也是0。理由在哪儿呢?

进一步研究我们可以知道,如果用一个字节表示bool类型,那么当其值为true时,应该是0000 0001,而当其为false时,应该是0000 0000。

也就是说,无论真实值为何值,最后一个字节总是按真实值的bool类型重新覆盖了一次!

原结构确实有一个bool类型的bval与int32类型的lval在同一个位置对齐,会不会是它把lval覆盖了一次呢?

我把bval提到lval的前面,结构变成:

[[StructLayout(LayoutKind.Sequential)]

public struct TAGVAL

{

[ MarshalAs( UnmanagedType.ByValTStr, SizeConst=C_FULL_TAGNAME_LEN)]

public string TagName;

public System.Int32 Timestamp;

public System.Int32 TagState;

public System.Byte TagType;

[ StructLayout( LayoutKind.Explicit ,Size=C_STRINGVALUE_LEN )]

public struct strcurrvalue

{

[ FieldOffset( 0 )]

public bool bval;

[ FieldOffset( 0 )]

public float rval;

[ FieldOffset( 0 )]

public int lval;

};

public strcurrvalue currvalue;

};

此时通过调用接口返回的值所有都是正确的!

这个结构里面的strcurrvalue应该跟联合(union)有相同的性质,但联合(union)却不应该有这样的问题,联合里面只有一个值,不应该再作一次覆盖。

这是编译器的一个bug吗?

 

时隔一年现在想起来应该数据类型:BOOLbval封送错误,对应的托管类型应是:long bval,而不是bool bval,当时编程的时候断章取义了,由于开发环境的改变无法去验证是否这个错误导致,希望有类似经验的朋友注意一下。

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  dll struct string float pascal c#