回到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());
}
}
}
在上面的C ++应用程序,我想要一个方法返回字符串
的数组,而另一个方法返回的整数
的数组。但是,正如我已经阅读做同样的,我很困惑,如果这会涉及编组?将方法的原型是什么样子都为C ++和C#?还有什么将改为调用C ++数组返回在C#应用程序的功能?
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.
有关此操作的建议方法是使用一个中间的C ++ / CLI项目(也被称为隐性的P / Invoke tecnique)。这样做对于涉及使用STL容器或复杂的类可以是令人沮丧的或不可能的任何明确的P / Invoke(的DllImport)。此外,它的输入不安全的:你必须猜测正确的签名做编组,往往有可能工作,这可能是混乱的多种可能性。在CLR在C ++(STL)的字符串和内部表示根本不匹配,所以你必须在它们之间转换,这使得和明确的P / Invoke编组痛苦的。
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.
让说你在DLL本地项目(头Utils.h)有这样的:
Let say you have this in a DLL native project (header Utils.h):
DLLFUNCTIONEXPOSETEST_API std::vector<std::string> GetStrings();
请注意,重要的是要知道如何将这些字符串在C ++ STL内部编码::字符串(例如UTF-8,如果你是爽):
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):
现在您创建一个CRL C ++项目(UtilsW.h / UtilsW.cpp对):
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;
}
};
}
现在你用C#创建一个.NET项目,例如:
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
}
}
}