Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

562 lines
11 KiB

  1. /*++
  2. Implements IShellFolder.
  3. IUnknown
  4. IPersist
  5. IPersistFolder
  6. IShellFolder
  7. IExtractIcon
  8. Soon:
  9. IContextMenu
  10. IDataObject
  11. IShellPropSheetExt ?
  12. --*/
  13. #include <windows.h>
  14. #include <shlobj.h>
  15. #include <docobj.h> // IOleCommandTarget
  16. #include "pstore.h"
  17. #include "enumid.h"
  18. #include "utility.h"
  19. #include "shfolder.h"
  20. #include "shview.h"
  21. #include "icon.h" // icon handling
  22. #include "guid.h"
  23. #include "resource.h"
  24. extern HINSTANCE g_hInst;
  25. extern LONG g_DllRefCount;
  26. CShellFolder::CShellFolder(
  27. CShellFolder *pParent,
  28. LPCITEMIDLIST pidl
  29. )
  30. {
  31. m_pSFParent = pParent;
  32. if(m_pSFParent) {
  33. m_pSFParent->AddRef();
  34. }
  35. //
  36. // get the shell's IMalloc pointer
  37. // we'll keep this until we get destroyed
  38. //
  39. if(FAILED(SHGetMalloc(&m_pMalloc)))
  40. delete this;
  41. //
  42. // make a copy of the pidl in it's entirety. We do this so we are free
  43. // to look at the pidl later.
  44. //
  45. m_pidl = CopyPidl(m_pMalloc, pidl);
  46. m_ObjRefCount = 1;
  47. InterlockedIncrement(&g_DllRefCount);
  48. }
  49. CShellFolder::~CShellFolder()
  50. {
  51. if(m_pSFParent)
  52. m_pSFParent->Release();
  53. if(m_pidl)
  54. m_pMalloc->Free(m_pidl);
  55. if(m_pMalloc)
  56. m_pMalloc->Release();
  57. InterlockedDecrement(&g_DllRefCount);
  58. }
  59. STDMETHODIMP
  60. CShellFolder::QueryInterface(
  61. REFIID riid,
  62. LPVOID *ppReturn
  63. )
  64. {
  65. *ppReturn = NULL;
  66. if(IsEqualIID(riid, IID_IUnknown))
  67. *ppReturn = (IUnknown*)(IShellFolder*)this;
  68. else if(IsEqualIID(riid, IID_IPersistFolder))
  69. *ppReturn = (IPersistFolder*)(CShellFolder*)this;
  70. else if(IsEqualIID(riid, IID_IShellFolder))
  71. *ppReturn = (CShellFolder*)this;
  72. if(*ppReturn == NULL)
  73. return E_NOINTERFACE;
  74. (*(LPUNKNOWN*)ppReturn)->AddRef();
  75. return S_OK;
  76. }
  77. STDMETHODIMP_(DWORD)
  78. CShellFolder::AddRef()
  79. {
  80. return InterlockedIncrement(&m_ObjRefCount);
  81. }
  82. STDMETHODIMP_(DWORD)
  83. CShellFolder::Release()
  84. {
  85. LONG lDecremented = InterlockedDecrement(&m_ObjRefCount);
  86. if(lDecremented == 0)
  87. delete this;
  88. return lDecremented;
  89. }
  90. STDMETHODIMP
  91. CShellFolder::GetClassID(
  92. LPCLSID lpClassID
  93. )
  94. /*++
  95. IPersist::GetClassID
  96. --*/
  97. {
  98. *lpClassID = CLSID_PStoreNameSpace;
  99. return S_OK;
  100. }
  101. STDMETHODIMP
  102. CShellFolder::Initialize(
  103. LPCITEMIDLIST pidl
  104. )
  105. /*++
  106. IPersistFolder::Initialize
  107. --*/
  108. {
  109. return S_OK;
  110. }
  111. STDMETHODIMP
  112. CShellFolder::BindToObject(
  113. LPCITEMIDLIST pidl,
  114. LPBC pbcReserved,
  115. REFIID riid,
  116. LPVOID *ppvOut
  117. )
  118. /*++
  119. Creates an IShellFolder object for a subfolder.
  120. --*/
  121. {
  122. CShellFolder *pShellFolder;
  123. pShellFolder = new CShellFolder(this, pidl);
  124. if(pShellFolder == NULL)
  125. return E_OUTOFMEMORY;
  126. HRESULT hr = pShellFolder->QueryInterface(riid, ppvOut);
  127. pShellFolder->Release();
  128. return hr;
  129. }
  130. STDMETHODIMP
  131. CShellFolder::CompareIDs(
  132. LPARAM lParam,
  133. LPCITEMIDLIST pidl1,
  134. LPCITEMIDLIST pidl2
  135. )
  136. /*++
  137. Determines the relative ordering of two file objects or folders,
  138. given their item identifier lists.
  139. Returns a handle to a result code. If this method is successful,
  140. the CODE field of the status code (SCODE) has the following meaning:
  141. Less than zero The first item should precede the second (pidl1 < pidl2).
  142. Greater than zero The first item should follow the second (pidl1 > pidl2)
  143. Zero The two items are the same (pidl1 = pidl2).
  144. Passing 0 as the lParam indicates sort by name.
  145. 0x00000001-0x7fffffff are for folder specific sorting rules.
  146. 0x80000000-0xfffffff are used by the system.
  147. --*/
  148. {
  149. LPCWSTR szString1;
  150. LPCWSTR szString2;
  151. DWORD cbLength1;
  152. DWORD cbLength2;
  153. DWORD dwCompare;
  154. //
  155. // compare strings first, then GUID if equality is encountered
  156. // this is important because we do not want to return a value indicating
  157. // equality based on display string only; we also want to account for the
  158. // GUID associated with the display name, since we can have multiple GUIDs
  159. // with the same display name. If we just compared the string values,
  160. // the shell would not display all types/subtypes.
  161. //
  162. szString1 = GetPidlText(pidl1);
  163. szString2 = GetPidlText(pidl2);
  164. cbLength1 = lstrlenW(szString1) ;
  165. cbLength2 = lstrlenW(szString2) ;
  166. //
  167. // check via shortest length string
  168. //
  169. if(cbLength2 < cbLength1)
  170. cbLength1 = cbLength2;
  171. cbLength1 *= sizeof(WCHAR);
  172. dwCompare = memcmp(szString1, szString2, cbLength1);
  173. if(dwCompare == 0) {
  174. GUID *guid1;
  175. GUID *guid2;
  176. //
  177. // now compare the GUIDs.
  178. //
  179. guid1 = GetPidlGuid(pidl1);
  180. guid2 = GetPidlGuid(pidl2);
  181. dwCompare = memcmp(guid1, guid2, sizeof(GUID));
  182. }
  183. //
  184. // still equal? sort by PST_KEY_CURRENT_USER, then PST_KEY_LOCAL_MACHINE
  185. //
  186. if(dwCompare == 0) {
  187. dwCompare = GetPidlKeyType(pidl1) - GetPidlKeyType(pidl2);
  188. }
  189. return ResultFromDWORD(dwCompare);
  190. }
  191. STDMETHODIMP
  192. CShellFolder::CreateViewObject(
  193. HWND hwndOwner,
  194. REFIID riid,
  195. LPVOID *ppvOut
  196. )
  197. /*++
  198. CreateViewWindow creates a view window. This can be either the right pane
  199. of the Explorer or the client window of a folder window.
  200. --*/
  201. {
  202. HRESULT hr;
  203. CShellView *pShellView;
  204. pShellView = new CShellView(this, m_pidl);
  205. if(pShellView == NULL)
  206. return E_OUTOFMEMORY;
  207. hr = pShellView->QueryInterface(riid, ppvOut);
  208. pShellView->Release();
  209. return hr;
  210. }
  211. STDMETHODIMP
  212. CShellFolder::EnumObjects(
  213. HWND hwndOwner,
  214. DWORD dwFlags,
  215. LPENUMIDLIST *ppEnumIDList
  216. )
  217. /*++
  218. Determines the contents of a folder by creating an item enumeration
  219. object (a set of item identifiers) that can be retrieved using the
  220. IEnumIDList interface.
  221. --*/
  222. {
  223. *ppEnumIDList = new CEnumIDList(m_pidl, FALSE);
  224. if(*ppEnumIDList == NULL)
  225. return E_FAIL;
  226. return NOERROR;
  227. }
  228. STDMETHODIMP
  229. CShellFolder::GetAttributesOf(
  230. UINT uCount,
  231. LPCITEMIDLIST aPidls[],
  232. ULONG *pulAttribs
  233. )
  234. /*++
  235. Retrieves the attributes of one or more file objects or subfolders.
  236. Builds a ULONG value that specifies the common (logically AND'ed)
  237. attributes of specified file objects, which is supplied to the caller
  238. via the pdwAttribs parameter.
  239. --*/
  240. {
  241. UINT i;
  242. *pulAttribs = (ULONG)-1; // assume all in common initially
  243. for(i = 0; i < uCount; i++)
  244. {
  245. DWORD dwSubFolder = SFGAO_CANDELETE | SFGAO_HASPROPSHEET;
  246. if(HasSubFolders(aPidls[i]))
  247. dwSubFolder |= SFGAO_HASSUBFOLDER;
  248. *pulAttribs &= (SFGAO_FOLDER | dwSubFolder);
  249. }
  250. return NOERROR;
  251. }
  252. BOOL
  253. CShellFolder::HasSubFolders(
  254. LPCITEMIDLIST pidl
  255. )
  256. /*++
  257. This function is used to test if the specified pidl has subfolders.
  258. The combination of this->m_pidl and the supplied pidl can be used
  259. to make this determination.
  260. --*/
  261. {
  262. //
  263. // If we are at the subtype level, then no subfolders exist
  264. //
  265. if(GetPidlType(pidl) >= PIDL_TYPE_SUBTYPE)
  266. return FALSE;
  267. //
  268. // TODO: is there anyway to check if the root has subfolders?
  269. // m_pidl == NULL or pidl == NULL ???
  270. // then try to enum providers.
  271. //
  272. //
  273. // make a fully qualified (absolute) pidl out of m_pidl and pidl,
  274. // then call the enum interface to see if subfolders exist.
  275. //
  276. LPITEMIDLIST pidlNew = CopyCatPidl(m_pidl, pidl);
  277. if(pidlNew == NULL)
  278. return FALSE;
  279. BOOL bSubfolder = FALSE;
  280. LPENUMIDLIST pEnumIDList = new CEnumIDList(pidlNew, FALSE);
  281. if(pEnumIDList != NULL) {
  282. ULONG ulFetched;
  283. LPITEMIDLIST pidl = NULL;
  284. if( NOERROR == pEnumIDList->Next(1, &pidl, &ulFetched) && ulFetched == 1) {
  285. FreePidl(pidl);
  286. bSubfolder = TRUE;
  287. }
  288. pEnumIDList->Release();
  289. }
  290. FreePidl(pidlNew);
  291. return bSubfolder;
  292. }
  293. STDMETHODIMP
  294. CShellFolder::GetDisplayNameOf(
  295. LPCITEMIDLIST pidl,
  296. DWORD dwFlags,
  297. LPSTRRET lpName
  298. )
  299. /*++
  300. Retrieves the display name for the specified file object or subfolder,
  301. returning it in a STRRET structure.
  302. If the ID contains the display name (in the local character set),
  303. it returns the offset to the name. If not, it returns a pointer to
  304. the display name string (UNICODE) allocated by the task allocator,
  305. or it fills in a buffer. The type of string returned depends on the
  306. type of display specified.
  307. Values identifying different types of display names are contained
  308. in the enumeration SHGNO.
  309. --*/
  310. {
  311. switch(dwFlags)
  312. {
  313. case SHGDN_NORMAL:
  314. case SHGDN_INFOLDER:
  315. case SHGDN_FORPARSING:
  316. {
  317. LPCWSTR szDisplay;
  318. DWORD cbDisplay;
  319. LPWSTR pOleStr;
  320. szDisplay = GetPidlText(pidl);
  321. cbDisplay = (lstrlenW(szDisplay) + 1) * sizeof(WCHAR);
  322. pOleStr = (LPWSTR)CoTaskMemAlloc(cbDisplay);
  323. if(pOleStr == NULL)
  324. return E_OUTOFMEMORY;
  325. CopyMemory(pOleStr, szDisplay, cbDisplay);
  326. lpName->uType = STRRET_WSTR;
  327. lpName->pOleStr = pOleStr;
  328. return NOERROR;
  329. }
  330. default:
  331. return E_INVALIDARG;
  332. }
  333. }
  334. BOOL
  335. CShellFolder::GetPidlFullText(
  336. LPCITEMIDLIST pidl,
  337. LPTSTR lpszOut,
  338. DWORD dwOutSize
  339. )
  340. /*++
  341. This function returns a string containing the full-text associated with
  342. the supplied pidl. The full text will consist of a "full path" string,
  343. similiar to a fully qualified directory entry.
  344. --*/
  345. {
  346. return FALSE;
  347. }
  348. STDMETHODIMP
  349. CShellFolder::BindToStorage(
  350. LPCITEMIDLIST pidl,
  351. LPBC pbcReserved,
  352. REFIID riid,
  353. LPVOID *ppvOut
  354. )
  355. /*++
  356. ...Reserved for a future use. This method should return E_NOTIMPL. ...
  357. --*/
  358. {
  359. return E_NOTIMPL;
  360. }
  361. STDMETHODIMP
  362. CShellFolder::GetUIObjectOf(
  363. HWND hwndOwner,
  364. UINT cidl,
  365. LPCITEMIDLIST *pPidl,
  366. REFIID riid,
  367. LPUINT puReserved,
  368. LPVOID *ppvReturn
  369. )
  370. /*++
  371. Creates a COM object that can be used to carry out actions on the
  372. specified file objects or folders, typically, to create context menus
  373. or carry out drag-and-drop operations.
  374. --*/
  375. {
  376. *ppvReturn = NULL;
  377. if(IsEqualIID(riid, IID_IExtractIcon)) {
  378. if(cidl != 1)
  379. return E_INVALIDARG;
  380. *ppvReturn = (IExtractIcon*)( new CExtractIcon( pPidl[0] ) );
  381. }
  382. else if(IsEqualIID(riid, IID_IContextMenu)) {
  383. if(cidl == 0)
  384. return E_INVALIDARG;
  385. }
  386. else if(IsEqualIID(riid, IID_IDataObject)) {
  387. if(cidl == 0)
  388. return E_INVALIDARG;
  389. }
  390. if(*ppvReturn == NULL)
  391. return E_NOINTERFACE;
  392. (*(LPUNKNOWN*)ppvReturn)->AddRef();
  393. return NOERROR;
  394. }
  395. STDMETHODIMP
  396. CShellFolder::ParseDisplayName(
  397. HWND hwndOwner,
  398. LPBC pbcReserved,
  399. LPOLESTR lpDisplayName,
  400. LPDWORD pdwEaten,
  401. LPITEMIDLIST *pPidlNew,
  402. LPDWORD pdwAttributes
  403. )
  404. {
  405. return E_NOTIMPL;
  406. }
  407. STDMETHODIMP
  408. CShellFolder::SetNameOf(
  409. HWND hwndOwner,
  410. LPCITEMIDLIST pidl,
  411. LPCOLESTR lpName,
  412. DWORD dw,
  413. LPITEMIDLIST *pPidlOut
  414. )
  415. {
  416. return E_NOTIMPL;
  417. }