window程序设计学习笔记

本文介绍了Win32编程的基础知识,包括从编写简单的Windows程序开始,深入讲解Unicode字符集,探讨ASCII、扩展ASCII和双字节字符集的历史与局限,以及Unicode作为解决方案的角色。接着,文章讨论了宽字符在C语言和Windows环境中的使用,展示了如何创建和管理窗口,以及利用GDI进行文本输出和图形绘制。此外,还涵盖了滚动条的原理和应用,以及在Windows程序中如何实现和控制它们。

第一部分:基础知识

第一章:起步

###4G空间的虚拟内存的概念

1.3第1个windows程序

/***************************************************************************************************
helloMsg.c---Displays "hello,Windows 98! in a message box
(c)2023
**************************************************************************************************/
/*HelloMsg.c*/
#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;
}	
WinMain函数的返回值被定义为int,WINDEF.H中用以下语句来定义WINAPI标识符
#define WINAPI      __stdcall

1.3第2个windows程序

#include<Windows.h>

int WINAPI WinMain (_In_ HINSTANCE hInstance,
	_In_opt_ HINSTANCE hPrevInstance,
	_In_ LPSTR lpCmdLine,
	_In_ int iCmdShow)
{
	MessageBox(NULL, TEXT("hello,windows,98!"), TEXT("helloMsg"), 0);
	return 0;
}

第二章 Unicode简介

简单来说 Unicode是ASCII字符编码的一个扩展,Unicode用的是16位字母编码,而不是像ASCII那样 用7位表示,已不是计算机上面常用的每个字符八位的方式,这样在计算机通信中,世界上所有书面语言中的字母,象形文字和其他符号都可以用Unicode来表示,人最初的意图是用Unicode作为ASCII的补充,如果运气好的画,希望他可以最终取代ASCII,考虑到ASCII编码仍然是计算机中最有重要位置的标志之一,这显然是一个不太容易完成的任务
Unicode影响着计算机行业的方方面面,最深刻的或许是操作系统和编程语言的影响,在这方面,我们只完成了将近一半的任务。windows NT从底层上支持了Unicode。(不幸的是,windows98 只包含了少量的Unicode支持。)被ANSI规范化的C编程语言已他对宽字符的支援从本质上支持了Unicode,我将在后面详细讨论这一点。
当然。像往常一样,我们作为程序员会面对许多繁复的基本工作,为了减轻大家的负担。我们把本书中所有程序都写成了Unicode兼容版,在本书稍后关于Unicode的讨论会是其含义更加清晰

2.1字符集简史

人类从何时开始用口头语言至今尚无定论,但书面文字貌似已经有六千年的历史,早期文字有着象形的性质。字母表是三千年以前才发明的。它的特征是每个字母有相应的发音。虽然各种世界语言文字貌似很好地发挥着他的用途。几个十九世界的发明家则认为人们还有更多的需要。摩尔斯在1838年到1954年间发明了电报,他还设计了一种用于电报打代码,这套代码中,字母表中每个字母都被赋予一个相应的短和长的停顿系列(点和破折号)。这套代码系统不区分大小写字母,但数字和标点书号有着字节的代码。
摩尔斯并非是用于非符号或非印刷符号来表示书面语言的首列,在1821年到1824年,年轻的莱布尔受军事上一个用于夜间书写和阅读的系统启发。发明了可供盲人阅读的,把凸点印在纸上的代码,盲文本质上是用6位编码来表示字母,常用字母组合,蟾宫词和标点符号。一个特别的转义码表没随后的字母代码是大写:而一个特殊的转换码表示随后的字母代码是数字。

2.1.1美国标准

最重要的是,ASCII码是一种非常可靠的标准,没有其他那种标准像ASCII一样普及,它扎根于我们的键盘,显示器,系统硬件,打印机,字体文件,操作系统和因特网

2.1.2美国以为的世界

不能满足各个国家文字的需求,所有进行了变种,但是没能有效解决国际化语言的最好的方法

2.1.3扩展ASCII

8位代码表示,前面七位ascii占用,只是定义了后面的128个
汉卡就是基于扩展ASCII标准,而存在的

2.1.4 双字节字符集

双字节编码字节的问题

2.1.5 Unicode 的解救方案

unicode彻底解决编码的问题,unicode标准只是解决了编码的问题,但没有告诉大家如何实现的,和存储utf16 utf8是两种实现的方法

2.2 宽字符和C语言

多个字节表示一个字符集
宽字符不一定是Unicode,unicode只是宽字符编码的一种实现,然而本书的重点是window,我会将宽字符和Unicode作为 同义词

2.2.1 char 数据类型

声明定义和初始哈一个包含单一字符的变量,变量C需要一个字节的存储空间而且用十六进制0x41来初始哈,也就是ASCII字符的A
char c =‘A’;

char* p
指针变量需要四个字节的存储空间
char *p =“hello!”;
7个字节的存储空间,其中有一个字符串的结束标志0

2.2.2 更宽的字符
2.2.3 宽字符库函数
2.2.4 维护一个源代码文件

2.3 宽字符和windows

2.3.1 windows头文件类型
2.3.2 windows函数调用
2.3.3 windows的字符串函数
2.3.4 windows中使用printf
2.3.5 格式化的消息框
2.3.4 国际化之于本书

第三章 窗口与信息

前面我们学习了MessageBox(),MessageBox函数虽然能创建窗口,但是它比较特殊,其灵活性有限,消息窗口带有一个标题,一个可选图标,一行文字,几个按钮,这是非常有限的
下面我们学习并创建属于字节的窗口,还可以为窗口添加菜单等

3.1窗口的创建

创建窗口,只需要调用CreateWindow函数

3.1.1 系统结构概述

windows程序的执行并不是从上到下顺序执行的,这里是按消息的机制执行,内核的内容,这里先不涉及

3.1.2 Hellowin程序

要创建窗口,首先需要注册一个窗口类,而窗口类有需要窗口过程来处理窗口信息。
这些是几乎所有windows程序都会包含的一些常规的步骤。

/*----------------------------------------------------------
hellowin.c---Displays "hello,Windows 98!"in client area 
2023
-----------------------------------------------------------*/
#include<Windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,
	PSTR lpCmdLine,int iCmdShow)//PSTR非宽字符串指针char*
{
	static TCHAR szAppName[] = TEXT("helloWin");//窗口类名
	HWND hwnd;//窗口句柄
	MSG msg;//消息 结构变量-F1
	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("The Hello Program"),//窗口标题
		WS_OVERLAPPEDWINDOW,//窗口样式
		CW_USEDEFAULT,//initial x position
		CW_USEDEFAULT,//initial y position 
		CW_USEDEFAULT,//initial x size
		CW_USEDEFAULT,//initial y size
		NULL,//父窗口句柄
		NULL,//窗口菜单句柄
		hInstance,//程序控制句柄
		NULL);//创建参数

	ShowWindow(hwnd, iCmdShow);
	UpdateWindow(hwnd);//更新窗口

	while (GetMessage(&msg, NULL, 0, 0))//消息列队中获取信息,message字段为WM_QUIT返回
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return	msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM 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);

		GetClientRect(hwnd, &rect);

		DrawText(hdc, TEXT("hello,windows 98!"), -1, &rect,
			DT_SINGLELINE | DT_CENTER | DT_VCENTER);
		EndPaint(hwnd, &ps);
		return 0;

	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	}
	return DefWindowProc(hwnd, message, wParam, lParam);
}

3.1.3 通盘考虑

一般的,windows程序结构都和这个hellowin.c大体类似,没有人会真正记住这种框架的所有细节。一般就是复制现有代码到新程序中,然后加以修改,你可以这样自由的组合光盘中的代码。

**window 函数调用**

hellowin 用到直接调用的函数有18个
loadIcon
LoadCursor
GetStockObject
RegsiterClass
MessageBox
CreateWindows
ShowWindow
UpdateWindow
GetMessage
TranslateMessage
DispatchMessage
PlaySound
BeginPaint
GetClientRect
DrawText
EndPaint
PostQuitMessage
DefWindowProc

大写标识符

CS
CW
DT
IDI
IDC
MB
SND
WM
WS
这些前缀都在头文件中有定义

新数据类型

第四章 文本输出

/*----------------------------------------------------------
013.c 每日一练
	TextOut()
	Ellipse()

注意:关闭窗口后,程序还在运行
	2023.6.24
-----------------------------------------------------------*/
#include<Windows.h>
#pragma comment(lib,"winmm.lib")//playSound函数在winmm.lib

/*************************回调函数*****************************
LRESULT:返回值,被宏定义未long型
CALLBACK:函数参数进栈顺序(从右到左,已反汇编证实)----stdcall调用约定
4个参数:未MSG结构体的前4个成员变量
*************************************************************/
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);//消息回调函数


/**********************************************************
WINAPI:		函数调用约定,_stdcall
hInstance:	用用程序(进程 )句柄
hPrvInstance:兼容windows16
lpCmdLine:	命令行参数
nCmdShow:	窗体显示方式-正常,最大化,最小化,全屏等
********************************************************/
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
	PSTR lpCmdLine, int iCmdShow)//PSTR非宽字符串指针char*
{
	static TCHAR szAppName[] = TEXT("helloWin");//窗口类名---一个以0结尾的字符串数组

	/*****************************1,设计窗口类****************************************/
	WNDCLASS wndclass;//定义窗口结构对象,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;

	//************************2,注册窗口类*****************************************
	if (!RegisterClass(&wndclass))//未成功注册
	{
		MessageBox(NULL, TEXT("---未成功注册一个窗口类!"),
			szAppName, MB_ICONEXCLAMATION);
		return 0;
	}
	//******************************3,创建窗口************************************************
	HWND hwnd;
	hwnd = CreateWindow(
		szAppName,//窗口类名
		TEXT("标题我的第一个窗口程序!"),//窗口标题
		//TEXT("add is test!"),		//通过这里知道wc设计好的窗口
		WS_OVERLAPPEDWINDOW,//窗口样式
		CW_USEDEFAULT,//initial x position
		CW_USEDEFAULT,//initial y position 
		CW_USEDEFAULT,//initial x size
		CW_USEDEFAULT,//initial y size
		NULL,//父窗口句柄
		NULL,//窗口菜单句柄
		hInstance,//程序控制句柄
		NULL
	);//创建参数
/*创建窗口完成(只存在于内存),系统会发下哦那个一条消息WM_CREATE,注意此时,程序并未直线到消息循环处,
操作系统烧过应用程序的消息队列,直接向应用曾向发出,该消息在所有的消息之前,目的是为了在程序直线之前
可以有机会进行一些初始化工作或装在动态链接库操作。
a)CreatWindow的第一个参数就是窗口类的名字,通过这个名字可以找到仓才注册的窗口类,然后再根据他来创建
b)显示器上的坐标与数字中的不同,显示器的左上角是远点坐标,从原点坐标向右是X轴,向下是Y轴,都是正坐标,没有负坐标
c)参数hInstance是通过主函数winmain传入的
注意hInstance()函数创建窗口后,仅仅是位了窗口分配了内存空间,获得了句柄,
但窗口并没有显示出来,所有还需要调用ShowWindows()函数来显示窗口
*/

//********************************4,显示窗口和更新窗口*************************************
	ShowWindow(hwnd, iCmdShow);
	/*1,单纯一个ShowWindow,照样会正确画出旷课内容,只不过是再消息队列区孔之后才画的,
	又是,我们虚妄唱K被立即重画,而不是等待那个不确定的消息队列,此时就需要
	用到updataWindow,updateWindow的作用只有一个:假若当时被标记位重画的区域邨彩(不
	存在的画声明也不做),那么立刻让windows使用SendMessage的方式来对你的窗口
	发送WM_PAINT
	2,ShowWindow本身不发送重绘信息,它的作用仅仅是吧窗口显示出来。
	所有还需要调用updatawindow()函数,生产VM_PAINT消息,将客户区中的各种控件绘制出来。
	不过,当窗口显示的时候,windows会自动探测窗口的内容是否需要重画,已经需要重画的区域。
	3,Showwindow调用中会发哦是那个WM_SIZE 和SHOWWINDOW消息给窗口过程
	4,第一次调用和以后的调用时右区别的。第一次调用时,它的输入参数nCmdShow时需要输入
	winMain函数里传入来的nCmdShow参数,而不能时其它参数。
	*/

	UpdateWindow(hwnd);
	/*直线updatewindow时会立即发送一条消息WM_PAIN,同样右操作系统直接发送,updatewindow执行
	时,先判断无效区域是否位空,部位空,则发送,为空为不发送,可见,将上面的shouwindow注释掉,则
	不发送,应为为显示出来,当然没有无效区域,即无效区域为空
	*/
	/*updataWindow和Invalidata的区别:
	1,updatawindow()的作用时使用窗口立即重绘(因为操作系统绕过应用程序的消息队列,直接给就目标窗口发送WM_Pain,
	而Invalidata等函数后窗口不会立即重绘,这是由于WM_PAINT消息的优先级很低,他需要等消息队里中的其他直线完毕
	2,updatewindow直线时,先判断无效区域是否为空,部位空才发送消息, invalidate无效区为整个客户区
	*/

	//*****************************消息循环**********************************************
	MSG msg;
	/*1,调用GetMsessage函数,传入msg的地址,从应用程序饿消息队里中获取消息, 其中的
	消息结构的内容由操作系统自动填充
	2,这是windows核心的,消息循环处理过程,只有在收到WM_QUIT里才退出,结束程序
	*/
	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);//将虚拟键消息转换为字符消息
		DispatchMessage(&msg); //发送消息给函数,先把msg发送给系统,然后由系统
								//dispatch函数的内部实现的答题流程
								//push 
								//.....
								//call wndproc 
								//.....
								//结束

	}

	return msg.wParam;//msg.wParam来自一条表示退出的消息,返回这个值给系统,从而退出
}

//******************************回调函数---消息处理过程************************************
//窗口回调函数,此函数只有声明和定义,没有回调!这说明此函数确实有操作系统调用的!
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	HDC hDC;//设备环境---绘图的地方
	PAINTSTRUCT ps;//绘图结构体变量
	RECT rect;//绘图区范围

	switch (message)
	{
	case WM_CREATE://创建窗口消息,此消息时一个应用程序发送的第一个消息,也是唯一的一次
		PlaySound(TEXT("hellowin.wav"), NULL, SND_FILENAME | SND_ASYNC);//播放声音文件
		//return 0;
		break;
	case WM_PAINT:
		hDC = BeginPaint(hwnd, &ps);
		/*ps结构的第三个成员rect rcPaint,时值一个无效区域当调用BeginPaint函数时,由操
		作系统填充,因此,严格来讲hDC为无效区域的“设备环境”,在此区域外的作图,都
		被忽略(即x,y坐标在此区域外的)*/
		//获取客户区大小
		GetClientRect(hwnd, &rect);
		//绘制字符串
		TextOut(hDC, 200, 200, TEXT("江江!"), lstrlen(TEXT("江江!")));
		//绘制椭圆图形
		Ellipse(hDC, 500, 50, 800, 500);//左上角坐标,右下角坐标
		//绘制格式化文本,客户区中间垂直居中对齐
		DrawText(hDC, TEXT("这是我的第一个窗口程序!"), -1, &rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);

		EndPaint(hwnd, &ps);
		break;
	}
	return DefWindowProc(hwnd, message, wParam, lParam);//未处理任何窗口消息
}

4.1 绘制和重绘

4.1.1 WM_PAINT
4.1.2 有效矩形和无效矩形

4.2 GDI简介

4.2.1 设备环境
4.2.2 获取设备环境句柄:方法一
4.2.3 绘制消息结构
4.2.4 获取设备环境句柄:方法二
4.2.5 TEXT函数详解
4.2.6 系统字体
4.2.7 字符大小
4.2.8 文本尺寸的度量
4.2.9 文本的格式化
4.2.10 综合使用
4.2.11 SYSTEMS1.C窗口过程
4.2.13 客户区的尺寸
/*---------------------------------------------------------
	sysmets2.c---system metrics display program no -3
	第二十一个例子:获取系统配置信息3,改进:适当大小的窗口滚动条
		SCROLLINFO结构
		SetScrollInfo函数
		GetScrollInfo函数
		ScrollWindows函数   滚动窗口
		2023 06 26 18:16
-----------------------------------------------------------*/

#include<Windows.h>
#include<tchar.h>
//#include<sysmets.h>
#include "SYSMETS.H"//定义系统信息结构数组即NUMLINES(显示行数 )

LRESULT  CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE hPrevInstance,
	PSTR szCmdLine, int iCmdShow)
{
	static TCHAR szAppName[] = TEXT("SysMets3");//移植你更改的地方
	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("Get System Metrics No.3"),//更新
		WS_OVERLAPPEDWINDOW | WS_VSCROLL|WS_HSCROLL,//增加水平滚动条WS_HSCROLL
		CW_USEDEFAULT, CW_USEDEFAULT,
		CW_USEDEFAULT, CW_USEDEFAULT,
		NULL, NULL, hinstance, NULL);

	ShowWindow(hwnd, iCmdShow);
	UpdateWindow(hwnd);

	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;
}

LRESULT  CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	static int cxChar;//字符平均宽度
	static int cxCaps;//字符间距
	static int cyChar;//字符行距
	static int cxClient,cyClient;//新客户区高度
	static int iMaxWidth;
	HDC hdc;
	int i,x,y,iVertPos,iHorzPos,iPaintBeg,iPaintEnd;
	SCROLLINFO si;//滚动条参数结构变量
	PAINTSTRUCT ps;
	TCHAR szBuffer[10];
	TEXTMETRIC tm;	//物理字体的基本信息

	switch (message)
	{
	case WM_CREATE:
		hdc = GetDC(hwnd);//获取设备环境句柄
		GetTextMetrics(hdc, &tm);
		cxChar = tm.tmAveCharWidth;//字符平均宽度
		//tm.tmPitchAndFamily字体间距,tm.tmPitchAndFamily字段的地位决定是否为等宽字符,1代表是变宽字符,0代表等宽字符
		cxCaps = (tm.tmPitchAndFamily & 1 ? 3 : 2) * cxChar / 2;
		//字体简介cxCaps设为cxcar的1.5倍,1表示宽字符
		cyChar = tm.tmHeight + tm.tmExternalLeading;//字符行距=字符高+字符顶部空间数目
		ReleaseDC(hwnd, hdc);//释放设备环境句柄

		//保留三列文本的宽度,水平方向一页足以显示全部文本,因此会 隐藏了水平滚动条
		iMaxWidth = 40 * cxChar + 22 * cxCaps;
		return 0;

	case WM_SIZE:
		cxClient = LOWORD(lParam);//客户区的新高度
		cyClient = HIWORD(lParam);//客户区的新高度
		//set vertical scroll bar range and page size
		si.cbSize = sizeof(si);
		si.fMask = SIF_RANGE | SIF_PAGE;

		si.nMin = 0;//滚动条的最小值
		si.nMax = NUMLINES - 1;//滚动条的最大值
		si.nPage = cyClient / cyChar;//页面大小
		//设置垂直滚动条范围和页面大小
		SetScrollInfo(hwnd, SB_VERT, &si,TRUE);

		
		//SET horizontal scroll bar range and page size
		si.cbSize = sizeof(si);
		//SIF_DISABLENOSCROLL :滚动条不需要时,禁用而不是删除,则可以显示滚动条
		//si.fMask = SIF_RANGE | SIF_PAGE | SIF_DISABLENOSCROLL;
		si.fMask = SIF_RANGE | SIF_PAGE;
		si.nMin = 0;
		si.nMax = 2 + iMaxWidth / cxChar;
		//客户区宽度/平均字符宽度=列数。即水平滚动条一页大大小
		si.nPage = cxClient / cxChar;
		//设置水平滚动条范围和页面大小
		SetScrollInfo(hwnd, SB_HORZ, &si, TRUE);
		return 0;
	

	case WM_VSCROLL:
		//获取所有垂直滚动条信息之前,先设置下面两个参数
		si.cbSize = sizeof(si);
		si.fMask = SIF_ALL;
		GetScrollInfo(hwnd, SB_VERT, &si);

		//保存位置以便稍后进行比较
		iVertPos = si.nPos;//指定当前的滚动框的位置
		/*******************nPos滚动条位置计算方法***************************
		//NUMLINES = 75
		//cyClient / cyChar = 50
		//iVscrollMax = 75 - 50 = 25;
		//上面两行代码将滚动条的范围设置为从0---25
		//当滚动条位置是0时,程序显示0--49行
		//当滚动条位置是1时 ,程序显示1---50行
		//当滚动条位置是25最大值时,显示25---74行
		*******************************************************************/
		switch (LOWORD(wParam))//wParam低16位是滚动条值
		{
		case SB_TOP:
			si.nPos =si.nMin;
			break;

		case SB_BOTTOM:
			si.nPos =si.nMax;
			break;

		case SB_LINEUP:
			si.nPos -=1;
			break;

		case SB_LINEDOWN:
			si.nPos += 1;
			break;

		case SB_PAGEUP:
			si.nPos -= si.nPage;
			break;

		case SB_PAGEDOWN:
			si.nPos += si.nPage;
			break;

		case SB_THUMBTRACK:
			si.nPos = si.nTrackPos;
			break;

		default:
			break;
		}
		//设置位置,然后检索他,由于窗口的调整,他可能与设置的值不一样
		si.fMask = SIF_POS;
		//设置滑块位置,有可能超出范围
		SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
		//返回正确的位置,试试跟踪
		GetScrollInfo(hwnd, SB_VERT, &si);
		//如果位置改变,滚动窗口并更新他
		if (si.nPage!=iVertPos)
		{
			//华东整个客户区的内容,第二个参数为水平滑动多少像素,第三个参数为春之滑动像素
			ScrollWindow(hwnd, 0, cyChar * (iVertPos - si.nPos),NULL,NULL);//下断点测试
			//InvalidateRect(hwnd, NULL, TRUE);//并擦除背景
			UpdateWindow(hwnd);//立即更新,发送一个WM_pAINT消息
		}
		return 0;

	case WM_HSCROLL:
		//Get all the vertical scroll bar information
		si.cbSize = sizeof(si);
		si.fMask = SIF_ALL;

		//save the position for comparison later on 
		GetScrollInfo(hwnd, SB_HORZ, &si);
		iHorzPos = si.nPos;
		switch (LOWORD(wParam))
		{
		case SB_LINELEFT:
			si.nPos -= 1;
			break;

		case SB_LINERIGHT:
			si.nPos += 1;
			break;

		case SB_PAGELEFT:
			si.nPos -= si.nPage;
			break;

		case SB_PAGERIGHT:
			si.nPos += si.nPage;
			break;

		case SB_THUMBPOSITION:
			si.nPos = si.nTrackPos;
			break;

		default:
			break;
		}

		si.fMask = SIF_POS;
		SetScrollInfo(hwnd, SB_HORZ, &si, TRUE);
		GetScrollInfo(hwnd, SB_HORZ, &si);
		
		if (si.nPos != iHorzPos)
		{
			ScrollWindow(hwnd, cxChar * (iHorzPos - si.nPos), 0, NULL, NULL);
		}
		return 0;

	case WM_PAINT:
		hdc = BeginPaint(hwnd, &ps);

		si.cbSize = sizeof(si);
		si.fMask = SIF_POS;
		GetScrollInfo(hwnd, SB_VERT, &si);
		iVertPos = si.nPos;

		GetScrollInfo(hwnd, SB_HORZ, &si);
		iHorzPos = si.nPos;

		//find painting limits 
		iPaintBeg = max(0, iVertPos + ps.rcPaint.top / cyChar);
		iPaintEnd = min(NUMLINES - 1,
			iVertPos + ps.rcPaint.bottom / cyChar);

		for (i = iPaintBeg; i <= iPaintEnd; i++)
		{
			x = cxChar * (0 - iHorzPos);//这里用0,1都没问题
			y = cyChar * (i - iVertPos);

			TextOut(hdc, x, y,
				sysmetrics[i].szLabel,
				lstrlen(sysmetrics[i].szLabel));

			TextOut(hdc, x+22*cxCaps, y,
				sysmetrics[i].szDesc,
				lstrlen(sysmetrics[i].szDesc));

			SetTextAlign(hdc, TA_RIGHT | TA_TOP);

			TextOut(hdc, x+22 * cxCaps + 40 * cxChar, y, szBuffer,
				wsprintf(szBuffer, TEXT("%5d"),//tchar.h头文件通用函数
					GetSystemMetrics(sysmetrics[i].iIndex)));

			SetTextAlign(hdc, TA_LEFT | TA_TOP);
		}
		EndPaint(hwnd, &ps);
		return 0;

	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	}

	return DefWindowProc(hwnd, message, wParam, lParam);
}

4.3 滚动条

4.3.1 滚动条的范围和位置
4.3.2 滚动条消息
4.3.3 加入滚动条的SYSTEMS
4.3.4 程序的绘制代码的结构

4.3 效果更好的滚动

4.4.1 滚动条信息函数
4.4.2 最远可以卷动到哪里
4.4.3 新的SYSMETS
4.4.4 可我不想用鼠标

第五章 绘图基础

更新中

更新中

更新中

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程序的开头都可看到:
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值