C#编组来自C++ DLL的无符号字符返回值[重复]

Posted

技术标签:

【中文标题】C#编组来自C++ DLL的无符号字符返回值[重复]【英文标题】:C# Marshaling Unsigned Char Return Value from C++ DLL [duplicate] 【发布时间】:2018-01-14 22:39:37 【问题描述】:

我正在将 C++ DLL 中的函数导入 C# 应用程序。我可以导入某些功能,但不能导入其他功能。 C++ DLL 附带一个 C++ 项目,该项目展示了如何从 C++ 中使用它。我想在 C# 中复制它,但我在编组时遇到了麻烦。这是 C++:

头文件声明:

unsigned long __stdcall mfcs_initialisation(unsigned short serial);
unsigned long __stdcall mfcsez_initialisation(unsigned short serial);   
unsigned char __stdcall mfcs_close(unsigned long handle);
unsigned char __stdcall mfcs_set_purge_on(unsigned long int handle);
unsigned char __stdcall mfcs_set_purge_off(unsigned long int handle);

unsigned char __stdcall mfcs_get_purge(unsigned long int handle,
    unsigned char * c);

unsigned char __stdcall mfcs_get_status(
    unsigned long int handle,
    unsigned char * c);

unsigned char __stdcall mfcs_read_chan(
    unsigned long int handle,
    unsigned char canal,
    float * pressure,
    unsigned short * chrono);

unsigned char __stdcall mfcs_data_chan(
    unsigned long int  handle,
    unsigned char canal,
    unsigned char * unite,
    unsigned short * max,
    unsigned short * zero,
    unsigned short * mesure,
    unsigned short * chrono);

unsigned char __stdcall mfcs_get_serial(unsigned long int handle,
    unsigned short * us);

unsigned char __stdcall mfcs_set_auto(
    unsigned long int handle,
    unsigned char canal,
    float pcons);

unsigned char __stdcall mfcs_set_alpha(
    unsigned long int handle,
    unsigned char canal,
    unsigned char alpha);

unsigned char __stdcall mfcs_set_manual(
    unsigned long int handle,
    unsigned char canal,
    float pcons);

unsigned char __stdcall mfcs_set_zero(
    unsigned long int handle,
    unsigned char canal,
    unsigned short zero);

unsigned char __stdcall mfcs_detect(unsigned short table[256]);
unsigned char __stdcall mfcsez_detect(unsigned short table[256]);

C++ 代码:

/* Define functions prototype */
typedef unsigned long(__stdcall *init)(int);
typedef unsigned char(__stdcall *purgeOn)(unsigned long handle);
typedef unsigned char(__stdcall *purgeOff)(unsigned long handle);
typedef unsigned char(__stdcall *serial)(unsigned long handle, unsigned short *serial);
typedef unsigned char(__stdcall *close)(unsigned long handle);
typedef unsigned char(__stdcall *setAuto)(unsigned long int handle, unsigned 
char canal, float pcons);
typedef unsigned char(__stdcall *readChannel)(unsigned long handle, unsigned 
char chan, float * pressure, unsigned short * chrono);
typedef unsigned char(__stdcall *setAlpha)(unsigned long int handle, unsigned 
char canal, unsigned char alpha);

int main(int argc, char *argv[])

/* System settings variable definition */
float start_pressure = 70;         //Pressure set (mbar) at the beginning
float target_pressure = 100;        //Maximal pressure setpoint (mbar)
unsigned char pressureChannel = 1; //Selected channel (0 for all channels or put the channel number you would like to control)
HINSTANCE hGetProcIDDLL=NULL;          // Define dll handler

/* Load DLL into memory */
hGetProcIDDLL = LoadLibrary(TEXT("mfcs_c.dll"));

/* Declare pointers on dll functions */
init dll_init;
purgeOn dll_purgeOn;
purgeOff dll_purgeOff;
serial dll_serial;
close dll_close;
setAuto dll_setAuto;
readChannel dll_readChannel;
setAlpha dll_setAlpha;

/* Link dll pointers with functions prototype */
dll_init = (init)GetProcAddress(hGetProcIDDLL, "mfcsez_initialisation");
dll_serial = (serial)GetProcAddress(hGetProcIDDLL, "mfcs_get_serial");
dll_purgeOn = (purgeOn)GetProcAddress(hGetProcIDDLL, "mfcs_set_purge_on");
dll_purgeOff = (purgeOff)GetProcAddress(hGetProcIDDLL, "mfcs_set_purge_off");
dll_close = (close)GetProcAddress(hGetProcIDDLL, "mfcs_close");
dll_setAuto = (setAuto)GetProcAddress(hGetProcIDDLL, "mfcs_set_auto");
dll_readChannel = (readChannel)GetProcAddress(hGetProcIDDLL, "mfcs_read_chan");
dll_setAlpha = (setAlpha)GetProcAddress(hGetProcIDDLL, "mfcs_set_alpha");

/* Define variables used for MFCS device */
unsigned long mfcsHandle;
unsigned short mySerial;
float read_pressure;
unsigned short chrono;
int loop_index;


if (hGetProcIDDLL != NULL)         // If dll loaded
    std::cout << "mfcs_c.dll is loaded" << std::endl;

    /* Initialize device */
    if (dll_init != NULL)          // Check if function was properly linked to the dll file
        /* Initialize the first MFCS in Windows enumeration list */
        mfcsHandle = dll_init(0);
        /* After the initialization we need to add a delay of 500ms to make sure that the USB communication is properly established */
        Sleep(500);
        std::cout << "MFCS initialized" << std::endl;
    

    /* Read device serial number */
    if (dll_serial != NULL) 
        /*Get the serial number of the MFCS*/
        dll_serial(mfcsHandle, &mySerial);
        std::cout << "MFCS SN: " << mySerial << std::endl;
    

    /* Set pressure regulation servitude coefficient */
    if (dll_setAlpha != NULL) 
        dll_setAlpha(mfcsHandle ,pressureChannel, 4);       // Default value (just after power on) is 0 
                                                           // Alpha value has to be grather than 0 in order to regulate pressure

    /* Change and read pressure every second until reaching 'target_pressure' value */
    for (loop_index = int(start_pressure); loop_index<target_pressure; loop_index++)
        dll_setAuto(mfcsHandle,pressureChannel,float(loop_index));               // Set required output pressure value         
        Sleep(1000);                                                             // Wait 1 s
        dll_readChannel(mfcsHandle, pressureChannel, &read_pressure, &chrono);   // Get the pressure value on the specified channel
        std::cout << "Set pressure at: " << loop_index << "mbar" << "; Read pressure: " << read_pressure
            << "mbar" << std::endl;                                              // Display pressure setpoint and channel pressure value
    

    /* Close MFCS session */
    if (dll_close != NULL) 
        dll_close(mfcsHandle);
        std::cout << "MFCS closed" << std::endl;
    


/* Release the DLL */
FreeLibrary(hGetProcIDDLL);
std::cout << "mfcs_c.dll unloaded" << std::endl;

/* Exit application */
system("PAUSE");

return EXIT_SUCCESS;

基本上,我已经尝试了许多导入函数的排列方式,以及任何包含无符号字符或指针的排列方式,我无法弄清楚如何映射。

非常感谢大卫促使我变得更加有用。

注意:对我有帮助的一个例子是基于上面 C++ 代码的明确指令,用于拉入 mfcs_get_serial(unsigned long int 句柄, unsigned short * serial) 转换为 C#。

【问题讨论】:

第一个版本,返回值类型为byte的版本应该是正确的。 mySerial是如何初始化的? related 你确定你的unsigned long int 是 64 位的吗?它可能是 32 位的,在这种情况下,您希望使用 uint 在 C# 中编组 dlatikay - 我没有初始化 mySerial。我刚刚宣布它为“ushort mySerial”。看到您的评论后,我尝试初始化为零,但仍然出现同样的错误。 Tim - 我从 ulong 切换到了 unit(见下文),但它仍然会出现同样的错误。 【参考方案1】:

请注意,此答案是针对问题的原始版本编写的,然后才被编辑为不同的问题。与其冒着编辑战争的风险,我将保持原样。

在 Windows 上的 C++ 中,long 是 32 位类型。所以

unsigned long __stdcall mfcs_initialisation(unsigned short serial);

应该是

[DllImport("mfcs_c_64.dll", CallingConvention = CallingConvention.StdCall)]
public static extern uint mfcs_initialisation(ushort serial);

鉴于提供的信息,另一个函数更难翻译。第二个参数可以是单个值,也可以是数组。前者的可能性更大。所以翻译应该是

[DllImport("mfcs_c_64.dll", CallingConvention = CallingConvention.StdCall)]
public static extern byte mfcs_get_serial(uint handle, out ushort us);

【讨论】:

大卫 - 谢谢你的回答!我将 ulong 更改为 uint,并尝试按照您的建议将导入的足迹更改为“out”而不是“ref”,我得到了同样的结果。如果我使用返回类型字节,我会从关闭应用程序的 DLL 中得到一个异常 - try/catch 不会捕获它。如果我使用 byte[] 我会得到一个 try/catch 捕获的异常。似乎它必须是返回类型编组。当我从 DLL 中调用返回类型为 ulong (uint) 的函数时,调用工作正常。任何从 DLL 中返回类型为 unsigned char 的东西都会爆炸。 我只是继续我在问题中可以看到的内容。毫无疑问,您还没有分享一些重要的东西。鉴于信息,答案是准确的。如果您制作了一个最小的测试台,您会看到这一点。当然,我确实必须做出一个假设,我对此进行了解释。你没有证实我的假设是正确的。就目前而言,除非您付出更多努力,否则对于所提出的问题,这将提供尽可能多的帮助。 例如,您声称返回类型为unsigned char。这有点令人惊讶。你能仔细检查一下它不是unsigned char* 大卫 - 如果我没有提供足够的信息,我们深表歉意!我正尽最大能力。这是从函数的头文件复制的声明: 大卫 - 抱歉!我正尽最大能力。这是从头文件复制的声明: unsigned char __stdcall mfcs_get_serial( unsigned long int handle, unsigned short * us);那不是 unsigned char 而不是 unsigned char* 吗?我的 CPP 生锈了。

以上是关于C#编组来自C++ DLL的无符号字符返回值[重复]的主要内容,如果未能解决你的问题,请参考以下文章

将 C++ int* 编组为 C#

C# 中的无符号字符使用哪种数据类型?

创建要在 C# 中编组的 C++ Dll 的最佳实践 [关闭]

从 C++ dll 编组导出的字符串向量

将字符串从 c# 编组到 c++

C++ <--> C# 修改编组的字节数组