如何在 windows 7 上用 C++ 创建虚拟文件夹的快捷方式?
How to create shortcut for virtual folder in C++ on windows 7?
我使用的平台是windows 7. 我需要为windows 上的虚拟文件夹创建快捷方式 7.
我使用 windows 7 SDK 示例在计算机下创建一个虚拟文件夹:
示例项目名为 ExplorerDataProvider
,它定义了 IShellFolder class:
的 CLSID
// add classes supported by this module here
const CLASS_OBJECT_INIT c_rgClassObjectInit[] =
{
{ &CLSID_FolderViewImpl, CFolderViewImplFolder_CreateInstance },
{ &CLSID_FolderViewImplContextMenu,CFolderViewImplContextMenu_CreateInstance },
};
CFolderViewImplFolder_CreateInstance
的定义是:
HRESULT CFolderViewImplFolder_CreateInstance(REFIID riid, void **ppv)
{
*ppv = NULL;
CFolderViewImplFolder* pFolderViewImplShellFolder = new (std::nothrow) CFolderViewImplFolder(0);
HRESULT hr = pFolderViewImplShellFolder ? S_OK : E_OUTOFMEMORY;
if (SUCCEEDED(hr))
{
hr = pFolderViewImplShellFolder->QueryInterface(riid, ppv);
pFolderViewImplShellFolder->Release();
}
return hr;
}
并且 CFolderViewImplFolder
实现 IShellFolder2
amd IPersistFolder2
。
我在这里找到了一个类似的代码,用于为打印机创建快捷方式:
https://www.codeproject.com/Articles/596642/Creating-a-shortcut-programmatically-in-Cplusplus
并在
https://msdn.microsoft.com/en-us/library/aa969393.aspx#Shellink_Item_Identifiers
获得 IShellFolder 的 class 标识符后,您可以调用 CoCreateInstance 函数来检索接口的地址。然后就可以调用接口枚举文件夹中的对象,并获取要查找的对象的项标识地址。最后,您可以在对 IShellLink::SetIDList 成员函数的调用中使用该地址来创建对象的快捷方式。
我修改了
hr = SHGetMalloc(&pMalloc);
hr = SHGetDesktopFolder( &pDesktopFolder );
hr = SHGetSpecialFolderLocation( NULL, CSIDL_PRINTERS, &netItemIdLst );
hr = pDesktopFolder->BindToObject( netItemIdLst, NULL, IID_IShellFolder, (void **)&pPrinterFolder );
至
// testFolder is the CLSID for the virtual folder implementation
hr = CoCreateInstance(testFolder, NULL, CLSCTX_INPROC_SERVER, IID_IShellFolder, (LPVOID*)&pVirtualFolder);
或
hr = CoCreateInstance(testFolder, NULL, CLSCTX_INPROC_SERVER, IID_IShellFolder2, (LPVOID*)&pVirtualFolder);
但是 pVirtualFolder 仍然是 NULL,它打印出 "cannot find the corresponding interface".
我用的时候CoCreateInstance
有什么问题吗?或者我不应该使用这个解决方案?有任何示例代码吗?
要创建快捷方式,您可以使用 official documentation. Here is a sample code that creates a shortcut for a children of "This PC" (aka: ComputerFolder)
int main()
{
CoInitialize(NULL);
// I've used my Apple's iCloud as an example (name is in french)
// it's a virtual folder, a shell namespace extension
HRESULT hr = CreateComputerChildShortCut(L"Photos iCloud", L"c:\temp\my icloud");
printf("hr:0x%08X\n", hr);
CoUninitialize();
return 0;
}
HRESULT CreateComputerChildShortCut(LPWSTR childDisplayName, LPWSTR path)
{
// get My Computer folder's ShellItem (there are other ways for this...)
CComPtr<IShellItem> folder;
HRESULT hr = SHCreateItemInKnownFolder(FOLDERID_ComputerFolder, 0, NULL, IID_PPV_ARGS(&folder));
if (FAILED(hr)) return hr;
// enumerate children
CComPtr<IEnumShellItems> items;
hr = folder->BindToHandler(NULL, BHID_EnumItems, IID_PPV_ARGS(&items));
if (FAILED(hr)) return hr;
for (CComPtr<IShellItem> item; items->Next(1, &item, NULL) == S_OK; item.Release())
{
// get parsing path (if's often useful)
CComHeapPtr<wchar_t> parsingPath;
item->GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING, &parsingPath);
wprintf(L"Path: %s\n", parsingPath);
// get display name
CComHeapPtr<wchar_t> displayName;
item->GetDisplayName(SIGDN_NORMALDISPLAY, &displayName);
wprintf(L" Name: %s\n", displayName);
if (!lstrcmpi(childDisplayName, displayName))
{
// get pidl
// it's the unambiguous way of referencing a shell thing
CComHeapPtr<ITEMIDLIST> pidl;
hr = SHGetIDListFromObject(item, &pidl);
if (FAILED(hr)) return hr;
// create an instance of the standard Shell's folder shortcut creator
CComPtr<IShellLink> link;
hr = link.CoCreateInstance(CLSID_FolderShortcut);
if (FAILED(hr)) return hr;
// just use the pidl
hr = link->SetIDList(pidl);
if (FAILED(hr)) return hr;
CComPtr<IPersistFile> file;
hr = link->QueryInterface(&file);
if (FAILED(hr)) return hr;
// save the shortcut (we could also change other IShellLink parameters)
hr = file->Save(path, FALSE);
if (FAILED(hr)) return hr;
break;
}
}
return S_OK;
}
当然,如果您知道绝对解析路径或绝对pidl,则无需枚举任何内容,这只是为了演示目的。
我使用的平台是windows 7. 我需要为windows 上的虚拟文件夹创建快捷方式 7. 我使用 windows 7 SDK 示例在计算机下创建一个虚拟文件夹:
示例项目名为 ExplorerDataProvider
,它定义了 IShellFolder class:
// add classes supported by this module here
const CLASS_OBJECT_INIT c_rgClassObjectInit[] =
{
{ &CLSID_FolderViewImpl, CFolderViewImplFolder_CreateInstance },
{ &CLSID_FolderViewImplContextMenu,CFolderViewImplContextMenu_CreateInstance },
};
CFolderViewImplFolder_CreateInstance
的定义是:
HRESULT CFolderViewImplFolder_CreateInstance(REFIID riid, void **ppv)
{
*ppv = NULL;
CFolderViewImplFolder* pFolderViewImplShellFolder = new (std::nothrow) CFolderViewImplFolder(0);
HRESULT hr = pFolderViewImplShellFolder ? S_OK : E_OUTOFMEMORY;
if (SUCCEEDED(hr))
{
hr = pFolderViewImplShellFolder->QueryInterface(riid, ppv);
pFolderViewImplShellFolder->Release();
}
return hr;
}
并且 CFolderViewImplFolder
实现 IShellFolder2
amd IPersistFolder2
。
我在这里找到了一个类似的代码,用于为打印机创建快捷方式:
https://www.codeproject.com/Articles/596642/Creating-a-shortcut-programmatically-in-Cplusplus
并在
https://msdn.microsoft.com/en-us/library/aa969393.aspx#Shellink_Item_Identifiers
获得 IShellFolder 的 class 标识符后,您可以调用 CoCreateInstance 函数来检索接口的地址。然后就可以调用接口枚举文件夹中的对象,并获取要查找的对象的项标识地址。最后,您可以在对 IShellLink::SetIDList 成员函数的调用中使用该地址来创建对象的快捷方式。
我修改了
hr = SHGetMalloc(&pMalloc);
hr = SHGetDesktopFolder( &pDesktopFolder );
hr = SHGetSpecialFolderLocation( NULL, CSIDL_PRINTERS, &netItemIdLst );
hr = pDesktopFolder->BindToObject( netItemIdLst, NULL, IID_IShellFolder, (void **)&pPrinterFolder );
至
// testFolder is the CLSID for the virtual folder implementation
hr = CoCreateInstance(testFolder, NULL, CLSCTX_INPROC_SERVER, IID_IShellFolder, (LPVOID*)&pVirtualFolder);
或
hr = CoCreateInstance(testFolder, NULL, CLSCTX_INPROC_SERVER, IID_IShellFolder2, (LPVOID*)&pVirtualFolder);
但是 pVirtualFolder 仍然是 NULL,它打印出 "cannot find the corresponding interface".
我用的时候CoCreateInstance
有什么问题吗?或者我不应该使用这个解决方案?有任何示例代码吗?
要创建快捷方式,您可以使用 official documentation. Here is a sample code that creates a shortcut for a children of "This PC" (aka: ComputerFolder)
int main()
{
CoInitialize(NULL);
// I've used my Apple's iCloud as an example (name is in french)
// it's a virtual folder, a shell namespace extension
HRESULT hr = CreateComputerChildShortCut(L"Photos iCloud", L"c:\temp\my icloud");
printf("hr:0x%08X\n", hr);
CoUninitialize();
return 0;
}
HRESULT CreateComputerChildShortCut(LPWSTR childDisplayName, LPWSTR path)
{
// get My Computer folder's ShellItem (there are other ways for this...)
CComPtr<IShellItem> folder;
HRESULT hr = SHCreateItemInKnownFolder(FOLDERID_ComputerFolder, 0, NULL, IID_PPV_ARGS(&folder));
if (FAILED(hr)) return hr;
// enumerate children
CComPtr<IEnumShellItems> items;
hr = folder->BindToHandler(NULL, BHID_EnumItems, IID_PPV_ARGS(&items));
if (FAILED(hr)) return hr;
for (CComPtr<IShellItem> item; items->Next(1, &item, NULL) == S_OK; item.Release())
{
// get parsing path (if's often useful)
CComHeapPtr<wchar_t> parsingPath;
item->GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING, &parsingPath);
wprintf(L"Path: %s\n", parsingPath);
// get display name
CComHeapPtr<wchar_t> displayName;
item->GetDisplayName(SIGDN_NORMALDISPLAY, &displayName);
wprintf(L" Name: %s\n", displayName);
if (!lstrcmpi(childDisplayName, displayName))
{
// get pidl
// it's the unambiguous way of referencing a shell thing
CComHeapPtr<ITEMIDLIST> pidl;
hr = SHGetIDListFromObject(item, &pidl);
if (FAILED(hr)) return hr;
// create an instance of the standard Shell's folder shortcut creator
CComPtr<IShellLink> link;
hr = link.CoCreateInstance(CLSID_FolderShortcut);
if (FAILED(hr)) return hr;
// just use the pidl
hr = link->SetIDList(pidl);
if (FAILED(hr)) return hr;
CComPtr<IPersistFile> file;
hr = link->QueryInterface(&file);
if (FAILED(hr)) return hr;
// save the shortcut (we could also change other IShellLink parameters)
hr = file->Save(path, FALSE);
if (FAILED(hr)) return hr;
break;
}
}
return S_OK;
}
当然,如果您知道绝对解析路径或绝对pidl,则无需枚举任何内容,这只是为了演示目的。