2017-09-05 278 views
1

我有一個.lnk文件創建一個IShellItem對象,我想獲得它指向的文件,無論是作爲另一個的IShellItem或一個PIDL。由於目標可能不是實際的文件(僅僅是虛擬文件系統中的一個項目),所以我想完全避免使用任何路徑。得到一個.lnk文件BHID_LinkTargetItem如果IsShortcut從註冊表中刪除

我發現的最接近的方法是IShellItem::BindToHandler,它接受BHID_LinkTargetItem作爲參數,並且獲得所述目標作爲一個的IShellItem。這很完美,但是存在一個小問題 - 如果我刪除HKCL中的IsShortcut註冊表項(lnkfile等),則不起作用。在這種情況下調用IShellItem::GetAttributes不會返回SFGAO_LINK,並且該方法失敗。

當篡改註冊表時,系統功能停止工作並不奇怪,但刪除此特定鍵以從快捷方式圖標中隱藏箭頭是常見的事情。我知道可能有更好的方法來做到這一點,但我不希望我的代碼在用戶完成之後失敗。

我目前有決心,是構建IPersistFile在它的位置和鑄造IShellLink。現在我只需撥打IShellLink::GetIDList,我很高興。如果外殼項目不是真實文件,我嘗試獲取第二段中指定的鏈接。

但是,我必須獲得鏈接文件的路徑這一事實令我擔憂了一下。有沒有一些情況下,這可能會失敗的一個有效的鏈接對象?它可以完成而不必使用路徑?

+0

繼續爲用戶創建的混亂提供解決方法的路徑只會導致黑暗的一面;-)。相反,你應該告訴他們隱藏快捷箭頭的正確方法是[用一個透明圖標代替它](http://www.theeldergeek.com/windows_7/shortcut_remove_arrow_overlay.htm)。我認爲這仍然適用於Win 10。甚至有免費工具可以做到這一點,而無需手動篡改註冊表。 – zett42

+0

@ zett42好點,但我不是在尋找「黑客」或解決方法,只是解決問題的另一種方法。也能夠直接從* IShellItem *獲得* IShellLink *將會很有用。 – IllidanS4

+0

夠公平的。 [Raymond Chen通過'IShellFolder :: GetUIObjectOf()')(https://blogs.msdn.microsoft.com/oldnewthing/20100702-00/?p=13523/),在結果中獲得一個'IShellLink'。當你從一個'IShellItem'開始時,你可以用'BHID_SFUIObject'來合理地調用'IShellItem :: BindToHandler()'來獲得'IShellLink'。我不知道這是否也取決於'IsShortcut'註冊表項,但它可能值得一試。 – zett42

回答

1

你可以通過調用IShellItem::BindToHandler()並通過BHID_SFUIObjectrbhid參數獲得直接從IShellItemIShellLink。然後,請撥打IShellLink::GetIDList()以獲取目前解決方案中已經完成的鏈接目標。

在我的Windows 10測試中,它獨立於IsShortcut註冊表項的存在。

例子:

#include <ShlObj.h>  // Shell API 
#include <atlcomcli.h> // CComPtr 
#include <atlbase.h> // CComHeapPtr 
#include <iostream> 
#include <system_error> 

// Throw a std::system_error if the HRESULT indicates failure. 
template< typename T > 
void ThrowIfFailed(HRESULT hr, T&& msg) 
{ 
    if(FAILED(hr)) 
     throw std::system_error{ hr, std::system_category(), std::forward<T>(msg) }; 
} 

// RAII wrapper to initialize/uninitialize COM 
struct CComInit 
{ 
    HRESULT hr = ::CoInitialize(nullptr); 
    CComInit() { ThrowIfFailed(hr, "CoInitialize failed"); } 
    ~CComInit() { ::CoUninitialize(); } 
}; 

int main() 
{ 
    try 
    { 
     CComInit init; 

     // Create a shell item from a file for testing purposes. 
     CComPtr<IShellItem> pItem; 
     ThrowIfFailed(
      SHCreateItemFromParsingName(L"C:\\test.lnk", nullptr, IID_PPV_ARGS(&pItem)), 
      "Could not create shell item from path"); 

     // Obtain a link object from the shell item. 
     CComPtr<IShellLink> pLink; 
     ThrowIfFailed(
      pItem->BindToHandler(nullptr, BHID_SFUIObject, IID_PPV_ARGS(&pLink)), 
      "Could not obtain IShellLink"); 

     // Get the link target as PIDL. 
     CComHeapPtr<ITEMIDLIST> pidlTarget; 
     ThrowIfFailed(
      pLink->GetIDList(&pidlTarget), 
      "Could not get link target as PIDL"); 

     // Create a shell item from the link target PIDL... 
     CComPtr<IShellItem> pItemTarget; 
     ThrowIfFailed(
      SHCreateShellItem(nullptr, nullptr, pidlTarget, &pItemTarget), 
      "Could not create shell item for link target"); 

     //... to obtain the display name (we may use SIGDN_FILESYSPATH to obtain a 
     // path only if we know the target is a filesystem item). 
     CComHeapPtr<wchar_t> pDisplayName; 
     ThrowIfFailed(
      pItemTarget->GetDisplayName(SIGDN_DESKTOPABSOLUTEEDITING, &pDisplayName), 
      "Could not get display name of link target"); 

     std::wcout << L"Link target (display name): " << pDisplayName.m_pData << '\n'; 

     return 0; 
    } 
    catch(std::system_error const& e) 
    { 
     std::cout << "ERROR: " << e.what() << ", error code: " << e.code() << "\n"; 

     return 1; 
    } 
} 

注:

  • 代碼使用ATL COM智能指針異常安全,乾淨的代碼。 CComPtr用於管理COM對象(當對象超出作用域時,其析構函數自動調用Release()方法)。 CComHeapPtr用於管理由shell分配的「原始內存」(其析構函數自動調用::CoTaskMemFree())。
+0

我認爲它可能不適用於自定義shell命名空間擴展,因爲IShellLink實現不是強制性的。在這種情況下,IShellLink實現由shell本身提供,我懷疑在這種情況下我們會回到原來的問題。 –