从C#调用C++库

8
这个问题可能看起来像之前的问题的重复。我已经阅读了一系列的帖子,但对我的情况不是完全清楚。
我有一个使用Momentics IDE创建的C++库。我必须能够将这个库用于C#项目中。
之前有人在这个项目上工作过,然后交给了我。目前,有两个层次可以实现这一点。首先,一个C++项目包括完整的库和一个C++包装器。这个项目创建了一个dll作为输出。然后将这个C++ dll提供给一个C#项目,这个C#项目具有对C++ dll的dllimport调用。这个C#项目再次创建一个dll。最后,为了在C#应用程序中使用库,我必须引用这两个dll。
这是使它正常工作的正确方法吗?我想可能有一种简化这个过程的方法。
请问有人能帮我解决这个问题吗?

可能是重复的问题:如何从C#调用本地的C++? - Randolpho
3个回答

9
考虑到您使用的是C++库,我假设它利用了C++语义,例如类,而不仅仅是暴露过程。如果是这种情况,通常是通过手动创建托管C++互操作库来完成的。
基本上,您可以在Visual Studio中创建一个托管的C++库,引用现有的C++库,并在现有的C++类周围创建一个托管的包装器。然后,在C#项目中引用此(托管)C++程序集,并将原始(非托管)C++库包含在C#程序集中,就像放置在构建目录中的文件一样。
这是必需的,因为没有办法通过P/Invoke(DllImport)调用引用诸如C++类之类的内容。
如果您的基础库只是一系列函数,则可以通过P/Invoke函数直接在C#项目中引用它。
无论哪种方式,上述所有库(对于第一种方式,非托管C++库、托管C++程序集和C#项目,或对于第二种方式,非托管C++库和C#项目)都必须包含在引用它们的任何项目中。您不能将非托管库静态链接到托管程序集中。

3
+1,但我想指出,除非您使用的是.NET 1.1,否则“managed C++”现在被称为“C++/CLI”,两者之间存在巨大差异。 - Randolpho
另外,我认为你的意思是“无法动态链接非托管库”。静态链接是使用该库的唯一方法。 - Randolpho
非常感谢您的回复。正如您所提到的,未托管的库确实具有类和接口。既然如此,我认为我必须要有一个C++包装器和一个C#包装器才能在我的C#应用程序中使用未托管的C++库,对吗?现在,C++包装器的任务是创建类的实例并实现接口,这是正确的吗?我还有一个问题。未托管的库是否提供extern "C"和_dllexport功能?目前它没有,但我可以将其作为下一个项目的要求,所以只想确认一下。 - Batul
1
@Batul:回答您的第一个问题,是的;您需要在C++/CLI中创建一个包装类(感谢@Randolpho澄清术语),该类消耗经典的C++类型。对于您的第二个问题,如果您想走“DllImport”这条路线,那么您的非托管库需要公开“extern "C"”函数,以便它们在完全托管的(C#、VB.NET等)程序集中可用。请注意,我们在这里谈论的是两件不同的事情;通常,您要么使用C++/CLI程序集作为包装器,要么导出过程函数,并在您的托管代码中“DllImport”它们。 - Adam Robinson
@AdamRobinson 我试图在C#应用程序中使用Sony相机远程SDK,该SDK具有返回C ++类的某些函数,这些类又包含指向其他类的方法和指针。 我认为C ++ CLI包装器是正确的方法,需要创建一些包装器类。 在哪里可以找到一个好的当前示例,使用VS 2019进行操作。术语C ++ / CLI似乎已不再存在。 谢谢。 我发了一个关于如何做到这一点的问题,但目前没有有用的回复。 - Duncan Groenewald
显示剩余4条评论

3
似乎你有一个多余的封装,但也许有人正在实施某种外观或添加属性等。托管和非托管之间的基本连接将是DllImport“flat”函数调用 - 不是成员函数 - 或C++/CLI代码调用成员函数。如果包装器是C++/CLI,它最容易编写(只需包含C++库的头文件),最容易调用(C#代码只需向其添加.NET引用并像往常一样继续)所以如果项目中有C ++专业知识,这将是我的首选。

听起来你接手的人在走弯路。如果方法不到20个,建议从头开始。


重新开始似乎也是我最好的解决方案,但一旦开始,无论我看什么,都只指出当前的解决方案是正确的方法。从您的建议中我理解到的是,需要有一个C++包装器来处理未管理的C++库中的类,然后在C#中使用dllImport调用。我的理解正确吗? - Batul
如果您添加了一个C++/CLI包装器,它可以避免使用P/Invoke(DllImport)。基本上,您的C++/CLI代码只需“包含头文件,链接库”即可使用本机代码。在C++/CLI代码中任何被声明为“public ref class Foo”的内容都可以从C#中调用,而无需任何互操作机制 - 只需添加引用并使用即可。 - Kate Gregory
谢谢Kate。从我目前所了解的来看,DllImport需要在未托管的lib中使用平面结构,即没有类和接口。但是我的未托管的lib有类和接口,这是否意味着我不能使用DllImport?如果是这样,那么创建c++/cli包装器是唯一的选择吗? - Batul
在 C++ 领域中,很少有“唯一的方法” ,只有更好的方法。编写一个平面包装器以便使用 DllImport 似乎不是一个好主意。我更喜欢采用 C++/CLI 的方法。 - Kate Gregory

0

您可以使用:

DllImport

class Example
{
    // Use DllImport to import the Win32 MessageBox function.
    [DllImport("user32.dll", CharSet = CharSet.Unicode)]
    public static extern int MessageBox(IntPtr hWnd, String text, String caption, uint type);

    static void Main()
    {
        // Call the MessageBox function using platform invoke.
        MessageBox(new IntPtr(0), "Hello World!", "Hello Dialog", 0);
    }
}

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