将C#数据类型参数传递给用C ++编写的dll?

时间:2009-03-26 01:04:08

标签: c# c++ types

仍然处理从这里开始的问题 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 ++类型?

2 个答案:

答案 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包装器时我偶然发现了几点:

  • 我不得不调用非托管类的成员函数。花了一些时间才发现我不能直接使用DllImport,但必​​须自己编写一个“包装器”。
  • 在包装器本身中,仅包装成员函数也是不够的。我必须能够创建一个指向C ++类的指针,所以我必须将指针导出到构造函数(至少,这就是我理解它的方式,也许这不完全正确。)。我最初尝试导出成员函数,它已编译但在运行时返回“AccessViolationException”。也被困在那里一段时间。

因此,我的包装器看起来像这样(如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中预期的结果。