1 /*
2 * The MIT License (MIT)
3 *
4 * Copyright (c) 2018 Nathan Osman
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to
8 * deal in the Software without restriction, including without limitation the
9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 * sell copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22 * IN THE SOFTWARE.
23 **/
24
25 #include <strsafe.h>
26 #include <windows.h>
27 #include <shellapi.h>
28
29 #include <nitroshare/apiutil.h>
30
31 #include "contextmenu.h"
32
ContextMenu()33 ContextMenu::ContextMenu()
34 : mRefCount(1)
35 {
36 }
37
QueryInterface(REFIID riid,LPVOID * ppvObject)38 STDMETHODIMP ContextMenu::QueryInterface(REFIID riid, LPVOID *ppvObject)
39 {
40 if (!ppvObject) {
41 return E_INVALIDARG;
42 }
43
44 *ppvObject = NULL;
45
46 if (IsEqualIID(riid, IID_IUnknown)) {
47 OutputDebugString(TEXT("ContextMenu queried for IUnknown"));
48 *ppvObject = this;
49 } else if (IsEqualIID(riid, IID_IShellExtInit)) {
50 OutputDebugString(TEXT("ContextMenu queried for IShellExtInit"));
51 *ppvObject = (IShellExtInit*) this;
52 } else if (IsEqualIID(riid, IID_IContextMenu)) {
53 OutputDebugString(TEXT("ContextMenu queried for IContextMenu"));
54 *ppvObject = (IContextMenu*) this;
55 } else {
56 return E_NOINTERFACE;
57 }
58
59 AddRef();
60 return S_OK;
61 }
62
STDMETHODIMP_(ULONG)63 STDMETHODIMP_(ULONG) ContextMenu::AddRef()
64 {
65 InterlockedIncrement(&mRefCount);
66 return mRefCount;
67 }
68
STDMETHODIMP_(ULONG)69 STDMETHODIMP_(ULONG) ContextMenu::Release()
70 {
71 ULONG refCount = InterlockedDecrement(&mRefCount);
72 if (!mRefCount) {
73 delete this;
74 }
75 return refCount;
76 }
77
Initialize(PCIDLIST_ABSOLUTE pidlFolder,IDataObject * pdtobj,HKEY hkeyProgID)78 STDMETHODIMP ContextMenu::Initialize(PCIDLIST_ABSOLUTE pidlFolder, IDataObject *pdtobj, HKEY hkeyProgID)
79 {
80 FORMATETC formatetc = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
81 STGMEDIUM medium;
82
83 HRESULT hResult = pdtobj->GetData(&formatetc, &medium);
84 if (hResult != S_OK) {
85 return hResult;
86 }
87
88 // Lock the memory for reading the list of files
89 HDROP hDrop = (HDROP) GlobalLock(medium.hGlobal);
90 if (hDrop == NULL) {
91 return E_FAIL;
92 }
93
94 // Read the filenames into the list
95 mFilenames.clear();
96 UINT nFiles = DragQueryFile(hDrop, 0xFFFFFFFF, NULL, 0);
97 for (UINT i = 0; i < nFiles; ++i) {
98 WCHAR filename[MAX_PATH];
99 if (!DragQueryFileW(hDrop, i, filename, MAX_PATH)) {
100 hResult = E_FAIL;
101 }
102 mFilenames.append(QString::fromWCharArray(filename));
103 }
104
105 // Free the memory
106 GlobalUnlock(medium.hGlobal);
107
108 return hResult;
109 }
110
GetCommandString(UINT_PTR idCmd,UINT uFlags,UINT * pwReserved,LPSTR pszName,UINT cchMax)111 STDMETHODIMP ContextMenu::GetCommandString(UINT_PTR idCmd, UINT uFlags, UINT *pwReserved, LPSTR pszName, UINT cchMax)
112 {
113 switch (uFlags) {
114 case GCS_HELPTEXTA:
115 StringCchCopyA(pszName, cchMax, "Send the selected items with NitroShare");
116 break;
117 case GCS_HELPTEXTW:
118 StringCchCopyW((LPWSTR) pszName, cchMax, L"Send the selected items with NitroShare");
119 break;
120 case GCS_VALIDATEA:
121 case GCS_VALIDATEW:
122 break;
123 case GCS_VERBA:
124 StringCchCopyA(pszName, cchMax, "Send");
125 break;
126 case GCS_VERBW:
127 StringCchCopyW((LPWSTR) pszName, cchMax, L"Send");
128 break;
129 }
130
131 return S_OK;
132 }
133
InvokeCommand(LPCMINVOKECOMMANDINFO pici)134 STDMETHODIMP ContextMenu::InvokeCommand(LPCMINVOKECOMMANDINFO pici)
135 {
136 if (!IS_INTRESOURCE(pici->lpVerb)) {
137 return E_INVALIDARG;
138 }
139
140 // Only index 0 is valid
141 if (LOWORD(pici->lpVerb)) {
142 return E_INVALIDARG;
143 }
144
145 // Attempt to send the items
146 QVariantMap params{
147 { "items", mFilenames }
148 };
149 QVariant ret;
150 if (!ApiUtil::sendRequest("senditemsui", params, ret, nullptr)) {
151 MessageBox(NULL, TEXT("Unable to send the selected items."), TEXT("Error"), MB_ICONERROR);
152 return E_FAIL;
153 }
154
155 return S_OK;
156 }
157
QueryContextMenu(HMENU hmenu,UINT indexMenu,UINT idCmdFirst,UINT idCmdLast,UINT uFlags)158 STDMETHODIMP ContextMenu::QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
159 {
160 if (uFlags & CMF_DEFAULTONLY) {
161 return MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, 0);
162 }
163
164 UINT uMenuFlags = MF_BYPOSITION;
165 TCHAR caption[2048];
166
167 // Set the flags and caption according to the current status
168 QVariant ret;
169 if (!ApiUtil::sendRequest("version", QVariantMap(), ret, nullptr)) {
170 uMenuFlags |= MF_GRAYED;
171 StringCchCopy(caption, sizeof(caption), TEXT("NitroShare is not running"));
172 } else if (mFilenames.size() == 1) {
173 StringCchCopy(caption, sizeof(caption), TEXT("Send item with NitroShare"));
174 } else {
175 StringCbPrintf(caption, sizeof(caption), TEXT("Send %d items with NitroShare"), mFilenames.size());
176 }
177
178 // Insert the new menu item
179 if (!InsertMenu(hmenu, indexMenu, uMenuFlags, idCmdFirst, caption)) {
180 return E_FAIL;
181 }
182
183 return MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, 1);
184 }
185