2012-02-18 42 views
1

我正在尝试一个小的互操作应用程序,其中我从C#调用了一些C++方法。我有一个非常基本的例子,用于调用一个方法并返回一个整数,这很好。将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()); 
     } 
    } 
} 

在AB Ove C++应用程序,我想要一种方法返回一个strings的数组,另一种方法返回一个integers的数组。但是,正如我读过的一样,如果这涉及编组,我很困惑。方法原型对于C++和C#都会是什么样子?在C#应用程序中还会改变什么来调用C++数组返回函数?

对于上面的问题,很容易得到一个简单的例子,因为我无法找到任何简单的例子来开始。

+0

你有什么试过的?你的意思是什么类型? 'std :: string'(或'char *'?)和'int'?或者没有关系? – svick 2012-02-18 10:22:44

+0

@svick:我宁愿以'char *'和'int'数组返回方法开始。 – Cipher 2012-02-18 10:37:27

回答

0

这个推荐的方法是使用一个中间的C/CLI项目(也称为隐式的P/Invoke tecnique)。对涉及使用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; 
     } 
    }; 
} 

现在您在例如C#创建.NET项目:

using Interop; 

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

他似乎并不需要“stl容器或复杂的类”,所以PInvoke可能是一个更简单的方法。 – svick 2012-02-18 15:09:18

0

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

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

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