Return C++ arrays to C#

社会主义新天地 提交于 2019-12-23 02:09:08

问题


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.


回答1:


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
        }
    }
}



回答2:


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



来源:https://stackoverflow.com/questions/9340333/return-c-arrays-to-c-sharp

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!