2012-02-18 69 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