CShellExt::CShellExt() { instanceCount = 0; referenceCount++; } CShellExt::~CShellExt() { referenceCount--; } STDMETHODIMP CShellExt::QueryInterface(REFIID riid, LPVOID *ppv) { *ppv = nullptr; if(IsEqualIID(riid, IID_IShellExtInit) || IsEqualIID(riid, IID_IUnknown)) { *ppv = (IShellExtInit*)this; } else if(IsEqualIID(riid, IID_IContextMenu)) { *ppv = (IContextMenu*)this; } if(*ppv) { AddRef(); return NOERROR; } return E_NOINTERFACE; } STDMETHODIMP_(ULONG) CShellExt::AddRef() { return ++instanceCount; } STDMETHODIMP_(ULONG) CShellExt::Release() { if(--instanceCount) return instanceCount; delete this; return 0; } STDMETHODIMP CShellExt::Initialize(LPCITEMIDLIST pIDFolder, IDataObject *pDataObject, HKEY hRegKey) { fileList.reset(); if(pDataObject) { wchar_t filename[PATH_MAX]; STGMEDIUM medium; FORMATETC fe = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; HDROP hDrop; if(FAILED(pDataObject->GetData(&fe, &medium))) return E_INVALIDARG; hDrop = (HDROP)GlobalLock(medium.hGlobal); if(hDrop == NULL) return E_INVALIDARG; fileList.reset(); unsigned count = DragQueryFileW(hDrop, 0xffffffff, NULL, 0); for(unsigned i = 0; i < count; i++) { DragQueryFileW((HDROP)medium.hGlobal, i, filename, PATH_MAX); string name = (const char*)utf8_t(filename); name.transform("/", "\\"); if(name.endswith("\\")) name.rtrim<1>("\\"); fileList.append(name); } GlobalUnlock(medium.hGlobal); ReleaseStgMedium(&medium); } return S_OK; } STDMETHODIMP CShellExt::QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags) { settings.load(); unsigned idCmd = idCmdFirst; bool firstDefault = true; auto ruleIDs = matchedRules(); for(auto &ruleID : ruleIDs) { auto &rule = settings.rules(ruleID); if(idCmd < idCmdLast) { InsertMenuW(hMenu, indexMenu, MF_STRING | MF_BYPOSITION, idCmd++, (const wchar_t*)utf16_t(rule.name)); if(rule.defaultAction && firstDefault) { firstDefault = false; //there can be only one default menu item SetMenuDefaultItem(hMenu, indexMenu, TRUE); } indexMenu++; } } return MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(idCmd - idCmdFirst)); } STDMETHODIMP CShellExt::GetCommandString(UINT_PTR idCmd, UINT uFlags, LPUINT lpReserved, LPSTR pszName, UINT uMaxNameLen) { if(idCmd < settings.rules.size()) return S_OK; return E_INVALIDARG; } STDMETHODIMP CShellExt::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi) { if(HIWORD(lpcmi->lpVerb) != 0) return E_INVALIDARG; //ignore actual string verbs settings.load(); unsigned id = LOWORD(lpcmi->lpVerb); auto ruleIDs = matchedRules(); auto &rule = settings.rules(ruleIDs(id)); string nameID = fileList(0); string pathnameID = dir(nameID).rtrim<1>("\\"); string filenameID = notdir(nameID); string basenameID = notdir(nall::basename(nameID)); string extensionID = extension(nameID); string pathID = {"\"", pathnameID, "\""}; string fileID = {"\"", nameID, "\""}; string pathsID, filesID; for(auto &filename : fileList) pathsID.append("\"", dir(filename).rtrim<1>("\\"), "\"", " "); for(auto &filename : fileList) filesID.append("\"", filename, "\"", " "); pathsID.rtrim<1>(" "), filesID.rtrim<1>(" "); lstring params = rule.command.qsplit<1>(" "); params(1).replace("{name}", nameID); params(1).replace("{pathname}", pathnameID); params(1).replace("{filename}", filenameID); params(1).replace("{basename}", basenameID); params(1).replace("{extension}", extensionID); params(1).replace("{path}", pathID); params(1).replace("{file}", fileID); params(1).replace("{paths}", pathsID); params(1).replace("{files}", filesID); if((intptr_t)ShellExecuteW(NULL, L"open", utf16_t(params(0)), utf16_t(params(1)), utf16_t(pathnameID), SW_SHOWNORMAL) <= 32) { MessageBoxW(0, L"Error opening associated program.", L"kaijuu", MB_OK); } return S_OK; } vector CShellExt::matchedRules() { vector matched; if(fileList.size() == 0) return matched; for(unsigned id = 0; id < settings.rules.size(); id++) { auto &rule = settings.rules(id); if(rule.multiSelection == false && fileList.size() > 1) continue; bool proceed = true; for(auto &filename : fileList) { string name = filename; if(directory::exists(name)) { if(rule.matchFolders == false) { proceed = false; break; } } else { if(rule.matchFiles == false) { proceed = false; break; } } name = notdir(name); lstring patternList = rule.pattern.split(";"); bool found = false; for(auto &pattern : patternList) { if(name.wildcard(pattern)) { found = true; break; } } if(found == false) { proceed = false; break; } } if(proceed == false) continue; matched.append(id); } return matched; }