Можно ли вызвать функцию C из C#.Net

Я имею C lib и хочу вызвать функцию в этой библиотеке из приложения C#. Я попытался создать оболочку C++/CLI для C lib, добавив файл C lib в качестве входных данных компоновщика и добавив исходные файлы в качестве дополнительных зависимостей.

Есть ли лучший способ добиться этого, так как я не уверен, как добавить вывод C в приложение C#.

Мой код C -

__declspec(dllexport) unsigned long ConnectSession(unsigned long handle,
                            unsigned char * publicKey,
                            unsigned char   publicKeyLen);

Мой CPP Wrapper -

long MyClass::ConnectSessionWrapper(unsigned long handle,
                                unsigned char * publicKey,
                                unsigned char   publicKeyLen)
    {
        return ConnectSession(handle, publicKey, publicKeyLen);
    }

3 ответа

Решение

Пример будет для Linux:

1) Создать C файл, libtest.c с этим содержанием:

#include <stdio.h>

void print(const char *message)
{
  printf("%s\\n", message);
}

Это простая псевдообертка для printf. Но представляет собой любой C функция в библиотеке, которую вы хотите вызвать. Если у тебя есть C++ функция не забудьте поставить extern C чтобы избежать искажения имени.

2) создать C# файл

using System;

using System.Runtime.InteropServices;

public class Tester
{
        [DllImport("libtest.so", EntryPoint="print")]

        static extern void print(string message);

        public static void Main(string[] args)
        {

                print("Hello World C# => C++");
        }
}

3) Если у вас нет библиотеки libtest.so по стандартному пути, например, "/usr/lib", вы, скорее всего, увидите исключение System.DllNotFoundException, чтобы это исправить, вы можете переместить ваш libtest.so в /usr/lib или еще лучше, просто добавьте свой CWD в путь к библиотеке: export LD_LIBRARY_PATH=pwd

кредиты отсюда

РЕДАКТИРОВАТЬ

Для Windows это не сильно отличается. Принимая пример отсюда, у вас есть только в вашем *.cpp подать ваш метод с extern "C"Что-то вроде

extern "C"
{
//Note: must use __declspec(dllexport) to make (export) methods as 'public'
      __declspec(dllexport) void DoSomethingInC(unsigned short int ExampleParam, unsigned char AnotherExampleParam)
      {
            printf("You called method DoSomethingInC(), You passed in %d and %c\n\r", ExampleParam, AnotherExampleParam);
      }
}//End 'extern "C"' to prevent name mangling

затем скомпилируйте, и в вашем файле C# сделайте

[DllImport("C_DLL_with_Csharp.dll", EntryPoint="DoSomethingInC")]

public static extern void DoSomethingInC(ushort ExampleParam, char AnotherExampleParam);

а затем просто используйте его:

using System;

    using System.Runtime.InteropServices;

    public class Tester
    {
            [DllImport("C_DLL_with_Csharp.dll", EntryPoint="DoSomethingInC")]

    public static extern void DoSomethingInC(ushort ExampleParam, char AnotherExampleParam);

            public static void Main(string[] args)
            {
                    ushort var1 = 2;
                    char var2 = '';  
                    DoSomethingInC(var1, var2);
            }
    }

Вы можете напрямую вызывать функции C из C#, используя P/Invoke.
Вот краткое руководство по созданию библиотеки C#, которая охватывает C dll.

  1. Создайте новый проект библиотеки C# (я назову его "Wrapper")
  2. Добавьте проект Win32 к решению, установите тип приложения: DLL (я назову его "CLibrary")

    • Вы можете удалить все остальные файлы cpp/h, так как они нам не понадобятся
    • Переименуйте файл CLibrary.cpp в CLibrary.c
    • Добавьте заголовочный файл CLibrary.h
  3. Теперь нам нужно настроить проект CLibrary, щелкнуть его правой кнопкой мыши, перейти к свойствам и выбрать "Конфигурация": "Все конфигурации".

    • В свойствах конфигурации> C/C++ > предварительно скомпилированные заголовки задайте для предварительно скомпилированных заголовков: "Не использовать предварительно скомпилированные заголовки"
    • В той же ветке C / C++ перейдите в Advanced, измените Compile As на: "Compile as C code (/TC)"
    • Теперь в ветке Linker перейдите в General и измените Output File на: "$(SolutionDir)Wrapper\$(ProjectName).dll", это скопирует встроенную C DLL в корень проекта C#.

CLibrary.h

__declspec(dllexport) unsigned long ConnectSession(unsigned long   handle,
                                                   unsigned char * publicKey,
                                                   unsigned char   publicKeyLen);

CLibrary.c

#include "CLibrary.h"

unsigned long ConnectSession(unsigned long   handle,
                                      unsigned char * publicKey,
                                      unsigned char   publicKeyLen)
{
    return 42;
}
  • Щелкните правой кнопкой мыши проект CLibrary, соберите его, чтобы мы получили DLL в каталоге проекта C#
  • Щелкните правой кнопкой мыши по проекту C# Wrapper, добавьте существующий элемент, добавьте CLibrary.dll.
  • Нажмите на CLibrary.dll, перейдите на панель свойств, установите "Копировать в выходной каталог" в "Копировать всегда"

Хорошей идеей будет сделать проект Wrapper зависимым от CLibrary, чтобы сначала создавалась CLibrary. Это можно сделать, щелкнув правой кнопкой мыши проект Wrapper, перейдя в "Зависимости проекта" и выбрав "CLibrary". Теперь для фактического кода оболочки:

ConnectSessionWrapper.cs

using System;
using System.Runtime.InteropServices;

namespace Wrapper
{
    public class ConnectSessionWrapper
    {
        [DllImport("CLibrary.dll", CallingConvention = CallingConvention.Cdecl)]
        unsafe static extern UInt32 ConnectSession(UInt32 handle,
                                                   char*  publicKey,
                                                   char   publicKeyLen);

        public unsafe UInt32 GetConnectSession(UInt32 handle, 
                                               string publicKey,
                                               char publicKeyLen)
        {
            //"Convert" string to char*
            char* pubKey;
            fixed (char* bptr = publicKey)
            {
                pubKey = (char*)bptr;
            }

            return ConnectSession(handle, pubKey, publicKeyLen);
        }
    }
}
  • Последнее, что вам нужно сделать, - это щелкнуть правой кнопкой мыши по проекту Wrapper, установить "Конфигурация" на "Все конфигурации" и установить флажок " Разрешить небезопасный код".
    Единственная причина использования небезопасного кода в том, что в качестве параметра у вас был символ char *, и нам нужно было преобразовать строку в фиксированный указатель на символ.

Результат:
Тестирование библиотеки-оболочки в консольном приложении

Хорошо, откройте VS 2010, перейдите в Файл -> Создать -> Проект -> Visual C++ -> Win32 -> Проект Win32 и дайте ему имя (в моем случае HelloWorldDll), затем в окне, которое следует в разделе Тип приложения, выберите "DLL" и в разделе " Дополнительные параметры" выберите "Пустой проект".

Теперь перейдите на вкладку Solution Explorer, обычно в правой части окна VS, щелкните правой кнопкой мыши Исходные файлы -> Добавить элемент -> C++ файл (.cpp) и дайте ему имя (HelloWorld в моем случае)

Затем в новом классе вставьте этот код:

#include <stdio.h>

extern "C"
{
  __declspec(dllexport) void DisplayHelloFromDLL()
  {
    printf ("Hello from DLL !\n");
  }
}

Создайте проект, перейдите в папку DEBUG вашего проекта и там вы найдете: HelloWorldDll.dll.

Теперь давайте создадим наше приложение на C#, которое будет обращаться к dll, Перейти к файлу -> Создать -> Проект -> Visual C# -> Консольное приложение и присвоить ему имя (CallDllCSharp), а теперь скопировать и вставить этот код в свой основной файл:

using System;
using System.Runtime.InteropServices;
...
        static void Main(string[] args)
        {
            Console.WriteLine("This is C# program");
            DisplayHelloFromDLL();
            Console.ReadKey();
        }

и соберите программу, теперь, когда у нас есть оба приложения, давайте их использовать, поместим ваши *.dll и .exe (bin / debug /.exe) в один каталог и выполните вывод приложения.

Это программа на C#

Привет из DLL!

Надеюсь, это прояснит некоторые ваши проблемы.

Рекомендации:

ПРИМЕЧАНИЕ: НИЖЕ КОД ДЛЯ НЕСКОЛЬКИХ МЕТОДОВ ИЗ DLL.

[DllImport("MyLibc.so")] public static extern bool MyLib_GetName();
[DllImport("MyLibc.so")] public static extern bool MyLib_SetName(string name);
[DllImport("MyLibc.so")] public static extern bool MyLib_DisplayName(string name);

public static void Main(string[] args)
{
string name = MyLib_GetName();
MyLib_SetName(name);
MyLib_DisplayName(name);
}

Метод P/Invoke был подробно и неоднократно описан до сих пор. Что мне здесь не хватает, так это того, что метод C++/CLI имеет большое преимущество: безопасность вызовов. В отличие от P/Invoke, где вызов функции C подобен выстрелу вслепую в небо (если это сравнение разрешено), никто не будет проверять аргументы функции при вызове функции C. Использование C++/CLI в этом случае обычно означает, что вы включаете файл заголовка с прототипами функций, которые хотите использовать. Если вы вызываете функцию C с неправильным / слишком большим / малым количеством аргументов, компилятор сообщит вам.

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

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