我可以使用如何正确地将C ++ DLL中的多个函数导入到C#应用程序中的帮助。以下是C ++方面的一些示例,展示了我在C#中尝试做的事情。我认为我没有正确地编组返回类型和一些参数(尤其是指针/ ref / out)。
C ++头文件声明:
unsigned long __stdcall mfcsez_initialisation(unsigned short serial);
unsigned char __stdcall mfcs_get_serial(unsigned long int handle,
unsigned short * serial);
unsigned char __stdcall mfcs_read_chan(unsigned long int handle,
unsigned char canal,
float * pressure,
unsigned short * chrono);
C ++代码:
/* Define functions prototype */
typedef unsigned long(__stdcall *init)(int);
typedef unsigned char(__stdcall *serial)(unsigned long handle, unsigned
short *serial);
typedef unsigned char(__stdcall *readChannel)(unsigned long handle,
unsigned char chan,
float * pressure,
unsigned short * chrono);
int main(int argc, char *argv[])
{
unsigned char pressureChannel = 1;
HINSTANCE hGetProcIDDLL=NULL;
/* Load DLL into memory */
hGetProcIDDLL = LoadLibrary(TEXT("mfcs64_c.dll"));
/* Declare pointers on dll functions */
init dll_init;
serial dll_serial;
readChannel dll_readChannel;
/* Link dll pointers with functions prototype */
dll_init = (init)GetProcAddress(hGetProcIDDLL,
"mfcsez_initialisation");
dll_serial = (serial)GetProcAddress(hGetProcIDDLL,
"mfcs_get_serial");
dll_readChannel = (readChannel)GetProcAddress(hGetProcIDDLL,
"mfcs_read_chan");
/* Define variables used for MFCS device */
unsigned long mfcsHandle;
unsigned short mySerial;
float read_pressure;
unsigned short chrono;
int loop_index;
if (hGetProcIDDLL != NULL)
{
std::cout << "mfcs_c.dll is loaded" << std::endl;
/* Initialize device */
if (dll_init != NULL)
{
/* Initialize the first MFCS in Windows enumeration list */
mfcsHandle = dll_init(0);
}
/* Read device serial number */
dll_serial(mfcsHandle, &mySerial);
for (loop_index = int(start_pressure);
loop_index<target_pressure; loop_index++)
{
Sleep(1000);
dll_readChannel(mfcsHandle, pressureChannel,
&read_pressure, &chrono);
}
}
return EXIT_SUCCESS;
}
我尝试用各种脚印导入它们。我可以调用mfcsez_initialisation,它可以正常工作,如下所示。另外两个我尝试了很多不同的方法,总是得到一个例外 - 从DLL(不可恢复的)或我可以尝试/捕获的不正确的编组。
C#导入语句示例:
[DllImport("mfcs_c_64.dll", CallingConvention =
CallingConvention.StdCall)]
protected static unsafe extern uint mfcsez_initialisation(ushort
serial_number);
[DllImport("mfcs_c_64.dll", CallingConvention =
CallingConvention.StdCall)]
public static unsafe extern byte mfcs_get_serial(uint handle, ref
ushort serial);
[DllImport("mfcs_c_64.dll", CallingConvention =
CallingConvention.StdCall)]
protected static unsafe extern byte mfcs_read_chan(ulong handle, byte
canal, ref float pressure, ref ushort chrono);
C#代码示例:
unit mfcsHandle = mfcsez_initialisation(0); // Returns with valid handle
mfcs_get_serial(mfcsHandle, mySerial); // Memory write exception
float pressure = -1.0f;
ushort chrono = 0;
mfcs_read_chan(mfcsHandle, 1, ref pressure, ref chrono); // Same ex
感谢任何和所有帮助!
答案 0 :(得分:0)
正如您在评论中所述(随后删除),您无法确定问题是否存在于互操作或传递给函数的参数中。你是如何解决这个疑问的?
这样做的方法是创建一个具有相同签名的函数的测试床DLL,然后证明您可以在该DLL和C#p / invoke代码之间正确地移动数据。一旦你能做到这一点,你可以删除interop作为你的问题的潜在来源,并专注于传递给函数的参数。因此,这是制作该测试床DLL所需的内容。
<强> dllmain.cpp 强>
#include <Windows.h>
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
<强> Dll1.cpp 强>
#include <iostream>
extern "C"
{
unsigned long __stdcall mfcsez_initialisation(unsigned short serial)
{
std::cout << "mfcsez_initialisation, " << serial << std::endl;
return 1;
}
unsigned char __stdcall mfcs_get_serial(unsigned long int handle,
unsigned short * serial)
{
std::cout << "mfcs_get_serial, " << handle << std::endl;
*serial = 2;
return 3;
}
unsigned char __stdcall mfcs_read_chan(unsigned long int handle,
unsigned char canal,
float * pressure,
unsigned short * chrono)
{
std::cout << "mfcs_read_chan, " << handle << ", " << static_cast<int>(canal) << std::endl;
*pressure = 4.5f;
*chrono = 5;
return 6;
}
}
<强> Dll1.def 强>
LIBRARY Dll1
EXPORTS
mfcsez_initialisation
mfcs_get_serial
mfcs_read_chan
请注意,我使用的是.def文件,以确保使用未修饰的名称导出函数。
调用它的C#程序如下所示:
<强> Program1.cs 强>
using System;
using System.Runtime.InteropServices;
using System.Text;
namespace ConsoleApp1
{
class Program
{
const string dllname = "Dll1.dll";
[DllImport(dllname, CallingConvention = CallingConvention.StdCall)]
static extern uint mfcsez_initialisation(ushort serial);
[DllImport(dllname, CallingConvention = CallingConvention.StdCall)]
static extern byte mfcs_get_serial(uint handle, out ushort serial);
[DllImport(dllname, CallingConvention = CallingConvention.StdCall)]
static extern byte mfcs_read_chan(uint handle, byte canal, out float pressure, out ushort chrono);
static void Main(string[] args)
{
uint retval1 = mfcsez_initialisation(11);
Console.WriteLine("return value = " + retval1.ToString());
Console.WriteLine();
ushort serial;
byte retval2 = mfcs_get_serial(12, out serial);
Console.WriteLine("serial = " + serial.ToString());
Console.WriteLine("return value = " + retval2.ToString());
Console.WriteLine();
float pressure;
ushort chrono;
byte retval3 = mfcs_read_chan(13, 14, out pressure, out chrono);
Console.WriteLine("pressure = " + pressure.ToString());
Console.WriteLine("chrono = " + chrono.ToString());
Console.WriteLine("return value = " + retval3.ToString());
Console.ReadLine();
}
}
}
输出结果为:
mfcsez_initialisation, 11 return value = 1 mfcs_get_serial, 12 serial = 2 return value = 3 mfcs_read_chan, 13, 14 pressure = 4.5 chrono = 5 return value = 6
如您所见,所有所需的值都在两个模块之间正确传输。这表明这里的p / invoke互操作代码是正确的。
备注强>:
int
和long int
都是32位类型。因此,对于无符号变体,它们会映射到C#int
uint`上的, or
。long
和ulong
是64位类型,因此不匹配C ++ int
或long int`。unsigned char
映射到C#上的byte
。unsafe
,但是你不需要,很少这样做。out
而不是ref
,因为我推断这些参数仅用于流出DLL的数据。如果你对你的DLL使用这个互操作代码并且仍然遇到失败,那么有两个似是而非的解释:
答案 1 :(得分:-1)
这取决于这个DLL除了用C ++编写之外。
如果它是C ++ .NET DLL,您可以像使用任何其他.NET DLL一样使用它。就像你已经使用框架提供的那些一样。
如果它是用.NET的前任COM编写的,则可以使用COM互操作。在制作.NET时考虑了向后兼容性
如果不是那些,则有P / Invoke。
请注意,COM interop和P / Invoke通常涉及处理裸指针。这意味着双重问题并且必须进入非托管代码。我不羡慕你不得不去那种低级语言。