c# - 未从 C# 中找到 C++ COM DLL 中的导出函数

标签 c# c++ dll com

我有一个现有的 COM DLL,当前可通过 VB 包装类访问(仅一个函数)并从 C# 类调用。

我正在尝试将回调添加到我的 C# 代码中(4 个单独的回调)。我选择的方法是我发现的唯一方法,但我遇到了问题。

它显示“无法在 DLL 'xxxx' 中找到名为 'InitDotNet' 的入口点。

我的DLL头文件:

extern "C"
{
#define DLL __declspec(dllexport)
typedef void (__stdcall * CB_func1)(int);
typedef void (__stdcall * CB_func2)(char *);

DLL void InitDotNet(CB_func1 func1, CB_func2 func2);
}

...

class CComInterface : public CCmdTarget
...
   afx_msg void mainCall(short parm1, LPCTSTR parm2);
...

我的 DLL C++ 文件:

...
CB_func1  func1Function;
CB_func2  func2Function;
...
IMPLEMENT_DYNCREATE(CComInterface, CCmdTarget)
...
BEGIN_DISPATCH_MAP(CComInterface, CCmdTarget)
   DISP_FUNCTION(CComInterface, "mainCall", mainCall, VT_EMPTY, VTS_I2 VTS_BSTR)
END_DISPATCH_MAP()
...
IMPLEMENT_OLECREATE(CComInterface, "MyDll.Interface", ...)

...
void CComInterface::mainCall(short parm1, LPCTSTR parm2)
{
   ...

   // at various times call func1Functoin and func2Function

   ...
}

DLL void InitDotNet(CB_func1 func1, CB_func2 func2)
{
   func1Function = func1;
   func2Function = func2;
}

我的 VB 包装如下所示:

Public Class MyWrapperClass
   Private Shared Protocol As Object = CreateObject("MyDll.Interface")

   Public Shared Sub mainCall(ByVal parm1 As Short, ByVal parm2 As String)
      Protocol.mainCall(parm1, parm2)
   End Sub
End Class

我的 C# 代码如下所示:

...
using System.Runtime.InteropServices
namespace MyNamespace
{
   public partial class MyForm : AnotherForm
   {
      ...
      [UnmanagedFunctionPointer(CallingConvention.StdCall)]
      public delegate void func1Callback(int value);

      [UnmanagedFunctionPointer(CallingConvention.StdCall)]
      public delegate void func2Callback(string value);

      [DllImport("mycppdll.dll")]
      public static extern void InitDotNet([MarshalAs(UnmanagedType.FunctionPtr)] func1Callback f1c,
         [MarshalAs(UnmanagedType.FunctionPtr)] func2Callback f2c);
      ...
      private void MyFunc()
      {
         func1Callback f1c = 
            (value) =>
            {
               // work here
            };
         func2Callback f2c =
            (value) =>
            {
               // work here
            };

         InitDotNet(f1c, f2c);

         MyWrapperDll.MyWrapperClass.mainCall(1, "One");

}

有人对我做错了什么有什么想法吗?

最佳答案

我看到的问题:

  1. InitDotNet 需要很长时间,而不是 CB_func1CB_func2。对于 64 位版本的程序来说,这是一个双重问题:它会导致 stdcall 函数的导出名称不匹配,更糟糕的是,如果 InitDotNet 设法以某种方式被调用,它可能会导致指针截断。

  2. InitDotNet 未标记为 __stdcall。默认调用约定是 cdecl。 cdecl命名约定是“前缀带下划线”,因此导出的名称是“_InitDotNet”。但是,stdcall 命名约定是“前缀带下划线,后缀带@,后跟参数的大小(以字节为单位)”,因此预期的导出名称将为“_InitDotNet@8”(当前签名采用两个长整型)。您应该使用像 dumpbin 或 depends.exe 这样的程序查看 DLL 导出的函数的名称。 这种不匹配可能是运行时找不到 InitDotNet 的原因(假设是 32 位 Windows)。如果已更正此问题,则不应将 EntryPoint 指定为 DllImport 属性(运行时将自动找出适当的名称)。

  3. 正如 cdhowie 在评论中所指出的,您需要保持传递给 native 代码的两个委托(delegate)“事件”。 .NET 垃圾收集器无法知道函数指针是否由 native 代码存储。为了防止垃圾收集器收集它们,请保留对委托(delegate)的引用(例如在保证比 native 代码使用它们的时间更长的对象的字段中)或使用GCHandle。请注意,如果您使用GCHandle:您不需要使用固定 handle ;实际传递给代码的函数指针是一个 stub ,即使垃圾收集器移动了委托(delegate),该 stub 仍保留在同一位置。但是,当收集委托(delegate)时, stub 将被删除,因此确保在 native 代码不再需要回调之前不会收集委托(delegate)至关重要。

关于c# - 未从 C# 中找到 C++ COM DLL 中的导出函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14385524/

相关文章:

c++ - 如何将数据写入 MutableBufferSequence

c++ - 从现有代码创建 .dll

c# - 如何获取 dll 函数的 id(内存地址)?

delphi - 如何将delphi tframe从dll加载到delphi应用程序

c# - 处理 .NET 核心构建的错误 CA1416 的正确方法是什么?

c# - 在 Windows 服务中使用 ocx 文件

android - 原生android库编译

Delphi LoadLibrary 找不到其他目录的 DLL - 有什么好的选择吗?

c# - GetHostEntry() 不再解析地址

c# - 为什么 var m = 6++++++++ 6;在 C# 中有效吗?