【Windows】《深入浅出Windows API程序设计:编程基础篇》笔记-Chapter10-通用对话框

作者:江湖人称菠萝包日期:2026/9/9

第10章 通用对话框

通用对话框库包含一组用于执行常见应用程序任务的对话框,例如打开文件、选择颜色和打印文档等,通用对话框实现了应用程序用户界面的一致性,例如单击不同程序的文件菜单下的打开文件子菜单项,通常都会弹出相同的打开文件对话框。要弹出打开文件、选择颜色和打印文档等通用对话框用的是不同的函数,在使用这些函数时,基本上都要初始化一个结构的一些字段并将该结构的指针传递给这些函数。函数会创建并显示相应的对话框,当用户关闭对话框时,函数将控制权返还给程序,然后程序可以从先前传递给函数的结构中获取需要的信息。

常见的通用对话框以及创建通用对话框所需的函数和所用结构如表10.1所示。

表10.1

通用对话框说明函数所用结构
打开文件用户可以在对话框中输入或选择要打开的文件名,还包括一个文件扩展名列表以过滤显示的文件名GetOpenFileNameOPENFILENAME
保存文件用户可以在对话框中输入或选择用于保存的文件名,还包括一个文件扩展名列表以过滤显示的文件名GetSaveFileNameOPENFILENAME
查找用户可以在对话框中输入要查找的字符串,还可以指定搜索选项,例如搜索方向以及是否区分大小写FindTextFINDREPLACE
替换用户可以在对话框中输入要查找的字符串和替换字符串,还可以指定搜索选项,例如是否区分大小写以及替换选项ReplaceTextFINDREPLACE
选择字体用户可以在对话框的字体列表中选择一个字体及其样式、磅值和其他字体属性,例如字体颜色、下划线、删除线ChooseFontCHOOSEFONT
选择颜色用户可以选择基本颜色或自定义颜色ChooseColorCHOOSECOLOR
页面设置用户可以选择页面配置选项,例如纸张方向、大小、来源和边距PageSetupDlgPAGESETUPDLG
打印显示已安装的打印机及其配置信息,用户可以选择打印作业选项,例如要打印的页面范围和份数,然后开始打印过程PrintDlgPRINTDLG

除“查找”和“替换”对话框以外,其他通用对话框都是模态的。“查找”和“替换”对话框则是非模态对话框,如果使用“查找”和“替换”对话框,还应该在程序的主消息循环中使用IsDialogMessage函数,以确保“查找”和“替换”对话框正确处理键盘输入(例如Tab和Esc键)。

10.1 打开和保存文件

用于创建打开文件对话框的函数是GetOpenFileName:

1BOOL WINAPI GetOpenFileName(_Inout_ LPOPENFILENAME lpofn);
2

参数lpofn是一个指向OPENFILENAME结构的指针,调用GetOpenFileName函数前需要初始化该结构的一些字段,当函数返回时会填充该结构的相关字段,结构中包含用户所选择文件的信息。如果用户输入或选择了一个文件名并单击“打开”按钮,则函数返回值为TRUE,OPENFILENAME结构的lpstrFile字段指向的缓冲区包含用户选择的文件名(完整路径);如果用户单击了“取消”按钮或关闭了对话框或发生错误,则返回值为FALSE。

GetOpenFileName函数只是返回用户所选择文件名的信息,至于如何打开这个文件并读写文件内容,还需要用户自己写代码去操作,例如记事本程序在用户单击打开菜单以后会调用GetOpenFileName函数获取到文件名,然后额外写代码打开这个文本文件,读取文本内容并显示到多行编辑控件中。

OPENFILENAME结构在commdlg.h头文件中定义如下:

1typedef struct tagOFN {
2    DWORD         lStructSize;      // 该结构的大小
3    HWND          hwndOwner;        // 对话框的拥有者窗口句柄
4    HINSTANCE     hInstance;        // 用于自定义对话框,指定包含对话框模板的模块句柄
5    LPCTSTR       lpstrFilter;      // 文件扩展名过滤字符串
6    LPTSTR        lpstrCustomFilter;
7    DWORD         nMaxCustFilter;
8    DWORD         nFilterIndex;     // 过滤器索引(从1开始),设置为1表示默认显示第1个过滤器
9    LPTSTR        lpstrFile;        // 返回用户所选择的文件名(完整路径)的缓冲区
10    DWORD         nMaxFile;         // lpstrFile缓冲区的大小,以字符为单位
11    LPTSTR        lpstrFileTitle;   // 返回用户所选择文件的文件名和扩展名(不包括路径)的缓冲区
12    DWORD         nMaxFileTitle;    // lpstrFileTitle缓冲区的大小,以字符为单位
13    LPCTSTR       lpstrInitialDir;  // 初始目录字符串指针,对话框显示以后默认显示的目录
14    LPCTSTR       lpstrTitle;       // 在对话框标题栏中显示的字符串
15    DWORD         Flags;            // 标志位
16    WORD          nFileOffset;      // lpstrFile指向的字符串中的文件名部分从0开始的字符偏移量
17    WORD          nFileExtension;   // lpstrFile指向的字符串中的扩展名部分从0开始的字符偏移量
18    LPCTSTR       lpstrDefExt;      // 默认扩展名字符串
19    LPARAM        lCustData;
20    LPOFNHOOKPROC lpfnHook;
21    LPCTSTR       lpTemplateName;   // 自定义对话框模板名称
22    void          *pvReserved;
23    DWORD         dwReserved;
24    DWORD         FlagsEx;
25} OPENFILENAME, *LPOPENFILENAME;
26

常用的字段解释如下。

  • lStructSize字段指定该结构的大小。
  • hwndOwner字段指定对话框的拥有者窗口句柄。
  • lpstrFilter字段指定文件扩展名过滤字符串列表,显示在对话框右下角的文件类型组合框中。每一项由两个字符串组成。第1个字符串是过滤器描述字符串,例如“文本文件(*.txt)\0”;第2个字符串指定过滤器模式,例如“*.txt\0”。要为一项指定多个过滤器模式,需要使用分号分隔过滤器模式,例如“*.txt;*.doc;*.docx\0”,不要在过滤器模式字符串中包含空格。文件扩展名过滤字符串列表的最后一个字符串必须以两个空字符结束。如果lpstrFilter字段为NULL,则对话框不显示任何过滤器,此时可以选择任何扩展名的文件。下面的文件扩展名过滤字符串列表定义了两项:
1ofn.lpstrFilter = TEXT("文本文件(*.txt, *.doc, *.docx)\0*.txt;*.doc;*.docx\0All(*.*)  
2\0*. *\0");
3

用" "定义的字符串结尾会自动添加一个“\0”,因此双引号内部最后一个字符串后面只需要一个“\0”。

  • 文件扩展名过滤字符串列表的每一项都有一个索引,从1开始,nFilterIndex字段指定默认显示哪一项,例如设置为1表示默认显示第1个过滤器。GetOpenFileName函数返回后,nFilterIndex字段被设置为用户选择的过滤器的索引。
  • lpstrFile字段是返回用户所选择文件名(完整路径)的缓冲区,该缓冲区也用于初始化对话框中编辑控件所显示的文件名。如果不需要初始化,则缓冲区的第1个字符必须设置为NULL。

如果Flags字段指定了OFN_ALLOWMULTISELECT标志并且用户选择了多个文件,则缓冲区包含当前目录,后跟所有所选文件的文件名。对于资源管理器样式的对话框,目录和每个文件名字符串以NULL分隔,在最后一个文件名后面有一个额外的NULL字符。如果缓冲区太小,则函数返回FALSE,调用CommDlgExtendedError函数获取错误码会返回FNERR_BUFFERTOOSMALL。在这种情况下,lpstrFile缓冲区的前2字节包含所需的大小,以字符为单位。例如原书的图10.1中打开了6个文件。

lpstrFile字段返回的内容如原书的图10.2所示。

目录名末尾通常没有“\”字符(在根目录下选择多个文件时会包括“\”字符)。如果需要把目录名和每一个文件名拼接成一个带完整路径的文件名,需要做一些处理,具体请看后面的示例程序。

  • nMaxFile字段指定lpstrFile缓冲区的大小,以字符为单位。
  • lpstrFileTitle字段是返回用户所选文件的文件名和扩展名的缓冲区。
  • nMaxFileTitle字段指定lpstrFileTitle缓冲区的大小,以字符为单位。
  • lpstrInitialDir字段指定初始目录,也就是对话框显示以后默认显示的目录。
  • lpstrTitle字段指定在对话框标题栏中显示的字符串。如果该字段为NULL,则系统使用默认标题(“打开”或“另存为(GetSaveFileName)”)。
  • Flags字段是标志位,常用的标志如表10.2所示。

表10.2

标志含义
OFN_EXPLORER默认值,对话框使用资源管理器样式的用户界面。如果指定了OFN_ALLOWMULTISELECT多选标志,则必须指定该标志才可以使用资源管理器样式的用户界面
OFN_FILEMUSTEXIST用户只能在文件名编辑控件中输入一个已经存在的文件名,如果用户输入了一个当前所选目录中不存在的文件名,则系统会提示文件不存在,使用这个标志的时候必须同时指定 OFN_PATHMUSTEXIST标志
OFN_PATHMUSTEXIST用户只能在文件名编辑控件中输入一个已经存在的路径
OFN_ALLOWMULTISELECT用户可以选择多个文件
OFN_CREATEPROMPT如果用户在文件名编辑控件中输入了一个不存在的文件,系统会弹出对话框提示用户是否新建这个文件,如果用户选择了“是”按钮,对话框关闭并且函数返回所指定的名字
OFN_OVERWRITEPROMPT保存文件时,如果用户在文件名编辑控件中输入了一个已经存在的文件名,系统会提示用户是否覆盖已经存在的文件
  • nFileOffset字段返回lpstrFile指向的字符串中的文件名部分从0开始的字符偏移量。
  • nFileExtension字段返回lpstrFile指向的字符串中的扩展名部分从0开始的字符偏移量,扩展名是文件名字符串中最后一个点(.)之后的子字符串。例如对于文件名“C:\dir1\dir2\file.ext”,nFileOffset字段返回13(file.ext),nFileExtension字段返回18(ext)。
  • lpstrDefExt字段是默认扩展名字符串。如果用户没有在编辑控件中输入扩展名,则会将默认扩展名附加到文件名字符串中,扩展名字符串不需要包含点(.)。

用于创建保存文件对话框的函数是GetSaveFileName:

1BOOL WINAPI GetSaveFileName(_Inout_ LPOPENFILENAME lpofn);
2

参数lpofn同样是一个指向OPENFILENAME结构的指针。调用GetSaveFileName函数前需要初始化该结构的一些字段,当函数返回时会填充该结构的相关字段,结构中包含用户选择的文件的信息。如果用户输入或选择了一个文件名并单击“保存”按钮,则返回值为TRUE,OPENFILENAME结构的lpstrFile字段指向的缓冲区包含用户选择的文件名(完整路径);如果用户单击了“取消”按钮或关闭了对话框或发生错误,则返回值为FALSE。

保存文件对话框让用户选择一个要保存的文件名,函数返回所选择的文件名的信息。保存文件需要程序额外书写代码。

下面实现一个打开文件(支持多选)、保存文件对话框的程序OpenSaveFile:

1#include <windows.h>
2#include <tchar.h>
3#include <strsafe.h>
4#include "resource.h"
5
6#pragma comment(linker,"\"/manifestdependency:type='win32' \
7    name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
8    processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
9
10// 全局变量
11HINSTANCE g_hInstance;
12
13// 函数声明
14INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
15
16int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int   
17nCmdShow)
18{
19    g_hInstance = hInstance;
20
21    // 创建模态对话框
22    DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_MAIN), NULL, DialogProc, NULL);
23    return 0;
24}
25
26INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
27{
28    static HWND hwndList;
29    TCHAR szFile[MAX_PATH * 512] = { 0 };   // 返回用户选择的文件名的缓冲区大一点,本程序允许多选
30    TCHAR szFileTitle[MAX_PATH] = { 0 };    // 返回用户所选文件的文件名和扩展名的缓冲区
31
32    OPENFILENAME ofn = { 0 };
33    ofn.lStructSize = sizeof(ofn);
34    ofn.hwndOwner = hwndDlg;
35    ofn.lpstrFilter =
36        TEXT("文本文件(*.txt, *.doc, *.docx)\0*.txt;*.doc;*.docx\0All(*.*)\  0*.*\0");
37    ofn.nFilterIndex = 1;                   // 默认选择第1个过滤器
38    ofn.lpstrFile = szFile;                 // 返回用户选择的文件名的缓冲区
39    ofn.lpstrFile[0] = NULL;                // 不需要初始化文件名编辑控件
40    ofn.nMaxFile = _countof(szFile);
41    ofn.lpstrFileTitle = szFileTitle;       // 返回用户选择的文件的文件名和扩展名的缓冲区
42    ofn.nMaxFileTitle = _countof(szFileTitle);
43    ofn.lpstrInitialDir = TEXT("C:\\");     // 初始目录
44
45    LPTSTR lpStr;
46    TCHAR szDir[MAX_PATH] = { 0 };
47    TCHAR szBuf[MAX_PATH] = { 0 };
48
49    switch (uMsg)
50    {
51    case WM_INITDIALOG:
52        hwndList = GetDlgItem(hwndDlg, IDC_LIST_FILENAME);
53        return TRUE;
54
55    case WM_COMMAND:
56        switch (LOWORD(wParam))
57        {
58        case IDC_BTN_OPEN:
59            ofn.lpstrTitle = TEXT("请选择要打开的文件");// 对话框标题栏中显示的字符串
60            ofn.Flags = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_ CREATEPROMPT | OFN_ALLOWMULTISELECT;
61            if (GetOpenFileName(&ofn))
62            {
63                // 先清空列表框
64                SendMessage(hwndList, LB_RESETCONTENT, 0, 0);
65
66                lpStr = ofn.lpstrFile + _tcslen(ofn.lpstrFile) + 1;
67                if (lpStr[0] == NULL)
68                {
69                    // 用户只选择了一个文件
70                    SendMessage(hwndList, LB_ADDSTRING, 0, (LPARAM)ofn.lpstrFile);
71                }
72                else
73                {
74                    // 用户选择了多个文件
75                    StringCchCopy(szDir, _countof(szDir), ofn.lpstrFile);
76                    if (szDir[_tcslen(szDir) - 1] != TEXT('\\'))
77                        StringCchCat(szDir, _countof(szDir), TEXT("\\"));
78                    while (lpStr[0] != NULL)
79                    {
80                        StringCchCopy(szBuf, _countof(szBuf), szDir);
81                        StringCchCat(szBuf, _countof(szBuf), lpStr);
82                        SendMessage(hwndList, LB_ADDSTRING, 0, (LPARAM)szBuf);
83                        lpStr += _tcslen(lpStr) + 1;
84                    }
85                }
86            }
87            break;
88
89        case IDC_BTN_SAVE:
90            ofn.lpstrTitle = TEXT("请选择要保存的文件名");     // 对话框标题栏中显示的字符串
91            ofn.lpstrDefExt = TEXT("txt");                  // 默认扩展名
92            ofn.Flags = OFN_EXPLORER | OFN_OVERWRITEPROMPT;
93            if (GetSaveFileName(&ofn))
94            {
95                SendMessage(hwndList, LB_RESETCONTENT, 0, 0);
96                SendMessage(hwndList, LB_ADDSTRING, 0, (LPARAM)ofn.lpstrFile);
97            }
98            break;
99
100        case IDCANCEL:
101            EndDialog(hwndDlg, 0);
102            break;
103        }
104        return TRUE;
105    }
106
107    return FALSE;
108}
109

具体代码参见Chapter10\OpenSaveFile项目。

10.2 浏览文件夹与遍历目录

要想让用户选择一个文件夹,可以调用SHBrowseForFolder函数:

1PIDLIST_ABSOLUTE  SHBrowseForFolder(_In_ LPBROWSEINFO lpbi);
2

函数返回值为PIDLIST_ABSOLUTE数据类型,在shtypes.h头文件中定义如下:

1#define PIDLIST_ABSOLUTE  LPITEMIDLIST
2typedef ITEMIDLIST        *LPITEMIDLIST;
3
4typedef struct _ITEMIDLIST
5{
6    SHITEMID mkid;
7}   ITEMIDLIST;
8
9typedef struct _SHITEMID
10{
11    USHORT cb;
12    BYTE abID[ 1 ];
13}    SHITEMID;
14

返回值类型PIDLIST_ABSOLUTE指定所选文件夹相对于命名空间根目录的位置。如果用户在对话框中单击了“取消”按钮、关闭了对话框或发生了错误,则返回值为NULL。

lpbi参数是一个指向BROWSEINFO结构的指针,BROWSEINFO结构在ShlObj.h头文件中定义如下:

1typedef struct _browseinfo {
2    HWND              hwndOwner;     // 对话框的拥有者窗口句柄
3    PCIDLIST_ABSOLUTE pidlRoot;      // 开始浏览的根文件夹的位置,不需要可以设置为NULL
4    LPTSTR            pszDisplayName;// 返回用户选择的文件夹名称的缓冲区
5    LPCTSTR           lpszTitle;     // 显示在对话框上部静态控件中的文字
6    UINT              ulFlags;       // 标志位
7    BFFCALLBACK       lpfn;
8    LPARAM            lParam;
9    int               iImage;
10} BROWSEINFO, *PBROWSEINFO, *LPBROWSEINFO;
11

ulFlags字段指定标志位,常用的标志如表10.3所示。

表10.3

标志含义
BIF_RETURNONLYFSDIRS如果用户选择不属于文件系统的文件夹(例如网络、家庭组),则对话框的“确定”按钮将显示为灰色
BIF_NEWDIALOGSTYLE使用新的用户界面,新的用户界面具有多项新功能
BIF_EDITBOX在对话框中包含一个编辑控件,允许用户输入文件夹名称
BIF_USENEWUI使用新用户界面,对话框中包含一个编辑控件,相当于`BIF_NEWDIALOGSTYLE
BIF_BROWSEINCLUDEFILES对话框中显示文件和文件夹,通常不设置该标志
BIF_BROWSEINCLUDEURLS对话框可以显示URL快捷方式,还必须设置BIF_USENEWUI和BIF_BROWSEINCLUDEFILES标志,通常不设置该标志

SHBrowseForFolder函数返回一个PIDLIST_ABSOLUTE类型值,即指向ITEMIDLIST结构的指针。不必深究这个结构,因为使用SHGetPathFromIDList函数可以很方便地将它转换成目录名称字符串(完整路径):

1BOOL SHGetPathFromIDList(
2    _In_  PCIDLIST_ABSOLUTE pidl,    // 指向ITEMIDLIST结构的指针
3    _Out_ LPTSTR            pszPath);// 返回目录名称的缓冲区
4

要遍历一个目录中的子目录或文件,首先调用FindFirstFile函数。该函数返回一个HANDLE类型的查找句柄hFindFile,并返回找到的第一个子目录或文件的信息;如果FindFirstFile函数执行成功,则接下来可以利用hFindFile句柄循环调用FindNextFile函数继续查找其他目录或文件,直到FindNextFile函数返回FALSE为止;最后调用FindClose函数关闭hFindFile查找句柄。使用这几个函数查找文件的代码通常如下所示:

1WIN32_FIND_DATA fd = { 0 };
2// 遍历目录
3hFindFile = FindFirstFile(szDir, &fd);
4if (hFindFile != INVALID_HANDLE_VALUE)
5{
6    do
7    {
8        // 处理本次找到的文件
9    } while (FindNextFile(hFindFile, &fd));
10
11    FindClose(hFindFile);
12}
13

FindFirstFile函数用于在目录中搜索指定名称的子目录或文件:

1HANDLE WINAPI FindFirstFile(
2    _In_  LPCTSTR           lpFileName,     // 要查找的文件名,文件名可以包含通配符,例如*或?
3    _Out_ LPWIN32_FIND_DATA lpFindFileData);// 指向查找结构WIN32_FIND_DATA的指针
4
  • lpFileName参数指定要查找的文件名。如果文件名中不包含路径,那么将在当前目录中查找,包含路径的话将在指定路径中查找。在文件名中可以使用通配符“*”或“?”指定查找符合指定特征的文件。下面是文件名格式的几个示例:
1C:\Windows\*.*              // 在C:\Windows目录中查找所有类型的文件
2C:\Windows\System32\*.dll   // 在C:\Windows\System32目录中查找所有扩展名为.dll的文件
3C:\Windows\System.ini       // 在C:\Windows目录中查找System.ini文件
4C:\Windows\a???.*           // 在C:\Windows目录中查找所有以a开头的长度为4个字符的任何文件
5Test.dat                    // 在当前目录中查找Test.dat文件
6*.*                         // 在当前目录中查找所有文件
7
  • lpFindFileData参数是一个指向查找结构WIN32_FIND_DATA的指针。该结构返回找到的目录或文件的信息,在minwinbase.h头文件中定义如下:
1typedef struct _WIN32_FIND_DATA {
2    DWORD    dwFileAttributes;      // 文件系统属性
3    FILETIME ftCreationTime;        // FILETIME格式的文件创建时间
4    FILETIME ftLastAccessTime;      // FILETIME格式的最后访问时间
5    FILETIME ftLastWriteTime;       // FILETIME格式的最后修改时间
6    DWORD    nFileSizeHigh;         // 文件大小的高32位DWORD值,以字节为单位
7    DWORD    nFileSizeLow;          // 文件大小的低32位DWORD值,以字节为单位
8    DWORD    dwReserved0;           // 保留字段
9    DWORD    dwReserved1;           // 保留字段
10    TCHAR    cFileName[MAX_PATH];   // 文件名称(不包括路径)
11    TCHAR    cAlternateFileName[14];// 该文件的替代名称,8.3文件名格式,通常用不到
12} WIN32_FIND_DATA, *PWIN32_FIND_DATA, *LPWIN32_FIND_DATA;
13

dwFileAttributes字段包含文件系统属性,通过这个字段可以检查找到的究竟是一个子目录还是一个文件,以及其他文件系统属性,常见的属性如表10.4所示。

表10.4

属性含义
FILE_ATTRIBUTE_NORMAL普通文件
FILE_ATTRIBUTE_DIRECTORY找到的是一个目录
FILE_ATTRIBUTE_READONLY只读文件
FILE_ATTRIBUTE_TEMPORARY用于临时存储的文件
FILE_ATTRIBUTE_HIDDEN隐藏文件或目录
FILE_ATTRIBUTE_SYSTEM操作系统使用的文件或目录
FILE_ATTRIBUTE_ARCHIVE存档文件或目录
FILE_ATTRIBUTE_COMPRESSED压缩的文件或目录
FILE_ATTRIBUTE_ENCRYPTED已加密的文件或目录

如果FindFirstFile函数执行成功,则返回值是在后续调用FindNextFile或FindClose时使用的查找句柄,而lpFindFileData参数指向的WIN32_FIND_DATA结构包含找到的第一个目录或文件的信息;如果函数执行失败或无法从lpFileName参数指定的搜索字符串中找到目录或文件,则返回值为INVALID_HANDLE_VALUE(−1),这种情况下的lpFindFileData参数指向的结构的内容不确定。

FindNextFile函数以查找句柄和查找结构为参数继续查找目录或文件:

1BOOL WINAPI FindNextFile(
2    _In_  HANDLE            hFindFile,      // 查找句柄
3    _Out_ LPWIN32_FIND_DATA lpFindFileData);// 指向查找结构WIN32_FIND_DATA的指针
4

如果函数执行成功,则返回值为TRUE,lpFindFileData参数指向的结构包含找到的下一个目录或文件的信息;如果函数执行失败,则返回值为FALSE,这种情况下的lpFindFileData参数指向的结构的内容不确定。

例如下面的代码,调用SHBrowseForFolder函数创建一个浏览文件夹对话框,然后调用SHGetPathFromIDList函数把上一个函数的返回值转换为完整目录名称字符串,拼接一个查找字符串szSearch用于FindFirstFile函数,接着就是FindNextFile(hFindFile, &fd)的循环调用。如果找到的是一个目录,那么应该递归往下一层找。这有点麻烦,本例中仅处理了找到的各种文件,例如普通文件、临时文件、隐藏文件等,后面会有递归查找的示例。代码如下:

1#include <windows.h>
2#include <Shlobj.h>
3#include <tchar.h>
4#include <strsafe.h>
5#include "resource.h"
6
7#pragma comment(linker,"\"/manifestdependency:type='win32' \
8    name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
9    processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
10
11// 全局变量
12HINSTANCE g_hInstance;
13
14// 函数声明
15INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
16
17int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int   
18nCmdShow)
19{
20    g_hInstance = hInstance;
21
22    // 创建模态对话框
23    DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_MAIN), NULL, DialogProc, NULL);
24    return 0;
25}
26
27INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
28{
29    static HWND hwndList;
30    PIDLIST_ABSOLUTE pItemIdList;   // SHBrowseForFolder函数返回值
31    BROWSEINFO bi = { 0 };
32    bi.hwndOwner = hwndDlg;
33    bi.lpszTitle = TEXT("请选择一个文件夹");
34    bi.ulFlags = BIF_USENEWUI | BIF_RETURNONLYFSDIRS;
35
36    TCHAR szDir[MAX_PATH] = { 0 };  // SHGetPathFromIDList函数返回的目录名称的缓冲区
37    HANDLE hFindFile;
38    WIN32_FIND_DATA fd = { 0 };
39    TCHAR szSearch[MAX_PATH] = { 0 };
40    TCHAR szDirFile[MAX_PATH] = { 0 };
41
42    switch (uMsg)
43    {
44    case WM_INITDIALOG:
45        hwndList = GetDlgItem(hwndDlg, IDC_LIST_FILENAME);
46        return TRUE;
47
48    case WM_COMMAND:
49        switch (LOWORD(wParam))
50        {
51        case IDC_BTN_BROWSE:
52            pItemIdList = SHBrowseForFolder(&bi);
53            if (pItemIdList)
54            {
55                // 用户选择的文件夹名称显示到静态控件中
56                SHGetPathFromIDList(pItemIdList, szDir);
57                SetDlgItemText(hwndDlg, IDC_STATIC_DIR, szDir);
58
59                if (szDir[_tcslen(szDir) - 1] != TEXT('\\'))
60                    StringCchCat(szDir, _countof(szDir), TEXT("\\"));
61                // 拼接搜索字符串
62                StringCchCopy(szSearch, _countof(szSearch), szDir);
63                StringCchCat(szSearch, _countof(szSearch), TEXT("*.*"));
64                // 遍历目录
65                hFindFile = FindFirstFile(szSearch, &fd);
66                if (hFindFile != INVALID_HANDLE_VALUE)
67                {
68                    // 先清空列表框
69                    SendMessage(hwndList, LB_RESETCONTENT, 0, 0);
70
71                    do
72                    {
73                        // 处理本次找到的各种文件,没有处理找到的目录
74                        if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
75                        {
76                            StringCchCopy(szDirFile, _countof(szDirFile), szDir);
77                            StringCchCat(szDirFile, _countof(szDirFile), fd.cFileName);
78                            SendMessage(hwndList, LB_ADDSTRING, 0, (LPARAM)szDirFile);
79                        }
80                    } while (FindNextFile(hFindFile, &fd));
81
82                    // 关闭查找句柄
83                    FindClose(hFindFile);
84                }
85            }
86            break;
87
88        case IDCANCEL:
89            EndDialog(hwndDlg, 0);
90            break;
91        }
92        return TRUE;
93    }
94
95    return FALSE;
96}
97

具体代码参见Chapter10\SHBrowseForFolder项目。程序运行效果如图10.3所示。

10.3 查找和替换

“查找”和“替换”对话框的样式通常如原书的图10.4所示。

创建查找对话框的函数是FindText,用户可以在对话框中输入要搜索的字符串和相关搜索选项;创建替换对话框的函数是ReplaceText,用户可以在对话框中输入要搜索的字符串和替换的字符串,以及控制查找和替换操作的选项。

1HWND WINAPI FindText(_In_ LPFINDREPLACE lpfr);
2HWND WINAPI ReplaceText(_Inout_ LPFINDREPLACE lpfr);
3

“查找”和“替换”对话框都是非模态对话框。如果函数执行成功,则返回值是对话框的窗口句柄,可以通过该句柄与父窗口进行通信;如果函数执行失败,则返回值为NULL。

参数lpfr是一个指向FINDREPLACE结构的指针,先看一下该结构的定义,再介绍“查找”和“替换”对话框的工作原理。该结构在commdlg.h头文件中定义如下:

1typedef struct {
2    DWORD        lStructSize;     // 该结构的大小
3    HWND         hwndOwner;       // 对话框的拥有者窗口句柄,不能为NULL
4    HINSTANCE    hInstance;
5    DWORD        Flags;           // 标志位
6    LPTSTR       lpstrFindWhat;   //“查找内容”编辑控件中的字符串缓冲区,缓冲区大小至少为80个字符
7    LPTSTR       lpstrReplaceWith;//“替换为”编辑控件中的字符串缓冲区,缓冲区大小至少为80个字符
8    WORD         wFindWhatLen;    // lpstrFindWhat字段指向的缓冲区的长度,以字节为单位
9    WORD         wReplaceWithLen; // lpstrReplaceWith字段指向的缓冲区的长度,以字节为单位
10    LPARAM       lCustData;
11    LPFRHOOKPROC lpfnHook;
12    LPCTSTR      lpTemplateName;
13} FINDREPLACE, *LPFINDREPLACE;
14

Flags字段是标志位,可以是标志的组合(常用的),如表10.5所示。

表10.5

标志含义
FR_FINDNEXT单击了“查找下一个”按钮
FR_REPLACE单击了“替换”按钮
FR_REPLACEALL单击了“全部替换”按钮
FR_DOWN已选中搜索方向的“向下”单选按钮,指示应从当前位置搜索到文档末尾;否则就是已选中“向上”单选按钮,指示应从当前位置搜索到文档开头
FR_MATCHCASE已选中“匹配大小写”复选框
FR_WHOLEWORD已选中“全字匹配”复选框
FR_HIDEUPDOWN隐藏搜索方向的“向上”和“向下”单选按钮
FR_HIDEMATCHCASE隐藏“匹配大小写”复选框
FR_HIDEWHOLEWORD隐藏“全字匹配”复选框
FR_NOUPDOWN禁用搜索方向的“向上”和“向下”单选按钮
FR_NOMATCHCASE禁用“匹配大小写”复选框
FR_NOWHOLEWORD禁用“全字匹配”复选框
FR_DIALOGTERM对话框正在关闭

在调用FindText和ReplaceText函数后,系统根据FINDREPLACE结构中字段的设置初始化对话框,例如根据Flags字段的值设置对话框中相关控件的选中、禁用或隐藏状态,lpstrFindWhat字段指向的缓冲区的内容会显示到“查找内容”编辑控件中,lpstrReplaceWith字段指向的缓冲区的内容会显示到“替换为”编辑控件中(查找对话框不需要该字段)。

“查找”和“替换”对话框都是非模态对话框。在调用FindText和ReplaceText函数后,系统显示对话框并马上返回,对话框保持显示状态,直到用户关闭对话框。如果用户按下了对话框中的某个按钮,则对话框设置FINDREPLACE结构的相关字段并通过“查找替换消息”通知父窗口的窗口过程,程序中处理查找和替换的功能集中在这个“查找替换消息”中完成。在该消息中可以根据Flags字段的值确定用户按下了哪个按钮,根据lpstrFindWhat字段指向的缓冲区确定“查找内容”编辑控件的内容,根据lpstrReplaceWith字段指向的缓冲区确定“替换为”编辑控件的内容。由于对话框必须向父窗口发送消息,因此hwndOwner字段必须指定为父窗口的句柄。

“查找替换消息”的ID值是多少呢?和拖动列表消息类似,在调用FindText和ReplaceText前,需要调用RegisterWindowMessage(FINDMSGSTRING)函数以获取“查找替换消息”的ID,“查找替换消息”的lParam参数是指向创建对话框时指定的FINDREPLACE结构的指针。FINDMSGSTRING常量在commdlg.h头文件中定义如下:

1#define FINDMSGSTRING  TEXT("commdlg_FindReplace")
2

在程序的消息循环中使用IsDialogMessage函数以确保对话框正确处理键盘输入(例如Tab键和Esc键)。因此,如果需要使用“查找”和“替换”对话框,不适合使用对话框程序。另外,FINDREPLACE结构和搜索与替换字符串的缓冲区变量应该是全局或静态局部变量,这样在下一次消息处理的时候还可以继续使用上次保存的值。

“查找”和“替换”对话框的应用场合不多,主要用于多行编辑控件和富文本控件,这里仅实现一个简单的示例程序FindReplaceText,FindReplaceText.cpp,源文件的内容如下所示:

1#include <Windows.h>
2#include <tchar.h>
3#include <strsafe.h>
4#include "resource.h"
5
6#pragma comment(linker,"\"/manifestdependency:type='win32' \
7    name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
8    processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
9
10// 函数声明
11LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
12
13// 全局变量
14HWND g_hwndFind;    // 消息循环中会用到这两个全局变量
15HWND g_hwndReplace;
16
17int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int   
18                       nCmdShow)
19{
20    WNDCLASSEX wndclass;
21    TCHAR szAppName[] = TEXT("FindReplaceText");
22    HWND hwnd;
23    MSG msg;
24
25    wndclass.cbSize = sizeof(WNDCLASSEX);
26    wndclass.style = CS_HREDRAW | CS_VREDRAW;
27    wndclass.lpfnWndProc = WindowProc;
28    wndclass.cbClsExtra = 0;
29    wndclass.cbWndExtra = 0;
30    wndclass.hInstance = hInstance;
31    wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
32    wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
33    wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
34    wndclass.lpszMenuName = MAKEINTRESOURCE(IDR_MENU);
35    wndclass.lpszClassName = szAppName;
36    wndclass.hIconSm = NULL;
37    RegisterClassEx(&wndclass);
38
39    hwnd = CreateWindowEx(0, szAppName, szAppName, WS_OVERLAPPEDWINDOW,
40        CW_USEDEFAULT, CW_USEDEFAULT, 400, 300, NULL, NULL, hInstance, NULL);
41
42    ShowWindow(hwnd, nCmdShow);
43    UpdateWindow(hwnd);
44
45    while (GetMessage(&msg, NULL, 0, 0) != 0)
46    {
47        if ((g_hwndFind == NULL || !IsDialogMessage(g_hwndFind, &msg)) &&
48            (g_hwndReplace == NULL || !IsDialogMessage(g_hwndReplace, &msg)))
49        {
50            TranslateMessage(&msg);
51            DispatchMessage(&msg);
52        }
53    }
54
55    return msg.wParam;
56}
57
58LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
59{
60    static UINT WM_FINDREPLACE;
61    static TCHAR szFindWhat[80] = { 0 };
62    static TCHAR szReplaceWith[80] = { 0 };
63    static FINDREPLACE fr = { 0 };
64
65    LPFINDREPLACE lpfr;
66    TCHAR szBuf[256] = { 0 };
67
68    switch (uMsg)
69    {
70    case WM_CREATE:
71        // 获取查找替换消息的ID
72        WM_FINDREPLACE = RegisterWindowMessage(FINDMSGSTRING);
73
74        // 初始化FINDREPLACE结构,该结构只应该初始化一次
75        fr.lStructSize = sizeof(FINDREPLACE);
76        fr.hwndOwner = hwnd;
77        fr.lpstrFindWhat = szFindWhat;
78        fr.lpstrReplaceWith = szReplaceWith;
79        fr.wFindWhatLen = sizeof(szFindWhat);
80        fr.wReplaceWithLen = sizeof(szReplaceWith);
81        //fr.Flags = FR_DOWN | FR_MATCHCASE;
82        return 0;
83
84    case WM_COMMAND:
85        switch (LOWORD(wParam))
86        {
87        case ID_EDIT_FIND:
88            if (g_hwndFind == NULL && g_hwndReplace == NULL)
89            {
90                // &掉上次关闭对话框时设置的FR_DIALOGTERM标志值
91                fr.Flags &= ~FR_DIALOGTERM;
92                g_hwndFind = FindText(&fr);
93            }
94            break;
95
96        case ID_EDIT_REPLACE:
97            if (g_hwndReplace == NULL && g_hwndFind == NULL)
98            {
99                // &掉上次关闭对话框时设置的FR_DIALOGTERM标志值
100                fr.Flags &= ~FR_DIALOGTERM;
101                g_hwndReplace = ReplaceText(&fr);
102            }
103            break;
104        }
105        return 0;
106
107    case WM_DESTROY:
108        PostQuitMessage(0);
109        return 0;
110    }
111
112    //////////////////////////////////////////////////////////////////////////
113    if (uMsg == WM_FINDREPLACE)
114    {
115        lpfr = (LPFINDREPLACE)lParam;
116        // 关闭对话框
117        if (lpfr->Flags & FR_DIALOGTERM)
118        {
119            g_hwndFind = NULL;
120            g_hwndReplace = NULL;
121        }
122
123        // 查找下一个
124        else if (lpfr->Flags & FR_FINDNEXT)
125        {
126            StringCchCopy(szBuf, _countof(szBuf), TEXT("单击了"查找下一个"按钮"));
127            if (lpfr->Flags & FR_DOWN)
128                StringCchCat(szBuf, _countof(szBuf), TEXT("\n选中了"向下"单选按钮"));
129            else
130                StringCchCat(szBuf, _countof(szBuf), TEXT("\n选中了"向上"单选按钮"));
131            if (lpfr->Flags & FR_MATCHCASE)
132                StringCchCat(szBuf, _countof(szBuf), TEXT("\n选中了"匹配大小写"复选框"));
133            if (lpfr->Flags & FR_WHOLEWORD)
134                StringCchCat(szBuf, _countof(szBuf), TEXT("\n选中了"全字匹配"复选框"));
135
136            StringCchCat(szBuf, _countof(szBuf), TEXT("\n\n查找内容:"));
137            StringCchCat(szBuf, _countof(szBuf), fr.lpstrFindWhat);
138            MessageBox(hwnd, szBuf, TEXT("提示"), MB_OK);
139        }
140
141        // 替换
142        else if (lpfr->Flags & FR_REPLACE)
143        {
144            StringCchCopy(szBuf, _countof(szBuf), TEXT("单击了"替换"按钮"));
145            if (lpfr->Flags & FR_DOWN)
146                StringCchCat(szBuf, _countof(szBuf), TEXT("\n选中了"向下"单选按钮"));
147            else
148                StringCchCat(szBuf, _countof(szBuf), TEXT("\n选中了"向上"单选按钮"));
149            if (lpfr->Flags & FR_MATCHCASE)
150                StringCchCat(szBuf, _countof(szBuf), TEXT("\n选中了"匹配大小写"复选框"));
151            if (lpfr->Flags & FR_WHOLEWORD)
152                StringCchCat(szBuf, _countof(szBuf), TEXT("\n选中了"全字匹配"复选框"));
153
154            StringCchCat(szBuf, _countof(szBuf), TEXT("\n\n查找内容:"));
155            StringCchCat(szBuf, _countof(szBuf), fr.lpstrFindWhat);
156            StringCchCat(szBuf, _countof(szBuf), TEXT("\n替换内容:"));
157            StringCchCat(szBuf, _countof(szBuf), fr.lpstrReplaceWith);
158            MessageBox(hwnd, szBuf, TEXT("提示"), MB_OK);
159        }
160
161        // 全部替换
162        else if (lpfr->Flags & FR_REPLACEALL)
163        {
164            StringCchCopy(szBuf, _countof(szBuf), TEXT("单击了"全部替换"按钮"));
165            if (lpfr->Flags & FR_DOWN)
166                StringCchCat(szBuf, _countof(szBuf), TEXT("\n选中了"向下"单选按钮"));
167            else
168                StringCchCat(szBuf, _countof(szBuf), TEXT("\n选中了"向上"单选按钮"));
169            if (lpfr->Flags & FR_MATCHCASE)
170                StringCchCat(szBuf, _countof(szBuf), TEXT("\n选中了"匹配大小写”复选框"));
171            if (lpfr->Flags & FR_WHOLEWORD)
172                StringCchCat(szBuf, _countof(szBuf), TEXT("\n选中了"全字匹配"复选框"));
173            StringCchCat(szBuf, _countof(szBuf), TEXT("\n\n查找内容:"));
174            StringCchCat(szBuf, _countof(szBuf), fr.lpstrFindWhat);
175            StringCchCat(szBuf, _countof(szBuf), TEXT("\n替换内容:"));
176            StringCchCat(szBuf, _countof(szBuf), fr.lpstrReplaceWith);
177            MessageBox(hwnd, szBuf, TEXT("提示"), MB_OK);
178        }
179
180        return 0;
181    }
182    //////////////////////////////////////////////////////////////////////////
183
184    return DefWindowProc(hwnd, uMsg, wParam, lParam);
185}
186

10.4 选择字体

字体对话框允许用户选择逻辑字体的属性,例如字体名称、大小、字形、效果(下划线、删除线、字体颜色)以及字符集等。创建字体对话框用的函数是ChooseFont:

1BOOL WINAPI ChooseFont(_Inout_ LPCHOOSEFONT lpcf);
2

如果用户单击了“确定”按钮,则函数返回TRUE,并在CHOOSEFONT结构中返回有关用户选择的字体的信息;如果用户单击了“取消”按钮、关闭了对话框或发生错误,则返回FALSE。

lpcf参数是一个指向CHOOSEFONT结构的指针,该结构包含函数用于初始化字体对话框的信息,函数在该结构中返回有关用户选择的字体的信息。该结构在commdlg.h头文件中定义如下:

1typedef struct {
2    DWORD        lStructSize;   // 该结构的大小
3    HWND         hwndOwner;     // 对话框的拥有者窗口句柄
4    HDC          hDC;           // 忽略该字段
5
6    LPLOGFONT    lpLogFont;     // 函数使用该LOGFONT结构初始化对话框的字体,并返回字体信息
7    INT          iPointSize;    // 返回所选字体的大小,单位是1/10磅
8    DWORD        Flags;         // 标志位
9    COLORREF     rgbColors;     // 字体颜色
10    LPARAM       lCustData;
11    LPCFHOOKPROC lpfnHook;
12    LPCTSTR      lpTemplateName;
13    HINSTANCE    hInstance;
14    LPTSTR       lpszStyle;
15    WORD         nFontType;     // 返回所选字体的类型
16    INT          nSizeMin;
17    INT          nSizeMax;
18} CHOOSEFONT, *LPCHOOSEFONT;
19
  • lStructSize字段指定该结构的大小。
  • hwndOwner字段指定对话框的拥有者窗口句柄。
  • lpLogFont字段指定为一个指向LOGFONT结构的指针,函数使用该结构初始化对话框的字体,并通过该结构返回用户所选择的字体信息,返回的LOGFONT结构可以用于创建逻辑字体。
  • iPointSize字段返回所选字体的大小,单位是1/10磅。
  • Flags字段是标志位,用于初始化字体对话框,函数返回时也会设置这些标志以指示用户的选择。常用的标志如表10.6所示。

表10.6

标志含义
CF_EFFECTS显示“效果”分组框,包括删除线、下划线复选框和颜色组合框。指定该标志以后,可以通过CHOOSEFONT结构的lpLogFont->lfStrikeOut、lpLogFont-> lfUnderline、rgbColors字段初始化字体对话框的效果分组框中的删除线、下划线、颜色控件,函数在这些字段中返回用户的相关选择
CF_INITTOLOGFONTSTRUCT使用lpLogFont字段指向的结构来初始化对话框相关控件
CF_NOVERTFONTS仅列出水平方向字体
CF_TTONLY仅列出TrueType字体
CF_FIXEDPITCHONLY仅列出等宽字体
  • nFontType字段返回用户所选字体的类型,例如BOLD_FONTTYPE粗体、ITALIC_FONTTYPE斜体、REGULAR_FONTTYPE常规等。实际上这些字体类型信息以及字体大小、效果信息等都已经包含在LOGFONT结构中。

关于字体对话框的简单示例程序参见Chapter10\ChooseFont项目。

10.5 选择颜色

选择颜色对话框的效果如原书的图10.5所示。

用户可以从对话框左侧的“基本颜色”或右侧的“自定义颜色”中选择一种颜色。“基本颜色”是系统预定义好的颜色,也可以在“自定义颜色”中单击选择一种颜色,或者输入红、绿、蓝颜色值,也可以输入色调、饱和度、亮度来生成一个颜色值。选择一种颜色后,颜色效果会实时显示在“颜色|纯色(O)”静态控件中。实际上对话框中除几个编辑控件和按钮以外,其他的控件基本上都是静态控件。

创建选择颜色对话框的函数是ChooseColor:

1BOOL WINAPI ChooseColor(_Inout_ LPCHOOSECOLOR lpcc);
2

lpcc参数是一个指向CHOOSECOLOR结构的指针,该结构包含用于初始化对话框的信息。当ChooseColor函数返回时,该结构包含用户选择的颜色的信息。

如果用户单击对话框的“确定”按钮,则返回值为TRUE;如果用户单击了“取消”按钮、关闭了对话框或发生错误,则返回值为FALSE。

CHOOSECOLOR结构在commdlg.h头文件中定义如下:

1typedef struct {
2    DWORD        lStructSize;   // 该结构的大小
3    HWND         hwndOwner;     // 对话框的拥有者窗口句柄
4    HWND         hInstance;
5    COLORREF     rgbResult;     // 创建对话框时初始选择的颜色,函数返回用户所选择的颜色
6    COLORREF     *lpCustColors; // 自定义颜色数组,用于存放对话框左下角的16个自定义颜色
7    DWORD        Flags;         // 标志位
8    LPARAM       lCustData;
9    LPCCHOOKPROC lpfnHook;
10    LPCTSTR      lpTemplateName;
11} CHOOSECOLOR, *LPCHOOSECOLOR;
12

Flags字段是标志位,用于初始化颜色对话框。当对话框返回时,系统会设置这些标志以指示用户的输入。该字段可以是表10.7所示的标志的组合。

表10.7

标志含义
CC_FULLOPEN对话框显示右侧的自定义颜色相关控件。如果未设置该标志,则用户必须单击对话框左侧的“规定自定义颜色(D)>>”按钮才可以显示自定义颜色相关控件
CC_PREVENTFULLOPEN禁用对话框左侧的“规定自定义颜色(D)>>”按钮
CC_RGBINIT使用CHOOSECOLOR结构中rgbResult字段指定的颜色作为对话框的初始颜色选择

关于选择颜色对话框的简单示例程序参见Chapter10\ChooseColor项目。

还有一个用于创建页面设置对话框的PageSetupDlg函数,用于打印的PrintDlg函数,因为应用不多,有需要的读者请自行参考MSDN。


《【Windows】《深入浅出Windows API程序设计:编程基础篇》笔记-Chapter10-通用对话框》 是转载文章,点击查看原文。


相关推荐


从零创建一个 Django 项目
2601_962065252026/9/1

1. 准备环境在开始之前,确保你的开发环境满足以下要求:* 安装了 Python (推荐 3.8 或更高版本)。* 安装 pip 包管理工具。* 如果要使用 MySQL 或 PostgreSQL,确保对应的数据库已安装。#### 创建虚拟环境在项目目录中创建并激活虚拟环境,保证项目依赖隔离: # 创建虚拟环境 python -m venv env # 激活虚拟环境 # Windows envScriptsactivate # Linux/Mac source env/bin/activate *


算法二叉树(一)
纪念 2292026/8/24

༺ 个人主页 · 纪念229 ༻ 🏠我的博客主页🏠 ༒专栏目录:《数据结构》༒ ༒专栏目录:《算法》༒ ༒专栏目录:《MySQL数据库》༒ ༒专栏目录:《前端开发》༒ ༒其它有趣的计算机知识༒ ༺世上本没有路,走的人多了自然就有了༻ 本节讲的是有关二叉树的OJ题,希望对你有所帮助 题目链接 二叉树前序遍历 二叉树中序遍历 二叉树的后序遍历 单值二叉树 对称二叉树 文章目录 1.二叉树前序遍历2.单值二叉树3.对称二叉树 文章开始前我与大家讨论一


AI agent学习之一基础知识
风之清扬2026/8/11

AI agent学习之一基础知识 一、基础知识案例1:获取 GitHub 用户粉丝数案例2:YAML文件的读写案例3:Git操作(git clone、commit和push)1)git clone2)先将修改文件暂存3)提交4)推送5)转换分支(checkout) 案例4:文件操作Linux端:Windows端: 案例5:API auth验证带token和不带token区别 AI agent的使用已经成为一个趋势,尽早入行才是王道。面对海量资料,如何入门确实是一个


Vue项目创建与入口配置全攻略
En^_^Joy2026/8/2

文章目录 一、vue项目创建二、入口界面配置 一、vue项目创建 创建项目:npm create vue@latest 执行后输入项目名(VueDemo),包名称(VueDemo),是否使用typeScript语法 E:\CODE\vueDemo>npm create vue@latest // 执行的指令 Need to install the following packages: create-vue@3.22.4 Ok to proceed? (y


C++ async 异步学习
小侯不躺平.2026/7/25

创建异步任务、延迟任务。任务或操作可以独立于主线程运行,主线程无需等待任务完成便可执行其他的任务,当异步的任务完成的时候可以通过某种机制获取其结果(std::future) 1、异步任务--async 正常使用环境 future.get #include<iostream> #include<thread> #include<chrono> #include<future> int task(int number) { // 抛出异常 或 返回正常结果 std::cout<


Spark 源码 | SparkSubmitArguments 参数解析(三)
董可伦2026/7/17

前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站:https://www.captainai.net/dongkelun 前言 在第二篇文章 Spark 源码 | SparkSubmit 提交流程分析(二) 中,我们分析了 SparkSubmit 的提交流程,其中 doSubmit 方法会调用 parseArguments 解析命令行参数。本文详细介绍参数解析的过程。 版本 Spark 3.2.3 SparkSubmitArguments


LeetCode 459. 重复的子字符串
Best_Jerry2026/7/9

leetcode.cn/problems/re… programmercarl.com/0459.%E9%87… 给定一个非空的字符串 s ,检查是否可以通过由它的一个子串重复多次构成。   示例 1: 输入: s = "abab" 输出: true 解释: 可由子串 "ab" 重复两次构成。 示例 2: 输入: s = "aba" 输出: false 示例 3: 输入: s = "abcabcabcabc" 输出: true 解释: 可由子串 "abc" 重复四次构成。 (或子串 "abc


HDFS javaAPI-windows的IDEA中java文件在linux中的hadoop平台运行
chde2Wang2026/7/1

目录 运行前提 一、在IDEA的Maven项目中创建MkDirDemo类 (1)先确认目录结构(Maven 标准目录,必须按这个来) (2)hdfs java操作代码输入 (3)验证pom.xml文件中hadoop依赖是否和linux中hadoop版本一致 (4)确认 HDFS RPC 地址(关键) 二、Windows 本地配置 Hadoop 运行环境(必做,否则报 winutils 缺失) (1)下载 Windows 适配 hadoop 二进制包Hadoop3.x 下载对应


Obsidian - 使用 Share Note 分享笔记并自部署
LinXunFeng2026/6/22

欢迎关注微信公众号:FSA全栈行动 👋 一、前言 最近在整理 Obsidian 笔记时,经常会遇到一个小需求:想把某一篇笔记快速分享给别人,但又不想为了它单独搭建博客、导出 PDF 或复制到其它平台。 如果只是分享纯文本,复制粘贴当然可以。但 Obsidian 笔记里往往会有这些内容: 图片附件 代码块 Callout 提示块 标签、任务列表 Dataview 查询结果 当前主题样式和自定义 CSS 笔记之间的内部链接 这时候手动搬运就有点麻烦了,格式容易丢,图片也要重新处理。这里介绍一


RabbitMQ 从入门到精通:Spring Boot 实战三部曲(一)—— 基础核心与快速上手
绝知此事2026/6/14

RabbitMQ 从入门到精通:Spring Boot 实战三部曲(一)—— 基础核心与快速上手 专题导读:本系列共三篇,从基础到高级,带你系统掌握 RabbitMQ 在 Spring Boot 项目中的实战应用。 第一篇:基础核心与快速上手(本文)第二篇:进阶特性与可靠性保障第三篇:高级应用与性能优化 📖 前言 在当今的分布式系统中,消息队列已成为不可或缺的基础设施。RabbitMQ 作为最流行的消息中间件之一,以其可靠性、灵活性和易用性著称。 本文将从 RabbitMQ 的基础概念出

首页编辑器站点地图

本站内容在 CC BY-SA 4.0 协议下发布

Copyright © 2026 聚合阅读