在C#windows应用程序中使用C ++ DLL:获取错误“找不到入口点”

时间:2013-03-19 19:06:01

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

我是C#中使用C ++库的新手,也是C ++编程的新手。我有一个用C ++代码构建的DLL,我认为它是一个“托管”代码,因为DLL的名称是“TestManaged.dll”。如果dll / C ++代码是托管/非托管的,我不能100%确定。

我想在我的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 struct - 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:getProductData类中的Product方法,它接受ProductData类型的对象作为参数,并返回{C}中的枚举类型ReqStatus。以下是gerProductData方法的声明(如Object Browser中所示):

public ReqStatus getProductData(ProductData data)

同样方法的C ++ delcaration是:(实际方法太长,因此只是给出声明):此方法在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:是getConnected类中的Product方法,它接受一个字符数组(我不确定)和一个ProductData类型的对象作为参数并返回ReqStatus这是C ++中的枚举类型。以下是getConnected方法的声明(如Object Browser中所示):

public ReqStatus getConnected(sbyte* someChar, ProductData data)

同样方法的C ++ delcaration是:(实际方法太长,因此只是给出声明):此方法在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

getProductData方法内部调用getConnected方法。

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

我的C#代码如下,我在两个方法调用中都出错:我在下面的代码片段中将错误放在同一行。两种方法都是独立的。只是在C ++代码中从getProductData方法调用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 ++代码需要进行任何更改,请提供指示。

此致 Rumit

3 个答案:

答案 0 :(得分:1)

 enum ReqStatus

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

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

答案 1 :(得分:0)

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

答案 2 :(得分:0)

谢谢汉斯,指出问题所在。就是这样,我将枚举定义定义为“公开”。但是,我不确定你是否错误地设置了“课程”,或者它是故意的,因为它给了我很多错误,因为它没有把它作为枚举,而是在我使用枚举的每个地方都要求一个对象。如果我在这里误解了什么,请告诉我。

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

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

此致 Rumit