2008-08-25 17:39

前几天在工作中需要写一段代码,获取一些视频文件的大小,心想:这还不简单吗?直接用标准C 的文件操作函数就OK了。于是写了下面的一段代码来实现:

unsigned long get_file_size(const char *filename)

{

unsigned long size;

FILE* fp = fopen( filename, "rb" );

if(fp==NULL)

{

printf("ERROR: Open file %s failed.\n", filename);

return 0;

}

fseek( fp, SEEK_SET, SEEK_END );

size=ftell(fp);

fclose(fp);

return size;

}

没有想到的是,在程序执行后发现有的文件能正确的获取大小,而有的文件则不能正确的获取到文件大小,检查了代码,也没有发现有什么不对的地方。但是在这过程中发现了一个问题,就是能正确获取大小的文件都是相对比较小的文件,而出现错误的都是很大的文件。于是想到会不会是因为标准C文件操作函数对超过一定大小的文件不支持所造成的呢,于是Google了一下,没想到我的猜测是正确的,标准C的文件操作函数不支持对超过2G的文件读取。

问题找到了,看来只有换一种方法来实现了,因为平时很少用到标准C的一些函数,所以,又只有求助于Google了,在看了网上不少的参考文章之后,发现调用stat函数可以正确的得到超大文件的状态信息(当然包括文件大小),于是最终实现了如下的代码:

unsigned long get_file_size(const char *filename)

{

struct stat buf;

if(stat(filename, &buf)<0)

{

return 0;

}

return (unsigned long)buf.st_size;

}

从写这么一个小小功能的函数可以看出,平时多积累一些计算机方面的知识真的是很重要的,同时对代码的全面测试也是相当重要的,否则,看着很正确的代码可能在某些情况下会给你带来意想不到的麻烦。

posted @ 2010-06-21 22:56 lhking 阅读(2822) | 评论 (0)编辑 收藏
---- 所谓遍历目录,就是给定一个目录,访问其中的所有文件(包括子目录下的文件)。迭代是比较常用的遍历算法。本文利用C++面向对象的特性,通过一个类CBrowseDir,对目录遍历进行了封装。用户只需掌握该类四个成员函数的用法,就可以在自己的程序中,很方便地实现目录遍历。

---- 类CBrowseDir使用了迭代算法。因为算法不是本文重点,笔者不打算展开进一步讨论,对其感兴趣者可参考相关资料。

一、类成员函数说明:

---- bool SetInitDir(const char *dir);

---- 功能:设置要遍历的目录。

---- 参数:dir 指向要遍历的目录,可以使用相对路径,比如"d:..hawk";还可以使用网络路径,比如"\wfdhawk"(其中wf是主机名,d是共享目录,hawk是目录)。

---- 返回值:返回true,表示设置成功;返回false,说明目录不可用。

---- bool BeginBrowse(const char *filespec);

---- 功能:开始遍历目录中由filespec指定的文件(包括隐藏文件)。

---- 参数:filespec 指定文件类型,可以使用通配符*和?,比如"*.exe"或"a?.*"都是合法参数。注意:filespec中不能包含路径,象"hawk*.*"是错误的。

---- 返回值:函数返回true,表明已顺利遍历完所有文件;返回false,遍历过程被用户中止。

---- virtual bool ProcessFile(const char *filename);

---- 功能:虚函数。每找到一个文件,程序就会调用ProcessFile,并把文件名作为参数传递给函数。如果函数返回false,则强制遍历中止,并导致类成员函数函数BeginBrowse返回false。 用户应该覆写此函数,以加入自己的处理代码。

---- 参数:filename 指向一个文件名。注意:filename使用绝对路径。

---- 返回值:返回true,继续遍历;否则,中止遍历。

---- virtual void ProcessDir (const char *currentdir,const char *parentdir);

---- 功能:虚函数。在遍历过程中,每进入一个子目录,程序就会调用ProcessDir,并把目录名及其上一级目录名作为参数传递给函数。如果该目录是成员函数SetInitDir指定的初始目录,则parentdir=NULL。用户可以覆写此函数,以加入自己的处理代码。比如可以在这里统计子目录的个数。

---- 参数:currentdir 指向一个子目录。
---- parentdir 指向currentdir的父目录。
---- 注意:currentdir和parentdir均使用绝对路径。

二、使用:

---- 把类CBrowseDir的头文件BrowseDir.h及实现文件BrowseDir.cpp加到项目(Project)中,然后派生自己的类并覆写虚函数ProcessFile和ProcessDir。遍历目录时,先构造一个派生类对象,用成员函数SetInitDir指定目录,然后调用BeginBrowse开始遍历。

---- 本文提供了一个例子 example.cpp,它从CBrowseDir派生出子类CStatDir,通过统计函数ProcessFile及ProcessDir的调用次数,可以得知目录中的文件及子目录个数。程序都有注释,这里就不再罗嗦了。

三、注意事项:

---- 1. 类CBrowseDir会改变当前工作目录。同一个相对路径,使用CBrowseDir前后,可能会有不同的含义。因此用户编程时,要小心使用相对路径。

---- 2. 如果项目(Project)是一个MFC应用程序,直接加入BrowseDir.h及BrowseDir.cpp会导致编译出错。这是因为缺省情况下,MFC项目使用了预编译头(Precompiled Header),而BrowseDir.h和BrowseDir.cpp是用标准C++语句编写的,没用预编译。一个解决办法是先用类向导生成类CBrowseDir的"架子",再把相应的代码拷贝过去。

---- 本文代码均在Win95、Visual C++ 5.0环境下调试通过。

附源代码:

/**************************************************
    这是CBrowseDir的类定义文件 BrowseDir.h

/**************************************************
#include "stdlib.h"

class CBrowseDir
{
protected:
//存放初始目录的绝对路径,以结尾
char m_szInitDir[_MAX_PATH];

public:
//缺省构造器
CBrowseDir();

//设置初始目录为dir,如果返回false,表示目录不可用
bool SetInitDir(const char *dir);

//开始遍历初始目录及其子目录下由filespec指定类型的文件
//filespec可以使用通配符 * ?,不能包含路径。
//如果返回false,表示遍历过程被用户中止
bool BeginBrowse(const char *filespec);

protected:
//遍历目录dir下由filespec指定的文件
//对于子目录,采用迭代的方法
//如果返回false,表示中止遍历文件
bool BrowseDir(const char *dir,const char *filespec);

//函数BrowseDir每找到一个文件,就调用ProcessFile
//并把文件名作为参数传递过去
//如果返回false,表示中止遍历文件
//用户可以覆写该函数,加入自己的处理代码
virtual bool ProcessFile(const char *filename);

//函数BrowseDir每进入一个目录,就调用ProcessDir
//并把正在处理的目录名及上一级目录名作为参数传递过去
//如果正在处理的是初始目录,则parentdir=NULL
//用户可以覆写该函数,加入自己的处理代码
//比如用户可以在这里统计子目录的个数
virtual void ProcessDir(const char
*currentdir,const char *parentdir);
};


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

这是CBrowseDir的类实现文件 BrowseDir.cpp

/***********************************************/
#include "stdlib.h"
#include "direct.h"
#include "string.h"
#include "io.h"

#include "browsedir.h"

CBrowseDir::CBrowseDir()
{
//用当前目录初始化m_szInitDir
getcwd(m_szInitDir,_MAX_PATH);

//如果目录的最后一个字母不是,则在最后加上一个
int len=strlen(m_szInitDir);
if (m_szInitDir[len-1] != \)
strcat(m_szInitDir,"\");
}

bool CBrowseDir::SetInitDir(const char *dir)
{
//先把dir转换为绝对路径
if (_fullpath(m_szInitDir,dir,_MAX_PATH) == NULL)
return false;

//判断目录是否存在
if (_chdir(m_szInitDir) != 0)
return false;

//如果目录的最后一个字母不是,则在最后加上一个
int len=strlen(m_szInitDir);
if (m_szInitDir[len-1] != \)
strcat(m_szInitDir,"\");

return true;
}

bool CBrowseDir::BeginBrowse(const char *filespec)
{
ProcessDir(m_szInitDir,NULL);
return BrowseDir(m_szInitDir,filespec);
}

bool CBrowseDir::BrowseDir
(const char *dir,const char *filespec)
{
_chdir(dir);

//首先查找dir中符合要求的文件
long hFile;
_finddata_t fileinfo;
if ((hFile=_findfirst(filespec,&fileinfo)) != -1)
{
do
{
//检查是不是目录
//如果不是,则进行处理
if (!(fileinfo.attrib & _A_SUBDIR))
{
char filename[_MAX_PATH];
strcpy(filename,dir);
strcat(filename,fileinfo.name);
if (!ProcessFile(filename))
return false;
}
} while (_findnext(hFile,&fileinfo) == 0);
_findclose(hFile);
}

//查找dir中的子目录
//因为在处理dir中的文件时,派生类的ProcessFile有可能改变了
//当前目录,因此还要重新设置当前目录为dir。
//执行过_findfirst后,可能系统记录下了相关信息,因此改变目录
//对_findnext没有影响。
_chdir(dir);
if ((hFile=_findfirst("*.*",&fileinfo)) != -1)
{
do
{
//检查是不是目录
//如果是,再检查是不是 . 或 ..
//如果不是,进行迭代
if ((fileinfo.attrib & _A_SUBDIR))
{
if (strcmp(fileinfo.name,".") != 0 && strcmp
(fileinfo.name,"..") != 0)
{
char subdir[_MAX_PATH];
strcpy(subdir,dir);
strcat(subdir,fileinfo.name);
strcat(subdir,"\");
ProcessDir(subdir,dir);
if (!BrowseDir(subdir,filespec))
return false;
}
}
   } while (_findnext(hFile,&fileinfo) == 0);
_findclose(hFile);
}
return true;
}

bool CBrowseDir::ProcessFile(const char *filename)
{
return true;
}

void CBrowseDir::ProcessDir(const char
*currentdir,const char *parentdir)
{
}


/*************************************************
这是例子example.cpp
  
/*************************************************
#include "stdio.h"

#include "BrowseDir.h"

//从CBrowseDir派生出的子类,用来统计目录中的文件及子目录个数
class CStatDir:public CBrowseDir
{
protected:
int m_nFileCount;    //保存文件个数
int m_nSubdirCount; //保存子目录个数

public:
//缺省构造器
CStatDir()
{
//初始化数据成员m_nFileCount和m_nSubdirCount
m_nFileCount=m_nSubdirCount=0;
}

//返回文件个数
int GetFileCount()
{
return m_nFileCount;
}

//返回子目录个数
int GetSubdirCount()
{
//因为进入初始目录时,也会调用函数ProcessDir,
//所以减1后才是真正的子目录个数。
return m_nSubdirCount-1;
}

protected:
//覆写虚函数ProcessFile,每调用一次,文件个数加1
virtual bool ProcessFile(const char *filename)
{
m_nFileCount++;
return CBrowseDir::ProcessFile(filename);
}

//覆写虚函数ProcessDir,每调用一次,子目录个数加1
virtual void ProcessDir
(const char *currentdir,const char *parentdir)
{
m_nSubdirCount++;
CBrowseDir::ProcessDir(currentdir,parentdir);
}
};

void main()
{
//获取目录名
char buf[256];
printf("请输入要统计的目录名:");
gets(buf);

//构造类对象
CStatDir statdir;

//设置要遍历的目录
if (!statdir.SetInitDir(buf))
{
puts("目录不存在。");
return;
}

//开始遍历
statdir.BeginBrowse("*.*");

//统计结果中,子目录个数不含 . 及 ..
printf("文件总数: %d 子目录总数:
%d ",statdir.GetFileCount(),
statdir.GetSubdirCount());
}  
posted @ 2010-06-21 22:55 lhking 阅读(1287) | 评论 (2)编辑 收藏

// dll.cpp : Defines the initialization routines for the DLL.
// Author:秋镇菜

#include "stdafx.h"
#include <afxdllx.h>

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif


static AFX_EXTENSION_MODULE DllDLL = { NULL, NULL };

extern "C" int APIENTRY
DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
{
// Remove this if you use lpReserved
UNREFERENCED_PARAMETER(lpReserved);

if (dwReason == DLL_PROCESS_ATTACH)
{
   TRACE0("DLL.DLL Initializing!\n");
  
   // Extension DLL one-time initialization
   if (!AfxInitExtensionModule(DllDLL, hInstance))
    return 0;

   // Insert this DLL into the resource chain
   // NOTE: If this Extension DLL is being implicitly linked to by
   // an MFC Regular DLL (such as an ActiveX Control)
   // instead of an MFC application, then you will want to
   // remove this line from DllMain and put it in a separate
   // function exported from this Extension DLL. The Regular DLL
   // that uses this Extension DLL should then explicitly call that
   // function to initialize this Extension DLL. Otherwise,
   // the CDynLinkLibrary object will not be attached to the
   // Regular DLL's resource chain, and serious problems will
   // result.
   MessageBox(NULL, "对话框", NULL, MB_OK);
   new CDynLinkLibrary(DllDLL);
}
else if (dwReason == DLL_PROCESS_DETACH)
{
   TRACE0("DLL.DLL Terminating!\n");
   // Terminate the library before destructors are called
   AfxTermExtensionModule(DllDLL);
}
return 1;   // ok
}


// remotethread.cpp : Defines the entry point for the console application.
// Author:秋镇菜

#include "stdafx.h"
#include "windows.h"

int main(int argc, char* argv[])
{
HWND hWnd = FindWindow("notepad", NULL);
DWORD dwId;
GetWindowThreadProcessId(hWnd, &dwId);
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwId);
if (! hProcess)
   return 0;

char sz[MAX_PATH];
GetModuleFileName(NULL, sz, MAX_PATH);
strcpy(strstr(sz,".exe"), ".dll");
strcpy(sz, "c:\\windows\\dll.dll");
void *pData = VirtualAllocEx(hProcess, 0, sizeof (sz), MEM_COMMIT, PAGE_READWRITE);
if (! pData)
   return 0;
if (! WriteProcessMemory(hProcess, pData, sz, sizeof (sz), 0))
   return 0;
HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0,
   (LPTHREAD_START_ROUTINE)GetProcAddress(
   LoadLibrary("kernel32.dll"), "LoadLibraryA"), pData, 0, 0);
if (hThread == NULL)
   return 0;
printf("sssssssssssssssssssssssssssssss\r\n");
WaitForSingleObject(hThread, INFINITE);
DWORD dwModule;
GetExitCodeThread(hThread, &dwModule);
CloseHandle(hThread);
VirtualFreeEx(hProcess, pData, sizeof (sz), MEM_RELEASE);
printf("...............................\r\n");
hThread = CreateRemoteThread(hProcess, NULL, 0,
   (LPTHREAD_START_ROUTINE)GetProcAddress(
   LoadLibrary("kernel32.dll"), "FreeLibrary"), &dwModule, 0, 0);
if (hThread == NULL)
   return 0;
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
CloseHandle(hProcess);
printf(sz);
printf("\r\n");
Sleep(2000);
return 0;
}

posted @ 2010-06-21 22:53 lhking 阅读(370) | 评论 (0)编辑 收藏

// _remotethreaddemo.cpp : Defines the entry point for the console application.
// Author:秋镇菜

#include "stdafx.h"
#include "windows.h"


// ========== 定义一个代码结构,本例为一个对话框============
struct MyData
{
char sz[64]; // 对话框显示内容
DWORD dwMessageBox; // 对话框的地址
};

// ========== 远程线程的函数 ==============================
DWORD __stdcall RMTFunc(MyData *pData)
{
typedef int(__stdcall*MMessageBox)(HWND,LPCTSTR,LPCTSTR,UINT);
MMessageBox MsgBox = (MMessageBox)pData->dwMessageBox;
MsgBox(NULL, pData->sz, NULL, MB_OK);
return 0;
}
int main(int argc, char* argv[])
{
// ===== 获得需要创建REMOTETHREAD的进程句柄 ===============================
HWND hWnd = FindWindow("notepad", NULL); // 以NOTEPAD为例
DWORD dwProcessId;
::GetWindowThreadProcessId(hWnd, &dwProcessId);
HANDLE hProcess = OpenProcess(
         PROCESS_ALL_ACCESS,
         FALSE,
         dwProcessId);

// ========= 代码结构 ================================================
MyData data;
ZeroMemory(&data, sizeof (MyData));
strcat(data.sz, "对话框的内容.");
HINSTANCE hUser = LoadLibrary("user32.dll");
if (! hUser)
{
   printf("Can not load library.\n");
   return 0;
}
data.dwMessageBox = (DWORD)GetProcAddress(hUser, "MessageBoxA");
FreeLibrary(hUser);
if (! data.dwMessageBox)
   return 0;

// ======= 分配空间 ===================================================
void *pRemoteThread
   = VirtualAllocEx(hProcess, 0,
       1024*4, MEM_COMMIT|MEM_RESERVE,
       PAGE_EXECUTE_READWRITE);
if (! pRemoteThread)
   return 0;
if (! WriteProcessMemory(hProcess, pRemoteThread, &RMTFunc, 1024*4, 0))
   return 0;

MyData *pData
   = (MyData*)VirtualAllocEx(hProcess, 0,
       sizeof (MyData), MEM_COMMIT,
       PAGE_READWRITE);
if (!pData)
   return 0;

if (! WriteProcessMemory(hProcess, pData, &data, sizeof (MyData), 0))
   return 0;

// =========== 创建远程线程 ===========================================
HANDLE hThread
   = CreateRemoteThread(hProcess, 0,
        0, (LPTHREAD_START_ROUTINE)pRemoteThread,
        pData, 0, 0);
if (! hThread)
{
   printf("远程线程创建失败");
   return 0;
}
CloseHandle(hThread);
VirtualFreeEx(hProcess, pRemoteThread, 1024*3, MEM_RELEASE);
VirtualFreeEx(hProcess, pData, sizeof (MyData), MEM_RELEASE);
CloseHandle(hProcess);
printf("Hello World!\n");
return 0;
}


posted @ 2010-06-21 22:53 lhking 阅读(457) | 评论 (0)编辑 收藏
Windows黑客编程基础
文章作者:不详  
本文连接:http://hi.baidu.com/wwwanq/


  从理论上说,任何一门语言都可以在任何一个系统上编程,只要找到该系统提供的“接口”和对系统内部机制有深

入的了解就可以了,至少我是这么认为的。正如c语言可以在windows下编程,也同样可以在Linux上大放异彩一样。

  编程是一项很繁杂的工作,除了应用编程工具之外,了解系统本身内部工作机理非常重要,这是你写出稳定兼容的

程序所必不可少的前提条件。你要在哪一种系统上编程就要对该系统的机制进行研究,至少你应该知道一个程序在那个

系统上是如何运行的。

  一、了解Windows 内部机制

  Windows 是一个“基于事件的,消息驱动的”操作系统。

  在Windows下执行一个程序,只要用户进行了影响窗口的动作(如改变窗口大小或移动、单击鼠标等)该动作就会

触发一个相应的“事件”。系统每次检测到一个事件时,就会给程序发送一个“消息”,从而使程序可以处理该事件。

每个Windows 应用程序都是基于事件和消息的,而且包含一个主事件循环,它不停地、反复地检测是否有用户事件发

生。每次检测到一个用户事件,程序就对该事件做出响应,处理完再等待下一个事件的发生。

  Windows 下的应用程序不断地重复这一过程,直至用户终止程序,用代码来描述实际上也就是一个消息处理过程

的while循环语句。

  下面便简单介绍一下与 Windows 系统密切相关的几个基本概念:

  ⒈窗口:这是我要说的第一个概念。似乎是地球人都知道的事儿了,窗口是Windows本身以及Windows 环境下的

应用程序的基本界面单位,但是很多人都误以为只有具有标题栏、状态栏、最大化、最小化按钮这样标准的方框才叫

窗口。其实窗口的概念很广,例如按钮和对话框等也是窗口哦,只不过是一种特殊的窗口罢了。

  从用户的角度看,窗口就是显示在屏幕上的一个矩形区域,其外观独立于应用程序,事实上它就是生成该窗口

的应用程序与用户间的直观接口;从应用程序的角度看,窗口是受其控制的一部分矩形屏幕区。应用程序生成并控

制与窗口有关的一切内容,包括窗口的大小、风格、位置以及窗口内显示的内容等。用户打开一个应用程序后,程

序将创建一个窗口,并在那里默默地等待用户的要求。每当用户选择窗口中的选项,程序即对此做出响应。

  ⒉程序:通常说的程序都是指一个能让计算机识别的文件,接触得最多的便是.exe型的可执行文件,这个不难

理解。

  ⒊进程:说到进程,学过《操作系统》的人都很清楚,所谓进程就是应用程序的执行实例(或称一个执行程序)

需要注意的是:进程是程序动态的描述,而上面说到的程序是静态的描述,两者有本质的区别。举个例子,从网上

Down了一个瑞星杀毒软件到C盘但没有运行,那个.exe 可执行文件叫做程序,它是一个二进制码的文件。一旦双击

了exe文件图标运行程序,那个“正在运行着的瑞星杀毒”便称为进程,它在双击的那一刻被系统创建,当你关机

或者在任务栏的图标上单击鼠标右键选“退出”时,进程便消亡,彻底结束了生命。进程经历了由“创建”到

“消亡”的生命期,而程序自始至终存在于你的硬盘上,不管你的机器是否启动。

  ⒋线程:线程是进程中的一个执行单元,同一个进程中的各个线程对应于一组CPU指令、一组CPU寄存器以及一

堆栈。进程本来就具有动态的含义,然而实质上是通过线程来执行体现的,从这个意义上说,Windows 中进程的动

态性意义已经不是很明显了,只算是给程序所占的资源划定一个范围而已(个人观点,纯属个人理解,不必引起争

议!),真正具有动态性意义的是线程。以前在大二学习操作系统课的时候就有个同学跟笔者提起这点,笔者还跟

他驳得面红耳赤呢!现在想想,觉得很有道理,不得不佩服那位同学对Windows内部机制了解得如此清楚。

  之所以在此花那么多的篇幅说线程,是因为下面将要介绍到多线程编程技巧,如果不理解这点,那就很难应

用到实践上,希望大家明白。

  ⒌消息:我们几乎做每一个动作都会产生一个消息,在用鼠标指点江山的今天,鼠标被移动会产生WM_MOUSEMOVE

消息,鼠标左键被按下会产生WM_LBUTTONDOWN的消息,鼠标右键按下便产生WM_RBUTTONDOWN消息等等。所有的这些都

可以通过GetMessage,SendMessage等函数得到,以后的操作中我们会经常接触到这些函数。

  ⒍事件:何谓事件?从它的字面意思我们就可以明白它的含义,如在程序运行的过程中改变窗口的大小或者移动

窗口等,都会触发相应的“事件”。

  ⒎句柄:单单一个“柄”字便可以解释它的意思了,我们天气热摇扇子的时候只要抓住扇柄便可以控制整个扇子

的运动了,在程序中也差不多是这个意思。通常一个句柄就可以传递我们所要做的事情。有经验的读者肯定清楚,编

写程序总是要和各种句柄打交道的,句柄是系统用来标识不同对象类型的工具,如窗口、菜单等,这些东西在系统中

被视为不同类型的对象,用不同的句柄将他们区分开来。

  看看C++ 教材中是如何给句柄下定义的:“在Win32里,句柄是指向一个无值型对象(void *)的指针,是一个4

字节长的数据”。虽然我对它的本质是什么还是很迷惑,但我知道句柄并不是一个真正意义上的指针。从结构上看,

句柄的确是一个指针,尽管它没有指向用于存储某个对象的内存位置(很多书都这么说,这正是我的迷惑所在),而

实际上句柄指向的是一个包含了对该对象进行的引用的位置。在编程时,只要抓住了对象的句柄就可以对该对象进行

操作了(我在《一个简单木马程序的编写与伪装策略》中说到的对QQ密码的截获就是要找到QQ登陆窗口的句柄后才开

始截密行动的)。下面再举个例子来说明句柄的运用:编一个程序,使QQ登陆窗口的号码框和密码框均变黑,相关代

码及解释:

void __fastcall Tform1::formCreate(TObject *Sender)
{
HWND hCurWindow,HC,HE;//定义三个窗口句柄变量,hCurWindow用于存放QQ用户登陆窗口的句柄,HC、HE分别存放
//号码框和密码框的句柄。
if((hCurWindow= FindWindow(NULL,"QQ用户登录"))!=0||(hCurWindow=FindWindow(NULL,"OICQ用户登录"))!=0)
{//很明显,调用FindWindow()函数去获得QQ登陆窗口的句柄
String str;
str.sprintf("0x%x",hCurWindow);
}
TCHAR wClassName[255];//类名变量
HC=GetWindow(hCurWindow, GW_CHILD);//得到号码框的句柄
HE=GetWindow(HC, GW_HWNDNEXT);//接着得到密码框的句柄
GetClassName(HE, wClassName, sizeof(wClassName));//得到类名
GetClassName(HC, wClassName, sizeof(wClassName));//得到类名
EnableWindow(HE,false);//使窗口失效
EnableWindow(HC,false);//使窗口失效
}
  以上代码在C++ Builder下编译通过,只要运行次程序,QQ登陆窗口的号码框和密码框马上变黑色,无非是

EnableWindow()函数所起的作用。

  你还可以添加一个Timer控件,将上面的代码copy到void __fastcall Tform1::Timer1Timer

(TObject *Sender)函数中,并在后边加上这一句代码:

SendMessage(hCurWindow,WM_CLOSE,0,0); 使QQ一启动就关闭,让别人永远也用不了QQ,挺有趣儿的哦:).

  ⒏API与SDK:API是英文 Application Programming Interface 的简称,意为“应用程序接口”,泛指系

统为应用程序提供的一系列接口函数。其实质是程序内的一套函数调用,在编程的时候可以直接调用,而不必

知道其内部实现的过程,只知道它的原型和返回值就可以了,此外,手头经常放着一本“Windows API大全”之

类的书也是必不可少的,不然你根本不知道哪些API是干什么用的,瞎编也编不出什么东西来。在后面我们会介

绍调用API编程的例子,调用API编程工作虽然烦琐,但由于API函数都被封装在dll库里,程序只有在运行的时

候才调用的,因此程序的体积小而且运行效率高。

  SDK是英文 Software Development Kit 的缩写,指“软件开发工具包”,在防火墙的设计中就经常涉及到

SDK。

  有关基本的概念就谈这些,那些C/C++的基本语法、什么是面向对象等知识请大家查阅相关的书,此类书

籍各大书店已汗牛充栋,不再多叙。下面直接谈谈语种和编程工具的选择问题,这也是初学者们最迷惑的问题。

  二、编程语言以及工具的选择:

  从上面的介绍我们对Windows 有了进一步的了解,现在就该开始行动了,选择要学的语言和工具是第一步

,而且是非常重要的一步工作,笔者建议一切以简单、易接受为原则,不然你会自信心大减的,何必偏要跟

自己过不去自讨苦吃呢?

  在开始的时候很多人都感到迷惑,目前的编程语言那么多,有c、c++、c#、java、汇编、html等等,究

竟学哪些好呢?最开始我该学什么呢?甚至有人将vc、c++ builder也列为两种不同的语言!这些都是对编

程语言缺乏了解造成的。笔者开始的时候也犯过同样的错误,曾经给自己写过一份计划书:先学c语言,接

着学c++、c#、java、汇编、vb、vc、c++ builder……,哪一种语言用多少时间去专攻等等,现在回想起

来觉得多么的可笑!只要学得精,一门就够了。从实用的角度来讲,C++ 是最好的选择(个人意见,其实

每一种语言都很好),而VC和C++ Builder是其相应开发工具的两大主流,笔者极力推荐初学者使用C++ Builder,

因为很容易上手,如果一下子就用VC的话,也许会打击你的自信心:)。

  三、谈谈促进编程能力提高的两个途径

  如果你是一个黑客技术的狂热者的话,到雅虎去搜索黑客教程的时候就会发现,很多的中文教程在谈到如何

进行黑客编程时,十有八九都会介绍以下两大最佳途径:一、读程序;二、写程序,并且都提出了教程作者的看法

,下面我想谈谈这方面的个人观点。

  ⒈读程序:我将读程序放在前面是有原因的。在你没有阅读过一份完整的源代码之前,你别指望能写出有多好

的程序来!这是对每一位初学者的忠告也是警告,而且必须具备一定的语言基础知识,这里的基础知识主要是指语

法知识,最起码要能读懂别人的程序的每一行意思。有没有程序的设计思想,在这个时期并不重要,只要具备一定

的语法基础就可以了,思想可以通过阅读完别人的源程序后分析得来。

  记得在大一学习C语言的时候,我们都很重视语法的学习,整天都看教材、做练习,而且赶在老师的讲课前预习,

课后又复习巩固,将一些语法点记得滚瓜烂熟,可后来一到做课程设计的时候,坐在电脑面前简直是老鼠拖鸡蛋—

无从下手了,而且不断的问自己:“我平时的努力哪去了?语法都会了呀,怎么还是做不出程序来?”相信很多人

都像笔者以前那样,错误地以为学会了语法就等于掌握了编程。

编程的能力包括经验、技巧、耐心等几个因素,而并非想象中的那样简单,更不要以为编程就是简简单单的写程序!

  其实学一门语言并不需要刻意去记那些条条框框的语法,在看代码的时候,遇到了不明白的地方再去查相关的

资料,一点一点补充基础知识再配合源程序的思路,这时的理解才是最深刻的,我可以肯定地说,这个时候对语法

的接受程度绝对比你刚开始时的死记要强!

  读程序也不能单纯地读,要真正做到“俯而读,昂而思”。好的代码是百读不厌的,比如Shotgun的那道构造

洪水Ping攻击的代码,我至少读了20遍。笔者喜欢将从网上搜集来的代码打印到纸上(尽管学校的打印费贵得要命

,打一份代码就得花去十几块甚至几十块大洋~~~),然后边看边做好眉批,遇到一个新函数记下它的功能,一些

忘记了的知识在旁边标出来,还可以写上对程序的看法等等。特别是遇到了一些新的API函数,最好标出来,对你

以后编程的时候也许会用得着,最后别忘了分析一下程序的思路,这样对你以后编写类似的程序很有帮助的。

  ⒉写程序:问题可谈到点子上了,学那么多语言,读那么多程序最终还不是为了写程序,做出适合需要的软件

来?“君子性非异也,善加于物也”,笔者认为一切从借鉴开始,先是修改别人的程序,等到有了一定的程度再写

出属于自己的程序。

  刚开始写程序,不要奢望一下子写出很出色的程序来,“万丈高楼平底起”,编程贵在动手,只要你动手去写

了,就算只有一句“printf(“Hello!”);”也是一次进步!此外,还要依照自身的能力循序渐进地写,开始的时候

写一点功能简单的、篇幅短小的代码,力求简洁、完整,“麻雀虽小,但五脏俱全”,然后在此基础上进行扩充,

一点一点添加功能,下面笔者摘录一位国内一流编程高手、“豪杰超级解霸”的作者梁肇新的编程心得,请大家看

看一个成功的程序员是如何写程序的,希望对广大菜鸟有所启发:

  写程序的方法:在Win98的环境中,先写主干,用最少的代码实现最基本的功能。然后一点点添加功能,每加

一点都要调试。尽量少用动态分配、全局变量。充分利用操作系统直接提供的API。在Win98下调试通过之后,再

在Win95下调试通过,然后是Win97,WindowsME,WinNT4.0。这样才能写出稳定、快速的程序。

  给程序员的建议:1、不要急于求成,这样往往欲速不达。2、不要什么东西都想学,什么都没掌握。3、每天

都要自我总结,分析自己的错误率和废码率,不断加强自我管理。4、代码格式很重要。代码要规范、严谨,效率要高。

5、不要盲从简单的开发工具(这点笔者不是很同意,最起码要有一定的功底的人才敢这么说)。6、有了成果要公开,

不要舍不得,不然很快会过时的(以上两段摘自《程序员》增值合订本2001.上册P18,请读者前往参考)。

参考书籍:
《Windows C 程序设计》,清华大学出版社
《超级解霸梁肇新》,《程序员》合订本

黑客编程的几个基本技巧
  以下将要谈到的几个基本技巧很重要,虽然对于编程高手来说这是在玩小孩子把戏,但对于一位初学者,掌握

以下几个技巧将为你的编程扫清道路,而且很容易编写出有趣的程序,培养你对编程的兴趣。

  技巧⒈学会修改注册表。

  相信大家都知道当浏览了一些网页恶意代码,IE标题、默认主页等被改得面目全非,这就是通过改动注册表来

更改系统设置的例子。Windows中的注册表是个好东东,它是windows系统的灵魂,是许多软件记录数据的地方(当

然也包括windows本身)。windows通过它记录大量的数据,然后在下一次启动时再读取相应的数据来设置系统。通

过控制注册表就可以控制整个系统,所以很多的黑客程序都在注册表上动手脚(尤其是木马程序和作恶剧程序),

学会修改注册表可以实现一些有趣而强大的功能。我们完全可以通过编程来操作注册表,达到与手动更改注册表

编辑器产生一样的效果。“超级兔子”中的大部分功能就是通过修改注册表来完成的。操作注册表有专门的API函

数,大家可以参考有关资料,下面笔者以C++ Builder为例说明如何在程序中操作注册表:

  程序二:编程修改IE标题内容

  新建一个工程,在Unit1.h文件中包含Registry单元:

#include

然后就可以在.cpp文件操作注册表了,接着来!在窗体的OnCreate()里加入以下代码(你可以在try{}里面加入

任何操作注册表的代码):

TRegistry* Registry;
Registry = new TRegistry();创建一个TRegistry类型的对象Registry,用于修改注册表。
try{
Registry->RootKey = HKEY_CURRENT_USER;//设置主键,这是必不可少的,设置好主键后,就可以操作这个主
//键下所有的键值了。
if( Registry->OpenKey("Software\Microsoft\Internet Explorer\Main",FALSE))//调用OpenKey()

//打开.
//括号里所指的键
{
Registry->WriteString("Window Title",”台湾是中国的一部分,世界上只有一个中国!”);
//调用WriteString()往注册表里写入IE标题
Registry->CloseKey();//关闭该键
}
else
{//如果打开失败的话
Registry->CreateKey("Software\Microsoft\Internet Explorer\Main");//就调用CreateKey()
//新建上述键
Registry->WriteString("Window Title","台湾是中国的一部分,世界上只有一个中国!");//再写入//IE标
//题内容
Registry->CloseKey();//最后关闭该键,这个也不能忽视,它跟上面的OpenKey成对使用的}//End of try
__finally
{//要是出错,跳到这里处理
Registry->CloseKey();//关闭所要打开的键
delete Registry;//销毁Registry对象,释放资源。
}

  编译运行上面的代码就可以将IE的标题改为“台湾是中国的一部分,世界上只有一个中国!”了。笔者写了个

小程序,可以测出当前的IE标题和默认主页是什么,并可随意修改他们,还可以禁止别人修改你的默认主页和注册

表编辑器.。

  技巧⒉调用API编程

  其实这是最简单的,API是系统在DLL里为我们提供的程序接口,可以直接调用的。只要我们有一本《Windows

API大全》之类的书就足够了,下面举个简单的例子:

  程序三:调用API函数隐藏Windows的任务栏:

HWND WndHandle;//定义句柄类型变量

WndHandle=FindWindow("Shell_TrayWnd",NULL);//调用API函数FindWindow()获得任务栏的句柄

ShowWindow(WndHandle,SW_HIDE);//再调用API函数ShowWindow()隐藏任务栏

  大家看到,在上面调用API函数FindWindow()和ShowWindow()的过程中,只要我们知道函数的

名字和括号里的参数是什么就行了,至于实现的过程不必理会,也轮不到我们这些菜鸟去理会:)学会

调用API,你可以写出功能强大的程序来,这一技巧对于初学者来说是必须掌握的(代码请参考黑防光

盘)。

  技巧⒊多线程编程技术

  通过上一篇的介绍 ,大家都很清楚线程的概念了,它是进程内部的一个执行单元(如一个函数等),上期说

了那么多理论,现在该派上用场了。编写多线程应用程序是指使程序在运行时创建多个线程并发地运行于同一个

进程中。今年6月份横空出世的“中国黑客”病毒不是采用了全球独创的“三线程技术”吗?虽然笔者没机会分析

它的样本代码,但此种病毒的工作效率如此之高是与它的多线程技术分不开的。
  
使用多线程技术编程有如下优点:

  ①提高CPU的利用率。由于多线程并发运行,可以使用户在做一件事情的时候还可以做另外一件事。特别是在

多个CPU的情况下,更可以充分地利用硬件资源的优势:将一个大任务分成几个小任务,由不同的CPU来合作完成。

  ②采用多线程技术,可以设置每个线程的优先级,调整工作的进度。

清楚了使用多线程技术的优势之后,下面便来谈谈如何在C++ Builder环境下开发多线程的应用程序,在C++

Builder 环境中,通过 TThread 类就可以很方便地编写多线程应用程序(但不能直接使用,因此要派生新类),

具体流程如下:

  从TThread 类派生出一个新的线程类->创建线程对象->设置线程对象的属性项->挂起或唤醒线程(根据具体

情况操作)->结束线程。

  要说明一点的是:在应用程序中要合理地设置线程的优先级。不要因为某些线程的优先级很高而使其他一些

线程因为等不到CPU的处理时间而被“饿死”,也不要因为线程的级别都差不多而导致的频繁切换花费大量的CPU时

间。(本段引自《C++ Builder 5 编程实例与技巧》P284)。

  技巧⒋让程序实现后台监控
  这是一个很基本的技巧。如果你是一个木马程序的爱好者,当你阅读众多的木马源程序的时候,就会发现100%

的木马程序都很注意自身的后台监控本领,也就是隐身技术,面对不同的系统要施展不同的对策才能实现。很多杀

毒程序就采用了这种后台监控技术,使程序随着系统的启动而运行,然后在后台悄悄地监视系统的一举一动,一发

现有不对路的程序就把它“揪”出来示众。实现程序的后台监控技术有如下几个关键:

  ①正常运行时,不显示程序的窗体;

  ②系统每次启动都自动运行程序一次;

  ③程序图标不显示在任务栏上;

  ④不显示在按Ctrl+Alt+Del 调出的任务列表中;

  ⑤通过热键可以调出隐藏的窗体

  实现方法:对于①,要不显示窗体,我们可以编辑WinMain函数,设置ShowMainform值为False就可以隐藏程序

的窗体了。参考代码:Application->ShowMainform = false ;对于②,可以利用技巧1所介绍的方法修改注册表,

键值如下:HKEY_LOCAL_MACHINESOFTWAREMicrosoftWindowsCurrentVersionRun ,使用的是WriteString()

方法。这是冰河等多种旧木马惯用的启动手段之一(当然还有文件关联、注入dll等方法);对于③,要使程序图

标不显示在任务栏上,也很简单,调用API函数SetWindowLong 可以让程序运行后不出现在任务栏里,不过要放在

窗体的OnCreate()里面。代码如下:

SetWindowLong(Application->Handle,GWL_EXstyle,WS_EX_TOOLWINDOW);

对于④,调用RegisterServiceProcess API 函数将程序注册成为一个服务模式程序,让它运行在较高的优先级下,

就不会出现在程序列表中(对Win9X有效,WinNT/2000/XP下无效)。具体的代码请参考笔者的《一个简单木马程序

的编写与伪装策略》一文,不在此重叙对于⑤,要先定义捕获Windows消息WM_HOTKEY的钩子函数,然后向Windows

加入一个全局原子,并保留其句柄,最后向Windows登记热键,这个可以调用API函数RegisterHotKey来实现。

  技巧⒌使用定时触发器

  在C++ Builder 环境下,定时触发器即Timer控件,有时候我们希望程序隔一段时间重复执行相同的动作,比

如对QQ密码截获的时候,就要隔一段间隔寻找一次QQ登录窗口。在C++ Builder中,只要将执行这些动作的代码放

到一个Timer中去就OK了。

  听说“中国黑客”病毒运行几分钟后就自动创建一个新的线程,用于寻找OICQ的“发送消息”窗口,在10分

钟内一直在找,一旦找到就将“去*****功”等带有政治色彩的言论发送给受害者QQ上的好友,10分钟后自动

结束该线程。我想在查找“发送消息”窗口的10分钟内就运用了定时器,该病毒是用汇编开发的。可是在C++

Builder中是如何运用的呢?其实控件的出现使得编程变得很简单,添加一个Timer控件,设置几下控件的属性,

双击Timer控件,将代码放到里面去就行了。程序执行的时候,相隔指定的时间就重复执行里面的代码了。实际

上笔者在上一期的“程序一”中寻找QQ登录窗口时,就运用了定时器.

  有关编程技巧的介绍到此为止,请读者参考另类书籍,掌握更多的黑客编程技巧,编写出受欢迎的黑客程序

来。

  五、Socket 编程与网络通信基础

由于本文的主题是“黑客编程基础”,而黑客是互连网上“来无影,去无踪”的黑衣人,如冰河、网络神偷等

黑客程序都是基于互连网的,谈黑客编程离开网络编程就会大失其味。所以,下面接着谈谈网络编程,大凡基

于网络应用的程序都离不开Socket。

  Socket 为套接字之意,是作为计算机与计算机之间通信的接口。有关Socket的概念在第6期《黑客防线》

的《Socket 编程的基础和基本过程》一文中有详细的描述,请大家参考,不在此多叙。需要指出的是:Winsock

是访问众多的基层网络协议的一种接口,在每个Win32平台上,它都以不同的形式存在着,Winsock 是网络编程的

接口,不是协议,这是容易弄错的地方。

  现在来谈谈Winsock 编程的过程,大凡在Win32平台上的Winsock编程都要经过下列的基本步骤:定义变量->

获得Winsock版本->加载Winsock库->初始化->创建套接字->设置套接字选项->关闭套接字->卸载Winsock库,

释放所有资源。

  下面以一道极其简单的程序来说明如何进行Winsock编程。程序四:编一个程序来获取本地机器的IP地址。

  使用Winsock提供的API函数是最基本的网络技术,为了给初学者看个清楚,笔者打算在Visual C++ 和

C++ Builder下各写一个,便于大家区分这两种不同的编程工具的特性(对于本程序来说,他们都差不多,而

对于某些通信程序,他们实现起来就相差很远了,但本质是差不多的)。先来看Visual C++ 下的源程序,实

现步骤:打开Visual C++ ,从“File”菜单中的“New”新建一个工程,选中“Win 32 Console Application”

,意思是说生成的是Win32的控制台程序。另外,初学者要注意一点:只要程序中用到了 Winsock API 函数,

都要在工程设置的Link 中增加 Ws2_32.lib 文件,不然程序将不能通过编译,方法是:点击“Project”菜单,

选择“Settings... ALT+F7” ,在弹出的“Project Settings”对话框右侧选“Link”标签,再在“Project

Options”下方的编辑框中增加Ws2_32.lib文件,点“OK”就可以了。

  加载好文件之后,就可以在CheckIP.cpp文件里加入以下代码了:
//-------Begin from ------------
//包含需要使用的头文件
#include "stdafx.h"
#include "windows.h"
#include
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
void CheckIP(void) //定义CheckIP()函数,用于获取本机IP地址
{
WORD wVersionRequested;// WORD类型变量,用于存放Winsock版本的正确值SADATA wsaData;
char name[255];//定义用于存放获得的主机名的变量
CString ip;//定义IP地址变量
PHOSTENT hostinfo;
wVersionRequested = MAKEWORD( 2, 0 );
//调用MAKEWORD()获得Winsock版本的正确值,用于下面的加载Winsock库
if ( WSAStartup( wVersionRequested, &wsaData ) == 0 ) {
//现在是加载Winsock库,如果WSAStartup()函数返回值为0,说明加载成功,程序可以继续
//往下执行
if( gethostname ( name, sizeof(name)) == 0) {
//如果成功地将本地主机名存放入由name参数指定的缓冲区中
if((hostinfo = gethostbyname(name)) != NULL) {
//这是获取主机名,如果获得主机名成功的话,将返回一个指针,指向hostinfo,hostinfo
//为PHOSTENT型的变量,下面即将用到这个结构体
LPCSTR ip = inet_ntoa (*(struct in_addr *)*hostinfo->h_addr_list);
//调用inet_ntoa()函数,将hostinfo结构变量中的h_addr_list转化为标准的点分表示的IP
//地址(如192.168.0.1)
printf("%sn",ip);//输出IP地址 } }
WSACleanup( );//卸载Winsock库,并释放所有资源 } }
int main(int argc, char* argv[])//主函数,程序的入口
{
CheckIP();//调用CheckIP()函数获得、输出IP地址
return 0;//由于main()定义为int型,所以应带回一个int型的数值
}


  下面接着来看看在C++ Builder 下如何实现,其实两者的思想是一样的,只是在C++ Builder下实现

的界面友好点而已,实现方法:打开C++ Builder 5,默认情况下已经新建一个工程,保存这个工程文件

就可以了,构造如下面图4所示的界面,在相应之处添入下面的代码即可。

程序代码:
//包含头文件
#include
#include
#pragma hdrstop
#include "Unit1.h"
#pragma package(smart_init)
#pragma resource "*.dfm"
Tform1 *form1;
__fastcall Tform1::Tform1(TComponent* Owner)
: Tform(Owner)
{ }
void Tform1::GetHostIpAddress()
{// GetHostIpAddress()获得本机IP地址
struct hostent *thisHost;
struct in_addr in;
char MyName[80];
char *ptr;
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD( 2, 0 );
err = WSAStartup( wVersionRequested, &wsaData );
if( err != 0 )
return;
if(LOBYTE( wsaData.wVersion ) != 2 ||
HIBYTE( wsaData.wVersion ) != 0 )
{ WSACleanup( );
return; }
if(gethostname(MyName,80)==SOCKET_ERROR)
return;
if(!(thisHost=gethostbyname(MyName)))
return;
memset((void *)&in,sizeof(in),0);
in.s_addr=*((unsigned long *)thisHost->h_addr_list[0]);
if(!(ptr=inet_ntoa(in)))
return;
WSACleanup( );
Edit1->Text=AnsiString(ptr);}
void __fastcall Tform1::formCreate(TObject *Sender)
{ GetHostIpAddress();}
void __fastcall Tform1::Button1Click(TObject *Sender)
{Close();//添加一个“确定”按钮,点击即关闭程序。}
  程序在 C++ Builder 5 下编译通过,通过比较你会发现他们是大同小异的,对于同一程序,

两者工具各有秋千,至于选择哪种由你决定,最好是两者相得益彰。


  “临渊羡鱼,不如退而结网”,虽说“通往电脑的路不止一条”,然而对于编程,道路却

只有一条,就是:动手去做,亲身实践。
posted @ 2010-06-21 22:50 lhking 阅读(281) | 评论 (0)编辑 收藏
C/C++头文件一览
信息来源:http://hi.baidu.com/wwwanq/
原始连接:http://www.vbnew.net/article/html/vbnew1154334399.php?hits=356

C/C++头文件一览
C、传统 C++

#include <assert.h>    //设定插入点
#include <ctype.h>     //字符处理
#include <errno.h>     //定义错误码
#include <float.h>     //浮点数处理
#include <fstream.h>    //文件输入/输出
#include <iomanip.h>    //参数化输入/输出
#include <iostream.h>   //数据流输入/输出
#include <limits.h>    //定义各种数据类型最值常量
#include <locale.h>    //定义本地化函数
#include <math.h>     //定义数学函数
#include <stdio.h>     //定义输入/输出函数
#include <stdlib.h>    //定义杂项函数及内存分配函数
#include <string.h>    //字符串处理
#include <strstrea.h>   //基于数组的输入/输出
#include <time.h>     //定义关于时间的函数
#include <wchar.h>     //宽字符处理及输入/输出
#include <wctype.h>    //宽字符分类

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

标准 C++ (同上的不再注释)

#include <algorithm>    //STL 通用算法
#include <bitset>     //STL 位集容器
#include <cctype>
#include <cerrno>
#include <clocale>
#include <cmath>
#include <complex>     //复数类
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <deque>      //STL 双端队列容器
#include <exception>    //异常处理类
#include <fstream>
#include <functional>   //STL 定义运算函数(代替运算符)
#include <limits>
#include <list>      //STL 线性列表容器
#include <map>       //STL 映射容器
#include <iomanip>
#include <ios>       //基本输入/输出支持
#include <iosfwd>     //输入/输出系统使用的前置声明
#include <iostream>
#include <istream>     //基本输入流
#include <ostream>     //基本输出流
#include <queue>      //STL 队列容器
#include <set>       //STL 集合容器
#include <sstream>     //基于字符串的流
#include <stack>      //STL 堆栈容器    
#include <stdexcept>    //标准异常类
#include <streambuf>    //底层输入/输出支持
#include <string>     //字符串类
#include <utility>     //STL 通用模板类
#include <vector>     //STL 动态数组容器
#include <cwchar>
#include <cwctype>

using namespace std;

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

C99 增加

#include <complex.h>   //复数处理
#include <fenv.h>    //浮点环境
#include <inttypes.h>  //整数格式转换
#include <stdbool.h>   //布尔环境
#include <stdint.h>   //整型环境
#include <tgmath.h>   //通用类型数学宏
posted @ 2010-06-21 22:48 lhking 阅读(291) | 评论 (0)编辑 收藏
限制程序只打开一个实例
信息来源:http://hi.baidu.com/wwwanq/
文章作者:fictiony

当我们在做一些管理平台类的程序(比如Windows的任务管理器)时,往往需要限制程序只能打开一个实例。解决这个问题的大致思路很简单,无非是在程序打开的时候判断一下是否有与自己相同的进程开着,如果有,则关闭自身,否则正常运行。
  但是,问题就出在如何判别是否有一个与自己相同的进程开着上面。我在网上搜索了一下相关的文章,发现对于这个问题的解决不外乎以下几种方式:
  1、在进程初始化时使用::CreateMutex创建一个互斥对象,通过检测互斥对象是否已存在来确定该程序是否已经运行。
  该方式的确可以很容易的实现判别程序实例是否已存在,只需要在InitInstance方法开头添加以下语句:
m_hUnique = ::CreateMutex(NULL, FALSE, UNIQUE_ID);
if (GetLastError() == ERROR_ALREADY_EXISTS) return FALSE;

  UNIQUE_ID为具有唯一性的字符串,一般可以用VC++为主程序头文件自动生成的包含标识宏(就是.h文件顶上的那一长串宏定义),当然,也可以用工具自己手动生成,随君所好了^^。要注意的是别忘了在ExitInstance方法中用 CloseHandle(m_hUnique) 将该互斥对象关闭。但这种方式存在一个很大的问题,就是很难获取已打开程序实例的主窗口句柄。而我们绝大多数时候,都需要将那个程序实例的主窗口激活。为了获取主窗口句柄,就需要再用到后面提到的其他方法。
  2、遍历所有已经打开的进程主窗口,比较窗口标题,如果找到满足条件的标题,则表示程序已经运行,并激活该窗口。
  这种方式虽然可以找到程序的主窗口,但问题明显:A.如果窗口标题经常变化怎么办(比如标题中会带有打开文档的文件名)?B.如果其他程序的主窗口标题恰好与该程序的相同怎么办?
  第一个问题可以通过写注册表或者写INI文件的方式来解决。即当主窗口标题改变时,将新标题写入注册表或者INI文件。不过这种解决方式也忒麻烦了吧-_-||   第二个问题就麻烦了,至少我还没有找到好的解决方案。如果非要说一个,那我提议你“想尽办法”“不择手段”的将窗口标题设的和别的程序绝对不同。不过估计搞定了这步,你半条命也快没了。
  3、用::SetProp给主窗口添加一个具有唯一性的属性值,以便在进程初始化的时候可以通过遍历所有窗口的该属性来判断。
  添加属性值的代码一般可以放在InitInstance方法的最后,如下:
::SetProp(m_pMainWnd->m_hWnd, "UNIQUE_ID", (HANDLE)UNIQUE_ID);

  UNIQUE_ID是一个具有唯一性的整数值(为什么不能用字符串?因为字符串的比较需要将字符串读取出来,而这儿只能记录字符串地址,在别的程序里这个地址无意义,所以无法读出这个字符串)。这种方式仅有的问题就出在如何确定该整数值是具有唯一性的。我们后面提出的解决方法,就是在这种方法的基础上发展出来的。
  在总结了上述几种方式的利弊之后,我发现,只需要为程序建立一个具有唯一性的整数值,一方面可以通过这个值是否存在来判断程序是否已经运行(::CreateMutex其实也是类似的概念),另一方面可以通过将这个值赋给主窗口,以便能够找到已打开的程序实例的主窗口句柄。于是,ATOM量便派上用场了(ATOM变量类型等同于WORD,因而是一个整数值)。
  ATOM量本质上就是散列表的键标识符,其对应键值为一个字符串。每个程序都有自己的ATOM量表,同时Windows也有一个全局的ATOM表。我们要用的方法就是,为程序创建一个全局的ATOM量,通过这个量是否存在来判断程序是否已经运行,并通过将这个量作为属性值添加到主窗口来标识这个主窗口。具体过程如下:
  1、给主程序App类添加一个ATOM类型的成员变量:m_aAppId,作为程序ID。
  2、在InitInstance方法开头添加以下代码(UNIQUE_ID是具有唯一性的字符串宏):
m_aAppId = ::GlobalFindAtom(UNIQUE_ID); //查找程序ID是否存在
if (m_aAppId) //程序ID存在,激活已打开的程序实例的主窗口
{
HWND hWnd = ::GetWindow(::GetForegroundWindow(), GW_HWNDFIRST);
for (; hWnd; hWnd = ::GetWindow(hWnd, GW_HWNDNEXT))
{
if ((ATOM)::GetProp(hWnd, "APP_ID") == m_aAppId)
{
if (::IsIconic(hWnd)) ::ShowWindow(hWnd, SW_RESTORE); //还原最小化的窗口
::SetForegroundWindow(hWnd); //激活窗口
m_aAppId = 0; //赋值0是为了防止ExitInstance中将找到的ATOM量删除
break;
}
}
return FALSE;
}
else //程序ID不存在,创建程序ID
{
m_aAppId = ::GlobalAddAtom(APP_ID);
}

  3、在InitInstance方法最后为主窗口添加标识属性:
::SetProp(m_pMainWnd->m_hWnd, "APP_ID", (HANDLE)m_aAppId);

  4、在ExitInstance方法中添加下面代码以删除程序ID:
if (m_aAppId) ::GlobalDeleteAtom(m_aAppId);

心得:该方法所用到的ATOM量是一个应用广泛的技术,如::CreateMutex、::SetProp等API函数都间接用到了ATOM量。利用它,我们可以做很多需要用到唯一性验证的事情。

posted @ 2010-06-21 22:47 lhking 阅读(586) | 评论 (0)编辑 收藏
MFC消息映射机制的剖析

一,消息映射机制

1,消息响应函数:(例:在CDrawView类响应鼠标左键按下消息)
1)在头文件(DrawView.h)中声明消息响应函数原型。
//{{AFX_MSG(CDrawView)   //注释宏
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
//}}AFX_MSG   //注释宏
说明:
在注释宏之间的声明在VC中灰色显示。afx_msg宏表示声明的是一个消息响应函数。
2)在源文件(DrawView.cpp)中进行消息映射。
BEGIN_MESSAGE_MAP(CDrawView, CView)
//{{AFX_MSG_MAP(CDrawView)
ON_WM_LBUTTONDOWN()
//}}AFX_MSG_MAP
// Standard printing commands
ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview)
END_MESSAGE_MAP()
说明:
在宏BEGIN_MESSAGE_MAP()与END_MESSAGE_MAP()之间进行消息映射。
宏ON_WM_LBUTTONDOWN()把消息WM_LBUTTONDOWN与它的响应函数OnLButtonDown()相关联。这样一旦有消息的产生,就会自动调用相关联的消息响应函数去处理。
宏ON_WM_LBUTTONDOWN()定义如下:
#define ON_WM_LBUTTONDOWN() \
{ WM_LBUTTONDOWN, 0, 0, 0, AfxSig_vwp, \
   (AFX_PMSG)(AFX_PMSGW)(void (AFX_MSG_CALL CWnd::*)(UINT, CPoint))&OnLButtonDown },
3)源文件中进行消息响应函数处理。(DrawView.cpp中自动生成OnLButtonDown函数轮廓,如下)
void CDrawView::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
CView::OnLButtonDown(nFlags, point);
}
说明:
可见当增加一个消息响应处理,在以上三处进行了修改。可在消息响应函数里添加消息处理代码完成对消息的响应、处理。

2,消息响应的方式:
1)在基类中针对每种消息做一个虚函数,当子类对消息响应时候,只要在子类中重写这个虚函数即可。缺点:MFC类派生层次很多,如果在基类对每个消息进行虚函数处理,那么从基类派生的每个子类都将背负一个庞大的虚表,这样浪费内存,故MFC没有采取这中方式而采取消息映射方式。
2)消息映射方式:MFC在后台维护了一个句柄和C++对象指针对照表,当收到一个消息后,通过消息结构里资源句柄(查对照表)就可找到与它对应的一个C++对象指针,然后把这个指针传给基类,基类利用这个指针调用WindowProc()函数对消息进行处理,WindowProc()函数中调用OnWndMsg()函数,真正的消息路由及处理是由OnWndMsg()函数完成的。由于WindowProc()和OnWndMsg()都是虚函数,而且是用派生类对象指针调用的,由多态性知最总终调用子类的。在OnWndMsg()函数处理的时候,根据消息种类去查找消息映射,判断所发的消息有没有响应函数,具体方式是到相关的头文件和源文件中寻找消息响应函数声明(从注释宏//{{AFX_MSG(CDrawView)...//}}AFX_MSG之间寻找),消息映射(从宏BEGIN_MESSAGE_MAP(...)....END_MESSAGE_MAP()之间寻找),最终找到对应的消息处理函数。当然,如果子类中没有对消息进行处理,则消息交由基类处理。
说明:
virtual LRESULT WindowProc(UINT message, WPARAM wParam, LPARAM lParam);
virtual BOOL OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult);

 

二,有关绘图

1,使用SDK获取DC句柄:
HDC hdc;
hdc=::GetDc(m_hWnd);//获取DC句柄
MoveToEx(hdc,m_ptOrigin.x,m_ptOrigin.y,NULL);
LineTo(hdc,point.x,point.y);
::ReleaseDC(m_hWnd,hdc);//释放DC

2,利用CDC类指针和CWin类成员函数获取DC。
CDC *pDC=GetDC();
pDC->MoveTo(m_ptOrigin);
pDC->LineTo(point);
ReleaseDC(pDC);

3,利用CClientDC对象。(CClientDC类从CDC类派生来的)
CClientDC dc(this);
dc.MoveTo(m_ptOrigin);
dc.LineTo(point);
说明:
The CClientDC class is derived from CDC and takes care of calling the Windows functions GetDC at construction time and ReleaseDC at destruction time. This means that the device context associated with a CClientDC object is the client area of a window.

4,利用CWindowDC对象。(CWindowDC类从CDC类派生来的)
CWindowDC dc(this);//
dc.MoveTo(m_ptOrigin);
dc.LineTo(point);
说明:
The CWindowDC class is derived from CDC. It calls the Windows functionsGetWindowDC at construction time andReleaseDC at destruction time. This means that a CWindowDC object accesses the entire screen area of a CWnd (both client and nonclient areas).

5,GetParent()得到父窗口指针;GetDesktopWindow()得到屏幕窗口指针。

6,利用画笔改变线条颜色和类型:
CPen pen(PS_DOT,1,RGB(0,255,0));//构造画笔对象
CClientDC dc(this);CPen *pOldPen=dc.SelectObject(&pen);//将画笔选入DC
dc.MoveTo(m_ptOrigin);
dc.LineTo(point);
dc.SelectObject(pOldPen);//恢复先前的画笔

7,使用画刷(通常利用画刷去填充矩形区域):
使用单色画刷
CBrush brush(RGB(255,0,0));//构造画刷对象
CClientDC dc(this);
dc.FillRect(CRect(m_ptOrigin,point),&brush);//用指定的画刷去填充矩形区域

使用位图画刷
CBitmap bitmap;//构造位图对象(使用前需要初试化)
bitmap.LoadBitmap(IDB_BITMAP1);//初试化位图对象
CBrush brush(&bitmap);//构造位图画刷
CClientDC dc(this);
dc.FillRect(CRect(m_ptOrigin,point),&brush);//用指定的位图画刷去填充矩形区域

使用透明画刷
CBrush *pBrush=CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));//获取透明画刷对象指针
CClientDC dc(this);
CBrush *pOldBrush=dc.SelectObject(pBrush);//将透明画刷选入DC
dc.Rectangle(CRect(m_ptOrigin,point));
dc.SelectObject(pOldBrush);//释放透明画刷
说明:
The GetStockObject function retrieves a handle to one of the predefined stock pens, brushes, fonts, or palettes.
HGDIOBJ GetStockObject(
int fnObject   // type of stock object
);

Returns a pointer to a CBrush object when given a handle to a Windows HBRUSH object.
static CBrush* PASCAL FromHandle( HBRUSH hBrush );//FromHandle是一个静态方法,故可用CBrush::FromHandle()形式调用。
注意点:
1)静态方法不属于某一个具体对象,而属于类本身,在类加载的时候就已经为类静态方法分配了代码去,故可用CBrush::FromHandle()形式调用。
2)静态方法中,不能引用非静态的数据成员和方法。
3)静态数据成员需要在类外单独做初始化,形式如: 变量类型 类名::变量名=初始值;

8,CDC::SetROP2方法:
int SetROP2( int nDrawMode );
Sets the current drawing mode.  

posted @ 2010-06-21 22:43 lhking 阅读(267) | 评论 (0)编辑 收藏

1,寻找WinMain人口:
在安装目录下找到MFC文件夹下的SRC文件夹,SRC下是MFC源代码。
路径:MFC|SRC|APPMODUL.CPP:
_tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPTSTR lpCmdLine, int nCmdShow)
{
// call shared/exported WinMain
return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
}
注意:(#define _tWinMain   WinMain)

2,对于全局对象或全局变量来说,在程序运行即WINMAIN函数加载的时候,已经为全局对象或全局变量分配了内存和赋初值。
所以:CTEApp theApp;->CTEApp ::CTEApp(){}->_tWinMain(){}
说明:每一个MFC程序,有且只有一个从WinApp类派生的类(应用程序类),也只有一个从应用程序类所事例化的对象,表示应用程序本身。在WIN32程序当中,表示应用程序是通过WINMAIN入口函数来表示的(通过一个应用程序的一个事例号这一个标识来表示的)。在基于MFC应用程序中,是通过产生一个应用程序对象,用它来唯一的表示了应用程序。

3,通过构造应用程序对象过程中调用基类CWinApp的构造函数,在CWinApp的构造函数中对程序包括运行时一些初始化工作完成了。
CWinApp构造函数:MFC|SRC|APPCORE.CPP
CWinApp::CWinApp(LPCTSTR lpszAppName){...}//带参数,而CTEApp构造函数没有显式向父类传参,难道CWinApp()有默认参数?见下:
(在CWinApp类定义中, CWinApp(LPCTSTR lpszAppName = NULL); )
注意:CWinApp()函数中:
pThreadState->m_pCurrentWinThread = this;
pModuleState->m_pCurrentWinApp = this
(this指向的是派生类CTEApp对象,即theApp)
调试:CWinApp::CWinApp();->CTEApp theApp;(->CTEApp ::CTEApp())->CWinApp::CWinApp()->CTEApp ::CTEApp()->_tWinMain(){}

4,_tWinMain函数中通过调用AfxWinMain()函数来完成它要完成的功能。(Afx*前缀代表这是应用程序框架函数,是一些全局函数,应用程序框架是一套辅助生成应用程序的框架模型,把一些类做一些有机的集成,我们可根据这些类函数来设计自己的应用程序)。
AfxWinMain()函数路径:MFC|SRC|WINMAIN.CPP:
在AfxWinMain()函数中:
CWinApp* pApp = AfxGetApp();
说明:pApp存储的是指向WinApp派生类对象(theApp)的指针。
//_AFXWIN_INLINE CWinApp* AFXAPI AfxGetApp()
// { return afxCurrentWinApp; }

调用pThread->InitInstance()
说明:pThread也指向theApp,由于基类中virtual BOOL InitApplication()定义为虚函数,所以调用pThread->InitInstance()时候,调用的是派生类CTEApp的InitInstance()函数。

nReturnCode = pThread->Run();
说明:pThread->Run()完成了消息循环。

5,注册窗口类:AfxEndDeferRegisterClass();
AfxEndDeferRegisterClass()函数所在文件:MFC|SRC|APPCORE.CPP
BOOL AFXAPI AfxEndDeferRegisterClass(LONG fToRegister){...}
说明:设计窗口类:在MFC中事先设计好了几种缺省的窗口类,根据不同的应用程序的选择,调用AfxEndDeferRegisterClass()函数注册所选择的窗口类。
调试:CWinApp::CWinApp();->CTEApp theApp;(->CTEApp ::CTEApp())->CWinApp::CWinApp()->CTEApp ::CTEApp()->_tWinMain(){}//进入程序
->AfxWinMain();->pApp->InitApplication();->pThread->InitInstance()//父类InitInstance虚函数;->CTEApp::InitInstance()//子类实现函数;->AfxEndDeferRegisterClass(LONG fToRegister)//注册所选择的窗口类(出于文档管理,注册提前,正常的应在PreCreateWindow中进行注册)//之后进入创建窗口阶段(以下再不做调试)

6,PreCreateWindow()://主要是注册窗口类
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
if( !CFrameWnd::PreCreateWindow(cs) )
   return FALSE;
return TRUE;
}
说明:
CFrameWnd::PreCreateWindow()函数所在文件:MFC|SRC|WINFRM.CPP
BOOL CFrameWnd::PreCreateWindow(CREATESTRUCT& cs)
{
if (cs.lpszClass == NULL)
{
   VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG));
    //判断AFX_WNDFRAMEORVIEW_REG型号窗口类是否注册,如果没有注册则注册
   cs.lpszClass = _afxWndFrameOrView; // COLOR_WINDOW background
    //把注册后的窗口类名赋给cs.lpszClass
}

if ((cs.style & FWS_ADDTOTITLE) && afxData.bWin4)
   cs.style |= FWS_PREFIXTITLE;

if (afxData.bWin4)
   cs.dwExStyle |= WS_EX_CLIENTEDGE;

return TRUE;
}

其中:
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);//PreCreateWindow()是个虚函数,如果子类有则调用子类的。
#define VERIFY(f)          ASSERT(f)
#define AfxDeferRegisterClass(fClass) AfxEndDeferRegisterClass(fClass)
define AFX_WNDFRAMEORVIEW_REG          0x00008
const TCHAR _afxWndFrameOrView[] = AFX_WNDFRAMEORVIEW;//WINCORE.CPP文件中,定义为全局数组。
//#define AFX_WNDFRAMEORVIEW AFX_WNDCLASS("FrameOrView")

7,创建窗口:
Create()函数路径:MFC|SRC|WINFRM.CPP:
CFrameWnd::Create(...){
...
CreateEx(...);//从父类继承来的,调用CWnd::CreateEx().
...
}

CWnd::CreateEx()函数路径:MFC|SRC|WINCORE.CPP
BOOL CWnd::CreateEx(...){
...
if (!PreCreateWindow(cs))//虚函数,如果子类有调用子类的。
{
   PostNcDestroy();
   return FALSE;
}
...
HWND hWnd = ::CreateWindowEx(cs.dwExStyle, cs.lpszClass,
   cs.lpszName, cs.style, cs.x, cs.y, cs.cx, cs.cy,
   cs.hwndParent, cs.hMenu, cs.hInstance, cs.lpCreateParams);

...
}
说明:CreateWindowEx()函数与CREATESTRUCT结构体参数的对应关系,使我们在创建窗口之前通过可PreCreateWindow(cs)修改cs结构体成员来修改所要的窗口外观。PreCreateWindow(cs))//是虚函数,如果子类有调用子类的。
HWND CreateWindowEx(
DWORD dwExStyle,     
LPCTSTR lpClassName,
LPCTSTR lpWindowName,
DWORD dwStyle,       
int x,               
int y,               
int nWidth,          
int nHeight,         
HWND hWndParent,     
HMENU hMenu,         
HINSTANCE hInstance,
LPVOID lpParam       
);
typedef struct tagCREATESTRUCT { // cs
    LPVOID    lpCreateParams;
    HINSTANCE hInstance;
    HMENU     hMenu;
    HWND      hwndParent;
    int       cy;
    int       cx;
    int       y;
    int       x;
    LONG      style;
    LPCTSTR   lpszName;
    LPCTSTR   lpszClass;
    DWORD     dwExStyle;
} CREATESTRUCT;

8,显示和更新窗口:
CTEApp类,TEApp.cpp中
m_pMainWnd->ShowWindow(SW_SHOW);//显示窗口,m_pMainWnd指向框架窗口
m_pMainWnd->UpdateWindow();//更新窗口
说明:
class CTEApp : public CWinApp{...}
class CWinApp : public CWinThread{...}
class CWinThread : public CCmdTarget
{
...
public:
CWnd* m_pMainWnd;
...
...
}

9,消息循环:
int AFXAPI AfxWinMain()
{ ...
// Perform specific initializations
if (!pThread->InitInstance()){...}
//完成窗口初始化工作,完成窗口的注册,完成窗口的创建,显示和更新。
nReturnCode = pThread->Run();
//继承基类Run()方法,调用CWinThread::Run()来完成消息循环
...
}
////////////////////////////////////////////////////////////////
CWinThread::Run()方法路径:MFC|SRC|THRDCORE.CPP
int CWinThread::Run()
{ ...
   // phase2: pump messages while available
   do//消息循环
   {
    // pump message, but quit on WM_QUIT
    if (!PumpMessage())//取消息并处理
     return ExitInstance();
    ...
   } while (::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE));
...
}
说明:
BOOL PeekMessage(,,,,)函数说明
The PeekMessage function checks a thread message queue for a message and places the message (if any) in the specified structure.
If a message is available, the return value is nonzero.
If no messages are available, the return value is zero.

/////////////////////////////////////////////////////////////
BOOL CWinThread::PumpMessage()
{
...
if (!::GetMessage(&m_msgCur, NULL, NULL, NULL))//取消息
{...}
...
// process this message
if (m_msgCur.message != WM_KICKIDLE && !PreTranslateMessage(&m_msgCur))
{
   ::TranslateMessage(&m_msgCur);//进行消息(如键盘消息)转换
   ::DispatchMessage(&m_msgCur);//分派消息到窗口的回调函数处理(实际上分派的消息经过消息映射,交由消息响应函数进行处理。)
}
return TRUE;
}

9,文档与视结构:
可以认为View类窗口是CMainFram类窗口的子窗口。
DOCument类是文档类。
DOC-VIEW结构将数据本身与它的显示分离开。
文档类:数据的存储,加载
视类:数据的显示,修改

10,文档类,视类,框架类的有机结合:
在CTEApp类CTEApp::InitInstance()函数中通过文档模板将文档类,视类,框架类的有机组织一起。
...
CSingleDocTemplate* pDocTemplate;
pDocTemplate = new CSingleDocTemplate(
IDR_MAINFRAME,
RUNTIME_CLASS(CTEDoc),
RUNTIME_CLASS(CMainFrame),       // main SDI frame window
RUNTIME_CLASS(CTEView));
AddDocTemplate(pDocTemplate);//增加到模板
...


posted @ 2010-06-21 22:43 lhking 阅读(443) | 评论 (0)编辑 收藏
Windows程序内部运行机制

1,windows程序设计是种事件驱动方式的程序设计,主要基于消息的。当用户需要完成某种功能时,需要调用OS某种支持,然后OS将用户的需要包装成消息,并投入到消息队列中,最后应用程序从消息队列中取走消息并进行响应。
2,消息结构:
typedef struct tagMSG {     // msg
    HWND   hwnd;     //接收消息的窗口句柄。和哪个窗口相关联。
    UINT   message; //消息标识。消息本身是什么。
    WPARAM wParam;   //消息的附加信息。具体取决于消息本身。  
    LPARAM lParam;
    DWORD time;     //消息投递时间。
    POINT pt;       //消息投递时,光标在屏幕上的位置。
} MSG;

3,消息队列:
每个应用程序OS都为它建立一个消息队列,消息队列是个先进先出的缓冲区,其中每个元素都是一个消息,OS将生成的每个消息按先后顺序放进消息队列中,应用程序总是取走当前消息队列中的第一条消息,应用程序取走消息后便知道用户的操作和程序的状态,然后对其处理即消息响应,消息响应通过编码实现。

4,使用VC编程除了良好的C基础外还需要掌握两方面:
一,消息本身。不同消息所代表的用户操作和应用程序的状态。
二,对于某个特定的消息来说,要让OS执行某个特定的功能去响应消息。

5,Window程序入口:
int WINAPI WinMain(
HINSTANCE hInstance, // 当前事例句柄。
HINSTANCE hPrevInstance, // 先前事例句柄。
LPSTR lpCmdLine,      // 命令行指针
int nCmdShow          // (窗口)显示的状态
);
说明:WinMain函数是Windows程序入口点函数,由OS调用,当OS启动应用程序的时候,winmain函数的参数由OS传递的。

6,创建一个完整的窗口需要经过下面四个操作步骤:
一,设计一个窗口类;如:WNDCLASS wndcls;
二,注册窗口类;    如:RegisterClass(&wndcls);
三,创建窗口;      如:CreateWindow(),CreateWindowEX();
四,显示及更新窗口。如:ShowWindow(),UpdateWindow();

说明:创建窗口的时候一定要基于已经注册的窗口类.

7,Windows提供的窗口类:
typedef struct _WNDCLASS {
    UINT    style;        //窗口的类型
    WNDPROC lpfnWndProc; //窗口过程函数指针(回调函数)
    int     cbClsExtra; //窗口类附加字节,为该类窗口所共享。通常0。
    int     cbWndExtra; //窗口附加字节。通常设为0。
    HANDLE hInstance; //当前应用程序事例句柄。
    HICON   hIcon;      //图标句柄 LoadIcon();
    HCURSOR hCursor;    //光标句柄 LoadCursor();
    HBRUSH hbrBackground; //画刷句柄 (HBRUSH)GetStockObject();
    LPCTSTR lpszMenuName; //菜单名字
    LPCTSTR lpszClassName; //类的名字
} WNDCLASS;

8,窗口类注册:
ATOM RegisterClass(
CONST WNDCLASS *lpWndClass   // address of structure with class
                               // data
);

9,创建窗口:
HWND CreateWindow(
LPCTSTR lpClassName, // pointer to registered class name
LPCTSTR lpWindowName, // pointer to window name
DWORD dwStyle,        // window style
int x,                // horizontal position of window
int y,                // vertical position of window
int nWidth,           // window width
int nHeight,          // window height
HWND hWndParent,      // handle to parent or owner window
HMENU hMenu,          // handle to menu or child-window identifier
HANDLE hInstance,     // handle to application instance
LPVOID lpParam        // pointer to window-creation data
);

10,显示和更新窗口窗口:
BOOL ShowWindow(
HWND hWnd,     // handle to window
int nCmdShow   // show state of window
);
BOOL UpdateWindow(
HWND hWnd   // handle of window
);

11,消息循环:
MSG msg;
while(GetMessage(&msg,...))    //从消息队列中取出一条消息
{
TranslateMessage(&msg); //进行消息(如键盘消息)转换
DispatchMessage(&msg); //分派消息到窗口的回调函数处理,(OS调用窗口回调函数进行处理)。
}

其中:
//**The GetMessage function retrieves a message from the calling thread's message queue and places it in the specified structure.
//**If the function retrieves a message other than WM_QUIT, the return value is nonzero.If the function retrieves the WM_QUIT message, the return value is zero. If there is an error, the return value is -1.

BOOL GetMessage(
LPMSG lpMsg,         // address of structure with message
HWND hWnd,           // handle of window
UINT wMsgFilterMin, // first message
UINT wMsgFilterMax   // last message
);


//The TranslateMessage function translates virtual-key messages into character messages. The character messages are posted to the calling thread's message queue, to be read the next time the thread calls the GetMessage or PeekMessage function.
BOOL TranslateMessage(
CONST MSG *lpMsg   // address of structure with message
);

//The DispatchMessage function dispatches a message to a window procedure.
LONG DispatchMessage(
CONST MSG *lpmsg   // pointer to structure with message
);


12,窗口过程函数(回调函数)原型:
The WindowProc function is an application-defined function that processes messages sent to a window. The WNDPROC type defines a pointer to this callback function. WindowProc is a placeholder(占位符) for the application-defined function name.

LRESULT CALLBACK WindowProc( //这里WindowProc是个代号名字。
HWND hwnd,      // handle to window
UINT uMsg,      // message identifier
WPARAM wParam, // first message parameter
LPARAM lParam   // second message parameter
);

说明:两种函数调用约定(__stdcall 和 __cdecl):
#define CALLBACK    __stdcall
//__stdcall 标准调用预定,是PASCAL 调用约定,象DELPHI使用的就是标准调用约定
#define WINAPIV     __cdecl
// __cdecl 是C 语言形式的调用约定。


主要区别:函数参数传递顺序 和 对堆栈的清除上。
问题:除了那些可变参数的函数调用外,其余的一般都是__stdcall约定。但 C/C++编译默然的是__cdecl约定。所以如果在VC等环境中调用__stdcall约定的函数,必须要在函数声明的时加上 __stdcall 修饰符,以便对这个函数的调用是使用__stdcall约定(如使用DELPHI编写的DLL时候)。
(VC中可通过这途径修改:project|settings..|c/c++|...)


在窗口过程函数中通过一组switch语句来对消息进行处理:
如:
LRESULT CALLBACK WindowProc(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam  
)
{
    switch(uMsg)
    {
case WM_PAINT:
   ...
   break;
case ...
   break;
case WM_CLOSE:
   //DestroyWindow(hwnd);
    //销毁窗口,并发送WM_DESTROY消息。
   break;
case WM_DESTROY:
   //PostQuitMessage(0);
   //发送WM_QUIT消息到消息队列中,请求终止。
         //GetMessage()取到WM_QUIT消息后,返回0,退出消息循                //   环,从而终止应用程序。
   break;
default:
   return DefWindowProc(hwnd,uMsg,wParam,lParam);
//用缺省的窗口过程处理我们不感兴趣的消息(其它消息)。
//这是必须的。
    }//switch
return 0;
}//WindowProc

13,DestroyWindow()函数和PostQuitMessage()函数原型:
//**The DestroyWindow function destroys the specified window. The function sends WM_DESTROY and WM_NCDESTROY messages。

BOOL DestroyWindow(
HWND hWnd   // handle to window to destroy
);

//**The PostQuitMessage function indicates to the system that a thread has made a request to terminate (quit). It is typically used in response to a WM_DESTROY message.
//**The PostQuitMessage function posts a WM_QUIT message to the thread's message queue and returns immediately; the function simply indicates(预示,通知) to the system that the thread is requesting to quit at some time in the future.

When the thread retrieves the WM_QUIT message from its message queue, it should exit its message loop and return control to the system.

VOID PostQuitMessage(
int nExitCode   // exit code
);

14,关于DC句柄获取:
a)使用BeginPaint(),EndPaint()对。注意只能在响应WM_PAINT消息时使用。
b)使用GetDc(),ReleaseDC()对。注意他们不能在响应WM_PAINT中使用。

posted @ 2010-06-21 22:41 lhking 阅读(125) | 评论 (0)编辑 收藏
仅列出标题
共3页: 1 2 3 

导航

<2024年12月>
24252627282930
1234567
891011121314
15161718192021
22232425262728
2930311234

统计

常用链接

留言簿

随笔档案

搜索

最新评论

阅读排行榜

评论排行榜