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..5eda5c76eef 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,273 @@ 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 ShellLinkObject_Constructor(IShellLinkDual2 **link) +{ + ShellLinkObjectImpl *This; + + FIXME("(%p)\n", link); + + *link = NULL; + + This = heap_alloc(sizeof(*This)); + if (!This) return E_OUTOFMEMORY; + This->IShellLinkDual2_iface.lpVtbl = &ShellLinkObjectVtbl; + This->ref = 1; + + *link = (IShellLinkDual2 *)&This->IShellLinkDual2_iface; + return S_OK; +} + static HRESULT WINAPI FolderItemImpl_QueryInterface(FolderItem2 *iface, REFIID riid, LPVOID *ppv) { @@ -803,10 +1081,23 @@ static HRESULT WINAPI FolderItemImpl_get_Path(FolderItem2 *iface, BSTR *path) static HRESULT WINAPI FolderItemImpl_get_GetLink(FolderItem2 *iface, IDispatch **ppid) { - FIXME("(%p,%p)\n", iface, ppid); + IShellLinkDual2 *link; + HRESULT hr; + FolderItemImpl *This = impl_from_FolderItem(iface); + + TRACE("(%p,%p)\n", iface, ppid); *ppid = NULL; - return E_NOTIMPL; + + if (!(This->attributes & SFGAO_LINK)) + return E_NOTIMPL; + + hr = ShellLinkObject_Constructor(&link); + if (hr != S_OK) + return hr; + + *ppid = (IDispatch*)link; + return S_OK; } static HRESULT WINAPI FolderItemImpl_get_GetFolder(FolderItem2 *iface, diff --git a/dlls/shell32/tests/shelldispatch.c b/dlls/shell32/tests/shelldispatch.c index 54957875615..ae1cf086270 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); + ok(hr == S_OK, "got 0x%08x\n", hr); + 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();