comm1RxChar这是哪个spcomm控件下载的事件

串口的奇怪问题:发送数据竟然触发EV_RXCHAR事件_Java123社区微信号:java123msg |||[][]当前位置: &
& 串口的奇怪问题:发送数据竟然触发EV_RXCHAR事件本人是菜鸟 想在上位机串口程序中做一个串口检测函数,定期(100MS)向单片机下位机发送一个特定数据,等待单片机发上来的数据,如果一段时间(50MS)没有收到单片机发上来的数据本人是菜鸟 想在上位机串口程序中做一个串口检测函数,定期(100MS)向单片机下位机发送一个特定数据,等待单片机发上来的数据,如果一段时间(50MS)没有收到单片机发上来的数据那么判定串口出问题了则重新初始化一次串口。接收数据是通过事件触发方式完成的。由于是调试我没有将串口跟单片机相连,也就是说串口不可能有接收数据。但是现在遇到一个很奇怪的问题,每当我发送数据过后总会有EV_RXCHAR事件。由于串口根本就没和单片机连接这个接收数据是哪里来的呢?停止发送数据以后EV_RXCHAR事件也不再有。可以肯定EV_RXCHAR事件跟发送数据有关。非常困惑。大概程序如下: COMSTAT
/***********************settimer的回调函数定时给串口发送数据***********************\void CALLBACK TimerProc(HWND hWnd,UINT nMsg,UINT nTimerid,DWORD dwTime){
TRACE(&CSerialPort thread started, ID is 0x%X.\n&, m_Thread-&m_nThreadID); CMainFrame* pMainW
pMainWnd=(CMainFrame*)AfxGetApp()-&m_pMainW KillTimer(hWnd,nTimerid); pMainWnd-&m_VGSPrinter.Senddata();//给串口发送数据}
// event from port./***********************打开串口监视线程***********************\BOOL CSerialPort::StartMonitoring(){ if (!(m_Thread = AfxBeginThread(CommThread, this))) return TRUE;
for (;;)& /***********************串口事件处理函数***********************\UINT CSerialPort::CommThread(LPVOID pParam){ // Cast the void pointer passed to the thread back to // a pointer of CSerialPort class
// the higest priority and be serviced first. CSerialPort *port = (CSerialPort*)pP // Set the status variable in the dialog class to // TRUE to indicate the thread is running. port-&m_bThreadAlive = TRUE;
// Misc. variables DWORD BytesTransfered = 0;&
DWORD Event = 0; DWORD CommEvent = 0; DWORD dwError = 0; BOOL
bResult = TRUE;
return FALSE;
// Clear comm buffers at startup if (port-&m_hComm)
// check if the port is opened
PurgeComm(port-&m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT); // begin forever loop.
This loop will run as long as the thread is alive. {
bResult = WaitCommEvent(port-&m_hComm, &Event, &port-&m_ov);
if (!bResult) &
switch (dwError = GetLastError())&
case ERROR_IO_PENDING:
port-&ProcessErrorMessage(&WaitCommEvent()&);
Event = WaitForMultipleObjects(3, port-&m_hEventArray, FALSE, 50);//
bResult = ClearCommError(port-&m_hComm, &dwError, &comstat);
if (comstat.cbInQue == 0)
} // end if bResult
// Main wait function.
This function will normally block the thread
// until one of nine events occur that require action.
if(Event == WAIT_TIMEOUT)
AfxMessageBox(&等待串口&);
CMainFrame* pMainW
pMainWnd=(CMainFrame*)AfxGetApp()-&m_pMainW &
pMainWnd-&m_VGSPrinter.m_bSerialPortOpened = FALSE;//串口响应失败将串口状态置为FALS&
switch (Event)
// Shutdown event.
This is event zero so it will be
CloseHandle(port-&m_hComm);
port-&m_hComm=NULL;
port-&m_bThreadAlive = FALSE;
// Kill this thread.
break is not needed, but makes me feel better.
AfxEndThread(100);
case 1: // read event
GetCommMask(port-&m_hComm, &CommEvent);
if (CommEvent & EV_RXCHAR)//程序运行只要是发送数据便会触发这里
AfxMessageBox(&有接收数据&);
if (CommEvent & EV_CTS)
::SendMessage(port-&m_pOwner-&m_hWnd, WM_COMM_CTS_DETECTED, (WPARAM) 0, (LPARAM) port-&m_nPortNr);
if (CommEvent & EV_BREAK)
::SendMessage(port-&m_pOwner-&m_hWnd, WM_COMM_BREAK_DETECTED, (WPARAM) 0, (LPARAM) port-&m_nPortNr);
if (CommEvent & EV_ERR)
::SendMessage(port-&m_pOwner-&m_hWnd, WM_COMM_ERR_DETECTED, (WPARAM) 0, (LPARAM) port-&m_nPortNr);
if (CommEvent & EV_RING)
::SendMessage(port-&m_pOwner-&m_hWnd, WM_COMM_RING_DETECTED, (WPARAM) 0, (LPARAM) port-&m_nPortNr);
::SendMessage(port-&m_pOwner-&m_hWnd, WM_COMM_RXFLAG_DETECTED, (WPARAM) 0, (LPARAM) port-&m_nPortNr);共2页顶一下(0)0%踩一下(0)0%------分隔线------上一篇: 下一篇: 栏目列表推荐内容如题,程序如下: if(INVALID_HANDLE_VALUE =http://topic.csdn.net/u/2012052...用MFC实现串口编程
用MFC实现串口编程
(作者:付杰
本文详细介绍了串行通信的基本原理,以及在Windows NT、Win98环境下用MFC实现串口(COM)通信的方法:使用ActiveX控件或Win API.并给出用Visual
C++6.0编写的相应MFC32位应用程序。关键词:串行通信、VC++6.0、ActiveX控件、Win API、MFC32位应用程序、事件驱动、非阻塞通信、多线程.
  在Windows应用程序的开发中,我们常常需要面临与外围数据源设备通信的问题。计算机和单片机(如MCS-51)都具有串行通信口,可以设计相应的串口通信程序,完成二者之间的数据通信任务。
  实际工作中利用串口完成通信任务的时候非常之多。已有一些文章介绍串口编程的文章在计算机杂志上发表。但总的感觉说来不太全面,特别是介绍32位下编程的更少,且很不详细。笔者在实际工作中积累了较多经验,结合硬件、软件,重点提及比较新的技术,及需要注意的要点作一番探讨。希望对各位需要编写串口通信程序的朋友有一些帮助。
一.串行通信的基本原理
串行端口的本质功能是作为CPU和串行设备间的编码转换器。当数据从 CPU经过串行端口发送出去时,字节数据转换为串行的位。在接收数据时,串行的位被转换为字节数据。
在Windows环境(Windows NT、Win98、Windows2000)下,串口是系统资源的一部分。
应用程序要使用串口进行通信,必须在使用之前向操作系统提出资源申请要求(打开串口),通信完成后必须释放资源(关闭串口)。
二.串口信号线的接法&&&&&&&&&&&&&&&&&&&&&&
一个完整的RS-232C接口有22根线,采用标准的25芯插头座(或者9芯插头座)。25芯和9芯的主要信号线相同。以下的介绍是以25芯的RS-232C为例。
①主要信号线定义:
     2脚:发送数据TXD; 3脚:接收数据RXD; 4脚:请求发送RTS; 5脚:清除发送CTS;
     6脚:数据设备就绪DSR;20脚:数据终端就绪DTR; 8脚:数据载波检测DCD;
1脚:保护地;   7脚:信号地。
②电气特性:
数据传输速率最大可到20K bps,最大距离仅15m.
注:看了微软的MSDN 6.0,其Windows API中关于串行通讯设备(不一定都是串口RS-232C或RS-422或RS-449)速率的设置,最大可支持到RS_256000,即256K
bps! 也不知道到底是什么串行通讯设备?但不管怎样,一般主机和单片机的串口通讯大多都在9600 bps,可以满足通讯需求。
③接口的典型应用:
大多数计算机应用系统与智能单元之间只需使用3到5根信号线即可工作。这时,除了TXD、RXD以外,还需使用RTS、CTS、DCD、DTR、DSR等信号线。(当然,在程序中也需要对相应的信号线进行设置。)
   以上接法,在设计程序时,直接进行数据的接收和发送就可以了,不需要对信号线的状态进行判断或设置。(如果应用的场合需要使用握手信号等,需要对相应的信号线的状态进行监测或设置。)
三.16位串口应用程序的简单回顾&&&&&&&&&&&&&&&&&&&
  16位串口应用程序中,使用的16位的Windows API通信函数:
① OpenComm() 打开串口资源,并指定输入、输出缓冲区的大小(以字节计);
   CloseComm() 关闭串口;
   例:int idComD
idComDev = OpenComm(&COM1&, );
CloseComm(idComDev);
② BuildCommDCB() 、setCommState()填写设备控制块DCB,然后对已打开的串口进行参数配置;
   例:DCB
BuildCommDCB(&COM1:2400,n,8,1&, &dcb);
SetCommState(&dcb);
③ ReadComm 、WriteComm()对串口进行读写操作,即数据的接收和发送.
   例:char *m_pR
     ReadComm(idComDev,m_pRecieve,count);
     Char wr[30]; int count2;
     WriteComm(idComDev,wr,count2);
16位下的串口通信程序最大的特点就在于:串口等外部设备的操作有自己特有的API函数;而32位程序则把串口操作(以及并口等)和文件操作统一起来了,使用类似的操作。&&&&&&&&&&&
四.在MFC下的32位串口应用程序&&&&&&&&&&&&&&&&&&&&&&&&
32位下串口通信程序可以用两种方法实现:利用ActiveX控件;使用API 通信函数。
使用ActiveX控件,程序实现非常简单,结构清晰,缺点是欠灵活;使用API 通信函数的优缺点则基本上相反。
以下介绍的都是在单文档(SDI)应用程序中加入串口通信能力的程序。
㈠ 使用ActiveX控件:
VC++ 6.0提供的MSComm控件通过串行端口发送和接收数据,为应用程序提供串行通信功能。使用非常方便,但可惜的是,很少有介绍MSComm控件的资料。
  ⑴.在当前的Workspace中插入MSComm控件。
   Project菜单------&Add to Project----&Components
and Controls-----&Registered
   ActiveX Controls---&选择Components: Microsoft
Communications Control,
   version 6.0 插入到当前的Workspace中。
结果添加了类CMSComm(及相应文件:mscomm.h和mscomm.cpp )。
  ⑵.在MainFrm.h中加入MSComm控件。
protected:
   CMSComm m_ComP
在Mainfrm.cpp::OnCreare()中:
  DWORD style=WS_VISIBLE|WS_CHILD;
(!m_ComPort.Create(NULL,style,CRect(0,0,0,0),this,ID_COMMCTRL)){
TRACE0(&Failed to create OLE Communications
Control\n&);
return -1;   // fail to create
  ⑶.初始化串口
m_ComPort.SetCommPort(1);  //选择COM?
m_ComPort. SetInBufferSize(1024); //设置输入缓冲区的大小,Bytes
m_ComPort. SetOutBufferSize(512); //设置输入缓冲区的大小,Bytes//
if(!m_ComPort.GetPortOpen()) //打开串口
m_ComPort.SetPortOpen(TRUE);
m_ComPort.SetInputMode(1); //设置输入方式为二进制方式
m_ComPort.SetSettings(&9600,n,8,1&); //设置波特率等参数
m_ComPort.SetRThreshold(1); //为1表示有一个字符引发一个事件
     m_ComPort.SetInputLen(0);
⑷.捕捉串口事项。
MSComm控件可以采用轮询或事件驱动的方法从端口获取数据。我们介绍比较使用的事件驱动方法:有事件(如接收到数据)时通知程序。在程序中需要捕获并处理这些通讯事件。
在MainFrm.h中:
protected:
afx_msg void OnCommMscomm();
DECLARE_EVENTSINK_MAP()
在MainFrm.cpp中:
BEGIN_EVENTSINK_MAP(CMainFrame,CFrameWnd )  
ON_EVENT(CMainFrame,ID_COMMCTRL,1,OnCommMscomm,VTS_NONE)
           //映射ActiveX控件事件
END_EVENTSINK_MAP()
⑸.串口读写.
&完成读写的函数的确很简单,GetInput()和SetOutput()就可。两个函数的原型是:
VARIANT GetInput();及 void SetOutput(const VARIANT&
newValue);都要使用VARIANT类型(所有Idispatch::Invoke的参数和返回值在内部都是作为VARIANT对象处理的)。
无论是在PC机读取上传数据时还是在PC机发送下行命令时,我们都习惯于使用字符串的形式(也可以说是数组形式)。查阅VARIANT文档知道,可以用BSTR表示字符串,但遗憾的是所有的BSTR都是包含宽字符,即使我们没有定义_UNICODE_UNICODE也是这样!
WinNT支持宽字符, 而Win95并不支持。为解决上述问题,我们在实际工作中使用CbyteArray,给出相应的部分程序如下:
    void CMainFrame::OnCommMscomm(){
     VARIANT vR  
if(m_commCtrl.GetCommEvent()==2) {      
k=m_commCtrl.GetInBufferCount(); //接收到的字符数目
vResponse=m_commCtrl.GetInput(); //read
SaveData(k,(unsigned char*) vResponse.parray-&pvData);
} // 接收到字符,MSComm控件发送事件 }
   。。。。。 // 处理其他MSComm控件
void CMainFrame::OnCommSend() {
。。。。。。。。 // 准备需要发送的命令,放在TxData[]中
array.RemoveAll();
array.SetSize(Count);
for(i=0;i&Ci++)
array.SetAt(i, TxData[i]);
   m_ComPort.SetOutput(COleVariant(array)); // 发送数据
请大家认真关注第⑷、⑸中内容,在实际工作中是重点、难点所在。&&&&&&&&&&&&&&&&&&&&
㈡ 使用32位的API
通信函数:&&&&&&&&&&&&&&&&&&
可能很多朋友会觉得奇怪:用32位API函数编写串口通信程序,不就是把16位的API换成32位吗?16位的串口通信程序可是多年之前就有很多人研讨过了……
此文主要想介绍一下在API串口通信中如何结合非阻塞通信、多线程等手段,编写出高质量的通信程序。特别是在CPU处理任务比较繁重、与外围设备中有大量的通信数据时,更有实际意义。
⑴.在中MainFrm.cpp定义全局变量
HANDLE    hC // 准备打开的串口的句柄
HANDLE    hCommWatchT//辅助线程的全局函数
⑵.打开串口,设置串口
hCom =CreateFile( &COM2&, GENERIC_READ |
GENERIC_WRITE, // 允许读写
         0,          // 此项必须为0
         NULL,         // no security attrs
         OPEN_EXISTING,    //设置产生方式
         FILE_FLAG_OVERLAPPED, // 我们准备使用异步通信
         NULL );
请大家注意,我们使用了FILE_FLAG_OVERLAPPED结构。这正是使用API实现非阻塞通信的关键所在。
ASSERT(hCom!=INVALID_HANDLE_VALUE); //检测打开串口操作是否成功
SetCommMask(hCom, EV_RXCHAR|EV_TXEMPTY );//设置事件驱动的类型
SetupComm( hCom, ) ; //设置输入、输出缓冲区的大小
PurgeComm( hCom, PURGE_TXABORT | PURGE_RXABORT |
PURGE_TXCLEAR
           | PURGE_RXCLEAR ); //清干净输入、输出缓冲区
COMMTIMEOUTS CommTimeO //定义超时结构,并填写该结构
   …………
SetCommTimeouts( hCom, &CommTimeOuts ) ;//设置读写操作所允许的超时
DCB     // 定义数据控制块结构
GetCommState(hCom, &dcb ) ; //读串口原来的参数设置
dcb.BaudRate =9600; dcb.ByteSize =8; dcb.Parity =
dcb.StopBits = ONESTOPBIT ;dcb.fBinary = TRUE ;dcb.fParity
SetCommState(hCom, &dcb ) ; //串口参数配置
上述的COMMTIMEOUTS结构和DCB都很重要,实际工作中需要仔细选择参数。
⑶启动一个辅助线程,用于串口事件的处理。
Windows提供了两种线程,辅助线程和用户界面线程。区别在于:辅助线程没有窗口,所以它没有自己的消息循环。但是辅助线程很容易编程,通常也很有用。
在次,我们使用辅助线程。主要用它来监视串口状态,看有无数据到达、通信有无错误;而主线程则可专心进行数据处理、提供友好的用户界面等重要的工作。
hCommWatchThread=
     CreateThread( (LPSECURITY_ATTRIBUTES) NULL, //安全属性
         0,//初始化线程栈的大小,缺省为与主线程大小相同
         (LPTHREAD_START_ROUTINE)CommWatchProc, //线程的全局函数
         GetSafeHwnd(), //此处传入了主框架的句柄
         0, &dwThreadID );
  ASSERT(hCommWatchThread!=NULL);
⑷为辅助线程写一个全局函数,主要完成数据接收的工作。
请注意OVERLAPPED结构的使用,以及怎样实现了非阻塞通信。
UINT CommWatchProc(HWND hSendWnd){
  DWORD dwEvtMask=0 ;
  SetCommMask( hCom, EV_RXCHAR|EV_TXEMPTY );//有哪些串口事件需要监视?
  WaitCommEvent( hCom, &dwEvtMask, os );// 等待串口通信事件的发生
  检测返回的dwEvtMask,知道发生了什么串口事件:
  if ((dwEvtMask & EV_RXCHAR) == EV_RXCHAR){ // 缓冲区中有数据到达
  COMSTAT ComS DWORD dwL
  ClearCommError(hCom, &dwErrorFlags, &ComStat )
  dwLength = ComStat.cbInQ //输入缓冲区有多少数据?
  if (dwLength & 0) {
BOOL fReadS  
  fReadStat = ReadFile( hCom, lpBuffer,dwLength,
&dwBytesRead,
            &READ_OS( npTTYInfo ) ); //读数据
注:我们在CreareFile()时使用了FILE_FLAG_OVERLAPPED,现在ReadFile()也必须使用
  LPOVERLAPPED结构.否则,函数会不正确地报告读操作已完成了.
    使用LPOVERLAPPED结构, ReadFile()立即返回,不必等待读操作完成,实现非阻塞
    通信.此时, ReadFile()返回FALSE, GetLastError()返回ERROR_IO_PENDING.
if (!fReadStat){
   if (GetLastError() == ERROR_IO_PENDING){
     while(!GetOverlappedResult(hCom,
       &READ_OS( npTTYInfo ), & dwBytesRead, TRUE
       dwError = GetLastError();
       if(dwError == ERROR_IO_INCOMPLETE) continue;
             //缓冲区数据没有读完,继续
       …… ……      
   ::PostMessage((HWND)hSendWnd,WM_NOTIFYPROCESS,0,0);//通知主线程,串口收到数据  }
  所谓的非阻塞通信,也即异步通信。是指在进行需要花费大量时间的数据读写操作(不仅仅是指串行通信操作)时,一旦调用ReadFile()、WriteFile(),
就能立即返回,而让实际的读写操作在后台运行;相反,如使用阻塞通信,则必须在读或写操作全部完成后才能返回。由于操作可能需要任意长的时间才能完成,于是问题就出现了。
非常阻塞操作还允许读、写操作能同时进行(即重叠操作?),在实际工作中非常有用。
要使用非阻塞通信,首先在CreateFile()时必须使用FILE_FLAG_OVERLAPPED;然后在
ReadFile()时lpOverlapped参数一定不能为NULL,接着检查函数调用的返回值,调用GetLastError(),看是否返回ERROR_IO_PENDING。如是,最后调用GetOverlappedResult()返回重叠操作(overlapped
operation)的结果;WriteFile()的使用类似。
⑸.在主线程中发送下行命令。
BOOL  fWriteS char szBuffer[count];
       …………//准备好发送的数据,放在szBuffer[]中
fWriteStat = WriteFile(hCom, szBuffer, dwBytesToWrite,
           &dwBytesWritten, &WRITE_OS( npTTYInfo
) ); //写数据
注:我们在CreareFile()时使用了FILE_FLAG_OVERLAPPED,现在WriteFile()也必须使用  
LPOVERLAPPED结构.否则,函数会不正确地报告写操作已完成了.
   使用LPOVERLAPPED结构,WriteFile()立即返回,不必等待写操作完成,实现非阻塞 通信.此时,
WriteFile()返回FALSE, GetLastError()返回ERROR_IO_PENDING.
int err=GetLastError();
if (!fWriteStat) {
   if(GetLastError() == ERROR_IO_PENDING){
    while(!GetOverlappedResult(hCom, &WRITE_OS(
npTTYInfo ),
           &dwBytesWritten, TRUE )) {
      dwError = GetLastError();
      if(dwError == ERROR_IO_INCOMPLETE){
           // normal result if not finished
        dwBytesSent += dwBytesW }
综上,我们使用了多线程技术,在辅助线程中监视串口,有数据到达时依靠事件驱动,读入数据并向主线程报告(发送数据在主线程中,相对说来,下行命令的数据总是少得多);并且,WaitCommEvent()、ReadFile()、WriteFile()都使用了非阻塞通信技术,依靠重叠(overlapped)读写操作,让串口读写操作在后台运行。
依托vc6.0丰富的功能,结合我们提及的技术,写出有强大控制能力的串口通信应用程序。就个人而言,我更偏爱API技术,因为控制手段要灵活的多,功能也要强大得多。下次自动登录
现在的位置:
& 综合 & 正文
C++Builder 资料库
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 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 T 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 GetSystemInfo (&systeminfo); Memo1→Lines→Add(撃?腃PU类型是:敚玈tring( systeminfo.dwProcessorType )); } 运行它,点击Test试试,CPU型号出来了吧!   2.检测内存状态   获得内存状态的方法和CPU型号差不多,只是他用到的是另外一个API:GlobalMemoryStatus。   其中,成员dwTotalPhys用来获得物理内存总量,而dwAvailPhys顾名思义是有效物理内存的意思。我们只要把下面几行代码加到上面程序的后面就可以了(不用重做,下同): //获得内存状态 MEMORYSTATUS memory.dwLength =sizeof(memory); //初始化 GlobalMemoryStatus(&memory); Memo1→Lines→Add(撃?奈锢砟诖媸?Mb):敚玈tring(int(memory.dwTotalPhys /))); Memo1→Lines→Add(撈渲锌捎媚诖媸?Kb):敚玈tring(int( memory. /1024)));   怎么样,看出点门道了么?两段程序的格式几乎一模一样,其实,GetSystemInfoGlobalMemoryStatus还可以获得许多其他有关CPU和内存的信息,就按照上面的格式去套就行了,更详细的资料可以去看C++ Builder4的Help。   3. 检测可用硬盘空间 好了,经过前面两个简单问题的热身,我们来处理一个稍微复杂的问题:我们知道安装程序大都有一个检测硬盘空间的过程,那么这是怎么实现的呢?他用到的是API函数GetDiskFreeSpace,这个函数输入一个参数:目标盘的路径;返回四个参数,依次是每簇的扇区数、每扇区的字节数、空闲的簇数、总簇数。假如我们需要检测C盘的总容量和可用容量,那么可以把以下代码加到上面的程序中: //获得C盘可用空间 DWORD sector,byte,cluster, long int freespace, GetDiskFreeSpace(揅:?&sector,&byte,&free,&cluster); //获得返回参数 totalspace=int(cluster)*int(byte)*int(sector)/; //计算总容量 freespace=int(free)*int(byte)*int(sector)/; //计算可用空间 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.LP//从Message中取得参数 this-&HandleCmdLineFile(lpCmdLine);//处理命令行的参数
} void __fastcall TSomeForm::SearchDocumentNode(TMessage Message) {//响应查找消息 //Message中的参数在此处不需要。 this-&SearchNode();
} void __fastcall TSomeForm::GetWindowMinMaxInfo(TWMGetMinMaxInfo Messag e) {//设置主窗口的最小尺寸 MINMAXINFO *MinMaxInfo=Message.MinMaxI MinMaxInfo-&ptMinTrackSize.x=400; MinMaxInfo-&ptMinTrackSize.y=300;
} 其中: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=lpsWinT StatusForm- &ShowModal(); gi_WindowActive=0; delete StatusF gi_RequestClose=0; } /* 定义一个线程实例指针 */ TFormThread *FormT /********************************************** * 输出函数代码实现部分 * 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)hW memset(lpsWinTitle,0,sizeof(lpsWinTitle)); strncpy(lpsWinTitle,WinTitle,sizeof(lpsWinTitle)-1); if (AVIType &0 && AVIType& =8) gi_AVIType=AVIT FormThread=new TFormThread(true); FormThread- &Priority = tpN FormThread- &Resume(); } __declspec(dllexport) int pascal GetStatus(int ai_CloseWin){ if (gi_Canceled) if (gi_WindowActive){ gi_RequestClose=1; while(gi_RequestClose); } return gi_C } __declspec(dllexport) int pascal CloseStatusWindow(){ if (gi_WindowActive){ gi_RequestClose=1; while(gi_RequestClose); } return gi_C }   2.窗体StatusForm的代码: TStatusForm *StatusF //----------------------------------- extern int gi_C extern int gi_AVIT 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=mrC } //----------------------------------- // 激活动画,在FORMCREATE事件中 void __fastcall TStatusForm:: FormCreate(TObject *Sender) { Animate1- &CommonAVI=g_CommonAVI[gi_AVIType]; Animate1- &Active = } //----------------------------------- extern int gi_RequestC // 定时器事件检测到结束标志关闭窗体 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属性即可。这段程序如下: RECT//Line1 HWNDhWnd=(HWND) (atol(ParamStr(2).c_str()));//Line2 ::GetClientRect(hWnd,&rc);//Line3 ParentWindow=hW//Line4 Left=rc.//Line5 Top=rc.//Line6 Width=rc.right-rc.//Line7 Height=rc.bottom-rc.//Line8   在上面的程序片段中,第2行语句是将传递过来的第2个参数转换成窗口句柄;然后,第3行语句利用这个窗口句柄,调用API函数以获得该窗口的客户区域;第4行语句将选中的屏幕保护程序的父窗口设置为指定的窗口;余下的语句是将该程序的窗口大小设置成副窗口的客户区大小。这一程序片段的位置应该是在窗体的OnCreate事件处理中。   需要说明的是,这种类型(包括第三步介绍的窗体)的窗体样式应是: FormStyle=fsStayOnT   窗体边界的样式应为: BorderStyle=bsN 当然,这时也不需要鼠标图形,因此,可以将鼠标的形状设为crNone: Cursor=crN   二、初始化参数的设置 单击"显示器属性"模块的"屏幕保护程序"页面中的"设置"按钮时,系统会启动指定的保护程序的初始值设置对话框,这时传递过来的命令行参数是:"/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();
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();
...... } }   对于其他的消息仅仅是调用Close()函数来关闭应用程序即可。应用这种消息处理方式时,必须要类定义时进行消息映射,不然的话,就要在相应的消息响应中进行处理(使用一定的布尔变量,就可以与第一步合用一个窗体)。   与第一步类似,在该步骤中,也不需要具体的鼠标指针的形状,因此,将鼠标指针设为crNone: Cursor=crN   四、修改项目源文件   在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(); deleteScrC 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(); deleteScreenS 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 BYTE WORD tot_ WORD WORD frag_ BYTE BYTE WORD DWORD DWORD /* 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 SourP WORD DestP DWORD SeqNo; DWORD AckNo; BYTE HL BYTE F WORD W WORD ChkS WORD UrgP /* Put options here. */ };
一、说明 使用UDP时,直接使用API代替控件。
第一个程序(ReadBufferUdp)使用来接收到缓存中。
"Destino"变量非常重要,如果你从其他地方接收数据到Buffer,你必须设置Destino = 0 并且在以后执行的时候赋值你将要发送的包的地址给它(after the execution it will have the address which send you the packet.)。 如果你只想从一个指定的地址接收数据,你必须设置变量Destino = &address&.
"gvEncerrar" 用来中止处理过程。(gvEncerrar被设置为全局变量。)
超时时间设置。"Inicio + 12" = 12 sec of timeout.
第三个程序是用来准备WinSock程序。
int ReadBufferUdp(unsigned long *Destino,void *T,int Size) { char Buffer[128]; SOCKADDR_IN SockA int LenSockAddr=sizeof(SOCKADDR_IN); fd_set FdR struct timeval t_ int R time_t Inicio = time(NULL);
Application-&ProcessMessages(); if(gvEncerrar)
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)
if(recvfrom(gvSocket,Buffer,Size,0,(LPSOCKADDR)&SockAddr,&LenSockAddr)!=Size)
if(*Destino == 0) { *Destino = SockAddr.sin_addr.s_ } else if(*Destino != SockAddr.sin_addr.s_addr)
memcpy(T,Buffer,Size);
int WriteBufferUdp(unsigned long Destino,void *T,int Size) { SOCKADDR_IN SockA int S
Application-&ProcessMessages(); SockAddr.sin_family = AF_INET; SockAddr.sin_port = gvPortU SockAddr.sin_addr.s_addr = D Sent = sendto(gvSocket,(char *)T,Size,0,(LPSOCKADDR)&SockAddr,sizeof(SockAddr)); if(Sent != Size)
void InicializaTCPIP() {
WORD wVersionR WSADATA wsaD IN_ADDR In; PSERVENT PS SOCKADDR_IN SockAddrIn; wVersionRequested = MAKEWORD( 1, 1 );
if(WSAStartup( wVersionRequested, &wsaData )) { ShowMessage("Erro na inicializao do TCP/IP"); Application-&Terminate();
// Get the port on service file if((PServent=getservbyname("your_service_name","udp"))==NULL) { ShowMessage("Erro obtendo port do servi transurb/udp"); Application-&Terminate();
} gvPortUdp = PServent-&s_ 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();
} Log("Socket criado com sucesso");
// Do the bind SockAddrIn.sin_family = AF_INET; SockAddrIn.sin_port = gvPortU SockAddrIn.sin_addr.s_addr = NULL;
if(bind(gvSocket,(LPSOCKADDR)&SockAddrIn,sizeof(SockAddrIn))==SOCKET_ERROR)
{ ShowMessage("Erro no bind do socket"); Application-&Terminate();
} 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 LPMALLOC pShellM char szDir[MAX_PATH];
if(SUCCEEDED(SHGetMalloc(&pShellMalloc))) { if(SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_DESKTOPDIRECTORY, &pidl))) { // 如果成功返回true if(SHGetPathFromIDList(pidl, szDir)) { Label1-&Caption = szD }
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 wVersionR WSADATA wsaD
//Start up WinSock wVersionRequested = MAKEWORD(1, 1); WSAStartup(wVersionRequested, &wsaData); }
void __fastcall TForm1::FormDestroy(TObject *Sender) { WSACleanup(); }
用C++Builder创建数字签名
  如果你在网络上传递一份数据,但却存在着种种不安全的因素,使你对数据能否原封不动地到达目的地而心存疑惑,这时,你就可以给数据加上数字签名,从而使对方可以通过验证签名来检查你所传过去的数据是否已被他人修改。
  一、程序原理
  数字签名的工作原理还是比较简单的,它是根据你所提供的原始数据,经过复杂的算法,产生特定的数据签名,对方通过同样的过程也产生签名,如果数据已被修改,那么就不可能得到两份一模一样的签名,从而就可判断数据已被他人修改。编程人员利用Windows的CAPI接口,就可以实现数据的加密、解密和数字签名。  
  二、程序清单
  下面用C++ Builder的语句来看一下它的具体实现过程。   先来创建数字签名,假定其数据来自于一个文件。   //变量声明:   HCRYPTPROV hProv;   // CSP的句柄   HCRYPTHASH hHash;   // 散列的句柄   const int BUFFER=4096;   // 缓冲区大小常数   BYTE pBuffer[BUFFER];   // 存放读文件内容的缓冲区   BYTE pSignature[256];   // 存放签名的缓冲区   DWORD dSignatureLen=256;   // 签名的长度   TFileStream *sourceFile;   // 一个文件流   if(!CryptAcquireContext(&hProv,NULL,NULL,PROV-RSA-FULL,0))   // 连接默认的CSP,接受它的句柄放入hProv   {     // 错误处理   }   if(!CryptCreateHash(hProv,CALG-MD5,0,0,&hHash))   // 创建一个散列对象,得到它的句柄放入hHash   {     // 错误处理   }   do   {    dReadLen=sourceFile-&Read(pBuffer,BUFFER);    if(!CryptHashData(hHash,pBuffer,dReadLen,0))   // 根据文件的内容计算散列值    {     // 错误处理    }   }while(!(dReadLen&BUFFER));   if(!CryptSignHash(hHash,AT-SIGNATURE,NULL,0,pSignature,&dSignatureLen))   //使用私人密钥对散列值进行数字签名   //签名数据放入pSignature,长度放入dSignatureLen   // 错误处理   }   对基于文件的数据签名进行检验。   //变量声明:   HCRYPTPROV hProv;   // CSP的句柄   HCRYPTHASH hHash;   // 散列的句柄   HCRYPTKEY hPublicK       // 公共密钥的句柄   const int BUFFER=4096;     // 缓冲区大小常数   BYTE pBuffer[BUFFER];       // 存放读文件内容的缓冲区   TFileStream *sourceFile; // 一个文件流   BYTE pSignature[256];       // 上一段得到的签名的缓冲区   DWORD dSignatureLen;       // 上一段得到的签名的长度   if(!CryptAcquireContext(&hProv,NULL,NULL,PROV-RSA-FULL,0))   // 连接默认的CSP,接受它的句柄放入hProv   {     // 错误处理   }   if(!CryptGetUserKey(hProv,AT_SIGNATURE,&hPublicKey); // 得到公共密钥的句柄   {     // 错误处理   }   if(!CryptCreateHash(hProv,CALG-MD5,0,0,&hHash)) // 创建一个散列对象,得到它的句柄放入hHash   {     // 错误处理   }   do   {    dReadLen=sourceFile-&Read(pBuffer,BUFFER);    if(!CryptHashData(hHash,pBuffer,dReadLen,0))   // 根据文件的内容计算散列值    {     // 错误处理    }   }while(!(dReadLen&BUFFER));   if(!CryptVerifySignature(hHash,pSignature,dSignatureLen,hPublicKey,NULL,0))   {     if(GetLastError()==NTE-BAD-SIGNATURE) ShowMessage(″文件已被修改″);   }   else   {    ShowMessage(″文件没被修改″);   }
  以上是一个数字签名的简单实现,得到的签名数据可以单独保存,也可以分开保存。
用Enter 键 控 制 焦 点 切 换 的 方 法
在Windows 环 境 下, 要 使 一 个 控 件 取 得 焦 点, 可 在 该 控 件 上 用 鼠 标 单 击 一 下, 或 按Tab 键 将 焦 点 移 至 该 控 件 上。 这 种 控 制 焦 点 切 换 的 方 法 有 时 不 符 合 用 户 的 习 惯。 就 图 一 而 言, 用 户 就 希 望 用Enter 键, 控 制 焦 点 由Edit1 切 换 到 Edit2。 要 实 现 这 样 的 功 能 需 借 助WinAPI 函 数SendMessage 来 完 成。 方 法 是: 先 设Form1 的KeyPreview 属 性 为true, 然 后 在Form1 的OnKeyPress 事 件 中 加 入 如 下 的 代 码。 这 样, 用 户 就 可 以 通 过 按Enter, 键 控 制 焦 点 按 定 义 好 的Taborder 顺 序 来 移 动 了 ! void __fastcall TForm1::FormKeyPress(TObject *Sender, char &Key) { if(Key==VK_RETURN)
{ SendMessage(this- &Handle,WM_NEXTDLGCTL,0,0); Key=0; } }
拦 截 Windows 消 息
- --Borland C++ Builder的API后门
---- C++ Builder不愧为Borland公司的优秀产品,用它来开发Windows程序非常快捷高效,但在编程过程中你也会发现它的一些限制性,让你无法实现自己的想法。比如你无法在修改表单的系统菜单;比如使用跟踪栏时,你找不到StartTrack和EndTrack事件,而偏偏你的程序需要这两个事件。Windows API编程中,你就不会有这些麻烦,只需处理一下WM_SYSCOMMAND和WM_HSCROLL(或WM_VSCROLL)消息,就能实现上述功能。Windows API的缺点是编程十分麻烦,太多的时间要耗在细节上面,但它的功能却是最强大的。C++ Builder的VCL在功能上只是它的一个子集,因为VCL是在API的基础上封装的,封装时舍弃了一些不常用到的功能。但是程序员的想象力没有被封装,他们总怀着更大的热情去实现别出心裁的想法,修改系统菜单和给跟踪栏增加StartTrack和ndTrack事件只是其中的小把戏而已。可是VCL并没有这些功能,怎么办?
---- 幸好,Borland公司没有把路堵死,而是留了个后门--允许程序员自己拦截并处理Windows消息,就象API编程一样。于是,办法有了...
---- 拦截Windows消息需要以下几步: ---- 在表单头文件内(如Unit1.h) ---- 1. 在类声明中建立消息映射表,把某条消息的处理权交给自定义的消息处理函数。
BEGIN_MESSAGE_MAP MESSAGE_HANDLER(Windows消息名,TMessage,消息处理函数名) MESSAGE_HANDLER(...) END_MESSAGE_MAP(TForm)
---- 2. 在类声明的private区内声明消息处理函数。
private: // User declarations void __fastcall 消息处理函数名(TMessage &Message); 在表单文件内(如Unit1.cpp)
---- 3. 写出消息处理函数,在这里实现你需要的功能。比如 void __fastcall MainForm::OnWMHScroll (TMessage &Message) { ... // 在此加入你自己的代码 TForm::Dispatch(&Message); }
---- 1. 关于TMessage
---- TMessage是VCL预定义的结构,定义如下: struct TMessage { unsigned int M //消息 int WP //字参数 int LP //长字参数 int R //消息结果 };
---- 2. 关于TForm::Dispatch(&Message)
---- 自定义的消息处理函数末尾最好加一句TForm::Dispatch(&Message),这一句的作用是让消息继续传递下去。如果没有这一句,消息将被完全拦截,VCL类可能由于得不到消息而无法实现正常功能。
---- 实例一:修改系统菜单
---- 有一些程序,主窗口很小,菜单也没有,如果想加入关于或设置对话框,最好的办法是拿系统菜单开刀。Windows API编程中,修改系统菜单与实现其他功能一样,不太容易,也不会太难。但在C++ Builder中,表单类(TForm)没有提供有关系统菜单的任何属性与方法,实现其他功能易如反掌,而修改系统菜单似乎难于上青天。
---- 还好,Borland公司允许程序员自已处理Window消息,于是机会来了!
一、用Window API函数修改系统菜单
假定表单名为MainForm,设置MainForm::OnCreate()函数:
1. 用GetSystemMenu(MainForm-&Handle,false)取得系统菜单句柄;
2. 用AppendMenu,DeleteMenu,ModifyMenu函数修改系统菜单,把新的ID号赋于自定义的菜单项。 这时运行程序,可以看到系统菜单也被修改,但自定义的菜单项却不能被响应。
二、拦截WM_SYSCOMMAND消息以响应自定义的菜单项 在表单头文件内(如Unit1.h)
1. 在表单类定义末尾加入消息响应表,取得WM_SYSCOMMAND消息的处理权 BEGIN_MESSAGE_MAP MESSAGE_HANDLER(WM_SYSCOMMAND,TMessage,OnWMSysCommand) END_MESSAGE_MAP(TForm)
2. 在表单类定义的private区内加入消息处理函数声明 private: // User declarations void __fastcall OnWMSysCommand(TMessage& Message);
在表单文件内(如Unit1.h)
3. 写出消息响应函数 void __fastcall TForm1::OnWMSysCommand(TMessage& Message) { if(Message.WParam==ID_SysMenu_MyItem) { // Your Code Here, Do Something } TForm::Dispatch(&Message); }
三、完整程序示例
实例二:给跟踪栏增加OnStartTrack和OnEndTrack事件
当跟踪栏用于进度控制时,OnStartTrack和OnEndTrack很可能是你需要的事件。比如在控制多媒体播放进度的场合,当用户移动滑块时,你需要OnStartTrack事件让播放停止,需要OnEndTrack事件定位新的播放位置。但Borland公司没有提供这两个事件,我等编程爱好者只好自力更生,打拦截Windows消息的主意了。
一、拦截WM_HSCROLL消息,给跟踪栏增加OnStartTrack和OnEndTrack事件
在表单头文件内(如Unit.h)
1. 在表单类定义末尾加入消息响应表,把WM_HSCROLL消息处理权交给OnWMHScroll函数。 BEGIN_MESSAGE_MAP MESSAGE_HANDLER(WM_HSCROLL,TMessage,OnWMHScroll) END_MESSAGE_MAP(TForm)
2. 在表单类定义的private区内加入OnWMHScroll函数声明。 private: // User declarations void __fastcall OnWMHScroll(TMessage &Message);
3. 在表单类定义的private区内加入StartTrack和EndTrack函数声明。 private: // User declarations void __fastcall TrackBar1StartTrack(TObject *Sender); void __fastcall TrackBar1EndTrack(TObject *Sender);
在表单文件内(如Unit.cpp)
4. 写出OnWMHScroll函数,使它能根据消息参数调用StartTrack和EndTrack函数,在实际意义上产生OnStartTrack和OnEndTrack事件。
5. 写出StartTrack和EndTrack函数。
如果是垂直跟踪栏,把上面的WM_HSCROLL改为WM_VSCROLL即可。
二、完整程序示例
Borland C++ Builder编程中,拦截Windows消息是一项高级编程技术,能让你尽量挖掘Windows的潜力,尤其让曾用API编程的程序员感到心慰。拦截Windows消息是API尽情发挥的舞台,当VCL不能为你做什么时,请想起底层的API。
使用CommaText
有时需要一个方便的方法存放一个StringList,它只有简单的一行。例如,当你想使用一个INI文件,如何向一个INI文件中写入一行呢,使用CommaText 就能完成这个工作。
这里有个例子,功能是创建一个blah.ini文件,并写入一个如下形式的值:
[My Section] Memo1=(你在Memo1中输入的文字)
1.在Form1上有两个按钮btnLoad and btnSave和一个Memo1
2.还要加入: #include &inifiles.hpp&
3.定义变量: const String iniFile="blah.ini",iniSection="My Section",iniValue="Memo1";
4.保存按钮代码: void __fastcall TForm1::btnSaveClick(TObject *Sender) { TIniFile *ini=new IniFile(ExtractFilePath(Application-&ExeName)+iniFile); ini-&WriteString(iniSection,iniValue,Memo1-&Lines-&CommaText);
5.装载按钮代码: void __fastcall TForm1::btnLoadClick(TObject *Sender) { TIniFile *ini=new TIniFile(ExtractFilePath(Application-&ExeName)+iniFile); Memo1-&Lines-&CommaText=ini-&ReadString(iniSection,iniValue,"");
6.以下代码支持加载后对内容进行排序,到实际存储不变: void __fastcall TForm1::btnSortLoadClick(TObject *Sender) { TStringList *sl=new TStringL TIniFile *ini=new TIniFile(ExtractFilePath(Application-&ExeName)+iniFile); sl-&CommaText=ini-&ReadString(iniSection,iniValue,""); sl-&Sort(); Memo1-&Lines=
程序开始时先显示信息框
一、软件进入主窗口前,先显示一个信息框,告诉用户一些有关该软件的信息,比如软件名称,版本号等。该信息框在显示1~2秒后自动消失。 1.建立New Application,这时系统自动生成一个Form1,这作为主Form.
2.File-&New Form 建立一个新Form为Form2,这个作为信息框。
3.在Form2上添加组件TTimer(System控件条上),用于设定信息框的显示时间。 4.TTimer的事件OnTimer中加入:Form2-&Close();
5.在WinMain()函数中加入: Application-&CreateForm(__classid(TForm2), &Form2); Form2-&ShowModal( ); //这句要自己加入 Application-&Run(); 并且要把Form2的头文件Unit2.h包括到WinMain()所在的Project1.cpp中。
6.运行程序,将先显示Form2,显示时间由TTimer的Interval属性决定,1000是一秒。
二、软 件 封 面 的 实 现 现 代 软 件 设 计 的 流 行 做 法 是, 在 程 序 运 行 完 成 初 始 化 之 前, 先 调 用 一 幅 画 面 做 为 封 面, 通 常 是1/4 屏 幕 大 小, 显 示 一 下 软 件 的 名 称、 作 者、 版 本 等 信 息。 要 用C++ Builder 实 现 这 样 的 功 能, 方 法 很 简 单:
① 自 定 义 一 窗 体 类 TSplashForm, 将 其 设 置 成" 透 明 窗 口", 即 BorderIcons 下 的 所 有 选 项 均 置 成false, BorderStyle=bsNone,FormStyle=fsStayOnTop, Position=poScreenCenter;
② 在TSplashForm 窗 体 上 放 置 一TPanel( 相 当 于 图 形 的 镜 框);
③ 在TPanel 上 放 置 一TImage 控 件, 调 入 所 需 要 的 图 形;
④ 对WinMain 函 数 稍 加 修 改, 加 入 如 下 所 示 代 码 即 可。 需 要 指 出 的 是, 这 段 代 码 通 过 函 数 FindWindow, 搜 索 内 存 中 是 否 有 窗 口 标 题 为 "Demo" 应 用 程 序 存 在, 若 存 在, 则 退 出 程 序 的 运 行。 该 功 能 可 防 止 程 序 的 再 次 运 行。 在 某 些 场 合 这 样 设 计 是 必 须 的。
WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) { try { if(FindWindow(NULL,"Demo")!=0) { Application- &MessageBox (" 程 序 已 经 运 行!"," 警 告",MB_ICONSTOP); return 0; }
TSplashForm *splash=new TSplashForm(Application);
splash- &Show();
splash- &Update();
Application- &Initialize();
Application- &CreateForm(__classid(TForm1), &Form1);
splash- &Close();
Application- &Run();
catch (Exception &exception)
Application- &ShowException(&exception);
怎样获取程序的命令行参数?
你可以用下面的两种不同的技巧来解决这个问题。
技巧1:首先,也许是最简单的方法是调用VCL ParaStr()函数。你可使用ParamCount()函数来确定到底有多少个命令行参数传递给了应用程序。
ParamStr需要一个整数参数并且返回一个AnsiString对象。若参数为0,ParamStr 将返回可执行文件的全称路径。若参数为1,将返回程序名及第一个命令行参数。若参数为2,将返回第二个参数,等等。
作为一个实践,开启一个新的项目,在主窗口上放置5个Label,将下面的代码添加到窗口的构造函数中:
Label1-&Caption = ParamStr(0); Label2-&Caption = ParamStr(1); Label3-&Caption = ParamStr(2); Label4-&Caption = ParamStr(3); Label5-&Caption = ParamStr(4); 再运行程序。一般应能看到类似字符串:
E:/CBUILDER/PROJECTS/PROJECT1.EXE 如果没传递参数到程序,那么Label2到Label5是空字符串。关闭程序,从C++Builder菜单中选择 Run | Parameters。输入几个参数(-debug -testing -param)再次运行程序。你将看到:
E:/CBUILDER/PROJECTS/PROJECT1.EXE -debug -testing -param 提示: ParamStr 对目录中的空格能智能判断。为证实这点,把生成的EXE文件拷贝到Program Files目录下再运行它,你将会看到ParamStr(0)返回全路径,并包含空格。
技巧2:第二个方法就是调用GetCommandLine API函数。GetCommandLine不需要参数,并且返回一个C风格的char *,包含全部的命令行参数。你将不得不分解字符串以取得相关参数。
Label5-&Caption = AnsiString(GetCommandLine()); 运行后,Label5将为:
"E:/CBuilder/Projects/Project1.exe" -debug -testing -param
如何监视剪贴板
在Form1的.h的private加上: void __fastcall ClipboardChanged(TMessage& Msg);
在Form1的.h的public加上: BEGIN_MESSAGE_MAP  MESSAGE_HANDLER(WM_DRAWCLIPBOARD,TMessage,ClipboardChanged) END_MESSAGE_MAP(TForm)
在Form1的.cpp内加上: void __fastcall TForm1::ClipboardChanged(TMessage& Msg) {  POINT MouseP  GetCursorPos(&MousePos);  PopupMenu4-&PopupComponent=Form1;  PopupMenu4-&Popup(MousePos.x,MousePos.y); //一有变化,就弹出一个菜单,复制,剪切或清除都能引发此函数 }
在Form1的.cpp内有一个ToolButton void __fastcall TForm1::ToolButton9Click(TObject *Sender) {  static HWND LastH  static bool clip=  if(clip==true)  {   ToolButton9-&Down=   ChangeClipboardChain(Form1-&Handle,LastHandle); //结束监视  }  else  {   ToolButton9-&Down=   Clipboard()-&Clear();   Application-&Minimize();   LastHandle=SetClipboardViewer(Form1-&Handle); //启动监视  }  clip=! }
如何使用OnIdle事件
使用OnIdle事件随时监视剪贴板内容以改变弹出菜单的可执行项。
在Form1的.h的private加上: void __fastcall OnIdle(TObject* Sender,bool& Done);
在Form1的.cpp内加上: void __fastcall TForm1::OnIdle(TObject* Sender,bool& Done) {  bool TextSelected=DBRichEdit1-&SelLength&0;  N17-&Enabled=TextS//剪切,复制,清除  N18-&Enabled=TextS  N20-&Enabled=TextS  bool CBHasText=Clipboard()-&HasFormat(CF_TEXT);// 需加入#include&Clipbrd.h&  N19-&Enabled=CBHasT//粘贴  bool HasText=RichEdit1-&Lines-&Count&0;  N21-&Enabled=HasT//全选  bool HasChanged=RichEdit1-&M  ToolButton2-&Enabled=HasC  ToolButton4-&Enabled=HasC }
在Form1的OnCreate内加上: Application-&OnIdle=OnI
  用C++Builder4.0编写Win 95下的串行异步通信程序
  ·串口操纵的基本方法·
  在Win32下,对串口的操作就如同对文件一样打开或关闭,对串行数据的读写可在用户定义的读写缓冲区中进行。具体使用的函数为:
  首先用CreateFile( )打开通信串口,其中参数lpFileName指向串口逻辑名,如“COM1”或“COM2”等,参数dwDesiredAccess定义文件的读写权限,一般设为GENERIC-READ|GENERIC-WRITE;参数dwShareMode定义资源共享方式,此处必须设为0,为独占方式;lpSecurityAttributes定义安全属性,Win 95下为NULL;dwCreationDistribution定义文件创建方式;dwFlagsAndAttributes定义文件属性和标记,应设为FILE-FLAG-OVERLAPPED,表示异步通信方式;hTemplateFile 指向一个模板文件的句柄,在 Windows 95下为NULL。
  然后用BuildCommDCB( )和SetCommState( )函数通过通信设备控制块DCB(Device Control Block)设置串口通信参数(如波特率、停止位、数据位、校验位等),其中BuildCommDCB( )中的字符串参数lpDef 定义同DOS命令中MODE的参数格式,关于DCB更具体的设置需要根据用户对数据流定义、握手信号及通信控制要求具体定义,参见有关Windows。用GetCommState()可以得到当前的DCB参数值。如果需要还可通过SetCommTimeouts()和GetCommTomeouts()重新设置读写的超时参数;读写缓冲区的设置使用SetupComm(),参数dwInQueue和 dwOutQueue分别定义为输入和输出缓冲区的大小。
  在串口初始化完毕后,还要建立与通信有关的事件对象。一般使用CreateEvent()函数,它返回一事件句柄,其中参数lpEventAttributes指向安全属性结构地址,在Win 95(无安全属性)中为NULL;布尔参数bManualReset 定义事件重置方式,true 表示手工重置,false表示自动重置(相关函数为SetEvent()和ResetEvent());参数bInitialState定义事件初始状态,true表示发信号,否则为不发信号;lpName是为多进程设置的事件名,对于单进程定义为NULL。然后用SetCommMask()定义用户程序可监视的通信事件类别。
  以上设置完成后,用户程序就可以等待通信事件的产生,一般调用函数WaitCommEvent()监视通信事件,其中参数lpEvtMask指向产生事件的掩码地址,用于判断事件产生的性质,lpOverlapped指向重叠结构地址,可简单定义为NULL。对于串口事件的响应一般有四种方式:查询、同步I/O、异步I/O和事件驱动I/O,需要根据用户不同控制要求而定。查询方式占用较长的计算机时间,同步I/O方式直到读取完指定的字节数或超时时才返回,容易造成线程阻塞,异步I/O用于后台处理,事件驱动是由系统通知用户程序发生的事件并进行串口操作。 比较而言事件驱动I/O方式较灵活。
  当有通信事件产生时,就可用函数ReadFile()和WriteFile()直接对串口缓冲区进行读写操作了。其中lpBuffer 指向读写缓冲区,nNumberOfBytes为要读写的字节数,lpNumberOfBytes为实际读写的字节数,lpOverlapped指定同步或异步操作。通信结束后,调用函数CloseHandle()将串口关闭。
  ·应用实例说明·
  使用以上的API函数,笔者给出了简化后的串口初始化的实例。图1为使用C++ Builder 组件生成的串口通信基本参数设置的界面实例。
  HANDLE //定义句柄
  OVERLAPPED //定义重叠结构
  void -fastcall TForm1::OkBtnClick(TObject?Sender)
  { hcom=CreateFile("COM2",GENERIC-READ|GENERIC-WRITE,0,NULL,OPEN-EXISTING, FILE-ATTRIBUTE-NORMAL|FILE-FLAG-OVERLAPPED,NULL); //打开通讯口
   BuildCommDCB("9600,O,8,1",&dcb);
//第一个字符串参数实际使用时由图1选择后组合,这里仅简单说明其格式
   SetCommState(hcom,&dcb);
   SetupComm(hcom,512,512);//设置读写缓冲区
   e.hEvent=CreateEvent(NULL,false,false,NULL); //设置事件
   SetCommMask(hcom,EV-RXCHAR| EV-TXEMPTY); //设置事件掩码
   OkBtn-〉Enabled=}
C++BUILDER非可视组件的消息处理技巧
  一个非可视的组件必须对Windows操作系统或用户定义的消息作出响应。然而,由于一个非可视组件没有窗口,因此它也没有窗口句柄,自然它也不能接收到消息,为了解决这一问题,我们的思路是创建一个隐藏的窗口,使非可视组件能够接收到消息。
  为了给你的非可视组件创建一个隐藏的窗口,需要有以下:
  1.一个私有变量型(Private Variable)的HWnd来取得窗口句柄。
  2.一个用来捕捉窗口发送给组件的函数(a WndProc)。
  3.对AllcolateHwnd的调用使之创建窗口句柄并设置WndProc。
  为了清楚的解释上述思路和展示创建过程,下面我们将以一个具体的实例来说明。
  首先我们先创建一个新的组件,在C++Builder中,选择FILE|NEW...双击组件图标显示一个新的组件对话框改变Ancestor Type为Tcomponent和Class name为TTest并设置完毕。
  然后,切换到新组件的头文件,在类的私有部分(private section)加入以下声明:
  HWnd FH
   void-fastcall WndProc
(TMessage& Msg);
  第一行声明了一个调用Fhandle的HWnd变量,这个变量将用于窗口创建后捕获窗口句柄。第二行声明了一个用于接收消息的WndProc函数。这个函数的声明必须加以标识,以便限定它是一个WndProc,然后在类声明Public(公有)部分构造以下声明:
  Viod DoIt( );
  这个公有函数将被我们用来测试组件,类声明应如下:
  class PACKAGE TTest : public
TComponent
   private:
   HWnd FH
   void-fastcall WndProc
(TMessage& Msg);
   protected:
   public:
   -fastcall TTest
&&&&推荐文章:
【上篇】【下篇】

我要回帖

更多关于 mscomm控件使用实例 的文章

 

随机推荐