Можно ли вызвать функцию 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.
- Создайте новый проект библиотеки C# (я назову его "Wrapper")
Добавьте проект Win32 к решению, установите тип приложения: DLL (я назову его "CLibrary")
- Вы можете удалить все остальные файлы cpp/h, так как они нам не понадобятся
- Переименуйте файл CLibrary.cpp в CLibrary.c
- Добавьте заголовочный файл CLibrary.h
Теперь нам нужно настроить проект 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 с неправильным / слишком большим / малым количеством аргументов, компилятор сообщит вам.
Я не думаю, что вы можете сказать в целом, какой метод лучше, честно говоря, мне не нравится ни один из них.