Merge pull request #114525 from bruvzg/icon_free

[Windows] Fix icon leak.
This commit is contained in:
Thaddeus Crews
2026-01-05 10:18:01 -06:00
2 changed files with 105 additions and 54 deletions

View File

@@ -1989,7 +1989,7 @@ Size2i DisplayServerWindows::window_get_title_size(const String &p_title, Window
size.y = MAX(size.y, rect.bottom - rect.top);
}
}
if (icon.is_valid()) {
if (icon_big) {
size.x += 32;
} else {
size.x += 16;
@@ -2451,8 +2451,16 @@ void DisplayServerWindows::_update_window_style(WindowID p_window, bool p_repain
SetWindowLongPtr(wd.hWnd, GWL_STYLE, style);
SetWindowLongPtr(wd.hWnd, GWL_EXSTYLE, style_ex);
if (icon.is_valid()) {
set_icon(icon);
if (icon_big && !icon_small) {
SendMessage(wd.hWnd, WM_SETICON, ICON_BIG, (LPARAM)icon_big);
SendMessage(wd.hWnd, WM_SETICON, ICON_SMALL, (LPARAM)icon_big);
} else {
if (icon_big) {
SendMessage(wd.hWnd, WM_SETICON, ICON_BIG, (LPARAM)icon_big);
}
if (icon_small) {
SendMessage(wd.hWnd, WM_SETICON, ICON_SMALL, (LPARAM)icon_small);
}
}
SetWindowPos(wd.hWnd, _is_always_on_top_recursive(p_window) ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | ((wd.no_focus || wd.is_popup) ? SWP_NOACTIVATE : 0));
@@ -3922,6 +3930,17 @@ void DisplayServerWindows::swap_buffers() {
void DisplayServerWindows::set_native_icon(const String &p_filename) {
_THREAD_SAFE_METHOD_
if (icon_big) {
DestroyIcon(icon_big);
icon_buffer_big.clear();
icon_big = nullptr;
}
if (icon_small) {
DestroyIcon(icon_small);
icon_buffer_small.clear();
icon_small = nullptr;
}
Ref<FileAccess> f = FileAccess::open(p_filename, FileAccess::READ);
ERR_FAIL_COND_MSG(f.is_null(), "Cannot open file with icon '" + p_filename + "'.");
@@ -3979,59 +3998,60 @@ void DisplayServerWindows::set_native_icon(const String &p_filename) {
// Read the big icon.
DWORD bytecount_big = icon_dir->idEntries[big_icon_index].dwBytesInRes;
Vector<uint8_t> data_big;
data_big.resize(bytecount_big);
icon_buffer_big.resize(bytecount_big);
pos = icon_dir->idEntries[big_icon_index].dwImageOffset;
f->seek(pos);
f->get_buffer((uint8_t *)&data_big.write[0], bytecount_big);
HICON icon_big = CreateIconFromResource((PBYTE)&data_big.write[0], bytecount_big, TRUE, 0x00030000);
f->get_buffer((uint8_t *)&icon_buffer_big.write[0], bytecount_big);
icon_big = CreateIconFromResourceEx((PBYTE)&icon_buffer_big.write[0], bytecount_big, TRUE, 0x00030000, 0, 0, LR_DEFAULTSIZE);
ERR_FAIL_NULL_MSG(icon_big, "Could not create " + itos(big_icon_width) + "x" + itos(big_icon_width) + " @" + itos(big_icon_cc) + " icon, error: " + format_error_message(GetLastError()) + ".");
// Read the small icon.
DWORD bytecount_small = icon_dir->idEntries[small_icon_index].dwBytesInRes;
Vector<uint8_t> data_small;
data_small.resize(bytecount_small);
icon_buffer_small.resize(bytecount_small);
pos = icon_dir->idEntries[small_icon_index].dwImageOffset;
f->seek(pos);
f->get_buffer((uint8_t *)&data_small.write[0], bytecount_small);
HICON icon_small = CreateIconFromResource((PBYTE)&data_small.write[0], bytecount_small, TRUE, 0x00030000);
f->get_buffer((uint8_t *)&icon_buffer_small.write[0], bytecount_small);
icon_small = CreateIconFromResourceEx((PBYTE)&icon_buffer_small.write[0], bytecount_small, TRUE, 0x00030000, 0, 0, LR_DEFAULTSIZE);
ERR_FAIL_NULL_MSG(icon_small, "Could not create 16x16 @" + itos(small_icon_cc) + " icon, error: " + format_error_message(GetLastError()) + ".");
// Online tradition says to be sure last error is cleared and set the small icon first.
int err = 0;
SetLastError(err);
SendMessage(windows[MAIN_WINDOW_ID].hWnd, WM_SETICON, ICON_SMALL, (LPARAM)icon_small);
err = GetLastError();
ERR_FAIL_COND_MSG(err, "Error setting ICON_SMALL: " + format_error_message(err) + ".");
SendMessage(windows[MAIN_WINDOW_ID].hWnd, WM_SETICON, ICON_BIG, (LPARAM)icon_big);
err = GetLastError();
ERR_FAIL_COND_MSG(err, "Error setting ICON_BIG: " + format_error_message(err) + ".");
for (const KeyValue<WindowID, WindowData> &E : windows) {
SendMessage(E.value.hWnd, WM_SETICON, ICON_SMALL, (LPARAM)icon_small);
SendMessage(E.value.hWnd, WM_SETICON, ICON_BIG, (LPARAM)icon_big);
}
memdelete(icon_dir);
}
void DisplayServerWindows::set_icon(const Ref<Image> &p_icon) {
_THREAD_SAFE_METHOD_
if (icon_big) {
DestroyIcon(icon_big);
icon_buffer_big.clear();
icon_big = nullptr;
}
if (icon_small) {
DestroyIcon(icon_small);
icon_buffer_small.clear();
icon_small = nullptr;
}
if (p_icon.is_valid()) {
ERR_FAIL_COND(p_icon->get_width() <= 0 || p_icon->get_height() <= 0);
Ref<Image> img = p_icon;
if (img != icon) {
img = img->duplicate();
img->convert(Image::FORMAT_RGBA8);
}
Ref<Image> img = p_icon->duplicate();
img->convert(Image::FORMAT_RGBA8);
int w = img->get_width();
int h = img->get_height();
// Create temporary bitmap buffer.
int icon_len = 40 + h * w * 4;
Vector<BYTE> v;
v.resize(icon_len);
BYTE *icon_bmp = v.ptrw();
icon_buffer_big.resize(icon_len);
BYTE *icon_bmp = icon_buffer_big.ptrw();
encode_uint32(40, &icon_bmp[0]);
encode_uint32(w, &icon_bmp[4]);
@@ -4058,26 +4078,23 @@ void DisplayServerWindows::set_icon(const Ref<Image> &p_icon) {
wpx[3] = rpx[3];
}
}
icon_big = CreateIconFromResourceEx(icon_bmp, icon_len, TRUE, 0x00030000, 0, 0, LR_DEFAULTSIZE);
ERR_FAIL_NULL(icon_big);
HICON hicon = CreateIconFromResource(icon_bmp, icon_len, TRUE, 0x00030000);
ERR_FAIL_NULL(hicon);
icon = img;
// Set the icon for the window.
SendMessage(windows[MAIN_WINDOW_ID].hWnd, WM_SETICON, ICON_SMALL, (LPARAM)hicon);
// Set the icon in the task manager (should we do this?).
SendMessage(windows[MAIN_WINDOW_ID].hWnd, WM_SETICON, ICON_BIG, (LPARAM)hicon);
for (const KeyValue<WindowID, WindowData> &E : windows) {
SendMessage(E.value.hWnd, WM_SETICON, ICON_SMALL, (LPARAM)icon_big);
SendMessage(E.value.hWnd, WM_SETICON, ICON_BIG, (LPARAM)icon_big);
}
} else {
icon = Ref<Image>();
SendMessage(windows[MAIN_WINDOW_ID].hWnd, WM_SETICON, ICON_SMALL, 0);
SendMessage(windows[MAIN_WINDOW_ID].hWnd, WM_SETICON, ICON_BIG, 0);
for (const KeyValue<WindowID, WindowData> &E : windows) {
SendMessage(E.value.hWnd, WM_SETICON, ICON_SMALL, 0);
SendMessage(E.value.hWnd, WM_SETICON, ICON_BIG, 0);
}
}
}
DisplayServer::IndicatorID DisplayServerWindows::create_status_indicator(const Ref<Texture2D> &p_icon, const String &p_tooltip, const Callable &p_callback) {
HICON hicon = nullptr;
IndicatorData idat;
if (p_icon.is_valid() && p_icon->get_width() > 0 && p_icon->get_height() > 0 && p_icon->get_image().is_valid()) {
Ref<Image> img = p_icon->get_image();
img = img->duplicate();
@@ -4091,9 +4108,8 @@ DisplayServer::IndicatorID DisplayServerWindows::create_status_indicator(const R
// Create temporary bitmap buffer.
int icon_len = 40 + h * w * 4;
Vector<BYTE> v;
v.resize(icon_len);
BYTE *icon_bmp = v.ptrw();
idat.icon_buffer.resize(icon_len);
BYTE *icon_bmp = idat.icon_buffer.ptrw();
encode_uint32(40, &icon_bmp[0]);
encode_uint32(w, &icon_bmp[4]);
@@ -4121,10 +4137,9 @@ DisplayServer::IndicatorID DisplayServerWindows::create_status_indicator(const R
}
}
hicon = CreateIconFromResource(icon_bmp, icon_len, TRUE, 0x00030000);
idat.icon = CreateIconFromResourceEx(icon_bmp, icon_len, TRUE, 0x00030000, 0, 0, LR_DEFAULTSIZE);
}
IndicatorData idat;
idat.callback = p_callback;
NOTIFYICONDATAW ndat;
@@ -4134,7 +4149,7 @@ DisplayServer::IndicatorID DisplayServerWindows::create_status_indicator(const R
ndat.uID = indicator_id_counter;
ndat.uFlags = NIF_ICON | NIF_TIP | NIF_MESSAGE;
ndat.uCallbackMessage = WM_INDICATOR_CALLBACK_MESSAGE;
ndat.hIcon = hicon;
ndat.hIcon = idat.icon;
memcpy(ndat.szTip, p_tooltip.utf16().get_data(), MIN(p_tooltip.utf16().length(), 127) * sizeof(WCHAR));
ndat.uVersion = NOTIFYICON_VERSION;
@@ -4150,7 +4165,14 @@ DisplayServer::IndicatorID DisplayServerWindows::create_status_indicator(const R
void DisplayServerWindows::status_indicator_set_icon(IndicatorID p_id, const Ref<Texture2D> &p_icon) {
ERR_FAIL_COND(!indicators.has(p_id));
HICON hicon = nullptr;
IndicatorData &idat = indicators[p_id];
if (idat.icon) {
DestroyIcon(idat.icon);
idat.icon_buffer.clear();
idat.icon = nullptr;
}
if (p_icon.is_valid() && p_icon->get_width() > 0 && p_icon->get_height() > 0 && p_icon->get_image().is_valid()) {
Ref<Image> img = p_icon->get_image();
img = img->duplicate();
@@ -4164,9 +4186,8 @@ void DisplayServerWindows::status_indicator_set_icon(IndicatorID p_id, const Ref
// Create temporary bitmap buffer.
int icon_len = 40 + h * w * 4;
Vector<BYTE> v;
v.resize(icon_len);
BYTE *icon_bmp = v.ptrw();
idat.icon_buffer.resize(icon_len);
BYTE *icon_bmp = idat.icon_buffer.ptrw();
encode_uint32(40, &icon_bmp[0]);
encode_uint32(w, &icon_bmp[4]);
@@ -4194,7 +4215,7 @@ void DisplayServerWindows::status_indicator_set_icon(IndicatorID p_id, const Ref
}
}
hicon = CreateIconFromResource(icon_bmp, icon_len, TRUE, 0x00030000);
idat.icon = CreateIconFromResourceEx(icon_bmp, icon_len, TRUE, 0x00030000, 0, 0, LR_DEFAULTSIZE);
}
NOTIFYICONDATAW ndat;
@@ -4203,7 +4224,7 @@ void DisplayServerWindows::status_indicator_set_icon(IndicatorID p_id, const Ref
ndat.hWnd = windows[MAIN_WINDOW_ID].hWnd;
ndat.uID = p_id;
ndat.uFlags = NIF_ICON;
ndat.hIcon = hicon;
ndat.hIcon = idat.icon;
ndat.uVersion = NOTIFYICON_VERSION;
Shell_NotifyIconW(NIM_MODIFY, &ndat);
@@ -4263,6 +4284,13 @@ Rect2 DisplayServerWindows::status_indicator_get_rect(IndicatorID p_id) const {
void DisplayServerWindows::delete_status_indicator(IndicatorID p_id) {
ERR_FAIL_COND(!indicators.has(p_id));
IndicatorData &idat = indicators[p_id];
if (idat.icon) {
DestroyIcon(idat.icon);
idat.icon_buffer.clear();
idat.icon = nullptr;
}
NOTIFYICONDATAW ndat;
ZeroMemory(&ndat, sizeof(NOTIFYICONDATAW));
ndat.cbSize = sizeof(NOTIFYICONDATAW);
@@ -7653,6 +7681,12 @@ DisplayServerWindows::~DisplayServerWindows() {
ndat.uID = E->key;
ndat.uVersion = NOTIFYICON_VERSION;
if (E->value.icon) {
DestroyIcon(E->value.icon);
E->value.icon_buffer.clear();
E->value.icon = nullptr;
}
Shell_NotifyIconW(NIM_DELETE, &ndat);
}
@@ -7702,6 +7736,17 @@ DisplayServerWindows::~DisplayServerWindows() {
}
#endif
if (icon_big) {
DestroyIcon(icon_big);
icon_buffer_big.clear();
icon_big = nullptr;
}
if (icon_small) {
DestroyIcon(icon_small);
icon_buffer_small.clear();
icon_small = nullptr;
}
if (restore_mouse_trails > 1) {
SystemParametersInfoA(SPI_SETMOUSETRAILS, restore_mouse_trails, nullptr, 0);
}

View File

@@ -274,6 +274,11 @@ class DisplayServerWindows : public DisplayServer {
RBMap<int, Vector2> touch_state;
Vector<BYTE> icon_buffer_big;
HICON icon_big = nullptr;
Vector<BYTE> icon_buffer_small;
HICON icon_small = nullptr;
int pressrc;
HINSTANCE hInstance; // Holds The Instance Of The Application
String rendering_driver;
@@ -387,7 +392,6 @@ class DisplayServerWindows : public DisplayServer {
HHOOK mouse_monitor = nullptr;
List<WindowID> popup_list;
uint64_t time_since_popup = 0;
Ref<Image> icon;
Error _create_window(WindowID p_window_id, WindowMode p_mode, uint32_t p_flags, const Rect2i &p_rect, bool p_exclusive, WindowID p_transient_parent, HWND p_parent_hwnd, bool p_no_redirection_bitmap);
void _destroy_window(WindowID p_window_id); // Destroys only what was needed to be created for the main window. Does not destroy transient parent dependencies or GL/rendering context windows.
@@ -413,6 +417,8 @@ class DisplayServerWindows : public DisplayServer {
struct IndicatorData {
RID menu_rid;
Callable callback;
Vector<BYTE> icon_buffer;
HICON icon = nullptr;
};
IndicatorID indicator_id_counter = 0;