Я использую расширение пространства имен оболочки для создания виртуальной папки в разделе «Мой компьютер».
Теперь мне нужно создать ярлык для него программно в C ++.
На основании ответа от Создание ярлыка для нефайлового объекта и из Как создать ярлык для виртуальной папки в C ++ на Windows 7?, Я создаю отдельный проект EXE для создания ярлыка для моей виртуальной папки, и он работает.
Однако, когда я копирую тот же код в свою DLL и вызываю эту функцию DLL из другого проекта MFC, созданный «ярлык» не является настоящим ярлыком.
Пожалуйста, смотрите следующий код и фотографии:
В индивидуальном проекте EXE:
HRESULT CreateComputerChildShortCut(LPWSTR childDisplayName, LPWSTR path)
{
// get My Computer folder's ShellItem
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 display name
CComHeapPtr<wchar_t> displayName;
item->GetDisplayName(SIGDN_NORMALDISPLAY, &displayName);
if (!lstrcmpi(childDisplayName, displayName))
{
HRESULT hres;
IShellLink* psl;
// Get a pointer to the IShellLink interface. It is assumed that CoInitialize
// has already been called.
hres = CoCreateInstance(CLSID_FolderShortcut, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&psl);
if (SUCCEEDED(hres))
{
CComHeapPtr<ITEMIDLIST> pidl;
hr = SHGetIDListFromObject(item, &pidl);
if (FAILED(hr)) return hr;
IPersistFile* ppf;
psl->SetIDList(pidl);
// Query IShellLink for the IPersistFile interface, used for saving the
// shortcut in persistent storage.
hres = psl->QueryInterface(IID_IPersistFile, (LPVOID*)&ppf);
if (SUCCEEDED(hres))
{
// Save the link by calling IPersistFile::Save.
hres = ppf->Save(path, FALSE);
ppf->Release();
}
psl->Release();
break;
}
}
}
return S_OK;
}
int main()
{
CoInitialize(NULL);
// I've used my Apple's iCloud as an example
// it's a virtual folder, a shell namespace extension
LPWSTR szFolderPath = new TCHAR[MAX_PATH];
SHGetSpecialFolderPath(NULL, szFolderPath, CSIDL_PROFILE, FALSE);
std::wstring temp(szFolderPath);
std::wstring links = L"\\Links\\MyShortcut-fromExe";
temp += links;
LPWSTR path = (LPWSTR)temp.c_str();
HRESULT hr = CreateComputerChildShortCut(L"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{5B952D69-65F5-42cf-ABF3-0AF269C5EC5F}", path);
//the first GUID means "My Computer", the second is my virtual folder's GUID
//SetFileAttributes(path, FILE_ATTRIBUTE_HIDDEN);
printf("hr:0x%08X\n", hr);
CoUninitialize();
return 0;
}
После того, как я его запустил, он отлично создает ярлык:
Как видите, ярлык, созданный из EXE-файла, также может отображаться слева от папки, и его свойства открываются нормально:
Однако, когда я запускаю тот же код из DLL, вызванной из моего проекта MFC, созданный код не является обычным ярлыком.
Он не отображается слева автоматически, как в EXE, и его свойства выглядят странно. Когда я дважды щелкаю «ярлык», то он добавляется слева.
Ниже приведен код в DLL:
CoInitialize(NULL);
LPWSTR szFolderPath = new TCHAR[MAX_PATH];
SHGetSpecialFolderPath(NULL, szFolderPath, CSIDL_PROFILE, FALSE);
std::wstring temp(szFolderPath);
std::wstring links = L"\\Links\\MyShortcut-fromDll";
temp += links;
LPWSTR path = (LPWSTR)temp.c_str();
// the value of VirtualFolderTitle is "VirtualFolder"HRESULT hr = CreateShortCut((LPWSTR)VirtualFolderTitle.c_str(), path);
if (hr != S_OK)
{
LOG_ERROR(L"cannot create short cut for Thermo Fisher Cloud", 0);
}
CoUninitialize();
а также
HRESULT CreateShortCut(LPWSTR childDisplayName, LPWSTR path)
{
// get My Computer folder's ShellItem
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 display name
CComHeapPtr<wchar_t> displayName;
item->GetDisplayName(SIGDN_NORMALDISPLAY, &displayName);
if (!lstrcmpi(childDisplayName, displayName))
{
HRESULT hres;
IShellLink* psl;
// Get a pointer to the IShellLink interface. It is assumed that CoInitialize
// has already been called.
hres = CoCreateInstance(CLSID_FolderShortcut, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&psl);
if (SUCCEEDED(hres))
{
CComHeapPtr<ITEMIDLIST> pidl;
hr = SHGetIDListFromObject(item, &pidl);
if (FAILED(hr)) return hr;
IPersistFile* ppf;
psl->SetIDList(pidl);
// Query IShellLink for the IPersistFile interface, used for saving the
// shortcut in persistent storage.
hres = psl->QueryInterface(IID_IPersistFile, (LPVOID*)&ppf);
if (SUCCEEDED(hres))
{
// Save the link by calling IPersistFile::Save.
hres = ppf->Save(path, FALSE);
ppf->Release();
}
psl->Release();
break;
}
}
}
return S_OK;
}
Я пытался отладить его самостоятельно, но единственное различие, которое я обнаружил между ними, это когда они item->GetDisplayName(SIGDN_NORMALDISPLAY, &displayName);
, проект EXE получит строку ::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\::{5B952D69-65F5-42cf-ABF3-0AF269C5EC5F}
, но DLL получит строку VirtualFolder
вместо.
Это действительно странно. Таким образом, единственное различие между кодом EXE и DLL в том, что в коде EXE мне нужно передать GUID
, но для DLL мне нужно передать имя виртуальной папки.
Я также пытался использовать точно такой же код из Как создать ярлык для виртуальной папки в C ++ на Windows 7?, но проект DLL по-прежнему создает другой «ярлык», чем EXE.
Кто-нибудь знает, как это исправить? Как я могу создать нормальный ярлык из DLL и вызываться проектом MFC?
Примечание. Оба «ярлыка» могут направить пользователя в нужную виртуальную папку.
Задача ещё не решена.
Других решений пока нет …