【发布时间】:2012-05-03 18:11:37
【问题描述】:
经过大量研究和实验,我仍然无法解决从外部库访问非托管函数时遇到的问题。
快速相关的背景故事:我用 Visual C++ 编写了一个应用程序来驱动一个通过 USB 连接到系统的外部仪表,但正在虚拟化一个串行端口,我注意到奇怪的输出行为。为了为自己辩护,制造商希望我使用他们的 .dll 从我的应用程序中控制仪表。很好,但是....
我无法直接包含这个 .dll 作为参考(名称和路径已删除):
所以为了使用它,我大概查看了 DllImport。我决定将自己的程序集作为驱动程序的包装器,而不是将代码直接包含到我的应用程序中,这样我就可以通过类访问功能。在 .dll 上做了一个 dumpbin /exports 后,我找到了所有函数的入口点,并制作了一个这样的 C# 类库,仅包含相关示例:
namespace Meter
{
public class PortDrv
{
[DllImport("PortDrv.dll", EntryPoint = "SERIALNUMBER",
CallingConvention = CallingConvention.StdCall,
CharSet = CharSet.Auto)]
public static extern long SerialNumber(Byte Index);
[DllImport("PortDrv.dll", EntryPoint = "OPENPORT",
CallingConvention = CallingConvention.StdCall,
CharSet = CharSet.Auto)]
public static extern int OpenPort();
};
}
函数原型是从他们发给我的描述其库的 .pdf 文件中提取的:
SERIALNUMBER (ByVal Index As Byte) As Long
OPENPORT () As Integer
并且也在他们的示例VB程序中使用:
Private Declare Function SerialNumber Lib "PortDrv" Alias "SERIALNUMBER" (Index As Byte) As Long
Private Declare Function OpenPort Lib "PortDrv" Alias "OPENPORT" () As Integer
还和我在一起吗?行。所以在编译我自己的程序集之后,我添加了对我的应用程序的引用并访问了包装器:
int port_return = PortDrv::OpenPort();
Byte bite = 0x31;
__int64 serial = PortDrv::SerialNumber(bite);
但在尝试检索序列号后它会炸弹:
而且我不太确定我哪里出错了。一些函数返回正确的信息,但似乎我必须传递信息才能失败。我已经尝试了 CharSets 和 CallingConventions 的所有不同组合,我已经将 ExactSpelling 设置为 true 等。有什么明显的地方我做错了,或者我可以不在我当前的环境中使用这个库吗?
编辑:我忘了提,我将“1”传递给函数的原因是,如果只有一米连接到系统,“索引”将为 1。如果有 2 米,我可以通过传入'2'来访问第二个。
【问题讨论】:
-
哎哟。我不认为制造商可以为您提供.NET 包装器或.NET 环境的工作示例代码?鉴于明显的困难,这似乎不是一个不合理的要求。
-
嗯。您确定 0x31 是有效输入吗?
-
@Cameron:我相信。我也尝试过传入'1'和1。我想通过明确说明角色来排除所有可能的错误解释。或者你是在暗示我应该通过 0x01
-
访问您的 C# 包装类的代码似乎是 C++/CLI 代码。你为什么要创建一个访问 C/C++ 代码的 C# 包装类。我猜 C++/CLU 的调用约定不是 StdCall。
-
VB 中的 Integer 数据类型为 16 位大小,Long 数据类型为 32 位大小。所以,我认为你必须改变你的互操作签名。对 SerialNumber 使用 short 而不是 int (OpenPort) 和 int 而不是 long。