【问题标题】:C# wrapper for importing .dll, "Attempted to read or write protected memory"用于导入 .dll 的 C# 包装器,“尝试读取或写入受保护的内存”
【发布时间】: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。

标签: c# .net dll vb6 pinvoke


【解决方案1】:

您提供的示例 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。这是他们的一个小问题吗?
  • 我将签名更改为 ref Byte 并且内存故障已经消退,但该函数仍然没有返回任何内容(使用 int 和 long)。我想将您的答案标记为正确,因为这是该主题的重点,但由于我的问题仍未解决,我想暂时保留它。
  • 是的,我想原型是错误的。谢谢你敏锐的眼光,本。
猜你喜欢
  • 1970-01-01
  • 2014-03-16
  • 2010-10-10
  • 2010-10-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多