/ src / modules / FileLocksmith / FileLocksmithExt / ExplorerCommand.cpp
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  }