Windows SDK 教程(四) 记事本与SendMessage

茵蒂克丝

SendMessage 相关内容整理中。。下载见底部。。

加载 Rich Edit 控件

跟前面的流程类似,新建一个 Dialog 接着从工具箱中拖出来 Rich Edit 控件。 在代码中设置 LoadLibrary(“RICHED20.DLL”); 即可。一下是代码

#include <Windows.h>
#include "resource.h"

int CALLBACK DialogProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
	switch(Message)
	{
	case WM_INITDIALOG:
		break;
	case WM_COMMAND:
		{		
			switch(wParam)
			{
			case IDOK:
				MessageBox(hwnd, TEXT("hello"), NULL, MB_OK);
				break;
			case IDCANCEL:
				EndDialog(hwnd, 0);
				break;
			}
		}
		break;
	case WM_CLOSE:				
		DestroyWindow(hwnd);
		break;
	}
	return 0;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, 
					LPSTR lpCmdLine, int nShowCmd )
{
	// 加载 Rich Edit 控件的 DLL 动态链接库
	LoadLibrary("RICHED20.DLL");
	DialogBox(hInstance, MAKEINTRESOURCE(IDD_MAIN), NULL, DialogProc);
}

不过,光是加载到窗口中还不够。通常我们还有几个选项要设置:

Multiline 可以多行编辑
Want Return 指定 Rich Edit 接受回车键。false 则按下 enter 键默认调用 IDOK 控件。
Vertical Scroll 指定垂直滚动条
Horizontal Scrollbar 指定水平滚动条

添加菜单项

添加->资源->菜单->编辑菜单->设置菜单ID ->将菜单加入 Dialog 的 Menu 项中, 详见上一讲

测试代码:

#include <Windows.h>
#include "resource.h"

void echo(char *str)
{
	MessageBox(NULL, str, TEXT("提示"), MB_OK);
}

int CALLBACK DialogProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
	switch(Message)
	{
	case WM_INITDIALOG:
		break;
	case WM_COMMAND:
		{       
			switch(wParam)
			{
			case ID_NEW_FILE:
				echo("ID_NEW_FILE");
				break;
			case ID_OPEN_FILE:
				echo("ID_OPEN_FILE");
				break;
			case ID_EXIT:
				echo("ID_EXIT");
				break;
			}
		}
		break;
	case WM_CLOSE:
		DestroyWindow(hwnd);
		break;
	}
	return 0;
}

OpenFileName 窗口

GetOpenFileName 函数用于打开一个窗口,该窗口可以让用户在使用系统自带的选择框来选择一个文件,当用户按下确认之后,该 API 就会获取到用户选择的文件的路径。

示例代码:

OPENFILENAME ofn;       // OpenFileName 结构体
char szFile[260];       // 用于保存文件名的缓冲字符串
HWND hwnd;              // 父窗口的句柄

// 初始化 OpenFileName 结构体
ZeroMemory(&ofn, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = hwnd;
ofn.lpstrFile = szFile;
// Set lpstrFile[0] to '\0' so that GetOpenFileName does not 
// use the contents of szFile to initialize itself.
ofn.lpstrFile[0] = '\0';
ofn.nMaxFile = sizeof(szFile);
ofn.lpstrFilter = "All\0*.*\0Text\0*.TXT\0";
ofn.nFilterIndex = 1;
ofn.lpstrFileTitle = NULL;
ofn.nMaxFileTitle = 0;
ofn.lpstrInitialDir = NULL;
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;

// 如果成功获取
if (GetOpenFileName(&ofn)==TRUE) 
{
	// 输出文件名
    MessageBox(NULL, ofn.lpstrFile, TEXT("Title"), MB_OK);
}

效果截图:

OpenFileNameDialog

读取文件内容到 Rich Edit

#include <Windows.h>
#include "resource.h"
#include <stdio.h>

void echo(char *str)
{
	MessageBox(NULL, str, TEXT("提示"), MB_OK);
}

int CALLBACK DialogProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
	OPENFILENAME ofn;       // OpenFileName 的结构体
	char szFile[260];       // 保存文件名称的缓冲字符串
	HANDLE hf;              // 文件句柄
	DWORD fileSize, readSize;
	char *buffer;

	switch(Message)
	{
	case WM_INITDIALOG:
		break;
	case WM_COMMAND:
		{       
			switch(wParam)
			{
			case ID_NEW_FILE:
				SetDlgItemText(hwnd, IDC_TEXT, "");
				break;
			case ID_OPEN_FILE:
				// 初始化 OPENFILENAME 结构体
				ZeroMemory(&ofn, sizeof(ofn));
				ofn.lStructSize = sizeof(ofn);
				ofn.hwndOwner = hwnd;
				ofn.lpstrFile = szFile;
				ofn.lpstrFile[0] = '\0';
				ofn.nMaxFile = sizeof(szFile);
				ofn.lpstrFilter = "All\0*.*\0Text\0*.TXT\0";
				ofn.nFilterIndex = 1;
				ofn.lpstrFileTitle = NULL;
				ofn.nMaxFileTitle = 0;
				ofn.lpstrInitialDir = NULL;
				ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;

				// 若成功获取到文件名
				if (GetOpenFileName(&ofn)==TRUE)
				{
					// 输出文件名
					//echo(szFile);

					// 获取文件句柄
					hf = CreateFile(
						szFile, 						// 文件名
						GENERIC_READ, 					// 只读方式打开
						0,								// 阻止其他进程访问
						(LPSECURITY_ATTRIBUTES) NULL,	// 子进程不可继承本句柄
						OPEN_EXISTING,					// 只当文件存在时打开
						FILE_ATTRIBUTE_NORMAL,			// 普通文件
						(HANDLE) NULL					// 不适用模板文件
					);
				}

				if (hf == INVALID_HANDLE_VALUE) // 如果打开失败
				{ 
					echo("无法打开文件n");
				} else 
				{
					fileSize = GetFileSize(hf,NULL); // 获取文件大小
					buffer = (char *)malloc(fileSize + 1); // 获取一块内存
					buffer[fileSize] = '\0'; // 设置结尾

					// 通过文件句柄读取文件到 buffer 中
					ReadFile(
						hf,			// 文件句柄
						buffer,     // 读取到的文件所存放的缓冲区
						fileSize,   // 要读取的字节数
						&readSize,  // 实际读取的字节数
						NULL        // 用 FILE_FLAG_OVERLAPPED 打开时所需的
						);

					// 将 buffer 中的内容吸入 Rich Edit
					SetDlgItemText(hwnd, IDC_TEXT, buffer);

					CloseHandle(hf); // 关闭文件句柄
					free(buffer);	// 释放内存
				}			
					
				break;
			case ID_EXIT:
				EndDialog(hwnd, 0);
				break;   				
			}
		}
		break;
	case WM_CLOSE:              
		DestroyWindow(hwnd);
		break;
	}
	return 0;
}

notepad-1

Rich Edit 控件常见 Message

WM_SETTEXT 设置整个控件的文本内容
EM_REPLACESEL 替换当前文本的内容
EM_EXGETSEL 获取当前选中的范围
EM_EXSETSEL 设置某一范围被选中
EM_LINEINDEX 获取某一行的起始位置
EM_EXLINEFROMCHAR 获取某一个位置所处的行数
EM_SETCHARFORMAT 设置文本格式

更多请参见 MSDN:http://msdn.microsoft.com/en-us/library/windows/desktop/ff486015(v=vs.85).aspx

WM_SETTEXT 设置文本内容 (set text)

原本是通过 SetDlgItemText 来设置 Rich Edit 控件内的文字,不过我们也可以通过 Rich Edit 原本的 Message 来实现这一效果。

SendMessage(hwnd, WM_SETTEXT, NULL, (LPARAM)"hello world");

EM_REPLACESEL 替换当前文本 (replace selection)

通过 WM_SETTEXT 或者 SetDlgItemText 来设置文本都一个不足就是,这个设置是直接设置整个不会保留原本的文本,还有一个缺陷就是如果要设置的文本过多的话就会有明显的延迟或者卡壳的感觉。

SendMessage(hwnd, EM_REPLACESEL, NULL, (LPARAM)"hello message!");

如果当前有选中文本的话,传入的文本就会替换过来,如果当前没有选中的话,那么默认是在文本的最末尾插入。要简单的优化一下记事本的话可以通过这个来实现————读取一点加载一点,这种分段式的加载方式就不会有延迟或者卡壳的情况了。

EM_EXGETSEL 获取选中的范围 (get selection)

首先要了解一下 CHARRANGE 结构体也即 char range 文本范围结构体

typedef struct _charrange
{
	LONG	cpMin; // 起点位置
	LONG	cpMax; // 终点位置
} CHARRANGE;

CHARRANGE cr;										
SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&cr); 

执行后就能获取到当前 RichEdit 中焦点所选中的区域或者是当前指针所在的为止。

EM_EXSETSEL 设置范围选中 (set selection)

CHARRANGE cr;
cr.cpMin = 0;
cr.cpMax = 5;
SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&cr);

该代码会使的 RichEdit 中 0~5 这个范围的文字被选中。

EM_LINEINDEX 获取某一行的起始位置 (line index)

int line = 10, pos = 0;
pos = SendMessage(hwnd, EM_LINEINDEX, line, 0); // 获取第10行第一个位置

EM_EXLINEFROMCHAR 获取某一个位置所处的行数 (line from char)

int line;
CHARRANGE cr;										
SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&cr); // 获取当前选中范围
line = SendMessage(hwnd, EM_EXLINEFROMCHAR, 0, cr.cpMin); // 获取当前所处的行数

EM_SETCHARFORMAT 设置文本格式 (set char format)

CHARFORMAT2 char format 文本格式结构体

int line = 10;
CHARFORMAT2 cf;
CHARRANGE cr;
cr.cpMin = SendMessage(hwnd, EM_LINEINDEX, line, 0);
cr.cpMax = SendMessage(hwnd, EM_LINEINDEX, line+1, 0) - 1;
SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&cr); // 选中第 10 行
	
memset( &cf, 0, sizeof(CHARFORMAT2) ); // 清空 cf
cf.cbSize = sizeof(CHARFORMAT2);  
cf.dwMask = CFM_BACKCOLOR | CFM_COLOR | CFM_UNDERLINETYPE ;      // 使crBackColor字段有效           
cf.crBackColor = RGB(255, 0, 255); // 设置背景颜色
cf.dwEffects = CFE_UNDERLINE;
cf.crTextColor = RGB(0,0,255); // 设置字体颜色
SendMessage( hwnd,EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf ); // 设置第10行文字的格式

文章索引百度网盘
上一讲:Windows SDK 教程(三) 一些细节以及动态创建控件

Advertisements

1 thought on “Windows SDK 教程(四) 记事本与SendMessage

发表评论

Fill in your details below or click an icon to log in:

WordPress.com 徽标

You are commenting using your WordPress.com account. Log Out /  更改 )

Google photo

You are commenting using your Google account. Log Out /  更改 )

Twitter picture

You are commenting using your Twitter account. Log Out /  更改 )

Facebook photo

You are commenting using your Facebook account. Log Out /  更改 )

Connecting to %s