Что не так с моей DLLImport (s) в моем проекте C # для моей пользовательской C ++ DLL? - PullRequest
1 голос
/ 03 июня 2019

В настоящее время я изучаю C ++ (для того, чтобы в конечном итоге создать DLL для Hooks) У меня есть сильные стороны в C #, и я хотел бы получить доступ к DLL через pInvoke / DllImports.

Я использую VS 2019 (и .NET Framework 4.7.2). В качестве примера для изучения я скопировал DLL, а также C ++ (консольное приложение), как описано здесь: https://docs.microsoft.com/en-us/cpp/build/walkthrough-creating-and-using-a-dynamic-link-library-cpp?view=vs-2019. Это работает отлично (как и ожидалось).

Теперь, когда я начинаю пытаться импортировать DLL в C # Project, у меня возникают проблемы (которые, я считаю, связаны с DllImport)

В примере (c ++) консольного приложения (которое работает):

int main()
{
    // Initialize a Fibonacci relation sequence.
    fibonacci_init(1, 1);
    // Write out the sequence values until overflow.
    do {
        std::cout << fibonacci_index() << ": " << fibonacci_current() << std::endl;
    } while (fibonacci_next());
    // Report count of values written before overflow.
    std::cout << fibonacci_index() + 1 << " Fibonacci sequence values fit in an " << "unsigned 64-bit integer." << std::endl;
}

В приложении консоли тестирования C # (не работает должным образом):

    class Program
    {

        [DllImport("MathLibrary.dll", CallingConvention = CallingConvention.Cdecl)]
        internal static extern void fibonacci_init(ulong a, ulong b);

        [DllImport("MathLibrary.dll", CallingConvention = CallingConvention.Cdecl))]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool fibonacci_next();

        [DllImport("MathLibrary.dll", CallingConvention = CallingConvention.Cdecl)]
        internal static extern ulong fibonacci_current();

        [DllImport("MathLibrary.dll", CallingConvention = CallingConvention.Cdecl)]
        internal static extern uint fibonacci_index();

        static void Main(string[] args)
        {

            // Initialize a Fibonacci relation sequence.
            fibonacci_init(1, 1);
            // Write out the sequence values until overflow.
            do {
                Console.WriteLine($"{fibonacci_index()}: {fibonacci_current()}");
            } while (fibonacci_next());
            // Report count of values written before overflow.
            Console.WriteLine($"{fibonacci_index() + 1} Fibonacci sequence values fit in an unsigned 64-bit integer.");
        }
    }

В рабочем (C ++) приложении цикл достигает 92 и печатает 92: 12200160415121876738 и затем выходит из цикла и печатает 93 Fibonacci sequence values fit in an unsigned 64-bit integer.

Однако в отказавшем приложении (C #) оно работает до 92: 12200160415121876738, но вместо выхода из цикла fabonacci_next() продолжает возвращать true и продолжает цикл 92: 12200160415121876738 (не более увеличивается и никогда не выходит).

Для DllImport в C # я пробовал оба:

[DllImport("MathLibrary.dll", CallingConvention = CallingConvention.Cdecl))]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool fibonacci_next();

и:

[DllImport("MathLibrary.dll", CallingConvention = CallingConvention.Cdecl))]
public static extern bool fibonacci_next();

Те же результаты в любом случае. Если кто-то может объяснить мне, что я делаю неправильно с DllImport (в результате чего он никогда не возвращает true, когда одна и та же DLL работает правильно при подключении к консольному приложению C ++).

--- ОБНОВЛЕНИЕ ---

Таким образом, решение для приведенного выше примера, изменяющее [return: MarshalAs(UnmanagedType.Bool)] на [return: MarshalAs(UnmanagedType.I1)], работает (с дальнейшей важной связанной информацией, предоставленной в ссылках, указанных GSerg в комментариях ниже).

--- ОБНОВЛЕНИЕ 2 ---

(@ David) Я намеренно (и неправильно) пропустил экспорты, как они были в ссылке, которую я предоставил на исходный DLL-файл "Пошаговое руководство" (мои извинения). Для полноты вопроса экспорт:

#ifdef MATHLIBRARY_EXPORTS
#define MATHLIBRARY_API __declspec(dllexport)
#else
#define MATHLIBRARY_API __declspec(dllimport)
#endif

extern "C" MATHLIBRARY_API void fibonacci_init(unsigned long long a, unsigned long long b);
extern "C" MATHLIBRARY_API bool fibonacci_next();
extern "C" MATHLIBRARY_API unsigned long long fibonacci_current();
extern "C" MATHLIBRARY_API unsigned fibonacci_index();

1 Ответ

0 голосов
/ 04 июня 2019

tldr;

Решением было просто изменить DllImport для импорта fibonacci_next () на:

[DllImport("MathLibrary.dll", CallingConvention = CallingConvention.Cdecl))]
[return: MarshalAs(UnmanagedType.I1)]
public static extern bool fibonacci_next();

Немного дополнительно:

Технически это было все, что было необходимо для ответа на этот вопрос. Для тех, кто нашел этот вопрос в аналогичных терминах, я добавляю немного больше информации ниже, поскольку реальная точка зрения вопроса была больше о понимании и изучении, а не о копировании и вставке кода других людей и попытке заставить его работать.

Начиная с комментария GSerg:

C ++ bool - это не C # bool. UnmanagedType.Bool - это COOL BOOL, а не bool.

Я использовал этот список при преобразовании типов данных:

Таблица ниже взята из Преобразование типов данных C ++ в C # (я уверен, что есть более официальные схемы преобразования)

C++ Type                       C# Type               Size
--------                       -------               ----
BOOL                           bool                  1 byte
BYTE                           byte                  1 byte
CHAR                           byte                  1 byte
DECIMAL                        Decimal               16 bytes
DOUBLE                         double                8 bytes
DWORD                          uint, UInt32          4 bytes
FLOAT                          float, single         4 bytes
INT, signed int                int, Int32            4 bytes
INT16, signed short int        short, Int16          2 bytes
INT32, signed int              int, Int32            4 bytes
INT64                          long, Int64           8 bytes
LONG                           int, Int32            4 bytes
LONG32, signed int             int, Int32            4 bytes
LONG64                         long, Int64           8 bytes
LONGLONG                       long, Int64           8 bytes
SHORT, signed short int        short, Int16          2 bytes
UCHAR, unsigned char           byte                  1 byte
UINT, unsigned int             uint, UInt32          4 bytes
UINT16, WORD                   ushort, UInt16        2 bytes
UINT32, unsigned int           uint, UInt32          4 bytes
UINT64                         ulong, UInt64         8 bytes
ULONG, unsigned long           uint, UInt32          4 bytes
ULONG32                        uint, UInt32          4 bytes
ULONG64                        ulong, UInt64         8 bytes
ULONGLONG                      ulong, UInt64         8 bytes
WORD                           ushort                2 bytes
void*, pointers                IntPtr                x86=4 bytes, x64=8 bytes

Поскольку я только начинаю немного изучать C ++, я сделал предположение, что (c ++) BOOL в приведенном выше списке эквивалентен (c ++) bool, и когда не было [return: MarshalAs(UnmanagedType.Bool)] с ошибкой, я попытался добавить это , что также не удалось.

Краткое примечание для получения дополнительной информации о UnmanagedTypes см. Microsoft: UnmanagedType Enum :

UnmanagedType.I1 : 1-байтовое целое число со знаком. Вы можете использовать этот элемент для преобразования логического значения в 1-байтовый тип bool в стиле C (true = 1, false = 0).

Еще один бит, который мне понравился, был в принятом решении https://stackoverflow.com/a/32115697/716296, которое не совсем относится к этому вопросу, так как я на самом деле не пытался изменить сторону вещей в c ++, а использовал его как базовый фрагмент кода, чтобы узнать, как вещи сочетаются друг с другом.

Также возможно использовать аргумент ref вместо возвращаемого значения. На самом деле это общая схема в неуправляемом взаимодействии:

Я видел, как этот "общий шаблон" довольно часто используется при работе со сторонними инструментами, которые оборачивают неуправляемые библиотеки. Конечно, если я пишу свои собственные неуправляемые библиотеки dll / библиотеки, я напишу их так, чтобы они наилучшим образом соответствовали их назначению и использованию, в отличие от исходного вопроса, который был всего лишь инструментом для начала изучения.

Я надеюсь, что этот ответ поможет нескольким людям (я ужасен в терминологии), но вопрос был довольно базовым, и я не только получил ответ, но и нашел несколько хороших ссылок для расширения сейчас.

...