Windows 7 引入了 Shell Library(Shell 程式庫) 的全新概念,徹底改變了使用者尋找與管理資料的方式。傳統版本中的「我的文件」、「圖片」、「音樂」等固有資料夾,如今統整為一個虛擬的「儲存庫」入口點。使用者不再受限於單一實體路徑,而是能將分散於多個磁碟、網路位置的資料夾納入同一個程式庫,系統自動為其建立索引,提供更快速的查詢與更豐富的中繼資料檢視。
Shell Library 的設計初衷在於解決「資料散落在 PC 各處」的痛點。使用者可自行決定哪些資料夾屬於「文件庫」,哪些屬於「圖片庫」,實現邏輯上的集中管理。對開發者而言,Shell Library API 提供一致的介面,讓應用程式能共享使用者自訂的資料範圍,不再像早期 Windows Media Player 與 iTunes 各自維護不同的音樂庫那樣造成混亂。
程式庫不僅限於本機路徑,同樣可包含網路資料夾。無論使用者在家中或辦公室,開啟檔案對話方塊時都能看到完整的程式庫視圖,大幅提升跨環境作業的連貫性。
本動手實驗將引導您開發 SLUtil,一個用於管理預設程式庫目錄的命令列工具。完成後您將掌握:
SLUtil,保持預設設定。CommandLineInterpreter.h 與 CommandLineInterpreter.cpp(位於 Starter 資料夾),利用 COMMAND 巨集註冊命令。stdafx.h 取代專案原有檔案,內含必要的 #include。targetver.h,將 _WIN32_WINNT 設為 0x0601 以支援 Windows 7 API。SLUtil.cpp 的 #include "stdafx.h" 之後加入 #include "CommandLineInterpreter.h"。#include "stdafx.h"
#include "CommandLineInterpreter.h"
int _tmain(int argc, _TCHAR* argv[])
{
// 初始化 COM
CoInitialize(NULL);
CCommandLineInterpreter::Execute(argc, argv);
CoUninitialize();
return 0;
}
? 測試,執行應顯示說明訊息。
程式化操作 Shell Library 需使用 IShellLibrary、IShellItem、IShellItem2、IShellItemArray 等介面,定義於 shobjidl.h。該標頭檔亦提供輔助函式,例如 SHCreateLibrary:
__inline HRESULT SHCreateLibrary(__in REFIID riid, __deref_out void **ppv)
{
return CoCreateInstance(CLSID_ShellLibrary, NULL, CLSCTX_INPROC_SERVER, riid, ppv);
}
在 SLUtil.cpp 加入建立程式庫的實作:
// 建立新的 Shell Library
void CreateLibrary(const CCommand &command, const vector<PCWSTR> &arguments)
{
IShellLibrary *shellLibrary = NULL;
IShellItem *savedTo = NULL;
// 建立 Shell Library COM 物件
HRESULT hr = SHCreateLibrary(IID_PPV_ARGS(&shellLibrary));
if (FAILED(hr))
{
wcerr << L"Create: Can't create Shell Library COM object." << endl;
exit(4);
}
// 儲存至使用者的 Libraries 資料夾,若同名自動加編號
hr = shellLibrary->SaveInKnownFolder(FOLDERID_UsersLibraries, arguments[0],
LSF_MAKEUNIQUENAME, &savedTo);
if (FAILED(hr))
{
wcerr << L"Create: Can't create Shell Library." << endl;
exit(5);
}
if (shellLibrary != NULL) shellLibrary->Release();
if (savedTo != NULL) savedTo->Release();
}
COMMAND(L"Create", L"SLUtil Create LibraryName", L"Create a new library",
L"SLUtil Create MyLib", 1, CreateLibrary);
Create MyLib。MyLib.library-ms。
對現有程式庫增減資料夾,需先重新開啟程式庫。為避免重複程式碼,先建立兩個輔助函式(僅處理使用者程式庫位置):
// 取得程式庫 COM 物件與完整路徑
HRESULT GetLibraryFromLibrariesFolder(PCWSTR libraryName, IShellLibrary **shellLibrary, bool openRead = true, PWSTR *libraryFullPath = NULL)
{
wstring realLibraryName(libraryName);
realLibraryName += L".library-ms";
IShellItem2 *shellItem = NULL;
HRESULT hr = SHCreateItemInKnownFolder(FOLDERID_UsersLibraries,
KF_FLAG_DEFAULT_PATH | KF_FLAG_NO_ALIAS, realLibraryName.c_str(),
IID_PPV_ARGS(&shellItem));
if (FAILED(hr)) return hr;
if (libraryFullPath != NULL)
hr = shellItem->GetString(PKEY_ParsingPath, libraryFullPath);
if (shellLibrary != NULL)
hr = SHLoadLibraryFromItem(shellItem, openRead ? STGM_READ : STGM_READWRITE, IID_PPV_ARGS(shellLibrary));
if (shellItem != NULL) shellItem->Release();
return hr;
}
// 開啟現有程式庫
IShellLibrary *OpenLibrary(PCWSTR commandName, PCWSTR libraryName, bool openRead = true)
{
IShellLibrary *shellLibrary = NULL;
HRESULT hr = GetLibraryFromLibrariesFolder(libraryName, &shellLibrary, openRead);
if (FAILED(hr))
{
wcerr << commandName << L": Can't load library." << endl;
exit(4);
}
return shellLibrary;
}
接著實作新增/移除資料夾:
// 新增資料夾到程式庫
void AddFolder(const CCommand &command, const vector<PCWSTR> &arguments)
{
IShellLibrary *shellLibrary = OpenLibrary(L"AddFolder", arguments[0], false);
HRESULT hr = SHAddFolderPathToLibrary(shellLibrary, arguments[1]);
if (FAILED(hr))
{
wcerr << L"AddFolder: Can't add folder to the library." << endl;
exit(6);
}
shellLibrary->Commit(); // 送交變更
if (shellLibrary != NULL) shellLibrary->Release();
}
COMMAND(L"AddFolder", L"SLUtil AddFolder LibraryName FolderPath",
L"Add a folder to a library", L"SLUtil AddFolder Documents C:\\Docs", 2, AddFolder);
// 從程式庫移除資料夾
void RemoveFolder(const CCommand &command, const vector<PCWSTR> &arguments)
{
IShellLibrary *shellLibrary = OpenLibrary(L"RemoveFolder", arguments[0], false);
HRESULT hr = SHRemoveFolderPathFromLibrary(shellLibrary, arguments[1]);
if (FAILED(hr))
{
wcerr << L"RemoveFolder: Can't remove folder from the library." << endl;
exit(5);
}
shellLibrary->Commit();
if (shellLibrary != NULL) shellLibrary->Release();
}
COMMAND(L"RemoveFolder", L"SLUtil RemoveFolder LibraryName FolderPath",
L"Remove a folder from a library", L"SLUtil RemoveFolder Documents C:\\Docs", 2, RemoveFolder);
編譯執行後,即可使用 SLUtil AddFolder MyLib C:\Data 等指令動態管理程式庫內容。