Skip to content

Commit a69fa44

Browse files
committed
Merge branch 'win11_shellcontextmenu_fix' into main
2 parents fb838f2 + 0799ae9 commit a69fa44

File tree

12 files changed

+165
-123
lines changed

12 files changed

+165
-123
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# PBO Manager change log
22

3+
## Version 1.4.0
4+
- [Fix] Sometimes the explorer context menu was crasing explorer.exe
5+
36
## Version 1.4.0
47
- [Feature] Dedicated executable `pboc.exe` for usage in scripts
58

pboe/CMakeLists.txt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,14 @@ set(PROJECT_SOURCES
1010
add_library(pboe MODULE ${PROJECT_SOURCES})
1111
target_link_libraries(pboe PRIVATE shlwapi)
1212
target_compile_definitions(pboe PRIVATE
13-
PBOM_EXECUTABLE="pbom.exe")
13+
PBOM_EXECUTABLE="pbom.exe"
14+
UNICODE)
1415

1516
add_library(pboe_s STATIC ${PROJECT_SOURCES})
1617
target_link_libraries(pboe_s PRIVATE shlwapi)
1718
target_compile_definitions(pboe_s PRIVATE
18-
PBOM_EXECUTABLE="pbom.exe")
19+
PBOM_EXECUTABLE="pbom.exe"
20+
UNICODE)
1921

2022
if(MSVC)
2123
set(CMAKE_STATIC_LINKER_FLAGS

pboe/__test__/dllmain_test.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ namespace pboman3::test {
88
const HRESULT hr = DllRegisterServer();
99
ASSERT_TRUE(SUCCEEDED(hr));
1010

11-
const string path = Registry::getExecutablePath();
11+
const wstring path = Registry::getExecutablePath();
1212
ASSERT_FALSE(path.empty());
1313
}
1414

@@ -39,13 +39,13 @@ namespace pboman3::test {
3939

4040
path binaryDir(BINARY_DIR);
4141
binaryDir = binaryDir.lexically_normal();
42-
const string exe = binaryDir.parent_path().append("pbom").append("pbom.exe").string();
43-
const string dll = binaryDir.append("pboe.dll").string();
42+
const wstring exe = binaryDir.parent_path().append("pbom").append("pbom.exe").wstring();
43+
const wstring dll = binaryDir.append("pboe.dll").wstring();
4444

4545
const HRESULT hr = Registry::registerServer(exe, dll);
4646
ASSERT_TRUE(SUCCEEDED(hr));
4747

48-
const string path = Registry::getExecutablePath();
48+
const wstring path = Registry::getExecutablePath();
4949
ASSERT_FALSE(path.empty());
5050
}
5151

pboe/contextmenu.cpp

Lines changed: 54 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -65,17 +65,19 @@ namespace pboman3 {
6565

6666
if (sel == SelectionMode::Files) {
6767
if (selectedPaths_->size() == 1) {
68-
TCHAR textItem1[] = "Unpack to...";
68+
TCHAR textItem1[] = L"Unpack to...";
6969
insertMenuItem(idCmdFirst + idUnpackWithPrompt, textItem1);
7070

71-
string textItem2 = "Unpack as \"" + selectedPaths_->at(0)
72-
.filename().replace_extension().string() + "\"";
71+
wstring textItem2 = L"Unpack as \"" + selectedPaths_->at(0)
72+
.filename().replace_extension().wstring() +
73+
L"\"";
7374
insertMenuItem(idCmdFirst + idUnpackToCwd, textItem2.data());
7475
} else {
75-
TCHAR textItem1[] = "Unpack to...";
76+
TCHAR textItem1[] = L"Unpack to...";
7677
insertMenuItem(idCmdFirst + idUnpackWithPrompt, textItem1);
7778

78-
string textItem2 = "Unpack in \"" + selectedPaths_->at(0).parent_path().filename().string() + "\"";
79+
wstring textItem2 = L"Unpack in \"" + selectedPaths_->at(0).parent_path().filename().wstring() +
80+
L"\"";
7981
insertMenuItem(idCmdFirst + idUnpackToCwd, textItem2.data());
8082
}
8183

@@ -84,16 +86,17 @@ namespace pboman3 {
8486
hr = MAKE_HRESULT(SEVERITY_SUCCESS, 0, static_cast<USHORT>(lastUsedMenuIndex + 1));
8587
} else if (sel == SelectionMode::Folders) {
8688
if (selectedPaths_->size() == 1) {
87-
TCHAR textItem1[] = "Pack to...";
89+
TCHAR textItem1[] = L"Pack to...";
8890
insertMenuItem(idCmdFirst + idPackWithPrompt, textItem1);
8991

90-
string textItem2 = "Pack as \"" + selectedPaths_->at(0).filename().string() + ".pbo\"";
92+
wstring textItem2 = L"Pack as \"" + selectedPaths_->at(0).filename().wstring() + L".pbo\"";
9193
insertMenuItem(idCmdFirst + idPackToCwd, textItem2.data());
9294
} else {
93-
TCHAR textItem3[] = "Pack to...";
95+
TCHAR textItem3[] = L"Pack to...";
9496
insertMenuItem(idCmdFirst + idPackWithPrompt, textItem3);
9597

96-
string textItem4 = "Pack in \"" + selectedPaths_->at(0).parent_path().filename().string() + "\"";
98+
wstring textItem4 = L"Pack in \"" + selectedPaths_->at(0).parent_path().filename().wstring() +
99+
L"\"";
97100
insertMenuItem(idCmdFirst + idPackToCwd, textItem4.data());
98101
}
99102

@@ -111,31 +114,52 @@ namespace pboman3 {
111114

112115
HRESULT hr = E_FAIL;
113116

114-
if (pici->cbSize == sizeof CMINVOKECOMMANDINFOEX
115-
&& pici->fMask & CMIC_MASK_UNICODE) {
117+
UWORD idCmd = -1;
118+
path directory;
119+
if (pici->cbSize == sizeof CMINVOKECOMMANDINFOEX && pici->fMask & CMIC_MASK_UNICODE) {
116120
const auto piciw = reinterpret_cast<CMINVOKECOMMANDINFOEX*>(pici);
121+
//click on a Desktop item or on an item in Explorer right pane
117122
if (HIWORD(piciw->lpVerbW) == 0) {
123+
//means piciw contains idCmd, otherwise would mean pici contains a verb
124+
idCmd = LOWORD(piciw->lpVerb);
125+
directory.append(wstring(piciw->lpDirectoryW, lstrlen(piciw->lpDirectoryW)));
126+
}
127+
} else {
128+
//click on a folder in Explorer left pane
129+
if (HIWORD(pici->lpVerb) == 0) {
118130
//means pici contains idCmd, otherwise would mean pici contains a verb
119-
const auto idCmd = LOWORD(pici->lpVerb);
120-
switch (idCmd) {
121-
case idUnpackWithPrompt:
122-
hr = executable_->unpackFiles(pici->lpDirectory, *selectedPaths_);
123-
break;
124-
case idUnpackToCwd:
125-
hr = executable_->unpackFiles(pici->lpDirectory, *selectedPaths_, pici->lpDirectory);
126-
break;
127-
case idPackWithPrompt:
128-
hr = executable_->packFolders(pici->lpDirectory, *selectedPaths_);
129-
break;
130-
case idPackToCwd:
131-
hr = executable_->packFolders(pici->lpDirectory, *selectedPaths_, pici->lpDirectory);
132-
break;
133-
default:
134-
break;
131+
idCmd = LOWORD(pici->lpVerb);
132+
if (pici->lpDirectory) {
133+
directory.append(string(pici->lpDirectory, strlen(pici->lpDirectory)));
134+
} else {
135+
if (selectedPaths_->size() == 1
136+
&& is_directory(selectedPaths_->at(0))
137+
&& selectedPaths_->at(0).has_parent_path()) {
138+
directory = selectedPaths_->at(0).parent_path();
139+
} else {
140+
idCmd = -1;
141+
}
135142
}
136143
}
137144
}
138145

146+
switch (idCmd) {
147+
case idUnpackWithPrompt:
148+
hr = executable_->unpackFiles(directory, *selectedPaths_);
149+
break;
150+
case idUnpackToCwd:
151+
hr = executable_->unpackFiles(directory, *selectedPaths_, directory);
152+
break;
153+
case idPackWithPrompt:
154+
hr = executable_->packFolders(directory, *selectedPaths_);
155+
break;
156+
case idPackToCwd:
157+
hr = executable_->packFolders(directory, *selectedPaths_, directory);
158+
break;
159+
default:
160+
break;
161+
}
162+
139163
return hr;
140164
}
141165

@@ -229,15 +253,15 @@ namespace pboman3 {
229253
}
230254

231255
void ContextMenu::insertRootItem(HMENU hmenu, UINT indexMenu) const {
232-
const MENUITEMINFO menu = makeRootItem(PBOM_PROJECT_NAME, subMenu_);
256+
const MENUITEMINFO menu = makeRootItem(W(PBOM_PROJECT_NAME), subMenu_);
233257
InsertMenuItem(hmenu, indexMenu, TRUE, &menu);
234258
}
235259

236260
shared_ptr<MenuIcon> ContextMenu::loadRootIcon() const {
237-
const string exePath = Registry::getExecutablePath();
261+
const wstring exePath = Registry::getExecutablePath();
238262
if (exePath.empty() || !is_regular_file(exePath))
239263
return nullptr;
240264

241-
return make_shared<MenuIcon>(exePath.data());
265+
return make_shared<MenuIcon>(exePath);
242266
}
243267
}

pboe/dllmain.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ STDAPI DllRegisterServer() {
5959
const path dllPath(buf);
6060
const path exePath = dllPath.parent_path().append(PBOM_EXECUTABLE);
6161

62-
hr = pboman3::Registry::registerServer(exePath.string(), dllPath.string());
62+
hr = pboman3::Registry::registerServer(exePath.wstring(), dllPath.wstring());
6363
} else {
6464
hr = HRESULT_FROM_WIN32(GetLastError());
6565
}
@@ -85,5 +85,5 @@ STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv) {
8585
}
8686

8787
STDAPI DllCanUnloadNow() {
88-
return pboman3::DllRefCount == 0 ? S_FALSE : S_OK;
88+
return pboman3::DllRefCount == 0 ? S_OK : S_FALSE;
8989
}

pboe/dllmain.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
#pragma once
22

3-
#define PBOM_SHELL_CLSID "{dd2a27fa-7c7f-4b50-9b54-836af42fb64d}"
4-
#define PBOM_SHELL_PROGID "pboman3_pbo"
3+
#define PBOM_SHELL_CLSID L"{dd2a27fa-7c7f-4b50-9b54-836af42fb64d}"
4+
#define PBOM_SHELL_PROGID L"pboman3_pbo"
5+
#define __W(T) L##T
6+
#define W(T) __W(T)
7+
58

69
namespace pboman3 {
710
void DllAddRef();

pboe/executable.cpp

Lines changed: 32 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,17 @@ namespace pboman3 {
1010
constexpr size_t additionalCharsReserve = 20;
1111

1212
std::shared_ptr<Executable> Executable::fromRegistry() {
13-
std::string path = Registry::getExecutablePath();
13+
wstring path = Registry::getExecutablePath();
1414
return make_shared<Executable>(path);
1515
}
1616

17-
Executable::Executable(std::string executablePath)
17+
Executable::Executable(wstring executablePath)
1818
: executablePath_(std::move(executablePath)) {
1919
}
2020

21-
HRESULT Executable::unpackFiles(const LPCSTR cwd, const vector<path>& files, const string& outputDir) const {
22-
std::string argv;
23-
reserveArgvSize(argv, files, outputDir.size() + additionalCharsReserve);
21+
HRESULT Executable::unpackFiles(const path& cwd, const vector<path>& files, const path& outputDir) const {
22+
wstring argv;
23+
reserveArgvSize(argv, files, outputDir.wstring().length() + additionalCharsReserve);
2424

2525
appendUnpackCommand(argv);
2626
appendPaths(argv, files);
@@ -30,8 +30,8 @@ namespace pboman3 {
3030
return hr;
3131
}
3232

33-
HRESULT Executable::unpackFiles(const LPCSTR cwd, const vector<path>& files) const {
34-
std::string argv;
33+
HRESULT Executable::unpackFiles(const path& cwd, const vector<path>& files) const {
34+
wstring argv;
3535
reserveArgvSize(argv, files, additionalCharsReserve);
3636

3737
appendUnpackCommand(argv);
@@ -42,9 +42,9 @@ namespace pboman3 {
4242
return hr;
4343
}
4444

45-
HRESULT Executable::packFolders(const LPCSTR cwd, const vector<path>& folders, const string& outputDir) const {
46-
std::string argv;
47-
reserveArgvSize(argv, folders, outputDir.size() + additionalCharsReserve);
45+
HRESULT Executable::packFolders(const path& cwd, const vector<path>& folders, const path& outputDir) const {
46+
wstring argv;
47+
reserveArgvSize(argv, folders, outputDir.wstring().length() + additionalCharsReserve);
4848

4949
appendPackCommand(argv);
5050
appendPaths(argv, folders);
@@ -54,8 +54,8 @@ namespace pboman3 {
5454
return hr;
5555
}
5656

57-
HRESULT Executable::packFolders(const LPCSTR cwd, const vector<path>& folders) const {
58-
std::string argv;
57+
HRESULT Executable::packFolders(const path& cwd, const vector<path>& folders) const {
58+
wstring argv;
5959
reserveArgvSize(argv, folders, additionalCharsReserve);
6060

6161
appendPackCommand(argv);
@@ -71,7 +71,7 @@ namespace pboman3 {
7171
&& is_regular_file(executablePath_);
7272
}
7373

74-
void Executable::reserveArgvSize(string& argv, vector<path> items, size_t additionalSymbols) {
74+
void Executable::reserveArgvSize(wstring& argv, vector<path> items, size_t additionalSymbols) {
7575
const auto reserve = std::accumulate(items.begin(), items.end(), static_cast<std::size_t>(0),
7676
[](ULONG64 sum, const path& p) {
7777
//each item will need 3 additional symbols: 2 quotes and separating whitespace
@@ -80,8 +80,9 @@ namespace pboman3 {
8080
argv.reserve(reserve + additionalSymbols);
8181
}
8282

83-
HRESULT Executable::shellExecute(LPCSTR cwd, const std::string& argv) const {
84-
HINSTANCE hinst = ShellExecute(nullptr, "open", executablePath_.c_str(), argv.c_str(), cwd, SW_SHOWNORMAL);
83+
HRESULT Executable::shellExecute(const path& cwd, const wstring& argv) const {
84+
const HINSTANCE hinst = ShellExecute(nullptr, L"open", executablePath_.c_str(), argv.c_str(), cwd.c_str(),
85+
SW_SHOWNORMAL);
8586
if (reinterpret_cast<INT_PTR>(hinst) > 32) {
8687
return S_OK;
8788
}
@@ -90,24 +91,30 @@ namespace pboman3 {
9091
return err;
9192
}
9293

93-
void Executable::appendPaths(string& argv, const vector<path>& paths) {
94+
void Executable::appendPaths(wstring& argv, const vector<path>& paths) {
9495
for (const path& item : paths)
95-
argv.append(" \"").append(item.string()).append("\"");
96+
argv.append(L" \"").append(item.wstring()).append(L"\"");
9697
}
9798

98-
void Executable::appendPackCommand(std::string& argv) {
99-
argv.append("pack");
99+
void Executable::appendPackCommand(wstring& argv) {
100+
argv.append(L"pack");
100101
}
101102

102-
void Executable::appendUnpackCommand(std::string& argv) {
103-
argv.append("unpack");
103+
void Executable::appendUnpackCommand(wstring& argv) {
104+
argv.append(L"unpack");
104105
}
105106

106-
void Executable::appendOutputDir(std::string& argv, const std::string& outputDir) {
107-
argv.append(" -o \"").append(outputDir).append("\"");
107+
void Executable::appendOutputDir(wstring& argv, const path& outputDir) {
108+
const bool needEscaping = outputDir != outputDir.root_path();
109+
argv.append(L" -o ");
110+
if (needEscaping)
111+
argv.append(L"\"");
112+
argv.append(outputDir);
113+
if (needEscaping)
114+
argv.append(L"\"");
108115
}
109116

110-
void Executable::appendPrompt(std::string& argv) {
111-
argv.append(" -p");
117+
void Executable::appendPrompt(wstring& argv) {
118+
argv.append(L" -p");
112119
}
113120
}

pboe/executable.h

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,33 +14,33 @@ namespace pboman3 {
1414
public :
1515
static shared_ptr<Executable> fromRegistry();
1616

17-
Executable(string executablePath);
17+
Executable(wstring executablePath);
1818

19-
HRESULT unpackFiles(LPCSTR cwd, const vector<path>& files, const string& outputDir) const;
19+
HRESULT unpackFiles(const path& cwd, const vector<path>& files, const path& outputDir) const;
2020

21-
HRESULT unpackFiles(LPCSTR cwd, const vector<path>& files) const;
21+
HRESULT unpackFiles(const path& cwd, const vector<path>& files) const;
2222

23-
HRESULT packFolders(LPCSTR cwd, const vector<path>& folders, const string& outputDir) const;
23+
HRESULT packFolders(const path& cwd, const vector<path>& folders, const path& outputDir) const;
2424

25-
HRESULT packFolders(LPCSTR cwd, const vector<path>& folders) const;
25+
HRESULT packFolders(const path& cwd, const vector<path>& folders) const;
2626

2727
bool isValid() const;
2828

2929
private:
30-
const string executablePath_;
30+
const wstring executablePath_;
3131

32-
static void reserveArgvSize(string& argv, vector<path> items, size_t additionalSymbols);
32+
static void reserveArgvSize(wstring& argv, vector<path> items, size_t additionalSymbols);
3333

34-
HRESULT shellExecute(LPCSTR cwd, const string& argv) const;
34+
HRESULT shellExecute(const path& cwd, const wstring& argv) const;
3535

36-
static void appendPaths(string& argv, const vector<path>& paths);
36+
static void appendPaths(wstring& argv, const vector<path>& paths);
3737

38-
static void appendPackCommand(string& argv);
38+
static void appendPackCommand(wstring& argv);
3939

40-
static void appendUnpackCommand(string& argv);
40+
static void appendUnpackCommand(wstring& argv);
4141

42-
static void appendOutputDir(string& argv, const string& outputDir);
42+
static void appendOutputDir(wstring& argv, const path& outputDir);
4343

44-
static void appendPrompt(string& argv);
44+
static void appendPrompt(wstring& argv);
4545
};
4646
}

0 commit comments

Comments
 (0)