Использование C++ DLL в приложении C# windows: Получение ошибки "Точка входа не найдена"

Я новичок в использовании библиотек C++ в C#, а также в программировании на C++ в целом. У меня есть библиотека DLL, созданная из кода C++, который, по моему мнению, является "управляемым" кодом, поскольку имя библиотеки DLL - "TestManaged.dll". Я не уверен на 100%, является ли код dll/C++ управляемым / неуправляемым.

Я хочу использовать классы и методы этой DLL в моем C# windows forms application код. В этой DLL есть несколько классов. Когда я проверял эти классы и методы внутри этих классов в Object Browser все они имеют Public идентификатор.

До сих пор я добавил эту DLL к своим ссылкам на код приложения C#. В моем вопросе я расскажу о трех классах: Product, ReqStatus, ProductData, Я мог бы создать объект (ы) для различных классов этой DLL следующим образом.

Product testCall = new ProductClass();

Есть еще один класс под названием ProductData в этой DLL, и я мог бы получить код C++ для этого класса, который заключается в следующем. В этом случае, ProductData отображается как класс в Object Browser в C#, где это на самом деле структура в коде C++. Я не уверен, если это важно, чтобы ответить на мой вопрос (в конце).

Ниже приведен код C++, который определяет ProductData структура - ProductData.h файл.

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

Ниже приведен код C++, который определяет ReqStatus enum - ReqStatus.h файл. Я создал такое же перечисление в моем коде 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 введите в качестве параметра и возвращает ReqStatus который является типом перечисления в C++. Итак, следующее заявление 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# выглядит следующим образом, и я получил ошибки при обоих вызовах методов: я поместил ошибки в одну и ту же строку в приведенном ниже фрагменте кода. Оба метода независимы. Это просто, что getProductData вызывается из getConnected метод в коде C++. Я хотел проверить, могу ли я позвонить как по отдельности.

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 я пытался найти решения из различных статей на Stackru и других форумах, но не смог ее решить. Просто для справки, я попытался изменить "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, я тоже попробовал это, и у меня возникла следующая проблема:

C# объявление DLLImport:

[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

ОБНОВЛЕНИЕ: Кроме того, я не вижу никаких методов в этой DLL через Зависимость Уокер. Теперь у меня есть исходный код для C++. Но я довольно плохо знаком с C++. В случае, если требуется внести какие-либо изменения в код C++, просим дать указания.

С уважением, Румит

3 ответа

Решение
 enum ReqStatus

Это ваше самое большое зависание. Он объявляет собственный тип перечисления, он не может использоваться в управляемом коде и делает любой код, который использует его, недоступным. Вы должны объявить управляемую версию с ключевым словом enum class, например так:

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

Небезопасный блок вокруг вашего кода сделает так, что ваша сборка не может быть проверена на наличие протоколов безопасности, так что будьте осторожны с этим. Когда я вызывал методы из C++ (нативные или нет) из C#, мне приходилось использовать PInvoke (Platform Invoke) для их вызова. Что касается уровня защиты, я знаю, что вы сказали, что в C++ все открыто, но если вы новичок в C++, возможно, вы допустили быструю синтаксическую ошибку. В C# всем методам должен предшествовать спецификатор хранилища (открытый, защищенный и т. Д.), Но в C++ вы помещаете спецификатор хранилища, за которым следует двоеточие, и все, что находится между хранилищем TH и следующим объявленным хранилищем, будет из этого хранилища. тип. Может быть, это ваша проблема?

Спасибо, Ганс, за указание на проблему. Просто я сделал перечисление определением "public". Но я не уверен, что вы указали "класс" по ошибке или он был преднамеренным, так как он давал мне столько ошибок, что он не воспринимался как перечисление и запрашивал объект в каждом месте, где я использовал перечисление, Дайте мне знать, если я что-то здесь неправильно понял.

Итак, я заработал, просто опубликовав enum. Тем не менее, я до сих пор не могу найти, как передать правильные значения в эту функцию C++ из C#. (Ошибка1 в моем оригинальном посте).

Когда я отлаживаю код C++, он передает значение "0x0034E808 "COM5"(я полагаю, это место в памяти и значение?) Для первого параметра getConnected метод. Я попытался передать это значение, реализовав небезопасный метод (описанный в моем исходном сообщении об ошибке 1), он передает "0x0277aab8" (опять-таки, кажется, некоторый адрес памяти), но не смог подключиться к нему (получение ошибок тайм-аута последовательного порта). Я неправильно передаю значение по сравнению с методом C++?

С уважением, Румит

Другие вопросы по тегам