2016-03-10 3 views
1

Позвольте мне задать еще один вопрос, потому что, хотя я видел много подобных, никто не говорит об этом аспекте ... У меня есть C++ DLL (нет исходного кода, но .lib и .h) и Я написал необходимую управляемую оболочку. Нет проблем с этим, вопрос о структурах и перечислениях, определенных в исходном коде на C++, и их очень много, все они должны быть подвержены коду C#. В учебниках и образцах обычно используются простые типы данных, такие как float и strings, а не сценарии реального мира сложных структур данных.Использование структур C++/CLI из C#

Моя управляемая оболочка C++/CLI потребляет неуправляемые структуры из файлов заголовков DL .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); 
} 

Но это не компилируется (не может конвертировать между структурами и не с найдено подходящее преобразование).

Есть ли решение, которое не требует создания новых структур данных и ручного копирования всех элементов структуры все время и обратно? Не только из-за дополнительной работы и времени, но есть действительно много структур.

ответ

2
public ref struct INFO_WRAP 

Вы не объявили структуру, это класс в терминах C#. Подробное описание реализации Quirky C++, структура C++ - это просто класс со всеми его членами. public. Вы должны использовать value struct в C++/CLI, чтобы объявить эквивалент структуры C#.

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

Это неправильно, как хорошо, так как INFO_WRAP на самом деле тип ссылки вы всегда должны объявить его с^шляпой. Или с%, чтобы передать его по ссылке, конечно, намерение здесь.

Основные препятствия в пути, о чем вы просите, напрямую не поддерживается. Управляемому компилятору не разрешается делать какие-либо предположения о макете управляемой структуры. И будет лаять, когда вы попробуете это в любом случае. По очень веской причине это just not that predictable. Макет - это сильная деталь исполнения во время выполнения и может измениться, если код работает в другой среде выполнения. Подобно 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)); 
} 

С работами просто отлично и выполняется на любой платформе, структура проста. Когда структура не проста или вы, скажем, модифицируете Native и забываете модифицировать Managed, тогда есть ад, чтобы заплатить, стек и GC-куча коррупции - очень неприятные неудачи и очень трудно отлаживать.

+0

Первой причиной попытки 'ref structs' было то, что в реальных структурах есть много членов' char [n] '. Насколько я могу судить, я должен использовать 'array <>' и инициализировать их до нужного размера в конструкторе, из которых не имеет значений structs. Что касается функции маршалинга, хорошая идея, я использовал ее несколько раз в C#. –

+0

'value struct' не позволяет вам определять конструктор * default *, но они позволяют вам определять любой другой конструктор. (Конструктор по умолчанию всегда «Assign zeros & nullptr для всех полей».) На мой взгляд, «ref struct» легко ошибиться с первого взгляда. Для максимальной ясности я всегда использую 'ref class' или' value struct' в моем коде, а не другие. –

+0

Да, но я бы предпочел, чтобы эти структуры имели char (или другие) массивы по умолчанию, без необходимости вызова специального конструктора. И, просматривая Интернет, я нашел решение, используя шаблон C++. Немного похоже на хак, но умный в этом ... :-) Жаль, что комментарий не позволяет правильно отформатировать код ... –

0

Вот полное решение, в полном описании, для других людей, следующих за мной. :-) Давайте предположим, что у нас есть DLL с Перечисления, структуры, классы, функции в файле заголовка .h:

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; 

Преобразовать STRUCT обычным способом, а также добавить две статические функции преобразования, которые обрабатывают маршалинга. Как отметил Ханс, столь же утомительным, как кажется, кусочное копирование является единственным действительно надежным решением для платформ и архитектур.

#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; 
} 

В принципе, вам нужно использовать обычные и собственные функции структуры сортировочных для преобразования входящих и исходящих данных вручную.

Смежные вопросы