好久没有在空间写点东西了,其实自己写的也乱七八糟,但是很庆幸终于把这个问题解决了,于是就随便记录下这不容易。把自己的快乐与大家分享,望大家提出宝贵意见。言有不尽,大家担待, 言归正传,现在开始。
首先向大家介绍该程序的目的:由于大型数据采集中vb采集不准确,定时器延时等原因造成不精确,于是打算做成DLL可能使数据采集更加准确,于是编写该DLL文件,介绍下:首先传感器将摩擦力或者加载力转化为电信号通过信号放大,通ISA7012卡输入,同过ISA7012DLL.dll文件里面的函数将获取传感器的值。
下面是我的工程的步骤:
1.文件--新建 新建一个 Win32 Dynamic-Link Library 的工程(在我的VC里面是倒数第二个),工程名称7012DLL,当然你可以根据你的需要任意填写 , 在向导中选择空的工程
2.文件--新建 新建三个文件 7012DLL.cpp(C++代码文件)
7012DLL.h(C++头文件)
7012DLL.def(模块定义文件,这个文件我是新建的文本文件,在文件名的地方直接写的7012DLL.def,当然你也可以在菜单 工程--增加到工程--文件 处添加也可以)
3.代码
7012DLL.h:
#ifdef __cplusplus
extern "C" {
#endif
__stdcall double CALLBACK Compare (int, int) ; //测试使用
__stdcall CALLBACK Friction (); //摩擦力
__stdcall CALLBACK Load (); //加载力
#ifdef __cplusplus
}
#endif
7012DLL.cpp:
#include <windows.h>
#include "7012Demo.h"
#include "Isa7012.h"
#pragma comment(lib, "ISA7012DLL.lib")
BOOL IsIniSuccess = FALSE ;
BOOL Ini7012(DWORD hwnd)
{
AD7012_WINCTRL_STRUCT ddd;
memset( &ddd, 0, sizeof(AD7012_WINCTRL_STRUCT) );
ddd.m_ADType = 1;
ddd.m_BufferBlock = 4;
ddd.m_ClkSrc = 0;
ddd.m_EndChn = 15;
ddd.m_hWnd = hwnd;
ddd.m_StartChn = 0;
ddd.m_StartType = 0;
ddd.m_Timer0Val = 200;
ddd.m_Timer1Val = 20;
ddd.m_Timer2Val = 20;
if ( Isa7012_Open(0) )
{
//设备已成功打开
}
else
{
return FALSE;
}
if ( Isa7012_IsOpen(0) )
{
//打开Isa7012
}
else
{
return FALSE;
}
if ( Isa7012_ADCtrl(0 , &ddd))
{
//初始化正确
}
else
{
return FALSE;
}
Isa7012_StartAD(0);
return TRUE;
}
int WINAPI DllMain (HINSTANCE hInstance, DWORD fdwReason, PVOID pvReserved)
{
return TRUE ;
}
//DllMain()函数可以不要
double __stdcall CALLBACK Compare (double a,double b) //如果写成__stdcall double CALLBACK Compare (double a,double b)VC默认函数返回值为int型(一定要注意啊 就为此我修改了两三天程序)
{
if(a>=b)
return a;
else
return b;
};
double __stdcall CALLBACK Friction (DWORD hwnd)
{
if ( !IsIniSuccess )
{
if (Ini7012(hwnd))
{
IsIniSuccess = TRUE ;
}
else
{
IsIniSuccess = FALSE ;
return 100;
}
}
int x =0;
double volL = 0.0;
WORD dddd;
if(!Isa7012_SoftADGetVal(0,4,&dddd))
{
return -50;
}
x = dddd & 0xFFF;
//x = double(x);
volL=((double(x) / 4095 * 20 - 3) * 100 / 15);
return volL;
};
double __stdcall CALLBACK Load (DWORD hwnd) //函数定义
{
if ( !IsIniSuccess )
{
if (Ini7012(hwnd))
{
IsIniSuccess = TRUE ;
}
else
{
IsIniSuccess = FALSE ;
return 100;
}
}
double vol = 0.0;
int x = 0;
WORD ddd;
if( !Isa7012_SoftADGetVal(0,2,&ddd))
{
return -60;
}
x = ddd & 0xFFF;
vol=((double(x) / 4095 * 20 - 3) * 300 / 15);
return vol;
};
TextDll.def:
#ifdef __cplusplus
extern "C" {
#endif
__stdcall double CALLBACK Compare (int, int) ;
__stdcall CALLBACK Friction ();
__stdcall CALLBACK Load ();
#ifdef __cplusplus
}
#endif
ISA7012.h
#ifndef _PCI_7012_INCLUDE_DLLLIB
#define _PCI_7012_INCLUDE_DLLLIB
#define CLK_2M 0
#define CLK_125K 1
#define CLK_EX 2
#define IN_GATE 0 //内门控
#define EX_GATE 1 //外门控
#define CT_GATE 2 //精密时段控制门控
#define WORK_TYPE_CONTINUE 0 //8254A-0连续发生中断
#define WORK_TYPE_STOPALL 1 //8254A-0发生中断停止全部计数
#define WORK_TYPE_STOPCNT 2 //8254A-0对发生计数器中断的停止该计数器计数
#define COUNT8254_TYPE_0 0x0 //计数方式0 0000
#define COUNT8254_TYPE_1 0x2 //计数方式1 0010
#define COUNT8254_TYPE_2 0x4 //计数方式2 0100
#define COUNT8254_TYPE_3 0x6 //计数方式3 0110
#define COUNT8254_TYPE_4 0x8 //计数方式4 1000
#define COUNT8254_TYPE_5 0xc //计数方式5 1010
#define COUNT8254_CODE_BIN 0x0 //二进制计数
#define COUNT8254_CODE_BCD 0x1 //BCD(十)计数
typedef struct _AD7012_WINCTRL_STRUCT {
WORD m_ADType; //触发方式
//程序触发/定时触发/外触发
WORD m_StartType; //定时器启动方式
//内部门控/外部门控/定时器2间歇门控
WORD m_StartChn; //起始AD通道号
WORD m_EndChn; //结束AD通道号
WORD m_Timer0Val; //定时器0预置值
WORD m_Timer1Val; //定时器1预置值
WORD m_Timer2Val; //定时器2预置值
WORD m_ClkSrc; //定时器时钟源
//定时器使用内部时钟/外部时钟
WORD m_BufferBlock; //缓冲区长度=m_BufferBlock*512 WORD
//需要缓冲数据的长度
//中断方式时为置事件时,到达半满中断的次数
//WORD m_IntFlag; //是否产生中断
WORD m_TransFlag; //是否使用fifo半满中断传递数据
DWORD m_hWnd; //接收消息的OCX窗口
//使用中断必须设置
}AD7012_WINCTRL_STRUCT,*PAD7012_WINCTRL_STRUCT;
typedef struct _AD7012_CTRL_STRUCT {
WORD m_ADType; //触发方式
//程序触发/定时出发/外触发
WORD m_StartType; //定时器启动方式
//内部门控/外部门控/定时器2间歇门控
WORD m_StartChn; //起始AD通道号
WORD m_EndChn; //结束AD通道号
WORD m_Timer0Val; //定时器0预置值
WORD m_Timer1Val; //定时器1预置值
WORD m_Timer2Val; //定时器2预置值
WORD m_ClkSrc; //定时器时钟源
//定时器使用内部时钟/外部时钟
WORD m_BufferBlock; //缓 冲区长度=m_BufferBlock*512 WORD
//需要缓冲数据的长度,最小值为3
//FIFO中断方式时为置事件时,到达半满中断的次数
// WORD m_TransFlag; //是否使用FIFO传递数据
DWORD m_hEvent; //中断外部事件句柄
//使用中断必须设置的中断事件传输局柄
//=NULL 使用DLL内部事件???
}AD7012_CTRL_STRUCT,*PAD7012_CTRL_STRUCT;
extern BOOL _stdcall Isa7012_IsOpen(int mDev);
extern BOOL _stdcall Isa7012_Open(int mDev);
extern BOOL _stdcall Isa7012_Close(int mDev);
extern BOOL _stdcall Isa7012_GetVersion(int mDev,char *pVersion);
extern BOOL _stdcall Isa7012_WriteIO(int mDev,WORD mVal);
extern BOOL _stdcall Isa7012_ReadIO(int mDev,WORD *pVal);
extern BOOL _stdcall Isa7012_WriteIOByte(int mDev,int Chn,BYTE mVal);
extern BOOL _stdcall Isa7012_ReadIOByte(int mDev,int Chn,BYTE *pVal);
extern BYTE _stdcall Isa7012_LabReadIOByte(int mDev,int Chn);
extern BOOL _stdcall Isa7012_InitTimer0(int mDev,DWORD mType,WORD mVal,BOOL mIntFlag,HWND mWnd);
extern BOOL _stdcall Isa7012_InitTimer0Ex(int mDev,DWORD mType,WORD mVal,BOOL mIntFlag,HANDLE mEvent);
extern BOOL _stdcall Isa7012_StartTimer0(int mDev);
extern BOOL _stdcall Isa7012_ReadTimer0(int mDev,WORD *pVal);
extern BOOL _stdcall Isa7012_StopTimer0(int mDev);
extern WORD _stdcall Isa7012_LabReadTimer0(int mDev);
extern BOOL _stdcall Isa7012_SoftADSetChn(int mDev,DWORD mChn);
extern BOOL _stdcall Isa7012_SoftADStart(int mDev);
extern BOOL _stdcall Isa7012_SoftADRead(int mDev,WORD *pVal);
extern BOOL _stdcall Isa7012_SoftADGetVal(int mDev,DWORD mChn,WORD *pVal);
extern BOOL _stdcall Isa7012_ADCtrlEx(int mDev,PAD7012_CTRL_STRUCT pCtrlStru);
extern BOOL _stdcall Isa7012_ADCtrl(int mDev,PAD7012_WINCTRL_STRUCT pCtrlStru);
extern DWORD _stdcall Isa7012_ReadADBuffer(int mDev,unsigned short int *pADVal,DWORD mDataLength);
//-1 错误读出
//0 正确读出 但没有数据
//正数 读出的数量
extern BOOL _stdcall Isa7012_StopAD(int mDev);
extern BOOL _stdcall Isa7012_StartAD(int mDev);
extern BOOL _stdcall Isa7012_ReadStatus(int mDev,BYTE *pVal);
extern BYTE _stdcall Isa7012_LabReadStatus(int mDev);
extern BOOL _stdcall Isa7012_TimerB_Set0(int mDev,DWORD mType,WORD mVal0,DWORD mClk,DWORD mGate);
extern BOOL _stdcall Isa7012_TimerB_Start0(int mDev);
extern BOOL _stdcall Isa7012_TimerB_Read0(int mDev,WORD *pVal0);
extern BOOL _stdcall Isa7012_TimerB_Stop0(int mDev);
extern WORD _stdcall Isa7012_LabTimerB_Read0(int mDev);
extern BOOL _stdcall Isa7012_SetTimerAB(int mDev,WORD mValA1,WORD mValA2,WORD mValB1,WORD mValB2,HANDLE mEvent);
extern BOOL _stdcall Isa7012_StartTimerAB(int mDev);
extern BOOL _stdcall Isa7012_StopTimerAB(int mDev);
extern BOOL _stdcall Isa7012_ReadTimerAB(int mDev,WORD *pValA1,WORD *pValA2,WORD *pValB1,WORD *pValB2);
#endif
好了,下面就是编译了,你可以在 组建菜单里使用组建,也可以使用直接按F7,更可以使用执行菜单,效果都是完成编译的需要~
编译完成后我们就可以测试一下,为了方便我把编译后的7012DLL放到了与exe同级的文件夹下面,当然也可以扔到SYSTEM32下面,那样声明的时候就不用写路径了,看VB测试代码:
Private Declare Function Friction Lib "7012Demo.dll" _
(ByVal hwnd As Long) As Double '
Private Declare Function Load Lib "7012Demo.dll" _
(ByVal hwnd As Long) As Double '
Private Declare Function Compare Lib "7012Demo.dll" _
(ByVal CompareA As long, ByVal CompareB As long) As Integer
Private Sub Command1_Click()
Dim a As Integer
a = Compare(1.8, 2.6)
MsgBox a
End Sub
Private Sub TmrDraw_Timer(ByVal Milliseconds As Long)
Dim F As double
Dim sngLoad As double
F = Friction(Me.hwnd)
sngLoad = Load(Me.hwnd)
Text1.Text = Format(F, "0.000") '摩擦力
Text2.Text = Format(sngLoad, "0.000")
End Sub
程序退出千万要记得释放资源,关于这点我会在以后给大家说的。
为此我把一些网络资料放到这儿:
和编写一般的DLL方法相同,需要注意以下两点:
(1)调用约定
C函数有_stdcall、_cdecl、_fastcall等多种调用约定,调用约定用来说明函数参数的压栈顺序和由谁(函数自身还是调用者)来修改堆栈。关于调用约定的详细说明,请参考我转载的另一篇文章。
编写供PB调用DLL,请使用_stdcall调用约定,如下所示:
extern "C" _declspec(dllexport) int _stdcall GetInt(char* name)
{
...
}
(2)def文件
在VC++中,如果生成DLL可以不使用.def文件,只需要在VC++的函数定义前加__declspec(dllexport)修饰就可以了。生成的DLL VC++用户可以直接使用,但PB、VB等用户使用会遇到函数名转换的问题。因为VC++对于__declspec(dllexport)声明的函数会进行名称转换,如下面的函数:
__declspec(dllexport) int _stdcall GetStr()
编译后会转换为 GetStr@0,这样在PB、VB中声明函数时应该声明GetStr@0,
如果函数带有参数,转换后的函数名将更加复杂,这使PB、VB用户使用起来很不方便。在def文件中由EXPORT输出函数可解决这个问题。
如dll要输出如下两个函数:
extern "C" _declspec(dllexport) int _stdcall GetInt(char* name);
extern "C" _declspec(dllexport) char* _stdcall GetStr(int id);
则def文件书写如下(TEST为工程名):
LIBRARY "TEST"
DESCRIPTION 'TEST Windows Dynamic Link Library'
EXPORTS
; Explicit exports can go here
GetInt @1
GetStr @2
编译生成DLL后,在PB中要调用GetStr函数,只需做如下声明即可:
function string GetStr(int a) library("TEST.dll");
注意,如果您建的是Win32 Dynamic-Link Library 工程,def文件需要自己创建,然后把它加入工程,def文件名需和工程名相同。如您的工程名为test,则创建test.def。
DllMain ()说明:
每一个DLL必须有一个入口点,DllMain是一个缺省的入口函数。DllMain负责初始化(Initialization)和结束(Termination)工作,每当一个新的进程或者该进程的新的线程访问DLL时,或者访问DLL的每一个进程或者线程不再使用DLL或者结束时,都会调用DllMain。但是,使用TerminateProcess或TerminateThread结束进程或者线程,不会调用DllMain。
如何用VC编写供delphi、PB等调用的DLL
extern "C" _declspec(dllexport) int _stdcall GetInt(char* name);
extern "C" _declspec(dllexport) char* _stdcall GetStr(int id);
__declspec (dllexport):这是关键,它标志着这个这个函数将成为对外的接口。(以下是我在网上下载的dllexport、dllimport、_declspec的一些说明):
使用包含在DLL的函数,必须将其导入。导入操作时通过dllimport来完成的,dllexport和dllimport都是vc(visual C++)和bc(Borland C++)所支持的扩展的关键字。但是dllexport和dllimport关键字不能被自身所使用,因此它的前面必须有另一个扩展关键字__declspec。通用格式如下:__declspec(specifier)其中specifier是存储类标示符。对于DLL,specifier将是dllexport和dllimport。而且为了简化说明导入和导出函数的语句,用一个宏名来代替__declspec.在此程序中,使用的是DllExport。如果用户的DLL被编译成一个C++程序,而且希望C程序也能使用它,就需要增加“C”的连接说明。#define DllExport extern "C"__declspec(dllexport),这样就避免了标准C++命名损坏。(当然,如果读者正在编译的是C程序,就不要加入extern “C”,因为不需要它,而且编译器也不接受它)。