您正在查看: 标签 应用程序 下的文章

使窗体的标题栏闪烁

-->



实例4 使窗体的标题栏闪烁

在程序运行过程中,经常会发生一些意想不到的情况,这时该如何提示用户呢?弹出一个对话框当然是一个办法,让窗体闪烁也不失为一个不错的选择。本实例演示如何让窗体闪烁起来。

在窗体中添加TCheckBoxTTimer组件,TCheckBox组件用来设置标题栏在窗体闪烁时是否变灰,TTimer组件用于为闪烁窗体定时。添加组件后的窗体如图1-7所示。

1-7 添加组件后的窗体

将窗体上TTimer组件的Interval属性设置为1000,也就是说每隔1 000毫秒就会自动执行下面的代码:

procedure TForm1.Timer1Timer(Sender: TObject);

begin

FlashWindow(self.Handle,self.CheckBox1.Checked);

end;

FlashWindow的功能是让指定的窗口闪烁一次,不过它的第2个参数可以设置在闪烁窗体时标题栏是否变灰。如果为True,则标题栏变灰;反之则没有变化。

程序代码如下:

unit Unit1;

interface

uses

Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

Dialogs, ExtCtrls, StdCtrls;

type

TForm1 = class(TForm)

Timer1: TTimer;

CheckBox1: TCheckBox;

procedure Timer1Timer(Sender: TObject);

private

{ Private declarations }

public

{ Public declarations }

end;

var

Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Timer1Timer(Sender: TObject);

begin

FlashWindow(self.Handle,self.CheckBox1.Checked);

end;

end.

保存文件,然后按F9键运行程序,窗体每隔1秒钟就会闪烁一次。不过如果未选中“闪烁时窗体标题栏变灰”复选框,是看不到任何效果的。程序运行结果如图1-8所示。

1-8 程序运行结果

本实例增加了应用程序提示用户的一个手段,虽然它比起弹出对话框的直观性略差了一些,不过提示方式却变得“温柔”多了。


实时的透明窗体



实例1 实时的透明窗体

界面是程序的面孔,很多应用程序的界面给用户留下了深刻的印象,如FlashGet的半透明窗体。本实例说明如何利用delphi制作一个Windows 2000下的透明窗体。

在窗体上分别添加一个TImageTTrackBar组件,前者用来显示一幅图片,后者用来调节窗体的透明度。添加组件后的窗体如图1-1所示。

1-1 添加组件后的窗体

为了达到实时的透明效果,在程序运行过程中用户可以通过调节TTrackBar组件上滑块的位置来设置窗体的透明程度。为此需要设置窗体的AlphaBlend属性为True,然后添加如下处理TTrackBar组件上滑块位置变化的代码:

procedure TfrmMain.TrackBar1Change(Sender: TObject);

begin

self.AlphaBlendValue:=self.TrackBar1.Max-self.TrackBar1.Position;

end;

AlphaBlend属性为True,通过改变窗体的AlphaBlendValue属性值即可设置窗体的透明度。

程序代码如下:

unit Unit1;

interface

uses

Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

Dialogs, ComCtrls, ExtCtrls;

type

TfrmMain = class(TForm)

Image1: TImage;

TrackBar1: TTrackBar;

procedure FormCreate(Sender: TObject);

procedure TrackBar1Change(Sender: TObject);

private

{ Private declarations }

public

{ Public declarations }

end;

var

frmMain: TfrmMain;

implementation

{$R *.dfm}

procedure TfrmMain.FormCreate(Sender: TObject);

begin

self.AlphaBlend:=true;

self.TrackBar1.Min:=0;

self.TrackBar1.Max:=255;

self.TrackBar1.Frequency:=25;

end;

procedure TfrmMain.TrackBar1Change(Sender: TObject);

begin

self.AlphaBlendValue:=self.TrackBar1.Max-self.TrackBar1.Position;

end;

end.

选择“File|Save All”选项,在弹出的对话框中选择合适的文件名保存文件。然后按F9键运行程序,运行结果如图1-2所示。

值得注意的是,本程序在Windows 环境下不能够得到透明效果,因为AlphaBlend属性和AlphaBlendValue属性只支持Windows 及以后版本的操作系统。通过这个实例,读者不但可以体验到delphi的强大功能,还可以为自己的应用程序界面加上看起来很神奇的透明效果。

1-2 程序运行结果


C++ Builder 研究--在CB6下基于api函数编写串口通信程序简介

1-在C++ Builder 6.0下基于api函数编写串口通信程序简介:

在dos/win95/win98的年代,操作系统对串口是不保护的,也就是说将串口的的资源完全

开放给用户,用户可以用直接操作硬件的函数(比如说TC2.0下的inport()和outport()函数)

跟串口直接打交道,这时候用户使用直接操作串口的函数怎样"折磨"串口都是没有问题的,

操作系统根本就不管不问,对串口操作所造成的一切后果都是用户一个人承担的,这时候用

户对串口具有高度自由的支配权;但是,这种情况好景不长,从win2000操作系统开始,微软

为了"照顾好"计算机上的硬件,开始实施了对硬件的保护策略,也就是说任何用户在他的操作

系统下企图操纵串口时必须经过他的同意方可进行,其实也就是变相的将用户往必须使用他的

通信api函数才能操作串口这条"羊肠小路"上赶(当然也有别的方法操作串口,但那些并非我等

普通用户能研究明白的),形象一点说就好像你想怎样操作串口的意图必须经过win2000的翻译

(其实是win2000的设备驱动程序)才能转达给串口一样,基于这一点我们说(其实是很多资料上

说的)win2000下通过api函数操作串口是具有"设备无关性的",什么意思呢?就是说你想怎样

操作串口就用相应的api函数告诉操作系统你想对串口干什么,然后操作系统就把你的意思转

告给串口让其做出相应的动作,相对于dos/win95/win98下来说,据我理解也就相当于你原来

写的直接操作串口的函数在win2000下他替你完成了,但是你必须用win2000通信api函数清楚

地向操作系统表达清楚你到底想干什么,所以说在这种情况下要想写好串口驱动程序你就必须

至少弄明白win2000下的通信api函数都是干什么的方可,啰里啰唆唠叨了这么多... ...sorry,

还没完呢,至少还有一件事我想说,原来在dos/win95/win98系统下有好多高手用c/c++对串口

进行直接操作是非常熟练的,尤其是dos时代的turbo 2.0操作串口的高手他们写的串口驱动程

序直到win98的时候还用的非常洋洋得意,但是到了win2000的时候,他们的程序突然不好使了

,而他们有的可能还会因为知识结构上的滞后始终弄不明白怎么回事儿,兄弟们,你们该明白

了吧?闲话少叙,下面介绍笔者写串口通信函数时用到的各个api函数---------

2-CreateFile()

用途:打开串口

原型:HANDLE CreateFile(LPCTSTR lpFileName,

DWORD dwDesiredAccess,

DWORD dwShareMode,

LPSECURITY_ATTRIBUTES lpSecurityAttributes,

DWORD dwCreationDistribution,

DWORD dwFlagsAndAttributes,

HANDLE hTemplateFile);

参数说明:

-lpFileName:要打开的文件名称。对串口通信来说就是COM1或COM2。

-dwDesiredAccess:读写模式设置。此处应该用GENERIC_READ及GENERIC_WRITE。

-dwShareMode:串口共享模式。此处不允许其他应用程序共享,应为0。

-lpSecurityAttributes:串口的安全属性,应为0,表示该串口不可被子程序继承。

-dwCreationDistribution:创建文件的性质,此处为OPEN_EXISTING.

-dwFlagsAndAttributes:属性及相关标志,这里使用异步方式应该用FILE_FLAG_OVERLAPPED。

-hTemplateFile:此处为0。

操作说明:若文件打开成功,串口即可使用了,该函数返回串口的句柄,以后对串口操作时

即可使用该句柄。

举例:HANDLE hComm;

hComm=CreateFile("COM1", //串口号

GENERIC_READ|GENERIC_WRITE, //允许读写

0, //通讯设备必须以独占方式打开

NULL, //无安全属性

OPEN_EXISTING, //通讯设备已存在

FILE_FLAG_OVERLAPPED, //异步I/O

0); //通讯设备不能用模板打开

hComm即为函数返回的串口1的句柄。

3-CloseHandle()

用途:关闭串口

原型:BOOL CloseHandle(HANDLE hObjedt)

参数说明:

-hObjedt:串口句柄

操作说明:成功关闭串口时返回true,否则返回false

举例:CloseHandle(hComm);

4-GetCommState()

用途:取得串口当前状态

原型:BOOL GetCommState(HANDLE hFile,

LPDCB lpDCB);

参数说明:

-hFile:串口句柄

-lpDCB:设备控制块(Device Control Block)结构地址。此结构中含有和设备相关的

参数。此处是与串口相关的参数。由于参数非常多,当需要设置串口参数

时,通常是先取得串口的参数结构,修改部分参数后再将参数结构写入。

在此仅介绍少数的几个常用的参数:

DWORD BaudRate:串口波特率

DWORD fParity:为1的话激活奇偶校验检查

DWORD Parity:校验方式,值0~4分别对应无校验、奇校验、偶校验、校验

置位、校验清零

DWORD ByteSize:一个字节的数据位个数,范围是5~8

DWORD StopBits:停止位个数,0~2分别对应1位、1.5位、2位停止位

操作举例:DCB ComDCB; //串口设备控制块

GetCommState(hComm,&ComDCB);

5-SetCommState()

用途:设置串口状态,包括常用的更改串口号、波特率、奇偶校验方式、数据位数等

原型:BOOL SetCommState(HANDLE hFile,

LPDCB lpDCB);

参数说明:

-hFile:串口句柄

-lpDCB:设备控制块(Device Control Block)结构地址。要更改的串口参数包含在此结构中。

操作举例:DCB ComDCB;

GetCommState(hComm,&ComDCB);//取得当前串口状态

ComDCB.BaudRate=9600;//更改为9600bps,该值即为你要修改后的波特率

SetCommState(hComm,&ComDCB;//将更改后的参数写入串口

6-WriteFile()

用途:向串口写数据

原型:BOOL WriteFile(HANDLE hFile,

LPCVOID lpBuffer,

DWORD nNumberOfBytesToWrite,

LPDWORD lpNumberOfBytesWritten,

LPOVERLAPPED lpOverlapped);

参数说明:

-hFile:串口句柄

-lpBuffer:待写入数据的首地址

-nNumberOfBytesToWrite:待写入数据的字节数长度

-lpNumberOfBytesWritten:函数返回的实际写入串口的数据个数的地址,利用此变量可判断

实际写入的字节数和准备写入的字节数是否相同。

-lpOverlapped:重叠I/O结构的指针

操作举例:DWORD BytesSent=0;

unsigned char SendBytes[5]={1,2,3,4,5};

OVERLAPPED ov_Write;

ov_Write.Offset=0;

ov_Write.OffsetHigh=0;

WriteFile(hComm, //调用成功返回非零,失败返回零

SendBytes, //输出缓冲区

5, //准备发送的字符长度

&BytesSent, //实际发出的字符数

&ov_Write); //重叠结构

如果函数执行成功的话检查BytesSent的值应该为5,此函数是WriteFile函数执行完毕后

自行填充的,利用此变量的填充值可以用来检查该函数是否将所有的数据成功写入串口

7-ReadFile()

用途:读串口数据

原型:BOOL ReadFile(HANDLE hFile,

LPVOID lpBuffer,

DWORD nNumberOfBytesToRead,

lpNumberOfBytesRead,

lpOverlapped);

参数说明:

-hFile:串口句柄

-lpBuffer:存储被读出数据的首地址

-nNumberOfBytesToRead:准备读出的字节个数

-NumberOfBytesRead:实际读出的字节个数

-lpOverlapped:异步I/O结构,

操作举例:unsigned char ucRxBuff[20];

COMSTAT ComStat;

DWORD dwError=0;

DWORD BytesRead=0;

OVERLAPPED ov_Read;

ov_Read.hEvent=CreateEvent(NULL, true, false, NULL);//必须创建有效事件



ClearCommError(hComm,&dwError,&ComStat);//检查串口接收缓冲区中的数据个数

bResult=ReadFile(hComm, //串口句柄

ucRxBuff, //输入缓冲区地址

ComStat.cbInQue, //想读入的字符数

&BytesRead, //实际读出的字节数的变量指针

&ov_Read); //重叠结构指针

假如当前串口中有5个字节数据的话,那么执行完ClearCommError()函数后,ComStat

结构中的ComStat.cbInQue将被填充为5,此值在ReadFile函数中可被直接利用。

8-ClearCommError()

用途:清除串口错误或者读取串口现在的状态

原型:BOOL ClearCommError(HANDLE hFile,

LPDWORD lpErrors,

LPCOMATAT lpStat

);

参数说明:

-hFile:串口句柄

-lpErrors:返回错误数值,错误常数如下:

1-CE_BREAK:检测到中断信号。意思是说检测到某个字节数据缺少合法的停止位。

2-CE_FRAME:硬件检测到帧错误。

3-CE_IOE:通信设备发生输入/输出错误。

4-CE_MODE:设置模式错误,或是hFile值错误。

5-CE_OVERRUN:溢出错误,缓冲区容量不足,数据将丢失。

6-CE_RXOVER:溢出错误。

7-CE_RXPARITY:硬件检查到校验位错误。

8-CE_TXFULL:发送缓冲区已满。

-lpStat:指向通信端口状态的结构变量,原型如下:

typedef struct _COMSTAT{

...

...

DWORD cbInQue; //输入缓冲区中的字节数

DWORD cbOutQue;//输出缓冲区中的字节数

}COMSTAT,LPCOMSTAT;

该结构中对我们很重要的只有上面两个参数,其他的我们可以不用管。

操作举例:COMSTAT ComStat;

DWORD dwError=0;

ClearCommError(hComm,&dwError,&ComStat);

上式执行完后,ComStat.cbInQue就是串口中当前含有的数据字节个数,我们利用此

数值就可以用ReadFile()函数去读串口中的数据了。

9-PurgeComm()

用途:清除串口缓冲区

原型:BOOL PurgeComm(HANDLE hFile,

DWORD dwFlags

);

参数说明:

-hFile:串口句柄

-dwFlags:指定串口执行的动作,由以下参数组成:

-PURGE_TXABORT:停止目前所有的传输工作立即返回不管是否完成传输动作。

-PURGE_RXABORT:停止目前所有的读取工作立即返回不管是否完成读取动作。

-PURGE_TXCLEAR:清除发送缓冲区的所有数据。

-PURGE_RXCLEAR:清除接收缓冲区的所有数据。

操作举例:PurgeComm(hComm, PURGE_RXCLEAR|PURGE_TXCLEAR|PURGE_RXABORT|PURGE_TXABORT);

清除串口的所有操作。

10-SetCommMask()

用途:设置串口通信事件。

原型:BOOL SetCommMask(HANDLE hFile,

DWORD dwEvtMask

);

参数说明:

-hFile:串口句柄

-dwEvtMask:准备监视的串口事件掩码

注:在用api函数撰写串口通信函数时大体上有两种方法,一种是查寻法,另外一种是事件通知法。

这两种方法的区别在于收串口数据时,前一种方法是主动的周期性的查询串口中当前有没有

数据;后一种方法是事先设置好需要监视的串口通信事件,然后依靠单独开设的辅助线程进行

监视该事件是否已发生,如果没有发生的话该线程就一直不停的等待直到该事件发生后,将

该串口事件以消息的方式通知主窗体,然后主窗体收到该消息后依据不同的事件性质进行处理。

比如说当主窗体收到监视线程发来的RX_CHAR(串口中有数据)的消息后,就可以用ReadFile()

函数去读串口。该参数有如下信息掩码位值:

EV_BREAK:收到BREAK信号

EV_CTS:CTS(clear to send)线路发生变化

EV_DSR:DST(Data Set Ready)线路发生变化

EV_ERR:线路状态错误,包括了CE_FRAME\CE_OVERRUN\CE_RXPARITY 3钟错误。

EV_RING:检测到振铃信号。

EV_RLSD:CD(Carrier Detect)线路信号发生变化。

EV_RXCHAR:输入缓冲区中已收到数据。

EV_RXFLAG:使用SetCommState()函数设置的DCB结构中的等待字符已被传入输入缓冲区中。

EV_TXEMPTY:输出缓冲区中的数据已被完全送出。

操作举例:SetCommMask(hComm,EV_RXCHAR|EV_TXEMPTY);

上面函数执行完毕后将监视串口中有无数据和发送缓冲区中的数据是否全部发送完毕。

11-WaitCommEvent()

用途:用来判断用SetCommMask()函数设置的串口通信事件是否已发生。

原型:BOOL WaitCommEvent(HANDLE hFile,

LPDWORD lpEvtMask,

LPOVERLAPPED lpOverlapped

);

参数说明:

-hFile:串口句柄

-lpEvtMask:函数执行完后如果检测到串口通信事件的话就将其写入该参数中。

-lpOverlapped:异步结构,用来保存异步操作结果。

操作举例:OVERLAPPED os;

DWORD dwMask,dwTrans,dwError=0,err;



memset(&os,0,sizeof(OVERLAPPED));

os.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);

if(!WaitCommEvent(hComm,&dwMask,&os)){

//如果异步操作不能立即完成的话,函数返回FALSE,并且调用GetLastError()函

//数分析错误原因后返回ERROR_IO_PENDING,指示异步操作正在后台进行.这种情

//况下,在函数返回之前系统设置OVERLAPPED结构中的事件为无信号状态,该函数

//等待用SetCommMask()函数设置的串口事件发生,共有9种事件可被监视:

//EV_BREAK,EV_CTS,EV_DSR,EV_ERR,EV_RING,EV_RLSD,EV_RXCHAR,

//EV_RXFLAG,EV_TXEMPTY;当其中一个事件发生或错误发生时,函数将

//OVERLAPPED结构中的事件置为有信号状态,并将事件掩码填充到dwMask参数中

if(GetLastError()==ERROR_IO_PENDING){

/**************************************************************/

/
在此等待异步操作结果,直到异步操作结束时才返回.实际上此时 /

/
WaitCommEvent()函数一直在等待串口监控的事件之一发生,当事件发/

/
生时该函数将OVERLAPPED结构中的事件句柄置为有信号状态,此时 /

/
GetOverlappedResult()函数发现此事件有信号后马上返回,然后下面/

/
的程序马上分析WaitCommEvent()函数等到的事件是被监视的串口事 /

/
件中的哪一个,然后执行相应的动作并发出相应消息. */

/**************************************************************/

GetOverlappedResult(hComm,&os,&dwTrans,true);

switch(dwMask){

case EV_RXCHAR:

PostMessage(Parent,WM_COMM_RXCHAR,0,0);

break;

case EV_TXEMPTY:

PostMessage(Parent,WM_COMM_TXEMPTY,0,0);

break;

case EV_ERR:

switch(dwError){

case CE_FRAME:

err=0;

break;

case CE_OVERRUN:

err=1;

break;

case CE_RXPARITY:

err=2;

break;

default:break;

}

PostMessage(Parent,WM_COMM_ERR,(WPARAM)0,(LPARAM)err);

break;

case EV_BREAK:

PostMessage(Parent,WM_COMM_BREAK,0,0);

break;

case ...://其他用SetCommMask()函数设置的被监视的串口通信事件。

... ...

break;

default:break;

}

}

12-以上简要介绍了大部分的串口通信api函数,笔者所写的串口通信软件用的是事件通知方式,该方式是

windows2000下效率较高的一种方式。而且只熟悉这些api函数也还是不够的,该机制下还要牵涉到多

线程和消息机制,其中读写串口的动作是由主线程来完成的,比如说操作者按下发送数据的按钮之后

,相应函数马上将某特定区域里面的数据发送出去,所以说用api函数写串口发送数据的功能是相对较

简单的。收数据的时候就要麻烦一点,在打开串口后首先主线程要设置要监视的串口通信事件,然后

将监视线程打开,用来监视主线程设置的这些串口通信事件是否已发生,当其中的某个事件发生后,

监视线程马上将该消息发送给主线程,其中监视线程在发送消息之前要确保主线程在收到消息后肯定

的知道串口究竟发生了什么样的事件,然后根据不同的事件类型进行处理。下面给出大致的主线程和

监视线程的大致工作流程:



主线程打开(其实就是主窗体打开之后)

|

|

V

打开串口(设置波特率、校验方式、数据位数、停止位数)

|

|

V

设置监视线程需要监视的串口通信事件

|

|

V

打开监视线程

|

|

V

等待各种事件的发生(比如发送数据单击事件,更改通信参数事件,监视线程发来的消息等)

--------------------------------------------------------------------------------

监视线程被打开

|

|

V

串口事件发生否(WaitCommEvent())(无论发生否均进入下面的代码)

|

|

V

异步操作是否正在后台进行?(if(GetLastError()==ERROR_IO_PENDING))

|

|

V

在此等待异步操作结果(GetOverlappedResult(hComm,&os,&dwTrans,true))

|

|

V

处理通信事件,根据事件类型的不同给主窗体发送不同的消息

---------------------------------------------------------------------------------

以上给出最最简单的工作流程,其实我感觉任何知识都是依靠查资料、问别人等等这些途径获得入门的

基本信息之后再依靠自己孜孜不倦的钻研得来的,饭来张口得到的那种东西往往不如自己嚼出来的东西

体会更深,有一句话我感觉很有味道:"书读百遍自然熟",但得是用心的读,或许一遍不懂,两遍不懂

,然后没有恒心的人便开始悄悄的准备放弃了,但有恒心的人当再读完一遍之后会越发的发现:奥,...

...,原来上次不懂的地方是这么回事...,但我个人认为,刚开始是很重要的,如果你连这个课题的最最

基本的东西都不知道的话那就很难了,所以任何人都应该有问别人的时候。

由于水平实在有限,只能介绍到这种层次了,而且直到现在我仍然在怀疑我写的串口软件是不是还

有什么不可靠的地方,但这只能靠时间来检验了,也希望大家帮我挑出毛病来,我的串口软件的位置在

http://www.chinabcb.com首页的"工具"一栏中,名字叫"串口大师(或串口监视软件)",欢迎试用。

虽然写的不好,但我感觉对于这方面的初学者还是应该有while(1){一点点}帮助的,如有对此话题

感兴趣者欢迎与我联系并讨论,欢迎转载,转载前请与本人联系!

作者:张景光

来自:沈阳

职业:电子工程师

邮箱:winloop@163.com

QQ: 43583576

C++ Builder 研究--再谈CMOS密码

对于CMOS而言,相信大家已经不再陌生。但就CMOS密码而言,我想真正了解的人就不太多了,所以我们就做了些实验,研究了一下。以前已经有不少人讨论过了,但我觉得还是有再谈的必要,下面就把其中合适的部分拿出来,以飨各位。

在谈密码之前,还是先说说什么是CMOS(本文所言CMOS均针对Award而言)。CMOS实际上存放的是计算机的系统时钟和硬件配置方面的一些信息,供系统引导时读取;同时初始化计算机各个部件的状态,总共有128个字节,存放在RAM芯片中。



好了,先看一个例子,用来向大家说明一下CMOS的一些结构,下面这128个字节就是我的CMOS的内容:

00000000H 30 00 FF 00 39 00 FF 00 12 00 FF 00 01 00 18 00

秒 秒报警 分 分报警 小时 时报警 星期 日

00000010H 11 00 98 00 26 00 02 00 70 00 80 00 00 00 00 00

月 年 寄存器A 寄存器B 寄存器C 寄存器D 诊断 下电

00000020H 40 00 7E 00 F0 00 03 00 0F 00 80 00 02 00 00 00

软驱 密码域 硬盘 未知 设备 基本内存 扩充

00000030H 7C 00 2E 00 00 00 7F 00 15 00 86 00 00 00 00 00

内存 硬盘类型 未知 密码数据位 未知

00000040H 00 00 00 00 00 00 00 00 00 00 00 00 E2 00 22 00

未知

00000050H 0F 00 FF 00 FF 00 E1 00 22 00 3F 00 08 00 59 00

未知

00000060H 00 00 7C 00 19 00 80 00 FF 00 FF 00 FF 00 FF 00

未知 世纪值 未知

00000070H 7D 00 81 00 AA 00 0F 00 39 00 9B 00 E8 00 19 00

未知



上述的内容参考了其他资料,所以不一定完全正确,不过38H-3AH的密码数据倒可以肯定,所以接下来就切入正题,谈一谈CMOS的密码,由于我能找到的均为Award的BIOS,所以以下的结论均针对Award的CMOS,并且在以下的主板及相应的BIOS上验证通过。(本文一下数值均为16进制)

主板名及型号 BIOS版本 BIOS日期

Aopen(建基)AP58 R1.50c 1998-07-13

Aopen(建基)AX5T R1.80 1998-07-30

EPoX(磐英)MVP3E 未知 1998-08-03

EPoX(磐英)P2-112A 未知 1998-09-16

FIC(大众)PA-2007 v1.0A 1997-06-25

在38H-3BH这四个字节中,由于39H和3BH这两个字节一直为00H,所以就略过,那么CMOS密码的关键就集中到了38H和3AH这两个字节上。先介绍一点Award的密码规则,Award允许一位至八位密码,每一个字符的范围由20H-7FH,也就是由空格到ASCII码的127号。想必大家已经发现了,八个字符要放到两个字节中去,好象不压缩一下是不行的。的确,Award是将其压缩了,但是不是普通的压缩方法,我想Award另有将其加密的想法,因为在CMOS中空位还很多,要想放八个字节看来是没有什么问题的,不过这么裸露的密码就更加没有什么用处了。通常的压缩方式有无损压缩,如zip,arj等,或者是有损压缩,象mpeg,jpeg等。但是对这么几个字节,这些方法就没有什么用武之地了,而且压缩过的东西,应该是可以还原的,否则压来压去就没有什么意义了。不过Award的方法就不同了,他不仅仅进行了超级的有损压缩,而且这种压缩是不可还原的,下面就给出他的加密压缩方法(以下数值,运算均基于16进制):

假如有一密码,八位,记为:ABCDEFGH(每一位的取值范围为20H-7FH),将其按下列公式运算:H+4G+10F+40E+100D+400C+1000B+4000A ,将结果按由低到高保存到:H1,H2,H3,字节中,然后将H2保存到地址:3AH中,将H1和H3的和保存到38H中。如果密码不足八位,以此类推。下面举一实例:

我的密码为:r
vte,ASCII码为:72H、2AH、76H、74H、65H,按公式运算得:72100+2A40+7610+744+65=8615,于是H1=00H,H2=86H,H3=15H,所以3AH的值为86H,38H的值为15H。

看来密码就这么简单,在你每次输入密码的时候,BIOS将其算算,再与CMOS中的值比较一下,如果一样就放行,否则免谈。过程就是这样,不过还是有些问题要说明,先算算看,两个字节可以表达的密码可以有多少种:164=65536种,而八位密码,每一位有96种选择,则可以表示的密码有:968≈7.2×1015 种,所以理论上说,每一个密码,都可以找出大约1011这么多个可以起相同作用的密码。但是事实上并不是大家都是八位的密码,或许没有大得这么吓人,不过也挺多的,就如我那个密码,光与他相同功能的五位密码就有二十五万多个,而六位,七位,八位的更多,数量不详,因为从来没有把他算完过,时间太长了,耗不起。所以我随便把我们研究用的那个小程序附上了,可以搜索Award的CMOS密码,大家也可算算看。

CMOS的密码谈的也差不多了,也该停笔了。不过为了减少大家的疑问,再多谈几句。现在的BIOS都比较先进了,在CMOS的设置中,大多有User和SuperVisor密码的设置,我这里讨论的地址为User的,至于SuperVisor的,自各儿研究吧,因为密码这个东西,说得太明,大家都没趣了,对不。好了,就此打住。

C++ Builder 研究--用PcommPro开发串行通信程序

用C++Builder在Win9x下开发串行通信程序是程序员们经常遇到却又令人头痛的事情,不但要理解许多复杂的API函数,还要掌握多线程编程。令人欣慰的是有一些公司专门为C++Builder开发了编写串行通信程序的开发工具,例如MOXA公司的Pcomm(该软件可在http:\www.moxa.com.tw下载),因而帮我们解决了串行编程这一难题。



----下面结合一个具体的例子来说明串行通信程序的开发。本程序的编程环境是Win98和C++Builder3.0。这个编程示例的功能比较强,它具有发送数据和自动接收数据的双重功能。在它的基础上稍加修改,即可以让用户选定进行传输的通信端口,并设定这个端口的相关参数,包括波特率、数据位、停止位、奇偶校验和流量控制等。



一、Pcomm的设置

----启动C++Builder3.0,点击File/NewApplication,建立一个项目文件,修改表单的Name属性为Comm,然后存盘,命名项目(Project)为CommTest,命名单元(Unit)为Comm。

----PcommLibrary是一个动态连接库(DLL)文件,当使用C++Builder编译器编译PComm.dll库时,我们必须告诉C++Builder的编译器怎样找到这些函数(sio_xxx())。



----因此我们用PCommPro在BorlandC++Builder中开发一个串行程序时,必须做到以下两点:



假如你的PcommPro是安装在c:\Programfiles(缺省安装目录)目录下,把c:\Programfiles\PcommPro\Lib下的Pcommb.lib文件加入到C++Builder的View菜单中ProjectManager的项目中,使之成为项目的一个单元(unit)。

把#include"c:\Programfiles\PcommPro\Lib\PComm.h"包含在你的Comm.cpp中。

二、表单及属性的设置

----添加控件,设置各控件的Name和Caption属性。



----该通信程序的工作原理为中断方式,即当输入缓存内有数据时,就会触发Pcomm的中断函数sio_cnt_irq(Port,CntIrq,count),再由它启动中断服务程序CntIrq(),然后由数据接收函数sio_read(port,ibuf,len)来接收数据并做其他相应的处理;至于函数Open()、Sent()、Close()则分别为打开按钮、发送按钮、关闭按钮的click事件函数;SendData、ReceiveData分别为发送数据编辑框和接收数据编辑框相对应的字符串变量。



三、主程序的编制

----双击表单上的Button控件,就会产生相应的事件,如双击"打开"按钮,就会产生Open()事件函数。在这些函数中添加代码,以及PcommPro的串行控制函数,就能实现对串口事件的处理。其中一个需要注意的问题是SendData和ReceiveData都为AnsiString字符串,而PcommPro的函数所需发送和接收的字符串都为char型,因此要正确使用Pcomm函数,还要注意字符串转换。AnsiString字符串可通过c_str()函数转换为char型,而char行字符串转换为AnsiString则比较简单。可用AnsiString(char)把char型强制转换为AnsiString型。程序主要代码如下所示:

void__fastcallTComm::Open(TObject
Sender)

//串口打开函数

{

inti;

sio_open(port);//打开串口

sio_ioctl(port,B2400,P_NONE|BIT_7|STOP_1);

//设置串口参数

 //包括波特率、数据位、停止位、奇偶校验

void__stdcall(p)(int);

p=cntirq;

i=sio_cnt_irq(port,
p,1);//设置中断函数

}

//-----------------

void__fastcallTComm::Sent(TObjectSender)

//数据发送函数

{

char
SendData=newchar[20];

SendData=SentEdit->Text.c_str();

//把SendEdit中的AnsiString型字

符串转换为char型

sio_write(port,SendData,20);//发送数据

}

//-----------------

void__fastcallTComm::Close(TObjectSender)

//串口关闭函数

{

sio_close(port);//关闭串口

}

//-----------------

void__stdcallcntirq(intport)

//中断服务函数(手工生成函数)

{

charibuf[20];

AnsiStringReceiveData[20];

sio_read(port,ibuf,20);//接收数据

ReceiveData=Ansistring(ibuf);

//char字符串转换AnsiString型字符串

ReceiveEdit->Text=ReceiveData;

//显示接受到的字符串

}



----在程序中,我们使用了一些sio_xxx()型的函数,它们都是Pcommpro自带的串行通信函数(函数的具体用法可以参考Pcommpro的帮助),通过这些函数,我们可以对串行端口进行设置。



----sio_open(port)和sio_close(port)为打开串口和关闭串口函数,参数port可设置具体操作的串口;sio_ioctl(intport,intbaud,intmode)为串口控制函数,可设置串口的波特率、数据位、停止位、奇偶校验;至于sio_write(port)和sio_read(port),则为读串口和写串口函数;sio_cnt_irq(intport,VOID(CALLBACK
func)(intport),intcount)为中断函数,当串口有数据时,就触发该函数,然后该函数就会启动其中断服务程序VOID(CALLBACK*func)(intport)(为一函数指针),这里是调用cntirq()函数接收数据,该函数需要程序员手工生成。



----由此我们可以看出,只要我们对程序稍加修改,在表单上再添加一些控件,使得sio_xxx()这些函数的参数可以由用户界面输入,就可以做到由用户选定进行数据传输的通信端口,并设定这个端口的相关参数,包括波特率、数据位、停止位、奇偶校验和流量控制等.