Skip to content

Commit 5977b6b

Browse files
committed
Add Windows string conversion utilities and update usage
Introduced string_utils_windows.h with functions for converting between std::string and std::wstring, and for handling WCHAR arrays. Updated display, menu, tray icon, window manager, and window implementations to use these utilities for proper UTF-8/UTF-16 conversions, improving Unicode support in Windows API calls.
1 parent 599f1b3 commit 5977b6b

File tree

6 files changed

+114
-60
lines changed

6 files changed

+114
-60
lines changed

src/platform/windows/display_windows.cpp

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include "../../display.h"
22

33
#include <windows.h>
4+
#include "string_utils_windows.h"
45

56
namespace nativeapi {
67

@@ -47,10 +48,10 @@ void* Display::GetNativeObjectInternal() const {
4748
}
4849

4950
// Helper function to get monitor info
50-
MONITORINFOEX GetMonitorInfo(HMONITOR hMonitor) {
51-
MONITORINFOEX monitorInfo;
52-
monitorInfo.cbSize = sizeof(MONITORINFOEX);
53-
GetMonitorInfo(hMonitor, &monitorInfo);
51+
MONITORINFOEXW GetMonitorInfoEx(HMONITOR hMonitor) {
52+
MONITORINFOEXW monitorInfo;
53+
monitorInfo.cbSize = sizeof(MONITORINFOEXW);
54+
GetMonitorInfoW(hMonitor, &monitorInfo);
5455
return monitorInfo;
5556
}
5657

@@ -64,22 +65,22 @@ std::string Display::GetId() const {
6465
std::string Display::GetName() const {
6566
if (!pimpl_->h_monitor_)
6667
return "";
67-
MONITORINFOEX monitorInfo = GetMonitorInfo(pimpl_->h_monitor_);
68-
return monitorInfo.szDevice;
68+
MONITORINFOEXW monitorInfo = GetMonitorInfoEx(pimpl_->h_monitor_);
69+
return WCharArrayToString(monitorInfo.szDevice);
6970
}
7071

7172
Point Display::GetPosition() const {
7273
if (!pimpl_->h_monitor_)
7374
return {0.0, 0.0};
74-
MONITORINFOEX monitorInfo = GetMonitorInfo(pimpl_->h_monitor_);
75+
MONITORINFOEXW monitorInfo = GetMonitorInfoEx(pimpl_->h_monitor_);
7576
RECT rect = monitorInfo.rcMonitor;
7677
return {static_cast<double>(rect.left), static_cast<double>(rect.top)};
7778
}
7879

7980
Size Display::GetSize() const {
8081
if (!pimpl_->h_monitor_)
8182
return {0.0, 0.0};
82-
MONITORINFOEX monitorInfo = GetMonitorInfo(pimpl_->h_monitor_);
83+
MONITORINFOEXW monitorInfo = GetMonitorInfoEx(pimpl_->h_monitor_);
8384
RECT rect = monitorInfo.rcMonitor;
8485
return {static_cast<double>(rect.right - rect.left),
8586
static_cast<double>(rect.bottom - rect.top)};
@@ -88,7 +89,7 @@ Size Display::GetSize() const {
8889
Rectangle Display::GetWorkArea() const {
8990
if (!pimpl_->h_monitor_)
9091
return {0.0, 0.0, 0.0, 0.0};
91-
MONITORINFOEX monitorInfo = GetMonitorInfo(pimpl_->h_monitor_);
92+
MONITORINFOEXW monitorInfo = GetMonitorInfoEx(pimpl_->h_monitor_);
9293
RECT workRect = monitorInfo.rcWork;
9394
return {static_cast<double>(workRect.left), static_cast<double>(workRect.top),
9495
static_cast<double>(workRect.right - workRect.left),
@@ -111,7 +112,7 @@ double Display::GetScaleFactor() const {
111112
bool Display::IsPrimary() const {
112113
if (!pimpl_->h_monitor_)
113114
return false;
114-
MONITORINFOEX monitorInfo = GetMonitorInfo(pimpl_->h_monitor_);
115+
MONITORINFOEXW monitorInfo = GetMonitorInfoEx(pimpl_->h_monitor_);
115116
return (monitorInfo.dwFlags & MONITORINFOF_PRIMARY) != 0;
116117
}
117118

src/platform/windows/menu_windows.cpp

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <vector>
66
#include "../../menu.h"
77
#include "../../menu_event.h"
8+
#include "string_utils_windows.h"
89

910
namespace nativeapi {
1011

@@ -148,12 +149,13 @@ MenuItemType MenuItem::GetType() const {
148149
void MenuItem::SetLabel(const std::optional<std::string>& label) {
149150
pimpl_->text_ = label;
150151
if (pimpl_->parent_menu_) {
151-
MENUITEMINFO mii = {};
152-
mii.cbSize = sizeof(MENUITEMINFO);
152+
MENUITEMINFOW mii = {};
153+
mii.cbSize = sizeof(MENUITEMINFOW);
153154
mii.fMask = MIIM_STRING;
154-
const char* labelStr = label.has_value() ? label->c_str() : "";
155-
mii.dwTypeData = const_cast<LPSTR>(labelStr);
156-
SetMenuItemInfo(pimpl_->parent_menu_, pimpl_->menu_item_id_, FALSE, &mii);
155+
std::string labelStr = label.has_value() ? *label : "";
156+
std::wstring wLabelStr = StringToWString(labelStr);
157+
mii.dwTypeData = const_cast<LPWSTR>(wLabelStr.c_str());
158+
SetMenuItemInfoW(pimpl_->parent_menu_, pimpl_->menu_item_id_, FALSE, &mii);
157159
}
158160
}
159161

@@ -261,11 +263,11 @@ int MenuItem::GetRadioGroup() const {
261263
void MenuItem::SetSubmenu(std::shared_ptr<Menu> submenu) {
262264
pimpl_->submenu_ = submenu;
263265
if (pimpl_->parent_menu_ && submenu) {
264-
MENUITEMINFO mii = {};
265-
mii.cbSize = sizeof(MENUITEMINFO);
266+
MENUITEMINFOW mii = {};
267+
mii.cbSize = sizeof(MENUITEMINFOW);
266268
mii.fMask = MIIM_SUBMENU;
267269
mii.hSubMenu = static_cast<HMENU>(submenu->GetNativeObject());
268-
SetMenuItemInfo(pimpl_->parent_menu_, pimpl_->menu_item_id_, FALSE, &mii);
270+
SetMenuItemInfoW(pimpl_->parent_menu_, pimpl_->menu_item_id_, FALSE, &mii);
269271
}
270272
}
271273

@@ -276,11 +278,11 @@ std::shared_ptr<Menu> MenuItem::GetSubmenu() const {
276278
void MenuItem::RemoveSubmenu() {
277279
pimpl_->submenu_.reset();
278280
if (pimpl_->parent_menu_) {
279-
MENUITEMINFO mii = {};
280-
mii.cbSize = sizeof(MENUITEMINFO);
281+
MENUITEMINFOW mii = {};
282+
mii.cbSize = sizeof(MENUITEMINFOW);
281283
mii.fMask = MIIM_SUBMENU;
282284
mii.hSubMenu = nullptr;
283-
SetMenuItemInfo(pimpl_->parent_menu_, pimpl_->menu_item_id_, FALSE, &mii);
285+
SetMenuItemInfoW(pimpl_->parent_menu_, pimpl_->menu_item_id_, FALSE, &mii);
284286
}
285287
}
286288

@@ -355,8 +357,9 @@ void Menu::AddItem(std::shared_ptr<MenuItem> item) {
355357
}
356358

357359
auto labelOpt = item->GetLabel();
358-
const char* labelStr = labelOpt.has_value() ? labelOpt->c_str() : "";
359-
AppendMenu(pimpl_->hmenu_, flags, menuId, labelStr);
360+
std::string labelStr = labelOpt.has_value() ? *labelOpt : "";
361+
std::wstring wLabelStr = StringToWString(labelStr);
362+
AppendMenuW(pimpl_->hmenu_, flags, menuId, wLabelStr.c_str());
360363

361364
// Update the item's impl with menu info
362365
item->pimpl_->parent_menu_ = pimpl_->hmenu_;
@@ -380,9 +383,10 @@ void Menu::InsertItem(size_t index, std::shared_ptr<MenuItem> item) {
380383
}
381384

382385
auto labelOpt = item->GetLabel();
383-
const char* labelStr = labelOpt.has_value() ? labelOpt->c_str() : "";
384-
InsertMenu(pimpl_->hmenu_, static_cast<UINT>(index), flags, item->id,
385-
labelStr);
386+
std::string labelStr = labelOpt.has_value() ? *labelOpt : "";
387+
std::wstring wLabelStr = StringToWString(labelStr);
388+
InsertMenuW(pimpl_->hmenu_, static_cast<UINT>(index), flags, item->id,
389+
wLabelStr.c_str());
386390

387391
item->pimpl_->parent_menu_ = pimpl_->hmenu_;
388392
item->pimpl_->menu_item_id_ = static_cast<UINT>(item->id);
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#ifndef NATIVEAPI_PLATFORM_WINDOWS_STRING_UTILS_H_
2+
#define NATIVEAPI_PLATFORM_WINDOWS_STRING_UTILS_H_
3+
4+
#include <windows.h>
5+
#include <string>
6+
7+
namespace nativeapi {
8+
namespace { // Anonymous namespace, visible only within the translation unit including this header
9+
10+
// Convert std::string (UTF-8) to std::wstring (UTF-16)
11+
inline std::wstring StringToWString(const std::string& str) {
12+
if (str.empty()) return std::wstring();
13+
14+
int size_needed = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), (int)str.size(), nullptr, 0);
15+
std::wstring wstr(size_needed, 0);
16+
MultiByteToWideChar(CP_UTF8, 0, str.c_str(), (int)str.size(), &wstr[0], size_needed);
17+
return wstr;
18+
}
19+
20+
// Convert std::wstring (UTF-16) to std::string (UTF-8)
21+
inline std::string WStringToString(const std::wstring& wstr) {
22+
if (wstr.empty()) return std::string();
23+
24+
int size_needed = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), (int)wstr.size(), nullptr, 0, nullptr, nullptr);
25+
std::string str(size_needed, 0);
26+
WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), (int)wstr.size(), &str[0], size_needed, nullptr, nullptr);
27+
return str;
28+
}
29+
30+
// Convert WCHAR array to std::string
31+
inline std::string WCharArrayToString(const WCHAR* wchar_array) {
32+
if (!wchar_array) return std::string();
33+
return WStringToString(std::wstring(wchar_array));
34+
}
35+
36+
} // anonymous namespace
37+
} // namespace nativeapi
38+
39+
#endif // NATIVEAPI_PLATFORM_WINDOWS_STRING_UTILS_H_
40+

src/platform/windows/tray_icon_windows.cpp

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "../../menu.h"
88
#include "../../tray_icon.h"
99
#include "../../tray_icon_event.h"
10+
#include "string_utils_windows.h"
1011

1112
namespace nativeapi {
1213

@@ -80,7 +81,7 @@ class TrayIcon::Impl {
8081

8182
~Impl() {
8283
if (hwnd_) {
83-
Shell_NotifyIcon(NIM_DELETE, &nid_);
84+
Shell_NotifyIconW(NIM_DELETE, &nid_);
8485
}
8586
if (icon_handle_) {
8687
DestroyIcon(icon_handle_);
@@ -102,18 +103,20 @@ TrayIcon::TrayIcon() : id(-1), pimpl_(std::make_unique<Impl>()) {
102103
static UINT next_class_id = 1;
103104
std::string class_name =
104105
"NativeAPITrayIcon_" + std::to_string(next_class_id++);
106+
std::wstring wclass_name = StringToWString(class_name);
105107

106-
WNDCLASS wc = {};
108+
WNDCLASSW wc = {};
107109
wc.lpfnWndProc = DefWindowProc;
108110
wc.hInstance = hInstance;
109-
wc.lpszClassName = class_name.c_str();
111+
wc.lpszClassName = wclass_name.c_str();
110112

111-
if (RegisterClass(&wc)) {
113+
if (RegisterClassW(&wc)) {
112114
// Create hidden message-only window
115+
std::wstring wtitle = StringToWString("NativeAPI Tray Icon");
113116
HWND hwnd =
114-
CreateWindow(class_name.c_str(), "NativeAPI Tray Icon", 0, 0, 0, 0, 0,
115-
HWND_MESSAGE, // Message-only window
116-
nullptr, hInstance, nullptr);
117+
CreateWindowW(wclass_name.c_str(), wtitle.c_str(), 0, 0, 0, 0, 0,
118+
HWND_MESSAGE, // Message-only window
119+
nullptr, hInstance, nullptr);
117120

118121
if (hwnd) {
119122
// Generate unique icon ID
@@ -148,12 +151,13 @@ void TrayIcon::SetIcon(std::string icon) {
148151
hIcon = LoadIcon(nullptr, IDI_APPLICATION);
149152
} else if (!icon.empty()) {
150153
// Try to load as file path first
151-
hIcon = (HICON)LoadImage(nullptr, icon.c_str(), IMAGE_ICON, 16, 16,
152-
LR_LOADFROMFILE);
154+
std::wstring wicon = StringToWString(icon);
155+
hIcon = (HICON)LoadImageW(nullptr, wicon.c_str(), IMAGE_ICON, 16, 16,
156+
LR_LOADFROMFILE);
153157

154158
// If file path failed, try as resource
155159
if (!hIcon) {
156-
hIcon = LoadIcon(GetModuleHandle(nullptr), icon.c_str());
160+
hIcon = LoadIconW(GetModuleHandle(nullptr), wicon.c_str());
157161
}
158162

159163
// If still failed, use default application icon
@@ -176,7 +180,7 @@ void TrayIcon::SetIcon(std::string icon) {
176180

177181
// Update the icon if it's currently visible
178182
if (IsVisible()) {
179-
Shell_NotifyIcon(NIM_MODIFY, &pimpl_->nid_);
183+
Shell_NotifyIconW(NIM_MODIFY, &pimpl_->nid_);
180184
}
181185
}
182186
}
@@ -193,21 +197,21 @@ std::optional<std::string> TrayIcon::GetTitle() {
193197

194198
void TrayIcon::SetTooltip(std::optional<std::string> tooltip) {
195199
if (pimpl_->hwnd_) {
196-
const char* tooltip_str = tooltip.has_value() ? tooltip->c_str() : "";
197-
strncpy_s(pimpl_->nid_.szTip, tooltip_str,
198-
sizeof(pimpl_->nid_.szTip) - 1);
199-
pimpl_->nid_.szTip[sizeof(pimpl_->nid_.szTip) - 1] = '\0';
200+
std::string tooltip_str = tooltip.has_value() ? *tooltip : "";
201+
std::wstring wtooltip = StringToWString(tooltip_str);
202+
wcsncpy_s(pimpl_->nid_.szTip, _countof(pimpl_->nid_.szTip),
203+
wtooltip.c_str(), _TRUNCATE);
200204

201205
// Update if icon is visible (check if hIcon is set as indicator)
202206
if (pimpl_->nid_.hIcon) {
203-
Shell_NotifyIcon(NIM_MODIFY, &pimpl_->nid_);
207+
Shell_NotifyIconW(NIM_MODIFY, &pimpl_->nid_);
204208
}
205209
}
206210
}
207211

208212
std::optional<std::string> TrayIcon::GetTooltip() {
209-
if (pimpl_->hwnd_ && pimpl_->nid_.szTip[0] != '\0') {
210-
return std::string(pimpl_->nid_.szTip);
213+
if (pimpl_->hwnd_ && pimpl_->nid_.szTip[0] != L'\0') {
214+
return WCharArrayToString(pimpl_->nid_.szTip);
211215
}
212216
return std::nullopt;
213217
}
@@ -251,10 +255,10 @@ bool TrayIcon::SetVisible(bool visible) {
251255

252256
if (visible && !currently_visible) {
253257
// Show the tray icon
254-
return Shell_NotifyIcon(NIM_ADD, &pimpl_->nid_) == TRUE;
258+
return Shell_NotifyIconW(NIM_ADD, &pimpl_->nid_) == TRUE;
255259
} else if (!visible && currently_visible) {
256260
// Hide the tray icon
257-
return Shell_NotifyIcon(NIM_DELETE, &pimpl_->nid_) == TRUE;
261+
return Shell_NotifyIconW(NIM_DELETE, &pimpl_->nid_) == TRUE;
258262
} else {
259263
// Already in the desired state
260264
return true;

src/platform/windows/window_manager_windows.cpp

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include "../../window.h"
66
#include "../../window_event.h"
77
#include "../../window_manager.h"
8+
#include "string_utils_windows.h"
89

910
namespace nativeapi {
1011

@@ -114,17 +115,17 @@ std::shared_ptr<Window> WindowManager::Create(const WindowOptions& options) {
114115

115116
// Register window class if not already registered
116117
static bool class_registered = false;
117-
const char* class_name = "NativeAPIWindow";
118+
static std::wstring wclass_name = StringToWString("NativeAPIWindow");
118119

119120
if (!class_registered) {
120-
WNDCLASS wc = {};
121+
WNDCLASSW wc = {};
121122
wc.lpfnWndProc = WindowProc;
122123
wc.hInstance = hInstance;
123-
wc.lpszClassName = class_name;
124+
wc.lpszClassName = wclass_name.c_str();
124125
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
125126
wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
126127

127-
if (RegisterClass(&wc)) {
128+
if (RegisterClassW(&wc)) {
128129
class_registered = true;
129130
} else {
130131
DWORD error = GetLastError();
@@ -140,12 +141,13 @@ std::shared_ptr<Window> WindowManager::Create(const WindowOptions& options) {
140141
// Create the window
141142
DWORD style = WS_OVERLAPPEDWINDOW;
142143
DWORD exStyle = 0;
144+
std::wstring wtitle = StringToWString(options.title);
143145

144-
HWND hwnd = CreateWindowEx(exStyle, class_name, options.title.c_str(), style,
145-
CW_USEDEFAULT, CW_USEDEFAULT,
146-
static_cast<int>(options.size.width),
147-
static_cast<int>(options.size.height), nullptr,
148-
nullptr, hInstance, nullptr);
146+
HWND hwnd = CreateWindowExW(exStyle, wclass_name.c_str(), wtitle.c_str(), style,
147+
CW_USEDEFAULT, CW_USEDEFAULT,
148+
static_cast<int>(options.size.width),
149+
static_cast<int>(options.size.height), nullptr,
150+
nullptr, hInstance, nullptr);
149151

150152
if (!hwnd) {
151153
std::cerr << "Failed to create window. Error: " << GetLastError()

src/platform/windows/window_windows.cpp

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#include <iostream>
33
#include "../../window.h"
44
#include "../../window_manager.h"
5+
#include "string_utils_windows.h"
56

67
namespace nativeapi {
78

@@ -393,21 +394,23 @@ Point Window::GetPosition() const {
393394

394395
void Window::SetTitle(std::string title) {
395396
if (pimpl_->hwnd_) {
396-
SetWindowText(pimpl_->hwnd_, title.c_str());
397+
std::wstring wtitle = StringToWString(title);
398+
SetWindowTextW(pimpl_->hwnd_, wtitle.c_str());
397399
}
398400
}
399401

400402
std::string Window::GetTitle() const {
401403
if (!pimpl_->hwnd_)
402404
return "";
403405

404-
int length = GetWindowTextLength(pimpl_->hwnd_);
406+
int length = GetWindowTextLengthW(pimpl_->hwnd_);
405407
if (length == 0)
406408
return "";
407409

408-
std::string title(length, '\0');
409-
GetWindowText(pimpl_->hwnd_, &title[0], length + 1);
410-
return title;
410+
std::wstring wtitle(length + 1, L'\0');
411+
GetWindowTextW(pimpl_->hwnd_, &wtitle[0], length + 1);
412+
wtitle.resize(length);
413+
return WStringToString(wtitle);
411414
}
412415

413416
void Window::SetHasShadow(bool has_shadow) {

0 commit comments

Comments
 (0)