www.pudn.com > clamwin-0.85.1-src.zip > ShellExtImpl.cpp
//----------------------------------------------------------------------------- // Name: ShellExtImpl.cpp // Product: ClamWin Antivirus // // Author: alch [alch at users dot sourceforge dot net] // // Created: 2004/19/03 // Copyright: Copyright alch (c) 2004 // Licence: // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. //----------------------------------------------------------------------------- #include#include #include #include #include "ShellExt.h" #define ResultFromShort(i) ResultFromScode(MAKE_SCODE(SEVERITY_SUCCESS, 0, (USHORT)(i))) extern HINSTANCE g_hmodThisDll; // Handle to this DLL itself. int _tcsreplace(LPTSTR sz, TCHAR chr, TCHAR repl_chr) { int count = 0; for (; *sz!=_T('\0'); sz++) if (*sz == chr) { *sz = repl_chr; count++; } return count; } // // FUNCTION: CShellExt::Initialize(LPCITEMIDLIST, LPDATAOBJECT, HKEY) // // PURPOSE: Called by the shell when initializing a context menu or property // sheet extension. // // PARAMETERS: // pIDFolder - Specifies the parent folder // pDataObj - Spefifies the set of items selected in that folder. // hRegKey - Specifies the type of the focused item in the selection. // // RETURN VALUE: // // NOERROR in all cases. // // COMMENTS: Note that at the time this function is called, we don't know // (or care) what type of shell extension is being initialized. // It could be a context menu or a property sheet. // STDMETHODIMP CShellExt::Initialize(LPCITEMIDLIST pIDFolder, LPDATAOBJECT pDataObj, HKEY hRegKey) { HRESULT hres = E_FAIL; STGMEDIUM medium; FORMATETC fmte = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; TCHAR szPath[MAX_PATH]; INT len, numFiles; // Initialize can be called more than once if (m_pDataObj) m_pDataObj->Release(); // duplicate the object pointer and registry handle if (pDataObj) { m_pDataObj = pDataObj; pDataObj->AddRef(); } // use the given IDataObject to get a list of filenames (CF_HDROP) hres = pDataObj->GetData(&fmte, &medium); if(FAILED(hres)) return E_FAIL; // find out how many files the user selected // not more than 250 though, otherwise explorer crashes numFiles = DragQueryFile((HDROP)medium.hGlobal, (UINT)-1, NULL, 0); if(numFiles > 250) return E_FAIL; // free old path (just in case) if(m_szPath) { delete [] m_szPath; m_szPath = NULL; } // allocate memory for our combined path // add length of [ --path=""] INT cbPath = (MAX_PATH + 10) * numFiles; m_szPath = new TCHAR[cbPath]; m_szPath[0] = _T('\0'); for(int i = 0; i < numFiles; i++) { DragQueryFile((HDROP)medium.hGlobal, i, szPath, sizeof(szPath)); // convert \ to / so cygwin doesn't go crazy (particularly over UNC names) _tcsreplace(szPath, _T('\\'), _T('/')); len = _tcslen(szPath); // remove last slash from the scanning path if(szPath[len-1] == _T('/')) szPath[len-1] = _T('\0'); _tcsncat(m_szPath, " --path=\"", cbPath); _tcsncat(m_szPath, szPath, cbPath); _tcsncat(m_szPath, "\"", cbPath); } ::ReleaseStgMedium(&medium); return NOERROR; } // // FUNCTION: CShellExt::QueryContextMenu(HMENU, UINT, UINT, UINT, UINT) // // PURPOSE: Called by the shell just before the context menu is displayed. // This is where you add your specific menu items. // // PARAMETERS: // hMenu - Handle to the context menu // indexMenu - Index of where to begin inserting menu items // idCmdFirst - Lowest value for new menu ID's // idCmtLast - Highest value for new menu ID's // uFlags - Specifies the context of the menu event // // RETURN VALUE: // // // COMMENTS: // // The menu text STDMETHODIMP CShellExt::QueryContextMenu(HMENU hMenu,UINT indexMenu,UINT idCmdFirst,UINT idCmdLast,UINT uFlags) { UINT idCmd = idCmdFirst; HRESULT hr = E_INVALIDARG; // Seperator ::InsertMenu(hMenu, indexMenu++, MF_SEPARATOR|MF_BYPOSITION, 0, NULL); ::InsertMenu(hMenu, indexMenu++, MF_STRING|MF_BYPOSITION, idCmd++, _T("Scan For Viruses With ClamWin")); // Seperator ::InsertMenu(hMenu, indexMenu++, MF_SEPARATOR|MF_BYPOSITION, 0, NULL); return ResultFromShort(idCmd-idCmdFirst); //Must return number of menu //items we added. } BOOL CShellExt::Scan(HWND hwnd) { DWORD len; if(!m_szPath || !_tcslen(m_szPath)) { MessageBox(hwnd, _T("Error: Unable to retrieve Path."), _T("ClamWin"), MB_OK | MB_ICONERROR); return FALSE; } DWORD dwType, cbData; DWORD szCmdSize = MAX_PATH*3 + _tcslen(m_szPath); PTCHAR szCmd = new TCHAR[szCmdSize]; TCHAR szClamWinPath[MAX_PATH] = _T(""); TCHAR szParams[MAX_PATH*2] = _T(""); TCHAR szPathExpanded[MAX_PATH], szParamsExpanded[MAX_PATH*2]; // get path to ClamWin // Try registry first HKEY hKey; // try in hkey_current_user if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CURRENT_USER, _T("Software\\ClamWin"), 0, KEY_READ, &hKey)) { cbData = sizeof(szClamWinPath); RegQueryValueEx(hKey, _T("Path"), NULL, &dwType, (PBYTE)szClamWinPath, &cbData); CloseHandle(hKey); } // try in hkey_local_machine if failed if (!_tcslen(szClamWinPath) && (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("Software\\ClamWin"), 0, KEY_READ, &hKey))) { cbData = sizeof(szClamWinPath); RegQueryValueEx(hKey, _T("Path"), NULL, &dwType, (PBYTE)szClamWinPath, &cbData); CloseHandle(hKey); } if(!_tcslen(szClamWinPath)) { // could not retrieve from registry // try in the same folder as the shell extension TCHAR szModule[MAX_PATH]; if(GetModuleFileName(NULL, szModule, sizeof(szModule))) { // get folder name _tsplitpath(szModule, NULL, szClamWinPath, NULL, NULL); } } len = _tcslen(szClamWinPath); if(!len) { MessageBox(hwnd, _T("Error: Unable to retrieve path to ClamWin. Please reinstall ClamWin"), _T("ClamWin"), MB_OK | MB_ICONERROR); delete [] szCmd; return FALSE; } // Expand Env ExpandEnvironmentStrings(szClamWinPath, szPathExpanded, sizeof(szPathExpanded)); len = _tcslen(szPathExpanded); // remove trailing slash if(szPathExpanded[len-1] == _T('\\')) szPathExpanded[len-1] = _T('\0'); _sntprintf(szCmd, szCmdSize, _T("\"%s\\%s\" --mode=scanner %s"), szPathExpanded, _T("ClamWin.exe"), m_szPath); // read optional params from registry if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CURRENT_USER, _T("Classes\\CLSID\\{65713842-C410-4f44-8383-BFE01A398C90}\\InProcServer32"), 0, KEY_READ, &hKey) || ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("Classes\\CLSID\\{65713842-C410-4f44-8383-BFE01A398C90}\\InProcServer32"), 0, KEY_READ, &hKey)) { cbData = sizeof(szParams); RegQueryValueEx(hKey, _T("params"), NULL, &dwType, (PBYTE)szParams, &cbData); CloseHandle(hKey); // append params if exist if (szParams[0] != _T('\0')) { // Expand Env ExpandEnvironmentStrings(szParams, szParamsExpanded, sizeof(szParamsExpanded)); _tcsncat(szCmd, _T(" "), sizeof(szCmd)); _tcsncat(szCmd, szParamsExpanded, sizeof(szCmd)); } } STARTUPINFO si; PROCESS_INFORMATION pi; ZeroMemory( &si, sizeof(si) ); si.cb = sizeof(si); ZeroMemory( &pi, sizeof(pi) ); // Start the child process. if( !CreateProcess( NULL, // No module name (use command line). szCmd, // Command line. NULL, // Process handle not inheritable. NULL, // Thread handle not inheritable. FALSE, // Set handle inheritance to FALSE. 0, // No creation flags. NULL, // Use parent's environment block. NULL, // Use parent's starting directory. &si, // Pointer to STARTUPINFO structure. &pi ) // Pointer to PROCESS_INFORMATION structure. ) { TCHAR szMsg[MAX_PATH*2]; _sntprintf(szMsg, sizeof(szMsg), "Error: Unable to execute command %s.", szCmd); MessageBox(hwnd, szMsg, "ClamWin", MB_OK | MB_ICONERROR); delete [] szCmd; return FALSE; } delete [] szCmd; // Close process and thread handles. CloseHandle( pi.hProcess ); CloseHandle( pi.hThread ); return TRUE; } // // FUNCTION: CShellExt::InvokeCommand(LPCMINVOKECOMMANDINFO) // // PURPOSE: Called by the shell after the user has selected on of the // menu items that was added in QueryContextMenu(). // // PARAMETERS: // lpcmi - Pointer to an CMINVOKECOMMANDINFO structure // // RETURN VALUE: HRESULT code signifying success or failure;NOERROR if no error // // // COMMENTS: // STDMETHODIMP CShellExt::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi) { HRESULT hr = E_INVALIDARG; //If HIWORD(lpcmi->lpVerb) then we have been called programmatically //and lpVerb is a command that should be invoked. Otherwise, the shell //has called us, and LOWORD(lpcmi->lpVerb) is the menu ID the user has //selected. Actually, it's (menu ID - idCmdFirst) from QueryContextMenu(). if (!HIWORD(lpcmi->lpVerb)) { UINT idCmd = LOWORD(lpcmi->lpVerb); switch (idCmd) { case 0: Scan(lpcmi->hwnd); break; } hr = NOERROR; } return hr; } // // FUNCTION: CShellExt::GetCommandString(UINT idCmd,UINT uFlags,UINT FAR *reserved,LPSTR pszName,UINT cchMax) // // PURPOSE: Called by the shell to retreive the cononical command name // or help text for a menu item added by a context menu extension handler // // PARAMETERS: // idCmd - Menu item ID offset // uFlags - Value specifying the type of information to retreive // *reserved - Pointer to reserved value // pszName - Pointer to buffer to receive name string or help text // cchMax - Buffer size // // RETURN VALUE: HRESULT code signifying success or failure;NOERROR if no error // // // COMMENTS: // STDMETHODIMP CShellExt::GetCommandString(UINT idCmd,UINT uFlags,UINT FAR *reserved,LPTSTR pszName,UINT cchMax) { switch (idCmd) { case 0: _tcsncpy(pszName, _T("ClamWin Antivirus"), cchMax); break; } return NOERROR; }