标签归档: C++

利用Active X控件开发串口通信软件

  摘要:本文介绍了在Microsoft Visual C++ 6.0环境下通过对Active X控件的编程来实现串口的通信的一般方法。

 一、 引言

当我们在Windows操作系统下开发串行通信程序时通常不得不面对许多复杂的API函数,因为在Windows操作系统下不能直接对设备端口进行操 作,也不能在系统级(Ring 3级别)使用任何DOS或BIOS中断,如要对端口进行编程则只能以文件的形式来对端口进行操作,这就使开发人员不得不面对非常烦琐的API函数编程。本 文对此提出了另外一种封装性很好的使用Microsoft Visual C++ 6.0自带的"Microsoft Communications Control,version 6.0"Active X控件的编程方法,通过对该控件的正确使用,我们可以比较轻松地编写出所需的串行通信程序。

下面,我们将结合一个实际的程序示例来对此方法进行说明。本程序的编程环境是Windows 98和Microsoft Visual C++ 6.0。在本程序示例中对为避免阻塞而对线程的使用以及在使用中遇到的一些问题也做了详细的介绍。

二、 程序的设计实现

在开始进行代码编程前,首先以在工程中插入组件或控件的方式将Active X控件"Microsoft Communications Control,version 6.0"加入到工程中来,此时将会在工程中添加一个关于此控件的新类。使用该控件的一些方法和属性时不能象使用类一样简单的声明一个实例对象,而要通 ClassWizard为该控件和一个成员变量建立起绑定关系,在此我们将该控件同变量m_Comm相绑定后就可以通过该控件提供的方法来对串口的各种通 讯参数进行设置了。为了编程方便起见,也可以在资源视图中直接对该控件的属性进行设置,如无特别要求,对下表所列属性进行设置就基本可以满足编程要求了。 现将常用的属性列表如下:

属性 设定值 属性说明
CommPort 1 串口号,一般从1到4
InBufferSize 30720 接收缓冲区大小,为保持程序的稳定,建议设得值足够大
InputMode 0-Text 接收数据的类型,0表示文本类型,1表示二进制类型
InputLen 0 从接收缓冲区读取的字节数,0表示全部读取
OutBufferSize 512 发送缓冲区大小
Settings 4800,n,8,1 串口的参数设置,依次为波特率、奇偶校验(n-无校验,e-偶校验,o-奇校验)、数据位数、停止位数
RThreshold 1 设定当接收几个字符时触发OnComm事件,0表示不产生事件,
1表示每接收一个字符就产生一个事件
SThreshold 0 设定在触发OnComm事件前,发送缓冲区内所允许的最少的字符数,
0表示发送数据时不产生事件,1表示当发送缓冲区空时产生OnComm事件

我们要求能在程序启动的同时就打开串口以便即时对从串口到达的数据进行接收、处理。一般来说可以将下面的打开端口的代码写在OnCreate()、OnInitialUpdate()、InitInstance ()等程序入口函数中:

……
if(!m_Comm.GetPortOpen()) //检测是否已经打开过端口
m_Comm.SetPortOpen(TRUE); //如没有打开则将端口打开
……

  接下来的工作就是对数据的发送与接收了,这也是本文所要介绍的重点所在。发送数据的代码原则上是可以写到一个成 员函数中被直接调用的,但这并不是一个良好的编程习惯:我们应当把比较耗时的操作,如文件拷贝、打印、端口传输等工作放到一个单独的线程当中,以避免其在 工作时会引起整个进程的阻塞,以提高整个系统对CPU的利用率。例如我们可以在视类中菜单或按钮的响应函数中用AfxBeginThread (WriteProc,this)函数来开启一个名为"WriteProc"的线程,由于在线程中还需要使用视类的函数和变量,为了不产生新的视类的实例 对象,我们通过该函数的第二个参数将指向当前的视类的指针this作为参数传递给线程。在线程中可以用如下两种方法之中的一种调用视类的成员函数:

((COLECommView*) pParam)->DoSendProc();

或是:

COLECommView* view=(COLECommView*) pParam;
View->DoSendProc();

其中从pParam传来的变量就是指向视类的指针。在线程中通过调用视类中的DoSendProc函数来完成对数据的发送,正是由于该函数是被全局的线 程所调用的,我们就不可以使用取编辑框上的数据时通常所用的UpdateData()函数了,取而带之的是API 函数GetDlgItemText(),取到输入的数据后通过控件的SetOutput() 方法就把数据从串口发出去了,其中发送数据必须经ColeVariant类将其转换为通用的VARIANT型变量。实现
主要代码如下:

……
char a[255];
HWND hwnd=GetSafeHwnd();
::GetDlgItemText(hwnd,IDC_EDIT1,a,255);
int i=0;
CString str;
while(a[i]!=”)
{
str.Format("%c",a[i]);
m_SendData+=str;
i++;
}
str.Format("%c",10);
m_SendData+=str;
m_Comm.SetOutput(COleVariant(m_SendData));
……

至于数据的接收,我们可以通过让MS Comm控件响应其OnComm事件来完成,通过ClassWizard加入其对事件的响应后,通过下面的事件映射,当有字符到达时便会通知 OnComm()函数去处理,从而实现数据的异步接收:

……
BEGIN_EVENTSINK_MAP(COLECommView, CFormView)
//{{AFX_EVENTSINK_MAP(COLECommView)
ON_EVENT(COLECommView, IDC_MSCOMM1, 1 /* OnComm */, OnComm, VTS_NONE)
//}}AFX_EVENTSINK_MAP
END_EVENTSINK_MAP()
……
void COLECommView::OnComm()
{
VARIANT Input;
if(m_Comm.GetCommEvent()==2)//接收缓冲区内有字符
{
Input=m_Comm.GetInput();//读取缓冲区内的数据
CString msg=Input.bstrVal;
CString str;
str.Format("%c",10);
if(msg.Right(1)==str)
{
m_RecvData+=msg;
m_History.AddString(m_RecvData);
m_RecvData="";
}
else
m_RecvData+=msg;
}
}

当数据被接收到接收缓冲区后,对于字符可以从VARIANT型结构变量的bstrVal成员变量中获取,VARIANT数据结构相当复杂,并牵扯到 COM(Component Object Model,组件对象模型)中的一些概念,具体详情请参阅Microsoft Corpration发布的MSDN中的有关论述。

三、 测试与实验

编译运行程序之前有必要对机器的端口做一番检查,以确保端口的完好,可以用常见的DOS程序Comdebug来检查。在确认串口工作正常后,可用串口线 将两台机器的串口相连,同时在两台机子上运行该程序,如果没有条件也可只用一台微机,将其串口的2脚和3脚短接,使其处于自发自收状态。经过数据的传输实 验证明该程序是可靠、正确的。

小结:利用通讯控件可以很容易的编写出串行通信程序。但相对来说通讯控件在VC中的使用要比在VB、Delphi中复杂的多,要想对串口通讯开发出更多更灵活的使用方法还需要不断的实践中摸索。本程序在
Windows 98下,由Microsoft Visual C++ 6.0编译通过。

上一页  1

Read: 636

通过COM技术实现Windows外壳编程

  摘要:本文介绍了一种有别于通常的Windows外壳编程方法。采用COM技术,通过Windows提供的外壳接口实现对其的编程。

一、 引言

在Windows环境下,不论是使用Visual C++还是Delphi或是其他一些软件开发工具,尽管存在着差别,但有一点是相同的:都是运行于Windows操作系统之下的。在程序开发过程中也经常 要在自己的应用程序中加入一些Windows系统本身就有的功能,比如文件的拷贝、删除、查找以及运行程序等等。而这些功能在Windows操作系统下都 是具备的,显然如果能直接从系统中调用这些功能将不仅仅减少程序的大小和开发人员的工作量,而且由于是直接通过操作系统来完成这些功能,将会大大减小这部 分程序出现异常错误的概率。Windows系统虽说也存在不少错误,但常用功能的错误还是比较少的,而且通过补丁程序可以更低限度减少系统错误,因此程序 员可以将调试检错的注意力放在应用程序的其他地方,对于调用系统功能这部分代码则可以不必投入太大的精力去调试,因为这部分调试的工作在操作系统发布的时 候就已经由微软做好了。

二、 Windows外壳编程

前面所说的直接使用Windows操作系统部分功能的编程方法就是针对Windows操作系统外壳的编程,可以通过对操作系统提供的几个编程接口对操作 系统的部分功能进行调用,甚至可以按照自己的意图在应用程序中对部分功能进行修改、扩展。但这方面的资料介绍不是特别多,讲的也大都语焉不详,而且用通常 的编程方法去进行外壳编程是非常麻烦的,动辄就要对相关的结构对象进行设置,而这样的结构里的数据成员少则十来个多则几十个,因此配置起来非常烦琐,下面 就以一个比较简单的外壳操作–拷贝文件进行举例说明:

……
SHFILEOPSTRUCT FileOp; //外壳的文件操作结构
FileOp.hwnd=m_hWnd; //设置句柄
//设置操作方式,拷贝用FO_COPY,删除用 FO_DELETE
FileOp.wFunc=FO_COPY;
FileOp.pFrom=m_source; //源文件路径
FileOp.pTo=m_detect; //目标文件路径
FileOp.fFlags=FOF_ALLOWUNDO; //允许恢复
FileOp.hNameMappings=NULL;
FileOp.lpszProgressTitle=strTitle; //设置标题
SHFileOperation(&FileOp); //执行外壳拷贝
if(FileOp.fAnyOperationsAborted) //监测有无中止
TRACE("An Operation was aborted!!!n");
……

上述代码实现起来虽然效果还是不错的,但然实现起来却是比较麻烦的,这仅仅是一个比较简单的外壳操作,对于一些比较复杂的外壳操作比如系统托盘、任务条 等等的编程,更是尤为严重,而且象此类编程,MFC里并没有提供封装好的程序类库,提供的只有系统的WinAPI 应用程序接口,因此在程序开发过程中往往会有一种在进行SDK编程的感觉。

  三、 COM技术在Windows外壳编程中的应用

COM (Component Object Model,组件对象模型)是Microsoft创建的一种二进制和网络标准,也是Microsoft大力推广并已取得广泛认可的一种组件标准。在COM 标准中,COM对象被很好的封装起来,客户无法访问对象的实现细节,提供给用户的唯一的访问途径是通过COM接口来访问。对于COM接口有两方面的含义: 首先它是一组可供调用的函数,由此客户可以让该对象做某些事情;其次,也是更为重要的,接口是组件及其客户程序之间的协议。也就是说接口不但定义了可用什 么函数,也定义了当调用这些函数时对象要做什么。Windows操作系统本身作为一个大的COM组件对象,也提供了一些必要的COM接口给客户程序,因此 我们可以通过这些COM接口来直接对Windows外壳进行编程。

在程序进行正式编写设计之前有一点是肯定的:程序里需要用到COM接口,要对COM对象进行操作。因此首先要加入初始化COM和终止COM的代码。一般是在应用程序类的InitInstance()函数的开始处和返回前添加初始化COM和终止COM代码的:

……
CoInitialize(NULL); //初始化COM
……
CoUninitialize(); //终止COM代码
……

以上两个函数在MFC程序和非MFC程序中都可以很好的使用。另外,如果程序框架是以MFC为基础的,那么只需简单的调用AfxOleInit()函数 就可以达到同样的目的。而且不必显式调用终止COM的代码。在COM标准中,访问COM对象的唯一途径是COM接口,因此在编写操纵Windows 系统外壳程序首先要得到其提供的COM接口。所用的COM接口是IShellDispatch,它是从IDispatch接口派生来的,在VC安装目录的 VC98IncludeExdisp.h头文件中有定义,下面节选了一些将要用到的接口定义:

……
EXTERN_C const IID IID_IShellDispatch;
#if defined(__cplusplus) && !defined(CINTERFACE)
interface DECLSPEC_UUID("D8F015C0-C278-11CE-A49E-444553540000")
IShellDispatch : public IDispatch
{
public:
……
virtual HRESULT STDMETHODCALLTYPE MinimizeAll( void) = 0;
virtual HRESULT STDMETHODCALLTYPE UndoMinimizeALL( void) = 0;
virtual HRESULT STDMETHODCALLTYPE FileRun( void) = 0;
virtual HRESULT STDMETHODCALLTYPE CascadeWindows( void) = 0;
virtual HRESULT STDMETHODCALLTYPE TileVertically( void) = 0;
virtual HRESULT STDMETHODCALLTYPE TileHorizontally( void) = 0;
virtual HRESULT STDMETHODCALLTYPE ShutdownWindows( void) = 0;
virtual HRESULT STDMETHODCALLTYPE Suspend( void) = 0;
virtual HRESULT STDMETHODCALLTYPE SetTime( void) = 0;
virtual HRESULT STDMETHODCALLTYPE TrayProperties( void) = 0;
virtual HRESULT STDMETHODCALLTYPE Help( void) = 0;
virtual HRESULT STDMETHODCALLTYPE FindFiles( void) = 0;
virtual HRESULT STDMETHODCALLTYPE FindComputer( void) = 0;
};
……

该接口在CoCreateInstance()函数创建COM对象时将会得到指向其的指针,通过这个函数客户程序可以避免显式同类厂打交道,其实该函数 内部也调用了CoGetClassObject()函数来获取COM对象的类厂,只不过它把通过类厂创建对象的过程封装起来了,只需用户指定对象类的 CLSID和待输出的接口指针及接口ID,显然这样直接创建COM对象是非常便捷的,在获取到COM对象指针之后就可以通过这个指针去访问调用COM对象 里的方法来实现Windows 外壳的种种功能调用了,下面是实现该功能的部分关键代码:

……
HRESULT sc;//返回结果
IShellDispatch *pShellDisp = NULL; //初始化接口指针
//直接创建COM对象
sc = CoCreateInstance( CLSID_Shell,//指定待创建的COM对象标识符
NULL, //指定被聚合时的外部对象的接口指针
CLSCTX_SERVER, //指定组件类别,可以指定进程内组件进程外组件或者进程内控制对象。
IID_IDispatch, //指定接口ID,需要注意的是这里指的是待
//创建的COM对象的接口ID,而非类厂对象的接口标识符
(LPVOID *) &pShellDisp );//存放函数返回的对象的接口指针

/* 在上述代码中,CoCreateInstance首先调用CoGetClassObject函数创建类厂对象,然后用得到的类厂对象的接口指针创建真正的COM对象,最后把类厂对象释放并返回,这样就很好的把类厂屏蔽起来,使用户用起来更为简单。*/

if( FAILED(sc) )//必须用FAILED 或SUCCECCED来判断COM对象是否创建成功
return;
pShellDisp->FindFiles(); //调用COM对象里的方法
pShellDisp->Release(); //释放申请到的接口指针
……

在这里通过pShellDisp接口指针调用了COM对象的FindFiles()方法去进行查找文件的系统外壳操作。同样,可以根据实际需要灵活调用响应的方法来执行相应的外壳操作,主要有以下几个方法:

MinimizeAll 所有窗口最小化

UndoMinimizeALL 恢复窗口最小化

FileRun 开始菜单的"运行…"

CascadeWindows 层叠窗口

TileVertically 垂直平铺

TileHorizontally 水平平铺

ShutdownWindows 关闭Windows

Suspend 挂起计算机

SetTime 设定时间

TrayProperties 任务栏属性

Help Windows帮助

FindFiles 查找文件

FindComputer 查找计算机

……

这些接口均在VC安装目录的VC98IncludeExdisp.h头文件中有定义,可以通过对该文件的查看来编写响应的外壳操作代码。

小结:本 文介绍了一种利用COM技术实现Windows系统外壳程序的简便实用的方法,对Windows系统外壳的程序设计和COM程序设计的方法和思想做了阐 述。在掌握了本文编程的中心思想前提下,不仅可以对Windows系统外壳进行程序设计,而且对于其他一些提供COM接口的应用程序进行编程,比如可以在 自己的应用程序中用类似的方法加入对Office办公套件的支持等等。因此重点不应放在具体的程序代码中,而是在于程序的设计思想与方法。本文所述程序在 Windows 98下,由Microsoft Visual C++ 6.0编译通过。

Read: 681

CGI扫描器的原理和实现过程

有很多网站为了安全起见,在WEB Server前面架了防火墙,或者做了TCP/IP过滤,对外只开放TCP 80 端口。从入侵者角度来看,要入侵那 么从80上跑的CGI入手是比较可行的,当然也可以用别的办法,例如旁敲侧击,呵呵。从网管角度来看,一是要保证CGI的安全性,另外网络的整体安全性也 是很重要的。针对基于80端口入侵、防范而出的CGI扫描器数不胜数,但基本上原理都一样。
CGI扫描器原理说起来其实非常简单,可以用四句话来概括:<1>连接目标WEB SERVER;<2>发送一个特殊的请求;<3>接收目标服务器返回数据;<4>根据返回数据判断目标服务器是否有此CGI漏洞。
当管理的服务器达到一定数量的时候,手工检测自己的服务器是否存在各种各样的CGI漏洞,那就太消耗时间和精力了,所以一个网管手上有个比较好用的 CGI漏洞扫描器还是必要的。OK!今天我们就自己来动手用C写一个简单的CGI扫描器,帮助自己在日常工作中检测服务器:))

源代码如下,很多地方我都加了注释,别嫌我烦哦:))编译好的程序可以从http://eyas.3322.net/program/cgicheck.exe下载。

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

说明:这是一个Console下多线程,带有进度显示的CGI扫描器的模板,更改一下szSign和SendBuff就可以扫描其他CGI漏洞,设置了连接、发送、接收超时,速度还可以哦。希望可以帮助到admins检测自己的服务器:))

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

#include <stdio.h>

#include <winsock2.h>

#include <time.h>

#define iPort 80//目标Web Server端口

#define szSign "500 13rnServer: Microsoft-IIS/5.0"//根据此标志来检查目标是否有漏洞

#pragma comment(lib,"ws2_32.lib")

///////////////////////////////////////////////////////////////////////////

//

file://定义&初始化全局变量

//

char *SendBuff="GET /NULL.printern",//发送的请求buff

CurrentTarget[52]={0},//存放最后一个线程将扫描的目标

turn[4][2]={"-","\"," ","/"};//显示进度时的字符

int SendBuffLen=strlen(SendBuff),//发送的buff长度

iConnTimeout,//TCP Connect TimeOut

ii=0,//扫描进度

iTotal;//服务器总数

HANDLE hSemaphore=NULL,//信标内核对象句柄,用来控制线程数量

hStdout;//console标准输出句柄,做进度显示的时候用的

struct timeval timeout;//连接、发送和接收的超时值

DWORD SleepTime;//每个一个线程后等待的时间

/*

SleepTime值根据用户输入的线程数量[ThreadNum]和TCP ConnectTimeOut[CONNTIMEO]来计算。确保在 CONNTIMEO时间左右开    ThreadNum个线程。这样在CONNTIMEO时间后,所开的线程开始陆续超时退出,可以继续稳定的开线程, 可以有效的保证同时有    ThreadNum个线程在运行。

*/

///////////////////////////////////////////////////////////////////////////

void ShowError(char *);//显示出错信息函数,可以写完善一些,偶偷懒了:)

BOOL ResetCursor(void);//重置光标位置,线程输出的时候调用的

DWORD WINAPI ShowProInfo(LPVOID);//显示进度信息

DWORD WINAPI scan(LPVOID);//扫描函数

void usage(char *);//帮助函数

///////////////////////////////////////////////////////////////////////////

int main(int argc,char **argv)

{

HANDLE hThread=NULL;//线程句柄

DWORD dwThreadID;//线程ID

struct sockaddr_in sa;

int i,

MaxThread;//最大线程数量

WSADATA    wsd;

long PreviousCount;

clock_t start,end;//程序运行的起始和结束时间

double duration;

file://检查用户输入参数

if(argc!=5)

{

usage(argv[0]);

return 1;

}

file://get target range

int StartNet=inet_addr(argv[1]);

int StopNet=inet_addr(argv[2]);

int StartHost=ntohl(StartNet);

int StopHost=ntohl(StopNet);

file://取得线程数量

MaxThread=atoi(argv[3]);

file://取得conn超时时间

iConnTimeout=atoi(argv[4]);

file://检查参数合法性

if((iConnTimeout>6) (iConnTimeout<2) (MaxThread<1) (MaxThread>500) (StopHost<StartHost))

{

usage(argv[0]);

return 1;

}

file://计算时间

SleepTime=1000*iConnTimeout/MaxThread;

file://设置连接超时值

timeout.tv_sec = iConnTimeout;

timeout.tv_usec =0;

__try

{

file://开始计时

start=clock();

file://加载winsock库

if (WSAStartup(MAKEWORD(1,1), &wsd) != 0)

{

    ShowError("WSAStartup");

    __leave;

}

file://创建信标内核对象句柄

hSemaphore=CreateSemaphore(NULL,MaxThread,MaxThread,NULL);

if(hSemaphore==NULL)

{

    ShowError("CreateSemaphore");

    __leave;

}

file://取得console标准输出句柄

hStdout=GetStdHandle(STD_OUTPUT_HANDLE);

if(hStdout==INVALID_HANDLE_VALUE)

{

    ShowError("GetStdHandle");

    __leave;

}

file://设置目标总数

iTotal=StopHost-StartHost;

file://创建进度显示线程

hThread=CreateThread(NULL,0,ShowProInfo,NULL,0,&dwThreadID);

if(hThread==NULL)

{

    ShowError("1 CreateThread");

    __leave;

}

file://关闭句柄

CloseHandle(hThread);

file://循环创建扫描线程

for(i=StartHost;i<=StopHost;i++)

{

file://等待信标内核对象通知

    WaitForSingleObject(hSemaphore,INFINITE);

    file://create thread to scan

    hThread=CreateThread(NULL,0,scan,(LPVOID)i,0,&dwThreadID);

    if(hThread==NULL)

{

    ShowError("2 CreateThread");

break;

}

file://进度自加1

    ii++;

file://重设最后一个线程扫描的目标

    sa.sin_addr.s_addr=htonl(i);

    strncpy(CurrentTarget,inet_ntoa(sa.sin_addr),sizeof(CurrentTarget));

file://休息一会儿:))

    Sleep(SleepTime);

file://关闭线程句柄

    CloseHandle(hThread);

}

file://等待所有线程结束

while(1)

{

    WaitForSingleObject(hSemaphore,INFINITE);

    if(!ReleaseSemaphore(hSemaphore,1,&PreviousCount))

{

ShowError("main() ReleaseSemaphore");

Sleep(5000);

break;

}

    if(PreviousCount==(MaxThread-1))

    {

printf("nAll done.");

break;

}

    Sleep(500);

}

}//end of try

file://搞定,清场,收工

__finally

{

file://计时结束

end=clock();

file://转换时间格式

duration = (double)(end – start) / CLOCKS_PER_SEC;

file://显示所用时间

printf("nnComplete.Scan %d targets use %2.1f seconds.Speed %0.3g/sn",iTotal,duration,iTotal/duration);

file://关闭句柄

CloseHandle(hStdout);

CloseHandle(hSemaphore);

WSACleanup();

}

return 0;

}

///////////////////////////////////////////////////////////////////////////

//

file://回显错误信息函数

//

void ShowError(char *msg)

{

MessageBox(NULL,msg,"ERROR",0);

file://printf("n%s failed:%d",GetLastError());

}

//////////////////////////////////////////////////////////////////////////

//

file://重置光标位置函数,以便扫描线程输出结果

//

BOOL ResetCursor()

{

CONSOLE_SCREEN_BUFFER_INFO ConsoleScreenBufferInfo;

file://取得当前光标位置

if(!GetConsoleScreenBufferInfo(hStdout,&ConsoleScreenBufferInfo))

{

ShowError("GetConsoleScreenBufferInfo");

return FALSE;

}

file://设置光标X坐标为0

ConsoleScreenBufferInfo.dwCursorPosition.X=0;

file://设置当前光标位置

SetConsoleCursorPosition(hStdout,ConsoleScreenBufferInfo.dwCursorPosition);

return TRUE;

}

///////////////////////////////////////////////////////////////////////////

//

file://显示进度信息函数

//

DWORD WINAPI ShowProInfo(LPVOID lp)

{  

int j,k;

CONSOLE_SCREEN_BUFFER_INFO ConsoleScreenBufferInfo;

float m;

for(j=0;ii<iTotal;j++)

{

file://休息一会儿:)))

Sleep(SleepTime);

file://取得当前光标位置

if(!GetConsoleScreenBufferInfo(hStdout,&ConsoleScreenBufferInfo))

{

    ShowError("GetConsoleScreenBufferInfo");

    return 1;

}

file://设置百分比进度显示的X坐标

ConsoleScreenBufferInfo.dwCursorPosition.X=0;

file://设置当前光标位置

SetConsoleCursorPosition(hStdout,ConsoleScreenBufferInfo.dwCursorPosition);

file://已经完成的百分比

m=(ii+1)*100.00/iTotal;

file://显示进度

if(ii==iTotal)

{

    printf("******** 100%% Wait %d seconds to exit ********       n",iConnTimeout);

    break;

}

else

{

    k=j%4;

    printf("%-15s %s [%d/%d] %s %%%0.3g",CurrentTarget,turn[k],ii,iTotal,turn[k],m);

}

}//end of for

return 0;

}

///////////////////////////////////////////////////////////////////////////

//

file://扫描函数

//

DWORD WINAPI scan(LPVOID lp)

{

int i=(int)lp,iErr;

struct sockaddr_in server;

SOCKET s=INVALID_SOCKET;

char RecvBuff[1024]={0},*ptr;

int RecvBuffLen=sizeof(RecvBuff);

u_long ul=1;//初始化为为非0值

fd_set r,w;

file://create socket

s=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

if(s==INVALID_SOCKET)

{

printf("nCreate socket failed:%d",GetLastError());

ExitProcess(1);

}

file://fill the addr struct

server.sin_family=AF_INET;

server.sin_port=htons(iPort);

server.sin_addr.S_un.S_addr=htonl(i);

__try

{

file://设置socket为非锁定模式,ul为0值的话,那么soocket将被设置为锁定模式

iErr=ioctlsocket(s,FIONBIO,(unsigned long*)&ul);

if(iErr==SOCKET_ERROR )

{

    ResetCursor();

    ShowError("ioctlsocket");

    ExitProcess(1);

}

file://printf("n%X ioctl ok.strat conn",i);

file://connect to target

connect(s,(struct sockaddr *)&server,sizeof(server));

file://printf("n%X conn return,start select w",i);

file://设置select参数

FD_ZERO(&w);

FD_SET(s, &w);

file://等待connect成功&socket可写

iErr=select(0, 0, &w, 0, &timeout);

file://printf("n%X select w return %d",i,iErr);

file://等待返回后,socket仍不可写则退出

if((iErr==SOCKET_ERROR) (iErr==0))

{

    file://printf("n%X select return w err,exit",i);

    __leave;

}

file://socket可写则继续

else

{

    file://send buff to target

    file://printf("n%X send",i);

    iErr=send(s,SendBuff,SendBuffLen,0);

    file://printf("n%X send return",i);

    if(iErr==SOCKET_ERROR)

__leave;

}

file://等待socket可读

FD_ZERO(&r);

FD_SET(s, &r);

file://printf("n%X start select r",i);

iErr=select(0, &r, 0, 0, &timeout);

file://printf("n%X select r return %d",i,iErr);

if((iErr==SOCKET_ERROR) (iErr==0))

{

    file://printf("n%X select r err,exit",i);

    __leave;

}

else

{

    file://recv buff from target

    file://printf("n%X start recv",i);

    iErr=recv(s,RecvBuff,RecvBuffLen,0);

    file://printf("n%X recv ret",i);

    if(iErr==SOCKET_ERROR)

__leave;

}

file://verify buff

ptr=strstr(RecvBuff,szSign);

if(ptr!=NULL)

{

file://线程输出前要先调用ResetCursor函数

    ResetCursor();

file://输出信息后务必加一个以上换行符号,输出前请别加换行符号,以免显示混乱

    printf("[%-15s] has .printer mapped.        n",inet_ntoa(server.sin_addr));

}

}

__finally

{

if(!ReleaseSemaphore(hSemaphore,1,NULL))

    ShowError("thread ReleaseSemaphore failed");

closesocket(s);

}

return 0;

}

///////////////////////////////////////////////////////////////////////////

void usage(char *proname)

{

printf("n%s v0.1 only can find IIS5 .Printer mapped"

"nPower by ey4s<ey4s@21cn.com> 2001.5.20"

"nhttp://www.patching.net"

"nnUsage:%s <StartIP> <EndIP> <ThreadNum> <CONNTIMEO>"

"nnNotice"

"n    StartIP StopIP ==>Don‘t forgot StopIP must large than StartIP "

"n    ThreadNum ==>Thread number,please input between 1-500"

"n    CONNTIMEO ==>TCP connect timeout,please input between 2-6"

"nnExample"

"n    %s 192.168.0.0 192.168.255.255 200 2",proname,proname,proname);

}

程序在VC++6.0上编译通过,在windows2000上运行良好:)

Read: 855

用VC++编写CGI程序

近年来,Internet与Intranet迅猛发展,企业纷纷建立自己的Intranet,以加强企业内部的联系,促进与外部世界的沟通,增强企业的竞 争能力。通过建立自己的Internet服务器,企业能在网络上发布广告,介绍本公司的情况与产品,收集顾客意见,以及建立虚拟电子商场等。大家都知道写 一个HTML超文本文件放在World Wide Web服务器上,即可通过各种浏览器访问你的主页,但是如何使你的静态主页能响应客户的动作,并与本地的数据库联系,将数据库的相应内容传送给远端的浏览 器呢?这就需要通过CGI接口进行联系。
CGI全名是Common Gateway Interface,它是WWW服务器调用外部程序的接口通过CGI,WW W服务器能完成一些本身所力不能及的工作。

在不同操作系统下CGI接口也不相同,可分为Standard CGI接口、DOS CGI接口与Win CG I接口等。Standard CGI接口用于Unix系统,它是通过环境变量来实现WWW Server与CGI应用程序之间的联系。DOS CGI接口与Standard CGI接口原理类同。Win CGI接口则与前两者大不相同。由于越来越多的WWW Server采用Windows系统,而Windows系统又不能有效地传递环境变量,由Bob Denny提出了一种Windows CGI标准,并得以不断推广。
Win CGI是通过.ini文件来进行联系的。在Windows环境下,许多应用程序都是利用.ini 文件,即profile文件来定义一些基本设定,而Win CGI程序与WWW Server之间也正是通过pro file文件来实现联系的。当WWW Client通过WWW Server触发CGI程序时,WWW Server先动态生成几个文件于临时目录下,分为.ini、.inp、.out等文件。在WWW Server生成CGI程序的进程时,通过命令行参数将.ini文件的绝对路径传给CGI进程。.ini文件中也包含了.inp、.ou t等文件路径信息。这样,CGI程序可读取.ini和.inp文件所提供的信息,了解所有来自WWW Server端和与WWW Client端的数据。然后,CGI程序可以调用外部程序,或由CGI直接执行外部程序的功能,并将要传给客户端的信息以HTML文本的形式写在.out 文件上。最后,WWW Server取.out文件的HTML文本,将信息传送给WWW Client端,并删除临时目录下的文件。那么,CGI的profile文件包含了什么数据呢?与Windows其它.ini文件一样,CGI profile 文件包含了许多session,每个session又包含一些key,以下是一个CGI profile的部份内容

[CGI]
Request Method=POST //传递方式,即HTML中的Form method有GET,POST,HEAD等。
Server Software=WebSite/1.1e
//Server的名称与版本。
Server Name=199.199.198.55
//Server的IP地址。
Server Port=80//Server的Socket Port。
Server Admin=tomy@vico.bme.zju.edu.cn//Server管理者的E-mail地址。
Referer=http://199.199.198.55/
//主页的URL路径。
Remote Address=199.199.198.89
//Client端的IP地址。
[System]
Output File=c:WebSitecgi-temp198ws.out//输出文件的路径名。
Content File=c:WebSitecgi-temp198ws.inp//输入文件的路径名。
[Form Literal]
type=B//接收到的Form data即client端动作的结果信息。

原则上可用来编写CGI程序的语言有:perl, AppleScript, Unix Shell, VB, C/C++等。但在Windows(NT、3x、95)环境下,还是必须借助成熟的Windows编程语言才能编写出精巧的Win CGI程序。C语言是一门深受广大程序员喜爱的语言,尤其是VCi++作为一门面向对象的语言,具备了Windows环境的许多基本功能,如OLE, ODBC等。通过ODBC这项功能,可以非常容易地连接FoxPro, Access, dBASE等多种数据库,而无需考虑各种数据库结构的差异。
以下是笔者用VC++4.0编写的一个用Win CGI连接数据库的范例。使用者可在屏幕上选择,以查询ISP服务商提供的internet服务价格表。系统环境采用Windows 95,数据库采用FoxP ro2.5, WWW Server采用Website。

1.建立数据库
本例的CGI程序是通过ODBC连接数据库,因此数据库建完后,应用32位ODBC驱动器加以登记。

2.编写HTML程序
使用者可在屏幕上选择一服务类型,再按"提交"键,即可查询这一服务类型的各价格栏目。其HTML文本描述如下:
<HTML>
<HEAD>{TITLE>维科公司internet服务收费标准</TITLE></HEAD>
<pre><H1>维科公司internet服务收费标准</H1>
</P>
<hr><form method="POST" action="cgi-win/wincgi.exe">
您要查询的类型是:
<UL><LI>A类用户<input name="usertype" type=radio value="A">
<LI>B类用户<input name="usertype" type=radio value="B">
<LI>C类用户<input name="usertype" type=radio value="C">
<LI>D类用户<input name="usertype" type=radio value="D">
</UL>
<center>
<input type=submit value="提交"> <input type=reset value="取消"></center>
</pre></form><hr>
</HTML>

3.编写VC++CGI程序

Website为C++程序员提供了一个CCGI类,可在website目录下找到。该类提供了一些方便的函数,无需程序员编写一些直接与CGI界面打 交道的底层工作,充分体现面向对象语言的优势。在VC++4.0中建立一个"Dialog based"工程框架。将CCGI类引入框架,即将cgi.h,cgi .cpp加入程序。然后用VC++的ClassWizard工具创建一个与数据库相对应的CUserSet类,该类继承MFC类库中的 CRecordSet类。
主要代码加在wincgi.cpp文件中。

//程序:wincgi.cpp
#include "stdafx.h"
#include "wincgi.h"
#include "cgi.h"
#include "userset.h"
……
void ProcessCGI(CCGI *cgi);
BOOL CBaseApp::InitInstance()
{
CCGI cgi(m-ipCmdLine); //实例化CCGI类,并用命令行参数初始化
if(cgi.CalledAsCGI())
{
try
{
ProcessCGI(&cgi);
//在此做所有真正的工作
}
catch(CException* e)
{
cgi.HandleException(e);
//异常处理
e->Delete();
}
}
return FALSE;
}
void ProcessCGI(CCGI *cgi)
{
int i;
CString strTemp, strWebmaster, strReferer;
strWebmaster=cgi->GetCGIVar("Server Admin");
//获得Server管理者的E-mail地址。
strReferer=cgi->GetCGIVar("Referer"); //获得主页的URL路径。
CStringArray csaFields;
cgi->EnumFormFieldNames(csaFields);
//取得所有变量名,放在数组中。
cgi->WriteHTMLHeader();
cgi->WriteHTMLBody("<HEAD><TITLE>VC++CGI test</TITLE></HEAD>1512");
cgi->WriteHTMLBody("<BODY>1512");
CString csaFieldVal;
csaFieldVal=cgi->GetFormField(csaFields[0]);
//得到第一个变量的值,即"usertype"变量的值。
cgi->WriteHTMLBody("<h1>internet服务收费标准查询结果</h1><hr>");
CUserSet m-user;
//实例化CUserSet类
char sfilter[80];
wsprintf(sfilter,"USER-TYPE=´%s´´´,csaFieldVal);
m-user.m-strFilter=sfilter;
//设置筛选器。
m-user.Open();
strTemp="<h2>"+m-user.m-USER-TYPE+"类用户"+"</h2>1512";
cgi->WriteHTMLBody(strTemp);
strTemp="<P>入网费:"+m-user-FARE-INI+".</P>1512";
cgi->WriteHTMLBody(strTemp);
……
strTemp="<P>通信费:"+m-user.m-FARE-COM+".</P>1512"
;
cgi->WriteHTMLBody(strTemp);
m-user.Close();
if(!strReferer.IsEmpty())
//设置回到主页的锚点。
{
cgi->WriteHTMLBody("<P>Go<A HREF="");
cgi->WriteHTMLBody(strReferer);
cgi->WriteHTMLBody("">Back</A>where you came from.</P>1512");
}
if(!strWebmaster.IsEmpty())
//设置Server管理者的E-mail地址。
{
cgi->WriteHTMLBody("<P>Please email comments to");
cgi->WriteHTMLBody("<A HREF="mailto:");
cgi->WriteHTMLBody(strWebmaster);
cgi->WriteHTMLBody("">");
cgi->WriteHTMLBody(strWebmaster);
cgi->WriteHTMLBody("</A>.</P>1512");
}
cgi->WriteHTMLBody("</BODY>1512");
cgi->WriteHTMLFooter();
}

4.结论
将程序编译后,把wincgi.exe拷入website/cgi-win/目录下,用浏览器查询结果。至此简单地阐述了用VC++编写Win CGI程序的整个过程,C程序员也可以用自己熟悉的语言编写CGI程序了。利用VC++的强大功能,你的WWW Server将无所不能。

Read: 897

VC小技巧15则

一、 一次只运行一个程序实例
下列两种方式都可以实现,建议采用第二种方式:
1、 if( FindWindow(NULL,"程序标题"))
         exit(0);
2、BOOL CDemoTBarEApp::InstanceIsRun()
{
HANDLE m_hMutex;
m_hMutex = ::CreateMutex(NULL, TRUE, _T("YourApplication"));
ASSERT(m_hMutex);
if (GetLastError() == ERROR_ALREADY_EXISTS)
{
m_hMutex = NULL;
return TRUE;//实例已经运行
}
return FALSE;//实例未运行
}

二、 装载光标
SetCursor(AfxGetApp()->LoadStandardCursor(IDC_WAIT));
其中::SetCursor()是全局函数,用来设置整个例程的光标参数是宏定义光标句柄。AfxGetApp ()是一个系统函数,它返回当前的一个CWinApp对象。其成员函数LoadStandardCursor()用来读取一个系统指针,每一种系统指针的具体宏定义如下:
IDC_APPSTARTING 带小沙漏的标准箭头
IDC_ARROW 标准箭头
IDC_CROSS 十字光标(用于定位)
IDC_HAND Windows 2000:手型
IDC_HELP 带问号的箭头
IDC_IBEAM I型标
IDC_ICON Obsolete for applications marked version 4.0 or later.
IDC_NO   禁止符号
IDC_SIZE Obsolete for applications marked version 4.0 or later. Use IDC_SIZEALL.
IDC_SIZEALL 十字箭头
IDC_SIZENESW 指向东北和西南的双向箭头
IDC_SIZENS 指向南和北的双向箭头
IDC_SIZENWSE 指向西北和东南的双向箭头
IDC_SIZEWE 指向东西的双向箭头
IDC_UPARROW 上箭头
IDC_WAIT 沙漏

三、获得主框架:
CMainFrame * pMainframe = (CMainFrame *) AfxGetApp()->m_pMainWnd;
.获取应用程序的实例句柄:
      Example: HANDLE hInstance=AfxGetInstanceHandle();

获得应用程序主窗口的指针:
      Example: AfxGetMainWnd() ->ShowWindow(SW_SHOWMAXMIZED); //使程序最大化

四、重新建立字体的代码
if(m_fontLogo.m_hObject)
m_fontLogo.Detach();

m_fontLogo.CreateFont(nHeight, 0, 0, 0, nWeight, bItalic, bUnderline,0,0,0,0,0,0, Name);

五、用指定颜色填充区域
dc.FillSolidRect(rect, ::GetSysColor(COLOR_3DFACE));

六、绘制立体字体效果的字体,很值得一看
void CTestView::OnPaint()
{
CPaintDC dc(this); // device context for painting

CRect rect;
GetWindowRect(rect);

CFont m_fontLogo;
m_fontLogo.CreateFont(24, 0, 0, 0, FW_BOLD, true,
FALSE,0,0,0,0,0,0, "Arial");
CString m_LogoText;
m_LogoText=_T("Benlux Pro3D System");
dc.SetBkMode(TRANSPARENT);

CFont * OldFont = dc.SelectObject(&m_fontLogo);

// draw text in DC
COLORREF OldColor = dc.SetTextColor( ::GetSysColor( COLOR_3DHILIGHT));

rect.right = rect.Width();
rect.bottom = rect.Height();
rect.left = rect.top = 0;
dc.FillSolidRect(rect, ::GetSysColor(COLOR_3DFACE));

dc.DrawText( m_LogoText, rect + CPoint(1,1), DT_SINGLELINE | DT_LEFT | DT_VCENTER);
dc.SetTextColor( ::GetSysColor( COLOR_3DSHADOW));
dc.DrawText( m_LogoText, rect, DT_SINGLELINE | DT_LEFT | DT_VCENTER);

// restore old text color
dc.SetTextColor( OldColor);
// restore old font
dc.SelectObject(OldFont);
// Do not call CView::OnPaint() for painting messages
}

七、简单的消息检索和抽取函数,能够让系统响应其它操作
BOOL PeekAndPump()
{
static MSG msg;

while (::PeekMessage(&msg,NULL,0,0,PM_NOREMOVE)) {
if (!AfxGetApp()->PumpMessage()) {
   ::PostQuitMessage(0);
   return FALSE;
}
}
return TRUE;
}

八、在你的程序中用动画光标替换默认的等待光标 (ANI光标的使用)
HCURSOR m_hAniCursor=NULL;
BeginWaitCursor();   //begin wait cursor for api function

//load ani cursor from file in current path
TCHAR cursorPath[MAX_PATH]; GetModuleFileName(NULL,cursorPath,MAX_PATH);
char drive[_MAX_DRIVE];
char dir[_MAX_DIR];
char fname[_MAX_FNAME];
char ext[_MAX_EXT];
_splitpath(cursorPath, drive, dir, fname, ext );
sprintf(cursorPath,"%s%swait.ani",drive,dir); //ani cursor file name is wait.ani

m_hAniCursor= LoadCursorFromFile(cursorPath);
HCURSOR oldCursor;
if(m_hAniCursor != NULL)
oldCursor=SetCursor(m_hAniCursor);

for(long i=0;i<1000;i++)
Sleep(5);

oldCursor=NULL;
m_hAniCursor=NULL;
EndWaitCursor(); //end wait cursor for api function

九、如何限制编辑框中的准许字符
     如果用户在编辑控件中只允许接收数字,可以使用一个标准的编辑控件并指
定新的创建标志ES_NUMBERS,它是Windows 95新增加的标志,该标志限制 编辑控
件只按收数字字符。
如果用户需要复杂的编辑控件,可以使用Microsoft 的屏蔽编辑控件,它是一个很有用的OLE定制控件。
    如果希望不使用OLE 定制控件自己处理字符,可以派生一个CEdit 类并处理WM_CHAR消息,然后从编辑控件中过滤出特定的字符。首先,使用ClassWizard 建立一个 CEdit的派生类,其次,在对话类中指定一个成员变量将编辑控件分类在OnInitdialog 中调用CWnd: : SubclassDlgItem .

//In your dialog class declaration (.H file )
private :
    CMyEdit m_wndEdit ; // Instance of your new edit control .

//In you dialog class implementation (.CPP file )
BOOL CSampleDialog : : OnInitDialog ( )
{

    //Subclass the edit lontrod .
    m_wndEdit .SubclassDlgItem (IDC_EDIT,this );
    …
}
    使用ClassWizard处理WM_CHAR消息,计算nChar参量并决定所执行的操作,用户可以确定是否修改、传送字符。下例说明了如何显示字母字符,如果字符是字母字符,则调用CWnd ; OnChar,否则不调用OnChar.
//Only display alphabetic dharacters .
void CMyEdit : : OnChar (UINT nChar , UINT nRepCnt , UITN nFlags )
{
    //Determine if nChar is an alphabetic character .
    if (: : IsCharAlpha ( ( TCHAR) nChar ) )
        CEdit : : OnChar (nChar, nRepCnt , nFlags );
}
    如果要修改字符,则不能仅仅简单地用修改过的nChar调用CEdit : : OnChar。要修改一个字符,需要首先修改nChar,然后用修改过的nChar调用CWnd: : DefWindowProc。下例说明了如何将字符转变为大写:
//Make all characters uppercase
void CMyEdit : : OnChar (UINT nChar , UINT nRepCnt , UINT nFlags )
{
    //Make sure character is uppercase .
    if (: : IsCharAlpha ( .( TCHAR) nChar)
         nChar=: : CharUpper (nChar ) ;
    //Bypass default OnChar processing and directly call default window proc.
    DefWindProc (WM_CHAR, nChar , MAKELPARAM (nRepCnt , nFlags )) ;
}

十、串太长时如何在其末尾显示一个省略号
    调用CDC:: DrawText并指定DT_END_ELLIPSIS标志,这样就可以用小略号取代串末尾的字符使其适合于指定的边界矩形。如果要显示路径信息,指定DT_END_ELLIPSIS标志并省略号取代串中间的字符。
void CSampleView:: OnDraw (CDC* pDC)
{
     CTestDoc* pDoc=GetDocument ();
     ASSERT_VALID (pDoc);
                                                                                   
     //Add ellpsis to end of string if it does not fit
     pDC->Drawtext (CString ("This is a long string"),
         CRect (10, 10, 80, 30), DT_LEFT | DT_END_ELLIPSIS);

     //Add ellpsis to middle of string if it does not fit
     pDC->DrawText (AfxgetApp () ->m_pszhelpfilePath,
         CRect (10, 40, 200, 60), DT_LEFT | DT_PATH_ELLIPSIS);
}

十一、如何实现一个橡皮区矩形(具有踪迹矩形并可移动、缩放的矩形)
     CRectTracker是一个很有用的类,可以通过调用CRectTracker:: TrackRubberBand响应WM_LBUTTONDOWN消息来创建一个橡皮区矩形。下例表明使用CRectTracker移动和重置视窗中的蓝色椭圆的大小是很容易的事情。
    首先,在文档类中声明一个CRectTracker数据成员:
class CTestDoc: Public CDocument
{…
public:
CRectTracker m_tracker;

};
     其次,在文档类的构造函数中初始化CRectTracker 对象:
CTestDoc::CTestDoc()
{
m_tracker.m_rect.SetRect (10, 10, 300, 300);
m_tracker.m_nStyle=CRectTracker:: resizeInside |
CRectTracker:: dottedLine;
}
     然后,在视图类的OnDraw函数中画椭圆和踪迹矩形:
void CTestView::OnDraw(CDC* pDC)
{
CTestDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);

//Select blue brush into device context.
     CBrush brush (RGB (0, 0, 255));
     CBrush* pOldBrush=pDC->SelectObject (&brush);

     //draw ellipse in tracking rectangle.
     CRect rcEllipse;
     pDoc->m_tracker.GetTrueRect (rcEllipse);
     pDC->Ellipse (rcEllipse);

     //Draw tracking rectangle.
     pDoc->m_tracker.Draw (pDC);
     //Select blue brush out of device context.
     pDC->SelectObject(pOldBrush);
}
    最后,视图类中处理WM_LBUTTONDOWN消息,并增加下述代码。该段代码根据鼠标击键情况可以拖放、移动或者重置椭圆的大小。

void CTestView::OnLButtonDown(UINT nFlags, CPoint point)
{
   //Get pointer to document.
     CTestDoc* pDoc=GetDocument();
     ASSERT_VALID (pDoc);

     //If clicked on ellipse, drag or resize it. Otherwise create a
     //rubber-band rectangle nd create a new ellipse.
     BOOL bResult=pDoc->m_tracker.HitTest (point)!=
         CRectTracker::hitNothing;

     //Tracker rectangle changed so update views.
     if (bResult)
     {
        pDoc->m_tracker.Track (this,point,TRUE);
        pDoc->SetModifiedFlag ();
        pDoc->UpdateAllViews (NULL);
     }
     else
        pDoc->m_tracker.TrackRubberBand (this,point,TRUE);

CView::OnLButtonDown(nFlags, point);
}

十二、如何在临时目录创建一个临时文件
如果你要在临时目录下创建临时文件,下面的代码能帮到你的忙。
bool GetuniqueTempName (CString& strTempName)
{
strTempName="";
     //Get the temporary files directory.
     TCHAR szTempPath [MAX_PATH];
     DWORD dwResult=:: GetTempPath (MAX_PATH, szTempPath);
     if (dwResult==0)
   return false;

     //Create a unique temporary file.
     TCHAR szTempFile[MAX_PATH];
     UINT nResult=GetTempFileName (szTempPath, _T ("~ex"),0,szTempFile);
     if (dwResult==0)
   return false;

     strTempName=szTempFile;
return true;
}

十三、如何限制窗口的最小范围
要限制窗体的大小,下面的代码能帮到你的忙。
在CMainFrame中增加WM_GETMAXMININFO消息的处理函数,然后在这个函数中写代码如下:
//限制主窗体的最小高度和宽度
void CMainFrame::OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI)
{
lpMMI->ptMinTrackSize.x=600;
lpMMI->ptMinTrackSize.y=400;
CNewFrameWnd::OnGetMinMaxInfo(lpMMI);
}

十四、怎样删除文件到回收站中
要删除文件到回收站,很简单。只要用SHFileOperation函数就行了,下面的代码我将为你演示了这一个函数的用法。当然你可以直接拷贝到你的项目中。
//删除文件到回收站中
//pszPath : 待删除的全路径文件名
//bDelete : TRUE 删除,不移到回收站,FALSE:移到回收站
一、 //返回    : TRUE 删除成功     FALSE 删除失败
BOOL CDelFileToRecycleDlg::Recycle(LPCTSTR pszPath, BOOL bDelete/*=FALSE*/)
{
SHFILEOPSTRUCT shDelFile;
memset(&shDelFile,0,sizeof(SHFILEOPSTRUCT));
shDelFile.fFlags |= FOF_SILENT;      // don’t report progress
shDelFile.fFlags |= FOF_NOERRORUI;     // don’t report errors
shDelFile.fFlags |= FOF_NOCONFIRMATION;    // don’t confirm delete
// Copy pathname to double-NULL-terminated string.
//
TCHAR buf[_MAX_PATH + 1]; // allow one more character
_tcscpy(buf, pszPath);   // copy caller’s pathname
buf[_tcslen(buf)+1]=0;   // need two NULLs at end

// Set SHFILEOPSTRUCT params for delete operation
shDelFile.wFunc = FO_DELETE;       // REQUIRED: delete operation
shDelFile.pFrom = buf;         // REQUIRED: which file(s)
shDelFile.pTo = NULL;          // MUST be NULL
if (bDelete)
{         // if delete requested..
shDelFile.fFlags &= ~FOF_ALLOWUNDO;    // ..don’t use Recycle Bin
}
else
{           // otherwise..
shDelFile.fFlags |= FOF_ALLOWUNDO;    // ..send to Recycle Bin
}
     return SHFileOperation(&shDelFile);    // do it!
}

十五、内存泄漏检查
    也许你已经知道,在C++和C语言中指针问题也就是内存申请与释放是一个令人头疼的事情,假如你申请了内存,但没有释放,并且你的程序需要长时间地运行,那么,系统的资源将逐渐减少,当系统的资源全部被用完时,系统将会崩溃。所以在开发程序的过程中一定要保证资源的完全释放。下面我们来介绍内存漏洞的检查。
示例如下:
// do your memory allocations and deallocations…
CString s = "This is a frame variable";
#ifdef _DEBUG
CMemoryState oldMemState, newMemState, diffMemState;
oldMemState.Checkpoint();
#endif
// the next object is a heap object
CString* p = new CString( "Smith Alan 581_0215" );
delete p;
p=NULL;
#ifdef _DEBUG
newMemState.Checkpoint();
BOOL b=diffMemState.Difference(oldMemState, newMemState);
if (b)
{
AfxMessageBox( "Memory leaked!n" );
}
#endif
     根据试验,由于我们无法释放掉象int CString char 申请的变量。只能释放指针型的变量。而检测内存时,照样会出现内存泄漏现象。所以,这种内存检测方式局限性还是很大。因为我们无法释放非指针型变量。

Read: 828