From 769462faaaf28593f992bf8daa9d4630874707d9 Mon Sep 17 00:00:00 2001 From: Whindmar Saksit Date: Tue, 1 Apr 2025 16:09:25 +0200 Subject: [PATCH] [IEFRAME] Implement URL PIDL (#7850) --- dll/win32/ieframe/CMakeLists.txt | 1 + dll/win32/ieframe/ieframe_main.c | 21 + dll/win32/ieframe/inetfolder.c | 358 ++++++++++++++++++ dll/win32/shell32/wine/pidl.h | 3 + .../rostests/apitests/shell32/ItemIDList.cpp | 34 ++ 5 files changed, 417 insertions(+) create mode 100644 dll/win32/ieframe/inetfolder.c diff --git a/dll/win32/ieframe/CMakeLists.txt b/dll/win32/ieframe/CMakeLists.txt index 6e4e2d4522e..b6a38232312 100644 --- a/dll/win32/ieframe/CMakeLists.txt +++ b/dll/win32/ieframe/CMakeLists.txt @@ -13,6 +13,7 @@ list(APPEND SOURCE ieframe_main.c iehtmlwnd.c iexplore.c + inetfolder.c intshcut.c navigate.c oleobject.c diff --git a/dll/win32/ieframe/ieframe_main.c b/dll/win32/ieframe/ieframe_main.c index 5abe502f1e4..b07569e92ec 100644 --- a/dll/win32/ieframe/ieframe_main.c +++ b/dll/win32/ieframe/ieframe_main.c @@ -178,6 +178,20 @@ static const IClassFactoryVtbl CUrlHistoryFactoryVtbl = { static IClassFactory CUrlHistoryFactory = { &CUrlHistoryFactoryVtbl }; +#ifdef __REACTOS__ +extern HRESULT WINAPI CInternetFolder_CreateInstance(IClassFactory *iface, IUnknown *outer, REFIID riid, void **ppv); + +static const IClassFactoryVtbl CInternetFolderFactoryVtbl = { + ClassFactory_QueryInterface, + ClassFactory_AddRef, + ClassFactory_Release, + CInternetFolder_CreateInstance, + ClassFactory_LockServer +}; + +static IClassFactory CInternetFolderFactory = { &CInternetFolderFactoryVtbl }; +#endif + /****************************************************************** * DllMain (ieframe.@) */ @@ -226,6 +240,13 @@ HRESULT WINAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv) return IClassFactory_QueryInterface(&CUrlHistoryFactory, riid, ppv); } +#ifdef __REACTOS__ + if(IsEqualGUID(&CLSID_Internet, rclsid)) { + TRACE("(CLSID_Internet %s %p)\n", debugstr_guid(riid), ppv); + return IClassFactory_QueryInterface(&CInternetFolderFactory, riid, ppv); + } +#endif + FIXME("%s %s %p\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv); return CLASS_E_CLASSNOTAVAILABLE; } diff --git a/dll/win32/ieframe/inetfolder.c b/dll/win32/ieframe/inetfolder.c new file mode 100644 index 00000000000..80c1333a959 --- /dev/null +++ b/dll/win32/ieframe/inetfolder.c @@ -0,0 +1,358 @@ +/* + * PROJECT: ReactOS ieframe + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Internet IShellFolder implementation + * COPYRIGHT: Copyright 2025 Whindmar Saksit + */ + +#define NONAMELESSUNION + +#include "ieframe.h" + +#include "shlobj.h" +#include "shobjidl.h" +#include "shellapi.h" +#include "shlwapi.h" +#include "shlguid.h" +#include "intshcut.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(ieframe); + +extern int WINAPI SHAnsiToUnicodeCP(UINT CodePage, LPCSTR pszSrc, LPWSTR pwszDst, int cwchBuf); + +#define MAX_URL_LENGTH 1024 + +#define PT_INTERNET_URL 0x61 +#define IFUIF_UNICODE 0x80 +typedef struct _IFURLITEM +{ + WORD cb; + BYTE Type; // PT_INTERNET_URL + BYTE Flags; // IFUIF_* + UINT Unknown; + WCHAR Url[ANYSIZE_ARRAY]; +} IFURLITEM; + +static int GetSchemeCharType(WCHAR c) +{ + if (('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')) + return 0; + return c == '+' || c == '-' || c == '.' ? 1 : -1; +} + +static unsigned int GetSchemeLength(PCWSTR s) +{ + if (GetSchemeCharType(s[0]) != 0) // The first character MUST be A-Z, a-z. + return 0; + for (unsigned int i = 0;;) + { + if (s[++i] == ':') + return ++i; + if (GetSchemeCharType(s[i]) < 0) + return 0; + } +} + +static LPITEMIDLIST CreateUrlItem(PCWSTR pszUrl) +{ + UINT cch = lstrlenW(pszUrl) + 1; + UINT cb = FIELD_OFFSET(IFURLITEM, Url[cch]); + IFURLITEM *pidl = SHAlloc(cb + sizeof(WORD)); + if (!pidl) + return (LPITEMIDLIST)pidl; + pidl->cb = cb; + pidl->Type = PT_INTERNET_URL; + pidl->Flags = IFUIF_UNICODE; + pidl->Unknown = 0; + CopyMemory(pidl->Url, pszUrl, cch * sizeof(*pszUrl)); + ILGetNext((LPITEMIDLIST)pidl)->mkid.cb = 0; + return (LPITEMIDLIST)pidl; +} + +static IFURLITEM* IsUrlItem(LPCITEMIDLIST pidl) +{ + IFURLITEM *p = (IFURLITEM*)pidl; + if (p && p->cb > FIELD_OFFSET(IFURLITEM, Url) && p->Type == PT_INTERNET_URL) + return p; + return NULL; +} + +static PWSTR GetUrl(IFURLITEM *pUrl, PWSTR Buffer) +{ + if (pUrl->Flags & IFUIF_UNICODE) + return pUrl->Url; + SHAnsiToUnicodeCP(CP_ACP, (PCSTR)pUrl->Url, Buffer, MAX_URL_LENGTH); + return Buffer; +} + +static HRESULT CreateUrlShortcut(PCWSTR Url, IUniformResourceLocatorW **ppv) +{ + HRESULT hr = CoCreateInstance(&CLSID_InternetShortcut, NULL, CLSCTX_INPROC_SERVER, + &IID_IUniformResourceLocatorW, (void**)ppv); + if (SUCCEEDED(hr)) + hr = (*ppv)->lpVtbl->SetURL(*ppv, Url, 0); + return hr; +} + +typedef struct _CInternetFolder +{ + IShellFolder IShellFolder_iface; + IPersistFolder IPersistFolder_iface; + LONG refCount; +} CInternetFolder; + +static HRESULT Unknown_QueryInterface(CInternetFolder *This, REFIID riid, PVOID *ppvObject) +{ + TRACE("(%p, %s, %p)\n", This, debugstr_guid(riid), ppvObject); + *ppvObject = NULL; + if (IsEqualGUID(&IID_IUnknown, riid)) + *ppvObject = &This->IShellFolder_iface; + else if (IsEqualGUID(&IID_IShellFolder, riid)) + *ppvObject = &This->IShellFolder_iface; + else if (IsEqualGUID(&IID_IPersist, riid) || IsEqualGUID(&IID_IPersistFolder, riid)) + *ppvObject = &This->IPersistFolder_iface; + else + return E_NOINTERFACE; + IUnknown_AddRef((IUnknown*)*ppvObject); + return S_OK; +} + +static ULONG Unknown_AddRef(CInternetFolder *This) +{ + return InterlockedIncrement(&This->refCount); +} + +static ULONG Unknown_Release(CInternetFolder *This) +{ + const ULONG count = InterlockedDecrement(&This->refCount); + if (count == 0) + { + SHFree(This); + unlock_module(); + } + return count; +} + +static HRESULT WINAPI ShellFolder_QueryInterface(IShellFolder *This, REFIID riid, PVOID *ppvObject) +{ + return Unknown_QueryInterface(CONTAINING_RECORD(This, CInternetFolder, IShellFolder_iface), riid, ppvObject); +} + +static ULONG WINAPI ShellFolder_AddRef(IShellFolder *This) +{ + return Unknown_AddRef(CONTAINING_RECORD(This, CInternetFolder, IShellFolder_iface)); +} + +static ULONG WINAPI ShellFolder_Release(IShellFolder *This) +{ + return Unknown_Release(CONTAINING_RECORD(This, CInternetFolder, IShellFolder_iface)); +} + +static HRESULT WINAPI ParseDisplayName(IShellFolder *This, HWND hwnd, IBindCtx *pbc, LPWSTR pszDisplayName, ULONG *pchEaten, PIDLIST_RELATIVE *ppidl, ULONG *pdwAttributes) +{ + UINT len; + + *ppidl = NULL; + len = GetSchemeLength(pszDisplayName); + if (len) + { + if (len + 1 == sizeof("shell:")) + { + WCHAR szBuf[100]; + lstrcpynW(szBuf, pszDisplayName, sizeof("shell:")); + if (!lstrcmpiW(szBuf, L"shell:")) + return E_FAIL; + } + + if ((*ppidl = CreateUrlItem(pszDisplayName)) == NULL) + return E_OUTOFMEMORY; + if (pchEaten) + *pchEaten = lstrlenW(pszDisplayName); + if (pdwAttributes) + IShellFolder_GetAttributesOf(This, 1, (PCUITEMID_CHILD_ARRAY)ppidl, pdwAttributes); + return S_OK; + } + return E_FAIL; +} + +static HRESULT WINAPI ShellFolder_EnumObjects(IShellFolder *This, HWND hwndOwner, SHCONTF grfFlags, IEnumIDList **ppenumIDList) +{ + *ppenumIDList = NULL; + return E_NOTIMPL; +} + +static HRESULT WINAPI BindToObject(IShellFolder *This, PCUIDLIST_RELATIVE pidl, IBindCtx *pbc, REFIID riid, void **ppvOut) +{ + *ppvOut = NULL; + return E_NOTIMPL; +} + +static HRESULT WINAPI BindToStorage(IShellFolder *This, PCUIDLIST_RELATIVE pidl, IBindCtx *pbc, REFIID riid, void **ppvOut) +{ + *ppvOut = NULL; + return E_NOTIMPL; +} + +static HRESULT WINAPI CompareIDs(IShellFolder *This, LPARAM lParam, PCUIDLIST_RELATIVE pidl1, PCUIDLIST_RELATIVE pidl2) +{ + IFURLITEM *pUrl1 = IsUrlItem(pidl1), *pUrl2 = IsUrlItem(pidl2); + if (pUrl1 && pUrl2) + { + WCHAR szUrl1[MAX_URL_LENGTH], *pszUrl1 = GetUrl(pUrl1, szUrl1); + WCHAR szUrl2[MAX_URL_LENGTH], *pszUrl2 = GetUrl(pUrl2, szUrl2); + int cmp = lstrcmpiW(pszUrl1, pszUrl2); + return cmp < 0 ? 0xffff : cmp != 0; + } + return E_FAIL; +} + +static HRESULT WINAPI CreateViewObject(IShellFolder *This, HWND hwndOwner,REFIID riid,void **ppv) +{ + *ppv = NULL; + return E_NOTIMPL; +} + +static HRESULT WINAPI GetAttributesOf(IShellFolder *This, UINT cidl, PCUITEMID_CHILD_ARRAY apidl, SFGAOF *rgfInOut) +{ + UINT i; + + if (cidl == 0) + { + *rgfInOut &= SFGAO_FOLDER | SFGAO_CANLINK | SFGAO_STREAM; // Folder attributes + return S_OK; + } + + for (i = 0; i < cidl; ++i) + { + IFURLITEM *pUrl = IsUrlItem(apidl[i]); + if (!pUrl) + return E_FAIL; + *rgfInOut &= SFGAO_CANLINK | SFGAO_BROWSABLE | SFGAO_STREAM; + } + return S_OK; +} + +static HRESULT WINAPI GetUIObjectOf(IShellFolder *This, HWND hwndOwner, UINT cidl, PCUITEMID_CHILD_ARRAY apidl, REFIID riid, UINT *rgfReserved, void **ppv) +{ + IFURLITEM *pUrl; + if (cidl == 1 && (pUrl = IsUrlItem(apidl[0])) != NULL) + { + if (IsEqualIID(riid, &IID_IExtractIconW) || IsEqualIID(riid, &IID_IExtractIconA) || + IsEqualIID(riid, &IID_IContextMenu) || + IsEqualIID(riid, &IID_IDataObject) || + IsEqualIID(riid, &IID_IQueryInfo)) + { + IUniformResourceLocatorW *pUrlLnk; + WCHAR szUrl[MAX_URL_LENGTH], *pszUrl = GetUrl(pUrl, szUrl); + HRESULT hr = CreateUrlShortcut(pszUrl, &pUrlLnk); + if (SUCCEEDED(hr)) + { + hr = IUnknown_QueryInterface(pUrlLnk, riid, ppv); + IUnknown_Release(pUrlLnk); + } + return hr; + } + } + return E_NOINTERFACE; +} + +static HRESULT WINAPI GetDisplayNameOf(IShellFolder *This, PCUITEMID_CHILD pidl, SHGDNF uFlags, STRRET *pSR) +{ + IFURLITEM *pUrl = IsUrlItem(pidl); + if (pUrl) + { + WCHAR szUrl[MAX_URL_LENGTH], *pszUrl = GetUrl(pUrl, szUrl); + pSR->uType = STRRET_WSTR; + return SHStrDupW(pszUrl, &pSR->u.pOleStr); + } + return E_FAIL; +} + +static HRESULT WINAPI SetNameOf(IShellFolder *This, HWND hwndOwner, PCUITEMID_CHILD pidl, LPCWSTR pszName, SHGDNF uFlags, PITEMID_CHILD*ppidlOut) +{ + if (ppidlOut) + *ppidlOut = NULL; + return E_NOTIMPL; +} + +static HRESULT WINAPI PersistFolder_QueryInterface(IPersistFolder *This, REFIID riid, PVOID *ppvObject) +{ + return Unknown_QueryInterface(CONTAINING_RECORD(This, CInternetFolder, IPersistFolder_iface), riid, ppvObject); +} + +static ULONG WINAPI PersistFolder_AddRef(IPersistFolder *This) +{ + return Unknown_AddRef(CONTAINING_RECORD(This, CInternetFolder, IPersistFolder_iface)); +} + +static ULONG WINAPI PersistFolder_Release(IPersistFolder *This) +{ + return Unknown_Release(CONTAINING_RECORD(This, CInternetFolder, IPersistFolder_iface)); +} + +static HRESULT WINAPI PersistFolder_GetClassID(IPersistFolder *This, CLSID *pClassID) +{ + *pClassID = CLSID_Internet; + return S_OK; +} + +static HRESULT WINAPI PersistFolder_Initialize(IPersistFolder *This, PCIDLIST_ABSOLUTE pidl) +{ + return S_OK; +} + +static const IShellFolderVtbl ShellFolderVtbl = { + ShellFolder_QueryInterface, + ShellFolder_AddRef, + ShellFolder_Release, + ParseDisplayName, + ShellFolder_EnumObjects, + BindToObject, + BindToStorage, + CompareIDs, + CreateViewObject, + GetAttributesOf, + GetUIObjectOf, + GetDisplayNameOf, + SetNameOf +}; + +static const IPersistFolderVtbl PersistFolderVtbl = { + PersistFolder_QueryInterface, + PersistFolder_AddRef, + PersistFolder_Release, + PersistFolder_GetClassID, + PersistFolder_Initialize +}; + +static CInternetFolder* CreateInstance(void) +{ + CInternetFolder *obj = SHAlloc(sizeof(CInternetFolder)); + if (obj) + { + obj->IShellFolder_iface.lpVtbl = &ShellFolderVtbl; + obj->IPersistFolder_iface.lpVtbl = &PersistFolderVtbl; + obj->refCount = 1; + lock_module(); + } + return obj; +} + +HRESULT WINAPI CInternetFolder_CreateInstance(IClassFactory *iface, IUnknown *outer, REFIID riid, void **ppv) +{ + CInternetFolder *pThis; + + *ppv = NULL; + if (outer) + return CLASS_E_NOAGGREGATION; + + if ((pThis = CreateInstance()) != NULL) + { + HRESULT hr = Unknown_QueryInterface(pThis, riid, ppv); + Unknown_Release(pThis); + return hr; + } + return E_OUTOFMEMORY; +} diff --git a/dll/win32/shell32/wine/pidl.h b/dll/win32/shell32/wine/pidl.h index e5cb36b2b2d..0711f3f5a17 100644 --- a/dll/win32/shell32/wine/pidl.h +++ b/dll/win32/shell32/wine/pidl.h @@ -108,12 +108,15 @@ extern "C" { #define PT_FOLDERTYPEMASK 0x70 #define PT_DESKTOP_REGITEM 0x1F // => SHDID_ROOT_REGITEM #define PT_COMPUTER_REGITEM 0x2E // => SHDID_COMPUTER_? +#define PT_COMPUTER_DRIVE 0x2F #define PT_FS 0x30 // Win95 SHSimpleIDListFromPath #define PT_FS_FOLDER_FLAG 0x01 #define PT_FS_FILE_FLAG 0x02 #define PT_FS_UNICODE_FLAG 0x04 #define PT_FS_COMMON_FLAG 0x08 // PT_NET_REGITEM 0x4? // => SHDID_NET_OTHER +#define PT_INTERNET 0x60 +#define PT_INTERNET_URL 0x61 #define PT_CONTROLS_OLDREGITEM 0x70 #define PT_CONTROLS_NEWREGITEM 0x71 #endif diff --git a/modules/rostests/apitests/shell32/ItemIDList.cpp b/modules/rostests/apitests/shell32/ItemIDList.cpp index 02c4e694bd6..14d9f551d47 100644 --- a/modules/rostests/apitests/shell32/ItemIDList.cpp +++ b/modules/rostests/apitests/shell32/ItemIDList.cpp @@ -12,6 +12,7 @@ enum { DIRBIT = 1, FILEBIT = 2, PT_COMPUTER_REGITEM = 0x2E, + PT_INTERNET_URL = 0x61, }; static BYTE GetPIDLType(LPCITEMIDLIST pidl) @@ -48,6 +49,15 @@ static int FileStruct_Att(LPCITEMIDLIST pidl) return p ? p->att : (UINT(1) << 31); } +static HRESULT GetDisplayNameOf(IShellFolder *pSF, LPCITEMIDLIST pidl, UINT Flags, PWSTR Buf, UINT Cap) +{ + STRRET sr; + HRESULT hr = pSF->GetDisplayNameOf(pidl, Flags, &sr); + if (SUCCEEDED(hr)) + hr = StrRetToBufW(&sr, pidl, Buf, Cap); + return hr; +} + #define TEST_CLSID(pidl, type, offset, clsid) \ do { \ ok_long(GetPIDLType(pidl), (type)); \ @@ -195,6 +205,7 @@ START_TEST(ILCreateFromPath) START_TEST(PIDL) { + CCoInit ComInit; LPITEMIDLIST pidl; pidl = SHCloneSpecialIDList(NULL, CSIDL_DRIVES, FALSE); @@ -210,6 +221,29 @@ START_TEST(PIDL) else skip("?\n"); ILFree(pidl); + + + CComPtr pInternet; + HRESULT hr = SHCoCreateInstance(NULL, &CLSID_Internet, NULL, IID_PPV_ARG(IShellFolder, &pInternet)); + if (SUCCEEDED(hr)) + { + PCWSTR pszUrl = L"http://example.com/page?query&foo=bar"; + PIDLIST_RELATIVE pidlUrl; + hr = pInternet->ParseDisplayName(NULL, NULL, const_cast(pszUrl), NULL, &pidlUrl, NULL); + ok_long(hr, S_OK); + if (hr == S_OK) + { + ok_int(pidlUrl->mkid.abID[0], PT_INTERNET_URL); + WCHAR buf[MAX_PATH]; + hr = GetDisplayNameOf(pInternet, pidlUrl, SHGDN_FORPARSING, buf, _countof(buf)); + ok_long(hr, S_OK); + if (SUCCEEDED(hr)) + { + ok(!lstrcmpiW(buf, pszUrl), "FORPARSING must match URL\n"); + } + ILFree(pidlUrl); + } + } } START_TEST(ILIsEqual)