Signed-off-by: Myah Caron <qsniyg(a)protonmail.com>
---
v4:
- Remove shell_link and persist_file from the structure.
shell_link is added back in #2.
dlls/shell32/shell32_main.h | 1 +
dlls/shell32/shelldispatch.c | 263 ++++++++++++++++++++++++++++-
dlls/shell32/tests/shelldispatch.c | 149 ++++++++++++++++
3 files changed, 412 insertions(+), 1 deletion(-)
diff --git a/dlls/shell32/shell32_main.h b/dlls/shell32/shell32_main.h
index 11a96309e8b..3465a3c8445 100644
--- a/dlls/shell32/shell32_main.h
+++ b/dlls/shell32/shell32_main.h
@@ -222,6 +222,7 @@ enum tid_t {
FolderItems3_tid,
FolderItemVerb_tid,
FolderItemVerbs_tid,
+ IShellLinkDual2_tid,
LAST_tid
};
diff --git a/dlls/shell32/shelldispatch.c b/dlls/shell32/shelldispatch.c
index c6d1dab7f62..afe1348bdec 100644
--- a/dlls/shell32/shelldispatch.c
+++ b/dlls/shell32/shelldispatch.c
@@ -51,7 +51,8 @@ static const IID * const tid_ids[] =
&IID_FolderItem2,
&IID_FolderItems3,
&IID_FolderItemVerb,
- &IID_FolderItemVerbs
+ &IID_FolderItemVerbs,
+ &IID_IShellLinkDual2
};
static ITypeInfo *typeinfos[LAST_tid];
@@ -102,6 +103,11 @@ typedef struct {
BSTR name;
} FolderItemVerbImpl;
+typedef struct {
+ IShellLinkDual2 IShellLinkDual2_iface;
+ LONG ref;
+} ShellLinkObjectImpl;
+
static inline ShellDispatch *impl_from_IShellDispatch6(IShellDispatch6 *iface)
{
return CONTAINING_RECORD(iface, ShellDispatch, IShellDispatch6_iface);
@@ -132,6 +138,11 @@ static inline FolderItemVerbImpl *impl_from_FolderItemVerb(FolderItemVerb *iface
return CONTAINING_RECORD(iface, FolderItemVerbImpl, FolderItemVerb_iface);
}
+static inline ShellLinkObjectImpl *impl_from_IShellLinkDual(IShellLinkDual2 *iface)
+{
+ return CONTAINING_RECORD(iface, ShellLinkObjectImpl, IShellLinkDual2_iface);
+}
+
static HRESULT load_typelib(void)
{
ITypeLib *tl;
@@ -617,6 +628,256 @@ failed:
return hr;
}
+static HRESULT WINAPI ShellLinkObject_QueryInterface(IShellLinkDual2 *iface, REFIID riid,
+ LPVOID *ppv)
+{
+ ShellLinkObjectImpl *This = impl_from_IShellLinkDual(iface);
+
+ TRACE("(%p,%s,%p)\n", iface, debugstr_guid(riid), ppv);
+
+ if (!ppv) return E_INVALIDARG;
+
+ if (IsEqualIID(&IID_IUnknown, riid) ||
+ IsEqualIID(&IID_IDispatch, riid) ||
+ IsEqualIID(&IID_IShellLinkDual, riid) ||
+ IsEqualIID(&IID_IShellLinkDual2, riid))
+ *ppv = &This->IShellLinkDual2_iface;
+ else
+ {
+ WARN("not implemented for %s\n", debugstr_guid(riid));
+ *ppv = NULL;
+ return E_NOINTERFACE;
+ }
+ IUnknown_AddRef((IUnknown*)*ppv);
+ return S_OK;
+}
+
+static ULONG WINAPI ShellLinkObject_AddRef(IShellLinkDual2 *iface)
+{
+ ShellLinkObjectImpl *This = impl_from_IShellLinkDual(iface);
+ ULONG ref = InterlockedIncrement(&This->ref);
+
+ TRACE("(%p), new refcount=%i\n", iface, ref);
+
+ return ref;
+}
+
+static ULONG WINAPI ShellLinkObject_Release(IShellLinkDual2 *iface)
+{
+ ShellLinkObjectImpl *This = impl_from_IShellLinkDual(iface);
+ ULONG ref = InterlockedDecrement(&This->ref);
+
+ TRACE("(%p), new refcount=%i\n", iface, ref);
+
+ if (!ref)
+ {
+ heap_free(This);
+ }
+ return ref;
+}
+
+static HRESULT WINAPI ShellLinkObject_GetTypeInfoCount(IShellLinkDual2 *iface, UINT *pctinfo)
+{
+ TRACE("(%p,%p)\n", iface, pctinfo);
+
+ *pctinfo = 1;
+ return S_OK;
+}
+
+static HRESULT WINAPI ShellLinkObject_GetTypeInfo(IShellLinkDual2 *iface, UINT iTInfo,
+ LCID lcid, ITypeInfo **ppTInfo)
+{
+ HRESULT hr;
+
+ TRACE("(%p,%u,%d,%p)\n", iface, iTInfo, lcid, ppTInfo);
+
+ hr = get_typeinfo(IShellLinkDual2_tid, ppTInfo);
+ if (SUCCEEDED(hr))
+ ITypeInfo_AddRef(*ppTInfo);
+
+ return hr;
+}
+
+static HRESULT WINAPI ShellLinkObject_GetIDsOfNames(IShellLinkDual2 *iface, REFIID riid,
+ LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId)
+{
+ ITypeInfo *ti;
+ HRESULT hr;
+
+ TRACE("(%p,%s,%p,%u,%d,%p)\n", iface, shdebugstr_guid(riid), rgszNames, cNames, lcid,
+ rgDispId);
+
+ hr = get_typeinfo(IShellLinkDual2_tid, &ti);
+ if (SUCCEEDED(hr))
+ hr = ITypeInfo_GetIDsOfNames(ti, rgszNames, cNames, rgDispId);
+ return hr;
+}
+
+static HRESULT WINAPI ShellLinkObject_Invoke(IShellLinkDual2 *iface, DISPID dispIdMember,
+ REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams,
+ VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
+{
+ ShellLinkObjectImpl *This = impl_from_IShellLinkDual(iface);
+ ITypeInfo *ti;
+ HRESULT hr;
+
+ TRACE("(%p,%d,%s,%d,%u,%p,%p,%p,%p)\n", iface, dispIdMember, shdebugstr_guid(riid), lcid,
+ wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
+
+ hr = get_typeinfo(IShellLinkDual2_tid, &ti);
+ if (SUCCEEDED(hr))
+ hr = ITypeInfo_Invoke(ti, This, dispIdMember, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
+ return hr;
+}
+
+static HRESULT WINAPI ShellLinkObject_get_Path(IShellLinkDual2 *iface, BSTR *pbs)
+{
+ FIXME("(%p, %p)\n", iface, pbs);
+
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ShellLinkObject_put_Path(IShellLinkDual2 *iface, BSTR bs)
+{
+ FIXME("(%p, %s)\n", iface, debugstr_w(bs));
+
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ShellLinkObject_get_Description(IShellLinkDual2 *iface, BSTR *pbs)
+{
+ FIXME("(%p, %p)\n", iface, pbs);
+
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ShellLinkObject_put_Description(IShellLinkDual2 *iface, BSTR bs)
+{
+ FIXME("(%p, %s)\n", iface, debugstr_w(bs));
+
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ShellLinkObject_get_WorkingDirectory(IShellLinkDual2 *iface, BSTR *pbs)
+{
+ FIXME("(%p, %p)\n", iface, pbs);
+
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ShellLinkObject_put_WorkingDirectory(IShellLinkDual2 *iface, BSTR bs)
+{
+ FIXME("(%p, %s)\n", iface, debugstr_w(bs));
+
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ShellLinkObject_get_Arguments(IShellLinkDual2 *iface, BSTR *pbs)
+{
+ FIXME("(%p, %p)\n", iface, pbs);
+
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ShellLinkObject_put_Arguments(IShellLinkDual2 *iface, BSTR bs)
+{
+ FIXME("(%p, %s)\n", iface, debugstr_w(bs));
+
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ShellLinkObject_get_Hotkey(IShellLinkDual2 *iface, int *piHK)
+{
+ FIXME("(%p, %p)\n", iface, piHK);
+
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ShellLinkObject_put_Hotkey(IShellLinkDual2 *iface, int iHK)
+{
+ FIXME("(%p, %d)\n", iface, iHK);
+
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ShellLinkObject_get_ShowCommand(IShellLinkDual2 *iface, int *piShowCommand)
+{
+ FIXME("(%p, %p)\n", iface, piShowCommand);
+
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ShellLinkObject_put_ShowCommand(IShellLinkDual2 *iface, int iShowCommand)
+{
+ FIXME("(%p, %d)\n", iface, iShowCommand);
+
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ShellLinkObject_Resolve(IShellLinkDual2 *iface, int fFlags)
+{
+ FIXME("(%p, %d)\n", iface, fFlags);
+
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ShellLinkObject_GetIconLocation(IShellLinkDual2 *iface, BSTR *pbs,
+ int *piIcon)
+{
+ FIXME("(%p, %p, %p)\n", iface, pbs, piIcon);
+
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ShellLinkObject_SetIconLocation(IShellLinkDual2 *iface, BSTR bs,
+ int iIcon)
+{
+ FIXME("(%p, %s, %d)\n", iface, debugstr_w(bs), iIcon);
+
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ShellLinkObject_Save(IShellLinkDual2 *iface, VARIANT vWhere)
+{
+ FIXME("(%p, %s)\n", iface, debugstr_variant(&vWhere));
+
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ShellLinkObject_get_Target(IShellLinkDual2 *iface, FolderItem **ppfi)
+{
+ FIXME("(%p, %p)\n", iface, ppfi);
+
+ return E_NOTIMPL;
+}
+
+static const IShellLinkDual2Vtbl ShellLinkObjectVtbl = {
+ ShellLinkObject_QueryInterface,
+ ShellLinkObject_AddRef,
+ ShellLinkObject_Release,
+ ShellLinkObject_GetTypeInfoCount,
+ ShellLinkObject_GetTypeInfo,
+ ShellLinkObject_GetIDsOfNames,
+ ShellLinkObject_Invoke,
+ ShellLinkObject_get_Path,
+ ShellLinkObject_put_Path,
+ ShellLinkObject_get_Description,
+ ShellLinkObject_put_Description,
+ ShellLinkObject_get_WorkingDirectory,
+ ShellLinkObject_put_WorkingDirectory,
+ ShellLinkObject_get_Arguments,
+ ShellLinkObject_put_Arguments,
+ ShellLinkObject_get_Hotkey,
+ ShellLinkObject_put_Hotkey,
+ ShellLinkObject_get_ShowCommand,
+ ShellLinkObject_put_ShowCommand,
+ ShellLinkObject_Resolve,
+ ShellLinkObject_GetIconLocation,
+ ShellLinkObject_SetIconLocation,
+ ShellLinkObject_Save,
+ ShellLinkObject_get_Target,
+};
+
static HRESULT WINAPI FolderItemImpl_QueryInterface(FolderItem2 *iface,
REFIID riid, LPVOID *ppv)
{
diff --git a/dlls/shell32/tests/shelldispatch.c b/dlls/shell32/tests/shelldispatch.c
index 54957875615..28b0b186b8e 100644
--- a/dlls/shell32/tests/shelldispatch.c
+++ b/dlls/shell32/tests/shelldispatch.c
@@ -1408,6 +1408,154 @@ if (0) { /* crashes on winxp/win2k3 */
IShellDispatch_Release(sd);
}
+static void test_ShellLinkObject(void)
+{
+ HRESULT hr;
+ IShellDispatch *sd;
+ WCHAR path[MAX_PATH],
+ empty_path[MAX_PATH],
+ link_path[MAX_PATH];
+ VARIANT v;
+ Folder2 *folder2;
+ Folder *folder;
+ FolderItem *item;
+ IDispatch *dispatch;
+ IShellLinkW *sl;
+ IShellLinkDual2* sld;
+ IPersistFile *pf;
+ BOOL ret;
+ BSTR str;
+ HANDLE file;
+ int hk;
+
+ hr = CoCreateInstance(&CLSID_Shell, NULL, CLSCTX_INPROC_SERVER,
+ &IID_IShellDispatch, (void**)&sd);
+ ok(hr == S_OK, "got 0x%08x\n", hr);
+
+ GetTempPathW(MAX_PATH, path);
+ V_VT(&v) = VT_BSTR;
+ V_BSTR(&v) = SysAllocString(path);
+ hr = IShellDispatch_NameSpace(sd, v, &folder);
+ ok(hr == S_OK, "got 0x%08x\n", hr);
+ VariantClear(&v);
+
+ hr = Folder_QueryInterface(folder, &IID_Folder2, (void**)&folder2);
+ ok(hr == S_OK, "got 0x%08x\n", hr);
+ Folder_Release(folder);
+
+ hr = Folder2_get_Self(folder2, &item);
+ ok(hr == S_OK, "got 0x%08x\n", hr);
+
+ dispatch = (IDispatch*)0xdeadbeef;
+ hr = FolderItem_get_GetLink(item, &dispatch);
+ ok(hr == E_NOTIMPL, "got 0x%08x\n", hr);
+ ok(dispatch == NULL, "got %p\n", dispatch);
+
+ FolderItem_Release(item);
+
+ PathCombineW(empty_path, path, L"winetest_empty_file.txt");
+ file = CreateFileW(empty_path, 0, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+ ok(file != INVALID_HANDLE_VALUE, "CreateFile failed: %08x\n", GetLastError());
+ CloseHandle(file);
+
+ hr = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, &IID_IShellLinkW, (LPVOID*)&sl);
+ ok(hr == S_OK, "got 0x%08x\n", hr);
+ hr = IShellLinkW_SetPath(sl, empty_path);
+ ok(hr == S_OK, "got 0x%08x\n", hr);
+ hr = IShellLinkW_GetPath(sl, empty_path, MAX_PATH, NULL, 0);
+ ok(hr == S_OK, "got 0x%08x\n", hr);
+ hr = IShellLinkW_SetDescription(sl, L"description");
+ ok(hr == S_OK, "got 0x%08x\n", hr);
+ hr = IShellLinkW_SetWorkingDirectory(sl, L"working directory");
+ ok(hr == S_OK, "got 0x%08x\n", hr);
+ hr = IShellLinkW_SetArguments(sl, L"arguments");
+ ok(hr == S_OK, "got 0x%08x\n", hr);
+ hr = IShellLinkW_SetHotkey(sl, 1234);
+ ok(hr == S_OK, "got 0x%08x\n", hr);
+ hr = IShellLinkW_SetShowCmd(sl, 1);
+ ok(hr == S_OK, "got 0x%08x\n", hr);
+
+ hr = IShellLinkW_QueryInterface(sl, &IID_IPersistFile, (LPVOID*)&pf);
+ ok(hr == S_OK, "got 0x%08x\n", hr);
+
+ PathCombineW(link_path, path, L"winetest_filled.lnk");
+ hr = IPersistFile_Save(pf, link_path, TRUE);
+ ok(hr == S_OK, "got 0x%08x\n", hr);
+
+ IPersistFile_Release(pf);
+ IShellLinkW_Release(sl);
+
+ str = SysAllocString(L"winetest_filled.lnk");
+ hr = Folder2_ParseName(folder2, str, &item);
+ ok(hr == S_OK, "got 0x%08x\n", hr);
+ SysFreeString(str);
+
+ dispatch = NULL;
+ hr = FolderItem_get_GetLink(item, &dispatch);
+ todo_wine ok(hr == S_OK, "got 0x%08x\n", hr);
+ todo_wine ok(dispatch != NULL, "got %p\n", dispatch);
+
+ if (dispatch) {
+ sld = (IShellLinkDual2*)dispatch;
+
+ str = NULL;
+ hr = IShellLinkDual2_get_Path(sld, &str);
+ todo_wine ok(hr == S_OK, "got 0x%08x\n", hr);
+ if (hr == S_OK) {
+ ok(!wcscmp(str, empty_path), "got %s (wanted %s)\n",
+ wine_dbgstr_w(str), wine_dbgstr_w(empty_path));
+ SysFreeString(str);
+ }
+
+ str = NULL;
+ hr = IShellLinkDual2_get_Description(sld, &str);
+ todo_wine ok(hr == S_OK, "got 0x%08x\n", hr);
+ if (hr == S_OK) {
+ ok(!wcscmp(str, L"description"), "got %s\n", wine_dbgstr_w(str));
+ SysFreeString(str);
+ }
+
+ str = NULL;
+ hr = IShellLinkDual2_get_WorkingDirectory(sld, &str);
+ todo_wine ok(hr == S_OK, "got 0x%08x\n", hr);
+ if (hr == S_OK) {
+ ok(!wcscmp(str, L"working directory"), "got %s\n", wine_dbgstr_w(str));
+ SysFreeString(str);
+ }
+
+ str = NULL;
+ hr = IShellLinkDual2_get_Arguments(sld, &str);
+ todo_wine ok(hr == S_OK, "got 0x%08x\n", hr);
+ if (hr == S_OK) {
+ ok(!wcscmp(str, L"arguments"), "got %s\n", wine_dbgstr_w(str));
+ SysFreeString(str);
+ }
+
+ hk = 0;
+ hr = IShellLinkDual2_get_Hotkey(sld, &hk);
+ todo_wine ok(hr == S_OK, "got 0x%08x\n", hr);
+ todo_wine ok(hk == 1234, "got %i\n", hk);
+
+ hk = 0;
+ hr = IShellLinkDual2_get_ShowCommand(sld, &hk);
+ todo_wine ok(hr == S_OK, "got 0x%08x\n", hr);
+ todo_wine ok(hk == 1, "got %i\n", hk);
+
+ IShellLinkDual2_Release(sld);
+ }
+
+ FolderItem_Release(item);
+
+ ret = DeleteFileW(link_path);
+ ok(ret, "DeleteFile failed: %08x\n", GetLastError());
+ ret = DeleteFileW(empty_path);
+ ok(ret, "DeleteFile failed: %08x\n", GetLastError());
+
+ Folder2_Release(folder2);
+
+ IShellDispatch_Release(sd);
+}
+
static void test_ShellExecute(void)
{
HRESULT hr;
@@ -1466,6 +1614,7 @@ START_TEST(shelldispatch)
test_ShellWindows();
test_ParseName();
test_Verbs();
+ test_ShellLinkObject();
test_ShellExecute();
CoUninitialize();
--
2.28.0