Skip to content

Commit 57f6b4f

Browse files
feat(shell, js): improve resid related stuffs
1 parent 5d548a1 commit 57f6b4f

File tree

10 files changed

+318
-184
lines changed

10 files changed

+318
-184
lines changed

scripts/rebuild.ps1

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@ foreach ($pidx in $pids) {
33
Stop-Process -Id $pidx -Force
44
}
55

6+
67
xmake b --yes inject
78
xmake b --yes shell && xmake r inject new

src/shell/contextmenu/contextmenu.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,8 @@ menu menu::construct_with_hmenu(HMENU hMenu, HWND hWnd, bool is_top) {
176176
item.type = menu_item::type::spacer;
177177
} else {
178178
item.name = wstring_to_utf8(strip_extra_infos(buffer));
179+
item.origin_name = wstring_to_utf8(buffer);
180+
179181
auto id_stripped = res_string_loader::string_to_id(buffer);
180182
if (std::get_if<size_t>(&id_stripped)) {
181183
item.name_resid =

src/shell/contextmenu/contextmenu.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,5 +44,6 @@ struct menu_item {
4444
// the two below are only for information; set them changes nothing
4545
std::optional<size_t> wID;
4646
std::optional<std::string> name_resid;
47+
std::optional<std::string> origin_name;
4748
};
4849
} // namespace mb_shell

src/shell/res_string_loader.cc

Lines changed: 104 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ static std::mutex lock_str_data;
2121
static std::unordered_map<std::wstring,
2222
res_string_loader::res_string_identifier>
2323
str_data;
24+
static std::unordered_map<res_string_loader::res_string_identifier,
25+
std::wstring>
26+
str_data_rev;
2427
static std::unordered_map<size_t, std::string> module_name_cache;
2528

2629
res_string_loader::string_id res_string_loader::string_to_id(std::wstring str) {
@@ -34,8 +37,10 @@ res_string_loader::string_id res_string_loader::string_to_id(std::wstring str) {
3437

3538
std::string get_module_name_from_instance(HINSTANCE hInstance) {
3639
char buffer[MAX_PATH];
37-
GetModuleFileNameA(hInstance, buffer, MAX_PATH);
38-
return std::filesystem::path(buffer).filename().string();
40+
if (GetModuleFileNameA(hInstance, buffer, MAX_PATH)) {
41+
return std::filesystem::path(buffer).filename().string();
42+
}
43+
return {};
3944
}
4045

4146
size_t store_module_name(HINSTANCE hInstance) {
@@ -57,7 +62,7 @@ void res_string_loader::init_hook() {
5762
static auto LoadStringWHook =
5863
kernelbase->exports("LoadStringW")->inline_hook();
5964
LoadStringWHook->install(+[](HINSTANCE hInstance, UINT uID, LPWSTR lpBuffer,
60-
int cchBufferMax) -> int {
65+
int cchBufferMax) -> int {
6166
auto res = LoadStringWHook->call_trampoline<int>(hInstance, uID, lpBuffer,
6267
cchBufferMax);
6368
if (res > 0) {
@@ -66,6 +71,7 @@ void res_string_loader::init_hook() {
6671
if (str_data.find(str) != str_data.end())
6772
return res;
6873
str_data[str] = {uID, store_module_name(hInstance)};
74+
str_data_rev[{uID, store_module_name(hInstance)}] = str;
6975
}
7076
return res;
7177
});
@@ -74,7 +80,7 @@ void res_string_loader::init_hook() {
7480
kernelbase->exports("LoadStringA")->inline_hook();
7581

7682
LoadStringAHook->install(+[](HINSTANCE hInstance, UINT uID, LPSTR lpBuffer,
77-
int cchBufferMax) -> int {
83+
int cchBufferMax) -> int {
7884
auto res = LoadStringAHook->call_trampoline<int>(hInstance, uID, lpBuffer,
7985
cchBufferMax);
8086
if (res > 0) {
@@ -84,6 +90,7 @@ void res_string_loader::init_hook() {
8490
if (str_data.find(s) != str_data.end())
8591
return res;
8692
str_data[s] = {uID, store_module_name(hInstance)};
93+
str_data_rev[{uID, store_module_name(hInstance)}] = s;
8794
}
8895

8996
return res;
@@ -154,6 +161,58 @@ void EnumerateStringResources(
154161
return TRUE;
155162
},
156163
reinterpret_cast<LONG_PTR>(&callback));
164+
// RT_MENU
165+
EnumResourceNamesW(
166+
mod, MAKEINTRESOURCEW(4),
167+
+[](HMODULE hModule, LPCWSTR /*lpType*/, LPWSTR lpName,
168+
LONG_PTR lParam) -> BOOL {
169+
auto &cb =
170+
*reinterpret_cast<std::function<void(std::wstring_view, size_t)> *>(
171+
lParam);
172+
if (!IS_INTRESOURCE(lpName))
173+
return TRUE;
174+
175+
HMENU hMenu = LoadMenuW(hModule, lpName);
176+
if (hMenu) {
177+
for (UINT id = 0; id < 0xFFFF; id++) {
178+
UINT state = GetMenuState(hMenu, id, MF_BYCOMMAND);
179+
if (state != 0xFFFFFFFF) {
180+
MENUITEMINFOW info = {sizeof(MENUITEMINFO)};
181+
info.fMask = MIIM_STRING;
182+
info.dwTypeData = nullptr;
183+
info.cch = 0;
184+
185+
if (auto res = GetMenuItemInfoW(hMenu, id, FALSE, &info); !res) {
186+
std::println("Failed to get menu item info for id {}: {}", id,
187+
GetLastError());
188+
continue;
189+
}
190+
191+
std::wstring name;
192+
if (info.cch == 0) {
193+
continue; // 没有名称
194+
} else {
195+
name.resize(info.cch + 1);
196+
info.dwTypeData = name.data();
197+
info.cch = static_cast<UINT>(name.size());
198+
if (!GetMenuItemInfoW(hMenu, id, FALSE, &info)) {
199+
std::println("Failed to get menu item info for id {}", id);
200+
continue;
201+
}
202+
}
203+
204+
if (info.dwTypeData) {
205+
cb(info.dwTypeData,
206+
static_cast<size_t>((id << 16) + (size_t)lpName));
207+
}
208+
}
209+
}
210+
DestroyMenu(hMenu);
211+
}
212+
213+
return TRUE;
214+
},
215+
reinterpret_cast<LONG_PTR>(&callback));
157216
}
158217

159218
void load_all_res_strings(std::string module) {
@@ -163,7 +222,11 @@ void load_all_res_strings(std::string module) {
163222
return;
164223
}
165224
if (!hInstance) {
166-
return;
225+
hInstance = LoadLibraryExA(module.data(), nullptr,
226+
LOAD_LIBRARY_AS_DATAFILE |
227+
LOAD_LIBRARY_AS_IMAGE_RESOURCE);
228+
if (!hInstance)
229+
return;
167230
}
168231
auto mod_hash = store_module_name(hInstance);
169232

@@ -175,6 +238,7 @@ void load_all_res_strings(std::string module) {
175238
if (str_data.find(s) != str_data.end())
176239
return;
177240
str_data[s] = {id, mod_hash};
241+
str_data_rev[{id, mod_hash}] = s;
178242
});
179243
}
180244

@@ -193,8 +257,40 @@ void res_string_loader::init_known_strings() {
193257
load_all_res_strings(dll);
194258
}
195259
dbgout("[perf] init_known_strings took {}ms",
196-
std::chrono::duration_cast<std::chrono::milliseconds>(
197-
std::chrono::high_resolution_clock::now() - now)
198-
.count());
260+
std::chrono::duration_cast<std::chrono::milliseconds>(
261+
std::chrono::high_resolution_clock::now() - now)
262+
.count());
263+
}
264+
std::string res_string_loader::string_from_id_string(const std::string &str) {
265+
auto pos = str.find('@');
266+
if (pos == std::string::npos) {
267+
return str;
268+
}
269+
auto id_str = str.substr(0, pos);
270+
auto module_name = str.substr(pos + 1);
271+
272+
size_t id = std::stoull(id_str);
273+
if (module_name == "0") {
274+
return std::to_string(id);
275+
}
276+
277+
res_string_identifier id_obj{id, std::hash<std::string>{}(module_name)};
278+
std::lock_guard lock(lock_str_data);
279+
auto it = str_data_rev.find(id_obj);
280+
if (it != str_data_rev.end()) {
281+
return wstring_to_utf8(it->second);
282+
}
283+
return "";
284+
}
285+
std::vector<std::string>
286+
res_string_loader::get_all_ids_of_string(const std::wstring &str) {
287+
std::vector<std::string> ids;
288+
std::lock_guard lock(lock_str_data);
289+
for (const auto &[id, s] : str_data_rev) {
290+
if (s == str) {
291+
ids.push_back(std::to_string(id.id) + "@" + module_name_cache[id.module]);
292+
}
293+
}
294+
return ids;
199295
}
200296
} // namespace mb_shell

src/shell/res_string_loader.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,40 @@
33
#include <memory>
44
#include <string>
55
#include <variant>
6+
#include <vector>
67
namespace mb_shell {
78
struct res_string_loader {
89
struct res_string_identifier {
910
size_t id;
1011
size_t module;
12+
13+
bool operator<=>(const res_string_identifier &other) const {
14+
return std::tie(module, id) < std::tie(other.module, other.id);
15+
}
16+
17+
bool operator==(const res_string_identifier &other) const {
18+
return module == other.module && id == other.id;
19+
}
1120
};
1221

1322
using string_id = std::variant<res_string_identifier, size_t>;
1423
static string_id string_to_id(std::wstring str);
1524
static std::string string_to_id_string(std::wstring str);
25+
static std::string string_from_id_string(const std::string &str);
26+
static std::vector<std::string>
27+
get_all_ids_of_string(const std::wstring &str);
1628
static void init_hook();
1729
static void init_known_strings();
1830

1931
static void init();
2032
};
33+
}
34+
35+
namespace std {
36+
template <>
37+
struct hash<mb_shell::res_string_loader::res_string_identifier> {
38+
size_t operator()(const mb_shell::res_string_loader::res_string_identifier &id) const noexcept {
39+
return std::hash<size_t>{}(id.id) ^ std::hash<size_t>{}(id.module);
40+
}
41+
};
2142
}

src/shell/script/binding_qjs.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,8 @@ template <> struct qjs::js_traits<mb_shell::js::js_menu_data> {
484484

485485
obj.name_resid = js_traits<std::optional<std::string>>::unwrap(ctx, JS_GetPropertyStr(ctx, v, "name_resid"));
486486

487+
obj.origin_name = js_traits<std::optional<std::string>>::unwrap(ctx, JS_GetPropertyStr(ctx, v, "origin_name"));
488+
487489
return obj;
488490
}
489491

@@ -508,6 +510,8 @@ template <> struct qjs::js_traits<mb_shell::js::js_menu_data> {
508510

509511
JS_SetPropertyStr(ctx, obj, "name_resid", js_traits<std::optional<std::string>>::wrap(ctx, val.name_resid));
510512

513+
JS_SetPropertyStr(ctx, obj, "origin_name", js_traits<std::optional<std::string>>::wrap(ctx, val.origin_name));
514+
511515
return obj;
512516
}
513517
};
@@ -524,6 +528,7 @@ template<> struct js_bind<mb_shell::js::js_menu_data> {
524528
.fun<&mb_shell::js::js_menu_data::disabled>("disabled")
525529
.fun<&mb_shell::js::js_menu_data::wID>("wID")
526530
.fun<&mb_shell::js::js_menu_data::name_resid>("name_resid")
531+
.fun<&mb_shell::js::js_menu_data::origin_name>("origin_name")
527532
;
528533
}
529534
};
@@ -996,6 +1001,8 @@ template<> struct js_bind<mb_shell::js::win32> {
9961001
mod.class_<mb_shell::js::win32>("win32")
9971002
.constructor<>()
9981003
.static_fun<&mb_shell::js::win32::resid_from_string>("resid_from_string")
1004+
.static_fun<&mb_shell::js::win32::string_from_resid>("string_from_resid")
1005+
.static_fun<&mb_shell::js::win32::all_resids_from_string>("all_resids_from_string")
9991006
.static_fun<&mb_shell::js::win32::load_library>("load_library")
10001007
.static_fun<&mb_shell::js::win32::env>("env")
10011008
.static_fun<&mb_shell::js::win32::load_file_icon>("load_file_icon")

src/shell/script/binding_types.cc

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,8 +258,8 @@ js_menu_data menu_item_controller::data() {
258258

259259
data.wID = item->item.wID;
260260
data.name_resid = item->item.name_resid;
261-
262261
data.disabled = item->item.disabled;
262+
data.origin_name = item->item.origin_name;
263263

264264
return data;
265265
}
@@ -1384,4 +1384,11 @@ void menu_controller::append_widget_after(
13841384

13851385
m->update_icon_width();
13861386
}
1387+
std::string win32::string_from_resid(std::string str) {
1388+
return res_string_loader::string_from_id_string(str);
1389+
1390+
}
1391+
std::vector<std::string> win32::all_resids_from_string(std::string str) {
1392+
return res_string_loader::get_all_ids_of_string(utf8_to_wstring(str));
1393+
}
13871394
} // namespace mb_shell::js

0 commit comments

Comments
 (0)