【问题标题】:Passing C# data type parameters to dll written in C++?将 C# 数据类型参数传递给用 C++ 编写的 dll?
【发布时间】:2009-03-26 01:04:08
【问题描述】:

仍在解决从这里开始的问题 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];
};

现在我有了一个可以导入到 C# 项目中的 dll。

但问题是: 当我想从 dll 文件中调用方法时,像这样:

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

它需要 C++ 参数 - 指向 char 和 RECO_DATA 的指针。

如何解决这个问题并从 C# 代码传递 C++ 类型?

【问题讨论】:

    标签: c# c++ types


    【解决方案1】:

    转换大多数 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 来完成工作。

    【讨论】:

      【解决方案2】:

      我在创建 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 中得到了我期望的结果。

      【讨论】:

        猜你喜欢
        • 2021-01-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-12-02
        • 2013-02-26
        • 2018-04-10
        • 1970-01-01
        相关资源
        最近更新 更多