动态库控件编程学习心得

第一章  非MFC规则的DLL编程

1 静态链接库和动态链接库的区别在于静态链接库中不能再包含其他的动态链接库或者静态库,而在动态链接库中还可以再包含其他的动态或静态链接库。
2 静态链接库:
lib.h 和lib.cpp 的源代码如下:
//文件:lib.h
#ifndef LIB_H
#define LIB_H
extern "C" int add(int x,int y); //声明为C 编译、连接方式的外部函数
#endif
//文件:lib.cpp
#include "lib.h"
int add(int x,int y)
{
return x + y;
}
main.cpp调用静态库中的函数:
#include <stdio.h>
#include "..\lib.h"
#pragma comment( lib, "..\\debug\\libTest.lib" ) //指定与静态库一起连接
int ma in(int argc, char* argv[])
{
printf( "2 + 3 = %d", add( 2, 3 ) );
}
3动态库编程:
动态库和动态库函数调用函数的例子:
在建立的工程中添加lib.h 及lib.cpp 文件,源代码如下:
/* 文件名: lib.h */
#if ndef LIB_H
#def ine LIB_H
extern "C" int __declspec(dllexport)add(int x, int y);
#endif
/* 文件名: lib.cpp */
#include "lib.h"
int add(int x, int y)
{
return x + y;
}
与静态链接库的调用相似,我们也建立一个与DLL 工程处于同一工作区的应用工程dllCall,它调用DLL 中的函数add,其源代码如下:
#include <stdio.h>
#include <w indows.h>
typedef int(*lpAddFun)(int, int); //宏定义函数指针类型
int main(int argc, char *argv[])
{
HINSTANCE hDll; //DLL 句柄
lpAddFun addFun; //函数指针
hDll = LoadLibrary("..\\Debug\\dllTest.dll");
if (hDll != NULL)
{
addFun = (lpAddFun)GetProcAddress(hDll, "add");
if (addFun != NULL)
{
int result = addFun(2, 3);
printf ("%d", result);
}
FreeLibrary(hDll);
}
return 0;
}
和静态库不同的是在声明函数是用到了__declspec(dllexport),将函数导出。
还可以通过.def文件批量的进行函数的导出:
.def文件:
LIBRARY dllTest         ;LIBRARY后指定对应的dll
EXPORTS ;EXPORT后指定导出的函数名
add @ 1 ;函数名,@后为编号,调用时使用
;后为注释,不可与语句共享一行

用到.def文件可以不再.h中使用extern

4 动态库加载调用的步骤:

动态加载动态库的方法(例子就是):
①首先宏定义动态库函数的指针:
typedef int(_stdcall funcName)(funcStyle);//必须与动态库中定义的函数一一对应
②创建这种指针的对象:
fucname var;
加载dll:
HINSTANCE hDll;//动态库句柄
hDll=LoadLibarary("动态库路径");
③加载动态库中的函数:
var = (funcName)GetProcAddress(hDll, "函数名");
通过var就可以对动态库中的函数进行调用。
最后要进行释放FreeLibrary。
这里需要注意的是:在获取动态库句柄和函数指针的时候要进行判断,以防止动态库加载失败而出现的程序错误。
5 静态调用动态库:
#pragma comment(lib,"dllTest.lib")
//.lib 文件中仅仅是关于其对应DLL 文件中函数的重定位信息
extern "C" __declspec(dllimport) add(int x,int y);
int main(int argc, char* argv[])
{
int result = add(2,3);
printf ("%d",result);
return 0;
}
这个例子是以静态的方式来调用动态库,#pragma comment(lib,"dllTest.lib")告诉编译器与DLL 相对应的.lib 文件所在的路径及文件名
extern "C" __declspec(dllimport) add(int x,int y);导入声明函数
这样调用的程序在生成的exe中会将lib包含进去,所以在使用动态库函数时就像使用内部函数一样。

GetProcAddress ( hDll, MAKEINTRESOURCE ( 1 ) );可以用着用方式来用编号调用函数。

6调用方式

如果通过VC++ 编写的DLL 欲被其他语言编写的程序调用,应将函数的调用方式声明为__stdcall 方式,
WINAPI 都采用这种方式,而C/C++缺省的调用方式却为__cdecl。
在.h文件中,应这样声明 函数:
int __stdcall funcname(int x, int y);
7 调用dll中的全局变量:
/* 文件名: lib.h */
#if ndef LIB_H
#def ine LIB_H
extern int dllGlobalVar;
#endif
/* 文件名: lib.cpp */
#include "lib.h"
#include <w indows.h>
int dllGlobalVar;
BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_f or_call, LPVOID lpReserved)
{
switch (ul_reason_f or_call)
{
case DLL _PROCESS_ATTACH:
dllGlobalVar = 100; //在dll 被加载时,赋全局变量为100
break;
case DLL _THREAD_ATTACH:
case DLL _THREAD_DETACH:
case DLL _PROCESS_DETACH:
break;
}
return TRUE;
}
;文件名:lib.def
;在DLL 中导出变量
LIBRARY "dllTest"
EXPORTS
dllGlobalVar CONSTANT
;或dllGlobalVar DATA(新的方法)
GetGlobalVar

//在应用程序中使用全局变量:
#include <stdio.h>
#pragma comment(lib,"dllTest.lib")
extern int dllGlobalVar;
int main(int argc, char *argv[])
{
printf ("%d ", *(int*)dllGlobalVar);
*(int*)dllGlobalVar = 1;
printf ("%d ", *(int*)dllGlobalVar);
return 0;
}
特别要注意的是用extern int dllGlobalVar 声明所导入的并不是DLL 中全局变量本身,而是其地址,所以要使用*(int*)dllGlobalVar取值
还有一种更安全的方法:
#include <stdio.h>
#pragma comment(lib,"dllTest.lib")
extern int _declspec(dllimport) dllGlobalVar; //用_declspec(dllimport)导入
int main(int argc, char *argv[])
{
printf ("%d ", dllGlobalVar);
dllGlobalVar = 1; //这里就可以直接使用, 无须进行强制指针转换
printf ("%d ", dllGlobalVar);
return 0;
}
通过_declspec(dllimport)导出的变量就不再是指针,而是直接可以使用的变量。
8 DLL 导出类:
//文件名:point.h ,point 类的声明
#ifndef POINT_H
#define POINT_H
#ifdef DLL _FILE
class _declspec(dllexport) point //导出类point
#else
class _declspec(dllimport) point //导入类point
#endif
{
public:
float y;
float x;
point();
point(f loat x_coordinate, float y_coordinate);
};
#endif
//文件名:point.cpp,point 类的实现
//******因为在类的实现中定义了DLL_FILE,所以类的实现中实在class _declspec(dllexport) point //导出类point
#ifndef DLL _FILE
#define DLL _FILE
#endif
#include "point.h"
//类point 的缺省构造函数
point::point()
{
x = 0.0;
y = 0.0;
}
//类point 的构造函数
point::point(f loat x_coordinate, float y_coordinate)
{
x = x_coordinate;
y = y_coordinate;
}
//文件名:circle.h,circle 类的声明
#if ndef CIRCLE_H
#def ine CIRCLE_H
#include "point.h"
#if def DLL _FILE
class _declspec(dllexport)circle //导出类circle
#else
class _declspec(dllimport)circle //导入类circle
#endif
{
public:
void SetCentre(const point ¢rePoint);
void SetRadius(f loat r);
float GetGirth();
float GetArea();
circle();
private:
float radius;
point centre;
};
#endif
//文件名:circle.cpp,circle 类的实现
#if ndef DLL _FILE
#def ine DLL _FILE
#endif
#include "circle.h"
#def ine PI 3.1415926
//circle 类的构造函数
circle::circle()
{
centre = point(0, 0);
radius = 0;
}
//得到圆的面积
float circle::GetArea()
{
return PI *radius * radius;
}
//得到圆的周长
float circle::GetGirth()
{
return 2 *PI * radius;
}
//设置圆心坐标
void circle::SetCentre(const point ¢rePoint)
{
centre = centrePoint;
}
//设置圆的半径
void circle::SetRadius(f loat r)
{
radius = r;
}
类的引用:
//******因为在类的引用找中没有定义了DLL_FILE,所以其实是在class _declspec(dllimport) point //导入类point
#include "..\circle.h" //包含类声明头文件
#pragma comment(lib,"dllTest.lib");
int main(int argc, char *argv[])
{
circle c;
point p(2.0, 2.0);
c.SetCentre(p);
c.SetRadius(1.0);
printf ("area:%f girth:%f ", c.GetArea(), c.GetGirth());
return 0;
}

很巧妙的使用了宏将程序简化。

第二章控件编程:
控件编程需要注意的几点事项:

1 想要在在控件中添加接口必须在接口处添加,不能在ctr类中直接添加
2 控件的对外接口中不能有传出的变量,如果想要传出数据,可以通过接口的返回值或者是控件的属性来实现。
控件的属性可以在接口处直接添加,编译器会自动的在ctr类中生成成员不变量m_。在外部可以通过控件的对象直接调用这个属性。

3 控件中调用动态库,需要注意的几个点:

 ①在ctr类的头文件中声明对应动态库中函数类型的指针

 ②在ctr的头文件中创建对应函数只针对的对象。

 ③在构造函数中初始化函数指针对象。

 ④在初始函数中调用LoadLibrary加载动态库,并通过GetProcAddress(hDll, "函数名");的方式获取指针并转换成对应动态库中函数类型的指针赋给响应的对象。

⑤在函数使用前先要进行判断动态库函数是否加载成功。

⑥在析构函数中FreeLibrary释放动态库资源。

注:在控件的编程过程中,应该对所有的传入参数进行判空。

并且应该对所有的传出参数进行显示的初始化。
第三章业务逻辑

1 在进行指令的传入和数据的返回时,应该对指令和数据进行响应的打解包的处理。

例:从外界传入的指令需要进行压缩的处理,将字符串转换成16进制数传入设备中。




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

春阳CYang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值