2008年(909)
分类:
2008-05-06 21:52:28
下载源代码
一、前言
在 Windows 的资源管理器窗口中,我们见过 WinZIP,WinRAR
等软件能在文件或文件夹的默认快捷菜单中添加几个菜单项,它可以使用户无须进入软件内部而直接在视窗中进行压缩/解压操作,十分方便用户操作,这无疑是一个较好的应用模型,它就是我们所说的Shell扩展技术。本文将以一个普通的源代码统计程序为例来说明怎样实现Shell扩展技术。下面是程序的运行效果图:
图一 示例代码运行效果图一
图二 示例代码运行效果图二
二、实现原理
为了在Windows的任何视窗中扩展文件或文件夹的默认菜单,我们必须使Windows在显示快捷菜单加载我们的程序段,一般我们利用COM组件来达到这个目的。COM组件分为三种:进程内服务程序,本地服务程序,以及远程服务程序。要想让explorer加载并执行我们的代码,当然得使用进程内服务程序,它的表现形式是DLL,
DLL在加载后被映射到可执行程序的虚拟地址空间,我们向explorer提供一些接口,explorer将在显示快捷菜单时调用它们时,我们可以在那些接口中做一些我们想做的事,如添加快捷菜单,实现菜单项功能等等,从而实现Shell扩展了。?
至于源代码统计,则不难实现。这里我以C/C++风格的源代码为例,并应用一种最简单的统计规则,当统计文件时,我们将代码内容读入缓存,判断每一个字符是否为换行符(\n),若是,计数加1。当然我们是对文件夹进行统计更有意义,所以我们可以使用递归的方法遍历文件夹内所有文件,找出有效文件(这里我仅统计C/C 程序,所以只处理后缀名为.C、.CPP、.H
的文件),根据前面的方法一一统计即可求出文件夹内所有代码的总行数。
三、实现过程
////////////////////////////////////////////////////////////////////////////////////////////////////////// // 作用:获取源文件的代码行数 // 参数:1. pFilePath :输入参数,指定源文件的路径; // 2. lines:输出参数,获得源文件的代码行数。 STDMETHODIMP CCountLines::GetFileLines(BSTR *pFilePath, int *lines) { // 存放代码的总行数 int totalLine = 0; ? // 打开文件 HANDLE hFile = CreateFile((TCHAR *)pFilePath, ? GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if ((HANDLE)-1 == hFile) { *lines = -1; return S_FALSE; } // 开辟缓冲区存放文件内容 DWORD dwFileSize = GetFileSize(hFile, NULL); BYTE *lpBuffer = new BYTE[dwFileSize]; memset(lpBuffer, 0, dwFileSize); ? DWORD dwRead = 0; BOOL bReadFile = ReadFile(hFile, lpBuffer, dwFileSize, &dwRead, NULL); assert(bReadFile && dwRead == dwFileSize); ? // 我们这里仅用一个简单的统计规则,即以换行符(‘\n’)为一行代码结束的标记 for (unsigned i = 0; i < dwFileSize; i ) { if (lpBuffer[i] == ''\n'') { totalLine ; } } // 释放缓冲区 delete []lpBuffer; CloseHandle(hFile); // 保存代码行数 *lines = totalLine 1; ? return S_OK; }
HRESULT Initialize(LPCITEMIDLIST pidlFolder,LPDATAOBJECT lpdobj, HKEY hkeyProgID );我们将在这个函数里进行必要的初始化动作,例如保存文件名的完整路径,保存注册表的键值等。
// 在视窗的状态栏上显示命令说明,这里值得注意的是,我们需要对ASCII码和UNICODE码进行处理, // 以适应不同系统。 HRESULT GetCommandString( UINT idCmd, UINT uFlags, UINT *pwReserved, LPSTR pszName, UINT cchMax ); // 执行菜单明命令,在此可以实现具体的功能。 HRESULT InvokeCommand( LPCMINVOKECOMMANDINFO pici ); // 在这里增加快捷菜单 HRESULT QueryContextMenu( HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags );这里仅举例 InvokeCommand()的实现,其他请看源代码。
////////////////////////////////////////////////////////////////////////////////////////////////////////// // 作用:执行快捷菜单命令 // 参数:1. pici:包含命令信息的结构体 HRESULT CCountLines::InvokeCommand(LPCMINVOKECOMMANDINFO pici) { BOOL bEx = FALSE; BOOL bUnicode = FALSE;? if (pici->cbSize = sizeof(CMINVOKECOMMANDINFOEX)) { bEx = TRUE; if ((pici->fMask & CMIC_MASK_UNICODE)) { bUnicode = TRUE; } } // lpVerb参数有两种标识:如高位字非0,则为命令字串,否则低位提供了快捷菜单的偏移值。 if (!bUnicode && HIWORD(pici->lpVerb)) { if(StrCmpIA(pici->lpVerb, "Stat.")) { return E_FAIL; } } else if (bUnicode && HIWORD(((CMINVOKECOMMANDINFOEX *) pici)->lpVerbW)) { if(StrCmpIW(((CMINVOKECOMMANDINFOEX *)pici)->lpVerbW, L"Stat.")) { return E_FAIL; } } else if (LOWORD(pici->lpVerb) != IDM_SRC_COUNT) { return E_FAIL; } else { assert(0 == HIWORD(pici->lpVerb)); int lines = 0; TCHAR szTitle[MAX_PATH] = {0}; TCHAR szMsg[MAX_PATH] = {0}; TCHAR szFormat[MAX_PATH] = {0}; memset(szMsg, 0, MAX_PATH); //保存当前光标并重设为等待形状 HCURSOR hOldCursor = GetCursor();?? HCURSOR hNewCursor = LoadCursor(_Module.GetModuleInstance(), MAKEINTRESOURCE(IDC_COUNT_WAIT)); assert(hNewCursor); SetCursor(hNewCursor);? TCHAR szTemp[MAX_PATH] = {0}; LoadString(_Module.GetModuleInstance(), IDS_TOTAL_LINES, szFormat, MAX_PATH); if (SUCCEEDED(GetFolderLines((BSTR *)&m_pszPath, &lines))) { wsprintf(szMsg, szFormat, (LPTSTR)m_pszPath, lines); } // 恢复默认光标形状 SetCursor(hOldCursor); // 显示统计代码信息 LoadString(_Module.GetModuleInstance(), IDS_TITLE, szTitle, MAX_PATH); MessageBox(pici->hwnd, szMsg, szTitle, MB_OK | MB_ICONINFORMATION); } return S_OK; }四、其它
HKEY_CLASSES_ROOT\Directory\Shellex\ContextMenuHandlers\下新建一项,命名为SrcCount,它的默认键值是组件的GUID,这里为:
{548773BA-874E-4C02-9DC7-B7A096772C7D}现在在资源管理器里对文件夹按快捷菜单,看到了吗,多出一菜单项了:源代码统计…,当我们单击该项时即可进行代码统计。