使用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值却跟实际值有偏差。以下是返回值和实际值的情况:
通过调用接口返回currvalue其它类型的值,例如bool类型的bval或float 类型的rval时,返回值和实际值却是正确的。只有返回int32类型的lval才出现以上错误。
百思不得其解......
一个星期后,我把所有数字都转换成二进制表示形式:
我们可以看到,真实值和实际值只有最后一个字节是不同的,前面三个字节都相同。还有什么规律呢?
进一步我们可以发现,无论为正数还是负数,返回值最后一个字节的值都是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,当时编程的时候断章取义了,由于开发环境的改变无法去验证是否这个错误导致,希望有类似经验的朋友注意一下。
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 |
| |
百思不得其解......
一个星期后,我把所有数字都转换成二进制表示形式:
返回值 | 实际值 |
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,当时编程的时候断章取义了,由于开发环境的改变无法去验证是否这个错误导致,希望有类似经验的朋友注意一下。
相关文章推荐
- 浅析C# 使用Process调用外部程序中所遇到的参数问题
- C#调用非托管DLL时遇到“找不到指定的模块”问题解决方法
- 从代码都发布遇到的问题总结(C#调用非托管dll文件,部署项目)
- 从代码都发布遇到的问题总结(C#调用非托管dll文件,部署项目) 转
- 使用VS编写ASP.NET(C#)遇到问题及解决(持续补充)
- asp.net c#.net项目中使用总结,遇到的各个问题的汇总
- ASP.NET 4.0中使用FreeTextBox和FCKeditor遇到安全问题警告的解决办法
- PInvoke调用导致堆栈不对称 c#调用C++win32非托管dll的问题深度分析
- sqllite (1) - c# .net 使用 sqllite 及版本选择问题
- C# 使用Linq递归查询数据库遇到的问题及解决方法
- 【转载】loadrunner使用system()函数调用Tesseract-OCR识别验证码遇到的问题
- 如何使用 ADO.NET 和 Visual C# .NET 调用带参数的存储过程
- 刚才遇到了关于C#使用外部DLL函数上的char*的问题。
- net中前台javascript与后台c#函数相互调用问题
- 初学AJAX遇到的的问题:使用javascript代码调用服务端代码时提示'WebForm1'未定义
- 【C#】ADO .Net Entities Framework使用查询语句时遇到的错误
- C#.NET常见问题(FAQ)-如何使用2D绘图控件ZedGraph绘制坐标轴和坐标曲线
- 升级Struts2.5后使用DMI动态方法调用遇到问题
- 在使用KSoap2调用webservice遇到的问题
- C#中使用textbox时遇到的换行问题