Introduction
DieShell is a Windows Explorer context menu extension implemented in C++ as a DLL. It provides a custom "Die" command that can be added to the context menu for selected files. This post explores the key components of the DieShell code and how you can use and extend it.
Key Components of DieShell
1. DLL Entry Point
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
switch (ul_reason_for_call) {
case DLL_PROCESS_ATTACH:
g_hModule = hModule;
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
The DllMain
function initializes the global module handle when the DLL is loaded. This is a standard entry point for DLLs.
2. Context Menu Command
class DieCommand : public RuntimeClass<RuntimeClassFlags<ClassicCom>, IExplorerCommand, IObjectWithSite> {
public:
IFACEMETHODIMP GetTitle(_In_opt_ IShellItemArray* items, _Outptr_result_nullonfailure_ PWSTR* name) {
*name = nullptr;
auto title = wil::make_cotaskmem_string_nothrow(L"Die");
RETURN_IF_NULL_ALLOC(title);
*name = title.release();
return S_OK;
}
};
The GetTitle
method provides the title "Die" for the context menu item. This method is crucial as it defines what the user will see in the context menu.
3. Retrieving the Icon
IFACEMETHODIMP GetIcon(_In_opt_ IShellItemArray* items, _Outptr_result_nullonfailure_ PWSTR* iconPath) {
*iconPath = nullptr;
PWSTR itemPath = nullptr;
if (items) {
DWORD count;
RETURN_IF_FAILED(items->GetItemAt(&count));
if (count > 0) {
ComPtr<IShellItem> item;
RETURN_IF_FAILED(items->GetItemAt(0, &item));
RETURN_IF_FAILED(item->GetDisplayName(SIGDN_FILESYSPATH, &itemPath));
wil::unique_cotaskmem_string itemPathCleanup(itemPath);
WCHAR modulePath[MAX_PATH];
if (GetModuleFileNameW(g_hModule, modulePath, ARRAYSIZE(modulePath))) {
PathRemoveFileSpecW(modulePath);
StringCchCatW(modulePath, ARRAYSIZE(modulePath), L"\\Die.exe");
auto iconPathStr = wil::make_cotaskmem_string_nothrow(modulePath);
if (iconPathStr) {
*iconPath = iconPathStr.release();
}
}
}
return *iconPath ? S_OK : E_FAIL;
}
The GetIcon
method sets the icon for the context menu item by constructing the path to "Die.exe". This provides a visual representation in the context menu.
4. Handling Command Execution
IFACEMETHODIMP Invoke(_In_opt_ IShellItemArray* selection, _In_opt_ IBindCtx*) noexcept try {
if (!selection) {
MessageBox(nullptr, L"Invalid argument", L"Debug Info", MB_OK);
return E_INVALIDARG;
}
DWORD count;
RETURN_IF_FAILED(selection->GetCount(&count));
if (count == 0) {
MessageBox(nullptr, L"No items to process", L"Debug Info", MB_OK);
return S_OK;
}
ComPtr<IShellItem> item;
RETURN_IF_FAILED(selection->GetItemAt(0, &item));
PWSTR filePath;
RETURN_IF_FAILED(item->GetDisplayName(SIGDN_FILESYSPATH, &filePath));
wil::unique_cotaskmem_string filePathCleanup(filePath);
std::wstring message = L"File path: " + std::wstring(filePath);
wchar_t dllDirectory[MAX_PATH];
GetModuleFileName(g_hModule>, dllDirectory, MAX_PATH);
PathRemoveFileSpec(dllDirectory);
std::wstring dieExePath = std::wstring(dllDirectory) + L"\\Die.exe";
if (GetFileAttributes(dieExePath.c_str()) == INVALID_FILE_ATTRIBUTES) {
MessageBox(nullptr, L"Die.exe not found", L"Error", MB_OK | MB_ICONERROR);
return E_FAIL;
}
std::wstring commandLineArgs = L"\"" + std::wstring(filePath) + L"\"";
if (!ShellExecute(nullptr, L"open", dieExePath.c_str(), commandLineArgs.c_str(), nullptr, SW_SHOWNORMAL)) {
MessageBox(nullptr, L"Failed to execute Die.exe", L"Error", MB_OK | MB_ICONERROR);
return E_FAIL;
}
return S_OK;
}
CATCH_RETURN();
The Invoke
method is triggered when the "Die" command is selected. It processes the selected file and launches "Die.exe" with the file path as an argument.
Conclusion
DieShell showcases how to create a custom context menu extension for Windows using C++. By understanding the code structure and methods used, developers can adapt this example to create their own context menu extensions and integrate custom functionality into the Windows Explorer shell.