#c -cli #marshalling #unmanaged #wrapper #managed
#c -cli #сортировка #неуправляемый #оболочка #управляемый
Вопрос:
Я новичок в C / CLI, но уже много лет пишу управляемый код… очевидно, слишком много лет. 🙂
Пытаюсь написать оболочку для неуправляемого класса, предоставленного третьей стороной, и вижу некоторые странные вещи. Я надеюсь, что вы все сможете помочь мне разобраться в том, в чем заключается мой нубизм и что на самом деле странно.
Оболочка CLI:
public ref class Wrapper
{
public:
Wrapper(const float* flts, unsigned int fltlen, int offset)
{
_unmanagedClass = new UnmanagedClass(flts, fltlen, offset);
}
~Wrapper()
{
delete _unmanagedClass;
}
String^ getSomeString()
{
string x = _unmanagedClass->getSomeString(); //1
String^ ret = gcnew String(x.c_str()); //2
return ret; //3
}
private:
UnmanagedClass* _unmanagedClass;
};
Я должен также отметить, что у меня есть эти директивы в заголовке;
#pragma managed(push, off)
#include "Unmanaged.h"
#pragma comment(lib, "lib\Unmanaged_dll.lib")
#pragma managed(pop)
Вот Unmanaged.h;
class UNMANGED_API UnmanagedClass
{
public:
UnmanagedClass(const float* flts, uint fltlen, int offset);
string getSomeString() { return _someString; }
private:
string _someString;
};
Все это компилируется. Затем возникает странность / недостаток опыта.
При отладке этого в конфигурации ОТЛАДКИ UnmanagedClass::getSomeString()
, похоже, возвращается допустимое / ожидаемое строковое значение. Я могу увидеть это, установив точку останова на //2
и просмотрев значение x
. Если я перейду к //3
, я увижу, что это ret
имеет значение x
. Однако, если я пытаюсь выйти / перезапустить //3
, я получаю пару неудачных утверждений ( BLOCK_TYPE_IS_VALID
и _CrtIsValidHeapPointer
), и отладчик останавливается, никогда не возвращаясь к управляемой реализации.
При отладке этого в конфигурации ВЫПУСКА я не получаю неудачных утверждений и возвращаюсь к своей управляемой реализации, но строковое значение, возвращаемое getSomeString()
, является мусором, где бы я его ни просматривал; //2
, //3
а также в управляемой реализации.
Я переделал код несколькими различными способами, но безрезультатно.
Я думаю, что есть некоторый мэшаллинг, который нужно обойти //2
, но я не смог найти ничего, что действительно попадало бы в цель в том, как преобразовать это basic_string
в System::String^
, или если это вообще требуется. Если это так, то мы были бы весьма признательны за некоторую помощь с явным синтаксисом.
Я также сузил вызов, который выдает неудачные утверждения, до //1
возврата return ""; //3
. Эти утверждения указывают на попытку изменить / удалить память, которая не существует в куче, к которой имеет доступ текущая среда выполнения. Связано ли это с необходимостью маршалирования возвращаемого значения UnmangedClass::getSomeString()
?
Надеюсь, я просто упускаю здесь какую-то простую концепцию, и проблем со сторонним кодом нет. Пожалуйста, дайте мне знать, если я смогу предоставить более подробную информацию, и приношу извинения за мое почти полное незнание прародителя всех языков.
Заранее спасибо за любую информацию или «указатели»;
РЕДАКТИРОВАТЬ: Добавление клиентской реализации, управляемой C #;
public unsafe string GetString(List<float> flts )
{
float[] fltArr = flts.ToArray();
Wrapper wrap;
fixed (float* ptrFlts = fltArr)
{
wrap = new Wrapper(ptrFlts , fltArr.Length, 0);
}
var x = wrap.getSomeString();
return x.ToString();
}
РЕДАКТИРОВАТЬ: Добавление Dumpbin.exe подпись Unmanged.dll !Не измененный класс::getSomeString()
(public: class std::basic_string
<char,struct std::char_traits<char>,class std::allocator<char> >
__thiscall Codegen::getSomeString(void))
Ответ №1:
Эта проблема не имеет ничего общего с .NET или C / CLI, проблема заключается исключительно в машинном коде.
Вы нарушили правило единого определения для std::string
, если ваше определение не совсем соответствует тому, что Unmanaged_dll.dll
используется, начнется настоящий ад. И это звучит так, как будто эта DLL используется для определения отладки / компоновки класса.
Комментарии:
1. Спасибо за ваш ответ. Правильно ли я вас понимаю, что определение std:string меняется между режимами отладки и выпуска? Я отредактировал основной пост, чтобы предоставить сигнатуру dumpbin для Unmanaged::getSomeString . Есть ли какой-нибудь способ, которым я могу более точно соответствовать их строковому типу? Нужно ли мне всегда запускать мою оболочку в конфигурации отладки, если я хочу использовать машинный код как есть?
2. @Josh: У тебя есть код для
Unmanaged_dll.dll
? Если это так, «правильное» исправление заключается в использовании разных имен для конфигураций отладки и выпуска, точно так же, как это делает Microsoft CRT (например MSVCR90.dll против MSVCR90D.dll ), а затем используйте#if NDEBUG
вместе с#pragma comment(lib)
для выбора правильной. Если вы не можете создать релизную версию DLL, то вы также не можете использовать release CRT для своего кода. Одна из причин, по которой я не рекомендую совместно использовать классы через границы DLL.3. @Josh: Также возможно, что меняется макет
UnmanagedClass
самого_someString
файла, так что местоположение, в котором хранится DLL, находится не там, где ваш компилятор ищет его.4. @Ben, еще раз спасибо за ваши быстрые ответы! У меня нет доступа к источнику, но у меня есть некоторый доступ к автору. Просто чтобы мои утки были в ряд, это так же просто, как если бы автор предоставил биты отладки и выпуска? Я также упомяну, что я связываюсь с использованием библиотеки lib, а не DllImport … к сожалению, я не знаю, имеет ли это значение…
5. @Josh: Это можно заставить работать, если оба модуля используют одинаковый макет для стандартной библиотеки … это означает версию компилятора (включая пакеты обновления), debug vs release, _SCL_ITERATOR_DEBUGGING и т.д. Куча должна быть общей, поэтому вам также необходимо использовать DLL-версию CRT. Это неизбежно вызовет проблемы, когда вы захотите перейти к следующей отличной новой версии компилятора. Менее хрупкое исправление заключается в том, что вы предоставляете буфер как
char*
и заставляете его копировать в него свою строку (может потребоваться вторая функция, чтобы сначала получить размер, чтобы вы могли выделить свой буфер достаточно большим).
Ответ №2:
Вы просто отлично преобразовали свою собственную строку в управляемую строку. В этой статье о MSDN приведены примеры преобразования между всеми различными типами строк, которые поставляются на платформах Microsoft:
Сказав это, я взял ваш код и скомпилировал его, и я не смог добиться сбоя. Конечно, мне пришлось придумать свой собственный способ инициализации UnmanagedClass ::_someString , что я и сделал, просто выполнив это:
UnmanagedClass::UnmanagedClass(const float* /*flts*/, unsigned int /*fltlen*/, int /*offset*/)
{
_someString = "A few of my favorite things";
}
Когда я сделал это и прошел через этот код:
#include "stdafx.h"
#include "Wrapper.h"
int _tmain(int argc, _TCHAR* argv[])
{
Wrapper^ w = gcnew Wrapper(NULL, 0, 0);
System::String^ s = w->getSomeString();
return 0;
}
Это сработало просто отлично.
Вот остальная часть того, что я сделал:
// UnmanagedClass.h
#pragma once
#pragma unmanaged
#include <vector>
class UnmanagedClass
{
public:
UnmanagedClass(const float* flts, unsigned int fltlen, int offset);
std::string getSomeString() { return _someString; }
private:
std::string _someString;
};
И это реализация:
// UnmanagedClass.cpp
#include "UnmanagedClass.h"
#include <tchar.h>
UnmanagedClass::UnmanagedClass(const float* /*flts*/, unsigned int /*fltlen*/, int /*offset*/)
{
_someString = "A few of my favorite things";
}
И управляемый класс
// wrapper.h
#pragma once
#pragma unmanaged
#include "UnmanagedClass.h"
#pragma managed
public ref class Wrapper
{
public:
Wrapper(const float* flts, unsigned int fltlen, int offset)
{
_unmanagedClass = new UnmanagedClass(flts, fltlen, offset);
}
~Wrapper()
{
delete _unmanagedClass;
}
System::String^ getSomeString()
{
std::string x = _unmanagedClass->getSomeString(); //1
System::String^ ret = gcnew System::String(x.c_str()); //2
return ret; //3
}
private:
UnmanagedClass* _unmanagedClass;
};
Я надеюсь, что это немного поможет.
Комментарии:
1. @C Johnson: Спасибо, что нашли время повторить мой senario и подтвердить мой собственный / управляемый переход, все помогает. (К сожалению, я все еще слишком новичок, чтобы голосовать.)