【问题标题】:using C++ DLL in C# windows application:Getting error "Entry point not found"在 C# windows 应用程序中使用 C++ DLL:出现错误“找不到入口点”
【发布时间】:2013-03-08 15:33:09
【问题描述】:

我不熟悉在 C# 中使用 C++ 库以及一般的 C++ 编程。我有一个由 C++ 代码构建的 DLL,我认为它是一个“托管”代码,因为 DLL 的名称是“TestManaged.dll”。我不是 100% 确定 dll/C++ 代码是托管还是非托管。

我想在我的C# windows forms application 代码中使用这个 DLL 的类和方法。此 DLL 中有多个类。当我在Object Browser 中的这些类中检查这些类和方法时,它们都有Public 标识符。

到目前为止,我已将此 DLL 添加到我对 C# 应用程序代码的引用中。我会在我的问题中讨论三个类:ProductReqStatusProductData。我可以为这个 DLL 的各种类创建一个对象,如下所示。

Product testCall = new ProductClass();

在这个 DLL 中有另一个名为 ProductData 的类,我可以得到这个类的 C++ 代码,如下所示。在这种情况下,ProductData 在 C# 中显示为 Object Browser 中的类,因为它实际上是 C++ 代码中的结构。我不确定这对回答我的问题是否重要(最后)。

以下是定义 ProductData 结构 - ProductData.h 文件的 C++ 代码。

#ifdef WIN32_MANAGED 
public ref  struct ProductData
#else
struct ProductData
#endif
{
    UINT32 ProductId;           //!< Product ID    
    UINT32 PRoductRev;         //!< Build Revision
};

以下是定义 ReqStatus 枚举 - ReqStatus.h 文件的 C++ 代码。我在我的 C# 代码中创建了相同的枚举,但没有指定标识符。

enum ReqStatus
{
    SUCCESS,            //!< Method was successful

    //Connection errors
    NOT_CONNECTED,      //!< Connection not open 
    CONN_TIMEOUT,       //!< Connection timed out commuincating with device
};

现在,我想调用两种方法,但两者都有问题:

方法 1:是Product 类中的getProductData 方法,它接受ProductData 类型的对象作为参数并返回ReqStatus,它是C++ 中的枚举类型。所以下面是gerProductData方法的声明(见Object Browser):

public ReqStatus getProductData(ProductData data)

同一方法的 C++ 声明是:(实际方法太长,因此只给出声明):此方法在 Prodcut.cpp 文件中

ReqStatus Product::getProductData(ProductData PLATFORM_PTR data)

PLATFORM_PTR 在Platform.h中定义如下

#ifdef WIN32_MANAGED
#define PLATFORM_PTR ^
#else
#define PLATFORM_PTR *
#endif

方法 2:是 Product 类中的 getConnected 方法,它接受一个字符数组(我不确定)和一个 ProductData 类型的对象作为参数并返回 ReqStatus 这是一个C++ 中的枚举类型。所以下面是getConnected方法的声明(见Object Browser):

public ReqStatus getConnected(sbyte* someChar, ProductData data)

相同方法的 C++ 声明是:(实际方法太长,因此只给出声明):此方法在 Prodcut.cpp 文件中

ReqStatus Product::getConnected(const char *someChar, ProductData PLATFORM_PTR data)

C++代码调用方法如下:

private : Product^  _testProduct;
testProduct = gcnew Product();
ProductData ^ data = gcnew ProductData();

int portNum = Decimal::ToInt16(7); 
char portName[32];
_snprintf(&portName[0], sizeof(portName),"COM%d", portNum);
ReqStatus status = _testProduct->getConnected(&portName[0], data); //Calling getConnected

getConnected 方法内部有一个对getProductData 方法的内部调用。

ReqStatus status = getProductData(data); //data is the same which was passed to the getConnected method

我的 C# 代码如下,我在两个方法调用中都遇到了错误:我在下面的代码 sn-p 中将错误放在了同一行。两种方法都是独立的。只是 getProductData 是从 C++ 代码中的 getConnected 方法调用的。我想检查我是否可以单独调用两者。

ProductData pData = new ProductData(); // OK
Product _testProduct = new Product();  // OK

ReqStatus status1 = _testProduct.getConnected("COM5", pData ); //Error 1: The best overloaded method getConnected has some invalid arguments
ReqStatus status2 = (ReqStatus)_testProduct.getProductData(pData ); // Error 2: Method is inaccessible due to its protection level

对于错误 1,我尝试了 StackOverflow 和其他论坛上各种文章的解决方案,但无法解决。仅供参考,我尝试如下更改“SomePortCOM”,但它不起作用。

更新此代码现在可以正常工作,我没有看到错误 1(无效参数)。现在,我只需要摆脱错误 2(保护级别错误)。请提供任何建议。谢谢。

String str = "COM5";
byte[] bytes = Encoding.ASCII.GetBytes(str);
unsafe
    {
        fixed (byte* p = bytes)
        {
                sbyte* sp = (sbyte*)p;
                //SP is now what you want
                   ReqStatus status1 = _testProduct.getConnected(sp, pData );
        }
    }

对于 Error2,我搜索了很多博客,发现可能的解决方案之一是使用 DLLImport,我也尝试过,但遇到以下问题:

DLLImport 的 C# 声明:

[DllImport("TestManaged.dll",EntryPoint="getConnected")]
        public static extern ReqStatus getConnected(String SerialPort, ref ProductData pData);

我从我的 C# 代码中调用这个函数如下:

ProductData pData = new ProductData();
String str = "COM7";
ReqStatus status1 = getConnected(str, ref pData);

但是,我收到 Entry point not found 错误。我尝试运行 dumpbin 函数以获取此 DLL 导出的函数列表。但是,我没有看到任何功能。而只是一个随机输出,如下所示。

Microsoft (R) COFF/PE Dumper Version 10.00.40219.01
Copyright (C) Microsoft Corporation.  All rights reserved.


Dump of file C:\Rumit\TestManaged.dll

File Type: DLL

  Summary

        2000 .data
       22000 .rdata
        1000 .reloc
        1000 .rsrc
       13000 .text

更新: 此外,我没有通过 Dependency Walker 在此 DLL 中看到任何方法。 现在,我得到了 C++ 的源代码。但我对 C++ 编码相当陌生。如果需要对 C++ 代码进行任何更改,请提供指导。

问候, 反刍

【问题讨论】:

    标签: c# c++ dllimport dependency-walker


    【解决方案1】:
     enum ReqStatus
    

    这是你最大的问题。这声明了一个本机枚举类型,它在托管代码中不可用,并且使任何使用它的代码都无法访问。您必须使用 enum class 关键字声明它的托管版本,如下所示:

    public enum class ReqStatus {
        // etc...
    }
    

    【讨论】:

      【解决方案2】:

      您的代码周围的不安全块会导致您的程序集无法通过安全协议进行验证,因此请注意这一点。当我从 C# 调用来自 C++(本机或非本机)的方法时,我不得不使用 PInvoke(平台调用)来调用它们。对于保护级别,我知道您说过 C++ 中的所有内容都是公开的,但是如果您是 C++ 新手,您可能会犯一个快速的语法错误。在 C# 中,所有方法都需要以存储说明符(公共、受保护等)开头,但在 C++ 中,您放置一个存储说明符后跟一个冒号,该存储和下一个声明的存储之间的所有内容都属于该存储类型。也许这是你的问题?

      【讨论】:

      • 我也尝试使用 DLLImport,但它给了我一个“找不到入口点错误”。当我尝试通过“dumpbin”和“dependency walker”检查 DLL 的导出函数时,我没有看不到任何函数。这是否意味着 DLL 的任何函数都不能在 C# 中使用,因为它们从未以这种方式使用?如果可能,您能否提供通过 PInvoke 调用的示例代码?另外,我没有可以更改和重新生成 DLL 的 C++ 源代码。它是由第三方团队提供的。
      【解决方案3】:

      感谢汉斯指出问题。就是这样,我将枚举定义为“公共”。但是,我不确定你是错误地放置了“类”还是故意的,因为它给了我很多错误,因为它没有把它当作一个枚举,而是在我使用枚举的每个地方都要求一个对象.如果我在这里误解了任何内容,请告诉我。

      所以,我只是通过公开枚举来实现它。但是,我仍然无法找到如何将正确的值从 C# 传递给 C++ 函数。 (我原来的帖子中的Error1)。

      当我调试 C++ 代码时,它为getConnected 方法的第一个参数传递了“0x0034E808”COM5”值(我假设它是一个内存位置和一个值?)。我尝试通过实现来传递这个值不安全的方法(在我的原始帖子中解释了错误 1),它通过了“0x0277aab8”(再次似乎是一些内存地址),但无法连接到它(获取串行端口超时错误)。与相比,我是否错误地传递了值C++方法?

      问候, 反刍

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-07-06
        • 2022-01-19
        • 2013-09-09
        • 2011-06-07
        • 1970-01-01
        • 2011-02-28
        • 2015-07-19
        • 1970-01-01
        相关资源
        最近更新 更多