Можно ли вызвать функцию C из С#.Net
У меня есть C lib и хочу вызвать функцию в этой библиотеке из приложения С#. Я попытался создать оболочку С++/CLI в C lib, добавив файл C lib в качестве вставки компоновщика и добавив исходные файлы в качестве дополнительных зависимостей.
Есть ли лучший способ достичь этого, так как я не уверен, как добавить вывод C в приложение С#.
Мой код C -
__declspec(dllexport) unsigned long ConnectSession(unsigned long handle,
unsigned char * publicKey,
unsigned char publicKeyLen);
My CPP Wrapper -
long MyClass::ConnectSessionWrapper(unsigned long handle,
unsigned char * publicKey,
unsigned char publicKeyLen)
{
return ConnectSession(handle, publicKey, publicKeyLen);
}
Ответы
Ответ 1
Пример для 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
файл в свой <<2 > Что-то вроде
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
затем, скомпилируйте, и в вашем файле С# сделайте
[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);
}
}
Ответ 2
ОБНОВЛЕНИЕ - 22 февраля 2019 г.: Поскольку этот ответ получил довольно много откликов, я решил обновить его, чтобы улучшить способ вызова метода C. Ранее я предлагал использовать код unsafe
, но безопасный и правильный способ - использовать атрибут MarshalAs
для преобразования .NET string
в char*
. Кроме того, в VS2017 больше нет проекта Win32, вам, вероятно, придется создать Visual C++ dll или пустой проект и изменить его. Спасибо!
Вы можете напрямую вызывать функции C из С#, используя P/Invoke.
Вот краткое руководство по созданию библиотеки С#, которая охватывает C dll.
- Создайте новый проект библиотеки С# (я назову его "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)"
- Теперь в ветке компоновщика перейдите в раздел "Общие" и измените файл вывода на "$ (SolutionDir) Wrapper\$ (ProjectName).dll", это скопирует встроенную библиотеку C DLL в корневой каталог проекта С#.
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 в каталоге проекта С#
- Щелкните правой кнопкой мыши по проекту С# Wrapper, добавьте существующий элемент, добавьте CLibrary.dll
- Нажмите CLibrary.dll, перейдите на панель свойств, установите "Копировать в выходной каталог" на "Копировать всегда"
Хорошей идеей будет сделать проект Wrapper зависимым от CLibrary, чтобы CLibrary создавался первым, вы можете сделать это, щелкнув правой кнопкой мыши проект Wrapper, перейдя в "Зависимости проекта" и выбрав "CLibrary".
Теперь для фактического кода оболочки:
ConnectSessionWrapper.cs
using System.Runtime.InteropServices;
namespace Wrapper
{
public class ConnectSessionWrapper
{
[DllImport("CLibrary.dll", CallingConvention = CallingConvention.Cdecl)]
static extern uint ConnectSession(uint handle,
[MarshalAs(UnmanagedType.LPStr)] string publicKey,
char publicKeyLen);
public uint GetConnectSession(uint handle,
string publicKey,
char publicKeyLen)
{
return ConnectSession(handle, publicKey, publicKeyLen);
}
}
}
Теперь просто позвоните GetConnectSession
, и он должен вернуть 42
.
Результат:
![Testing wrapper library in a console application]()
Ответ 3
Хорошо, Open VS 2010, Goto File → New → Project → Visual С++ → Win32 → Win32 Project и дайте ему имя (HelloWorldDll в моем случае), Затем в окне, которое следует по типу приложения выберите "DLL", а в разделе "Дополнительные параметры" выберите "Пустой проект".
Теперь перейдите на вкладку "Обозреватель решений", как правило, правой стороной окна VS, щелкните правой кнопкой мыши "Исходные файлы" → "Добавить элемент" → файл С++ (.cpp) и дайте ему имя (HelloWorld в моем случае)
Затем в новом классе вставьте этот код:
#include <stdio.h>
extern "C"
{
__declspec(dllexport) void DisplayHelloFromDLL()
{
printf ("Hello from DLL !\n");
}
}
Теперь создайте проект, после перехода к папке DEBUG, и там вы должны найти: HelloWorldDll.dll.
Теперь давайте создадим наше приложение С#, которое будет обращаться к DLL, Goto File → New → Project → Visual С# → Консольное приложение и присвоить ему имя (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) в том же каталоге и выполнить вывод приложения должен быть
Это программа С#
Привет из DLL!
Надеюсь, что некоторые из ваших проблем будут устранены.
Ссылки
Ответ 4
ПРИМЕЧАНИЕ. НИЖЕ КОД ДЛЯ НЕСКОЛЬКИХ МЕТОДОВ ИЗ 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);
}