Author: dquintana Date: Sat Jun 28 15:38:25 2014 New Revision: 63658
URL: http://svn.reactos.org/svn/reactos?rev=63658&view=rev Log: [RSHELL] * Implement a rudimentary but mostly functional (for start menu purposes) CMergedFolder class. It's still disabled by default because clicking on an item in a merged folder appears to fail to execute it.
Modified: branches/shell-experiments/base/shell/rshell/CMakeLists.txt branches/shell-experiments/base/shell/rshell/CMenuToolbars.cpp branches/shell-experiments/base/shell/rshell/CMergedFolder.cpp branches/shell-experiments/base/shell/rshell/CMergedFolder.h branches/shell-experiments/include/psdk/commctrl.h
Modified: branches/shell-experiments/base/shell/rshell/CMakeLists.txt URL: http://svn.reactos.org/svn/reactos/branches/shell-experiments/base/shell/rsh... ============================================================================== --- branches/shell-experiments/base/shell/rshell/CMakeLists.txt [iso-8859-1] (original) +++ branches/shell-experiments/base/shell/rshell/CMakeLists.txt [iso-8859-1] Sat Jun 28 15:38:25 2014 @@ -31,6 +31,7 @@ uxtheme shlwapi shell32 + comctl32 gdi32 ole32 user32
Modified: branches/shell-experiments/base/shell/rshell/CMenuToolbars.cpp URL: http://svn.reactos.org/svn/reactos/branches/shell-experiments/base/shell/rsh... ============================================================================== --- branches/shell-experiments/base/shell/rshell/CMenuToolbars.cpp [iso-8859-1] (original) +++ branches/shell-experiments/base/shell/rshell/CMenuToolbars.cpp [iso-8859-1] Sat Jun 28 15:38:25 2014 @@ -1260,10 +1260,9 @@ IEnumIDList * eidl; m_shellFolder->EnumObjects(GetToolbar(), SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &eidl);
- LPITEMIDLIST item = static_cast<LPITEMIDLIST>(CoTaskMemAlloc(sizeof(ITEMIDLIST))); - ULONG fetched; - hr = eidl->Next(1, &item, &fetched); - while (SUCCEEDED(hr) && fetched > 0) + LPITEMIDLIST item = { 0 }; + hr = eidl->Next(1, &item, NULL); + while (hr == S_OK) { INT index = 0; INT indexOpen = 0; @@ -1286,13 +1285,13 @@ DWORD_PTR dwData = reinterpret_cast<DWORD_PTR>(ILClone(item));
// Fetch next item already, so we know if the current one is the last - hr = eidl->Next(1, &item, &fetched); - - AddButton(++i, MenuString, attrs & SFGAO_FOLDER, index, dwData, FAILED(hr) || fetched == 0); + hr = eidl->Next(1, &item, NULL); + + AddButton(++i, MenuString, attrs & SFGAO_FOLDER, index, dwData, hr != S_OK);
CoTaskMemFree(MenuString); } - CoTaskMemFree(item); + ILFree(item);
// If no items were added, show the "empty" placeholder if (i == 0)
Modified: branches/shell-experiments/base/shell/rshell/CMergedFolder.cpp URL: http://svn.reactos.org/svn/reactos/branches/shell-experiments/base/shell/rsh... ============================================================================== --- branches/shell-experiments/base/shell/rshell/CMergedFolder.cpp [iso-8859-1] (original) +++ branches/shell-experiments/base/shell/rshell/CMergedFolder.cpp [iso-8859-1] Sat Jun 28 15:38:25 2014 @@ -25,18 +25,33 @@
WINE_DEFAULT_DEBUG_CHANNEL(CMergedFolder);
+struct LocalPidlInfo +{ + int side; // -1 local, 0 shared, 1 common + LPITEMIDLIST pidl; +}; + class CEnumMergedFolder : public CComObjectRootEx<CComMultiThreadModelNoCS>, public IEnumIDList { + private: + CComPtr<IShellFolder> m_UserLocalFolder; + CComPtr<IShellFolder> m_AllUSersFolder; CComPtr<IEnumIDList> m_UserLocal; CComPtr<IEnumIDList> m_AllUSers; - BOOL m_FirstDone; + + HWND m_HwndOwner; + SHCONTF m_Flags; + + HDSA m_hDsa; + UINT m_hDsaIndex; + UINT m_hDsaCount;
public: - CEnumMergedFolder() : m_UserLocal(NULL), m_AllUSers(NULL), m_FirstDone(FALSE) {} - virtual ~CEnumMergedFolder() {} + CEnumMergedFolder(); + virtual ~CEnumMergedFolder();
DECLARE_NOT_AGGREGATABLE(CEnumMergedFolder) DECLARE_PROTECT_FINAL_CONSTRUCT() @@ -45,83 +60,345 @@ COM_INTERFACE_ENTRY_IID(IID_IEnumIDList, IEnumIDList) END_COM_MAP()
- HRESULT Begin(HWND hwndOwner, SHCONTF flags, IShellFolder * userLocal, IShellFolder * allUSers) - { - HRESULT hr; - hr = userLocal->EnumObjects(hwndOwner, flags, &m_UserLocal); - if (FAILED_UNEXPECTEDLY(hr)) - return hr; - hr = userLocal->EnumObjects(hwndOwner, flags, &m_AllUSers); - if (FAILED_UNEXPECTEDLY(hr)) - { - m_UserLocal = NULL; - return hr; - } - m_FirstDone = FALSE; - return S_OK; - } + int DsaDeleteCallback(LocalPidlInfo * info); + + static int CALLBACK s_DsaDeleteCallback(void *pItem, void *pData); + + HRESULT SetSources(IShellFolder * userLocal, IShellFolder * allUSers); + HRESULT Begin(HWND hwndOwner, SHCONTF flags); + HRESULT FindPidlInList(LPCITEMIDLIST pcidl, LocalPidlInfo * pinfo);
virtual HRESULT STDMETHODCALLTYPE Next( ULONG celt, LPITEMIDLIST *rgelt, - ULONG *pceltFetched) + ULONG *pceltFetched); + + virtual HRESULT STDMETHODCALLTYPE Skip(ULONG celt); + virtual HRESULT STDMETHODCALLTYPE Reset(); + virtual HRESULT STDMETHODCALLTYPE Clone(IEnumIDList **ppenum); +}; + +CEnumMergedFolder::CEnumMergedFolder() : + m_UserLocalFolder(NULL), + m_AllUSersFolder(NULL), + m_UserLocal(NULL), + m_AllUSers(NULL), + m_HwndOwner(NULL), + m_Flags(0), + m_hDsaIndex(0) +{ + m_hDsa = DSA_Create(sizeof(LocalPidlInfo), 10); +} + +CEnumMergedFolder::~CEnumMergedFolder() +{ + DSA_DestroyCallback(m_hDsa, s_DsaDeleteCallback, this); +} + +int CEnumMergedFolder::DsaDeleteCallback(LocalPidlInfo * info) +{ + ILFree(info->pidl); + return 0; +} + +int CALLBACK CEnumMergedFolder::s_DsaDeleteCallback(void *pItem, void *pData) +{ + CEnumMergedFolder * mf = (CEnumMergedFolder*) pData; + LocalPidlInfo * item = (LocalPidlInfo*) pItem; + return mf->DsaDeleteCallback(item); +} + +HRESULT CEnumMergedFolder::SetSources(IShellFolder * userLocal, IShellFolder * allUSers) +{ + m_UserLocalFolder = userLocal; + m_AllUSersFolder = allUSers; + return S_OK; +} + +HRESULT CEnumMergedFolder::Begin(HWND hwndOwner, SHCONTF flags) +{ + HRESULT hr; + + if (m_HwndOwner == hwndOwner && m_Flags == flags) { - HRESULT hr; - - *pceltFetched = 0; - - if (!m_FirstDone) - { - hr = m_UserLocal->Next(celt, rgelt, pceltFetched); + return Reset(); + } + + TRACE("Search conditions changed, recreating list...\n"); + + hr = m_UserLocalFolder->EnumObjects(hwndOwner, flags, &m_UserLocal); + if (FAILED_UNEXPECTEDLY(hr)) + return hr; + hr = m_AllUSersFolder->EnumObjects(hwndOwner, flags, &m_AllUSers); + if (FAILED_UNEXPECTEDLY(hr)) + { + m_UserLocal = NULL; + return hr; + } + + DSA_EnumCallback(m_hDsa, s_DsaDeleteCallback, this); + DSA_DeleteAllItems(m_hDsa); + m_hDsaCount = 0; + + HRESULT hr1 = S_OK; + HRESULT hr2 = S_OK; + LPITEMIDLIST pidl1 = NULL; + LPITEMIDLIST pidl2 = NULL; + int order = 0; + do + { + if (order <= 0) + { + if (hr1 == S_OK) + { + hr1 = m_UserLocal->Next(1, &pidl1, NULL); + if (FAILED_UNEXPECTEDLY(hr1)) + return hr1; + } + else + { + pidl1 = NULL; + } + } + if (order >= 0) + { + if (hr2 == S_OK) + { + hr2 = m_AllUSers->Next(1, &pidl2, NULL); + if (FAILED_UNEXPECTEDLY(hr2)) + return hr2; + } + else + { + pidl2 = NULL; + } + } + + if (hr1 == S_OK && hr2 == S_OK) + { + LPWSTR name1; + LPWSTR name2; + STRRET str1 = { STRRET_WSTR }; + STRRET str2 = { STRRET_WSTR }; + hr = m_UserLocalFolder->GetDisplayNameOf(pidl1, SHGDN_FORPARSING | SHGDN_INFOLDER, &str1); + if (FAILED(hr)) + return hr; + hr = m_AllUSersFolder->GetDisplayNameOf(pidl2, SHGDN_FORPARSING | SHGDN_INFOLDER, &str2); + if (FAILED(hr)) + return hr; + StrRetToStrW(&str1, pidl1, &name1); + StrRetToStrW(&str2, pidl2, &name2); + order = StrCmpW(name1, name2); + CoTaskMemFree(name1); + CoTaskMemFree(name2); + } + else if (hr1 == S_OK) + { + order = -1; + } + else if (hr2 == S_OK) + { + order = 1; + } + else + { + break; + } + + LocalPidlInfo info; + if (order < 0) + { + info.side = -1; + info.pidl = ILClone(pidl1); + ILFree(pidl1); + } + else if (order > 0) + { + info.side = 1; + info.pidl = ILClone(pidl2); + ILFree(pidl2); + } + else // if (order == 0) + { + info.side = 0; + info.pidl = ILClone(pidl1); + ILFree(pidl1); + ILFree(pidl2); + } + + TRACE("Inserting item %d with side %d and pidl { cb=%d }\n", m_hDsaCount, info.side, info.pidl->mkid.cb); + int idx = DSA_InsertItem(m_hDsa, DSA_APPEND, &info); + TRACE("New index: %d\n", idx); + + m_hDsaCount++; + + } while (hr1 == S_OK || hr2 == S_OK); + + m_HwndOwner = hwndOwner; + m_Flags = flags; + + return Reset(); +} + +HRESULT CEnumMergedFolder::FindPidlInList(LPCITEMIDLIST pcidl, LocalPidlInfo * pinfo) +{ + HRESULT hr; + + TRACE("Searching for pidl { cb=%d } in a list of %d items\n", pcidl->mkid.cb, m_hDsaCount); + + for (int i = 0; i < (int)m_hDsaCount; i++) + { + LocalPidlInfo * tinfo = (LocalPidlInfo *)DSA_GetItemPtr(m_hDsa, i); + if (!tinfo) + return E_FAIL; + + LocalPidlInfo info = *tinfo; + + TRACE("Comparing with item at %d with side %d and pidl { cb=%d }\n", i, info.side, info.pidl->mkid.cb); + + if (info.side <= 0) + { +#if 0 + LPWSTR name1; + LPWSTR name2; + STRRET str1 = { STRRET_WSTR, 0 }; + STRRET str2 = { STRRET_WSTR, 0 }; + hr = m_UserLocalFolder->GetDisplayNameOf(info->pidl, SHGDN_FORPARSING | SHGDN_INFOLDER, &str1); + if (FAILED(hr)) + return hr; + hr = m_UserLocalFolder->GetDisplayNameOf(pcidl, SHGDN_FORPARSING | SHGDN_INFOLDER, &str2); + if (FAILED(hr)) + return hr; + StrRetToStrW(&str1, info->pidl, &name1); + StrRetToStrW(&str2, pcidl, &name2); + int order = StrCmpW(name1, name2); + CoTaskMemFree(name1); + CoTaskMemFree(name2); + + if (order == 0) + { + *pinfo = *info; + return S_OK; + } +#else + // FIXME: This works in windows. + hr = m_UserLocalFolder->CompareIDs(0, info.pidl, pcidl); if (FAILED_UNEXPECTEDLY(hr)) return hr; - if (hr == S_FALSE) - m_FirstDone = true; - else if (celt < 2) - return hr; - } - - DWORD offset = *pceltFetched; - if (*pceltFetched < celt) - { - rgelt += *pceltFetched; - celt = (celt - *pceltFetched); - *pceltFetched = 0; - - hr = m_AllUSers->Next(celt, rgelt, pceltFetched); + if (hr == S_OK) + { + *pinfo = info; + return S_OK; + } + else + { + TRACE("Comparison returned %d\n", (int) (short) (hr & 0xFFFF)); + } +#endif + } + else + { +#if 0 + LPWSTR name1; + LPWSTR name2; + STRRET str1 = { STRRET_WSTR, 0 }; + STRRET str2 = { STRRET_WSTR, 0 }; + hr = m_AllUSersFolder->GetDisplayNameOf(info->pidl, SHGDN_FORPARSING | SHGDN_INFOLDER, &str1); + if (FAILED(hr)) + return hr; + hr = m_AllUSersFolder->GetDisplayNameOf(pcidl, SHGDN_FORPARSING | SHGDN_INFOLDER, &str2); + if (FAILED(hr)) + return hr; + StrRetToStrW(&str1, info->pidl, &name1); + StrRetToStrW(&str2, pcidl, &name2); + int order = StrCmpW(name1, name2); + CoTaskMemFree(name1); + CoTaskMemFree(name2); + + if (order == 0) + { + *pinfo = *info; + return S_OK; + } +#else + // FIXME: This works in windows. + hr = m_AllUSersFolder->CompareIDs(0, info.pidl, pcidl); if (FAILED_UNEXPECTEDLY(hr)) return hr; - - *pceltFetched += offset; - - return hr; - } - - return S_OK; + if (hr == S_OK) + { + *pinfo = info; + return S_OK; + } + else + { + TRACE("Comparison returned %d\n", (int) (short) (hr & 0xFFFF)); + } +#endif + } }
- virtual HRESULT STDMETHODCALLTYPE Skip( - ULONG celt) + TRACE("Pidl not found\n"); + return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); +} + +HRESULT STDMETHODCALLTYPE CEnumMergedFolder::Next( + ULONG celt, + LPITEMIDLIST *rgelt, + ULONG *pceltFetched) +{ + if (pceltFetched) *pceltFetched = 0; + + if (m_hDsaIndex == m_hDsaCount) + return S_FALSE; + + for (int i = 0; i < (int)celt;) { - UNIMPLEMENTED; - return E_NOTIMPL; + LocalPidlInfo * tinfo = (LocalPidlInfo *) DSA_GetItemPtr(m_hDsa, m_hDsaIndex); + if (!tinfo) + return E_FAIL; + + LocalPidlInfo info = *tinfo; + + TRACE("Returning next item at %d with side %d and pidl { cb=%d }\n", m_hDsaIndex, info.side, info.pidl->mkid.cb); + + // FIXME: ILClone shouldn't be needed here! This should be causing leaks + if (rgelt) rgelt[i] = ILClone(info.pidl); + + m_hDsaIndex++; + i++; + + if (m_hDsaIndex == m_hDsaCount) + { + if (pceltFetched) *pceltFetched = i; + return (i == (int)celt) ? S_OK : S_FALSE; + } }
- virtual HRESULT STDMETHODCALLTYPE Reset( - ) - { - if (m_FirstDone) - m_AllUSers->Reset(); - return m_UserLocal->Reset(); - } - - virtual HRESULT STDMETHODCALLTYPE Clone( - IEnumIDList **ppenum) - { - UNIMPLEMENTED; - return E_NOTIMPL; - } -}; + if (pceltFetched) *pceltFetched = celt; + return S_OK; +} + +HRESULT STDMETHODCALLTYPE CEnumMergedFolder::Skip(ULONG celt) +{ + return Next(celt, NULL, NULL); +} + +HRESULT STDMETHODCALLTYPE CEnumMergedFolder::Reset() +{ + m_hDsaIndex = 0; + return S_OK; +} + +HRESULT STDMETHODCALLTYPE CEnumMergedFolder::Clone( + IEnumIDList **ppenum) +{ + UNIMPLEMENTED; + return E_NOTIMPL; +} + +//----------------------------------------------------------------------------- +// CMergedFolder
extern "C" HRESULT WINAPI CMergedFolder_Constructor(IShellFolder* userLocal, IShellFolder* allUsers, REFIID riid, LPVOID *ppv) @@ -148,7 +425,8 @@ { m_UserLocal = userLocal; m_AllUSers = allUsers; - return S_OK; + m_EnumSource = new CComObject<CEnumMergedFolder>(); + return m_EnumSource->SetSources(m_UserLocal, m_AllUSers); }
// IShellFolder @@ -169,9 +447,10 @@ SHCONTF grfFlags, IEnumIDList **ppenumIDList) { - CEnumMergedFolder * merged = new CComObject<CEnumMergedFolder>(); - *ppenumIDList = merged; - return merged->Begin(hwndOwner, grfFlags, m_UserLocal, m_AllUSers); + HRESULT hr = m_EnumSource->QueryInterface(IID_PPV_ARG(IEnumIDList, ppenumIDList)); + if (FAILED_UNEXPECTEDLY(hr)) + return hr; + return m_EnumSource->Begin(hwndOwner, grfFlags); }
HRESULT STDMETHODCALLTYPE CMergedFolder::BindToObject( @@ -180,15 +459,33 @@ REFIID riid, void **ppvOut) { + LocalPidlInfo info; HRESULT hr;
- hr = m_UserLocal->BindToObject(pidl, pbcReserved, riid, ppvOut); - if (SUCCEEDED(hr)) - return hr; - - hr = m_AllUSers->BindToObject(pidl, pbcReserved, riid, ppvOut); - - return hr; + hr = m_EnumSource->FindPidlInList(pidl, &info); + if (FAILED_UNEXPECTEDLY(hr)) + return hr; + + if (info.side < 0) + return m_UserLocal->BindToObject(pidl, pbcReserved, riid, ppvOut); + if (info.side > 0) + return m_AllUSers->BindToObject(pidl, pbcReserved, riid, ppvOut); + + if (riid != IID_IShellFolder) + return E_FAIL; + + CComPtr<IShellFolder> fld1; + CComPtr<IShellFolder> fld2; + + hr = m_UserLocal->BindToObject(pidl, pbcReserved, IID_PPV_ARG(IShellFolder, &fld1)); + if (FAILED_UNEXPECTEDLY(hr)) + return hr; + + hr = m_AllUSers->BindToObject(pidl, pbcReserved, IID_PPV_ARG(IShellFolder, &fld2)); + if (FAILED_UNEXPECTEDLY(hr)) + return hr; + + return CMergedFolder_Constructor(fld1, fld2, riid, ppvOut); }
HRESULT STDMETHODCALLTYPE CMergedFolder::BindToStorage( @@ -206,8 +503,7 @@ LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2) { - UNIMPLEMENTED; - return E_NOTIMPL; + return m_UserLocal->CompareIDs(lParam, pidl1, pidl2); }
HRESULT STDMETHODCALLTYPE CMergedFolder::CreateViewObject( @@ -224,16 +520,29 @@ LPCITEMIDLIST *apidl, SFGAOF *rgfInOut) { + LocalPidlInfo info; HRESULT hr;
- hr = m_UserLocal->GetAttributesOf(cidl, apidl, rgfInOut); - if (SUCCEEDED(hr)) - return hr; - - *rgfInOut = 0; - hr = m_AllUSers->GetAttributesOf(cidl, apidl, rgfInOut); - - return hr; + for (int i = 0; i < (int)cidl; i++) + { + LPCITEMIDLIST pidl = apidl[i]; + + hr = m_EnumSource->FindPidlInList(pidl, &info); + if (FAILED_UNEXPECTEDLY(hr)) + return hr; + + SFGAOF * pinOut1 = rgfInOut ? rgfInOut + i : NULL; + + if (info.side <= 0) + hr = m_UserLocal->GetAttributesOf(1, &pidl, pinOut1); + else + hr = m_AllUSers->GetAttributesOf(1, &pidl, pinOut1); + + if (FAILED_UNEXPECTEDLY(hr)) + return hr; + } + + return S_OK; }
HRESULT STDMETHODCALLTYPE CMergedFolder::GetUIObjectOf( @@ -244,15 +553,30 @@ UINT *prgfInOut, void **ppvOut) { + LocalPidlInfo info; HRESULT hr;
- hr = m_UserLocal->GetUIObjectOf(hwndOwner, cidl, apidl, riid, prgfInOut, ppvOut); - if (SUCCEEDED(hr)) - return hr; - - hr = m_AllUSers->GetUIObjectOf(hwndOwner, cidl, apidl, riid, prgfInOut, ppvOut); - - return hr; + for (int i = 0; i < (int)cidl; i++) + { + LPCITEMIDLIST pidl = apidl[i]; + + hr = m_EnumSource->FindPidlInList(pidl, &info); + if (FAILED_UNEXPECTEDLY(hr)) + return hr; + + UINT * pinOut1 = prgfInOut ? prgfInOut+i : NULL; + void** ppvOut1 = ppvOut ? ppvOut + i : NULL; + + if (info.side <= 0) + hr = m_UserLocal->GetUIObjectOf(hwndOwner, 1, &pidl, riid, pinOut1, ppvOut1); + else + hr = m_AllUSers->GetUIObjectOf(hwndOwner, 1, &pidl, riid, pinOut1, ppvOut1); + + if (FAILED_UNEXPECTEDLY(hr)) + return hr; + } + + return S_OK; }
HRESULT STDMETHODCALLTYPE CMergedFolder::GetDisplayNameOf( @@ -260,15 +584,21 @@ SHGDNF uFlags, STRRET *lpName) { + LocalPidlInfo info; HRESULT hr;
- hr = m_UserLocal->GetDisplayNameOf(pidl, uFlags, lpName); - if (SUCCEEDED(hr)) - return hr; - - hr = m_AllUSers->GetDisplayNameOf(pidl, uFlags, lpName); - - return hr; + hr = m_EnumSource->FindPidlInList(pidl, &info); + if (FAILED_UNEXPECTEDLY(hr)) + return hr; + + if (info.side <= 0) + hr = m_UserLocal->GetDisplayNameOf(pidl, uFlags, lpName); + else + hr = m_AllUSers->GetDisplayNameOf(pidl, uFlags, lpName); + + if (FAILED_UNEXPECTEDLY(hr)) + return hr; + return S_OK; }
HRESULT STDMETHODCALLTYPE CMergedFolder::SetNameOf(
Modified: branches/shell-experiments/base/shell/rshell/CMergedFolder.h URL: http://svn.reactos.org/svn/reactos/branches/shell-experiments/base/shell/rsh... ============================================================================== --- branches/shell-experiments/base/shell/rshell/CMergedFolder.h [iso-8859-1] (original) +++ branches/shell-experiments/base/shell/rshell/CMergedFolder.h [iso-8859-1] Sat Jun 28 15:38:25 2014 @@ -19,6 +19,8 @@ */ #pragma once
+class CEnumMergedFolder; + class CMergedFolder : public CComObjectRootEx<CComMultiThreadModelNoCS>, public IShellFolder2 @@ -26,6 +28,7 @@ private: CComPtr<IShellFolder> m_UserLocal; CComPtr<IShellFolder> m_AllUSers; + CComPtr<CEnumMergedFolder> m_EnumSource;
public: CMergedFolder() {}
Modified: branches/shell-experiments/include/psdk/commctrl.h URL: http://svn.reactos.org/svn/reactos/branches/shell-experiments/include/psdk/c... ============================================================================== --- branches/shell-experiments/include/psdk/commctrl.h [iso-8859-1] (original) +++ branches/shell-experiments/include/psdk/commctrl.h [iso-8859-1] Sat Jun 28 15:38:25 2014 @@ -4716,7 +4716,17 @@ _In_ PFNDSAENUMCALLBACK pfnCB, _In_opt_ void *pData);
+ WINCOMMCTRLAPI + VOID + WINAPI + DSA_EnumCallback( + _In_ HDSA hdsa, + _In_ PFNDSAENUMCALLBACK enumProc, + _In_opt_ LPVOID lParam); + WINCOMMCTRLAPI PVOID WINAPI DSA_GetItemPtr(_In_ HDSA hdsa, int i); + + WINCOMMCTRLAPI BOOL WINAPI DSA_DeleteAllItems(_In_ HDSA hdsa);
WINCOMMCTRLAPI int