【问题标题】:Hard time using DLL import in C#在 C# 中使用 DLL 导入很难
【发布时间】:2014-07-14 07:51:33
【问题描述】:

早上好/白天/晚上好!

我收到了必须从 SCADA 系统 (Indusoft Web Studio) 检索标签的 dll。它与实际上工作得很好的 VC++ 和 VB 示例一起出现。目前我需要获取这些值并在网络上显示它们(使用 ASP.NET)。我决定使用 C# 来处理从 SCADA 到 HTML 的值(嗯,实际上 Microsoft ASP.NET 指南有点建议这样做)。这就是我卡住的地方,我无法使该功能正常工作。

我为导入的 DLL 创建了类,如下所示:

using System.Runtime.InteropServices;

namespace TagAccess
{
    public class ISRW
    {
        [DllImport("C:\\Windows\\SysWOW64\\ISRWExtDLL.dll", CharSet = CharSet.Auto )]
        public static extern string UNReadString([MarshalAs(UnmanagedType.BStr)] string szTagName);
    }
}

不幸的是,当我尝试调用这个函数时,它给了我:

    Managed Debugging Assistant 'PInvokeStackImbalance' has detected a problem in 'C:\Users\Denis\Documents\Visual Studio 2013\Projects\TagAccess\TagAccess\bin\Debug\TagAccess.vshost.exe'.

    Additional information: A call to PInvoke function 'TagAccess!TagAccess.ISRW::UNReadString' has unbalanced the stack. This is likely because the managed PInvoke signature does not match the unmanaged target signature. Check that the
calling convention and parameters of the PInvoke signature match the target unmanaged signature.

在 C 中完美运行的功能如下所示:

CString CISRWExt::UNReadString(LPCTSTR szTagName)
{
    CString result;
    static BYTE parms[] =
        VTS_BSTR;
    InvokeHelper(0x1, DISPATCH_METHOD, VT_BSTR, (void*)&result, parms,
        szTagName);
    return result;
}

有什么建议吗?提前致谢。

上面的信息有点额外。我从 DLL 中导入了另一个函数,现在这个类看起来像这样:

namespace TagAccess
{


    public class ISRW
    {
        [DllImport("C:\\Windows\\SysWOW64\\ISRWExtDLL.dll", EntryPoint = "#1", CharSet = CharSet.Unicode,  SetLastError = true, CallingConvention = CallingConvention.Winapi, ThrowOnUnmappableChar = true )]
        public static extern string UNReadString([MarshalAs(UnmanagedType.BStr)] string szTagName);

        [DllImport("C:\\Windows\\SysWOW64\\ISRWExtDLL.dll", CharSet = CharSet.Auto)]
        public static extern string UNWriteString([MarshalAs(UnmanagedType.BStr)] string szTagName, [MarshalAs(UnmanagedType.BStr)] string szValue);

      }
}

Write Function (UNWriteString) 实际上将 C# 中的值写入 SCADA(我可以在 SCADA 查看器中看到正在更改的值),但在它正常工作之后我又收到另一个错误:

An unhandled exception of type 'System.NullReferenceException' occurred in mscorlib.dll

Additional information: Object reference not set to an instance of an object.

【问题讨论】:

  • 如何在 C# 中调用这个函数?
  • @User2012384 ISRW.UNReadString("SysUrl");

标签: c# c++ .net pinvoke scada


【解决方案1】:

在 C 中完美运行的功能如下所示:

它不是 C,它是 C++。一个名为 CISRWExt 的 C++ 类的实例方法。无法调用 C++ 类方法,不能正确调用类的构造函数和析构函数。只有 C++ 编译器可以做到这一点,你需要用 C++/CLI 语言编写一个包装器。

您展示的方法是自动生成的 方法。它是由用于 MFC 的 Visual Studio 向导从 COM 服务器的类型库生成的。这是从 C++ 使用 COM 服务器的好方法。这不是从 C# 中使用它的好方法。

您应该完全绕过这个 ISRWExtDLL.dll 包装器并直接使用 COM 服务器。 C# 对带有类型库的 COM 服务器提供了出色的支持,您可以简单地使用 Project + Add Reference。你唯一需要知道的是这个类型库的位置。从vendor's documentation 来看,你应该可以在c:\windows\syswow64\ISRWExt.OCX 中找到它。如果您需要帮助,请给他们打电话。

【讨论】:

    【解决方案2】:

    你说你可以像这样从 C++ 调用:

    CString CISRWExt::UNReadString(LPCTSTR szTagName)
    {
        CString result;
        static BYTE parms[] =
            VTS_BSTR;
        InvokeHelper(0x1, DISPATCH_METHOD, VT_BSTR, (void*)&result, parms,
            szTagName); //<--- this is a COM dispatch call
        return result;
    }
    

    这意味着该函数可以通过 COM 互操作调用,而不是 PInvoke。使用 COM 互操作调用可能会解决一些问题。 尝试绕过 C++ dll,通过 COM 互操作包装器直接调用 COM 对象。

    【讨论】:

    • 没有。 CString 不是有效的 COM 互操作类型。
    • @DavidHeffernan OP 显示的 COM 调用中不涉及 CString
    • 这是asker 试图调用的函数UNReadString。不仅是 CString 返回值,还有实例方法。
    • @DavidHeffernan 理解且真实。但是如果OP直接调用COM函数,连CString的麻烦都会消失。
    • 好的,我明白你的意思了。我认为答案有点不清楚。
    【解决方案3】:
    CString CISRWExt::UNReadString(LPCTSTR szTagName)
    

    首先,这是 C++ 而不是 C。

    无法从 C# 调用此 C++ 函数。如果该函数是非静态成员函数,那么您应该知道 C++ 类不能从 C# 中使用。并且即使该函数是静态成员函数,它也会返回CString。这是 C# 代码不能使用的非托管 C++ 类。

    您需要为此 DLL 创建一个包装器。显而易见的方法是创建一个包含非托管 C++ 库的混合模式 C++/CLI 类库。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-03-27
      • 2016-05-23
      • 1970-01-01
      • 1970-01-01
      • 2011-11-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多