windows SDK PopPad3
一个比较完整的记事本,借以学习
windows API程序设计
模块化程序设计
windows消息机制
模块之间通信
面向对象思想
项目结构
其项目结构为:
image-20220824171205363
其中源文件的名称是自解释的,
PopPad.c实现主窗口过程,它调用其他模块中的函数实现功能
PopFile.c模块,实现的是与文件系统交互的逻辑
PopFind.c模块,实现的是查找功能
PopFont.c模块,实现的是设置字体的功能
PopPrnt0.c模块,在未来实现打印机功能
PopPad.c
程序入口,主模块
初始化
本模块开始,定义全局变量,常量,函数引用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 #define EDITID 1 #define UNTITLED TEXT ("(untitled)" ) LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;BOOL CALLBACK AboutDlgProc (HWND, UINT, WPARAM, LPARAM) ; void PopFileInitialize (HWND) ;BOOL PopFileOpenDlg (HWND, PTSTR, PTSTR) ;BOOL PopFileSaveDlg (HWND, PTSTR, PTSTR) ;BOOL PopFileRead (HWND, PTSTR) ;BOOL PopFileWrite (HWND, PTSTR) ; HWND PopFindFindDlg (HWND) ;HWND PopFindReplaceDlg (HWND) ;BOOL PopFindFindText (HWND, int *, LPFINDREPLACE) ;BOOL PopFindReplaceText (HWND, int *, LPFINDREPLACE) ;BOOL PopFindNextText (HWND, int *) ;BOOL PopFindValidFind (void ) ; void PopFontInitialize (HWND) ;BOOL PopFontChooseFont (HWND) ;void PopFontSetFont (HWND) ;void PopFontDeinitialize (void ) ; BOOL PopPrntPrintFile (HINSTANCE, HWND, HWND, PTSTR) ; static HWND hDlgModeless ;static TCHAR szAppName[] = TEXT ("PopPad" ) ;
WinMain
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { MSG msg; HWND hwnd; HACCEL hAccel; WNDCLASS wndclass; wndclass.style = CS_HREDRAW | CS_VREDRAW; wndclass.lpfnWndProc = WndProc; wndclass.cbClsExtra = 0 ; wndclass.cbWndExtra = 0 ; wndclass.hInstance = hInstance; wndclass.hIcon = LoadIcon (hInstance, szAppName); wndclass.hCursor = LoadCursor (NULL , IDC_ARROW); wndclass.hbrBackground = (HBRUSH)GetStockObject (WHITE_BRUSH); wndclass.lpszMenuName = szAppName; wndclass.lpszClassName = szAppName; if (!RegisterClass (&wndclass)) { MessageBox (NULL , TEXT ("This program requires Windows NT!" ), szAppName, MB_ICONERROR); return 0 ; } hwnd = CreateWindow ( szAppName, NULL , WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL , NULL , hInstance, szCmdLine ); ShowWindow (hwnd, iCmdShow); UpdateWindow (hwnd); hAccel = LoadAccelerators (hInstance, szAppName); while (GetMessage (&msg, NULL , 0 , 0 )) { if (hDlgModeless == NULL || !IsDialogMessage (hDlgModeless, &msg)) { if (!TranslateAccelerator (hwnd, hAccel, &msg)) { TranslateMessage (&msg); DispatchMessage (&msg); } } } return msg.wParam; }
从主函数上看,本程序使用了键盘加速键,并且可以有查找框这种非模态窗口
WndProc主窗口过程
变量定义
1 2 3 4 5 6 7 8 static BOOL bNeedSave = FALSE ;static HINSTANCE hInst ; static HWND hwndEdit ; static int iOffset ; static TCHAR szFileName[MAX_PATH], szTitleName[MAX_PATH] ;static UINT messageFindReplace ;int iSelBeg, iSelEnd, iEnable ;LPFINDREPLACE pfr ;
下面就进入switch-case分拣消息了
switch(message)
WM_CREATE:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 case WM_CREATE: hInst = ((LPCREATESTRUCT) lParam) -> hInstance ; hwndEdit = CreateWindow (TEXT ("edit" ), NULL , WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL | WS_BORDER | ES_LEFT | ES_MULTILINE | ES_NOHIDESEL | ES_AUTOHSCROLL | ES_AUTOVSCROLL, 0 , 0 , 0 , 0 , hwnd, (HMENU) EDITID, hInst, NULL ) ; SendMessage (hwndEdit, EM_LIMITTEXT, 32000 , 0L ) ; PopFileInitialize (hwnd) ; PopFontInitialize (hwndEdit) ; messageFindReplace = RegisterWindowMessage (FINDMSGSTRING) ; DoCaption (hwnd, szTitleName) ; return 0 ;
WM_SETFOCUS
1 2 3 case WM_SETFOCUS: SetFocus (hwndEdit) ; return 0 ;
WM_SIZE
1 2 3 case WM_SIZE: MoveWindow (hwndEdit, 0 , 0 , LOWORD (lParam), HIWORD (lParam), TRUE) ; return 0 ;
switch(lParam)
case 1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 case 1 : EnableMenuItem ((HMENU) wParam, IDM_EDIT_UNDO, SendMessage (hwndEdit, EM_CANUNDO, 0 , 0L ) ? MF_ENABLED : MF_GRAYED) ; EnableMenuItem ((HMENU) wParam, IDM_EDIT_PASTE, IsClipboardFormatAvailable (CF_TEXT) ? MF_ENABLED : MF_GRAYED) ; SendMessage (hwndEdit, EM_GETSEL, (WPARAM) &iSelBeg, (LPARAM) &iSelEnd) ; iEnable = iSelBeg != iSelEnd ? MF_ENABLED : MF_GRAYED ; EnableMenuItem ((HMENU) wParam, IDM_EDIT_CUT, iEnable) ; EnableMenuItem ((HMENU) wParam, IDM_EDIT_COPY, iEnable) ; EnableMenuItem ((HMENU) wParam, IDM_EDIT_CLEAR, iEnable) ; break ;
case 2
1 2 3 4 5 6 7 8 9 10 11 12 case 2 : iEnable = hDlgModeless == NULL ? MF_ENABLED : MF_GRAYED ; EnableMenuItem ((HMENU) wParam, IDM_SEARCH_FIND, iEnable) ; EnableMenuItem ((HMENU) wParam, IDM_SEARCH_NEXT, iEnable) ; EnableMenuItem ((HMENU) wParam, IDM_SEARCH_REPLACE, iEnable) ; break ;
WM_COMMAND
控件消息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 if (lParam && LOWORD (wParam) == EDITID){ switch (HIWORD (wParam)) { case EN_UPDATE : bNeedSave = TRUE ; return 0 ; case EN_ERRSPACE : case EN_MAXTEXT : MessageBox (hwnd, TEXT ("Edit control out of space." ), szAppName, MB_OK | MB_ICONSTOP) ; return 0 ; } break ; }
菜单消息
switch(LOWORD(wParam))
IDM_FILE_NEW
1 2 3 4 5 6 7 8 9 10 11 case IDM_FILE_NEW: if (bNeedSave && IDCANCEL == AskAboutSave (hwnd, szTitleName)) return 0 ; SetWindowText (hwndEdit, TEXT ("\0" )) ; szFileName[0 ] = '\0' ; szTitleName[0 ] = '\0' ; DoCaption (hwnd, szTitleName) ; bNeedSave = FALSE ; return 0 ;
IDM_FILE_OPEN
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 case IDM_FILE_OPEN: if (bNeedSave && IDCANCEL == AskAboutSave (hwnd, szTitleName)) return 0 ; if (PopFileOpenDlg (hwnd, szFileName, szTitleName)) { if (!PopFileRead (hwndEdit, szFileName)) { OkMessage (hwnd, TEXT ("Could not read file %s!" ), szTitleName) ; szFileName[0 ] = '\0' ; szTitleName[0 ] = '\0' ; } } DoCaption (hwnd, szTitleName) ; bNeedSave = FALSE ; return 0 ;
IDM_FILE_SAVE&&IDM_FILE_SAVE_AS
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 case IDM_FILE_SAVE: if (szFileName[0 ]) { if (PopFileWrite (hwndEdit, szFileName)) { bNeedSave = FALSE ; return 1 ; } else { OkMessage (hwnd, TEXT ("Could not write file %s" ), szTitleName) ; return 0 ; } } case IDM_FILE_SAVE_AS: if (PopFileSaveDlg (hwnd, szFileName, szTitleName)) { DoCaption (hwnd, szTitleName) ; if (PopFileWrite (hwndEdit, szFileName)) { bNeedSave = FALSE ; return 1 ; } else { OkMessage (hwnd, TEXT ("Could not write file %s" ), szTitleName) ; return 0 ; } } return 0 ;
IDM_FILE_PRINT
1 2 3 4 5 6 case IDM_FILE_PRINT: if (!PopPrntPrintFile (hInst, hwnd, hwndEdit, szTitleName)) OkMessage (hwnd, TEXT ("Could not print file %s" ), szTitleName) ; return 0 ;
IDM_APP_EXIT
1 2 3 case IDM_APP_EXIT: SendMessage (hwnd, WM_CLOSE, 0 , 0 ) ; return 0 ;
撤剪拷粘清全选
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 case IDM_EDIT_UNDO: SendMessage (hwndEdit, WM_UNDO, 0 , 0 ) ; return 0 ; case IDM_EDIT_CUT: SendMessage (hwndEdit, WM_CUT, 0 , 0 ) ; return 0 ; case IDM_EDIT_COPY: SendMessage (hwndEdit, WM_COPY, 0 , 0 ) ; return 0 ; case IDM_EDIT_PASTE: SendMessage (hwndEdit, WM_PASTE, 0 , 0 ) ; return 0 ; case IDM_EDIT_CLEAR: SendMessage (hwndEdit, WM_CLEAR, 0 , 0 ) ; return 0 ; case IDM_EDIT_SELECT_ALL: SendMessage (hwndEdit, EM_SETSEL, 0 , -1 ) ; return 0 ;
查找&&查换
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 case IDM_SEARCH_FIND: SendMessage (hwndEdit, EM_GETSEL, 0 , (LPARAM) &iOffset) ; hDlgModeless = PopFindFindDlg (hwnd) ; return 0 ; case IDM_SEARCH_NEXT: SendMessage (hwndEdit, EM_GETSEL, 0 , (LPARAM) &iOffset) ; if (PopFindValidFind ()) PopFindNextText (hwndEdit, &iOffset) ; else hDlgModeless = PopFindFindDlg (hwnd) ; return 0 ; case IDM_SEARCH_REPLACE: SendMessage (hwndEdit, EM_GETSEL, 0 , (LPARAM) &iOffset) ; hDlgModeless = PopFindReplaceDlg (hwnd) ; return 0 ;
1 2 3 4 5 case IDM_FORMAT_FONT: if (PopFontChooseFont (hwnd)) PopFontSetFont (hwndEdit) ; return 0 ;
帮助&&关于
1 2 3 4 5 6 7 8 case IDM_HELP: OkMessage (hwnd, TEXT ("Help not yet implemented!" ), TEXT ("\0" )) ; return 0 ; case IDM_APP_ABOUT: DialogBox (hInst, TEXT ("AboutBox" ), hwnd, AboutDlgProc) ; return 0 ;
关闭
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 case WM_CLOSE: if (!bNeedSave || IDCANCEL != AskAboutSave (hwnd, szTitleName)) DestroyWindow (hwnd) ; return 0 ; case WM_QUERYENDSESSION : if (!bNeedSave || IDCANCEL != AskAboutSave (hwnd, szTitleName)) return 1 ; return 0 ; case WM_DESTROY: PopFontDeinitialize () ; PostQuitMessage (0 ) ; return 0 ;
messageFindReplace
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 default : if (message == messageFindReplace) { pfr = (LPFINDREPLACE) lParam ; if (pfr->Flags & FR_DIALOGTERM) hDlgModeless = NULL ; if (pfr->Flags & FR_FINDNEXT) if (!PopFindFindText (hwndEdit, &iOffset, pfr)) OkMessage (hwnd, TEXT ("Text not found!" ), TEXT ("\0" )) ; if (pfr->Flags & FR_REPLACE || pfr->Flags & FR_REPLACEALL) if (!PopFindReplaceText (hwndEdit, &iOffset, pfr)) OkMessage (hwnd, TEXT ("Text not found!" ), TEXT ("\0" )) ; if (pfr->Flags & FR_REPLACEALL) while (PopFindReplaceText (hwndEdit, &iOffset, pfr)) ; return 0 ; } break ; }
return
DefWindowProc (hwnd, message, wParam, lParam) ;
PopFile.c
整个模块包装了一个对象ofn,模块内的函数都是作用与该ofn对象的,本质上是面向对象风格的
模块变量ofn
1 static OPENFILENAME ofn ;
OPENFILENAME结构体用于保存用户关于保存文件的选择,比如文件名,文件位置等
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 #include <commdlg.h> typedef struct tagOFNA { DWORD lStructSize; HWND hwndOwner; HINSTANCE hInstance; LPCSTR lpstrFilter; LPSTR lpstrCustomFilter; DWORD nMaxCustFilter; DWORD nFilterIndex; LPSTR lpstrFile; DWORD nMaxFile; LPSTR lpstrFileTitle; DWORD nMaxFileTitle; LPCSTR lpstrInitialDir; LPCSTR lpstrTitle; DWORD Flags; WORD nFileOffset; WORD nFileExtension; LPCSTR lpstrDefExt; LPARAM lCustData; LPOFNHOOKPROC lpfnHook; LPCSTR lpTemplateName; LPEDITMENU lpEditInfo; LPCSTR lpstrPrompt; void *pvReserved; DWORD dwReserved; DWORD FlagsEx; } OPENFILENAMEA, *LPOPENFILENAMEA;
lStructSize
本结构体大小,冗余量
hwndOwner
拥有本对话框的窗口句柄
hInstance
如果Flags=OFN_ENABLETEMPLATEHANDLE ,那么hInstance时内存中的一个对话框模板
如果Flags=OFN_ENABLETEMPLATE
,那么hInstance是一个模块句柄,该模块中有lpTemplateName指明的对话框模板,
如果Flags=OFN_EXPLORER
,那么系统使用文件资源管理器Explorer风格的对话框
如果Flags=NULL,那么系统使用老式文件资源管理器Explorer风格对话框
lpstrFilter
文件名过滤器,打开文件的时候经常可以看见这个功能
image-20220831173604402
比如上传图片的时候限制后缀为.jpg或者.png格式
实际上是一个字符串
其格式是这样的:
1 2 3 4 static TCHAR szFilter[] = TEXT ("废话 \0*.<拓展名>\0" ) \ TEXT ("废话 \0*.<拓展名>\0" ) \ ...\ TEXT ("废话 \0*.<拓展名>\0\0" ) ;
废话是写给人看的
本函数在实现的时候就根据\0去分割,
两个\0之间的*.<拓展名>
不允许有空格
*
是通配符,表示所有字符串
比如
1 2 3 static TCHAR szFilter[] = TEXT ("Text Files (*.TXT)\0*.txt\0" ) \ TEXT ("ASCII Files (*.ASC)\0*.asc\0" ) \ TEXT ("All Files (*.*)\0*.*\0\0" ) ;
这就允许*.txt,*.asc
以及任意格式的文件了
既然允许任意格式,那还费力写txt和asc干啥
如果lpstrFilter=NULL,则不显示任何过滤器
模块函数
PopFileOpenDlg
实质上是封装了GetOpenFileName函数
1 2 3 4 5 6 7 8 9 10 BOOL PopFileOpenDlg (HWND hwnd, PTSTR pstrFileName, PTSTR pstrTitleName) { ofn.hwndOwner = hwnd ; ofn.lpstrFile = pstrFileName ; ofn.lpstrFileTitle = pstrTitleName ; ofn.Flags = OFN_HIDEREADONLY | OFN_CREATEPROMPT ; return GetOpenFileName (&ofn) ; }
这里Flags中有一个OFN_CREATEPROMPT,设置这个标记,当试图打开一个不存的文件的时候,会弹窗询问是否新建该文件
image-20220910214637043
否则,即如果不将这个Flag置起来,如此打开不存在的文件会被拒绝
image-20220910214723862
GetOpenFileName干了啥?
创建"打开"对话框,允许用户指定要打开的文件或文件夹.
image-20220910215708583
参数&ofn指定了获取文件名,目录放到哪里,以及Flags决定的标志
本函数在主模块PopPad.c的主窗口过程IDM_FILE_OPEN消息中唯一一次被调用,即发生点击Open菜单的时候,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 case IDM_FILE_OPEN: if (bNeedSave && IDCANCEL == AskAboutSave (hwnd, szTitleName)) return 0 ; if (PopFileOpenDlg (hwnd, szFileName, szTitleName)) { if (!PopFileRead (hwndEdit, szFileName)) { OkMessage (hwnd, TEXT ("Could not read file %s!" ), szTitleName) ; szFileName[0 ] = '\0' ; szTitleName[0 ] = '\0' ; } } DoCaption (hwnd, szTitleName) ; bNeedSave = FALSE ; return 0 ;
大概PopFileOpenDlg返回时,ofn结构体已经保存了用户的选择,而PopPad.c中调用该函数时传递的指针已经被PopFileOpenDlg设置为和ofn中的指针同指向了,因此PopPad.c中的szFileName和szTitleName旧获取到了用户希望打开的文件信息
PopFileSaveDlg
1 2 3 4 5 6 7 8 9 BOOL PopFileSaveDlg (HWND hwnd, PTSTR pstrFileName, PTSTR pstrTitleName) { ofn.hwndOwner = hwnd ; ofn.lpstrFile = pstrFileName ; ofn.lpstrFileTitle = pstrTitleName ; ofn.Flags = OFN_OVERWRITEPROMPT ; return GetSaveFileName (&ofn) ; }
OFN_OVERWRITEPROMPT标志的作用是,保存文件时如果有重名文件则弹窗让用户确认是否覆盖,否则会直接覆盖(可能会因为只读权限导致无法覆盖).
GetSaveFileName函数会创建一个保存对话框,让用户指定保存的位置
image-20220910220340540
显然该函数只会被主模块的Save和Save As菜单消息处理使用
PopFileRead
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 BOOL PopFileRead (HWND hwndEdit, PTSTR pstrFileName) { BYTE bySwap ; DWORD dwBytesRead ; HANDLE hFile ; int i, iFileLength, iUniTest ; PBYTE pBuffer, pText, pConv ; if (INVALID_HANDLE_VALUE == (hFile = CreateFile (pstrFileName, GENERIC_READ, FILE_SHARE_READ, NULL , OPEN_EXISTING, 0 , NULL ))) return FALSE ; iFileLength = GetFileSize (hFile, NULL ) ; pBuffer = malloc (iFileLength + 2 ) ; ReadFile (hFile, pBuffer, iFileLength, &dwBytesRead, NULL ) ; CloseHandle (hFile) ; pBuffer[iFileLength] = '\0' ; pBuffer[iFileLength + 1 ] = '\0' ; iUniTest = IS_TEXT_UNICODE_SIGNATURE | IS_TEXT_UNICODE_REVERSE_SIGNATURE ; if (IsTextUnicode (pBuffer, iFileLength, &iUniTest)) { pText = pBuffer + 2 ; iFileLength -= 2 ; if (iUniTest & IS_TEXT_UNICODE_REVERSE_SIGNATURE) { for (i = 0 ; i < iFileLength / 2 ; i++) { bySwap = ((BYTE *) pText) [2 * i] ; ((BYTE *) pText) [2 * i] = ((BYTE *) pText) [2 * i + 1 ] ; ((BYTE *) pText) [2 * i + 1 ] = bySwap ; } } pConv = malloc (iFileLength + 2 ); #ifndef UNICODE WideCharToMultiByte (CP_ACP, 0 , (PWSTR) pText, -1 , pConv, iFileLength + 2 , NULL , NULL ) ; #else lstrcpy ((PTSTR) pConv, (PTSTR) pText) ; #endif } else { pText = pBuffer ; pConv = malloc (2 * iFileLength + 2 ) ; #ifdef UNICODE MultiByteToWideChar (CP_ACP, 0 , pText, -1 , (PTSTR) pConv, iFileLength + 1 ) ; #else lstrcpy ((PTSTR) pConv, (PTSTR) pText) ; #endif } SetWindowText (hwndEdit, (PTSTR) pConv) ; free (pBuffer) ; free (pConv) ; return TRUE ; }
这里调用了很多API
CreateFile
1 2 3 4 5 6 7 8 9 HANDLE CreateFileA ( [in] LPCSTR lpFileName, [in] DWORD dwDesiredAccess, [in] DWORD dwShareMode, [in, optional] LPSECURITY_ATTRIBUTES lpSecurityAttributes, [in] DWORD dwCreationDisposition, [in] DWORD dwFlagsAndAttributes, [in, optional] HANDLE hTemplateFile ) ;
打开或者创建一个文件,返回其文件句柄,lpFileName是希望打开或者创建的文件,dwDesiredAccess是希望的访问权限,包括读写执行.后面使用该文件的时候不能超过CreateFile时限定的权限
dwShareMode一旦置起来,则该文件只允许被当前程序打开,直到本程序释放文件句柄后,才允许文件被下一个程序访问
lpSecurityAttributes安全属性,包括文件句柄的继承性以及访问权限,在<<windows核心编程>>
中有详细解释
dwCreationDisposition,指定打开文件的模式,包括文件存在或者不存在时的打开规则
dwFlagsAndAttributes文件属性标志,即创建只读文件或者隐藏文件等
hTemplateFile模板文件句柄,用另一个文件的权限等信息覆盖本文件的设置
在popfile.c中该函数是这样用的:
1 2 3 4 5 6 7 8 9 hFile = CreateFile ( pstrFileName, GENERIC_READ, FILE_SHARE_READ, NULL , OPEN_EXISTING, 0 , NULL );
GetFileSize
1 2 3 4 DWORD GetFileSize ( [in] HANDLE hFile, [out, optional] LPDWORD lpFileSizeHigh ) ;
lpFileSizeHigh返回文件长度的高二字节(文件总大小要占用四个字节,lpFileSizeHigh只返回高2字节,虽然不知道这样返回有啥意义)
调用成功则返回准确的文件大小,一个双字
1 iFileLength = GetFileSize (hFile, NULL ) ;
iFileLength获得了文件大小
ReadFile
1 2 3 4 5 6 7 BOOL ReadFile ( [in] HANDLE hFile, [out] LPVOID lpBuffer, [in] DWORD nNumberOfBytesToRead, [out, optional] LPDWORD lpNumberOfBytesRead, [in, out, optional] LPOVERLAPPED lpOverlapped ) ;
读取文件内容,从hFile句柄指定的文件读取至多nNumberOfBytesToRead个字节,放到lpBuffer指定的缓冲区,实际读取的字节数还要取决于hFile文件的
大小,lpNumberOfBytesRead存放实际读取到的字节数
lpOverlapped和异步IO有关,现在不管它,默认NULL
1 2 ReadFile (hFile, pBuffer, iFileLength, &dwBytesRead, NULL);
从hFile读取至多iFileLength个字节放到pBuffer中,实际读取了多少个放到dwBytesRead
PopFileWrite
逻辑和PopFileRead基本相同,读改成写就完了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 BOOL PopFileWrite (HWND hwndEdit, PTSTR pstrFileName) { DWORD dwBytesWritten ; HANDLE hFile ; int iLength ; PTSTR pstrBuffer ; WORD wByteOrderMark = 0xFEFF ; if (INVALID_HANDLE_VALUE == (hFile = CreateFile (pstrFileName, GENERIC_WRITE, 0 , NULL , CREATE_ALWAYS, 0 , NULL ))) return FALSE ; iLength = GetWindowTextLength (hwndEdit) ; pstrBuffer = (PTSTR) malloc ((iLength + 1 ) * sizeof (TCHAR)) ; if (!pstrBuffer) { CloseHandle (hFile) ; return FALSE ; } #ifdef UNICODE WriteFile (hFile, &wByteOrderMark, 2 , &dwBytesWritten, NULL ) ; #endif GetWindowText (hwndEdit, pstrBuffer, iLength + 1 ) ; WriteFile (hFile, pstrBuffer, iLength * sizeof (TCHAR), &dwBytesWritten, NULL ) ; if ((iLength * sizeof (TCHAR)) != (int ) dwBytesWritten) { CloseHandle (hFile) ; free (pstrBuffer) ; return FALSE ; } CloseHandle (hFile) ; free (pstrBuffer) ; return TRUE ; }
PopFind.c
本模块实现了对poppad的查找替换的支持
模块全局/静态变量
1 2 3 4 5 6 7 #define MAX_STRING_LEN 256 static TCHAR szFindText [MAX_STRING_LEN] ;static TCHAR szReplText [MAX_STRING_LEN] ;... static FINDREPLACE fr ;
关于FINDREPLACE
1 2 3 4 5 6 7 8 9 10 11 12 13 typedef struct tagFINDREPLACEW { DWORD lStructSize; HWND hwndOwner; HINSTANCE hInstance; DWORD Flags; LPWSTR lpstrFindWhat; LPWSTR lpstrReplaceWith; WORD wFindWhatLen; WORD wReplaceWithLen; LPARAM lCustData; LPFRHOOKPROC lpfnHook; LPCWSTR lpTemplateName; } FINDREPLACEW, *LPFINDREPLACEW;
模块函数
PopFindFindDlg
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 HWND PopFindFindDlg (HWND hwnd) { static FINDREPLACE fr ; fr.lStructSize = sizeof (FINDREPLACE) ; fr.hwndOwner = hwnd ; fr.hInstance = NULL ; fr.Flags = FR_HIDEUPDOWN | FR_HIDEMATCHCASE | FR_HIDEWHOLEWORD ; fr.lpstrFindWhat = szFindText ; fr.lpstrReplaceWith = NULL ; fr.wFindWhatLen = MAX_STRING_LEN ; fr.wReplaceWithLen = 0 ; fr.lCustData = 0 ; fr.lpfnHook = NULL ; fr.lpTemplateName = NULL ; return FindText (&fr) ; }
这里fr.Flags= FR_HIDEUPDOWN | FR_HIDEMATCHCASE | FR_HIDEWHOLEWORD ;
意思是:
隐藏查找方向,隐藏大小写匹配,隐藏全词匹配
如果这些都不隐藏,则查找对话框是这样的
image-20220910231639276
都隐藏之后的查找框
image-20220910231722908
FindText 只需要fr结构体作为参数,如果查找成功则返回查找框的句柄
PopFindReplaceDlg
逻辑和PopFindFindDlg相似
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 HWND PopFindReplaceDlg (HWND hwnd) { static FINDREPLACE fr ; fr.lStructSize = sizeof (FINDREPLACE) ; fr.hwndOwner = hwnd ; fr.hInstance = NULL ; fr.Flags = FR_HIDEUPDOWN | FR_HIDEMATCHCASE | FR_HIDEWHOLEWORD ; fr.lpstrFindWhat = szFindText ; fr.lpstrReplaceWith = szReplText ; fr.wFindWhatLen = MAX_STRING_LEN ; fr.wReplaceWithLen = MAX_STRING_LEN ; fr.lCustData = 0 ; fr.lpfnHook = NULL ; fr.lpTemplateName = NULL ; return ReplaceText (&fr) ; }
PopFindFindText
查找逻辑函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 BOOL PopFindFindText (HWND hwndEdit, int * piSearchOffset, LPFINDREPLACE pfr) { int iLength, iPos ; PTSTR pstrDoc, pstrPos ; iLength = GetWindowTextLength (hwndEdit) ; if (NULL == (pstrDoc = (PTSTR) malloc ((iLength + 1 ) * sizeof (TCHAR)))) return FALSE ; GetWindowText (hwndEdit, pstrDoc, iLength + 1 ) ; pstrPos = _tcsstr (pstrDoc + * piSearchOffset, pfr->lpstrFindWhat) ; free (pstrDoc) ; if (pstrPos == NULL ) return FALSE ; iPos = pstrPos - pstrDoc ; * piSearchOffset = iPos + lstrlen (pfr->lpstrFindWhat) ; SendMessage (hwndEdit, EM_SETSEL, iPos, * piSearchOffset) ; SendMessage (hwndEdit, EM_SCROLLCARET, 0 , 0 ) ; return TRUE ; }
到此需要再看一下主模块中对于调用查找框和使用查找逻辑函数,是怎么配合的
在主窗口过程的最后,default块中,处理查找替换消息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 case IDM_SEARCH_FIND: SendMessage (hwndEdit, EM_GETSEL, 0 , (LPARAM) &iOffset) ; hDlgModeless = PopFindFindDlg (hwnd) ; return 0 ; .... if (message == messageFindReplace) { pfr = (LPFINDREPLACE) lParam ; if (pfr->Flags & FR_DIALOGTERM) hDlgModeless = NULL ; if (pfr->Flags & FR_FINDNEXT) if (!PopFindFindText (hwndEdit, &iOffset, pfr)) OkMessage (hwnd, TEXT ("Text not found!" ), TEXT ("\0" )) ; if (pfr->Flags & FR_REPLACE || pfr->Flags & FR_REPLACEALL) if (!PopFindReplaceText (hwndEdit, &iOffset, pfr)) OkMessage (hwnd, TEXT ("Text not found!" ), TEXT ("\0" )) ; if (pfr->Flags & FR_REPLACEALL) while (PopFindReplaceText (hwndEdit, &iOffset, pfr)) ; return 0 ; }
也就是说,用户通过菜单或者ctrl+f快捷键,让主窗口过程处理IDM_SEARCH_FIND,创建了模态查找框,由于已经注册过messageFindReplace和查找框关联,主窗口会接着收到该消息,查找框用来获取用户希望查找的字符串,数据存放到popfind模块中,不归主模块poppad.c管理
然后messageFindReplace中调用PopFindFindText,再次将控制交给popfind模块,该模块的函数可以访问该模块的静态变量
PopFindFindText就在该模块中发挥作用了
PopFindNextText
查找下一个
1 2 3 4 5 6 7 8 BOOL PopFindNextText (HWND hwndEdit, int * piSearchOffset) { FINDREPLACE fr ; fr.lpstrFindWhat = szFindText ; return PopFindFindText (hwndEdit, piSearchOffset, &fr) ; }
只需要更新一下起始查找位置然后套用PopFindFindText就可以了
PopFindReplaceText
查找并替换
1 2 3 4 5 6 7 8 9 10 11 12 13 BOOL PopFindReplaceText (HWND hwndEdit, int * piSearchOffset, LPFINDREPLACE pfr) { if (!PopFindFindText (hwndEdit, piSearchOffset, pfr)) return FALSE ; SendMessage (hwndEdit, EM_REPLACESEL, 0 , (LPARAM) pfr->lpstrReplaceWith) ; return TRUE ; }
首先调用PopFindFindText找到目标字符串,然后给编辑控件发EM_REPLACESEL消息,提醒编辑控件用pfr->lpstrReplaceWith替换原来的字符串
而pfr->lpstrReplaceWith这个值是之前调用模态查替对话框获得的输入
替换编辑控件中哪个位置的字符串呢?
这个不需要操心,因为查替首先要查找,查找到了就会高亮,需要替换的就是高亮的部分
PopFindValidFind
是否是有效查找,待查缓冲区非空才有效
1 2 3 4 BOOL PopFindValidFind (void ) { return * szFindText != '\0' ; }