仍然处理从这里开始的问题 Calling C++ dll function from C#: Of structs, strings and wchar_t arrays.,但采用不同的方法。
在示例Calling Managed Code from Unmanaged Code and vice-versa之后,我用C ++编写了一个托管包装器来访问非托管C ++ dll中的unmanages类。
看起来像这样:
//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 ++类型?
答案 0 :(得分:5)
转换大多数C数据类型的一种方法是使用PInvoke Interop Assitant。它将为大多数C结构创建适当的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来完成工作。
答案 1 :(得分:0)
在创建dll包装器时我偶然发现了几点:
因此,我的包装器看起来像这样(如using a class defined in a c++ dll in c# code中所示):
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()从非托管dll返回一个指向该CcnOCRsdk类的指针,该函数我必须调用该函数。 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#中使用我的包装器了。当我这样做而没有完全按照我在dumpbin的输出中指定入口点时,我“无法找到入口点”错误。
[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中预期的结果。