1.怎样在C++Builder中创建使用DLL
2.用C++Bulider在WIN.INI中保存信息
3.如何在C++Builder中检测硬件
4.C++Builder如何响应消息及自定义消息
5.利用C++ Builder开发动画DLL
6.用C++ Builder 3制作屏幕保护程序
7.TCP/IP头格式
8.UDP
9.判断windows的Desktop及其它目录
10用C++Builder创建数字签名
11用Enter 键控制焦点切换的方法
12.拦 截 Windows 消 息
13.使用CommaText
14.程序开始时先显示信息框
15.怎样获取程序的命令行参数?
16.如何监视剪贴板
17.如何使用OnIdle事件
18.用C++Builder编写串行异步通信程序
19.C++BUILDER非可视组件的消息处理技巧
20.用C++Builder 建立数据库VCL使用经验
21.用C++ Builder创建基于Internet的点对点Chat
22.用C++Builder获取应用程序图标
23.BIG5到GB的转换技术
24.C++BUILDER让你的任务栏图标动起来
25.TFORM
26.用BCB在windows桌面创建快捷方式
27.读磁片磁区
28.I/O 端口读写的实现
29.检测鼠标位置
30.令Win32 应用程序跳入系统零层
31.如何取得Memo的行和列
32.使用Sockets
33.Windows95/98下怎样隐藏应用程序不让它出现在CTRL-ALT-DEL对话框中?
34.怎样隐藏应用程序的任务条图标
35.编写自己的Ping.exe程序
36.用C++Builder在WINNT下编制一个Service
37.如何在C++ BUILDER中自动关闭WINDOWS屏保
38.显示/隐藏任务栏图标
39.信箱监视程序
40.C++Building制作闹钟
41.拨号上网IP地址的检知
42.用C++ Builder编写Tray程序
43.怎样用代码来最小化或恢复程序
44.制作主窗口显示前的版权窗口
45.判断是否已经联到 internet
46.获取登陆用户名
47.隐藏桌面图标
48.程序启动时运行
49.控制面板的调用
50.模拟键盘按键
51.让标题栏闪烁
52.启动屏幕保护
53.年月日星期的取法
54.键盘事件
55.隐藏任务栏
56.禁止关机
57.怎样以最小化方式启动程序
58.在Memo中增加一行后,如何使最后一行能显示
59.设置壁纸方法
怎样在C++Builder中创建使用DLL
自从C++Builder从去年浪漫情人节上市以来,吸引了大量的Delphi、VC、Vb的程序员到它的怀抱,大量的C、C++程序员感叹道:总算有了C的可视化开发工具,对我也是一样,从BC、Delphi到C++Builder。
动态链接库(DLL)是Windows编程常遇到的编程方法,下面我就介绍一下在BCB (C++Builder下简称BCB) 中如何创建使用DLL和一些技巧。
一、创建:
使用BCB File|NEW建立一个新的DLL工程,并保存好文件BCB,生成一个DLL的程序框架。
1.DllEntryPoint函数为一个入口方法,如果使用者在DLL被系统初始化或者注销时被调用,用来写入对DLL的初始化程序和卸载程序;参数:hinst用来指示DLL的基地址;reason用来指示DLL的调用方式,用于区别多线程单线程对DLL的调用、创建、卸载DLL;
2.在程序中加入自己所要创建的DLL过程、函数;
3.用dllimport描述出口;
例程序如下:
#include
#pragma hdrstop
extern 揅?__declspec(dllexport) int test();
int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void*)
{
return 1;
}
int test()
{
return 3;
}
注意:动态链接库中调用过程、函数时有不同的CALL方式 __cdecl、 __pascal, __fastcall、__stdcall,BCB中默认的方式为__cdecl(可不写),如果考虑兼容性可用时__stdcall声明方法为:
extern 揅?__declspec(dllexport) int __stdcall test();
对于其中过程、函数也改为:
int __stdcall test()
二、使用DLL
在BCB中使用DLL有两种方法:
1.用静态调用法
首先需要在BCB的项目中加入输入接口库(import library),打开工程项目,使用BCB View|Project Manager打开项目列表,向项目中加入接口库(*.lib)。
其次在头文件中加入接口声明。
例程序如下:
//define in include file
extern 揅?__declspec(dllimport) int __cdecl test();
//use function in main program
int I;
I=test();
注意:
(1)动态链接库调用过程、函数时CALL方式 与创建时方式一样不写为__cdecl,其它需要声明。
(2)BCB创建的DLL有对应的输入接口库(import library),如只有DLL而无库时,可用BCB的implib工具产生:implib xxx.lib xxx.dll;另外可用:tlib xxx.lib,xxx.lst 产生DLL的内部函数列表,许多Windows的未公开技术就是用这种方法发现的。
2.动态调用法
动态调用法要用Windows API 中的LoadLibrary()和GetProcAddress()来调入DLL库,指出库中函数位置,这种方法较常见。
例程序如下:
HINSTANCE dd;
int _stdcall (*ddd)(void);
dd=LoadLibrary(搙xx.dll?;
ddd=GetProcAddress(dd,搕est?;
Caption=IntToStr(ddd());
FreeLibrary(dd);
三、注意:
创建DLL时编译链接时注意设置Project Options。
Packages标签:去除Builder with runtime packages检查框。
Linker标签:去除Use dynamic RTL检查框。
否则创建的DLL需要Runtime packages or Runtime library。
用C++Bulider在WIN.INI中保存信息
现在许多软件把程序中需要的数据保存在注册表中,这样当用户装的软件越来越多时,致使注册表越来越庞大,容易使系统出错。当然,微软也建议在注册表中保存数据,但当我们需要保存的数据不多时完全可以把数据保存在WIN.INI中,这样可以很方便地维护,实现方法相对来说比较简单。下面我以Borland C++ Builder为例来说说如何实现。
原理其实很简单,只需调用API的 WriteProfileString和GetProfileInt函数就可以了。这两个函数的原型是:BOOL WriteProfileString(LPCTSTR lpAppName,LPCTSTR lpKeyName,LPCTSTR lpString );
UINT GetProfileInt(LPCTSTR lpAppName,LPCTSTR lpKeyName,INT nDefault);
其中lpAppName指在WIN.INI中段的名字,即用[]括起来的字符串,lpKeyName指在这个段中每一个项目的名字,lpString指这个项目的值,即“=”后的数, nDefault为当GetProfileInt没有找到lpAppName和lpKeyName时返回的值,即缺省值,前者返回为布尔值(true 或 false),后者返回为无符号整形值。当在WriteProfileString函数中 lpKeyName 为空(NULL)时,则清除这个段的全部内容,lpString 为空时,则清除这一项目的内容,即这一行将清除掉。
下面举一例子来说明这两个函数的用法。新建一个应用程序,在Form1上放两个Edit和三个Button,其中Edit的Text为空,三个Button的Caption分别为“添加”、“查看”、“清除”。双击“添加”按钮加入下面代码:
WriteProfileString(“例子程序”,“项目”,Edit1→Text.c_str());
双击“查看”按钮加入如下代码:
unsigned int Temp;
Temp=GetProfileInt(“例子程序”,“项目”,100);
Edit2→Text=IntToStr(Temp);
双击“清除”按钮加入如下代码:
WriteProfileString(“例子程序”,NULL,NULL);
然后按F9键运行程序。
下来可以检验一下程序的正确性。在Edit1中输入数字,如“3265”,按“添加”按钮,这时运行“sysedit”来查看“WIN.INI”文件的最后面,可以看到加入了如下内容:
[例子程序]
项目=3265
其中“[]”和“=”是函数自动加上的。按下“查看”按钮,在Edit2中出现“3265”,当按下“清除”按钮可清除添加的部分。经过查看可知程序已达到预期的目的。
喜爱编程的朋友可以把上述方法应用到自己的程序中去,来达到保存数据信息的作用。当确实要把信息保存到注册表中,可以在C++ Builder中定义一个TRegistry类的对象来进行相关的操作,或者直接调用Windows的API函数,具体如何编程大家可以参阅相关资料或者同我联系。
如何在C++Builder中检测硬件
在我们编写的程序中常常要和硬件打交道,那么如何在程序中确定系统中是否有该设备,它的运行状态又是怎样的呢?对于初学者来说,这个问题常常不好解决,其实只需简单地利用几个API函数,硬件的问题并不神秘。下面就让我们一起看看在C++ Builder中是如何检测硬件的。
1. 检测CPU的型号
先让我们从最简单的做起,看一看自己的CPU型号。首先,在C++ Builder中画出图1所示的窗体,在下面的几个例子中我们将一直使用这个窗体作示范,它包括一个用来激活测试的Button和一个用来显示结果的Memo。我们可以用GetSystemInfo这个API获得CPU的型号。将下列代码添加到Button的Click事件里就可以了:
void __fastcall TForm1::Button1Click(TObject *Sender)
{
//获得CPU型号
SYSTEM_INFO systeminfo;
GetSystemInfo (&systeminfo);
Memo1→Lines→Add(撃腃PU类型是:敚玈tring( systeminfo.dwProcessorType ));
}
运行它,点击Test试试,CPU型号出来了吧!
2.检测内存状态
获得内存状态的方法和CPU型号差不多,只是他用到的是另外一个API:GlobalMemoryStatus。
其中,成员dwTotalPhys用来获得物理内存总量,而dwAvailPhys顾名思义是有效物理内存的意思。我们只要把下面几行代码加到上面程序的后面就可以了(不用重做,下同):
//获得内存状态
MEMORYSTATUS memory;
memory.dwLength =sizeof(memory); //初始化
GlobalMemoryStatus(&memory);
Memo1→Lines→Add(撃奈锢砟诖媸?Mb):敚玈tring(int(memory.dwTotalPhys /1024/1024)));
Memo1→Lines→Add(撈渲锌捎媚诖媸?Kb):敚玈tring(int( memory. /1024)));
怎么样,看出点门道了么?两段程序的格式几乎一模一样,其实,GetSystemInfoGlobalMemoryStatus还可以获得许多其他有关CPU和内存的信息,就按照上面的格式去套就行了,更详细的资料可以去看C++ Builder4的Help。
3. 检测可用硬盘空间
好了,经过前面两个简单问题的热身,我们来处理一个稍微复杂的问题:我们知道安装程序大都有一个检测硬盘空间的过程,那么这是怎么实现的呢?他用到的是API函数GetDiskFreeSpace,这个函数输入一个参数:目标盘的路径;返回四个参数,依次是每簇的扇区数、每扇区的字节数、空闲的簇数、总簇数。假如我们需要检测C盘的总容量和可用容量,那么可以把以下代码加到上面的程序中:
//获得C盘可用空间
DWORD sector,byte,cluster,free;
long int freespace,totalspace;
GetDiskFreeSpace(揅:?&sector,&byte,&free,&cluster); //获得返回参数
totalspace=int(cluster)*int(byte)*int(sector)/1024/1024; //计算总容量
freespace=int(free)*int(byte)*int(sector)/1024/1024; //计算可用空间
Memo1→Lines→Add(揅盘总空间(Mb):敚玈tring(totalspace));
Memo1→Lines→Add(揅盘可用空间(Mb):敚玈tring(freespace));
怎么样?现在可以自己做安装程序了吧!
C++Builder如何响应消息及自定义消息
Inprise(Borland) C++Builder中,可以象在Delphi中一样响应消息,只是看起来要稍复杂一点。
对于系统已定义的消息,可以直接响应:
#define WM_MY_OPEN_CMDLINE_FILE (WM_USER+1) //进程间通讯的自定义消息
#define WM_MY_SEARCH_NODE (WM_USER+2) //查找命令的自定义消息
class TSomeForm : public TForm
{
//...类中的其它代码
protected:
//消息的响应过程
void __fastcall OpenCmdLineFile(TMessage Message);
void __fastcall SearchDocumentNode(TMessage Message);
void __fastcall GetWindowMinMaxInfo(TWMGetMinMaxInfo Message);
//以下通过宏定义实现消息的正确响应
BEGIN_MESSAGE_MAP
MESSAGE_HANDLER(WM_MY_OPEN_CMDLINE_FILE, TMessage, OpenCmdLineFile)
MESSAGE_HANDLER(WM_MY_SEARCH_NODE, TMessage, SearchDocumentNode)
MESSAGE_HANDLER(WM_GETMINMAXINFO , TWMGetMinMaxInfo, GetWindowMinMaxIn fo)
END_MESSAGE_MAP(TForm)
};//end class
//以下为实现代码
void __fastcall TSomeForm::OpenCmdLineFile(TMessage Message)
{//直接通过消息结构传递参数
LPSTR lpCmdLine=(LPSTR)Message.LParam;//从Message中取得参数
this->HandleCmdLineFile(lpCmdLine);//处理命令行的参数
return;
}
void __fastcall TSomeForm::SearchDocumentNode(TMessage Message)
{//响应查找消息
//Message中的参数在此处不需要。
this->SearchNode();
return;
}
void __fastcall TSomeForm::GetWindowMinMaxInfo(TWMGetMinMaxInfo Messag
e)
{//设置主窗口的最小尺寸
MINMAXINFO *MinMaxInfo=Message.MinMaxInfo;
MinMaxInfo->ptMinTrackSize.x=400;
MinMaxInfo->ptMinTrackSize.y=300;
return;
}
其中:TMessage和TWMGetMinMaxInfo类型的定义可参见:
C:/Program Files/Borland/CBuilder/inlucde/vcl/Messages.hpp;其它的消息
响应方法与此相同。
另外,可以为自定义的消息也定义一个对应的消息结构(如:TSearchNode_Mes
sage),至于如何定义消息结构, 可以参考:
C:/Program Files/Borland/CBuilder/inlucde/vcl/Messages.hpp
利用C++ Builder开发动画DLL
我们在Windows98环境下执行拷贝文件、查找文件或计算机等耗时比较长的操作时,Windows会显示一个小小的动画,指示正在进行的操作,与死板的静止图像相比增色不少。那么我们自己开发软件时,能否也显示一个这样的动画提示呢?我在开发一个外贸应用软件系统时,遇到的数据量很大,当通过复合条件查找时,因为不是数据库表的每个项目都有索引,所以很费时,系统也会表现出长时间停顿,用户感觉极为不爽。我经过一段时间的探索,开发了一个能够在采用的开发环境PowerBuilder下调用的动画DLL,由于采用多线程编程,PB调用的DLL函数能够及时将控制权交还为PB,不影响应用系统的运转。用户能够看到一个东西在动,也就不会想到系统是不是停止响应了,感觉时间也似乎没那么久了。
代码与编译选项
(1) 在C++Builder的File菜单下选择New,在New Item对话框的New属性中选择DLL,C++Builder就会创建一个空白的DLL项目。
(2) 在File菜单下选择New Form,C++Builder创建一个空白的Form,修改它的属性为
BorderStyle=bsDialog
BorderIcons的子属性均为False
FormStyle=fsStayOnTop
Position= poScreenCenter
Name=StatusForm
(3) 在Form上添加一个Win32下的Animate控件Animate1,修改它的属性为
Align=alTop
(4) 在Form上添加一个Standard下的Button控件Button_Cancel,再添加System下的Timer控件Timer1,设置定时Interval时间位250,以较快的响应用户的取消请求。
因为PB应用系统与动画窗体代码分别属于两个线程,不能采用PB线程直接关闭动画窗体线程的窗口,否则会引起系统运行不正常,因此采用PB线程设置关闭标志,而动画线程采用Timer控件定时检查标志,一旦检测到关闭标志,就关闭窗口,清除线程标志,结束动画线程。
下面给出编码及编码原理:
1.DLL DLL主体代码:
/**********************************
* DLL主体代码
* 定义DLL公用变量
* g_CommonAVI
对Animate控件动画类型索引
* gi_Canceled
Button_Cancel按钮是否被选择过
* gi_AVIType
要显示的动画类型,由DLL输出函数做为参数输入
* gi_RequestClose
请求动画线程关闭标志
* gi_WindowActive
动画窗口所处的状态
* lpsWinTitle
动画窗体的标题,由DLL输出函数做为参数输入
*/
TCommonAVI g_CommonAVI[]={
aviNone, aviFindFolder,
aviFindFile, aviFindComputer,
aviCopyFiles, aviCopyFile,
aviRecycleFile, aviEmptyRecycle,
aviDeleteFile
};
int gi_Canceled=0,gi_AVIType=0;
int gi_RequestClose=0,gi_WindowActive=0;
char lpsWinTitle[256];
HWND hWndParent=NULL;
/* 定义DLL 输出函数 */
extern "C" __declspec(dllexport)
int pascal DllEntryPoint(HINSTANCE hinst,
unsigned long reason, void*);
extern "C" __declspec(dllexport) int
pascal ShowStatusWindow(int AVIType,
LPSTR WinTitle,long hWnd);
extern "C" __declspec(dllexport) int
pascal GetStatus(int ai_CloseWin);
extern "C" __declspec(dllexport) int
pascal CloseStatusWindow();
/*定义线程TformThread:*/
class TFormThread : public TThread{
public:// User declarations
__fastcall TFormThread(bool CreateSuspended);
void __fastcall Execute(void);
};
__fastcall TFormThread::
TFormThread(bool CreateSuspended):
TThread(CreateSuspended){
}
/* 动画线程执行代码,
动画窗体的定时器控件会关闭它,
清除窗体存在标志后结束线程的运行
*/
void __fastcall TFormThread::Execute(void){
gi_WindowActive=1;
StatusForm=new TStatusForm(NULL);
StatusForm- >Caption=lpsWinTitle;
StatusForm- >ShowModal();
gi_WindowActive=0;
delete StatusForm;
gi_RequestClose=0;
}
/* 定义一个线程实例指针 */
TFormThread *FormThread;
/**********************************************
* 输出函数代码实现部分
* DllEntryPoint 32位DLL入口
* ShowStatusWindow 显示动画窗口,
它通过创建一个线程来创建窗口,避免由于窗口
的MODAL属性而使控制权不能及时的返还给调用者
* GetStatus
取得撊∠麛状态,即用户有没有选择撊∠麛按钮
* CloseStatusWindow 关闭动画窗口,
*/
__declspec(dllexport) int WINAPI DllEntryPoint
(HINSTANCE hinst, unsigned long reason, void*)
{
return 1;
}
__declspec(dllexport) int pascal
ShowStatusWindow(int AVIType,LPSTR
WinTitle,long hWnd){
hWndParent=(HWND)hWnd;
memset(lpsWinTitle,0,sizeof(lpsWinTitle));
strncpy(lpsWinTitle,WinTitle,sizeof(lpsWinTitle)-1);
if (AVIType >0 && AVIType< =8)
gi_AVIType=AVIType;
FormThread=new TFormThread(true);
FormThread- >Priority = tpNormal;
FormThread- >Resume();
}
__declspec(dllexport) int pascal
GetStatus(int ai_CloseWin){
if (gi_Canceled)
if (gi_WindowActive){
gi_RequestClose=1;
while(gi_RequestClose);
}
return gi_Canceled;
}
__declspec(dllexport) int pascal
CloseStatusWindow(){
if (gi_WindowActive){
gi_RequestClose=1;
while(gi_RequestClose);
}
return gi_Canceled;
}
2.窗体StatusForm的代码:
TStatusForm *StatusForm;
//-----------------------------------
extern int gi_Canceled;
extern int gi_AVIType;
extern TCommonAVI g_CommonAVI[];
__fastcall TStatusForm::
TStatusForm(HWND ParentWindow)
: TForm(ParentWindow)
{
gi_Canceled=0;
}
//-----------------------------------
//取消按钮并不直接关闭窗体,
而指示设置取消标志,供调用者查看
void __fastcall TStatusForm::
Button_CancelClick(TObject *Sender)
{
gi_Canceled=1;
// ModalResult=mrCancel;
}
//-----------------------------------
// 激活动画,在FORMCREATE事件中
void __fastcall TStatusForm::
FormCreate(TObject *Sender)
{
Animate1- >CommonAVI=g_CommonAVI[gi_AVIType];
Animate1- >Active = true;
}
//-----------------------------------
extern int gi_RequestClose;
// 定时器事件检测到结束标志关闭窗体
void __fastcall TStatusForm::
Timer1Timer(TObject *Sender)
{
if (gi_RequestClose){
ModalResult=mrOk;
}
}
//-----------------------------------
(5) 设置编译选项:Project->Options打开Project Options对话框,清除Linker属性页中的Use Dynamic RTL标志,清除Packages属性页中的Build with runtime packages。这样只要单个DLL就可以运行了,而不必安装一些动态连接运行时间库。使用动画DLL
上面编译出DLL可以由其它任何开发语言调用,下面给出在PB中的使用方法。
(1) 定义:
//Declare - > Global External Functions
FUNCTION Long ShowStatusWindow(Long
AVIType,String WinTitle,long hWnd) &
LIBRARY "STATWIN.DLL" ALIAS FOR "ShowStatusWindow"
FUNCTION Long GetCancelStatus(Long CloseWindow) &
LIBRARY "STATWIN.DLL" ALIAS FOR "GetStatus"
FUNCTION Long CloseStatusWindow() &
LIBRARY "STATWIN.DLL" ALIAS FOR "CloseStatusWindow"
(2) 调用:
long ll_EndTime
//显示查找文件夹动画
ShowStatusWindow(2)
setpointer(HourGlass!)
ll_EndTime = Cpu() + 10 * 1000
DO
if GetCancelStatus(0)=1 then
exit
end if
// 做想做的事情
LOOP UNTIL cpu() > ll_EndTime
CloseStatusWindow()
用C++ Builder 3制作屏幕保护程序
屏幕保护程序是以scr为扩展名的标准Windows可执行程序,在激活控制面板的显示器属性的"屏幕保护程序"页时,该模块会自动在Windows启动目录(Windows目录和系统目录)下查找扩展名是scr的基于Windows的可执行文件。使用屏幕保护程序,不仅可以延长显示器的使用寿命,还可以保护私人信息。
编制屏幕保护程序不仅要涉及消息的处理,还要涉及命令行参数的处理。在WIN32SDK文档中描述了编制基于WIN32的标准的屏幕保护程序所必须遵守的严格标准。按照这些标准,屏幕保护程序必须要输出两个函数:ScreenSaverProc和ScreenSaverConfigureDialog,但是,在Windows系统中的很多屏幕保护程序并没有遵循这些标准(使用impdef或者tdump实用工具查看即可)。并且使用该文档中介绍的方法编写屏幕保护程序,不仅要使用资源编辑器,并且在链接时还要利用Scrsaver.lib文件(在C++Builder3环境下,不能成功连接)。不仅要涉及消息的处理,还要涉及命令行参数的处理。
C++Builder3是一种快速的应用程序开发工具,提供了许多类型的应用程序开发模板,但没有提供开发屏幕保护程序的模板,并且在其在线帮助中也没有提及如何开发这类应用程序。经过本人的研究,找到了用C++Builder3编制屏幕保护程序的方法。
在控制面板的"显示器属性"项的"屏幕保护程序"页中进行设置时,要遇到三种类型的命令行参数,并且,各种情况下的屏幕保护程序的显示结果也各不相同,一般来讲,就需要三种类型的窗体(或两种,在随后的内容中讨论)。下面将分四步来具体地说明如何编制屏幕保护程序。
一、屏幕保护程序的选择
如果在标题为"屏幕保护程序"的下拉列表框中选中了某个保护程序时,系统会自动启动该程序,这个程序的显示范围是在这个页面上的显示器图形的屏幕范围,同时,会将两个命令行参数:一个是"/p";另一个是显示窗口的句柄,传递给这个被选中的程序。因此,这类程序首先应该能够处理命令行参数。在C++Builder3中,与命令行参数处理有关的函数是:ParamCount()和ParamStr(),具体的申明方式如下:
1.externPACKAGEint__fastcallParamCount(void);
该函数返回命令行参数的个数,但不包含应用程序本身。
2.externPACKAGEAnsiString__fastcallParamStr(intIndex);
该函数返回指定索引值的命令行参数。ParamStr(0)返回的是应用程序本身。
所以,在这以步骤中的参数判断的语句如下:
if(UpperCase(ParamStr(1))==
"-p"||UpperCase(ParamStr(i))=="/p")
{
//addthecodeinhere
}
在完成了参数判断后,就应该对显示窗口的处理,为能够使程序在显示器图形的屏幕区域内显示,就要重新设置程序的父窗口和显示区域。这要涉及到父窗口句柄的获得及父窗口的设置,以及API函数的调用。这种环境下的父窗口句柄就是传递过来的第二个命令行参数;要设置父窗口,只需设置窗体的ParentWindow属性即可。这段程序如下:
RECTrc;//Line1
HWNDhWnd=(HWND)
(atol(ParamStr(2).c_str()));//Line2
::GetClientRect(hWnd,&rc);//Line3
ParentWindow=hWnd;//Line4
Left=rc.left;//Line5
Top=rc.top;//Line6
Width=rc.right-rc.left;//Line7
Height=rc.bottom-rc.top;//Line8
在上面的程序片段中,第2行语句是将传递过来的第2个参数转换成窗口句柄;然后,第3行语句利用这个窗口句柄,调用API函数以获得该窗口的客户区域;第4行语句将选中的屏幕保护程序的父窗口设置为指定的窗口;余下的语句是将该程序的窗口大小设置成副窗口的客户区大小。这一程序片段的位置应该是在窗体的OnCreate事件处理中。
需要说明的是,这种类型(包括第三步介绍的窗体)的窗体样式应是:
FormStyle=fsStayOnTop;
窗体边界的样式应为:
BorderStyle=bsNone;
当然,这时也不需要鼠标图形,因此,可以将鼠标的形状设为crNone:
Cursor=crNone;
二、初始化参数的设置
单击"显示器属性"模块的"屏幕保护程序"页面中的"设置"按钮时,系统会启动指定的保护程序的初始值设置对话框,这时传递过来的命令行参数是:"/c"或"-c"(参数的处理与前面介绍的相同)。通过该对话框,可以设置保护程序的一些初始参数,比如图形的变化快慢等。在这段程序中,还要涉及到初始化文件或注册表的读写,用以记录初始化参数,便于保护程序启动时使用。
三、预览及运行
预览的效果就是屏幕保护程序被激活后的显示。单击单击"显示器属性"模块的"屏幕保护程序"页面中的"预览"按钮,就可以观察保护程序运行的实际效果。这时,系统启动该程序时传递过来的命令行参数是:"/s"或"-s"。对于命令行参数的处理与前面的步骤相同,但在这一步中,还要对几个消息进行处理,这些消息是:WM_MOUSEMOVE,WM_LBUTTONDOWN,WM_MBUTTONDOWN,WM_RBUTTONDOWN,WM_KEYDOWN,WM_ACTIVATE。对WM_MOUSEMOVE和WM_ACTIVATE消息的处理形式如下:
void__fastcallHandleSomeMessage(TMessage&Msg)
{
switch(Msg.Msg)
{//......
caseWM_ACTIVATE:if(Msg.WParamLo==WA_INACTIVE)
Close();
break;
caseWM_MOUSEMOVE:if(OldMouseX==-1&&OldMouseY==-1)
//Intheconstructor,OldMouseXand
OldMouseYmustbeinitializedby-1.
{OldMouseX=Msg.LParamLo;
OldMouseY=Msg.LParamHi;
}
elseif(OldMouseX!=Msg.LParamLo
||OldMouse!=Msg.LParamHi)
Close();
break;
......
}
}
对于其他的消息仅仅是调用Close()函数来关闭应用程序即可。应用这种消息处理方式时,必须要类定义时进行消息映射,不然的话,就要在相应的消息响应中进行处理(使用一定的布尔变量,就可以与第一步合用一个窗体)。
与第一步类似,在该步骤中,也不需要具体的鼠标指针的形状,因此,将鼠标指针设为crNone:
Cursor=crNone;
四、修改项目源文件
在C++Builder3中,一个窗体也就是一个类,换句话说,具有某些特性的类也就是一个窗体,因此,编制屏幕保护程序时,也不需要什么主窗体,同时,也不用自动创建某些窗体了,这时就要修改项目源文件,下面所列出的程序就是笔者在编制某屏幕保护程序时使用的项目源文件,供读者参考。
WINAPIWinMain(HINSTANCE,HINSTANCE,LPSTR,int)
{
CreateMutex(NULL,true,"ScreenSaver");
if(GetLastError()!=ERROR_ALREADY_EXISTS)
{
try
{
Application->Initialize();
Application->Title="屏幕保护程序测试";
if(UpperCase(ParamStr(1))==
"/C"||UpperCase(ParamStr(1))=="-C"
||ParamCount()==0)
{TScrSaverConfiguerF*ScrCfg=
newTScrSaverConfiguerF(NULL);
ScrCfg->ShowModal();
deleteScrCfg;
return0;
}//单击"设置"按钮
elseif(UpperCase(ParamStr(1))==
"/P"||UpperCase(ParamStr(1))=="-P")
{TScrForP*ScrFP=newTScrForP(NULL);
ScrFP->ShowModal();
deleteScrFP;
return0;
}//在"屏幕保护程序"下拉列表框中选择一个程序
elseif(UpperCase(ParamStr(1))==
"/S"||UpperCase(ParamStr(1))=="-S")
{TScreenSaveF*ScreenSave=newTScreenSaveF(NULL);
ScreenSave->ShowModal();
deleteScreenSave;
return0;
}//单击"预览"按钮,及运行屏幕保护程序
else
return1;
}
catch(Exception&exception)
{
Application->ShowException(&exception);
}
}
return0;
}//theWinMainFunctionend
前面介绍了在C++Builder3下编制屏幕保护程序的方法.对于C++Builder3这种RAD工具来讲,开发这类程序也是相当方便的,按照前述的方法,可以在极短的时间开发出屏幕保护程序。对于屏幕保护程序,在本文中没有说明的就是如何设置口令的问题,这部分就由读者自己摸索吧。
TCP/IP头格式
一、先是常用的IP头格式。
IP头格式:
版本号 (4位)
IP头长度 (4位)
服务类型 (8位)
数据包长度 (16位)
标识段 (16位)
标志段 (16位)
生存时间 (8位)
传输协议 (8位)
头校验和 (16位)
发送地址 (16位)
目标地址 (16位)
选项
填充
简单说明
============
1. IP头长度计算所用单位为32位字, 常用来计算数据开始偏移量
2. 数据包长度用字节表示, 包括头的长度, 因此最大长度为65535字节
3. 生存时间表示数据被丢失前保存在网络上的时间, 以秒计.
4. 头校验和的算法为取所有16位字的16位和的补码.
5. 选项长度是可变的, 填充区域随选项长度变化, 用于确保长度为整字节的倍数.
描述
============
struct iphdr {
BYTE versionihl;
BYTE tos;
WORD tot_len;
WORD id;
WORD frag_off;
BYTE ttl;
BYTE protocol;
WORD check;
DWORD saddr;
DWORD daddr;
/* Put options here. */
};
二、TCP头格式
TCP头格式:
源端口 (16位)
目的端口 (16位)
序号 (32位)
确认号 (32位)
数据偏移 (4位)
保留 (6位)
标志 (6位)
窗口 (16位)
校验和 (16位)
紧急指针 (16位)
选项
填充
简单说明
============
1. 数据偏移用于标识数据段的开始
2. 保留段6位必须为0
3. 标志包括紧急标志、确认标志、入栈标志、重置标志、同步标志等。
4. 校验和计算方式为将头与16位二进制反码和中的16位二进制反码加在一起。
5. 选项长度是可变的, 填充区域随选项长度变化, 用于确保长度为整字节的倍数.
6. 更详细的说明请参阅有关资料。
描述
============
struct tcphdr {
WORD SourPort;
WORD DestPort;
DWORD SeqNo;
DWORD AckNo;
BYTE HLen;
BYTE Flag;
WORD Window;
WORD ChkSum;
WORD UrgPtr;
/* Put options here. */
};
UDP
一、说明
使用UDP时,直接使用API代替控件。
第一个程序(ReadBufferUdp)使用来接收到缓存中。
"Destino"变量非常重要,如果你从其他地方接收数据到Buffer,你必须设置Destino = 0 并且在以后执行的时候赋值你将要发送的包的地址给它(after the execution it will have the address which send you the packet.)。
如果你只想从一个指定的地址接收数据,你必须设置变量Destino =
"gvEncerrar" 用来中止处理过程。(gvEncerrar被设置为全局变量。)
超时时间设置。"Inicio + 12" = 12 sec of timeout.
第三个程序是用来准备WinSock程序。
二、代码
int ReadBufferUdp(unsigned long *Destino,void *T,int Size)
{
char Buffer[128];
SOCKADDR_IN SockAddr;
int LenSockAddr=sizeof(SOCKADDR_IN);
fd_set FdRead;
struct timeval t_val;
int Ret;
time_t Inicio = time(NULL);
Application->ProcessMessages();
if(gvEncerrar)
return false;
FD_ZERO(&FdRead);
FD_SET(gvSocket,&FdRead);
t_val.tv_sec=0;
t_val.tv_usec=0;
while((Ret=select(0,&FdRead,NULL,NULL,&t_val))!=1 && (Inicio + 12) >
time(NULL) && !gvEncerrar)
{
FD_ZERO(&FdRead);
FD_SET(gvSocket,&FdRead);
t_val.tv_sec=0;
t_val.tv_usec=0;
Application->ProcessMessages();
}
if(Ret != 1)
return false;
if(recvfrom(gvSocket,Buffer,Size,0,(LPSOCKADDR)&SockAddr,&LenSockAddr)!=Size)
return false;
if(*Destino == 0)
{
*Destino = SockAddr.sin_addr.s_addr;
}
else
if(*Destino != SockAddr.sin_addr.s_addr)
return false;
memcpy(T,Buffer,Size);
return true;
}
int WriteBufferUdp(unsigned long Destino,void *T,int Size)
{
SOCKADDR_IN SockAddr;
int Sent;
Application->ProcessMessages();
SockAddr.sin_family = AF_INET;
SockAddr.sin_port = gvPortUdp;
SockAddr.sin_addr.s_addr = Destino;
Sent = sendto(gvSocket,(char
*)T,Size,0,(LPSOCKADDR)&SockAddr,sizeof(SockAddr));
if(Sent != Size)
return false;
else
return true;
}
void InicializaTCPIP()
{
WORD wVersionRequested;
WSADATA wsaData;
IN_ADDR In;
PSERVENT PServent;
SOCKADDR_IN SockAddrIn;
wVersionRequested = MAKEWORD( 1, 1 );
if(WSAStartup( wVersionRequested, &wsaData ))
{
ShowMessage("Erro na inicializao do TCP/IP");
Application->Terminate();
return;
}
// Get the port on service file
if((PServent=getservbyname("your_service_name","udp"))==NULL)
{
ShowMessage("Erro obtendo port do servi transurb/udp");
Application->Terminate();
return;
}
gvPortUdp = PServent->s_port;
sprintf(StrAux,"Servi transurb/udp port:%d",ntohs(gvPortUdp));
Log(StrAux);
// Open de Socket
if((gvSocket = socket(AF_INET,SOCK_DGRAM,0))==INVALID_SOCKET)
{
ShowMessage("Erro na criao do socket");
Application->Terminate();
return;
}
Log("Socket criado com sucesso");
// Do the bind
SockAddrIn.sin_family = AF_INET;
SockAddrIn.sin_port = gvPortUdp;
SockAddrIn.sin_addr.s_addr = NULL;
if(bind(gvSocket,(LPSOCKADDR)&SockAddrIn,sizeof(SockAddrIn))==SOCKET_ERROR)
{
ShowMessage("Erro no bind do socket");
Application->Terminate();
return;
}
Log("Bind do socket com sucesso");
}
判断windows的Desktop及其它目录
使用API函数SHGetSpecialFolder。shlobj.h里有SHGetSpecialFolder的原型声明。这个函数可以帮我们找到windows的Desktop目录、启动目录、我的文档目录等。
SHGetSpecialFolder需要三个参数。 第一个参数是HWND,它指定了"所有者窗口":在调用这个函数时可能出现的对话框或消息框。第二个参数是一个整数id,决定哪个目录是待查找目录,它的取值可能是:
CSIDL_BITBUCKET 回收站
CSIDL_CONTROLS 控制面板
CSIDL_DESKTOP Windows 桌面desktop
CSIDL_DESKTOPDIRECTORY desktop的目录
CSIDL_DRIVES 我的电脑
CSIDL_FONTS 字体目录
CSIDL_NETHOOD 网上邻居
CSIDL_NETWORK 网上邻居virtual folder
CSIDL_PERSONAL 我的文档
CSIDL_PRINTERS 打印机
CSIDL_PROGRAMS 程序组
CSIDL_RECENT 大多数最近打开的文档列一
CSIDL_SENDTO “发送到”菜单项
CSIDL_STARTMENU 任务条启动菜单项
CSIDL_STARTUP 启动目录
CSIDL_TEMPLATES 临时文档
最后一个参数是pidl地址。SHGetSpecialFolderLocation把地址写到pidl。
下面的代码演示了怎样使用SHGetSpecialFolderLocation:
//----------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
LPITEMIDLIST pidl;
LPMALLOC pShellMalloc;
char szDir[MAX_PATH];
if(SUCCEEDED(SHGetMalloc(&pShellMalloc)))
{
if(SUCCEEDED(SHGetSpecialFolderLocation(NULL,
CSIDL_DESKTOPDIRECTORY,
&pidl)))
{
// 如果成功返回true
if(SHGetPathFromIDList(pidl, szDir))
{
Label1->Caption = szDir;
}
pShellMalloc->Free(pidl);
}
pShellMalloc->Release();
}
}
//----------------------------------------------------------------------
注意: 有些目录是空的。有些特定的目录在这个文件系统上并没有一个相应的目录。
取得本地internet机器的名字及IP地址
一、下面的例子使用 Winsock API 取得本地主机的名字及地址
void __fastcall TForm1::Button1Click(TObject *Sender)
{
hostent *p;
char s[128];
char *p2;
//Get the computer name
gethostname(s, 128);
p = gethostbyname(s);
Memo1->Lines->Add(p->h_name);
//Get the IpAddress
p2 = inet_ntoa(*((in_addr *)p->h_addr));
Memo1->Lines->Add(p2);
}
void __fastcall TForm1::FormCreate(TObject *Sender)
{
WORD wVersionRequested;
WSADATA wsaData;
//Start up WinSock
wVersionRequested = MAKEWORD(1, 1);
WSAStartup(wVersionRequested, &wsaData);
}
void __fastcall TForm1::FormDestroy(TObject *Sender)
{
WSACleanup();
} <