【问题标题】:using a class defined in a c++ dll in c# code在 c# 代码中使用在 c++ dll 中定义的类
【发布时间】:2010-09-23 19:46:08
【问题描述】:

我有一个用 c++ 编写的 dll,我需要在我的 c# 代码中使用这个 dll。经过搜索,我发现使用 P/Invoke 可以让我访问我需要的函数,但是这些函数是在一个类中定义的,并且使用非静态私有成员变量。所以我需要能够创建这个类的一个实例来正确使用这些函数。如何获得对此类的访问权限以便创建实例?我一直无法找到一种方法来做到这一点。

我想我应该注意 c++ dll 不是我的代码。

【问题讨论】:

    标签: c# c++ dll pinvoke


    【解决方案1】:

    无法在 C# 代码中直接使用 C++ 类。您可以间接使用 PInvoke 来访问您的类型。

    基本模式是为类 Foo 中的每个成员函数创建一个关联的非成员函数,该函数调用该成员函数。

    class Foo {
    public:
      int Bar();
    };
    extern "C" Foo* Foo_Create() { return new Foo(); }
    extern "C" int Foo_Bar(Foo* pFoo) { return pFoo->Bar(); }
    extern "C" void Foo_Delete(Foo* pFoo) { delete pFoo; }
    

    现在只需将这些方法 PInvoking 到您的 C# 代码中

    [DllImport("Foo.dll")]
    public static extern IntPtr Foo_Create();
    
    [DllImport("Foo.dll")]
    public static extern int Foo_Bar(IntPtr value);
    
    [DllImport("Foo.dll")]
    public static extern void Foo_Delete(IntPtr value);
    

    缺点是您将有一个笨拙的 IntPtr 来传递,但围绕该指针创建一个 C# 包装类以创建一个更可用的模型是一件简单的事情。

    即使您不拥有此代码,您也可以创建另一个 DLL,该 DLL 包装原始 DLL 并提供一个小的 PInvoke 层。

    【讨论】:

    • “没有办法在C#代码中直接使用C++类”的说法是不正确的。静态 C++ 函数的工作方式与 C 风格函数相同,实例函数可以通过添加指向实例本身的 ThisObject 作为第一个参数来声明为 ThisCall 调用约定。有关详细信息,您可能需要阅读我的Blogs。您可能还想试试我们的工具。(我是作者)
    • 虽然在当时是一个不错的答案,但使用 c++/CLI 可以说更好,以避免手动创建代理函数——这可能会很快变得相当乏味
    • C++ 代理函数应该被extern "C" { .... } 包围,还是纯粹在C++ 中完成?
    • @jaredpar 我应该在哪里复制 c++ dll?在 c# exe 旁边?我得到 System.EntryPointNotFoundException: 'Unable to find an entry point named '...' in DLL
    【解决方案2】:

    编组 C++ 类并使用 PInvoke

    C++ 代码,ClassName.h

    class __declspec(dllexport) CClassName
    {
     public:
        CClassName();
        ~CClassName();
        void function();
    
    };
    

    C++ 代码,类名.cpp

    CClassName::CClassName()
    {
    }
    
    CClassName::~CClassName()
    {
    }
    
    void CClassName::function()
    {
        std::cout << "Bla bla bla" << std::endl;
    
    }
    

    C++ 代码,调用函数的 ClassNameCaller.h 文件

    #include "ClassName.h"      
    
    #ifdef __cplusplus
    extern "C" {
    #endif
    
    extern __declspec(dllexport) CClassName* CreateClassName();
    
    extern __declspec(dllexport) void DisposeClassName(CClassName* a_pObject);
    
    extern __declspec(dllexport) void function(CClassName* a_pObject);
    
    
    #ifdef __cplusplus
    }
    #endif
    

    C++ 代码,调用函数的 ClassNameCaller.cpp 文件

    #include "ClassNameCaller.h"
    
    
    CClassName* CreateClassName()
    {
        return new CClassName();
    }
    
    void DisposeClassName(CClassName* a_pObject)
    {
        if(a_pObject!= NULL)
        {
            delete a_pObject;
            a_pObject= NULL;
        }
    }
    
    void function(CClassName* a_pObject)
    {
        if(a_pObject!= NULL)
        {
            a_pObject->function();
        }
    }
    

    C#代码

    [DllImport("ClassNameDll.dll")]
    static public extern IntPtr CreateClassName();
    
    [DllImport("ClassNameDll.dll")]
    static public extern void DisposeClassName(IntPtr pClassNameObject);
    
    [DllImport("ClassNameDll.dll")]
    static public extern void CallFunction(IntPtr pClassNameObject);
    
    //use the functions
    IntPtr pClassName = CreateClassName();
    
    CallFunction(pClassName);
    
    DisposeClassName(pClassName);
    
    pClassName = IntPtr.Zero; 
    

    【讨论】:

    • 第一个文件 ClassName.h 中不应该有“dllexport”而不是“dllimport”吗?
    • 是的,它用于将这个类导出到.Net项目中,这样我们就可以使用CClassName类
    • 这个例子真的很有帮助,非常感谢:-)
    • 嗨。不确定这是否为时已晚,但您能否展示对代码的修改,我可以通过函数传递字符串,修改它们,然后将它们返回给 c# 代码?我一直在尝试,但它不允许我在 a_pObject->function(std::string args1); 中添加类型名称
    • 附带说明,如果您收到“BadImageFormatException”,您可能需要更改平台。我像往常一样将我的 C++ 代码编译为 x64,但我为 C# 端创建的默认项目设置为“任何 CPU”。这不起作用,但一旦切换到 x64 就可以了。确保匹配。
    【解决方案3】:

    Here 是如何从 VB 调用 C++ 类方法的示例 - 对于 C#,您只需在步骤 4 中重写示例程序。

    【讨论】:

      【解决方案4】:

      您可能需要编写一个中间 DLL(也许用 C++)来为您处理这个并公开您需要的接口。您的 DLL 将负责加载第 3 方 DLL,创建此 C++ 对象的实例,并根据需要通过您设计的任何 API 公开其成员函数。然后,您将使用 P/Invoke 获取您的 API 并干净地操作该对象。

      注意:对于您的 DLL 的 API,请尝试将数据类型限制为基元(long、int、char* 等)以防止模块边界问题。

      【讨论】:

        【解决方案5】:

        我这样做的方法是在我的非托管 C++ DLL 周围创建一个精简的托管 C++ 包装器。托管包装器包含“代理”类,这些类包装非托管代码,公开 .NET 应用程序所需的接口。 这是一项双重工作,但它允许在正常环境中进行非常无缝的操作。在某些情况下(例如 ASP.NET),依赖关系确实会变得更棘手,但您可能不会遇到这种情况。

        【讨论】:

          【解决方案6】:

          我同意 JaredPar。 应该不可能在托管代码中创建非托管类的实例。

          另一件事是 - 如果您可以在托管 C++ 中重新编译 DLL 或用它制作一个 COM 组件,那会容易得多/

          【讨论】:

            猜你喜欢
            • 2013-12-31
            • 2013-10-09
            • 1970-01-01
            • 1970-01-01
            • 2014-08-20
            • 1970-01-01
            • 2012-01-05
            相关资源
            最近更新 更多