C#封装导入.dll的方法,“尝试读取或写入受保护的内存”错误

4
经过一番研究和实验,我仍然不能解决一个问题,即如何从外部库访问非托管函数。
简单的相关背景:我用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);

但在尝试检索序列号后它失败了:enter image description here 我不太确定哪里出错了。一些函数返回正确的信息,但似乎所有需要传递信息的函数都失败了。我已经尝试了各种字符集和调用约定的组合,将ExactSpelling设置为true等。我是否做错了明显的事情,或者我不能在当前环境中使用这个库?
编辑:我忘记提到,我将'1'传递给函数的原因是,如果系统只连接了一个电表,“Index”将为1。如果有两个电表,我可以通过传入“2”来访问第二个电表。

@Cameron:我相信是这样的。我还尝试了传入'1'和1。为了排除所有可能的错误解释,我想明确地说明字符。或者你是在暗示我应该传入0x01吗? - B L
3
VB中的整型数据类型大小为16位,长整型数据类型大小为32位。因此,我认为您需要更改Interop签名。在OpenPort中使用short代替int,在SerialNumber中使用int代替long。 - Hans
@Hans 不行。将所有的 int 改为 short,将所有的 long 改为 int,结果还是一样。 - B L
@Brett,示例VB程序是否有效? - Lukazoid
@Lukazoid 是的,它是用VB6编写的。 - B L
显示剩余5条评论
1个回答

5
你所提供的示例VB程序:
Private Declare Function SerialNumber Lib "PortDrv" Alias "SERIALNUMBER" (Index As Byte) As Long  

没有使用ByVal。在VB中默认为ByRef。这是你的笔误还是问题所在?如果是的话,你需要在签名中对SerialNumber说ref Byte

教训:始终使用ByRefByVal


以上那行代码是他们使用驱动程序的示例程序中包含的。你可能认为那是我的代码,但实际上我直接复制了它,所以是正确的。在他们的原型中,字节通过 ByVal 传递:SERIALNUMBER (ByVal Index As Byte) As Long。这是他们的失误吗? - B L
我将签名更改为ref Byte,内存故障已经消失,但是该函数仍然返回空值(使用int和long)。我想将您的答案标记为正确,因为这是主题的重点,但由于我的问题仍未解决,我现在想保持开放状态。 - B L
是的,我想原型是错误的。感谢你的敏锐眼力,本。 - B L

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接