第1章 起步
windows简单程序示例及说明
#include <windows.h>//包含其他windows头文件
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
//hInstance实例句柄,数值,标识此程序,多路运行生成多个实例
//hPrevInstance查看此参数可以知道是否有其他实例运行,win32中此参数弃用,总为0
//szCmdLine运行命令行,启动时把文件装入内存
//iCmdShow窗口如何显示(如最大化等)
{
MessageBox (NULL, TEXT ("Hello, Windows 98!"), TEXT ("HelloMsg"), 2) ;
//参数1窗口句柄
//参数2显示文本
//参数3标题栏
//参数4设置窗口按钮,可组合文字+外观(page16)。eg:0-OK,1-OKorCANCLE
//另:TEXT ("Hello, Windows 98!")打包到宏代码里,为方便转换成Unicode
return 0 ;
}
编译过程就是标准的C++程序编译过程
编译器将C源代码生成一个.OBJ目标文件
链接器将OBJ文件与lib库放到一起生成exe
??查看库程序文件列表
第2章 Unicode字符集
宽字符wchar_t定义如下:
typedef unsigned short wchar_t;
所以wchar_t为16位,可表示更多字符(char位8位)
wchar_t a[] = L"hello!";//L向编译器表明是宽字符
int i = sizeof(a);//i == 14
wchar_t b = 'h';//单个字符不需要加L
int d = wcslen(a);//d == 6,wcslen()代替原来的函数strlen();
#include <TCHAR.H>
该头文件目的是以一系列的宏来同时兼容宽字符和普通字符
例如名字为“_UNICODE”的标识符被定义了,
就有#define _tcslen wcslen #define wchar_t TCHAR
否则#define _tcslen strlen #define char TCHAR
#include <windows.h>
typedef CHAR *PCHAR,*LPCH ,*PCH,*NPSTR,*LPSTR ,*PSTR;
//PCHAR指向字符的char型指针
//PSTR,表示指向字符串的指针
typedef CONST CHAR *LPCCH, *PCCH,*LPCSTR ,*PCSTR;
知识点学习:#include <stdarg.h>可变参数列表
访问未命名的参数,首先必须在可变参数函数中声明va_list类型的变量。调用va_start并传入两个参数:第一个参数为va_list类型的变量,第二个为省略号前最后一个有名字的参数的名称,接着每一调用va_arg就会返回下一个参数,va_arg的第一个参数为va_list,第二个参数为返回的类型。最后va_end必须在函数返回前被va_list调用(va_list当作参数)(没有要求要读取完所有参数)。
#include <stdio.h>
#include <stdarg.h>
void printargs(int arg1, ...) /* 输出所有int类型的参数,直到-1结束 */
{
va_list ap;
int i;
va_start(ap, arg1);
for (i = arg1; i != -1; i = va_arg(ap, int))
printf("%d ", i);
va_end(ap);
putchar('\n');
}
int main(void)
{
printargs(5, 2, 14, 84, 97, 15, 24, 48, -1);
printargs(84, 51, -1);
printargs(-1);
printargs(1, -1);
return 0;
}
//result:
//5 2 14 84 97 15 24 48
//84 51
//1
格式化的消息框
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
int CDECL MessageBoxPrintf (TCHAR * szCaption, TCHAR * szFormat, ...)
{
TCHAR szBuffer [1024] ; //缓冲区大小
va_list pArgList ;
va_start (pArgList, szFormat) ;
_vsntprintf (szBuffer, sizeof (szBuffer) / sizeof (TCHAR),
szFormat, pArgList) ;
//参数1,存储文字到szBuffer
//参数2,要写入的最大字符数,非字节数,所以要做除法
//参数3,格式化文字
//参数4.格式化文字中的一系列替换文字
va_end (pArgList) ;
return MessageBox (NULL, szBuffer, szCaption, 0) ;
}
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
int cxScreen, cyScreen ;
cxScreen = GetSystemMetrics (SM_CXSCREEN) ;
cyScreen = GetSystemMetrics (SM_CYSCREEN) ;
//得到显示器的像素宽度和高度
MessageBoxPrintf (TEXT ("ScrnSize"),
TEXT ("The screen is %i pixels wide by %i pixels high."),
cxScreen, cyScreen) ;
return 0 ;
}
第3章 窗口与消息
//一个窗口类使用一个窗口过程(我理解就是响应函数)
//一个窗口类可以产生多个窗口
#include <windows.h>
//typedef unsigned int UINT;
//typedef char CHAR;
//typedef __nullterminated CHAR *NPSTR, *LPSTR, *PSTR; __nullterminated表示一个以’\0’结尾的字符串
//so,PSTR表示非宽字符串的指针char*
//typedef _W64 unsigned int WPARAM
//typedef _W64 long LPARAM
//typedef _W64 long LRESULT
//page45匈牙利记法
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;//函数声明,消息处理函数
//#define WINAPI __stdcall 被这个关键字修饰的函数,其参数都是从右向左通过堆栈传递
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,//HINSTANCE实例
PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT ("HelloWin") ;
HWND hwnd ;//窗口句柄
MSG msg ;//消息结构
WNDCLASS wndclass ;//窗口信息结构体
//设置窗口的各个属性和参数,CS_等标识符均为数值常量(page43)
wndclass.style = CS_HREDRAW | CS_VREDRAW ;
wndclass.lpfnWndProc = WndProc ;//对窗口过程函数的引用
wndclass.cbClsExtra = 0 ;//预留的额外空间??干什么用
wndclass.cbWndExtra = 0 ;
wndclass.hInstance = hInstance ;
//LoadXXX加载XXX函数
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;//默认图标NULL,自定义hInstance
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;//鼠标图标
wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;//GetStockObject()获取一个图形对象,本例子中是画刷
wndclass.lpszMenuName = NULL ;//窗口类菜单,NULL表示无菜单
wndclass.lpszClassName = szAppName ;//窗口类名称(当程序只创建一个窗口时,窗口类名称通常与程序名相同)
if (!RegisterClass (&wndclass))//注册窗口
{
MessageBox (NULL, TEXT ("This program requires Windows NT!"),
szAppName, MB_ICONERROR) ;
return 0 ;
}
//创建窗口,返回值为指向窗口的句柄
//CreateWindowA和CreateWindowW处理ASCⅡ和Unicode
hwnd = CreateWindow (szAppName, // 窗口类名称 //与窗口类建立关联
TEXT ("The Hello Program"), // title
WS_OVERLAPPEDWINDOW, // 窗口格式(按位与的组合)
CW_USEDEFAULT, // 初始X坐标
CW_USEDEFAULT, // 初始Y坐标
CW_USEDEFAULT, // 初始X方向尺寸
CW_USEDEFAULT, // 初始Y方向尺寸
NULL, // 父窗口句柄 //顶级窗口
NULL, // 窗口菜单句柄
hInstance, // 程序实例句柄
NULL) ; // 创建参数
ShowWindow (hwnd, iCmdShow) ;//显示窗口
UpdateWindow (hwnd) ;//更新窗口的客户区
//GetMessage是从调用线程的消息队列里取得一个消息并将其放于指定的结构。
//此函数可取得与指定窗口联系的消息和由PostThreadMessage寄送的线程消息。
//此函数接收一定范围的消息值。GetMessage不接收属于其他线程或应用程序的消息。
//获取消息成功后,线程将从消息队列中删除该消息。
//函数会一直等待直到有消息到来才有返回值。
//参数1:指向MSG结构的指针,该结构从线程的消息队列里接收消息信息。
//参数2:取得其消息的窗口的句柄。当其值取NULL时,GetMessage为任何属于调用线程的窗口检索消息,线程消息通过PostThreadMessage寄送给调用线程。
//参数3UNIT:指定被检索的最小消息值的整数。
//参数4UNIT:指定被检索的最大消息值的整数。
while (GetMessage (&msg, NULL, 0, 0))
{
TranslateMessage (&msg) ;//TranslateMessage函数用于将虚拟键消息转换为字符消息。
DispatchMessage (&msg) ;
}
return msg.wParam ;//从winmain中退出
}
//消息处理函数
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)//wParam与lParam传递提供关于消息的一些附加消息
{
HDC hdc ;//设备环境句柄
PAINTSTRUCT ps ;//绘制结构
RECT rect ;//矩形结构
switch (message)
{
case WM_CREATE:
PlaySound (TEXT ("hellowin.wav"), NULL, SND_FILENAME | SND_ASYNC) ;//播放声音函数
return 0 ;
case WM_PAINT:
hdc = BeginPaint (hwnd, &ps) ;//WM_PAINT消息必须以此函数开头,返回设备句柄
//BeginPaint调用期间会擦除背景,用注册窗口类的时候的hbrBackground
GetClientRect (hwnd, &rect) ;
//使用环境句柄hdc只能在客户区绘制
DrawText (hdc, TEXT ("Hello, Windows 98!"), -1, &rect,//绘制文本
DT_SINGLELINE | DT_CENTER | DT_VCENTER) ;
EndPaint (hwnd, &ps) ;//WM_PAINT消息必须以此函数结尾,释放设备句柄
return 0 ;
case WM_DESTROY:
PostQuitMessage (0) ;
return 0 ;
}
return DefWindowProc (hwnd, message, wParam, lParam) ;//DefWindowProc此函数返回值必须从窗口过程中返回
}
其中GetMessage函数也可以使用PeekMessage,两者的区别是:链接
第20章 多任务和多线程
多线程1/10秒规则:一个消息队列线程处理一条消息的时候不应该花费1/10秒以上的时间。
但有些操作不适用,比如打开一个大的文件,把它加载到内存中。
多线程架构:
主线程 - 创建所有窗口,包括窗口过程
其他线程 - 简单的后台运算,只和主线程通信
所有线程都是同一个进程的一部分,共享进程的所有资源,如内存及打开的文件,以及静态变量。
同步机制:semaphore信号量,critical section 临界区
i++;
16位程序中 被编译为两条机器代码指令,所以需要把代码放到临界区。
32位程序中 没有影响
WindowsNT 有一个函数能够让一个线程终止同一个进程中的另一个线程
创建一个执行线程的API函数
hThread = CreatThread(&security_attribute, dwStackSize, ThreadProc, pParam, dwFlags, &idThread);
//参数1:指向SECURITY_ATTRIBUTE结构的指针。 win98中没用,winNT可以设置为NULL
//参数2: 线程初始大小,0为默认值,可动态调整
//参数3:函数指针指向自己的线程函数 DWORD WINAPI ThreadProc(PVOID pParam);
//参数4:传递给线程函数的参数
//参数5:默认0,如果设置为CREATE_SUSPENDED表示线程创建后挂起,直到调用ResumeThread函数
//参数6:指针,指向新接收线程的ID值变量
也可以使用C的运行时函数库:
#include <PROCESS.H>
hThread = _beginthread(ThreadProc, uiStackSize, pParam);
//线程函数格式如下
void __cdecl ThreadProc (void *pParam);
环境设置:
项目设置 - c/c++ - category - code generation - use runtime library 改成多线程模式/MT
编程竞赛问题,非多线程实现代码(Page933)
学习注释如下:
/*---------------------------------------
MULTI1.C -- Multitasking Demo
(c) Charles Petzold, 1998
---------------------------------------*/
#include <windows.h>
#include <math.h>
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
int cyChar ;//系统字体的高度
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT ("Multi1") ;
HWND hwnd ;
MSG msg ;
WNDCLASS wndclass ;
wndclass.style = CS_HREDRAW | CS_VREDRAW ;
wndclass.lpfnWndProc = WndProc ;
wndclass.cbClsExtra = 0 ;
wndclass.cbWndExtra = 0 ;
wndclass.hInstance = hInstance ;
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
wndclass.lpszMenuName = NULL ;
wndclass.lpszClassName = szAppName ;
if (!RegisterClass (&wndclass))
{
MessageBox (NULL, TEXT ("This program requires Windows NT!"),
szAppName, MB_ICONERROR) ;
return 0 ;
}
hwnd = CreateWindow (szAppName, TEXT ("Multitasking Demo"),//入口主函数调用WndProc,WndProc调用1234子窗口过程
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL) ;
ShowWindow (hwnd, iCmdShow) ;
UpdateWindow