用api 创建一个Windows 窗口
windows窗口创建过程
Windows API 索引 - Win32 apps | Microsoft Learn
创建项目
配置属性,系统里面子系统选择窗口
-
注册窗口类: 首先需要调用RegisterClassEx 函数来注册一个窗口类,该函数会向操作系统注册一个新的窗口类, 并返回一个唯一的类标识符, 以供后续创建窗口时使用。在注册窗口类时需要指定窗口的基本属性, 如窗口过程、背景颜色、光标等信息。
-
创建窗口: 调用CreateWindowEx函数来创建一 个窗口实例。在创建窗口时需要指定窗口类名称、窗口名称、窗口样式等参数,同时可以设置窗口位置、大小、字体、菜单等属性。
-
显示窗口: 调用ShowWindow函数将窗口显示到屏蒂上。该函数可以设置窗口的可见性、最小化、最大化状态等。
-
进入消息循环: 通过调用GetMessage或者PeekMessage函数来读取并处理窗口消息队列中的消息。在消息循环中,窗口过程会接收和处理各种与窗口相关的事件,如鼠标点击、键盘输入、窗口移动、大小调整等。
创建一个WinMain.cpp,winMain函数按F1可以查看微软的官方文档,以下代码中的函数也可以F1查看官方文档
语法
int __clrcall WinMain([in] HINSTANCE hInstance,[in, optional] HINSTANCE hPrevInstance,[in] LPSTR lpCmdLine,[in] int nShowCmd
);
参数
[in] hInstance
类型:HINSTANCE
应用程序的当前实例的句柄。
[in, optional] hPrevInstance
类型:HINSTANCE
应用程序的上一个实例的句柄。 此参数始终 NULL。 如果需要检测另一个实例是否已存在,请使用 CreateMutex 函数创建唯一命名的互斥体。 即使互斥体已存在,CreateMutex 也会成功,但该函数将返回 ERROR_ALREADY_EXISTS。 这表示应用程序的另一个实例存在,因为它首先创建了互斥体。 但是,恶意用户可以在执行此操作之前创建此互斥体,并阻止应用程序启动。 为防止这种情况,请创建一个随机命名的互斥体并存储该名称,以便它只能由授权用户获取。 或者,可以将文件用于此目的。 若要将应用程序限制为每个用户的一个实例,请在用户的配置文件目录中创建锁定的文件。
[in] lpCmdLine
类型:LPSTR
应用程序的命令行,不包括程序名称。 若要检索整个命令行,请使用 GetCommandLine 函数。
[in] nShowCmd
类型:int
控制窗口的显示方式。 此参数可以是 ShowWindow 函数的 nCmdShow 参数中指定的任何值。
关于树视图控件
树视图控件是一种显示分层项目列表的窗口,例如文档中的标题、索引中的条目或磁盘上的文件和目录。 每个项包含一个标签和一个可选位图图像,且每个项可以有一个与之关联的子项列表。 通过单击某个项目,用户可以展开或折叠相关的子项目列表。
下图显示了一个简单的树形视图控件,其中包含一个根节点、一个展开节点和一个折叠节点。 该控件对选定项目使用一个位图,而对其他项目使用另一个位图。
创建树视图控件后,可以通过向控件发送消息来添加、删除、排列或以其他方式来操作项目。 每条消息都有一个或多个相应的宏,可以使用这些宏来代替显式发送消息。
显示卷路径
以下 C++ 示例演示如何显示每个卷和设备的所有路径。 对于系统中的每个卷,本示例查找卷,获取设备名称,获取该卷的所有路径,并显示路径。
#include <windows.h>
#include <stdio.h>void DisplayVolumePaths(__in PWCHAR VolumeName)
{DWORD CharCount = MAX_PATH + 1;PWCHAR Names = NULL;PWCHAR NameIdx = NULL;BOOL Success = FALSE;for (;;) {//// Allocate a buffer to hold the paths.Names = (PWCHAR) new BYTE [CharCount * sizeof(WCHAR)];if ( !Names ) {//// If memory can't be allocated, return.return;}//// Obtain all of the paths// for this volume.Success = GetVolumePathNamesForVolumeNameW(VolumeName, Names, CharCount, &CharCount);if ( Success ) {break;}if ( GetLastError() != ERROR_MORE_DATA ) {break;}//// Try again with the// new suggested size.delete [] Names;Names = NULL;}if ( Success ){//// Display the various paths.for ( NameIdx = Names; NameIdx[0] != L'\0'; NameIdx += wcslen(NameIdx) + 1 ) {wprintf(L" %s", NameIdx);}wprintf(L"\n");}if ( Names != NULL ) {delete [] Names;Names = NULL;}return;
}void __cdecl wmain(void)
{DWORD CharCount = 0;WCHAR DeviceName[MAX_PATH] = L"";DWORD Error = ERROR_SUCCESS;HANDLE FindHandle = INVALID_HANDLE_VALUE;BOOL Found = FALSE;size_t Index = 0;BOOL Success = FALSE;WCHAR VolumeName[MAX_PATH] = L"";//// Enumerate all volumes in the system.FindHandle = FindFirstVolumeW(VolumeName, ARRAYSIZE(VolumeName));if (FindHandle == INVALID_HANDLE_VALUE){Error = GetLastError();wprintf(L"FindFirstVolumeW failed with error code %d\n", Error);return;}for (;;){//// Skip the \\?\ prefix and remove the trailing backslash.Index = wcslen(VolumeName) - 1;if (VolumeName[0] != L'\\' ||VolumeName[1] != L'\\' ||VolumeName[2] != L'?' ||VolumeName[3] != L'\\' ||VolumeName[Index] != L'\\') {Error = ERROR_BAD_PATHNAME;wprintf(L"FindFirstVolumeW/FindNextVolumeW returned a bad path: %s\n", VolumeName);break;}//// QueryDosDeviceW does not allow a trailing backslash,// so temporarily remove it.VolumeName[Index] = L'\0';CharCount = QueryDosDeviceW(&VolumeName[4], DeviceName, ARRAYSIZE(DeviceName)); VolumeName[Index] = L'\\';if ( CharCount == 0 ) {Error = GetLastError();wprintf(L"QueryDosDeviceW failed with error code %d\n", Error);break;}wprintf(L"\nFound a device:\n %s", DeviceName);wprintf(L"\nVolume name: %s", VolumeName);wprintf(L"\nPaths:");DisplayVolumePaths(VolumeName);//// Move on to the next volume.Success = FindNextVolumeW(FindHandle, VolumeName, ARRAYSIZE(VolumeName));if ( !Success ) {Error = GetLastError();if (Error != ERROR_NO_MORE_FILES) {wprintf(L"FindNextVolumeW failed with error code %d\n", Error);break;}//// Finished iterating// through all the volumes.Error = ERROR_SUCCESS;break;}}FindVolumeClose(FindHandle);FindHandle = INVALID_HANDLE_VALUE;return;
}
下面是运行应用程序的示例输出。 对于每个卷,输出包括卷设备路径、卷 GUID 路径和驱动器号。
Found a device:\Device\HarddiskVolume2
Volume name: \\?\Volume{4c1b02c1-d990-11dc-99ae-806e6f6e6963}\
Paths: C:\Found a device:\Device\CdRom0
Volume name: \\?\Volume{4c1b02c4-d990-11dc-99ae-806e6f6e6963}\
Paths: D:\
列出目录中的文件
以下示例调用 FindFirstFile、 FindNextFile 和 FindClose 列出指定目录中的文件。
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include <strsafe.h>
#pragma comment(lib, "User32.lib")void DisplayErrorBox(LPTSTR lpszFunction);int _tmain(int argc, TCHAR *argv[])
{WIN32_FIND_DATA ffd;LARGE_INTEGER filesize;TCHAR szDir[MAX_PATH];size_t length_of_arg;HANDLE hFind = INVALID_HANDLE_VALUE;DWORD dwError=0;// If the directory is not specified as a command-line argument,// print usage.if(argc != 2){_tprintf(TEXT("\nUsage: %s <directory name>\n"), argv[0]);return (-1);}// Check that the input path plus 3 is not longer than MAX_PATH.// Three characters are for the "\*" plus NULL appended below.StringCchLength(argv[1], MAX_PATH, &length_of_arg);if (length_of_arg > (MAX_PATH - 3)){_tprintf(TEXT("\nDirectory path is too long.\n"));return (-1);}_tprintf(TEXT("\nTarget directory is %s\n\n"), argv[1]);// Prepare string for use with FindFile functions. First, copy the// string to a buffer, then append '\*' to the directory name.StringCchCopy(szDir, MAX_PATH, argv[1]);StringCchCat(szDir, MAX_PATH, TEXT("\\*"));// Find the first file in the directory.hFind = FindFirstFile(szDir, &ffd);if (INVALID_HANDLE_VALUE == hFind) {DisplayErrorBox(TEXT("FindFirstFile"));return dwError;} // List all the files in the directory with some info about them.do{if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY){_tprintf(TEXT(" %s <DIR>\n"), ffd.cFileName);}else{filesize.LowPart = ffd.nFileSizeLow;filesize.HighPart = ffd.nFileSizeHigh;_tprintf(TEXT(" %s %ld bytes\n"), ffd.cFileName, filesize.QuadPart);}}while (FindNextFile(hFind, &ffd) != 0);dwError = GetLastError();if (dwError != ERROR_NO_MORE_FILES) {DisplayErrorBox(TEXT("FindFirstFile"));}FindClose(hFind);return dwError;
}void DisplayErrorBox(LPTSTR lpszFunction)
{ // Retrieve the system error message for the last-error codeLPVOID lpMsgBuf;LPVOID lpDisplayBuf;DWORD dw = GetLastError(); FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |FORMAT_MESSAGE_IGNORE_INSERTS,NULL,dw,MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),(LPTSTR) &lpMsgBuf,0, NULL );// Display the error message and clean uplpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT, (lstrlen((LPCTSTR)lpMsgBuf)+lstrlen((LPCTSTR)lpszFunction)+40)*sizeof(TCHAR)); StringCchPrintf((LPTSTR)lpDisplayBuf, LocalSize(lpDisplayBuf) / sizeof(TCHAR),TEXT("%s failed with error %d: %s"), lpszFunction, dw, lpMsgBuf); MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK); LocalFree(lpMsgBuf);LocalFree(lpDisplayBuf);
}
用树视图列出文件夹代码示例:
#include<Windows.h>
#include<windowsx.h>
#include<commctrl.h>
#include <vector>
#include <string>#pragma comment(lib, "Comctl32.lib");
#define BTN_BTN_1 1000
#define BTN_CHECHEBOX_1 2000
#define BTN_RADIO_1 3000
#define ID_EDITCHILD 4000HWND hwndTV{};
HTREEITEM AddItemToTree(HWND hwndTV, LPTSTR lpszItem, int nLevel);/// <summary>
/// path 路径
/// </summary>
std::vector<std::wstring> GetFileList(std::wstring path);void InsrtTree(HTREEITEM hParent, HTREEITEM hInsertAfter,std::wstring name)
{TVINSERTSTRUCT trStruct;trStruct.hParent = hParent;trStruct.hInsertAfter = hInsertAfter;TVITEM tItem;WCHAR namebuf[MAX_PATH]{};tItem.mask = TVIF_TEXT;//tItem.hItem = hParent;tItem.pszText = (LPWSTR)name.c_str();tItem.cchTextMax = name.size() + 1;trStruct.item = tItem;TreeView_InsertItem(hwndTV, &trStruct);
}LRESULT CALLBACK MainWndProc(HWND hwnd, // handle to windowUINT uMsg, // message identifierWPARAM wParam, // first message parameterLPARAM lParam) // second message parameter
{static HWND hwndEdit;TCHAR lpszLatin[] = L"Lorem ipsum dolor sit amet, consectetur ";switch (uMsg){case WM_CREATE://hwndEdit = CreateWindowEx(// 0, L"EDIT", // predefined class // NULL, // no window title // WS_CHILD | WS_VISIBLE | WS_VSCROLL |// ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL| WS_BORDER,// 150, 150, 200, 200, // set size in WM_SIZE message // hwnd, // parent window // (HMENU)ID_EDITCHILD, // edit control ID // (HINSTANCE)GetWindowLongPtr(hwnd, GWLP_HINSTANCE),// NULL); // pointer not needed //// Add text to the window. //SendMessage(hwndEdit, WM_SETTEXT, 0, (LPARAM)lpszLatin);return 0;case WM_COMMAND:if (wParam == BTN_BTN_1){TCHAR lpszLatinl[500]{};Edit_GetText(hwndEdit, lpszLatinl, 500);MessageBox(0, lpszLatinl ,L"提示", MB_OK);}else if (wParam == BTN_CHECHEBOX_1){MessageBox(0, L"CHECKBOX 被按下", L"提示", MB_OK);}else if (wParam == BTN_RADIO_1){MessageBox(0, L"RADIO 被按下", L"提示", MB_OK);}break;case WM_NOTIFY:switch (((LPNMHDR)lParam)->code){case NM_RCLICK:MessageBox(0, L"WC_TREEVIEW 被右键", L"提示", MB_OK);break;case NM_DBLCLK:HTREEITEM ht = TreeView_GetSelection(hwndTV);WCHAR namebuf[MAX_PATH]{};TVITEM tItem;tItem.mask = TVIF_TEXT;tItem.pszText = namebuf;tItem.cchTextMax = MAX_PATH;HTREEITEM htup = ht;std::wstring fpath;while (true){htup = TreeView_GetParent(hwndTV, htup);if (htup == NULL){break;}tItem.hItem = htup;TreeView_GetItem(hwndTV, &tItem);fpath = L"\\" + fpath;fpath = tItem.pszText + fpath + L"\\";}tItem.hItem = ht;TreeView_GetItem(hwndTV,&tItem );std::wstring path = tItem.pszText;std::vector<std::wstring>flist = GetFileList(fpath + path);for (auto& var : flist){InsrtTree(ht, 0, var);}//MessageBox(0,tItem.pszText , L"提示", MB_OK);break;}break;default:return DefWindowProc(hwnd, uMsg, wParam, lParam);}return 0;
}HTREEITEM AddItemToTree(HWND hwndTV, LPTSTR lpszItem, int nLevel)
{TVITEM tvi;TVINSERTSTRUCT tvins;static HTREEITEM hPrev = (HTREEITEM)TVI_FIRST;static HTREEITEM hPrevRootItem = NULL;static HTREEITEM hPrevLev2Item = NULL;HTREEITEM hti;tvi.mask = TVIF_TEXT | TVIF_IMAGE| TVIF_SELECTEDIMAGE | TVIF_PARAM;// Set the text of the item. tvi.pszText = lpszItem;tvi.cchTextMax = sizeof(tvi.pszText) / sizeof(tvi.pszText[0]);// Assume the item is not a parent item, so give it a // document image. //tvi.iImage = g_nDocument;//tvi.iSelectedImage = g_nDocument;// Save the heading level in the item's application-defined // data area. tvi.lParam = (LPARAM)nLevel;tvins.item = tvi;tvins.hInsertAfter = hPrev;// Set the parent item based on the specified level. if (nLevel == 1)tvins.hParent = TVI_ROOT;else if (nLevel == 2)tvins.hParent = hPrevRootItem;elsetvins.hParent = hPrevLev2Item;// Add the item to the tree-view control. hPrev = (HTREEITEM)SendMessage(hwndTV, TVM_INSERTITEM,0, (LPARAM)(LPTVINSERTSTRUCT)&tvins);if (hPrev == NULL)return NULL;// Save the handle to the item. if (nLevel == 1)hPrevRootItem = hPrev;else if (nLevel == 2)hPrevLev2Item = hPrev;// The new item is a child item. Give the parent item a // closed folder bitmap to indicate it now has child items. if (nLevel > 1){hti = TreeView_GetParent(hwndTV, hPrev);tvi.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE;tvi.hItem = hti;//tvi.iImage = g_nClosed;//tvi.iSelectedImage = g_nClosed;TreeView_SetItem(hwndTV, &tvi);}return hPrev;
}//获取文件列表
std::vector<std::wstring> GetVolumeList()
{std::vector<std::wstring> voList;WCHAR DeviceName[MAX_PATH]{};HANDLE findHandle = FindFirstVolumeW(DeviceName, MAX_PATH);if (findHandle == INVALID_HANDLE_VALUE){return voList;}do{WCHAR pathName[MAX_PATH]{};DWORD len;GetVolumePathNamesForVolumeName(DeviceName, pathName, MAX_PATH, &len);if (len>1){voList.push_back(pathName);}} while (FindNextVolume(findHandle, DeviceName, MAX_PATH));FindVolumeClose(findHandle);return voList;
}/// <summary>
/// path 路径
/// </summary>
std::vector<std::wstring> GetFileList(std::wstring path)
{std::vector<std::wstring> voList;path += L"\\*";WIN32_FIND_DATAW ffd{};HANDLE fileHandle = FindFirstFile(path.c_str(),&ffd);if (INVALID_HANDLE_VALUE == fileHandle){return voList;}do{voList.push_back(ffd.cFileName);} while (FindNextFile(fileHandle,&ffd));FindClose(fileHandle);return voList;
}int WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nShowCmd)
{//std::vector<std::wstring> fList = GetFileList(L"c:\\");//for (auto& var : fList)//{// MessageBox(NULL, var.c_str(), L"", MB_OK);//}WNDCLASSEX wndcls{};wndcls.cbSize = sizeof(WNDCLASSEX);wndcls.style = CS_SAVEBITS | CS_DROPSHADOW;wndcls.lpfnWndProc = MainWndProc;wndcls.hInstance = hInstance;wndcls.lpszClassName = L"chao";RegisterClassEx(&wndcls);HWND m_hwnd = CreateWindowEx(0,wndcls.lpszClassName,L"Chao",0,100, 100, 600, 800,NULL, NULL,hInstance,NULL);
/*HWND hwndButton = CreateWindow(L"BUTTON", // Predefined class; Unicode assumed L"OK", // Button text WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON, // Styles 10, // x position 10, // y position 100, // Button width50, // Button heightm_hwnd, // Parent window(HMENU)BTN_BTN_1, // No menu.(HINSTANCE)GetWindowLongPtr(m_hwnd, GWLP_HINSTANCE),NULL); // Pointer not needed.HWND hwndCheckbox = CreateWindow(L"BUTTON", // Predefined class; Unicode assumed L"CHECKBOX", // Button text WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_AUTOCHECKBOX, // Styles 150, // x position 10, // y position 100, // Button width50, // Button heightm_hwnd, // Parent window(HMENU)BTN_CHECHEBOX_1, // No menu.(HINSTANCE)GetWindowLongPtr(m_hwnd, GWLP_HINSTANCE),NULL); // Pointer not needed.HWND hwndRADIO = CreateWindow(L"BUTTON", // Predefined class; Unicode assumed L"RADIO", // Button text WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_AUTORADIOBUTTON, // Styles 10, // x position 150, // y position 100, // Button width50, // Button heightm_hwnd, // Parent window(HMENU)BTN_RADIO_1, // No menu.(HINSTANCE)GetWindowLongPtr(m_hwnd, GWLP_HINSTANCE),NULL); // Pointer not needed.
*/InitCommonControls();HWND hwndTV = CreateWindowEx(0,WC_TREEVIEW,TEXT("Tree View"),WS_VISIBLE | WS_CHILD | WS_BORDER | TVS_HASLINES | TVS_HASBUTTONS | TVS_LINESATROOT | TVS_INFOTIP,10,10,300,500,m_hwnd,0,hInstance,NULL);std::vector<std::wstring> voList = GetVolumeList();for (auto& var : voList){AddItemToTree(hwndTV, (LPTSTR)var.c_str(), 1);/*MessageBox(NULL, var.c_str(), L"", MB_OK);*/}//AddItemToTree(hwndTV, (LPTSTR)L"ROOT", 1);//AddItemToTree(hwndTV, (LPTSTR)L"ROOT-2", 2);//AddItemToTree(hwndTV, (LPTSTR)L"ROOT-3", 3);//AddItemToTree(hwndTV, (LPTSTR)L"ROOT-2", 2);//AddItemToTree(hwndTV, (LPTSTR)L"ROOT", 1);//AddItemToTree(hwndTV, (LPTSTR)L"ROOT-2", 2);//AddItemToTree(hwndTV, (LPTSTR)L"ROOT-3", 3);//AddItemToTree(hwndTV, (LPTSTR)L"ROOT-2", 2);//AddItemToTree(hwndTV, (LPTSTR)L"ROOT-3", 3); //AddItemToTree(hwndTV, (LPTSTR)L"ROOT-3", 3);ShowWindow(m_hwnd,SW_NORMAL);BOOL bRet;MSG msg;while (GetMessage(&msg, m_hwnd, 0, 0)){TranslateMessage(&msg);DispatchMessage(&msg);}}
学习参考:
使用Windows API将文件显示到树形目录下 【C++ Windows API】_哔哩哔哩_bilibili