您的位置:首页 > 编程语言 > C语言/C++

c# 调用 研华库函数中 C++ 非托管 Dll 一例(包含指针成员的结构体的调用)

2009-05-03 13:49 459 查看
最近用到研华的一款高精度AD转换卡,PCL816,需要使用C#调用研华库函数中的动态库文件Adsapi32.dll中的函数,参考了网上关于C#调用非托管dll的方法。
其中的难点主要是数据类型的匹配问题。基本的方法在MSDN中关于 c# 调用 C++ 非托管 Dll 的主题有详细的原理说明和例程,我博客上已经转了MSDN这一篇,这里就不赘述了。网上也有很多类型对应关系表,下面是比较好的一个。

C#调用C++编写的Win32 DLL文件时参数对应表

Win32 Types
CLR Type
char, INT8, SBYTE, CHAR
System.SByte
short, short int, INT16, SHORT
System.Int16
int, long, long int, INT32, LONG32, BOOL , INT
INT System.Int32
__int64, INT64, LONGLONG
System.Int64
unsigned char, UINT8, UCHAR , BYTE
System.Byte
unsigned short, UINT16, USHORT, WORD, ATOM, WCHAR , __wchar_t
System.UInt16
unsigned, unsigned int, UINT32, ULONG32, DWORD32, ULONG, DWORD, UINT
System.UInt32
unsigned __int64, UINT64, DWORDLONG, ULONGLONG
System.UInt64
float, FLOAT
System.Single
double, long double, DOUBLE
System.Double
下面主要说一下调用研华的库函数时遇到的一个新问题。即包含指针类型成员的结构体如何调用的问题。下面是最终成功的源码片段。
/// <summary>
/// 获得输入模拟电压,获取指定句柄设备的输入模拟电压结构体
/// </summary>
/// <param name="DriverHandle">设备句柄</param>
/// <param name="lpAIVoltageIn">输入模拟电压</param>
/// <returns>成功或错误号</returns>
[DllImport("Adsapi32.dll")]
public static extern int DRV_AIVoltageIn(int DriverHandle, ref tagPT_AIVoltageIn lpAIVoltageIn);
/// <summary>
/// 模拟电压结构体
/// </summary>
public struct tagPT_AIVoltageIn
{
public ushort chan; //通道
public ushort gain; //增益码:参考用户手册中的电压范围
public ushort TrigMode; //触发模式:0,内部触发;1,外部触发
public IntPtr voltage; //输入模拟电压的指针
}
在C++函数原型中,tagPT_AIVoltageIn.voltage是一个float* voltage;形式定义的,这里要用C#中特有的指针或句柄类型IntPtr。
调用语句为
dwErrCde = DRV_AIVoltageIn(lDriverHandle, ref ptAIVoltageIn);//读取输入模拟电压
但是这时仍然会提示尝试写入受保护内存。其原因是在调用前没有为指针分配内存。使用下面的初始化语句。
tagPT_AIVoltageIn ptAIVoltageIn = new tagPT_AIVoltageIn(); //电压结构体
ptAIVoltageIn.voltage = Marshal.AllocHGlobal(sizeof(float));//为输入模拟电压指针分配内存,其大小为一个float型变量的大小。
定义一个float型的数组来接收这个指针所指地址的值,代码如下:
float[] fVoltage = new float[1]; //输入模拟电压
Marshal.Copy(ptAIVoltageIn.voltage, fVoltage, 0, 1); //从指针地址拷贝到数组中
终于成功接收到数据了!

下附我将研华示例代码中的一个C++命令行通用程序改写后的C#命令行程序的源码。

using System;

using System.Runtime.InteropServices;
using System.Threading;

namespace AD816
{
class Program
{
#region 研华库函数
/// <summary>
/// 打开设备,由设备号得到设备句柄
/// </summary>
/// <param name="DeviceNum">设备号</param>
/// <param name="DriverHandle">设备句柄</param>
/// <returns>成功或错误号</returns>
[DllImport("Adsapi32.dll")]
public static extern int DRV_DeviceOpen(uint DeviceNum, ref int DriverHandle);

/// <summary>
/// 关闭设备,关闭指定句柄的设备
/// </summary>
/// <param name="DriverHandle">设备句柄</param>
/// <returns>成功或错误号</returns>
[DllImport("Adsapi32.dll")]
public static extern int DRV_DeviceClose(ref int DriverHandle);

/// <summary>
/// 获得错误消息,获得指定错误号的错误消息
/// </summary>
/// <param name="lError">错误号</param>
/// <param name="lpszErrMsg">错误消息</param>
[DllImport("Adsapi32.dll")]
public static extern void DRV_GetErrorMessage(ref int lError, string lpszErrMsg);

/// <summary>
/// 配置设备,由配置结构体中的数据配置指定句柄的设备
/// </summary>
/// <param name="DriverHandle">设备句柄</param>
/// <param name="lpAIConfig">配置结构体</param>
/// <returns>成功或错误号</returns>
[DllImport("Adsapi32.dll")]
public static extern int DRV_AIConfig(int DriverHandle, ref tagPT_AIConfig lpAIConfig);

/// <summary>
/// 获得输入模拟电压,获取指定句柄设备的输入模拟电压结构体
/// </summary>
/// <param name="DriverHandle">设备句柄</param>
/// <param name="lpAIVoltageIn">输入模拟电压</param>
/// <returns>成功或错误号</returns>
[DllImport("Adsapi32.dll")]
public static extern int DRV_AIVoltageIn(int DriverHandle, ref tagPT_AIVoltageIn lpAIVoltageIn);

/// <summary>
/// 配置结构体
/// </summary>
public struct tagPT_AIConfig
{
public ushort DasChan; //通道
public ushort DasGain; //增益
}

/// <summary>
/// 模拟电压结构体
/// </summary>
public struct tagPT_AIVoltageIn
{
public ushort chan; //通道
public ushort gain; //增益码:参考用户手册中的电压范围
public ushort TrigMode; //触发模式:0,内部触发;1,外部触发
public IntPtr voltage; //输入模拟电压的指针
}

/// <summary>
/// 成功标志
/// </summary>

const uint SUCCESS = 0;
#endregion

static void Main(string[] args)
{
int dwErrCde = 0; //成功或错误号
uint lDevNum = 0; //设备号0
int lDriverHandle=0; //设备句柄
ushort usChan = 0; //通道0
ushort usGain = 0; //增益码0
ushort usMode = 0; //触发模式0,内部触发

tagPT_AIConfig ptAIConfig = new tagPT_AIConfig(); //配置结构体
tagPT_AIVoltageIn ptAIVoltageIn = new tagPT_AIVoltageIn(); //电压结构体

ptAIVoltageIn.voltage = Marshal.AllocHGlobal(sizeof(float));//为输入模拟电压指针分配内存
float[] fVoltage = new float[1]; //输入模拟电压

dwErrCde = DRV_DeviceOpen(lDevNum, ref lDriverHandle); //打开设备

if (dwErrCde != SUCCESS) //打开失败
{
ErrorHandler(dwErrCde); //打印错误
Console.WriteLine("Program terminated!/n");

Console.WriteLine("Press any key to exit....");
Console.ReadLine();
return;
}

Console.WriteLine("open device " + lDevNum + " success"); //打开成功

ptAIConfig.DasChan = usChan; //设置通道
ptAIConfig.DasGain = usGain; //设置增益

dwErrCde = DRV_AIConfig(lDriverHandle, ref ptAIConfig); //配置设备

if (dwErrCde != SUCCESS) //配置失败
{
ErrorStop(ref lDriverHandle, dwErrCde); //停止设备
return;
}

ptAIVoltageIn.chan = usChan; // 输入通道
ptAIVoltageIn.gain = usGain; // 增益码
ptAIVoltageIn.TrigMode = usMode; // 触发模式

Console.WriteLine("open chanel " + usChan + " success"); //配置成功

for (int i = 0; i < 1000; i++) //读取1000次,间隔0.1s
{
dwErrCde = DRV_AIVoltageIn(lDriverHandle, ref ptAIVoltageIn);//读取输入模拟电压

if (dwErrCde != SUCCESS) //读取出错,退出
{
ErrorStop(ref lDriverHandle, dwErrCde);
return;
}

Marshal.Copy(ptAIVoltageIn.voltage, fVoltage, 0, 1); //从指针地址拷贝到数组中
Console.WriteLine(fVoltage[0]); //打印模拟输入电压
Thread.Sleep(100); //延时0.1s
}

dwErrCde = DRV_DeviceClose(ref lDriverHandle);

if (dwErrCde != SUCCESS)
{
ErrorStop(ref lDriverHandle, dwErrCde);
return;
}

Console.ReadLine();
}

/// <summary>
/// 打印指定错误号的错误信息
/// </summary>
/// <param name="dwErrCde">错误号</param>
static void ErrorHandler(int dwErrCde)
{
string szErrMsg = string.Empty;

///读错误消息
DRV_GetErrorMessage(ref dwErrCde, szErrMsg);
Console.WriteLine("/nError(%d): %s/n", dwErrCde & 0xffff, szErrMsg);
}

/// <summary>
/// 错误退出
/// </summary>
/// <param name="pDrvHandle">设备句柄</param>
/// <param name="dwErrCde">错误号</param>
static void ErrorStop(ref int pDrvHandle, int dwErrCde)
{
//打印错误信息
ErrorHandler(dwErrCde);
Console.WriteLine("Program terminated!/n");

//关闭设备
DRV_DeviceClose(ref pDrvHandle);

Console.WriteLine("Press any key to exit....");
Console.ReadLine();
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: