返回 C++数组到 C#

发布于 2025-01-06 10:44:34 字数 1586 浏览 1 评论 0原文

我正在尝试一个小型互操作应用程序,其中我从 C# 调用一些 C++ 方法。我有一个非常基本的示例,用于调用方法并返回整数,效果很好。

InteropApp.h

#ifdef DLLFUNCTIONEXPOSETEST_EXPORTS
#define DLLFUNCTIONEXPOSETEST_API __declspec(dllexport)
#else
#define DLLFUNCTIONEXPOSETEST_API __declspec(dllimport)
#endif

extern "C" DLLFUNCTIONEXPOSETEST_API int fnSumofTwoDigits(int a, int b);

InteropApp.cpp

#include "stdafx.h"
#include "DLLFunctionExposeTest.h"
BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    return TRUE;
}
DLLFUNCTIONEXPOSETEST_API int fnSumofTwoDigits(int a, int b)
{
    return a + b;
}

C# InteropAppTest

static class TestImport
    {
        [DllImport("DLLFunctionExposeTest.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "fnSumofTwoDigits")]
        public static extern int fnSumofTwoDigits(int a, int b);
    }
public partial class MainWindow : Window
    {
        public MainWindow()
        {
            try
            {
            InitializeComponent();
            int g = TestImport.fnSumofTwoDigits(2, 1);
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.ToString());
        }
    }
}

在上面的 C++ 应用程序中,我希望有一个方法返回字符串数组 code>,以及另一个返回整数数组的方法。但是,正如我读到的那样,我很困惑这是否涉及编组? C++ 和 C# 的方法原型是什么样的?为了在 C# 应用程序中调用 C++ 数组返回函数,还需要更改哪些内容?

为上述事情获得一个简单的例子将是很棒的,因为我无法找到任何简单的例子来开始。

I am trying a small interop application, in which I am invoking some C++ methods from C#. I have a very basic example woking for invoking a method and returning an integer, which works just fine.

InteropApp.h

#ifdef DLLFUNCTIONEXPOSETEST_EXPORTS
#define DLLFUNCTIONEXPOSETEST_API __declspec(dllexport)
#else
#define DLLFUNCTIONEXPOSETEST_API __declspec(dllimport)
#endif

extern "C" DLLFUNCTIONEXPOSETEST_API int fnSumofTwoDigits(int a, int b);

InteropApp.cpp

#include "stdafx.h"
#include "DLLFunctionExposeTest.h"
BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    return TRUE;
}
DLLFUNCTIONEXPOSETEST_API int fnSumofTwoDigits(int a, int b)
{
    return a + b;
}

C# InteropAppTest

static class TestImport
    {
        [DllImport("DLLFunctionExposeTest.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "fnSumofTwoDigits")]
        public static extern int fnSumofTwoDigits(int a, int b);
    }
public partial class MainWindow : Window
    {
        public MainWindow()
        {
            try
            {
            InitializeComponent();
            int g = TestImport.fnSumofTwoDigits(2, 1);
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.ToString());
        }
    }
}

In the above C++ application, I want one method to return an array of strings, and another method to return an array of integers. But, as I have read to do the same, I am confused if this would involve marshalling? What would the method prototypes look like both for C++ and C#? What else would be changed to invoke the c++ array-returning function in C# app?

It would be great to get a simple example for the above thing, since I wasn't able to find any straightforward eg to start with.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

暗藏城府 2025-01-13 10:44:34

建议的方法是使用中间 C++/CLI 项目(也称为隐式 P/Invoke 技术)。对涉及使用 stl 容器或复杂类的任何内容进行显式 P/Invoke (DllImport) 可能会令人沮丧或不可能。此外,它的类型不安全:您必须猜测正确的签名才能进行编组,并且通常有多种可行的可能性,这可能会令人困惑。 C++(stl) 和 CLR 中字符串的内部表示根本不匹配,因此您必须在它们之间进行转换,这会导致渲染和显式 P/Invoke 封送处理变得很痛苦。

推荐的方法如下。请注意,我没有编译代码,因此可能会有一些小错误,但我可以向您保证它可以通过这种方式完成:我已经这样做了很多次。

假设您在 DLL 本机项目(标头 Utils.h)中有这个:

DLLFUNCTIONEXPOSETEST_API std::vector<std::string> GetStrings();

请注意,了解这些字符串在 c++ stl::string 中如何内部编码非常重要(例如 UTF-8,如果您很酷的话):

现在您创建一个 CRL C++ 项目(UtilsW.h/UtilsW.cpp 对):

#include <Utils.h>

using namespace std;
using namespace System;
using namespace System::Text;
using namespace System::Collections::Generics;

namespace Interop
{
    public ref class Utils
    {
    public:
        static List<String ^> ^ GetStrings()
        {
            List<String ^> ^ret = gcnew List<String ^>();
            vector<string> strings = ::GetStrings();
            vector<string>::iterator begin = strings.begin();
            vector<string>::iterator end = strings.end();
            for (; it != end; it++)
                ret.Add(gcnew String((*it).c_str(), 0, (*it).lenght(), Encoding::UTF-8);
            return ret;
        }
    };
}

现在您创建一个 .NET 项目,例如在 C# 中:

using Interop;

namespace NET
{
    public class Program
    {
        void foo()
        {
            List<string> strings = Utils.GetStrings();
            // Do something with strings
        }
    }
}

The recommended way for this is using an intermediate C++/CLI project (also called implicit P/Invoke tecnique). Doing explicit P/Invoke (DllImport) for anything that involves using stl containers or complex classes can be frustrating or impossible. Moreover it's type unsafe: you have to guess the correct signature to do the marshalling and often there are multiple possibilities that may work and this may be confusing. The internal representations of a string in C++(stl) and in the CLR simply don't match so you have to convert between them and this renders and explicit P/Invoke marshaling painful.

The recommended way is the following. Please note I didn't compile the code so there may be some little errors, but I can ensure you it can be done in this way: I've done it plenty of times.

Let say you have this in a DLL native project (header Utils.h):

DLLFUNCTIONEXPOSETEST_API std::vector<std::string> GetStrings();

Please note that is important to know how those strings are internally encoded in the c++ stl::string (for example UTF-8 if you are cool):

Now you create a CRL C++ project (UtilsW.h/UtilsW.cpp pair ):

#include <Utils.h>

using namespace std;
using namespace System;
using namespace System::Text;
using namespace System::Collections::Generics;

namespace Interop
{
    public ref class Utils
    {
    public:
        static List<String ^> ^ GetStrings()
        {
            List<String ^> ^ret = gcnew List<String ^>();
            vector<string> strings = ::GetStrings();
            vector<string>::iterator begin = strings.begin();
            vector<string>::iterator end = strings.end();
            for (; it != end; it++)
                ret.Add(gcnew String((*it).c_str(), 0, (*it).lenght(), Encoding::UTF-8);
            return ret;
        }
    };
}

Now you create a .NET project for example in C#:

using Interop;

namespace NET
{
    public class Program
    {
        void foo()
        {
            List<string> strings = Utils.GetStrings();
            // Do something with strings
        }
    }
}
暖树树初阳… 2025-01-13 10:44:34

如果您不必在 C++ dll 中分配数组,则可以将空整数数组传递给函数并让它填充那里。这适用于默认编组。

但是,您无法将 std::string 封送至 C#,因为此类未知。您必须将字符串作为 C 字符串传递,最好是 wchar_t*。

请参阅:http://msdn.microsoft.com/en-us/library/bd99e6zt .aspx

If you do not have to allocate the array in the C++ dll, you can pass an empty integer array to the function and let it fill there. This works with default marshalling.

However you cannot marshal std::string to C# as this class is not known. You have to pass strings as C strings, preferably wchar_t*.

See: http://msdn.microsoft.com/en-us/library/bd99e6zt.aspx

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文