C++ WinAPI. Неверное отображение текста в списке
У меня есть задача создать программу, которая отображает список дескрипторов всех окон в системе. Я получаю этот вывод:
Может быть, это неправильная кодировка?
Вот мой код:
#include <windows.h>
ATOM RegMyWindowClass(HINSTANCE, LPCTSTR);
HWND hListBox;
HINSTANCE hin;
BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam)
{
WCHAR str[255];
if (GetWindowTextW(hwnd, str, 255)) {
if (IsWindowVisible(hwnd) && (!GetWindow(hwnd, GW_OWNER)))
SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)str);
}
return 1;
}
LRESULT CALLBACK WndProc(
HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_LBUTTONUP:
MessageBox(hWnd, TEXT("Вы кликнули!"), TEXT("событие"), 0);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_CREATE:
hListBox = CreateWindow("LISTBOX", "", WS_CHILD | WS_VISIBLE | LBS_NOTIFY | WS_VSCROLL,
0, 0, 400, 400, hWnd, (HMENU)1111, hin, NULL);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
LPCTSTR lpzClass = TEXT("My Window Class!");
if (!RegMyWindowClass(hInstance, lpzClass))
return 1;
RECT screen_rect;
GetWindowRect(GetDesktopWindow(), &screen_rect);
int x = screen_rect.right / 2 - 200;
int y = screen_rect.bottom / 2 - 200;
HWND hWnd = CreateWindow(lpzClass, TEXT("Window"),
WS_OVERLAPPEDWINDOW | WS_VISIBLE, x, y, 400, 400, NULL, NULL,
hInstance, NULL);
ShowWindow(hWnd, SW_SHOW);
EnumWindows(&EnumWindowsProc, 0);
if (!hWnd) return 2;
MSG msg = { 0 };
int iGetOk = 0;
while ((iGetOk = GetMessage(&msg, NULL, 0, 0)) != 0)
{
if (iGetOk == -1) return 3;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
ATOM RegMyWindowClass(HINSTANCE hInst, LPCTSTR lpzClassName)
{
WNDCLASS wcWindowClass = { 0 };
wcWindowClass.lpfnWndProc = (WNDPROC)WndProc;
wcWindowClass.style = CS_HREDRAW | CS_VREDRAW;
wcWindowClass.hInstance = hInst;
wcWindowClass.lpszClassName = lpzClassName;
wcWindowClass.hCursor = LoadCursor(NULL, IDC_ARROW);
wcWindowClass.hbrBackground = (HBRUSH)COLOR_APPWORKSPACE;
return RegisterClass(&wcWindowClass);
}
Любые идеи, как это исправить?
1 ответ
Проблема в том, что вы компилируете свой проект для ANSI, где TCHAR
это псевдоним для CHAR
и, таким образом, создаем ListBox на основе ANSI, но вы отправляете в него строки Unicode. Вот почему вы видите мусор на выходе. Вам необходимо отправить строки ANSI в ListBox, например:
BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam)
{
if (IsWindowVisible(hwnd) && (!GetWindow(hwnd, GW_OWNER)))
CHAR str[255] = {};
if (GetWindowTextA(hwnd, str, 255)) {
SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)str);
}
}
return TRUE;
}
В качестве альтернативы:
BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam)
{
if (IsWindowVisible(hwnd) && (!GetWindow(hwnd, GW_OWNER))) {
CHAR str[255] = {};
if (IsWindowUnicode(hwnd)) {
WCHAR wstr[255] = {};
int len = GetWindowTextW(hwnd, wstr, 255);
if (len) {
len = WideCharToMultiByte(CP_ACP, 0, wstr, len+1, str, 255, NULL, NULL);
}
if (!len) {
return TRUE;
}
}
else if (!GetWindowTextA(hwnd, str, 255)) {
return TRUE;
}
SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)str);
}
return TRUE;
}
При этом вы смешиваете ANSI, Unicode и TCHAR
API-интерфейсы. Вам нужно выбрать один стиль API и придерживаться его для всего, не смешивайте их (если только вы не АБСОЛЮТНО должны это делать).
Поскольку большая часть кода, который вы показали, уже использует TCHAR
тогда вы можете использовать TCHAR
для всего (хотя, вы действительно не должны, так как TCHAR
предназначен для обратной совместимости с Win9x/ME, который больше никто не использует, и предназначен только для того, чтобы помочь людям перенести их код в Unicode. Современный код не должен использовать TCHAR
совсем):
#include <windows.h>
ATOM RegMyWindowClass(HINSTANCE, LPCTSTR);
HWND hListBox;
HINSTANCE hin;
BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam)
{
if (IsWindowVisible(hwnd) && (!GetWindow(hwnd, GW_OWNER))) {
TCHAR str[255];
if (GetWindowText(hwnd, str, 255)) {
SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)str);
}
}
return TRUE;
}
LRESULT CALLBACK WndProc(
HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_LBUTTONUP:
// this is one case where it doesn't make sense to use TCHAR
MessageBoxW(hWnd, L"Вы кликнули!", L"событие", 0);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_CREATE:
hListBox = CreateWindow(TEXT("LISTBOX"), TEXT(""), WS_CHILD | WS_VISIBLE | LBS_NOTIFY | WS_VSCROLL,
0, 0, 400, 400, hWnd, (HMENU)1111, hin, NULL);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
LPCTSTR lpzClass = TEXT("My Window Class!");
if (!RegMyWindowClass(hInstance, lpzClass))
return 1;
RECT screen_rect;
GetWindowRect(GetDesktopWindow(), &screen_rect);
int x = screen_rect.right / 2 - 200;
int y = screen_rect.bottom / 2 - 200;
HWND hWnd = CreateWindow(lpzClass, TEXT("Window"),
WS_OVERLAPPEDWINDOW | WS_VISIBLE, x, y, 400, 400, NULL, NULL,
hInstance, NULL);
ShowWindow(hWnd, SW_SHOW);
EnumWindows(&EnumWindowsProc, 0);
if (!hWnd) return 2;
MSG msg = { 0 };
int iGetOk = 0;
while ((iGetOk = GetMessage(&msg, NULL, 0, 0)) != 0)
{
if (iGetOk == -1) return 3;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
ATOM RegMyWindowClass(HINSTANCE hInst, LPCTSTR lpzClassName)
{
WNDCLASS wcWindowClass = { 0 };
wcWindowClass.lpfnWndProc = &WndProc;
wcWindowClass.style = CS_HREDRAW | CS_VREDRAW;
wcWindowClass.hInstance = hInst;
wcWindowClass.lpszClassName = lpzClassName;
wcWindowClass.hCursor = LoadCursor(NULL, IDC_ARROW);
wcWindowClass.hbrBackground = (HBRUSH)COLOR_APPWORKSPACE;
return RegisterClass(&wcWindowClass);
}
В противном случае используйте Unicode для всего:
#include <windows.h>
ATOM RegMyWindowClass(HINSTANCE, LPCWSTR);
HWND hListBox;
HINSTANCE hin;
BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam)
{
if (IsWindowVisible(hwnd) && (!GetWindow(hwnd, GW_OWNER))) {
WCHAR str[255];
if (GetWindowTextW(hwnd, str, 255)) {
SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)str);
}
}
return TRUE;
}
LRESULT CALLBACK WndProc(
HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_LBUTTONUP:
MessageBoxW(hWnd, L"Вы кликнули!", L"событие", 0);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_CREATE:
hListBox = CreateWindowW(L"LISTBOX", L"", WS_CHILD | WS_VISIBLE | LBS_NOTIFY | WS_VSCROLL,
0, 0, 400, 400, hWnd, (HMENU)1111, hin, NULL);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
LPCWSTR lpzClass = L"My Window Class!";
if (!RegMyWindowClass(hInstance, lpzClass))
return 1;
RECT screen_rect;
GetWindowRect(GetDesktopWindow(), &screen_rect);
int x = screen_rect.right / 2 - 200;
int y = screen_rect.bottom / 2 - 200;
HWND hWnd = CreateWindowW(lpzClass, L"Window",
WS_OVERLAPPEDWINDOW | WS_VISIBLE, x, y, 400, 400, NULL, NULL,
hInstance, NULL);
ShowWindow(hWnd, SW_SHOW);
EnumWindows(&EnumWindowsProc, 0);
if (!hWnd) return 2;
MSG msg = { 0 };
int iGetOk = 0;
while ((iGetOk = GetMessage(&msg, NULL, 0, 0)) != 0)
{
if (iGetOk == -1) return 3;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
ATOM RegMyWindowClass(HINSTANCE hInst, LPCWSTR lpzClassName)
{
WNDCLASSW wcWindowClass = { 0 };
wcWindowClass.lpfnWndProc = &WndProc;
wcWindowClass.style = CS_HREDRAW | CS_VREDRAW;
wcWindowClass.hInstance = hInst;
wcWindowClass.lpszClassName = lpzClassName;
wcWindowClass.hCursor = LoadCursor(NULL, IDC_ARROW);
wcWindowClass.hbrBackground = (HBRUSH)COLOR_APPWORKSPACE;
return RegisterClassW(&wcWindowClass);
}
Или придерживайтесь ANSI для всего, если вам нужно поддерживать совместимость с существующей логикой кода в другом месте вашего проекта:
#include <windows.h>
ATOM RegMyWindowClass(HINSTANCE, LPCSTR);
HWND hListBox;
HINSTANCE hin;
BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam)
{
if (IsWindowVisible(hwnd) && (!GetWindow(hwnd, GW_OWNER))) {
CHAR str[255];
if (GetWindowTextA(hwnd, str, 255)) {
SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)str);
}
}
return TRUE;
}
LRESULT CALLBACK WndProc(
HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_LBUTTONUP:
// this is one case where it doesn't make sense to use ANSI
MessageBoxW(hWnd, L"Вы кликнули!", L"событие", 0);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_CREATE:
hListBox = CreateWindowA("LISTBOX", "", WS_CHILD | WS_VISIBLE | LBS_NOTIFY | WS_VSCROLL,
0, 0, 400, 400, hWnd, (HMENU)1111, hin, NULL);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
LPCSTR lpzClass = "My Window Class!";
if (!RegMyWindowClass(hInstance, lpzClass))
return 1;
RECT screen_rect;
GetWindowRect(GetDesktopWindow(), &screen_rect);
int x = screen_rect.right / 2 - 200;
int y = screen_rect.bottom / 2 - 200;
HWND hWnd = CreateWindowA(lpzClass, "Window",
WS_OVERLAPPEDWINDOW | WS_VISIBLE, x, y, 400, 400, NULL, NULL,
hInstance, NULL);
ShowWindow(hWnd, SW_SHOW);
EnumWindows(&EnumWindowsProc, 0);
if (!hWnd) return 2;
MSG msg = { 0 };
int iGetOk = 0;
while ((iGetOk = GetMessage(&msg, NULL, 0, 0)) != 0)
{
if (iGetOk == -1) return 3;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
ATOM RegMyWindowClass(HINSTANCE hInst, LPCSTR lpzClassName)
{
WNDCLASSA wcWindowClass = { 0 };
wcWindowClass.lpfnWndProc = &WndProc;
wcWindowClass.style = CS_HREDRAW | CS_VREDRAW;
wcWindowClass.hInstance = hInst;
wcWindowClass.lpszClassName = lpzClassName;
wcWindowClass.hCursor = LoadCursor(NULL, IDC_ARROW);
wcWindowClass.hbrBackground = (HBRUSH)COLOR_APPWORKSPACE;
return RegisterClassA(&wcWindowClass);
}