Передача параметров типа данных C # в dll, написанные на C ++? - PullRequest
2 голосов
/ 26 марта 2009

Все еще работает над проблемой, которая началась здесь Вызов функции C ++ dll из C #: структур, строк и массивов wchar_t. , но с другим подходом.

Следуя примеру Вызов управляемого кода из неуправляемого кода и наоборот Я написал управляемую оболочку на C ++ для доступа к неуправляемому классу в неуправляемой DLL C ++.

Это выглядит так:

//in header file
public __gc class TSSLDllWrapper
{
public:
TSSLDllWrapper();
     //this is the unmanaged class
CcnOCRsdk * _sdk;

bool convertHKID_Name(char *code, RECO_DATA *o_data);
};

//in .cpp file
TSSLDllWrapper::TSSLDllWrapper(void)
{
  _sdk = new CcnOCRsdk();
}

bool TSSLDllWrapper::convertHKID_Name(char *code, RECO_DATA *o_data)
{
return _sdk->convertHKID_Name(code, o_data);
}

//C++ RECO_DATA structure definition:
struct RECO_DATA{
wchar_t FirstName[200];
wchar_t Surname[200];
};

Теперь у меня есть dll, который я могу импортировать в мой проект C #.

Вот проблема, однако: Когда я хочу вызвать метод из файла DLL, как это:

TSSLDllWrapper wrapper = new TSSLDllWrapper();
bool res = wrapper.convertHKID_NameSimple( //need to pass parameters here );

Он ожидает параметры C ++ - указатели на char и RECO_DATA.

Как я могу это исправить и передать типы C ++ из кода C #?

Ответы [ 2 ]

5 голосов
/ 26 марта 2009

Одним из способов преобразования большинства типов данных C является использование PInvoke Interop Assitant . Это создаст надлежащие типы C # / VB.Net для большинства структур Си. Вот вывод для RECO_DATA

[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential, CharSet=System.Runtime.InteropServices.CharSet.Unicode)]
public struct RECO_DATA {

    /// wchar_t[200]
    [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.ByValTStr, SizeConst=200)]
    public string FirstName;

    /// wchar_t[200]
    [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.ByValTStr, SizeConst=200)]
    public string Surname;
}

Для параметра char * вы можете передать IntPtr.Zero или использовать Marshal.StringToCoTaskMemAnsi, чтобы выполнить работу.

0 голосов
/ 26 марта 2009

Пара моментов, на которые я наткнулся при создании оболочки dll:

  • Мне пришлось вызвать функцию-член неуправляемого класса. Потребовалось некоторое время, чтобы выяснить, что я не могу сделать это, используя DllImport напрямую, но должен написать «обертку» сам.
  • В самой оболочке также недостаточно просто обернуть функцию-член. Я должен быть в состоянии создать указатель на класс C ++, поэтому я должен экспортировать указатель в конструктор (по крайней мере, я так понимаю, может быть, это не совсем правильно). Сначала я попытался просто экспортировать функцию-член, она скомпилировалась, но вернула «AccessViolationException» во время выполнения. Застрял там тоже на некоторое время.

Следовательно, моя оболочка выглядит следующим образом (как предложено в с использованием класса, определенного в c ++ dll в коде c # ):

    public class __declspec(dllexport) Wrapper
{
public:
    CcnOCRsdk* SDKCreate()
    {
        return new CcnOCRsdk();
    }

    bool CcnOCRsdk_HKID(CcnOCRsdk* pSDK, char *code, RECO_DATA *o_data)
    {
        return pSDK->convertHKID_Name(code, o_data);
    }

    void SDKDelete(CcnOCRsdk* pSDK)
    {
        delete pSDK;    
    }
};

__declspec (dllexport) на уровне класса экспортирует всех открытых членов класса. SDKCreate () возвращает указатель на этот класс CcnOCRsdk из неуправляемой библиотеки DLL, функцию-член которой я должен вызвать. CcnOCRsdk_HKID вызывает эту функцию-член. Обратите внимание, что указатель на CcnOCRsdk передается.

После того, как код встроен в dll, я должен использовать DUMPBIN , чтобы выяснить, каковы «искаженные» точки входа для оболочки dll, которую я написал.

Для моей оболочки результаты выглядят так:

      1    0 00001240 ??4Wrapper@TSSL@@QAEAAV01@ABV01@@Z = __t2m@??4Wrapper@TSSL@@QAEAAV01@ABV01@@Z ([T2M] public: class TSSL::Wrapper & __thiscall TSSL::Wrapper::operator=(class TSSL::Wrapper const &))
      2    1 00001220 ?CcnOCRsdk_HKID@Wrapper@TSSL@@QAE_NPAVCcnOCRsdk@@PADPAURECO_DATA@@@Z = __t2m@?CcnOCRsdk_HKID@Wrapper@TSSL@@QAE_NPAVCcnOCRsdk@@PADPAURECO_DATA@@@Z ([T2M] public: bool __thiscall TSSL::Wrapper::CcnOCRsdk_HKID(class CcnOCRsdk *,char *,struct RECO_DATA *))
      3    2 00001200 ?SDKCreate@Wrapper@TSSL@@QAEPAVCcnOCRsdk@@XZ = ?SDKCreate@Wrapper@TSSL@@QAEPAVCcnOCRsdk@@XZ (public: class CcnOCRsdk * __thiscall TSSL::Wrapper::SDKCreate(void))
      4    3 00001410 ?SDKDelete@Wrapper@TSSL@@QAEXPAVCcnOCRsdk@@@Z = ?SDKDelete@Wrapper@TSSL@@QAEXPAVCcnOCRsdk@@@Z (public: void __thiscall TSSL::Wrapper::SDKDelete(class CcnOCRsdk *))

Теперь я наконец готов использовать свою оболочку в C #. Когда я сделал это, не указав точку входа точно так же, как в выводе из дампа, я получил ошибку «невозможно найти точку входа».

    [DllImport(@"TSSLWrapper.dll", EntryPoint = "?SDKCreate@Wrapper@TSSL@@QAEPAVCcnOCRsdk@@XZ")]
    public static extern IntPtr SDKCreate();
    [DllImport(@"TSSLWrapper.dll", EntryPoint = "?CcnOCRsdk_HKID@Wrapper@TSSL@@QAE_NPAVCcnOCRsdk@@PADPAURECO_DATA@@@Z")]
    public static extern bool CcnOCRsdk_HKID(IntPtr ptr, string num, out RECO_DATA o_data);

RECO_DATA определяется как предложено JaredPar.

И последний шаг - наслаждаться результатами. Сначала я должен вызвать конструктор класса, а затем передать указатель на фактический вызов функции

  RECO_DATA recoData = new cnOCRsdk.RECO_DATA();
  string num = "262125355174";

  IntPtr ptr = cnOCRsdk.SDKCreate();
  bool res = cnOCRsdk.CcnOCRsdk_HKID(ptr, num, out recoData);

Мой res возвращает true, и я получаю ожидаемые результаты в recoData.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...