MFC程序及WinAPI窗口程序

MFC

1 基本概念

WinAPI:Windows平台下的系统调用,Windows.h 调用系统提供的特殊接口,得到系统的资源

窗口:父窗口和子窗口,客户区和非客户区

句柄:结构体变量,窗口句柄HWND,图标句柄HICO

消息队列 -> 消息 -> 窗口过程函数

WinMain() WINAPI 入口地址

2 WinAPI窗口程序

1. WinAPI创建窗口流程

  1. 定义入口函数WinMain()
  2. 创建一个窗口
    a) 设计窗口类WNDCLASS (给成员变量赋值)
    b) 注册窗口类
    c) 显示和更新窗口
  3. 消息循环
  4. 窗口过程函数

在这里插入图片描述

2. WinAPI定义

WinMain() WinAPI 入口地址

#include <windows.h>
int WINAPI WinMain(HINSTANCE hInstance, // 应用程序实例
    HINSTANCE hPrevInstance, // 上一个应用程序实例,不用
    LPSTR lpCmdLine, // 命令行参数  long pointer string -> char *
    int nCmdShow // 窗口显示的样式 最大化,最小化
    )
{
    
}

3. 创建窗口

#include <windows.h>

LRESULT CALLBACK DealMessage(HWND hWnd,
    UINT uMsg,
    WPARAM wParam,
    LPARAM lParam
    )
{
    return DefWindowProc(hWnd, uMsg, wParam, lParam);
}

/*
    1. 定义入口函数WinMain()
    2. 创建一个窗口
       a) 设计窗口类WNDCLASS (给成员变量赋值)
       b) 注册窗口类
       c) 创建窗口类
       d) 显示和更新窗口
    3. 消息循环
    4. 窗口过程函数
*/

// WINAPI: 修饰函数;规定函数参数入栈从右往左,函数调用完毕,自动清理堆栈
// CALLBACK 
int WINAPI WinMain(HINSTANCE hInstance, // 应用程序实例
    HINSTANCE hPrevInstance, // 上一个应用程序实例,不用
    LPSTR lpCmdLine, // 命令行参数  long pointer string -> char *
    int nCmdShow // 窗口显示的样式 最大化,最小化
)
{
    // 设计窗口类WNDCLASS (给成员变量赋值)
    WNDCLASS wc;
    wc.cbClsExtra = 0; // 类的附加信息
    wc.cbWndExtra = 0; // 窗口附加信息
    // 获取系统默认的白色画刷
    wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); // 背景色,必须写

    // wc.hCursor = NULL; // 使用默认
    wc.hCursor = LoadCursor(NULL, IDC_HELP); // 加载系统默认光标
    // wc.hIcon = NULL;
    wc.hIcon = LoadIcon(NULL, IDI_WARNING); // 加载系统默认图标

    wc.hInstance = hInstance; // 应用程序实列
    wc.lpfnWndProc = DealMessage; // 窗口过程函数名字, 消息处理函数
    
    wc.lpszClassName = TEXT("abc"); // 类的名字
    wc.lpszMenuName = NULL; // 菜单名字
    wc.style = nCmdShow; // 显示风格

    // b) 注册窗口类 告诉系统窗口过程函数的入口地址
    RegisterClass(&wc);

    // c) 创建窗口类
    HWND hWnd = CreateWindow(TEXT("abc"),
        TEXT("hello, windows"),
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        NULL,
        NULL,
        hInstance,
        NULL
        );

    // d) 显示和更新窗口
    ShowWindow(hWnd, SW_SHOWNORMAL);
    UpdateWindow(hWnd);

}

4. 消息循环

 // 消息循环
    MSG msg; // 定义一个消息
    // 如果接受到WM_QUIT,返回0,退出
    // 如果出错,返回-1,不退出
    while (GetMessage(&msg, NULL, 0, 0)) {
        // 翻译工作
        // 虚拟按键转为标准字符WM_CHAR
        TranslateMessage(&msg);
        // 给操作系统分发消息
        DispatchMessage(&msg);
    }

    return msg.wParam;

在这里插入图片描述

5. 窗口过程函数

操作系统调用程序中一个专门负责处理消息的函数,这个函数称为窗口过程

// 窗口过程函数
LRESULT CALLBACK DealMessage(HWND hWnd, // 窗口句柄,消息所属的窗口
    UINT uMsg, // 消息标志
    WPARAM wParam, // 附加信息
    LPARAM lParam
    )
{
    switch (uMsg)
    {
    case WM_LBUTTONDOWN: // 鼠标左键按下
        MessageBox(hWnd, TEXT("ABC"), TEXT("鼠标左键按下"), MB_OK);
        break;
    case WM_PAINT:
    {
        PAINTSTRUCT ps; // 绘图结构体
        HDC dc = BeginPaint(hWnd, &ps); // 开始
        TCHAR* p = L"AAAAAAAA"; // 转为宽字节
        TextOut(dc, 30, 30, p, wcslen(p));

        EndPaint(hWnd, &ps);
        break;
    }
    case WM_DESTROY:
        PostQuitMessage(2); // WM_QUIT, 给主函数 return msg.wParam
    default:
        return DefWindowProc(hWnd, uMsg, wParam, lParam);
    }
    return 0;
}

6. WinAPI窗口程序源代码

#include <windows.h>

// 窗口过程函数
LRESULT CALLBACK DealMessage(HWND hWnd, // 窗口句柄,消息所属的窗口
    UINT uMsg, // 消息标志
    WPARAM wParam, // 附加信息
    LPARAM lParam
    )
{
    switch (uMsg)
    {
    case WM_LBUTTONDOWN: // 鼠标左键按下
        MessageBox(hWnd, TEXT("ABC"), TEXT("鼠标左键按下"), MB_OK);
        break;
    case WM_PAINT:
    {
        PAINTSTRUCT ps; // 绘图结构体
        HDC dc = BeginPaint(hWnd, &ps); // 开始
        TCHAR* p = L"AAAAAAAA"; // 转为宽字节
        TextOut(dc, 30, 30, p, wcslen(p));

        EndPaint(hWnd, &ps);
        break;
    }
    case WM_DESTROY:
        PostQuitMessage(2); // WM_QUIT, 给主函数 return msg.wParam
    default:
        return DefWindowProc(hWnd, uMsg, wParam, lParam);
    }
    return 0;
}

/*
    1. 定义入口函数WinMain()
    2. 创建一个窗口
       a) 设计窗口类WNDCLASS (给成员变量赋值)
       b) 注册窗口类
       c) 创建窗口类
       d) 显示和更新窗口
    3. 消息循环
    4. 窗口过程函数
*/

// WINAPI: 修饰函数;规定函数参数入栈从右往左,函数调用完毕,自动清理堆栈
// CALLBACK 
int WINAPI WinMain(HINSTANCE hInstance, // 应用程序实例
    HINSTANCE hPrevInstance, // 上一个应用程序实例,不用
    LPSTR lpCmdLine, // 命令行参数  long pointer string -> char *
    int nCmdShow // 窗口显示的样式 最大化,最小化
)
{
    // 设计窗口类WNDCLASS (给成员变量赋值)
    WNDCLASS wc;
    wc.cbClsExtra = 0; // 类的附加信息
    wc.cbWndExtra = 0; // 窗口附加信息
    // 获取系统默认的白色画刷
    wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); // 背景色,必须写

    // wc.hCursor = NULL; // 使用默认
    wc.hCursor = LoadCursor(NULL, IDC_HELP); // 加载系统默认光标
    // wc.hIcon = NULL;
    wc.hIcon = LoadIcon(NULL, IDI_WARNING); // 加载系统默认图标

    wc.hInstance = hInstance; // 应用程序实列
    wc.lpfnWndProc = DealMessage; // 窗口过程函数名字, 消息处理函数
    
    wc.lpszClassName = TEXT("abc"); // 类的名字
    wc.lpszMenuName = NULL; // 菜单名字
    wc.style = nCmdShow; // 显示风格

    // b) 注册窗口类 告诉系统窗口过程函数的入口地址
    RegisterClass(&wc);

    // c) 创建窗口类
    HWND hWnd = CreateWindow(TEXT("abc"),
        TEXT("hello, windows"),
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        NULL,
        NULL,
        hInstance,
        NULL
        );

    // d) 显示和更新窗口
    ShowWindow(hWnd, SW_SHOWNORMAL);
    UpdateWindow(hWnd);

    // 消息循环
    MSG msg; // 定义一个消息
    // 如果接受到WM_QUIT,返回0,退出
    // 如果出错,返回-1,不退出
    while (GetMessage(&msg, NULL, 0, 0)) {
        // 翻译工作
        // 虚拟按键转为标准字符WM_CHAR
        TranslateMessage(&msg);
        // 给操作系统分发消息
        DispatchMessage(&msg);
    }

    return msg.wParam;
}

3 MFC程序

1. MFC创建窗口流程

  1. 应用程序类 CWinApp
  2. 框架类 CFrameWnd

创建 mfc.hmfc.cpp 文件

mfc.h 文件中

#pragma once

#include <afxwin.h>

// 1) 应用程序类  CWinApp
class MyApp : public CWinApp
{
public:
	// 基类的虚函数
	// 派生类只是重写
	// MFC程序的入口地址
	virtual BOOL InitInstance();
};


// 2) 框架类 CFrameWnd
class MyFrame : public CFrameWnd
{
public:
	MyFrame();

private:

};

mfc.cpp 文件中

#include "mfc.h"

// 有且只有一个全局的应用程序类对象
MyApp myApp;

// 程序入口的地址
BOOL MyApp::InitInstance()
{
	/*
	*	1.创建框架类对象
	*	2.显示窗口
	*	3.更新窗口
	*	4.保存框架类对象指针
	*/

	// 1.创建框架类对象
	MyFrame* frame = new MyFrame; // 构造函数

	// 2.显示窗口
	frame->ShowWindow(SW_SHOWNORMAL);

	// 3.更新窗口
	frame->UpdateWindow();

	// 4.保存框架类对象指针
	m_pMainWnd = frame;


	return TRUE;
}

MyFrame::MyFrame()
{
	// CWnd类的成员函数 CWnd::Create
	// CFrameWnd 继承于 CWnd

	Create(NULL, TEXT("MFC"));
}

2. MFC流程

  1. 有且只有一个全局的应用程序对象
  2. 在程序入口函数实现功能 InitInstance()
    1. 给框架类MyFrame对象动态分配空间(自动调用它的构造函数)
      1. 框架类MyFrame对象构造函数函数里创建窗口 CWnd::Create
    2. 框架类对象显示窗口 CWnd::ShowWindow
    3. 框架类对象更新窗口 CWnd::UpdateWindow
    4. 保存框架类对象指针 CWinThread::m_PMainWnd

3. 消息映射

事件处理:

消息映射

  1. 所操作类中,声明消息映射宏

DECLARE_MESSAGE_MAP()

  1. 对应的.cpp 定义宏

BEGIN_MESSAGE_MAP(MyFrame, CFrameWnd) // 派生类名 基类名
ON_WM_LBUTTONDOWN() // 消息映射入口标志
END_MESSAGE_MAP()

  1. 对应类中,消息处理函数的声明
  2. 对应.cpp 消息处理函数的定义
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
WINAPI,WinAPI手册,最全的WINAPI函数手册,WINAPI开发必备 目录 1 内容简介 14 前言 14 第一章 Win32 API概论 14 为什么使用 Wiu32 API 14 Win32 API 简介 15 第二章 窗口管理函数 16 第一节 易用特性函数(Accessibility Features) 20 SoundSentryProc 20 SystemParametersinfo 21 第二节 按钮函数(Button) 34 CheckDlgButton 34 CheckRadioButton 34 IsDlgButtonChecked 35 第三节 插入标记(^)函数(Caret) 36 CreateCaret 36 DestroyCaret 37 GetCaretBlinkTime 37 GetCaretPos 38 HideCaret 38 SetCaretBlinkTime 39 SetCaretPos 39 ShowCaret 40 第四节 组合框函数(Combo box) 40 CCHookProc 40 CFHookProc 42 ChooseColor 43 ChooseFont 44 CommDlgExtendedEorror 45 DlgDirListComboBox 47 DlgDirSelectEx 49 DlgDirSelectComboBoxEx 50 FindText 50 FRHookProc 51 GetFileTitle 52 GetOpenFileName 53 GetSaveFileName 54 OFNGookProc 54 OFNHookProcOldStyle 56 PagePaintHook 57 PageSetupDlg 58 pageSetupHook 58 PrintDlg 59 PrintdlgEx 60 PrintHookProc 61 ReplaceText 62 SetupHookProc 63 第五节 标函数(Cursor) 64 ClipCursor 64 CopyCursor 65 CreateCursor 65 DestroyCursor 66 GetClipCursor 67 GetCursor 67 GetCursorpos 67 LoadCursorFromFile 68 SetCursor 68 SetCursorPos 69 SetSystemCursor 69 ShowCursor 70 LoadCursor 71 第六节 对话框函数(Dialog Box) 72 CreateDialog 72 CreateDialoglndirect 73 CreateDialoglndirectParam 74 CreateDialogParam 76 DefDlgProc 77 DialogBox 78 DialogBoxlndirect 79 DialogBoxlndirectParam 80 DialogBoxParam 81 DialogProc 82 EndDialog 83 GetDialogBaseUnits 83 GetDigCtrllD 84 GetDigltem 85 GetDlgltemlnt 85 GetDlgltemText 86 GetNextDlgGroupltem 87 GetNexTDlgTabltem 88 IsDialogMessage 88 MapDialogRect 89 MessageBox 90 MessageBoxEx 93 SendDlgltemMessage 94 SetDlgltemlnt 95 SetDlgltemText 95 MessageBoxlndirect 96 第七节 编辑控制函数(Edit Control) 97 EditWordBreakproc 97 第八节 图标函数(Icon) 98 Copylcon 98 Createlcon 99 CreatelconFromResource 100 CreatelconFromResourceEx 101 Destroylcon 102 Create_cpm_mdorect_ZIWe 102 Drawlcon 103 DrawlconEx 104 ExtractAssociatedlcon 105 Extractlcon 106 ExtractlconEx 106 Getlconlnfo 107 LookuplconldFromDirectory 108 LookuplconldFrom 109 Loadlcon 110 第九节 键盘加速器函数(Keyboard Accelerator) 110 CopyAcceleratorTable 111 CreateAcceleratorTable 111 DestroyAcceleratorTable 112 LoadAccelerators 112 TranslateAccelerator 113 第十节 键盘输入函数(Keyboard Input) 114 ActivateKeyboadLayout 114 EnableWindow 115 GetActiveWindows 116 GetAsyncKeyState 116 GetFocus 117 GetKBCodePage 118 GetKeyboardLayout 118 GetKeyboardLayoutList 118 GetKeyboardLayoutName 119 GetKeyboardState 119 GetKeyNameText 120 GetKeyState 121 IsWindowEnabled 122 keybd_event 122 LoadKeyboardLayout 123 MapVirtualKey 124 MapVlrtualKeyEx 126 OemKeyScan 126 RegisterHotKey 127 SetActiveWindow 128 SetFocus 129 SetKeyboardState 130 ToAscii 130 ToAsciiCxToAsciiCx 131 ToUnicode 132 ToUnicodeEx 133 UnloadKeyboardL 134 UnreglsterHotKey 134 VkKeyScan 135 vkKeyScanEx 136 第十一节 列表框函数(List boX) 136 DlgDirList 136 DlgDirSelectEx(2) 137 第十二节 菜单函数(Menu) 138 CheckMenuRadlol 138 CreateMenu 139 CreatePopupMenu 140 DeleteMenu 140 DestroyMenu 141 DrawMenuBar 141 EnableMenultem 142 GetMenu 143 GetMenuDefaultltem 143 GetMenultemlD 144 GetMenultemlnfo 144 GetMenultemRect 145 getSubMenu 145 GetSystemMenu 146 HlllteMenultem 147 InsertMenultem 147 IsMenu 148 LoadMenu 149 LoadMenulndirect 149 MenultemFromPo 150 RemoveMenu 150 SetMenu 151 SetMenuDefaultltem 151 SetMenultemBitm 152 SetMenultemlnfo 153 TrackPopupMenu 154 TrackPopupMenuEx 155 AppendMenu 156 CheckMenultem 159 GetMenuCheckMarkDimensions 159 GetMenuState 160 GetMenuString 161 InsertMenu 161 ModifyMenu 163 第十三节 消息和消息总队列函数(Message and Message Queue) 165 BroadcastSystemMessage 165 DispatchMessage 166 GetlnputState 167 GetMessage 167 GetMessageExtralnfo 168 GetMessagePos 168 GetMessageTime 169 GetQueueStatus 170 InSendMessage 171 InSendMessageEx 171 PeekMessage 172 PostMessage 173 PostQuitMessage 174 PostThreadMessage 175 RegisterWindowsMessage 176 ReplyMessage 176 SendAsyncProc 177 SendMessage 178 SendMessageCallback 178 SendMessageTImeout 179 SendNotifyMessage 181 SendMessageExtralnfo 181 TranslateMessage 182 WaltMessage 183 PostAppMessage 183 SetMessageQueue 183 第十四节 鼠标输入函数(Mouse Input) 183 DragDetect 183 GetCapture 184 GetDoubleCllckTime 184 GetMouseMovePoints 185 mouse_event 186 ReleaseCapture 188 SetCapture 188 SetDoubleCllckTime 189 SwapMouseButton 189 TrackMouseEvent 190 第十五节 多文档接口函数(Multiple Document Interface) 191 CreateMDIWindow 191 DefFrameProc 192 DefMDIChildProc 193 TranslateMdISysAccel 194 第十六节 资源函数(Resource) 195 BeginUpdateResource 195 CopyImage 195 EndUpdateResource 197 EnumResLangProc 197 EnumResNameProc 198 EnumResourceLanguages 199 EnumResourceNames 200 EnumResourceTypes 200 EnumResTypeProc 201 FlndResource 202 FindResourceEx 203 LoadImage 204 LoadResource 206 LockResource 207 SlzeofResource 207 UpdateResource 208 FreeResource 209 UnlockResource 209 第十七节 滚动条函数(Scroll Bar) 209 EnableScrollBar 209 GetScrolllnfo 210 ScrollDC 211 ScrollWindowEx 212 SetScrolllnfo 214 ShowScrollBar 215 GetScrollPos 215 GetScrollRange 216 ScrollWindow 217 SetScrollPos 218 SetScrollRange 219 第十八节 窗口函数(Window) 220 AdlustWindowRect 220 AdjustWindowRectEx 221 AnImateWindow 222 ArrangelconlcWindows 223 BeginDeferWindowPos 223 BromgWindowToTop 224 CascadeWindows 225 ChildWindowFromaPoint 225 ChildWindowFromaPointEx 226 CloseWindow 227 Create Window 227 CreateWindowEx 233 DeferWindowPos 235 DestroyWindow 238 EnableWindow 238 EndDeferWindowPos 239 EnumChildProc 240 EnumTreadWindows 240 EnumThreadWndProc 241 EnumWindows 241 EnumWindowsProc 242 FindWindow 243 FlndWindowEx 243 GetClientRect 244 GetDesktopWindow 244 GetForegroundWindow 245 GetLastActivePopup 245 GetNextWindow 246 GetParent 246 GetTopWindow 247 GetWindow 247 GetWindowPlacement 248 GetWindowRect 249 GetWindowText 249 IsChild 250 GetWindowTextLength 251 GetWlndowThreadprocessld 251 IsIconic 252 IsWindow 252 IsWindowUnicode 253 IsWindowVlslble 253 IsZoomed 254 MoveWindow 254 Openlcon 255 SetForegroundWindow 255 SetParent 256 SetWindowLong 256 SetWindowPlacement 259 SetWindowPos 260 SetWindowText 263 ShowOwnedPopups 263 ShowWindow 264 ShowWindowAsync 265 TileWindows 266 WindowFromPoint 267 WinMain 267 AnyPopup 269 EnumTaskWindows 269 GetSysModalWindow 269 GetWindowTask 269 SetSysModalWindow 269 第十九节 窗口类函数(Window Class) 270 GetClasslnfoEx 270 GetClassLong 270 GetClassName 271 GetWindowLong 272 RegisterClassEx 273 SetClassLong 274 SetWindowLong 275 GetClasslnfoEx 278 GetClassWord 279 GetWindowWord 279 RegisterClass 279 SetClassWord 280 SetWindowWord 281 第二十节 窗口过程函数(Window Procedure) 281 CallWindowProc 281 DefWindowProc 282 WindowProc 283 第二十一节 窗口属性函数(Window Property) 284 EnumProps 284 EnumPropsEx 284 GetProp 285 PropEnumProcEx 285 RemoveProp 286 SetProp 287
编写一个如示例WinMain的Windows应用程序,其中窗口的背景色、光标、图标等属性可以设置成自己喜欢的风格。作为该示例的扩展,要求编程实现当在该窗口中单击右键时,播放一曲自己喜欢的音乐。(4学时) 编写一个如示例ExDlgCtl的MFC对话框应用程序,其中必须实现的功能包括: “对话框”菜单下的模态对话框、非模态对话框和文件对话框; “控件”菜单下的计算器、动态创建按钮、上网问卷调查、调整对话框背景色等四个对话框。 示例中的其它功能可以根据实际情况决定是否编写。(4学时) 3)编写一个如示例ExMenu的MFC菜单、工具栏和状态栏的应用程序,编写该实验时注意功能的完整性,包括两个菜单的切换、右键弹出式菜单、工具栏的切换、状态栏信息格的添加等功能。(4学时) 4)编写一个如示例ExDraw的MFC绘图应用程序,实现画点、线、矩形、椭圆等图形,并可以根据用户选择的画笔和画刷进行绘制,最后要求实现画笔的功能。(4学时) LRESULT CALLBACK WinProc( HWND hwnd, // handle to window UINT uMsg, // message identifier WPARAM wParam, // first message parameter LPARAM lParam // second message parameter ); int WINAPI WinMain( HINSTANCE hInstance, // handle to current instance HINSTANCE hPrevInstance, // handle to previous instance LPSTR lpCmdLine, // pointer to command line int nCmdShow // show state of window ) { WNDCLASS wndclas; wndclas.cbClsExtra=0; wndclas.cbWndExtra=0; wndclas.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH); wndclas.hCursor=LoadCursor(NULL,IDC_ARROW); wndclas.hIcon=LoadIcon(NULL,IDI_EXCLAMATION); wndclas.hInstance=hInstance; wndclas.lpfnWndProc=WinProc; wndclas.lpszClassName="ABCD"; wndclas.lpszMenuName=NULL; wndclas.style=CS_HREDRAW | CS_VREDRAW; RegisterClass(&wndclas;); void CTestView::OnDialogModal() //模态对话框 { // TODO: Add your command handler code here CTestDlg1 dlg; dlg.DoModal(); } void CTestView::OnDialogModalless()//非模态对话框 { // TODO: Add your command handler code here CTestDlg2 *pd=new CTestDlg2(); pd->Create(IDD_DIALOG2,this); pd->ShowWindow(SW_SHOW); } void CTestView::OnDialogFile() //文件对话框 {
WINDOWS环境 Windows几乎不需要介绍。然而人们很容易忘记Windows给办公室和家庭桌上型计算机所带来的重大改变。Windows在其早期曾经走过一段坎坷的道路,征服桌上型计算机市场的前途一度相当渺茫。 Windows简史 在1981年秋天IBM PC推出之后不久,MS-DOS就已经很明显成为PC上的主流操作系统。MS-DOS代表Microsoft Disk Operating System(磁盘操作系统)。MS-DOS是一个小型的操作系统。MS-DOS提供给用户一种命令列接口,提供如DIR和TYPE的命令,也可以将应用程序加载内存执行。对于应用程序写作者,它提供了一组函数呼叫,进行文件的输入输出(I/O )。对于其它的外围处理-尤其是将文字或图形写到显示器上-应用程序可以直接存取PC的硬件。 由于内存和硬件的限制,成熟的图形环境缓慢地才到来。当苹果计算机公司不幸的Lisa计算机在1983年1月发表时,它提供了不同于文字模式环境的另一种选择,并在1984年1月成为Macintosh上图形环境的一种标准。尽管Macintosh的市场占有率在下降,但是它仍然被认为是衡量所有其它图形环境的标准。包括Macintosh和Windows的所有图形环境,其实都要归功于Xerox Palo Alto Research Center(PARC)在70年代中期所作的开拓性研究工作。 Windows是由微软在1983年11月(在Lisa之后,Macintosh之前)宣布,并在两年后(1985年11月)发行。在此后的两年中,紧随着Microsoft Windows早期版本1.0之后,又推出了几种改进版本,以支持国际商业市场,并提供新型视讯显示器和打印机的驱动程序Windows版本2.0是在1987年11月正式在市场上推出的。该版本对使用者接口做了一些改进。这些改进中最有效的是使用了可重迭式窗口,而Windows 1.0中使用的是并排式窗口Windows 2.0还增强了键盘和鼠标接口,特别是加入了菜单和对话框。 至此,Windows还只要求Intel 8086或者8088等级的微处理器,以「实际模式」执行,只能存取地址在1MB以下的内存。Windows/386(在Windows 2.0之后不久发行的)使用Intel 386微处理器的「虚拟8086」模式,实现将直接存取硬件的多个MS-DOS程序窗口化和多任务化。为了统一起见,Windows版本2.1被更名为Windows/286。 Windows 3.0是在1990年5月22日发表的。它将Windows/286和Windows/386结合到同一种产品中。Windows 3.0有了一个很大的改变,这就是对Intel的286、386和486微处理器保护模式的支持。这能使WindowsWindows应用程序能存取高达16MB的内存。Windows用于执行程序和维护文件的「外壳」程序得到了全面的改进。Windows 3.0是第一个在家用和办公室市场上取得立足点的版本。 任何Windows的历史介绍都必须包括一些OS/2的说明,OS/2是对DOS和Windows的另一种选择,最初是由Microsoft和IBM合作开发的。OS/2版本1.0(只有文字模式)在Intel 286(或者后来的)微处理器上运行,在1987年末发布。在1988年10月的OS/2版本1.1中出现了管理图形使用者接口的PM(Presentation Manager)。PM最初的设计构想是成为Windows的一种保护模式版本,但是图形API改变程度太大,致使软件生产厂商很难提供对这两种平台的支持。 到1990年9月,IBM和Microsoft之间的冲突达到了高峰,导致这两个公司最后分道扬镳。IBM接管了OS/2,而Microsoft明确表示Windows将是他们操作系统策略的中心。虽然OS/2仍然拥有一些狂热的崇拜者,但是它远不及Windows这样的普及程度。 Microsoft Windows版本3.1是1992年4月发布的,其中包括的几个重要特性是TrueType字体技术(给Windows带来可缩放的轮廓字体)、多媒体(声音和音乐)、对象连结和嵌入(OLE:Object Linking and Embedding)和通用对话框。跟OS/2一样,Windows 3.1只能在保护模式下运作,并且要求至少配置了1MB内存的286或386处理器。 在1993年7月发表的Windows NT是第一个支持Intel 386、486和Pentium微处理器32位保护模式的Windows版本。Windows NT提供32位平坦寻址,并使用32位的指令集。(本章后面我会谈到一些寻址空间的问题)。Windows NT还可以移植到非Intel处理器上,并在几种使用RISC芯片的工作站上执行。 Windows 95是在1995年8月发布的。和Windows NT一样,Windows 95也支持Intel 386或更高等级处理器的32位保护模式。虽然它缺少Windows NT中的某些功能,诸如高安全性和对RISC机器的可移植性等,但是Windows 95具有需要较少硬件资源的优点。 Windows 98在1998年6月发布,具有许多加强功能,包括执行效能的提高、更好的硬件支持以及与因特网和全球信息网(WWW)更紧密的结合。 Windows方面 Windows 98和Windows NT都是支持32位优先权式多任务(preemptive multitasking)及多线程的图形操作系统。Windows拥有图形使用者接口(GUI ),这种使用者界面也称作「可视化接口」或「图形窗口环境」。有关GUI的概念可追溯至70年代中期,在Alto和Star等机器上以及SmallTalk等环境中由Xerox PARC所作的研究工作。该项研究的成果后来被Apple Computer和Microsoft引入主流并流行起来。虽然有一些争议,但现在已非常清楚,GUI是(Microsoft的Charles Simonyi的说法)一个在个人计算机工业史上集各方面技术大成于一体的最重要产物。 所有GUI都在点矩阵对应的视讯显示器上处理图形。图形提供了使用屏幕的最佳方式、传递信息的可视化丰富多彩环境,以及能够WYSIWYG(what you see is what you get:所见即所得)的图形视讯显示和为书面文件准备好格式化文字输出内容。 在早期,视讯显示器仅用于响应使用者通过键盘输入的文字。在图形使用者接口中,视讯显示器自身成为使用者输入的一个来源。视讯显示器以图标和输入设备(例如按钮和滚动条)的形式显示多种图形对象。使用者可以使用键盘(或者更直接地使用鼠标等指向设备)直接在屏幕上操纵这些对象,拖动图形对象、按下鼠标按钮以及滚动滚动条。 因此,使用者与程序的交流变得更为亲密。这不再是一种从键盘到程序,再到视讯显示器的单向信息流动,使用者已经能够与显示器上的对象直接交互作用了。 使用者不再需要花费长时间学习如何使用计算机或掌握新程序了。Windows让这一切成真,因为所有应用程序都有相同的基本外观和感觉。程序占据一个窗口-屏幕上的一块矩形区域。每个窗口由一个标题列标识。大多数程序功能由程序的菜单开始。用户可使用滚动条观察那些无法在一个屏幕中装下的信息。某些菜单项目触发对话框,用户可在其中输入额外的信息。几乎在每个大的Windows程序中都有一个用于开启文件的特殊对话框。该对话框在所有这些Windows程序中看起来都一样(或接近相同),而且几乎总是从同一菜单选项中启动。 一旦您了解使用一个Windows程序的方法,您就非常容易学习其它的Windows程序。菜单和对话框允许用户试验一个新程序并探究它的功能。大多数Windows程序同时具有键盘接口和鼠标接口。虽然Windows程序的大多数功能可通过键盘控制,但使用鼠标要容易得多。 从程序写作者的角度看,一致的使用者接口来自于Windows建构菜单和对话框的内置程序。所有菜单都有同样的键盘和鼠标接口,因为这项工作是由Windows处理,而不是由应用程序处理。 为便于多个程序的使用,以及这些程序间信息的交换,Windows支持多任务。在同一时刻能有多个Windows程序显示并运行。每个程序在屏幕上占据一个窗口。用户可在屏幕上移动窗口,改变它们的大小,在不同程序间切换,并从一个程序向另一个程序传送数据。因为这些窗口看起来有些像桌面上的纸(当然,这是计算机还未占据办公桌之前的年代),Windows有时被称作:一个显示多个程序的「具象化桌面」。 Windows的早期版本使用一种「非优先权式(non-preemptive)」的多任务系统。这意味着Windows不使用系统定时器将处理时间分配给系统中运行的多个应用程序程序必须自愿放弃控制以便其它程序运行。在Windows NT和Windows 98中,多任务是优先权式的,而且程序自身可分割成近乎同时执行的多个执行绪。 操作系统不对内存进行管理便无法实现多任务。当新程序启动、旧程序终止时,内存会出现碎裂空间。系统必须能够将闲置的内存空间组织在一起,因此系统必须能够移动内存中的程序代码和数据块。 即使是在8088微处理器上跑的Windows 1.0也能进行这类内存管理。在实际模式限制下,这种能力被认为是软件工程一个令人惊讶的成就。在Windows 1.0中,PC硬件结构的640KB内存限制,在不要求任何额外内存的情况下被有效地扩展了。但Microsoft并未就此停步:Windows 2.0允许Windows应用程序存取扩充内存(EMS);Windows 3.0在保护模式下,允许Windows应用程序存取高达16MB的扩展内存。Windows NT和Windows 98通过成熟的32位操作系统及平坦寻址空间,摆脱了这些旧的限制。 Windows上执行的程序可共享在称为「动态链接库」的文件中的例程。Windows包括一个机制,能够在执行时连结使用动态链接库中例程的程序Windows自身基本上就是一个动态链接库的集合。 Windows是一个图形接口,Windows程序能够在视讯显示器和打印机上充分利用图形和格式化文字。图形接口不仅在外观上更有吸引力,而且还能够让使用者传递高层次的信息。 Windows应用程序不能直接存取屏幕和打印机等图形显示设备硬件。相反,Windows提供一种图形程序语言(称作图形设备接口,或者GDI),使显示图形和格式化文字更容易。Windows虚拟化了显示硬件,使为Windows编写的程序可使用任何具有Windows设备驱动程序的视频卡或打印机,而程序无需确定系统相连的设备类型。 对Windows开发者来说,将与设备无关的图形接口输出到IBM PC上不是件轻松的事。PC的设计是基于开放式架构的原则,鼓励第三方硬件制造商为PC开发接口设备,而且开发了大量这样的设备。虽然出现了多种标准,PC上的传统MS-DOS程序仍不得不各自支持许多不同的硬设备。这对MS-DOS字处理软件来说非常普遍,它们连同1到2张有许多小文件的磁盘一同销售,每个文件支持一种特定的打印机。Windows程序不要求每个应用程序都自行开发这些驱动程序,因为这种支持是Windows的一部分。 动态链接 Windows运作机制的核心是一个称作「动态链接」的概念。Windows提供了应用程序丰富的可呼叫函数,大多数用于实作其使用者接口和在视讯显示器上显示文字和图形。这些函数采用动态链接库(Dynamic Linking Library,DLL)的方式撰写。这些动态链接库是些具有.DLL或者有时是.EXE扩展名的文件,在Windows 98中通常位于\WINDOWS\SYSTEM子目录中,在Windows NT中通常位于\WINNT\SYSTEM和\WINNT\SYSTEM32子目录中。 在早期,Windows的主要部分仅通过三个动态链接库实作。这代表了Windows的三个主要子系统,它们被称作Kernel、User和GDI。当子系统的数目在Windows最近版本中增多时,大多数典型的Windows程序产生的函数呼叫仍对应到这三个模块之一。Kernel(日前由16位的KRNL386.EXE和32位的KERNEL32.DLL实现)处理所有在传统上由操作系统核心处理的事务-内存管理、文件I/O和多任务管理。User(由16位的USER.EXE和32位的USER32.DLL实作)指使用者接口,实作所有窗口运作机制。GDI(由16位的GDI.EXE和32位的GDI32.DLL实作)是一个图形设备接口,允许程序在屏幕和打印机上显示文字和图形。 Windows 98支持应用程序可使用的上千种函数呼叫。每个函数都有一个描述名称,例如CreateWindow。该函数(如您所猜想的)为程序建立新窗口。所有应用程序可以使用的Windows函数都在表头文件里预先声明过。 在Windows程序中,使用Windows函数的方式通常与使用如strlen等C语言链接库函数的方式相同。主要的区别在于C语言链接库函数的机械码连结到您的程序代码中,而Windows函数的程序代码在您程序执行文件外的DLL中。 当您执行Windows程序时,它通过一个称作「动态链接」的过程与Windows相接。一个Windows的.EXE文件中有使用到的不同动态链接库的参考数据,所使用的函数即在那些动态链接库中。当Windows程序被加载到内存中时,程序中的呼叫被指向DLL函数的入口。如果该DLL不在内存中,就把它加载到内存中。 当您连结Windows程序以产生一个可执行文件时,您必须连结程序开发环境提供的特定「引用链接库(import library)」。这些引用链接库包含了动态链接库名称和所有Windows函数呼叫的引用信息。连结程序使用该信息在.EXE文件中建立一个表格,在加载程序时,Windows使用它将呼叫转换为Windows函数。 WINDOWS程序设计选项 为说明Windows程序设计的多种技术,本书提供了许多范例程序。这些程序使用C语言撰写并原原本本的使用Windows API来开发程序。我将这种方法称作「古典」Windows程序设计。这是我们在1985年为Windows 1.0写程序的方法,它今天仍是写作Windows程序的有效方法。 API和内存模式 对于程序写作者来说,操作系统是由本身的API定义的。API包含了所有应用程序能够使用的操作系统函数呼叫,同时包含了相关的数据型态和结构。在Windows中,API还意味着一个特殊的程序架构,我们将在每章的开头进行研究。 一般而言,Windows API自Windows 1.0以来一直保持一致,没什么重大改变。具有Windows 98程序写作经验的Windows程序写作者会对Windows 1.0程序的原始码感觉非常熟悉。API改变的一种方式是进行增强。Windows 1.0支持不到450个函数呼叫,现在已有了上千种函数呼叫。 Windows API和它的语法的最大变化来自于从16位架构向32位架构转化的过程中。Windows从版本1.0到版本3.1使用16位Intel 8086、8088、和286微处理器上所谓的分段内存模式,由于兼容性的原因,从386开始的32位Intel微处理器也支持该模式。在这种模式下,微处理器缓存器的大小为16位,因此C的int数据型态也是16位宽。在分段内存模式下,内存地址由两个部分组成-一个16位段(segment)指针和一个16位偏移量(offset)指标。从程序写作者的角度看,这非常凌乱并带来了long或far指针(包括段地址和偏移量地址)和short或near指标(包括带有假定段地址的偏移量地址)的区别。 从Windows NT和Windows 95开始,Windows支持使用Intel 386、486和Pentium处理器32位模式下的32位平坦寻址内存模式。C语言的int数据型态也扩展为32位的值。为32位版本Windows编写的程序使用简单的平坦线性空间寻址的32位指针值。 用于16位版本Windows的API(Windows 1.0到Windows 3.1)现在称作Win16。用于32位版本Windows的API(Windows 95、Windows 98和所有版本的Windows NT)现在称作Win32。许多函数呼叫在从Win16到Win32的转变中保持相同,但有些需要增强。例如,图像坐标点由Win16中的16位值变为Win32中的32位值。此外,某些Win16函数呼叫返回一个包含在32位整数值中的二维坐标点。这在Win32中不可能,因此增加的新函数呼叫以不同方式运作。 所有32位版本的Windows都支持Win16 API(以确保和旧有应用程序兼容)和Win32 API(以运行新应用程序)。非常有趣的是,Windows NT与Windows 95及Windows 98的工作方式不同。在Windows NT中,Win16函数呼叫通过一个转换层被转化为Win32函数呼叫,然后被操作系统处理。在Windows 95和Windows 98中,该操作正相反:Win32函数呼叫通过转换层转换为Win16函数呼叫,再由操作系统处理。 在同一时刻有两个不同的Windows API集(至少名称不同)。Win32s (「s」代表「subset(子集)」)是一个API,允许程序写作者编写在Windows 3.1上执行的32位应用程序。该API仅支持已被Win16支持的32位函数版本。此外,Windows 95 API一度被称作Win32c(「c」代表「compatibility(兼容性)」),但该术语已被抛弃了。 现在,Windows NT和Windows 98都被认为能够支持Win32 API。然而,每个操作系统依然都支持某些不被别的操作系统支持的某些功能特性。因为它们的相同之处是相当可观的,所以有可能编写在两个操作系统下都可执行的程序。而且,人们普遍认为这两个产品最终会合而为一。 语言选项 使用C语言和原始的API不是编写Windows 98程序的唯一方法。然而,这种方法却提供给您最佳的性能、最强大的功能和在发掘Windows特性方面最大的灵活性。可执行文件相对较小且运行时不要求外部链接库(自然,Windows DLL自身除外)。最重要的是,不管您最终以什么方式开发Windows应用程序,熟悉API会使您对Windows内部有更深入的了解。 虽然我认为学习古典的Windows程序设计对任何Windows程序写作者都是重要的,我没有必要建议使用C和API编写每个Windows应用程序。许多程序写作者,特别是那些为公司内部开发程序或在家编写娱乐程序程序写作者喜欢轻松的开发环境,例如Microsoft Visual Basic或者Borland Delphi(它结合了对象导向的Pascal版本)。这些环境使程序写作者将精力集中于应用程序的使用者接口和相关使用者接口对象的程序代码上。要学习Visual Basic,您也许需要参考Microsoft Press的一些其它图书,例如Michael Halvorson1996年着的《Learn Visual Basic Now》。 在专业程序写作者中-特别是那些开发商业应用程序程序写作者-Microsoft Visual C++和Microsoft Foundation Class Library(MFC)是近年来流行的选择。MFC在一组C++对象类别中封装了许多Windows程序设计中的琐碎细节。Jeff Prosise的《Programming Windows with MFC,第二版》(Microsoft Press,1999年)提供了MFC程序的写作指南。 最近,Internet和World Wide Web的流行大力推广着Sun Microsystems的Java,这是一个受C++启发却与微处理器无关的程序设计语言,而且结合了可在几个操作系统平台上执行的图形应用程序开发工具组。Microsoft Press有一本关于Microsoft J++(Microsoft的Java)开发工具的好书,《Programming Visual J++ 6.0》(1998年),由Stephen R. Davis着。 显然,很难说哪种方法更有利于开发Windows应用程序。更主要的是,也许是应用程序自身的特性决定了所使用的工具。不管您最后实际上使用什么工具写作程序,学习Windows API将使您更深入地了解Windows工作的方式。Windows是一个复杂的系统,在API上增加一个程序写作层并未减少它的复杂性,仅仅是掩盖了它,早晚您会碰到它。了解API会给您更好的补救机会。 在原始的Windows API之上的任何软件层都必定将您限制在全部功能的一个子集内。您也许发现,例如,使用Visual Basic编写应用程序非常理想,然而它不允许您做一个或两个很简单的基本工作。在这种情况下,您将不得不使用原始的API呼叫。API定义了作为Windows程序写作者所需的一切。没有什么方法比直接使用API更万能的了。 MFC尤其问题百出。虽然它大幅简化了某些工作(例如OLE),我却经常发现要让它们按我所想的去工作时,会在其它特性(例如Document/View架构)上碰壁。MFC还不是Windows程序设计者所追求的灵丹妙药,很少有人认为它是一个好的对象导向设计的模型。MFC程序写作者从他们使用的对象类别定义如何工作中受益颇深,并会发现他们经常参考MFC原始码,搞懂这些原始码是学习Windows API的好处之一。 程序开发环境 在本书中,假定您正使用Microsoft Visual C++ 6.0,标准版、专业版和企业版都可以。经济的标准版足以应付本书中的程序设计需求。Visual C++ 还是Visual Studio 6.0中的一部分。 Microsoft Visual C++ 软件包中包括C编译器和其它编译及连结Windows程序所需的文件和工具等。它还包括Visual C++ Developer Studio,一个可编辑原始码、以交谈方式建立资源(如图标和对话框)以及编辑、编译、执行和测试程序的环境。 如果您正使用Visual C++ 5.0,则需要为Windows 98和Windows NT 5.0更新表头文件和引用链接库,这些东西可从Microsoft的网站上得到。在 http://www.microsoft.com/msdn/,选择「Downloads」,然后选择「 Platform SDK」(软件开发套件),您就能在选择的目录中下载和安装更新文件。要让Microsoft Developer Studio浏览这些目录,可以从「Tool」菜单项选择「 Options」然后按下「Directories」标签。 Microsoft网站上的msdn部分代表「Microsoft Developer Network(Microsoft软件开发者网络)」。这是一个向程序写作者提供了经常更新的CD-ROM的计划,这些CD-ROM中包含了程序写作者在Windows开发中所需的最新东西。您也可以订阅MSDN,这样就避免经常得从Microsoft的网站下载文件。 API文件 本书不是Windows API权威的正式文件的替代品。那组文件不再以印刷形式出版,它仅能从CD-ROM或Internet上取得。 当您安装Visual C++ 6.0时,您将得到一个包括API文件的在线求助系统。您可通过订阅MSDN或使用Microsoft网站上的在线求助系统更新该文件。连接到 http://www.microsoft.com/msdn/,并选择「MSDN Library Online」。 在Visual C++ 6.0中,从「Help」菜单项选择「Contents」项目开启MSDN窗口。API文件按树形结构组织,寻找标有「 Platform SDK」的部分,所有在本书中引用的文件都来自于该部分。我将向您介绍如何从「 Platform SDK」开始寻找以斜线分层分门别类的文件的位置。(我知道「Platform SDK」是整个MSDN知识库中较为晦涩的部分,但我敢保证那是Windows程序设计的基本核心。)例如,对于如何在Windows程序中使用鼠标的文件,您可参考/ Platform SDK / User Interface Services / User Input / Mouse Input。 我在前面提到Windows大致分为Kernel、User和GDI子系统。kernel接口在/ Platform SDK / Windows Base Services中,User界面函数在 / Platform SDK / User Interface Services中,GDI位于 / Platform SDK / Graphics and Multimedia Services / GDI中。 编写第一个WINDOWS程序 现在是开始写些程序的时候了。为了便于对比,让我们以一个非常短的Windows程序和一个简短的文字模式程序开始。这会帮助我们找到使用开发环境并感受建立和编译程序机制的正确方向。 文字模式(Character-Mode)模型 程序写作者们喜爱的一本书是《The C Programming Language》(Prentice Hall,1978年和1988年),由Brian W. Kernighan和Dennis M. Ritchie(亲切地称为K&R)编着。该书的第一章以一个显示「hello, world」的C语言程序开始。 这里是在《The C Programming Language》第一版第6页中出现的程序: main () { printf ("hello, world\n") ; } 以前C程序写作者在使用printf等C执行期链接库函数时,无需先声明它们。但这是90年代,我们愿意给编译器一个在我们的程序中标出错误的机会。这里是在K&R第二版中修正的程序: #include <stdio.h> main () { printf ("hello, world\n") ; } 该程序仍然是那么短。但它可通过编译并执行得很好,但当今许多程序写作者更愿意清楚地说明main函数的返回值,在这种情况下ANSI C规定该函数必须返回一个值: #include <stdio.h> int main () { printf ("hello, world\n") ; return 0 ; } 我们还可以包括main的参数,把程序弄得更长一些,但让我们暂且这样就好了-包括一个include声明、程序的进入点、一个对执行期链接库函数的呼叫和一个return语句。 同样效果的Windows程序 Windows关于「hello, world」程序的等价程序有和文字模式版本完全相同的组件。它有一个include声明、一个程序进入点、一个函数呼叫和一个return语句。下面便是该程序: /*------------------------------------------------------------------ HelloMsg.c -- Displays "Hello, Windows 98!" in a message box (c) Charles Petzold, 1998 --------------------------------------------------------------------*/ #include <windows.h> int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { MessageBox (NULL, TEXT ("Hello, Windows 98!"), TEXT ("HelloMsg"), 0); return 0 ; } 在剖析该程序之前,让我们看一下在Visual C++ Developer Studio中建立新程序的方式。 首先,从File菜单中选New。在 New对话框中,单击Projects页面标签,选择 Win32 Application。在Location栏中,选择一个子目录,在 Project Name栏中,输入该项目的名称,此时该名称是HelloMsg,这便是在 Location栏中显示的目录的子目录。Create New Workspace复选框应该勾起来,Platforms部分应该显示 Win32,选择OK。 将会出现一个标题为Win32 Application - Step 1 Of 1的对话框,指出要建立一个Empty Project,并按下Finish按钮。 从File菜单中再次选择New。在 New对话框中,选择Files页面标签,选择 C++ Source File。Add To Project复选框应被选中,并应显示HelloMsg。在 File Name栏中输入HelloMsg.c,选中OK。 现在您可输入上面所示的HELLOMSG.C文件,您也可以选择Insert菜单和 File As Text选项从本书附带的CD-ROM上复制HELLOMSG.C的内容。 从结构上说,HELLOMSG.C与K&R的「hello,world」程序是相同的。表头文件STDIO.H已被WINDOWS.H所代替,进入点main被WinMain所代替,而且C语言执行时期链接库函数printf被Windows API函数MessageBox所代替。然而,在程序中有许多新东西,包括几个陌生的大写标识符。 让我们从头开始。 表头文件 HELLOMSG.C以一个前置处理器指示命令开始,实际上在每个用C编写的Windows程序的开头都可看到:
什么是句柄? 句柄,是整个Windows编程的基础。一个句柄是指使用的一个唯一的整数值,即一个4字节(64位程序中为8字节)长的数值,来标识应用程序中的不同对象和同类对象中的不同的实例,诸如,一个窗口,按钮,图标,滚动条,输出设备,控件或者文件等。应用程序能够通过句柄访问相应的对象的信息,但是句柄不是一个指针,程序不能利用句柄来直接阅读文件中的信息。如果句柄不用在I/O文件中,它是毫无用处的。 句柄是Windows用来标志应用程序中建立的或是使用的唯一整数,Windows使用了大量的句柄来标志很多对象。 一、MFC AppWizard 1、MFC(Microsoft Foundation Class,微软基础类库)是微软为了简化程序员的开发工作所开发的一套C++ 类的集合,是一套面向对象的函数库,以类的方式提供给用户使用 2、MFC AppWizard是一个辅助我们生成源代码的向导工具,它可以帮助我们自动生成基于MFC框架的源代码 二、基于MFC程序框架剖析 1、MFC程序的ClassView标签页(图) 2、继承关系 (1)CMainFrame继承于CFrameWnd (2)CTestApp继承于CWinApp (3)CTestDoc继承于CDocument (4)CTestView继承于CView 注:CFrameWnd和CView都继承于CWnd 3、CWnd类是MFC中一个非常重要的类,它封装了与窗口相关的操作 4、MFC类的简化组织结构图(图) 5、MFC程序也有一个WinMain函数,程序是在编译时,由链接器将它链接到程序中 6、MFC程序具有一个CTestApp类的全局对象theApp,在MFC程序运行时,程序执行的顺序为:theApp全局对象定义 处->CTestApp构造函数->WinMain函数 7、对于普通的VC++控制台程序,无论全局变量还是全局对象,程序运行时,在加载main函数之前,就已经为它们 分配了内存空间。对于一个全局对象来说,此时就会调用该对象的构造函数,构造该对象,并进行初始化操作 8、实例句柄与全局对象 (1)对于Win32 SDK程序,应用程序的实例是由实例句柄(WinMain函数的hInstance参数)来标识的 (2)对于MFC程序,应用程序的实例是由全局对象(每一个MFC程序有且仅有一个从应用程序类CWinApp派生的类, 如CTestApp,它实例化theApp全局对象)来标识的 9、基类构造函数中this指针的指向问题 在构造子类对象时,会自动调用父类的构造函数,此时在父类的构造函数中的this指针所指向的是子类对象地址 10、AfxWinMain函数 MFC程序的WinMain函数是通过调用AfxWinMain函数来完成它的功能的 注:Afx前缀的函数代表应用程序框架(Application Framework)函数,它们可以在程序的任何地方被调用 11、CTestApp::InitInstance函数 在AfxWinMain函数中,通过调用InitInstance函数来完成MFC内部管理方面的工作 12、AfxEndDeferRegisterClass函数 MFC提供了一些默认的标准窗口类,我们只需要选择所需的窗口类就行。然后,调用AfxEndDeferRegisterClass 函数来注册窗口类 13、CMainFrame::PreCreateWindow函数 MFC程序具有两个窗口(框架窗口和视类窗口),在框架窗口产生之前会调用PreCreateWindow函数 14、CWnd::CreateEx函数 在MFC程序中,窗口的创建是由CreateEx函数实现的 15、CWnd::CreateWindowEx函数 主要作用是当修改了CreateEx函数的CREATESTRUCT参数时,CreateWindowEx函数会根据参数发生的相应变化来创 建一个符合我们要求的窗口 注:MFC中后缀名为Ex的函数都是扩展函数 16、CMainFrame::ShowWindow函数和CMainFrame::UpdateWindow函数 用于显示应用程序框架窗口和更新这个窗口 17、CWinThread::Run函数和CWinThread::PumpMessage函数 用于完成消息循环 18、DefWindowProc函数 默认的窗口过程,但MFC程序对消息的处理实际上是通过消息映射机制来完成的 19、MFC程序的运行过程 (1)首先利用全局应用程序对象theApp启动应用程序 (2)调用全局应用程序对象的构造函数,从而就会调用其基类CWinApp的构造函数,以完成应用程序的一些初始化 (3)进入WinMain函数 (4)进入消息循环 20、MFC程序的主要过程 theApp-> CTestApp::CTestApp构造函数-> CWinApp::CWinApp构造函数-> _tWinMain(WinMain函数的宏)-> AfxWinMain函数-> CTestApp::InitInstance函数-> AfxEndDeferRegisterClass函数-> CMainFrame::PreCreateWindow函数-> CFrameWnd::PreCreateWindow函数-> AfxDeferRegisterClass(AfxEndDeferRegisterClass函数的宏)-> CFrameWnd::Create函数-> CWnd::CreateEx函数-> CMainFrame::PreCreateWindow函数-> CWnd::CreateEx函数-> CMainFrame::ShowWindow函数-> CMainFrame::UpdateWindow函数-> CWinThread::Run函数-> CWinThread::PumpMessage函数 21、框架窗口(整个应用程序外框所包括的部分)是视类窗口(框架窗口中空白的地方)的一个父窗口 22、MFC提供了一个文档/视类的结构,文档是指CDocument类,视类是指CView类。前者用于数据的存储和加载, 后者用于数据的显示和修改 23、框架对象、文档对象和视类对象是通过一个单文档模板指针来有机地组织在一起,并利用AddDocTemplate函数 把这个单文档模板添加到文档模板中,从而把这三个类组织成为一个整体 24、MFC程序的CAboutDlg类继承于CDialog类,用于为用户提供一些与程序相关的帮助信息 三、窗口类、窗口类对象与窗口 1、以“::”开始的函数是一个全局函数,表示调用的是Platform SDK的函数 2、如果我们关闭了一个窗口,这个窗口就销毁了,那么该窗口对应的C++窗口类对象销毁了吗? (1)当一个窗口销毁时,它会调用CWnd::DestroyWindow函数,该函数销毁窗口后,将CWnd::m_hWnd设为NULL (2)窗口的生命周期和C++窗口类对象的声明周期不是一致的。当一个窗口销毁时,与C++窗口类对象没有关系,它 们之间的纽带仅仅在于这个C++窗口类内部的成员变量m_hWnd,该变量保存了与这个C++窗口类对象相关的哪个窗口 的句柄 (3)但是,当C++窗口类对象销毁时,与之相关的窗口也将销毁,因为它们之间的纽带m_hWnd已经断了 3、示例---在窗口中显示按钮 (1)CButton按钮类继承于CWnd (2)对于一个CButton对象,在定义之后就可以使用了;但是,如果要显示这个按钮的话,还需调用 CButton::Create函数,把按钮窗口与CButton对象关联起来 (3)MFC程序窗口创建时都会产生WM_CREATE消息,该消息通过OnCreate函数来捕获。对于框架窗口来说,MFC直 接把OnCreate函数提供到了CMainFrame中;而在视类窗口中没有提供该函数,如需使用,要用户自行添加 (4)通常对MFC程序的操作,都是在CTestView视类窗口中进行的 (5)在窗口创建之后,要显示该窗口可以通过调用ShowWindow函数或指定窗口风格为WS_VISIBLE来实现 (6)实现过程 A:在CTestView类中,添加CButton类型的私有成员m_btn B:在CTestView类中,添加WM_CREATE消息的OnCreate处理函数 C:在CTestView类中,通过GetParent函数可以获得CMainFrame框架窗口对象的指针 D:实现一(在视类窗口中通过ShowWindow函数显示按钮) int CTestView::OnCreate(LPCREATESTRUCT lpCreateStruct) { ... m_btn.Create("按钮",WS_CHILD|BS_DEFPUSHBUTTON,CRect(0,0,100,100),this,123); m_btn.ShowWindow(SW_SHOWNORMAL); return 0: } E:实现二(在视类窗口中通过WS_VISIBLE风格显示窗口) int CTestView::OnCreate(LPCREATESTRUCT lpCreateStruct) { ... m_btn.Create("按钮",WS_CHILD|WS_VISIBLE|BS_DEFPUSHBUTTON,CRect(0,0,100,100),this,123); return 0: } F:实现三(在框架窗口中显示按钮) int CTestView::OnCreate(LPCREATESTRUCT lpCreateStruct) { ... m_btn.Create("按钮",WS_CHILD|WS_VISIBLE|BS_DEFPUSHBUTTON,CRect(0,0,100,100),GetParent(),123); return 0: } 即便是基于MFC的应用程序,建立窗口类也是会遵循如下的过程: 设计窗口类->注册窗口类->生成窗口->显示窗口->更新窗口->消息循环->消息路由到窗口过程函数处理。下面就剖析一下在MFC中是如何完成上述过程的。 (1)每个应用程序都有且仅有一个应用类的全局变量theApp,全局变量先于WinMain函数进行处理。 (2)WinMain函数体在APPMODUL.CPP文件中,定义如下: extern "C" int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { // call shared/exported WinMain return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow); } 其中#define _tWinMain WinMain (3)AfxWinMain函数体在WINMAIN.CPP文件中,里面有如下两句话: CWinThread* pThread = AfxGetThread(); CWinApp* pApp = AfxGetApp(); 其实这里得到的这两个指针都是指向全局的对象theApp的; 接下来有函数调用pThread->InitInstance(),根据多态性,会调用CXXXApp类中的InitInstance()函数。该函数很重要,在对该函数的调用中就会完成:设计窗口类->注册窗口类->生成窗口->显示窗口->更新窗口。 接下来,该函数中会继续调用pThread->Run(),这就完成了:消息循环->消息路由到窗口过程函数处理。 (4)进入CXXXApp::InitInstance()函数体中,对于单文档应用程序,调用ProcessShellCommand(cmdInfo),通过调用该函数就会完成:设计窗口类->注册窗口类->生成窗口。 再接下来就会调用m_pMainWnd->ShowWindow(SW_SHOW);m_pMainWnd->UpdateWindow();这就完成了:显示窗口->更新窗口。 (5)在函数CWinApp::ProcessShellCommand(CCommandLineInfo& rCmdInfo)中会进入到如下的case分支:case CCommandLineInfo::FileNew: if (!AfxGetApp()->OnCmdMsg(ID_FILE_NEW, 0, NULL, NULL)) (6)进入函数CCmdTarget::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo),调用_AfxDispatchCmdMsg(this, nID, nCode, lpEntry->pfn, pExtra, lpEntry->nSig, pHandlerInfo); (7)进入函数AFXAPI _AfxDispatchCmdMsg(CCmdTarget* pTarget, UINT nID, int nCode, AFX_PMSG pfn, void* pExtra, UINT nSig, AFX_CMDHANDLERINFO* pHandlerInfo),调用 case AfxSig_vv: // normal command or control notification ASSERT(CN_COMMAND == 0); // CN_COMMAND same as BN_CLICKED ASSERT(pExtra == NULL); (pTarget->*mmf.pfn_COMMAND)(); (8)进入CWinApp::OnFileNew(),调用m_pDocManager->OnFileNew();这个函数很特殊,它本身是个消息响应函数,当我们点击ID为ID_FILE_NEW的菜单时,会产生一个命令消息,由于命令消息可以被CCmdTarget类及其派生类来捕获,而CWinApp是从CCmdTarget派生出来的,因此可以捕获这个消息。当应用程序创建完成并成功显示后,当我们点击文件菜单下的新建菜单项时,就会首先进入这个函数,然后再依次执行下去,最后就会执行到pDocument->OnNewDocument()中,往往我们会对这个函数不解,不知道它为什么会响应ID_FILE_NEW的命令消息,至此真相大白了。顺便说一句,为什么程序在刚启动的时候,我们并没有点击菜单项,为什么会自动的产生这个消息呢?这是因为在CXXXXApp::InitInstance()函数中有“CCommandLineInfo cmdInfo;”这个类的构造函数是这样的:CCommandLineInfo::CCommandLineInfo() { m_bShowSplash = TRUE; m_bRunEmbedded = FALSE; m_bRunAutomated = FALSE; m_nShellCommand = FileNew; },因此就会在第(5)步骤的时候进入到“case CCommandLineInfo::FileNew:”这个分支中,就相当于产生了这样一个FileNew的消息。同理对于ID为ID_FILE_OPEN(在CWinApp::OnFileOpen()中响应)、ID_FILE_SAVE(在CDocument::OnFileSave()中响应)等等在MFC向导为我们生成的单文档类中找不到消息响应的入口时,其实都是在基类CWinApp或者CDocument类中进行了响应。对于CXXXXDoc::Serialize(CArchive& ar)函数也是通过ID_FILE_SAVE和ID_FILE_OPEN产生命令消息后就行响应从而才调用该函数的。 (9)进入CDocManager::OnFileNew(),CDocManager类有一个成员变量是CPtrList m_templateList;该变量保存了一个文档模版链表指针,在CDocManager::OnFileNew()函数体中会调用CDocTemplate* pTemplate = (CDocTemplate*)m_templateList.GetHead();得到链表中的头,也就是第一个文档模版,后面就会用得到的这个指针去调用pTemplate->OpenDocumentFile(NULL);紧接着就会有一个判断,用来确定该链表中是否只有一项,如果链表中保存了多个文档模版,则会弹出一个对话框,来让我们选择到底是使用哪一套文档模版来构建应用程序,相信大家也都见到过这种情况吧。对了,还有一点要说明的是:pTemplate是一个CDocTemplate的指针,但接下来程序为什么会进入到CSingleDocTemplate::OpenDocumentFile的函数体内呢,这是因为CDocTemplate类中的OpenDocumentFile函数被定义为纯虚函数,而CSingleDocTemplate类又是从CDocTemplate类派生出来的,并且实现了该函数,因此就会进入到子类的函数体中了。 (10)进入CDocument* CSingleDocTemplate::OpenDocumentFile(LPCTSTR lpszPathName, BOOL bMakeVisible),先调用CreateNewDocument()创建文档类,再调用pFrame = CreateNewFrame(pDocument, NULL);创建框架类和视图类,从这里也可以看出MFC体系结构中文档、框架、视图“三位一体”的模式,在这一个函数中同时创建三个类;再会调用pDocument->OnNewDocument();因此就会进入到子类的文档类中的pDocument->OnNewDocument()中了。 (11)进入CFrameWnd* CDocTemplate::CreateNewFrame(CDocument* pDoc, CFrameWnd* pOther),调用if (!pFrame->LoadFrame(m_nIDResource, WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE, // default frame styles NULL, &context)) (12)进入BOOL CFrameWnd::LoadFrame(UINT nIDResource, DWORD dwDefaultStyle, CWnd* pParentWnd, CCreateContext* pContext),调用VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG)); (13)进入BOOL AFXAPI AfxEndDeferRegisterClass(LONG fToRegister),该函数内部就完成了:设计窗口类->注册窗口类。MFC通过给我们提供好一些已经订制好的窗口类,我们不需要自己再设计窗口类,只需要到那些订制好的窗口类“仓库”中寻找一种适合我们需要的窗口类就可以了,然后通过AfxRegisterClass函数注册窗口类。还需要说明的是,再后续的跟踪过程中,我们会发现还会进入到AfxEndDeferRegisterClass函数中进行设计和注册窗口类,这主要是因为单文档应用程序比较特殊,它提前通过这样的一种途径进行了窗口类的设计和注册步骤,其实是应该在BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)函数的调用中完成窗口类的设计和注册的,这一点我们要清楚,也就是说设计和注册窗口类的正宗发源地应该是PreCreateWindow(CREATESTRUCT& cs)。此外,我们还会注意到在该函数体的前部分有一语句为“wndcls.lpfnWndProc = DefWindowProc;”因此所有窗口类的窗口过程函数都是DefWindowProc,这一点在后面的跟踪中可以看到,每次生成窗口之后都会调用几次DefWindowProc函数。也就是说MFC都是让我们采用默认的窗口过程函数,这并不是说我们因此就不能使用自己的窗口过程函数实现个性化的消息处理了,MFC采用了一种基于消息映射的机制完成了消息个性化处理。 (14)回到BOOL CFrameWnd::LoadFrame(UINT nIDResource, DWORD dwDefaultStyle, CWnd* pParentWnd, CCreateContext* pContext)中,调用LPCTSTR lpszClass = GetIconWndClass(dwDefaultStyle, nIDResource); (15)进入LPCTSTR CFrameWnd::GetIconWndClass(DWORD dwDefaultStyle, UINT nIDResource),调用PreCreateWindow(cs); (16)进入BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs),调用CFrameWnd::PreCreateWindow(cs) (17)进入BOOL CFrameWnd::PreCreateWindow(CREATESTRUCT& cs),调用VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG));又一次设计和注册窗口类 (18)回到BOOL CFrameWnd::LoadFrame(UINT nIDResource, DWORD dwDefaultStyle, CWnd* pParentWnd, CCreateContext* pContext)中,调用if (!Create(lpszClass, lpszTitle, dwDefaultStyle, rectDefault, pParentWnd, MAKEINTRESOURCE(nIDResource), 0L, pContext)) (19)进入BOOL CFrameWnd::Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, LPCTSTR lpszMenuName, DWORD dwExStyle, CCreateContext* pContext),调用if (!CreateEx(dwExStyle, lpszClassName, lpszWindowName, dwStyle, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, pParentWnd->GetSafeHwnd(), hMenu, (LPVOID)pContext)) (20)BOOL CWnd::CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hWndParent, HMENU nIDorHMenu, LPVOID lpParam),调用if (!PreCreateWindow(cs)) ,接下来调用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! (21)进入int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct),调用if (CFrameWnd::OnCreate(lpCreateStruct) == -1) (22)进入int CFrameWnd::OnCreate(LPCREATESTRUCT lpcs),调用return OnCreateHelper(lpcs, pContext); (23)进入int CFrameWnd::OnCreateHelper(LPCREATESTRUCT lpcs, CCreateContext* pContext),调用if (CWnd::OnCreate(lpcs) == -1) (24)进入_AFXWIN_INLINE int CWnd::OnCreate(LPCREATESTRUCT),调用return (int)Default(); (25)进入LRESULT CWnd::Default(),调用return DefWindowProc(pThreadState->m_lastSentMsg.message, pThreadState->m_lastSentMsg.wParam, pThreadState->m_lastSentMsg.lParam); (26)进入LRESULT CWnd::DefWindowProc(UINT nMsg, WPARAM wParam, LPARAM lParam),调用return ::CallWindowProc(m_pfnSuper, m_hWnd, nMsg, wParam, lParam); (27)回到int CFrameWnd::OnCreateHelper(LPCREATESTRUCT lpcs, CCreateContext* pContext),调用if (!OnCreateClient(lpcs, pContext)) (28)进入BOOL CFrameWnd::OnCreateClient(LPCREATESTRUCT, CCreateContext* pContext),调用if (CreateView(pContext, AFX_IDW_PANE_FIRST) == NULL) (29)进入CWnd* CFrameWnd::CreateView(CCreateContext* pContext, UINT nID),调用if (!pView->Create(NULL, NULL, AFX_WS_DEFAULT_VIEW, CRect(0,0,0,0), this, nID, pContext)) (30)进入BOOL CWnd::Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID, CCreateContext* pContext),调用return CreateEx(0, lpszClassName, lpszWindowName, dwStyle | WS_CHILD, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, pParentWnd->GetSafeHwnd(), (HMENU)nID, (LPVOID)pContext); (31)进入BOOL CWnd::CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hWndParent, HMENU nIDorHMenu, LPVOID lpParam),重复生成框架类CMainFrame的过程来生成CXXXView,因为它也是一个窗口类,因此也需要进行那一系列过程才能最终显示更新出来。 调用的顺序是这个样子的:PreCreateWindow(cs)->BOOL CXXXView::PreCreateWindow(CREATESTRUCT& cs)->CView::PreCreateWindow(cs)->VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG));->::CreateWindowEx(...)->CWnd::DefWindowProc->::CallWindowProc(...)->...->CXXXView::OnCreate->CView::OnCreate->CWnd::OnCreate->... 写到这里,基本上就清楚了,中间的省略号表示的部分大多数都是在与窗口过程函数有关的,因为在生成窗口的时候需要响应一些消息,因此需要调用一些窗口过程函数,每次在调用::CreateWindowEx(...)函数后都会调用一些窗口过程函数,然后再去调用该窗口类对应的OnCreate函数,其实在调用OnCreate函数之前调用CreateWindowEx只是生成了一个窗口,至于这个窗口里面要放置些什么东西,以及该如何装饰该窗口,则就需要由OnCreate来完成了,往往我们都会在OnCreate函数的后面(这样做是为了不影响窗口本身应该布置的格局)添加一些代码,创建我们自己的东西,比如我们通常会在CMainFrame类的OnCreate函数后面放置一些Create代码,来创建我们自己的可停靠的工具栏或者按钮之类的东西,当然我们也可以在CXXXView类的OnCreate函数的后面添加一些代码,来创建我们需要的东西,比如按钮之类的东西。在完成了从设计、注册到生成窗口的过程之后,往往还需要显示更新,有些时候,我们不必要每次都显示的调用CWnd的ShowWindow和UpdateWindow两个函数,我们可以在创建的时候,给窗口风格中添加WS_VISIBLE即可,因此有些时候会跟踪不到ShowWindow和UpdateWindow两个函数这两个函数,因为窗口在创建的时候就可见了。 总的来说,先初始化应用类,然后注册生成框架类,然后再注册生成视图类,然后注册生成视图类OnCreate函数后面用户添加的、用Create来准备创建的窗口,然后再注册生成框架类的OnCreate函数后面需要生成的m_wndToolBar、m_wndStatusBar以及我们自己添加的要创建的窗口类,最后在回到应用类的初始化的函数体中,调用框架类的显示和更新函数,然后再进入由框架类定义的窗口的消息循环中。 消息循环的过程是这个样子的: (1)调用int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)函数中的pThread->Run() (2)进入int CWinApp::Run(),调用return CWinThread::Run(); (3)进入int CWinThread::Run(),调用if (!PumpMessage()) (4)进入BOOL CWinThread::PumpMessage(),调用if (!::GetMessage(&m_msgCur, NULL, NULL, NULL)) (5)回到BOOL CWinThread::PumpMessage(),调用::TranslateMessage(&m_msgCur);::DispatchMessage(&m_msgCur); (6)回到int CWinThread::Run(),调用while (::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE)); (7)再重复(4)-(6)的步骤 下面给出int CWinThread::Run()中消息循环的部分代码: do { // pump message, but quit on WM_QUIT if (!PumpMessage()) return ExitInstance(); // reset "no idle" state after pumping "normal" message if (IsIdleMessage(&m_msgCur)) { bIdle = TRUE; lIdleCount = 0; } } while (::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE)); 这段代码其实本质上与我们基于Win32 SDK手写的代码: //消息循环 MSG msg; while(GetMessage(&msg,NULL,0,0)) { //简单的说,函数TranslateMessage就是把WM_KEYDOWN和WM_KEYUP翻译成WM_CHAR消息,没有该函数就不能产生WM_CHAR消息。 TranslateMessage(&msg); ::DispatchMessage(&msg); } 是一致的。 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);//增加到模板
Creating Windows CreateMDIWindow CreateWindow CreateWindowEx RegisterClass RegisterClassEx UnregisterClass Message Processing BroadcastSystemMessage CallNextHookEx CallWindowProc DefFrameProc DefMDIChildProc DefWindowProc DispatchMessage GetMessage GetMessageExtraInfo GetMessagePos GetMessageTime GetQueueStatus InSendMessage PeekMessage PostMessage PostQuitMessage PostThreadMessage RegisterWindowMessage ReplyMessage SendMessage SendMessageCallback SendMessageTimeout SendNotifyMessage SetMessageExtraInfo SetWindowsHookEx TranslateMessage UnhookWindowsHookEx WaitMessage Window Information AnyPopup ChildWindowFromPoint ChildWindowFromPointEx EnableWindow EnumChildWindows EnumPropsEx EnumThreadWindows EnumWindows FindWindow FindWindowEx GetClassInfoEx GetClassLong GetClassName GetClientRect GetDesktopWindow GetFocus GetForegroundWindow GetNextWindow GetParent GetProp GetTopWindow GetWindow GetWindowLong GetWindowRect GetWindowText GetWindowTextLength IsChild IsIconic IsWindow IsWindowEnabled IsWindowUnicode IsWindowVisible IsZoomed RemoveProp SetActiveWindow SetClassLong SetFocus SetForegroundWindow SetParent SetProp SetWindowLong SetWindowText WindowFromPoint Processes and Threads CreateEvent CreateMutex CreateProcess CreateSemaphore CreateThread DeleteCriticalSection DuplicateHandle EnterCriticalSection ExitProcess ExitThread GetCurrentProcess GetCurrentProcessId GetCurrentThread GetCurrentThreadId GetExitCodeProcess GetExitCodeThread GetPriorityClass GetThreadPriority GetWindowThreadProcessId InitializeCriticalSection InterlockedDecrement InterlockedExchange InterlockedIncrement LeaveCriticalSection OpenEvent OpenMutex OpenProcess OpenSemaphore PulseEvent ReleaseMutex ReleaseSemaphore ResetEvent ResumeThread SetEvent SetPr
### 回答1: 外部程序嵌入MFC窗口是指将一个独立的外部程序嵌入到MFC(Microsoft Foundation Class)窗口中。这种技术通常被用于在应用程序中集成一些第三方的功能或工具。以下是一个大致的实现过程: 首先,需要创建一个MFC窗口,可以使用MFC的资源编辑器来创建一个对话框或其他窗口控件。 然后,在MFC窗口类中创建一个CWnd控件对象,这个对象将用于承载外部程序窗口。 接下来,使用WinAPI函数来创建外部程序窗口,并将其父窗口设置为CWnd对象的句柄。这样外部程序窗口就会嵌入到MFC窗口中,成为其子窗口。 在实现过程中,可能需要处理一些与外部程序窗口的交互,例如获取外部程序的句柄,处理消息传递等。可以使用一些函数如FindWindow来获取外部程序的句柄,并通过重载MFC窗口的消息处理函数来处理与外部程序窗口的交互。 最后,需要在适当的时候销毁外部程序窗口,并释放相关资源,以确保程序的正常运行和内存的管理。 总的来说,外部程序嵌入MFC窗口是一种将两个独立程序的界面进行整合的方法,通过合理地处理消息传递和交互逻辑,可以实现功能上的集成和用户体验的提升。 ### 回答2: 外部程序嵌入MFC窗口是指将一个独立的外部程序嵌入到MFC应用程序窗口中显示,并且能够与应用程序进行交互。这样的嵌入可以提供更多的功能和服务,增强应用程序的功能性和用户体验。 实现外部程序嵌入MFC窗口的关键是利用MFC的类和函数来创建和管理嵌入的进程。下面是实现的基本步骤: 1. 创建一个MFC应用程序,并在窗口中添加一个控件(如一个 CStatic 控件)来充当嵌入的容器。 2. 在应用程序中引入系统的 COM 组件,如 OLE ,以便能够与外部程序进行交互。 3. 在应用程序中创建并启动外部程序,可以使用 MFC 提供的 CWinApp 类的 CreateProcess 函数或者使用 Windows API 中的 CreateProcess 函数。 4. 获取外部程序窗口句柄,可以使用 Windows API 中的 FindWindow 函数或者使用外部程序的进程 ID。 5. 将外部程序窗口句柄嵌入到应用程序窗口的容器控件中,可以使用 MFC 提供的 CWnd 类的 Attach 函数。 6. 根据需要,可以设置嵌入的程序窗口位置和大小。 7. 在应用程序中处理嵌入程序的消息和事件,可以使用 MFC 提供的消息映射机制和事件处理函数。 8. 在应用程序关闭或嵌入程序退出时,释放相关的资源,可以使用 MFC 提供的相应函数(如 CWnd::Detach 、 TerminateProcess 等)。 总结:外部程序嵌入到MFC窗口是一种提供更多功能和增强用户体验的方法。通过使用MFC的类和函数,我们可以轻松地将外部程序嵌入到应用程序窗口中,并与之进行交互。这样的嵌入给用户带来了更多的便利和功能性。 ### 回答3: 外部程序嵌入MFC窗口是指将一个独立的外部程序嵌入到MFC(Microsoft Foundation Class)窗口中进行显示和操作。这种技术常用于在MFC应用程序中集成其他应用程序的功能,并且能够与MFC应用程序进行交互。 要实现外部程序嵌入MFC窗口,可以按以下步骤进行: 1. 获取目标外部程序的句柄:通过使用Win32 API函数,如FindWindow函数,获取到目标外部程序的句柄。句柄是一个唯一标识符,用于标识一个窗口。 2. 创建子窗口:在MFC程序窗口中,使用CreateEx函数创建一个子窗口来容纳外部程序。可以根据需要设置子窗口的样式和属性。 3. 设置窗口句柄:将外部程序的句柄设置为子窗口的句柄,使用SetParent函数实现。这样,外部程序窗口就能嵌入到MFC程序窗口中了。 4. 调整窗口大小和位置:根据需要,可以使用MoveWindow函数来调整外部程序窗口嵌入的位置和大小。 5. 处理交互事件:通过重写MFC程序的消息处理函数,处理与外部程序的交互事件。根据外部程序提供的消息、回调函数或API,来实现与外部程序的通信和交互。 6. 销毁子窗口:在不需要嵌入外部程序窗口时,使用DestroyWindow函数来销毁子窗口,释放资源。 外部程序嵌入MFC窗口能够使得MFC应用程序具备更加丰富和强大的功能,提升用户体验,实现不同程序之间的无缝衔接。但也要注意兼容性和安全性问题,确保嵌入的外部程序不会对系统和数据造成损害。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值