簡體   English   中英

從C#使用C ++ / CLI結構

[英]Using C++/CLI structs from C#

讓我開始另一個問題,因為盡管我看到了許多類似的問題,但沒有人真正談論過這一方面……我有一個C ++ DLL(沒有源代碼,只有.lib和.h),並且編寫了必要的托管包裝器。 這樣做沒有問題,問題在於原始C ++代碼中定義的結構和枚舉,其中有很多都需要向C#代碼公開。 教程和示例通常使用簡單的數據類型(例如浮點數和字符串),而不是復雜數據結構的真實場景。

我的托管C ++ / CLI包裝器使用DLL的.h頭文件中的非托管結構。 我包裝的類成員函數一直都在使用它們。 因此,我需要在我的C#代碼中使用相同的結構,將它們傳遞並從C ++代碼接收。 顯然,我無法避免在C#中重新定義所有它們,但是即使那樣,使用它們還是有問題的。 讓我們舉個例子:一個簡單的結構,供非托管代碼中的函數使用:

typedef struct INFO {
  ...
} INFO;

int GetInfo(INFO& out_Info);

我在C ++ / CLI包裝器代碼中聲明了相同的結構:

public ref struct INFO_WRAP {
  ...
};

int GetInfo(INFO_WRAP out_Info);

包裝器代碼中的實現會嘗試將此新結構轉換為原始結構,以使用舊的非托管代碼:

int Namespace::Wrapper::GetInfo(INFO_WRAP out_Info) {
  pin_ptr<INFO> pin_out_Info = out_Info;
  return self->GetInfo(*(::INFO*)pin_out_Info);
}

但這不會編譯(無法在結構之間轉換並且找不到合適的轉換)。

有沒有一種解決方案不涉及創建新的數據結構以及始終無休止地手動復制所有struct成員? 不僅因為額外的工作和時間,而且確實有很多結構。

  public ref struct INFO_WRAP

您沒有聲明結構,這是C#術語中的類。 一個古怪的C ++實現細節,一個C ++結構只是一個所有成員都是public的類 您需要在C ++ / CLI中使用value struct來聲明與C#結構等效的結構。

  int Namespace::Wrapper::GetInfo(INFO_WRAP out_Info)

這也是錯誤的,因為INFO_WRAP實際上是引用類型,所以必須始終使用^帽子聲明它。 或使用%作為參考傳遞它,當然是這里的意圖。


基本障礙阻礙了您的要求,但不直接支持。 托管編譯器不允許對托管結構的布局進行任何假設。 並且無論如何嘗試都會吠叫。 出於很好的理由,它只是不可預測的 布局是一個重要的運行時實現細節,如果代碼在其他運行時上運行,則布局可以更改。 像32位和64位一樣,可能在一個中起作用,但在另一個中不起作用。 正如喬恩所發現的。

一對一地復制字段始終有效,並且性能足夠。 只是不是程序員曾經喜歡維護的代碼。 您可以要求框架為您執行此操作,請調用Marshal :: PtrToStructure()或StructureToPtr()。


作弊可能的,並且在編寫C ++ / CLI代碼時肯定會考慮到這種情況。 畢竟,該語言的重點是使互操作快速。 您只是使保修無效, 必須在要支持的任何平台上徹底測試代碼。 一個簡單的例子:

public value struct Managed {
    int member1;
    int member2;
};

struct Native {
    int member1;
    int member2;
};

void GetInfo(Managed% info) {
    Native n = { 1, 2 };
    pin_ptr<Managed> pinfo = &info;
    memcpy(pinfo, &n, sizeof(n));
}

由於工作正常且可以在任何平台上執行,因此結構很簡單。 如果結構不簡單,或者說您修改了本機而忘了修改托管,那么您將付出巨大的代價,堆棧和GC堆損壞是非常令人不愉快的事故,並且很難調試。

這是為跟隨我的其他人提供的完整解決方案的完整描述。 :-)假設我們在.h頭文件中有一個包含枚舉,結構,類和函數的DLL:

typedef int (*DelegateProc)(long inLong, char* inString, STRUCT2* inStruct, long* outLong, char* outString, STRUCT2* outString);

typedef struct STRUCT1 {
  long aLong;
  short aShort;
  BOOL aBoolean;
  char aString[64];
  STRUCT2 aStruct;
  DelegateProc aDelegateProc;
  char Reserved[32];
} STRUCT1;

按常規方式轉換結構,並添加兩個處理封送處理的靜態轉換函數。 正如Hans所指出的那樣,看起來很乏味,分段復制是跨平台和體系結構的唯一真正可靠的解決方案。

#include <msclr\marshal.h>
using namespace msclr::interop;

public delegate int DelegateProc(long inLong, String^ inString, STRUCT2 inStruct, [Out] long% outLong, [Out] String^ outString, [Out] STRUCT2 outStruct);

[StructLayout(LayoutKind::Sequential, Pack = 1)]
public value struct WRAP_STRUCT1 {
  long aLong;
  short aShort;
  bool aBoolean;
  [MarshalAs(UnmanagedType::ByValArray, SizeConst = 64)]
  array<char>^ aString;
  WRAP_STRUCT2 aStruct;
  DelegateProc^ aDelegateProc;
  [MarshalAs(UnmanagedType::ByValArray, SizeConst = 32)]
  array<char>^ Reserved;

  static STRUCT1 convert(WRAP_STRUCT1^ other) {
    STRUCT1 clone;
    clone.aLong = other->aLong;
    clone.aShort = other->aShort;
    clone.aBoolean = other->aBoolean;
    sprintf(clone.aString, "%s", other->aString);
    clone.aStruct = WRAP_STRUCT2::convert(other->aStruct);
    clone.aDelegateProc = (Delegate1Proc)Marshal::GetFunctionPointerForDelegate(other->aDelegateProc).ToPointer();
    return clone;
  }

  static WRAP_STRUCT1 convert(STRUCT1 other) {
    WRAP_STRUCT1 clone;
    clone.aLong = other.aLong;
    clone.aShort = other.aShort;
    clone.aBoolean = (other.aBoolean > 0);
    clone.aString = marshal_as<String^>(other.aString)->ToCharArray();
    clone.aStruct = WRAP_STRUCT2::convert(other.aStruct);
    clone.aDelegateProc = (DelegateProc)Marshal::GetDelegateForFunctionPointer((IntPtr)other.aDelegateProc, DelegateProc::typeid);
    return clone;
  }
};

接下來,我們通常在.h頭文件中有一個類:

class __declspec(dllexport) CLASS1 {

public:
  CLASS1();
  virtual ~CLASS1();

  virtual int Function1(long inLong, char* inString, STRUCT2* inStruct);
  virtual int Function2(long* outLong, char* outString, STRUCT2* outStruct);
};

我們需要創建一個包裝器類。 它的標題:

public ref class Class1Wrapper {

public:
  Class1Wrapper();
  ~Class1Wrapper();

  int Function1(long inLong, String^ inString, WRAP_STRUCT2 inStruct);
  int Function2([Out] long% outLong, [Out] String^ outString, [Out] WRAP_STRUCT2% outStruct);

private:
  CLASS1* self;
};

及其實現:

Namespace::Class1Wrapper::Class1Wrapper() {
  self = new CLASS1();
}

Namespace::Class1Wrapper::~Class1Wrapper() {
  self->~CLASS1();
}

int Namespace::Class1Wrapper::Function1(long inLong, String^ inString, WRAP_STRUCT2 inStruct) {
  char pinString[64];
  sprintf(pinString, "%s", inString);
  STRUCT2 pinStruct = WRAP_STRUCT2::convert(inStruct);

  return self->Function1(inLong, pinString, pinStruct);
}

int Namespace::Class1Wrapper::Function2([Out] long% outLong, [Out] String^ outString, [Out] WRAP_STRUCT2% outStruct) {
  long poutLong;
  char poutString[64];
  STRUCT2 poutStruct;
  ::ZeroMemory(&poutStruct, sizeof(poutStruct));

  int result = self->Function2(poutLong, poutString, poutStruct);

  outLong = poutLong;
  outString = marshal_as<String^>(poutString);
  outStruct = WRAP_STRUCT2::convert(poutStruct);
  return result;
}

基本上,您需要使用通常的和您自己的struct marshalling函數來手動轉換傳入和傳出數據。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM