Signed-off-by: Myah Caron qsniyg@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
New stubs are still not used, but in addition to that it will give unused variable warning now.
The stubs are unused in this patch, but the later patches do use them. There are no warnings when all 3 patches are applied (at least on my end).
If this is an issue, could you advise a way I could work around this? Would merging #2 with #1 be acceptable? I'd be worried about making this patch too monolithic by doing that though.
Thanks again for reviewing these!
‐‐‐‐‐‐‐ Original Message ‐‐‐‐‐‐‐ On Monday, September 14, 2020 12:47 PM, Nikolay Sivov nsivov@codeweavers.com wrote:
New stubs are still not used, but in addition to that it will give unused variable warning now.
Yes, it's okay to return this stub from GetLink() in a first patch. Next you can add internal IShellLink with corresponding IPersistFile initialization.
Just make sure you don't add dead code or introduce test failures at any point.
On 9/14/20 11:06 PM, Myah Caron wrote:
The stubs are unused in this patch, but the later patches do use them. There are no warnings when all 3 patches are applied (at least on my end).
If this is an issue, could you advise a way I could work around this? Would merging #2 with #1 be acceptable? I'd be worried about making this patch too monolithic by doing that though.
Thanks again for reviewing these!
‐‐‐‐‐‐‐ Original Message ‐‐‐‐‐‐‐ On Monday, September 14, 2020 12:47 PM, Nikolay Sivov nsivov@codeweavers.com wrote:
New stubs are still not used, but in addition to that it will give unused variable warning now.