背景: #EDF0F5 #FAFBE6 #FFF2E2 #FDE6E0 #F3FFE1 #DAFAF3 #EAEAEF 默认  
阅读新闻

如何实现快捷方式中的查找目标功能

[日期:2006-11-13] 作者: [字体: ]

如何实现快捷方式中的查找目标功能

作者:梁永喜

下载源代码

  最近写一个开发辅助工具,在这个过程要做一个类似文件快捷方式中查找目标的功能,先查MSDN98,大家不要见笑,我现在一直都用它,没有相应的API,后又. Net 2003中的MSDN,找到了可以实现该功能的API,SHOpenFolderAndSelectItems()函数,它的原型如下:
(具体用法参考MSDN)“Opens a Microsoft® Windows® Explorer window with specified items in a particular folder selected.”

HRESULT SHOpenFolderAndSelectItems(
    LPCITEMIDLIST pidlFolder,
    UINT cidl,
    LPCITEMIDLIST *apidl,
    DWORD dwFlags
);

  但是,它需要Windows XP及上,若在Win2000或Win98如何实现它呢?于是我就上网搜索,几经周折最终搜到的一篇文章,但它只是利用工具通过反汇编Windows API函数得到的代码,可能可以实现与快捷方式相同的对话框(我没有试过),但其代码可读性非常差,我只能参考一下大概的流程,他提到一个非常重要的一点,那就是使用一个未公开的API函数SHGetIDispatchForFolder,它可帮助我打开文件夹。好不多说了,下面是关键的部分:
  查找目标功能,分为两个步骤,首先打开或找到目标文件所在的文件夹,其次在打开的文件夹中选中相应的项目(即文件)。在说这个步骤之前,先认识一下,下面两个结构

typedef struct _SHITEMID { 
    USHORT cb; 
    BYTE   abID[1]; 
} SHITEMID, * LPSHITEMID; 
typedef const SHITEMID  * LPCSHITEMID; 

typedef struct _ITEMIDLIST {
    SHITEMID mkid;
} ITEMIDLIST, * LPITEMIDLIST; 
typedef const ITEMIDLIST * LPCITEMIDLIST; 
  这两个结构的数据保存的是项目定义符列表(仅是字面翻译),这个结构所表示的文件夹及文件除了正常的,还包括一些特殊的文件夹及文件(如目录,我的电脑等),SHGetIDispatchForFolder函数正是用它的做为参数,可以打开一些特殊的文件夹。SHGetIDispatchForFolder函数的原型是 :
      HRESULT (WINAPI*gpfSHGetIDispatchForFolder)(ITEMIDLIST* pidl, IWebBrowserApp** ppIWebBrowserApp); 
  通常快捷方式给我的ITEMIDLIST是包含文件名的,若直接调用上面的函数,它将直接会打开出目标文件,而不是打开文件夹。下面是区分文件及文件夹的代码:
  	pIdlFile = pidl;      
	/// 找出目标文件中文件名的偏移量
	while (cb = pIdlFile->mkid.cb) 
	{
		pidl2 = pIdlFile;
		pIdlFile = (ITEMIDLIST*)((BYTE*)pIdlFile + cb);
	}

	cb = pidl2->mkid.cb;
	pidl2->mkid.cb = 0;
下面是打开文件夹及选中文件的代码,相信大家不难理解。
   	/// 打开目标文件所在的文件夹
	if (SUCCEEDED(GetShellFolderViewDual(pidl, &pIShellFolderViewDual))) 
	{
		pidl2->mkid.cb = cb;
		// 0 Deselect the item. 
		// 1 Select the item. 
		// 3 Put the item in edit mode. 
		// 4 Deselect all but the specified item. 
		// 8 Ensure the item is displayed in the view. 
		// 0x10 Give the item the focus. 
		COleVariant bszFile(pidl2);
      
		if(pIShellFolderViewDual != NULL)
		{
			/// 选中相应的选项
			pIShellFolderViewDual->SelectItem(bszFile, 0x1d);
			pIShellFolderViewDual->Release();
		}
		return TRUE;
	}
  源代码中包含了一个DEMO。下面是完整的函数,可以直接调用FindTarget(CString str)参数为文件名,若是快捷方式则会自动指向其目标。若代码中已做过COM的初始化工作,请删除CoInitialize(NULL);及CoUninitialize();语句。
HRESULT GetShellFolderViewDual(ITEMIDLIST* pidl, IShellFolderViewDual** ppIShellFolderViewDual)
{
	IWebBrowserApp* pIWebBrowserApp;
	IDispatch* pDoc;
	HWND hWnd;
	HRESULT hr;
	HINSTANCE ghSHDOCVW;


	HRESULT (WINAPI*gpfSHGetIDispatchForFolder)(ITEMIDLIST* pidl, IWebBrowserApp** ppIWebBrowserApp);

	*ppIShellFolderViewDual = NULL;

	ghSHDOCVW = LoadLibrary(_T("SHDOCVW.DLL"));
	if (ghSHDOCVW == NULL)
		return FALSE;

	pIWebBrowserApp=NULL;
	gpfSHGetIDispatchForFolder = 
		(HRESULT (WINAPI*)(ITEMIDLIST*, IWebBrowserApp**)) GetProcAddress(ghSHDOCVW, "SHGetIDispatchForFolder");
	if (gpfSHGetIDispatchForFolder == NULL)
		return FALSE;

	/// 调用未公开的API函数 SHGetIDispatchForFolder
	if (SUCCEEDED(gpfSHGetIDispatchForFolder(pidl, &pIWebBrowserApp))) 
	{
		if (SUCCEEDED(pIWebBrowserApp->get_HWND((long*)&hWnd))) 
		{
			SetForegroundWindow(hWnd);
			ShowWindow(hWnd, SW_SHOWNORMAL);
		}

		if (SUCCEEDED(hr = pIWebBrowserApp->get_Document(&pDoc)))
		{
			pDoc->QueryInterface(IID_IShellFolderViewDual, (void**) ppIShellFolderViewDual);
			pDoc->Release();
		}

		pIWebBrowserApp->Release();
	}
	FreeLibrary(ghSHDOCVW);

	return TRUE;
}

BOOL XZSHOpenFolderAndSelectItems(ITEMIDLIST *pidlFolder)
{
	ITEMIDLIST *pidl, *pidl2;
	ITEMIDLIST* pIdlFile;
	USHORT cb;
	IShellFolderViewDual* pIShellFolderViewDual;

	HRESULT (WINAPI *gpfSHOpenFolderAndSelectItems)(LPCITEMIDLIST *pidlFolder, UINT cidl, LPCITEMIDLIST *apidl, DWORD dwFlags);
	HINSTANCE ghShell32;
/// 只有WinXp及以上及系统才支持SHOpenFolderAndSelectItems() API
/// 那其它系统该怎么实现这个功能呢?只能采用其它的方法来处理
/// 首先用XP跟踪到SHOpenFolderAndSelectItems()API中,看它是如何处理的,再用同样的方法去实现
/// 其它系统的这个功能使用工具 VC6 .net 2003 MSDN Ollydbg v1.10中文版

	ghShell32 = LoadLibrary(_T("Shell32.DLL"));
	if (ghShell32 == NULL)
		return FALSE;

	gpfSHOpenFolderAndSelectItems = 
         (HRESULT (WINAPI*)(LPCITEMIDLIST*, UINT, LPCITEMIDLIST*, DWORD)) GetProcAddress(ghShell32, "SHOpenFolderAndSelectItems");
	if (gpfSHOpenFolderAndSelectItems != NULL)
	{
		/// 可以获得SHOpenFolderAndSelectItems()函数的API地址
		if (SUCCEEDED(gpfSHOpenFolderAndSelectItems((LPCITEMIDLIST*)pidlFolder,0,(LPCITEMIDLIST*)NULL,0))) 
		{
			///直接调用系统的功能
			FreeLibrary(ghShell32);
			return TRUE;
		}
		FreeLibrary(ghShell32);
		return FALSE;
	}
	FreeLibrary(ghShell32);

	/// 当操作系统不支持SHOpenFolderAndSelectItems()函数的API时的处理,
	/// 自已动手写一个与系统功能相同的代码
	pidl = pidlFolder;
	pIdlFile = pidl;
	/// 找出目标文件中文件名的偏移量
	while (cb = pIdlFile->mkid.cb) 
	{
		pidl2 = pIdlFile;
		pIdlFile = (ITEMIDLIST*)((BYTE*)pIdlFile + cb);
	}

	cb = pidl2->mkid.cb;
	pidl2->mkid.cb = 0;

	/// 打开目标文件所在的文件夹
	if (SUCCEEDED(GetShellFolderViewDual(pidl, &pIShellFolderViewDual))) 
	{
		pidl2->mkid.cb = cb;
		// 0 Deselect the item.  
		// 1 Select the item.  
		// 3 Put the item in edit mode.  
		// 4 Deselect all but the specified item.  
		// 8 Ensure the item is displayed in the view.  
		// 0x10 Give the item the focus.  
		COleVariant bszFile(pidl2);

		if(pIShellFolderViewDual != NULL)
		{
			/// 选中相应的选项
			pIShellFolderViewDual->SelectItem(bszFile, 0x1d);
			pIShellFolderViewDual->Release();
		}
		return TRUE;
	}

	return FALSE;
}

void FindTarget(CString str)
{
	HRESULT hres; 
	IShellLink *psl; 
	ITEMIDLIST *pidl;
	IPersistFile *ppf; 

CoInitialize(NULL);

	// Get a pointer to the IShellLink interface. 
	hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, 
			    IID_IShellLink, (LPVOID*)&psl); 
	if (SUCCEEDED(hres)) 
	{ 
		// 设置目标文件
		psl->SetPath(str);
		/// 获得目标文件的ITEMIDLIST
		psl->GetIDList(&pidl);

		// Get a pointer to the IPersistFile interface. 
		hres = psl->QueryInterface(IID_IPersistFile, (void**)&ppf); 
		if (SUCCEEDED(hres)) 
		{ 
			WCHAR wsz[MAX_PATH]; 
#ifdef _UNICODE
			wcscpy(wsz, str);
#else
			// Ensure that the string is Unicode. 
			MultiByteToWideChar(CP_ACP, 0, str, -1, wsz, MAX_PATH); 
#endif 
 
			// Load the shortcut. 
			hres = ppf->Load(wsz, STGM_READ); 

			if (SUCCEEDED(hres)) 
			{ 
				/// 获得快捷方式的ITEMIDLIST
				psl->GetIDList(&pidl);
			}m 
			ppf->Release();
		}

		/// 打开文件夹并选中项目
		XZSHOpenFolderAndSelectItems(pidl);

		psl->Release();
	}
	CoUninitialize();
}
在VC6下编译后的代码,通过98,2k,XP的测试。

参考文章
阅读:
打印
相关新闻       相关关键词: