Windows API 学习~
用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 code
LPVOID 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 up
lpDisplayBuf = (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 4000
HWND 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 window
UINT uMsg, // message identifier
WPARAM wParam, // first message parameter
LPARAM 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;
else
tvins.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 width
50, // Button height
m_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 width
50, // Button height
m_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 width
50, // Button height
m_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);
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)