[IEFRAME] Implement URL PIDL (#7850)

This commit is contained in:
Whindmar Saksit 2025-04-01 16:09:25 +02:00 committed by GitHub
parent 13657fdb5b
commit 769462faaa
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 417 additions and 0 deletions

View file

@ -13,6 +13,7 @@ list(APPEND SOURCE
ieframe_main.c
iehtmlwnd.c
iexplore.c
inetfolder.c
intshcut.c
navigate.c
oleobject.c

View file

@ -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;
}

View file

@ -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 <whindsaks@proton.me>
*/
#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;
}

View file

@ -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

View file

@ -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<IShellFolder> 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<PWSTR>(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)