ExplorerCommand.cpp
1 #include "pch.h" 2 3 #include "ExplorerCommand.h" 4 #include "dllmain.h" 5 #include "Generated Files/resource.h" 6 7 #include "FileLocksmithLib/Constants.h" 8 #include "FileLocksmithLib/Settings.h" 9 #include "FileLocksmithLib/Trace.h" 10 11 #include <common/themes/icon_helpers.h> 12 #include <common/utils/process_path.h> 13 #include <common/utils/resources.h> 14 15 // Implementations of inherited IUnknown methods 16 17 IFACEMETHODIMP ExplorerCommand::QueryInterface(REFIID riid, void** ppv) 18 { 19 static const QITAB qit[] = { 20 QITABENT(ExplorerCommand, IExplorerCommand), 21 QITABENT(ExplorerCommand, IShellExtInit), 22 QITABENT(ExplorerCommand, IContextMenu), 23 { 0, 0 }, 24 }; 25 return QISearch(this, qit, riid, ppv); 26 } 27 28 IFACEMETHODIMP_(ULONG) ExplorerCommand::AddRef() 29 { 30 return ++m_ref_count; 31 } 32 33 IFACEMETHODIMP_(ULONG) ExplorerCommand::Release() 34 { 35 auto result = --m_ref_count; 36 if (result == 0) 37 { 38 delete this; 39 } 40 return result; 41 } 42 43 // Implementations of inherited IExplorerCommand methods 44 45 IFACEMETHODIMP ExplorerCommand::GetTitle(IShellItemArray* psiItemArray, LPWSTR* ppszName) 46 { 47 return SHStrDup(context_menu_caption.c_str(), ppszName); 48 } 49 50 IFACEMETHODIMP ExplorerCommand::GetIcon(IShellItemArray* psiItemArray, LPWSTR* ppszIcon) 51 { 52 std::wstring iconResourcePath = get_module_filename(); 53 iconResourcePath += L",-"; 54 iconResourcePath += std::to_wstring(IDI_FILELOCKSMITH); 55 return SHStrDup(iconResourcePath.c_str(), ppszIcon); 56 } 57 58 IFACEMETHODIMP ExplorerCommand::GetToolTip(IShellItemArray* psiItemArray, LPWSTR* ppszInfotip) 59 { 60 // No tooltip for now 61 return E_NOTIMPL; 62 } 63 64 IFACEMETHODIMP ExplorerCommand::GetCanonicalName(GUID* pguidCommandName) 65 { 66 *pguidCommandName = __uuidof(this); 67 return S_OK; 68 } 69 70 IFACEMETHODIMP ExplorerCommand::GetState(IShellItemArray* psiItemArray, BOOL fOkToBeSlow, EXPCMDSTATE* pCmdState) 71 { 72 *pCmdState = FileLocksmithSettingsInstance().GetEnabled() ? ECS_ENABLED : ECS_HIDDEN; 73 return S_OK; 74 } 75 76 IFACEMETHODIMP ExplorerCommand::Invoke(IShellItemArray* psiItemArray, IBindCtx* pbc) 77 { 78 return S_OK; 79 } 80 81 IFACEMETHODIMP ExplorerCommand::GetFlags(EXPCMDFLAGS* pFlags) 82 { 83 *pFlags = ECF_DEFAULT; 84 return S_OK; 85 } 86 87 IFACEMETHODIMP ExplorerCommand::EnumSubCommands(IEnumExplorerCommand** ppEnum) 88 { 89 *ppEnum = NULL; 90 return E_NOTIMPL; 91 } 92 93 // Implementations of inherited IShellExtInit methods 94 95 IFACEMETHODIMP ExplorerCommand::Initialize(PCIDLIST_ABSOLUTE pidlFolder, IDataObject* pdtobj, HKEY hkeyProgID) 96 { 97 m_data_obj = NULL; 98 99 if (!FileLocksmithSettingsInstance().GetEnabled()) 100 { 101 return E_FAIL; 102 } 103 104 if (pdtobj) 105 { 106 m_data_obj = pdtobj; 107 } 108 return S_OK; 109 } 110 111 // Implementations of inherited IContextMenu methods 112 113 IFACEMETHODIMP ExplorerCommand::QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags) 114 { 115 // Skip if disabled 116 if (!FileLocksmithSettingsInstance().GetEnabled()) 117 { 118 return E_FAIL; 119 } 120 121 if (FileLocksmithSettingsInstance().GetShowInExtendedContextMenu() && !(uFlags & CMF_EXTENDEDVERBS)) 122 { 123 return E_FAIL; 124 } 125 126 HRESULT hr = E_UNEXPECTED; 127 if (m_data_obj && !(uFlags & (CMF_DEFAULTONLY | CMF_VERBSONLY | CMF_OPTIMIZEFORINVOKE))) 128 { 129 wchar_t menuName[128] = { 0 }; 130 wcscpy_s(menuName, ARRAYSIZE(menuName), context_menu_caption.c_str()); 131 132 MENUITEMINFO mii; 133 mii.cbSize = sizeof(MENUITEMINFO); 134 mii.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_ID | MIIM_STATE; 135 mii.wID = idCmdFirst++; 136 mii.fType = MFT_STRING; 137 mii.dwTypeData = (PWSTR)menuName; 138 mii.fState = MFS_ENABLED; 139 140 // icon from file 141 HICON hIcon = static_cast<HICON>(LoadImage(globals::instance, MAKEINTRESOURCE(IDI_FILELOCKSMITH), IMAGE_ICON, 16, 16, 0)); 142 if (hIcon) 143 { 144 mii.fMask |= MIIM_BITMAP; 145 if (m_hbmpIcon == NULL) 146 { 147 m_hbmpIcon = CreateBitmapFromIcon(hIcon); 148 } 149 mii.hbmpItem = m_hbmpIcon; 150 DestroyIcon(hIcon); 151 } 152 153 if (!InsertMenuItem(hmenu, indexMenu, TRUE, &mii)) 154 { 155 m_etwTrace.UpdateState(true); 156 157 hr = HRESULT_FROM_WIN32(GetLastError()); 158 Trace::QueryContextMenuError(hr); 159 160 m_etwTrace.Flush(); 161 m_etwTrace.UpdateState(false); 162 } 163 else 164 { 165 hr = MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, 1); 166 } 167 } 168 169 return hr; 170 } 171 172 IFACEMETHODIMP ExplorerCommand::InvokeCommand(CMINVOKECOMMANDINFO* pici) 173 { 174 m_etwTrace.UpdateState(true); 175 176 HRESULT hr = E_FAIL; 177 178 if (FileLocksmithSettingsInstance().GetEnabled() && 179 pici && (IS_INTRESOURCE(pici->lpVerb)) && 180 (LOWORD(pici->lpVerb) == 0)) 181 { 182 Trace::Invoked(); 183 ipc::Writer writer; 184 185 if (HRESULT result = writer.start(); FAILED(result)) 186 { 187 Trace::InvokedRet(result); 188 m_etwTrace.Flush(); 189 m_etwTrace.UpdateState(false); 190 return result; 191 } 192 193 if (HRESULT result = LaunchUI(pici, &writer); FAILED(result)) 194 { 195 Trace::InvokedRet(result); 196 m_etwTrace.Flush(); 197 m_etwTrace.UpdateState(false); 198 return result; 199 } 200 201 IShellItemArray* shell_item_array; 202 hr = SHCreateShellItemArrayFromDataObject(m_data_obj, __uuidof(IShellItemArray), reinterpret_cast<void**>(&shell_item_array)); 203 if (SUCCEEDED(hr)) 204 { 205 DWORD num_items; 206 shell_item_array->GetCount(&num_items); 207 for (DWORD i = 0; i < num_items; i++) 208 { 209 IShellItem* item; 210 hr = shell_item_array->GetItemAt(i, &item); 211 if (SUCCEEDED(hr)) 212 { 213 LPWSTR file_path; 214 hr = item->GetDisplayName(SIGDN_FILESYSPATH, &file_path); 215 if (SUCCEEDED(hr)) 216 { 217 // TODO Aggregate items and send to UI 218 writer.add_path(file_path); 219 CoTaskMemFree(file_path); 220 } 221 222 item->Release(); 223 } 224 } 225 226 shell_item_array->Release(); 227 } 228 } 229 230 Trace::InvokedRet(hr); 231 232 m_etwTrace.Flush(); 233 m_etwTrace.UpdateState(false); 234 return hr; 235 } 236 237 IFACEMETHODIMP ExplorerCommand::GetCommandString(UINT_PTR idCmd, UINT uType, UINT* pReserved, CHAR* pszName, UINT cchMax) 238 { 239 return E_NOTIMPL; 240 } 241 242 HRESULT ExplorerCommand::s_CreateInstance(IUnknown* pUnkOuter, REFIID riid, void** ppvObject) 243 { 244 *ppvObject = NULL; 245 HRESULT hr = E_OUTOFMEMORY; 246 ExplorerCommand* pNew = new (std::nothrow) ExplorerCommand; 247 if (pNew) 248 { 249 hr = pNew->QueryInterface(riid, ppvObject); 250 pNew->Release(); 251 } 252 return hr; 253 } 254 255 ExplorerCommand::ExplorerCommand() 256 { 257 ++globals::ref_count; 258 context_menu_caption = GET_RESOURCE_STRING_FALLBACK(IDS_FILELOCKSMITH_CONTEXT_MENU_ENTRY, L"Unlock with File Locksmith"); 259 } 260 261 ExplorerCommand::~ExplorerCommand() 262 { 263 --globals::ref_count; 264 } 265 266 HRESULT ExplorerCommand::LaunchUI(CMINVOKECOMMANDINFO* pici, ipc::Writer* writer) 267 { 268 // Compute exe path 269 std::wstring exe_path = get_module_folderpath(globals::instance); 270 exe_path += L'\\'; 271 exe_path += constants::nonlocalizable::FileNameUIExe; 272 273 STARTUPINFO startupInfo; 274 ZeroMemory(&startupInfo, sizeof(STARTUPINFO)); 275 startupInfo.cb = sizeof(STARTUPINFO); 276 startupInfo.dwFlags = STARTF_USESHOWWINDOW; 277 278 if (pici) 279 { 280 startupInfo.wShowWindow = pici->nShow; 281 } 282 else 283 { 284 startupInfo.wShowWindow = SW_SHOWNORMAL; 285 } 286 287 PROCESS_INFORMATION processInformation; 288 std::wstring command_line = L"\""; 289 command_line += exe_path; 290 command_line += L"\"\0"; 291 292 CreateProcessW( 293 NULL, 294 command_line.data(), 295 NULL, 296 NULL, 297 TRUE, 298 0, 299 NULL, 300 NULL, 301 &startupInfo, 302 &processInformation); 303 304 // Discard handles 305 CloseHandle(processInformation.hProcess); 306 CloseHandle(processInformation.hThread); 307 308 return S_OK; 309 }