从 C# 调用 C++ dll 中的方法,没有任何信息,只有头文件

Posted

技术标签:

【中文标题】从 C# 调用 C++ dll 中的方法,没有任何信息,只有头文件【英文标题】:Calling methods in C++ dll from C# without any info, only header files 【发布时间】:2016-11-10 08:48:24 【问题描述】:

我有一个第三方 DLL,我正在尝试用 C# 编写一个包装器,并且有 C++ 示例代码,它可以工作,但是当我尝试调用 DLL 中的方法时,我得到了错误:

无法在 DLL 'scard-com.dll' 中找到名为 'scan' 的入口点。

请帮忙指出我的问题:

这是示例 C++ 应用程序中使用的 .h 文件的一部分

interface ISCard_CardReaderDevices : IUnknown 
virtual UINT __stdcall scan(void) = 0;
virtual UINT __stdcall getDeviceCount(void) = 0;
virtual const CHAR * __stdcall getDeviceName(UINT id) = 0;
virtual const CHAR * __stdcall getSerialName(UINT id) = 0;
virtual ISCard_CardReader * __stdcall connectById(UINT id) = 0;
virtual ISCard_CardReader * __stdcall connectByName(const CHAR *name) = 0;
virtual ISCard_CardReader * __stdcall connectBySerial(const CHAR *serial) = 0;
virtual BOOL __stdcall disconnect(ISCard_CardReader *reader) = 0;
virtual ISCard_SecMsg * __stdcall attachSecMsg(ISCard_CardReader *reader) = 0;
virtual void __stdcall detachSecMsg(ISCard_SecMsg *secMsg) = 0;
virtual ISCard_Script * __stdcall attachScript(ISCard_CardReader *reader) = 0;
virtual void __stdcall detachScript(ISCard_Script *script) = 0;
virtual ISCard_IsoCard * __stdcall attachIsoCardByReader(ISCard_CardReader *reader) = 0;
virtual ISCard_IsoCard * __stdcall attachIsoCardBySecMSG(ISCard_SecMsg *secMsg) = 0;
virtual void __stdcall detachIsoCard(ISCard_IsoCard *isocard) = 0;
virtual ISCard_MTCOS * __stdcall attachMTCOSByReader(ISCard_CardReader *reader) = 0;
virtual ISCard_MTCOS * __stdcall attachMTCOSBySecMSG(ISCard_SecMsg *secMsg) = 0;
virtual void __stdcall detachMTCOS(ISCard_MTCOS *os) = 0;
virtual ISCard_IcaoConverter * __stdcall attachIcaoConverter(void) = 0;
virtual void __stdcall detachIcaoConverter(ISCard_IcaoConverter *icaoconv) = 0;
virtual ISCard_ImageConverter * __stdcall attachImageConverter(void) = 0;
virtual void __stdcall detachImageConverter(ISCard_ImageConverter *imgconv) = 0;
#ifdef MULTIAPP_EXT
virtual ISCard_IDLConverter * __stdcall attachIDLConverter(void) = 0;
virtual void __stdcall detachIDLConverter(ISCard_IDLConverter *idlconv) = 0;
virtual ISCard_sscdConverter * __stdcall attachSSCDConverter(void) = 0;
virtual void __stdcall detachSSCDConverter(ISCard_sscdConverter *sscdconv) = 0;
virtual ISCard_eHealthConverter * __stdcall attacheHealthConverter(void) = 0;
virtual void __stdcall detacheHealthConverter(ISCard_eHealthConverter *eHealthconv) = 0;
#endif // MULTIAPP_EXT
;

在我的 C# 项目中,我添加了这个类:

public class SCardWrapper

    [DllImport("scard-com.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern uint scan();

// And call it like this:

public int listReaders()
    
        try
        
            uint numreaders = scan();
            if (numreaders < 1)
            
                return 0;
            
            for (int i = 0; i < numreaders; i++)
            
                IntPtr iDevice = getDeviceName(uint.Parse(i.ToString()));
                String sDevice = Marshal.PtrToStringAuto(iDevice);
                Debug.WriteLine(string.Format("0 : " + sDevice, i));
            
            return int.Parse(numreaders.ToString());
        
        catch
        
            return -1;
        
    

但是一旦它到达 scan() 方法,我就会得到上面的错误。任何帮助将不胜感激!

【问题讨论】:

您发布的标题部分没有scan DLL 名称中的“COM”一词值得注意。正如标题中的声明一样,这是一个 COM 接口。不,他们不导出这些功能。您只需添加对类型库的引用。通常嵌入在 DLL 本身中,因此使用项目 > 添加引用 > 浏览按钮 > 选择 scard-com.dll。 对不起,添加了错误的界面描述,编辑帖子以显示正确的。 尝试在 VS 中添加 DLL 作为对我的 C# 项目的引用不起作用,我收到错误消息“确保文件可访问,并且它是有效的程序集或 COM 组件。” IUnknown 绝对看起来像 COM 接口。阅读COM Interop: Client Tutorial 【参考方案1】:

P/Invoke 真的不支持 C++。您正在尝试调用接口上的方法,而不是 C 样式的函数。

有一些技巧可以让你得到你想要的结果,但通常最好的选择是编写一个 C++/CLI 互操作库,你可以在你的 C# 项目中使用。

此外,一般来说,头文件不足以正确调用本机库。您需要文档 - 没有人可以解决诸如“字符串缓冲区应该有多大?谁负责分配/释放内存?当缓冲区不够大时会发生什么?什么是可能的”之类的问题错误条件?”如果您没有文档,那么您无能为力,只能享受逆向工程:)

【讨论】:

【参考方案2】:

这样的scan声明:

public class SCardWrapper

    [DllImport("scard-com.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern uint scan();

是 DLL scard-com.dll 中名称为 scan 的函数的绑定。检查你的scard-com.dll DLL 接口中的这​​个函数。为此,您可以使用Dependency Walker。

scan 名称可以是您的 DLL 中的 mangled。在这种情况下,您可以在非托管代码中将其定义为 extern "C",或将 EntryPoint = "mangled_scan_name" 添加到您的 PInvoke 声明中。

顺便说一句,scan 不是上面interface ISCard_CardReader 的一部分。但如果是类的方法,则CallingConvention = CallingConvention.Cdecl是不正确的。

【讨论】:

以上是关于从 C# 调用 C++ dll 中的方法,没有任何信息,只有头文件的主要内容,如果未能解决你的问题,请参考以下文章

从 C# 调用带有 C++ 标头的 dll

从 C# 调用 C++ dll 函数时出现问题

从 C++ 调用 C# dll

从 C# 调用 C++ 代码而不创建 dll?

从 C# 调用 C++ dll 方法/类

C#如何静态调用C++中的方法(静态调用dll)