C# 使用C++/C中的CLI结构#

C# 使用C++/C中的CLI结构#,c#,c++-cli,C#,C++ Cli,让我开始另一个问题,因为虽然我看到了许多类似的问题,但没有一个真正谈到这方面。。。我有一个C++ DLL(没有源代码,但是.LIB和.H),我写了必要的托管包装。这个问题没有问题,问题在于C++原始代码中定义的结构和枚举,并且有很多,它们都需要暴露于C代码。教程和示例通常使用简单的数据类型,如浮点和字符串,而不是复杂数据结构的真实场景 我的托管C++/CLI包装使用DLL的.h头文件中的非托管结构。我包装的类成员函数一直在使用它们。因此,我需要在C代码中使用相同的结构,传递它们并从C++代码中接

让我开始另一个问题,因为虽然我看到了许多类似的问题,但没有一个真正谈到这方面。。。我有一个C++ DLL(没有源代码,但是.LIB和.H),我写了必要的托管包装。这个问题没有问题,问题在于C++原始代码中定义的结构和枚举,并且有很多,它们都需要暴露于C代码。教程和示例通常使用简单的数据类型,如浮点和字符串,而不是复杂数据结构的真实场景

我的托管C++/CLI包装使用DLL的.h头文件中的非托管结构。我包装的类成员函数一直在使用它们。因此,我需要在C代码中使用相同的结构,传递它们并从C++代码中接收。很明显,我无法避免在C#中重新定义所有这些函数,但即使如此,使用它们也是有问题的。让我们举个例子:非托管代码中的函数使用的简单结构:

typedef struct INFO {
  ...
} INFO;

int GetInfo(INFO& 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);
}
我在C++/CLI包装器代码中声明了相同的结构:

public ref struct INFO_WRAP {
  ...
};

int GetInfo(INFO_WRAP out_Info);
包装器代码中的实现尝试将此新结构转换为原始结构,以便使用旧的非托管代码:

typedef struct INFO {
  ...
} INFO;

int GetInfo(INFO& 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);
}
int Namespace::Wrapper::GetInfo(INFO\u WRAP out\u INFO){
pin_ptr pin_out_Info=out_Info;
return self->GetInfo(*(::INFO*)pin\u out\u INFO);
}
但这不会编译(无法在结构之间转换,并且找不到合适的转换)

是否有一种解决方案不涉及创建新的数据结构和始终来回手动复制所有结构成员?不仅仅是因为额外的工作和时间,还有很多结构

  public ref struct INFO_WRAP
您没有声明结构,这是一个C#术语的类。奇巧C++实现细节,C++结构是一个具有所有成员的类。您需要在C++/CLI中使用
value-struct
来声明C#struct的等价物

这也是错误的,因为INFO_WRAP实际上是一个引用类型,所以必须始终使用^hat声明它。或者用%通过引用传递,这肯定是本文的意图


基本障碍排除在外,你所要求的并没有得到直接支持。托管编译器不允许对托管结构的布局进行任何假设。不管怎样,当你尝试它时,它都会吠叫。这是一个很好的理由。布局是一个强大的运行时实现细节,如果代码在不同的运行时上运行,布局可能会发生变化。就像32位对64位一样,可能在其中一个中工作,但在另一个中不工作。正如乔恩发现的那样

一个接一个地复制字段总是有效的,而且性能足够好。只是不是程序员喜欢维护的代码。您可以通过调用或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));
}
管理的公共值结构{
国际会员1;
国际会员2;
};
结构本机{
国际会员1;
国际会员2;
};
作废GetInfo(托管%info){
本机n={1,2};
pin_ptr 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;
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);
};
以通常的方式转换结构,并添加两个处理封送处理的静态转换函数。正如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;
  }
};
我们需要创建一个包装器类。其标题:

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;
}
Namespace::Class1Wrapper::Class1Wrapper(){
self=newclass1();
}
命名空间::Class1Wrapper::~Class1Wrapper(){
self->~CLASS1();
}
int名称空间::Class1Wrapper::Function1(长inLong,字符串^inString,WRAP\u STRUCT2){
字符pinString[64];
sprintf(pinString,“%s”,inString);
STRUCT2 pinStruct=WRAP_STRUCT2::convert(指令);
返回self->Function1(inLong、pinString、pinStruct);
}
int Namespace::Class1Wrapper::Function2([Out]long%outLong,[Out]String^outString,[Out]WRAP_STRUCT2%outStruct){
长嘴;
char poutString[64];
结构2:浇注结构;
::零内存(&poutStruct,sizeof(poutStruct));
int result=self->Function2(poutLong、poutString、poutStruct);
outLong=撅嘴;
伸出=马歇尔_as(管柱);
outStruct=WRAP_STRUCT2::convert(poutStruct);
返回结果;
}

基本上,您需要使用常用的结构编组函数和您自己的结构编组函数来手动转换传入和传出数据。

尝试
ref structs
的第一个原因是实际结构有很多
char[n]
成员。据我所知,我必须使用
array
并在构造函数中将它们初始化为正确的大小——而结构没有值。至于封送函数,好主意,我在C#中也多次使用它。
value struct
s不允许您定义默认构造函数,但允许您定义任何其他构造函数。(默认构造函数总是“将零和空ptr分配给所有字段”。)在我看来,
ref struct
乍一看很容易出错。为了更清楚,我总是在代码中使用
ref class
value struct
,而不是其他的。是的,但我更希望这些结构在默认情况下有char(或其他)数组,带有