530 likes | 884 Views
Win32 API 开发. 尽可能快和尽可能清晰的方法教你开始 Win32 API 开发. Win32 API 开发. Win32 API即为Microsoft 32位平台的应用程序编程接口(Application Programming Interface)。所有在Win32平台上运行的应用程序都可以调用这些函数。 和windows操作系统打交道的底层途径. 内容. 一个简单的窗口 菜单和图标 基本控件. 理解消息循环机制 对话框. 1. 一个简单的窗口. 创建基于 Win32 的项目 步骤.
E N D
Win32 API开发 尽可能快和尽可能清晰的方法教你开始Win32 API开发
Win32 API开发 • Win32 API即为Microsoft 32位平台的应用程序编程接口(Application Programming Interface)。所有在Win32平台上运行的应用程序都可以调用这些函数。和windows操作系统打交道的底层途径
内容 • 一个简单的窗口 • 菜单和图标 • 基本控件 • 理解消息循环机制 • 对话框
创建基于 Win32 的项目步骤 • 在“文件”菜单上,单击“新建”,然后单击“项目”。 • 在“新建项目”对话框的左侧窗格中,单击“已安装的模板”,单击“Visual C++”,然后选择“Win32”。 在中间窗格中,选择“Win32 项目”。 • 在“名称”框中,键入项目名称,例如 win32app。 单击“确定”。 • 在“Win32 应用程序向导”的“欢迎”页上,单击“下一步”。 • 在“应用程序设置”页上的在“应用程序类型”下,选择“Windows 应用程序”。 在“附加选项”下,选择“空项目”。 单击“完成”创建项目。 • 在“解决方案资源管理器”中,右击 Win32app 项目,单击“添加”,然后单击“新建项”。 在“添加新项”对话框中选择“C++ 文件(.cpp)”。 在“名称”框中,键入文件名称,例如 myFirstWindow.cpp。 单击“添加”。 • 注意:项目创建完成后,要更改所使用的字符集: 项目-->属性-->General-->Character Set: 选择 Use Multi-Byte Character Set
从winMain开始 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) WINAPI: 调用规则 #define WINAPI __stdcall 1.参数传递顺序 2.调用堆栈由谁(调用函数或被调用函数)清理 _stdcall:被调用者来维护堆栈, 函数调用参数从右向左依次进栈 _cdecl:c和c++的默认调用规则, 函数调用参数从右向左依次进栈,调用者来维护堆栈 void fun(int m, int n); cout<<m<<n++<<n<<endl;
第一步:注冊窗口类 const char g_szClassName[] = "myWindowClass"; WNDCLASSEX wc; wc.cbSize = sizeof(WNDCLASSEX);wc.style = 0; wc.lpfnWndProc = WndProc;wc.cbClsExtra = 0;//ignore wc.cbWndExtra = 0;//ignorewc.hInstance = hInstance; wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);//Alt+tab wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); wc.lpszMenuName = NULL;wc.lpszClassName = g_szClassName; wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);// if(!RegisterClassEx(&wc)){ MessageBox(NULL, "Window Registration Failed!", "Error!", MB_ICONEXCLAMATION | MB_OK); return 0; } int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
第二步:创建窗口 HWND hwnd; hwnd = CreateWindowEx( WS_EX_CLIENTEDGE,\\扩展的窗口式样 g_szClassName,\\指定注册过的类来创建 "The title of my window", WS_OVERLAPPEDWINDOW,\\窗口式样 CW_USEDEFAULT, CW_USEDEFAULT, 240, 120,\\创建位置 NULL, NULL, hInstance, NULL); \\父窗口的句柄、菜单句柄、应用程序实例句柄,和窗口创建数据的指针 if(hwnd == NULL){ MessageBox(NULL, "Window Creation Failed!", "Error!", MB_ICONEXCLAMATION | MB_OK); return 0; } 句柄,一个句柄是指使用的一个唯一的整数值,用于标志应用程序中的不同对象和同类对象中的不同的实例,诸如,一个窗口,按钮,图标,滚动条,输出设备,控件或者文件等,应用程序能够通过句柄访问相应的对象的信息
使用WinMain()最后的参数来显示窗口,再更新它以确认它在屏幕上正确地重画了自己.使用WinMain()最后的参数来显示窗口,再更新它以确认它在屏幕上正确地重画了自己. ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd); nCmdShow是可选的参数,你可以简单地传递W_SHOWNORMAL即可.但是用从WinMain()传来的参数可给予运行此程序的用戶选择以可视,最大化,最小化等选项(可在快捷方式的属性中看到这些选项) int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
第三步:消息循环 while(GetMessage(&Msg, NULL, 0, 0) > 0){ TranslateMessage(&Msg); DispatchMessage(&Msg); } return Msg.wParam; GetMessage():从你应用的消息队列中取一个消息. TranslateMessage():为键盘事件做一些额外的处理,如WM_KEYDOWN消息产生WM_CHAR消息 DispatchMessage():将消息送到消息应该被送到的窗口 BOOL WINAPI GetMessage( _Out_ LPMSG lpMsg, _In_opt_ HWND hWnd, _In_ UINT wMsgFilterMin, _In_ UINT wMsgFilterMax );
第四步:窗口过程 LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAMlParam){ switch(msg){ case WM_CLOSE: DestroyWindow(hwnd);break; case WM_DESTROY: PostQuitMessage(0);break; default: return DefWindowProc(hwnd, msg, wParam, lParam); } return 0; }
处理消息 switch(msg){ case WM_CLOSE: DestroyWindow(hwnd);break; case WM_DESTROY: PostQuitMessage(0);break; default: return DefWindowProc(hwnd, msg, wParam, lParam); }
处理点击鼠标按下消息 • 按下鼠标消息: WM_LBUTTONDOWN(左键)、WM_RBUTTONDOWN(右键)与WM_MBUTTONDOWN(中间键) • 处理鼠标左键按下,并生成一个对话框 switch(msg){ case WM_LBUTTONDOWN:{ char szFileName[MAX_PATH];\\文件名最大长度 HINSTANCE hInstance = GetModuleHandle(NULL); GetModuleFileName(hInstance, szFileName, MAX_PATH); MessageBox(hwnd, szFileName, "This program is:", MB_OK | MB_ICONINFORMATION); } ... } LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
理解消息循环 • windows程序以消息为驱动 LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
什么是消息: 一个消息就是一个整数 #define WM_INITDIALOG 0x0110 #define WM_COMMAND 0x0111 #define WM_LBUTTONDOWN 0x0201 //选中,按F12,即可查看 • 功能:消息用来进行windows系统中的所有通信: 窗口操作(移动、放大、缩小等),鼠标操作,键盘操作,控件之间的通信等 • 消息参数:wParam和lParam(32bit整数) 注意:不是每个消息使用了这些参数,而且每个消息以不同的方式来使用 WM_CLOSE,一个也不用 wParam: 有两部分组成 HIWORD(wParam):暗示或提示的消息 LOWORD(wParam):发送消息的控件或菜单的标识号 WORD:一个字,16bit DWORD:两个字,32bit
lParam:含有发送消息的控件的HWND(窗口的句柄)或者为NULL(当消息不是由控件发送)lParam:含有发送消息的控件的HWND(窗口的句柄)或者为NULL(当消息不是由控件发送) • 消息的发送 PostMessage():把消息放入消息队列后立即返回. SendMessage():直接把消息送往窗口并且在窗口沒有结束处理之前不返回 如关闭一个窗口: PostMessage(hwnd,WM_CLOSE,0,0); • 向对话框中控件发送消息 1.获取控件的HWND: GetDlgItem(HWND hDlg, int nIDDlgItem); 2. 发送消息SendMessage(hDlg, message, wParam, lParam);
消息队列 消息被发送时就被放入队列,等待被处理,被处理后就从队列中删除.这就保证了你不会丟掉消息,处理某个的时候,另外的就在队列中等待你来取走它们. • 消息循环 GetMessage():获取一个消息 将获取的消息放到Msg这个 消息结构体对象里 TranslateMessage():将消息简单处理,虚键值转为字符信息 DispatchMessage():先看看信号是给哪个窗口的,再找到那个窗口的窗口过程. 再调用那个过程。 完成了对消息的处理,窗口过程返回,DispatchMessage()也返回,我们就再次循环. MSG Msg; while(GetMessage(&Msg, NULL, 0, 0) > 0){ TranslateMessage(&Msg); DispatchMessage(&Msg); }
MSG Msg; while(GetMessage(&Msg, NULL, 0, 0) > 0){ TranslateMessage(&Msg); DispatchMessage(&Msg); } • 何时退出消息循环 GetMessage()返回FLASE,即 PostQuitMessage()发送 WM_QUIT消息 注意:while(GetMessage(&Msg, NULL, 0, 0) > 0) 以下写法有误: while(GetMessage(&Msg, NULL, 0, 0)) while(GetMessage(&Msg, NULL, 0, 0) != 0) while(GetMessage(&Msg, NULL, 0, 0) == TRUE)
使用资源 位图(bitmap)、菜单(menu)、图标(Icon) 所有的资源代码放在 *.rc 文件里,且包含 #include "resource.h" 添加.rc文件:project->Add new item->Resource->Resource File(.rc) 自动生成resource.h
一个简单的图标资源 IDI_MYICONICON "my_icon.ico" ICON: 资源类别 IDI_MYICON: 宏定义,资源的唯一标识,在resource.h 中定义 #define IDI_MYICON 101 • MENU资源 IDR_MYMENUMENU BEGIN POPUP "&File" BEGIN MENUITEM "E&xit",ID_FILE_EXIT END END //resource.h #define IDR_MYMENU 101 #define IDI_MYICON 102 #define ID_FILE_EXIT 4001
加载资源 HICON WINAPI LoadIcon(HINSTANCE hInstance, LPCTSTR lpIconName); wc.hIcon= LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_MYICON)); MAKEINTRESOURCE:将UINT转换为LPCTSTR
静态菜单:在Resource.rc中添加, IDR_MYMENU MENU//在resource.h 文件定义 IDR_MYMENU BEGIN POPUP "&File" BEGIN MENUITEM "E&xit", ID_FILE_EXIT END POPUP "&Stuff" BEGIN MENUITEM "&Go", ID_STUFF_GO MENUITEM "G&o somewhere else", 0, GRAYED END END 说明:POPUP:,该项目就会呼叫一个弹出式菜单,并且没有ID与此项目相联系。如果没有选中 Pop-up复选框,该项目被选中时就会产生带有特定ID的WM_COMMAND消息。 「&」符号,指出后面一个字符在Windows显示菜单时要加底线。这种底线字符是在您使用Alt键选择菜单项时Windows要寻找的比对字符。 //resource.h #define IDR_MYMENU 101 #define IDI_MYICON 102 #define ID_FILE_EXIT 4001 #define ID_STUFF_GO 40002
静态菜单 • 加载菜单和图标 将菜单和图标加到你的窗口的最简单的方法是注冊你的窗口类的时候指明它们 WNDCLASSEX wc; .... wc.lpszMenuName = MAKEINTRESOURCE(IDR_MYMENU); wc.hIcon = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_MYICON)); wc.hIconSm = (HICON)LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_MYICON), IMAGE_ICON, 16, 16, 0); ..... if(!RegisterClassEx(&wc))..... LoadIcon()来装入大图标,但它只会装入默认的分辨率为32*32的图标,所以为了装入小图标,我们要用LoadImage().
动态菜单:程序运行过程中动态加载 目标:在窗口创建的时候加载 ,即调用CreateWindowEx()时, WM_CREAT消息 LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch(msg){ case WM_CREAT: .... break;
case WM_CREATE:{ HMENU hMenu, hSubMenu; HICON hIcon, hIconSm; hMenu = CreateMenu(); hSubMenu = CreatePopupMenu(); AppendMenu(hMenu, MF_STRING | MF_POPUP, (UINT)hSubMenu, "&File"); AppendMenu(hSubMenu, MF_STRING, ID_FILE_EXIT, "E&xit"); hSubMenu = CreatePopupMenu(); AppendMenu(hMenu, MF_STRING | MF_POPUP, (UINT)hSubMenu, "&Stuff"); AppendMenu(hSubMenu, MF_STRING, ID_STUFF_GO, "&Go"); SetMenu(hwnd, hMenu); hIcon = LoadImage(NULL, "menu_two.ico", IMAGE_ICON, 32, 32, LR_LOADFROMFILE); if(hIcon) SendMessage(hwnd, WM_SETICON, ICON_BIG, (LPARAM)hIcon); else MessageBox(hwnd, "Could not load large icon!", "Error", MB_OK | MB_ICONERROR); hIconSm = LoadImage(NULL, "menu_two.ico", IMAGE_ICON, 16, 16, LR_LOADFROMFILE); if(hIconSm)SendMessage(hwnd, WM_SETICON, ICON_SMALL, (LPARAM)hIconSm); elseMessageBox(hwnd, "Could not load small icon!", "Error", MB_OK | MB_ICONERROR); } break;
响应ID_FILE_EXIT和ID_STUFF_GO消息 • 点击菜单系统发出WM_COMMAND消息 case WM_COMMAND: switch(LOWORD(wParam)){ case ID_FILE_EXIT: PostMessage(hwnd, WM_CLOSE, 0, 0); break; case ID_STUFF_GO: MessageBox(hwnd, "You clicked Go!", "Woo!", MB_OK); break; } break; LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch(msg){ case WM_CREAT: .... break; case WM_COMMAND: ..... break;
添加回调函数 注意:3. 不调用DestroyWindow()来关闭一个对话框,而调用EndDialog(). 注意:2.通常上你不处理的消息就返回一个FALSE,处理的话就是TRUE,除非那个消息指明了你要返回一个別的值 注意:4. 不处理WM_CREATE消息,取而代之,处理WM_INITDIALOG消息来做对话框出现之前的任何操作,并返回TRUE以使键盘将焦点置于点的控件之上. 注意:1. 和普通窗口过程不同的是:不需处理的消息不调用DefWndowProc(). 在对话框中这是自动完成的。
Done • 有个问题:在此对话框返回之前无法操作其他窗口
无模态对话框 • DialogBox():创建模态对话框. 拥有自己的消息循环并且直到对话框关闭才返回 • CreateDialog():创建无模态对话框。向创建次对话框的父窗口的消息队列发送消息
创建窗口 注意:要声明一个全局变量来保持从CreateDialog()返回的窗口句柄以便在后面使用它.DialogBox()不向我们返回句柄因为DialogBox()在窗口销毀的时候才返回.
不为无模态窗口调用EndDialog(),像对常规的窗口一样调用DestroyWindow().我们的例子中主窗口销毀的时候对话框才销毀不为无模态窗口调用EndDialog(),像对常规的窗口一样调用DestroyWindow().我们的例子中主窗口销毀的时候对话框才销毀 LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam){ switch(Message){ ...... case WM_CLOSE: DestroyWindow(hwnd); break; case WM_DESTROY: DestroyWindow(g_hToolbar); PostQuitMessage(0); break; ... ...
显式和隐藏对话框 • 我们希望显示或隠藏我们的对话框,可在菜单上加上了两个命令来做这件事情,并这样处理:
无模态对话框的消息处理 问题:到此为止,如果你在此时运行你的程序并试图在两个按钮间切換的话,就会注意到不行,按Alt+P和Alt+O来激活按钮都不行 因为DialogBox()有自己的消息循环并默认地处理这些事件,CreateDialog()却不能默认处理,但我们可以设法做到 IsDialogMessage(HWND Dlg,LPMSG Msg): 判断消息Msg是否来自对话框Dlg,如果是则处理此消息,返回非零值,否则返回0
控件:按钮,编辑框,列表框 • 控件: 对于控件要记住的一件事就是 它们就是窗口.如同任何其它的窗 口,他们有窗口过程,窗口类, 等等...由系统注冊.任何你 可以对一个窗口做的事情你都可 以对一个控件做.
控件的静态创建 • .rc文件和resource.h添加
编辑框 • 功能: 用来让用戶来对文本进行输入,修改,复制,等等操作. Windows中的记事本就差不多是一个平凡的窗口再在里面嵌上一个大大的编辑框 • 设置文本 SetDlgItemText(hwnd, IDC_TEXT, "This is a string"); 这就是用来攺变控件中的文字所需的全部代码(可用于几乎所有有相关的文本值的控件,靜态控件,按钮等等) • 从控件获取文本 int len = GetWindowTextLength(GetDlgItem(hwnd, IDC_TEXT)); if(len > 0){ char* buf=new char[len+1]; GetDlgItemText(hwnd, IDC_TEXT, buf, len + 1); ... ... delete [] buf; }
列表框 • 添加一个列表项 char buf[]="This is a string!"; int index = SendDlgItemMessage(hwnd, IDC_LIST, LB_ADDSTRING, 0, (LPARAM)buf); tip:可以指定列表框的风格为LBS_SORT,新项将以字母顺序加进去,否则它将被加到列表的尾部. • 获取通知 case WM_COMMAND: switch(LOWORD(wParam)){ case IDC_LIST: switch(HIWORD(wParam)){ case LBN_SELCHANGE: ... break; } ...
列表框 • 从列表框中获取选择的数据(多选为例) HWND hList = GetDlgItem(hwnd, IDC_LIST); int count = SendMessage(hList, LB_GETSELCOUNT, 0, 0); int *item=new int[count];\\选择的列表项的索引放到item[]中 SendMessage(hList, LB_GETSELITEMS, (WPARAM)count, (LPARAM)item); for(int i=0;i<count;i++){ int len=SendMessage (hList, LB_GETTEXTLEN, item[i], 0) ; char *buf=new char[len+1]; SendMessage(hList,LB_GETTEXT,item[i],(LPARAM) buf); ... }
列表框 • 删除选定项 SendMessage(hList, LB_DELETESTRING, (WPARAM)item[i], 0); • 清空列表 SendDlgItemMessage(hwnd, IDC_LIST, LB_RESETCONTENT, 0, 0);
控件动态创建 • 不要在.rc里编辑