[PATCH] shell32: implements shell link's GetCurFile, with tests (fixes bug #21297)
Mikołaj Zalewski
mikolajz at tygrys.dom
Sun Feb 7 13:29:32 CST 2010
---
dlls/shell32/shelllink.c | 30 ++++++--
dlls/shell32/tests/shelllink.c | 144 ++++++++++++++++++++++++++++++++++++++-
2 files changed, 162 insertions(+), 12 deletions(-)
diff --git a/dlls/shell32/shelllink.c b/dlls/shell32/shelllink.c
index 1718df7..6aa6fd5 100644
--- a/dlls/shell32/shelllink.c
+++ b/dlls/shell32/shelllink.c
@@ -159,6 +159,7 @@ typedef struct
volume_info volume;
BOOL bDirty;
+ LPWSTR pszFileName;
INT iIdOpen; /* id of the "Open" entry in the context menu */
IUnknown *site;
} IShellLinkImpl;
@@ -307,6 +308,7 @@ static ULONG ShellLink_Release( IShellLinkImpl *This )
HeapFree(GetProcessHeap(), 0, This->sPathRel);
HeapFree(GetProcessHeap(), 0, This->sProduct);
HeapFree(GetProcessHeap(), 0, This->sComponent);
+ HeapFree(GetProcessHeap(), 0, This->pszFileName);
if (This->site)
IUnknown_Release( This->site );
@@ -392,8 +394,10 @@ static HRESULT WINAPI IPersistFile_fnLoad(IPersistFile* iface, LPCOLESTR pszFile
r = IPersistStream_Load(StreamThis, stm);
ShellLink_UpdatePath(This->sPathRel, pszFileName, This->sWorkDir, &This->sPath);
IStream_Release( stm );
+ Str_SetPtrW(&This->pszFileName, pszFileName);
This->bDirty = FALSE;
}
+ /* TODO: obejct should be reset on failure (see tests). */
TRACE("-- returning hr %08x\n", r);
return r;
}
@@ -476,8 +480,11 @@ static HRESULT WINAPI IPersistFile_fnSave(IPersistFile* iface, LPCOLESTR pszFile
if( SUCCEEDED( r ) )
{
StartLinkProcessor( pszFileName );
-
- This->bDirty = FALSE;
+ if (fRemember)
+ {
+ Str_SetPtrW(&This->pszFileName, strdupW(pszFileName));
+ This->bDirty = FALSE;
+ }
}
else
{
@@ -491,16 +498,22 @@ static HRESULT WINAPI IPersistFile_fnSave(IPersistFile* iface, LPCOLESTR pszFile
static HRESULT WINAPI IPersistFile_fnSaveCompleted(IPersistFile* iface, LPCOLESTR pszFileName)
{
- IShellLinkImpl *This = impl_from_IPersistFile(iface);
- FIXME("(%p)->(%s)\n",This,debugstr_w(pszFileName));
- return NOERROR;
+ /* We don't write anything to output files except for expicit requests
+ * in IPresistFile::Save, so, as I understand, we can ignore calls
+ * to this method. */
+ IShellLinkImpl *This = impl_from_IPersistFile(iface);
+ TRACE("(%p)->(%s)\n",This,debugstr_w(pszFileName));
+ return S_OK;
}
static HRESULT WINAPI IPersistFile_fnGetCurFile(IPersistFile* iface, LPOLESTR *ppszFileName)
{
- IShellLinkImpl *This = impl_from_IPersistFile(iface);
- FIXME("(%p)->(%p): stub\n", This, ppszFileName);
- return NOERROR;
+ IShellLinkImpl *This = impl_from_IPersistFile(iface);
+ TRACE("(%p)->(%p)\n", This, ppszFileName);
+ *ppszFileName = This->pszFileName;
+ if (This->pszFileName)
+ return S_OK;
+ return S_FALSE;
}
static const IPersistFileVtbl pfvt =
@@ -1236,6 +1249,7 @@ HRESULT WINAPI IShellLink_Constructor( IUnknown *pUnkOuter,
sl->lpvtblContextMenu = &cmvt;
sl->lpvtblObjectWithSite = &owsvt;
sl->iShowCmd = SW_SHOWNORMAL;
+ sl->pszFileName = NULL;
sl->bDirty = FALSE;
sl->iIdOpen = -1;
sl->site = NULL;
diff --git a/dlls/shell32/tests/shelllink.c b/dlls/shell32/tests/shelllink.c
index bb2d5b3..4e8bf67 100644
--- a/dlls/shell32/tests/shelllink.c
+++ b/dlls/shell32/tests/shelllink.c
@@ -33,6 +33,30 @@
#include "shell32_test.h"
+#define expect_eq_x(expected, actual) \
+ do { \
+ int value = (actual); \
+ ok((expected) == value, "Expected " #actual " to be 0x%x (" #expected ") is 0x%x\n", \
+ (expected), value); \
+ } while (0)
+#define expect_eq_p(expected, actual) \
+ do { \
+ void *value = (actual); \
+ ok((expected) == value, "Expected " #actual " to be %p (" #expected ") is %p\n", \
+ (expected), value); \
+ } while (0)
+#define expect_eq_ws(expected, actual) \
+ do { \
+ LPCWSTR value = (actual); \
+ ok(lstrcmpW((expected), value) == 0, "Expected " #actual " to be %s (" #expected ") is %s\n", \
+ wine_dbgstr_w(expected), wine_dbgstr_w(value)); \
+ } while (0)
+#define expect_true(actual) \
+ do { \
+ BOOL value = (actual); \
+ ok(value, "Expected " #actual " to be true, but it isn't\n"); \
+ } while (0)
+
#ifndef SLDF_HAS_LOGO3ID
# define SLDF_HAS_LOGO3ID 0x00000800 /* not available in the Vista SDK */
#endif
@@ -538,11 +562,35 @@ static void check_lnk_(int line, const WCHAR* path, lnk_desc_t* desc, int todo)
IShellLinkA_Release(sl);
}
+static void build_temp_path_aw(const char *file_name, char *buffer_a, WCHAR *buffer)
+{
+ GetTempPathA(MAX_PATH - 1 - strlen(file_name), buffer_a);
+ if (buffer_a[strlen(buffer_a) - 1] != '\\')
+ lstrcatA(buffer_a, "\\");
+ lstrcatA(buffer_a, file_name);
+ MultiByteToWideChar(CP_ACP, 0, buffer_a, -1, buffer, MAX_PATH);
+}
+
+static BOOL check_file_exists(const char *path)
+{
+ return GetFileAttributesA(path) != INVALID_FILE_ATTRIBUTES;
+}
+
+static BOOL check_file_exists_and_delete(const char *path)
+{
+ if (check_file_exists(path))
+ {
+ DeleteFileA(path);
+ return TRUE;
+ }
+ return FALSE;
+}
+
static void test_load_save(void)
{
WCHAR lnkfile[MAX_PATH];
char lnkfileA[MAX_PATH];
- static const char lnkfileA_name[] = "\\test.lnk";
+ static const char lnkfileA_name[] = "test.lnk";
lnk_desc_t desc;
char mypath[MAX_PATH];
@@ -559,9 +607,7 @@ static void test_load_save(void)
}
/* Don't used a fixed path for the test.lnk file */
- GetTempPathA(MAX_PATH, lnkfileA);
- lstrcatA(lnkfileA, lnkfileA_name);
- MultiByteToWideChar(CP_ACP, 0, lnkfileA, -1, lnkfile, MAX_PATH);
+ build_temp_path_aw(lnkfileA_name, lnkfileA, lnkfile);
/* Save an empty .lnk file */
memset(&desc, 0, sizeof(desc));
@@ -686,6 +732,95 @@ static void test_load_save(void)
ok(r, "failed to delete link '%s' (%d)\n", lnkfileA, GetLastError());
}
+static void test_persist_file(void)
+{
+ const WCHAR other_pathW[] = {'C',':','\\','a','b','c'};
+ char pathA[MAX_PATH];
+ WCHAR path[MAX_PATH];
+ IShellLinkA* link;
+ IPersistFile* pf;
+ LPOLESTR string;
+ WORD hotkey;
+ HRESULT r;
+ r = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
+ &IID_IShellLinkA, (LPVOID*)&link);
+ ok(SUCCEEDED(r), "Couldn't create CLSID_ShellLink instance.");
+ if (FAILED(r))
+ return;
+
+ r = IShellLinkA_QueryInterface(link, &IID_IPersistFile, (LPVOID*)&pf);
+ ok(SUCCEEDED(r), "CLSID_ShellLink has no IPersistFile.");
+ if (FAILED(r))
+ return;
+
+ expect_eq_x(S_FALSE, IPersistFile_IsDirty(pf));
+ expect_eq_x(S_FALSE, IPersistFile_GetCurFile(pf, &string));
+ expect_eq_p(NULL, string);
+ expect_eq_x(S_OK, IShellLinkA_GetHotkey(link, &hotkey));
+ expect_eq_x(0, hotkey);
+ expect_eq_x(S_FALSE, IPersistFile_IsDirty(pf));
+ // Setting the same value will not set IsDirty...
+ expect_eq_x(S_OK, IShellLinkA_SetHotkey(link, 0));
+ todo_wine expect_eq_x(S_FALSE, IPersistFile_IsDirty(pf));
+ // ... but a different one will.
+ expect_eq_x(S_OK, IShellLinkA_SetHotkey(link, 123));
+ expect_eq_x(S_OK, IPersistFile_IsDirty(pf));
+
+ build_temp_path_aw("winetest_persist1.lnk", pathA, path);
+ expect_eq_x(S_OK, IPersistFile_Save(pf, path, TRUE));
+ expect_true(check_file_exists_and_delete(pathA));
+ expect_eq_x(S_FALSE, IPersistFile_IsDirty(pf));
+ expect_eq_x(S_OK, IPersistFile_GetCurFile(pf, &string));
+ expect_eq_ws(path, string);
+
+ // One can do more saves without calling SaveCompleted.
+ build_temp_path_aw("winetest_persist2.lnk", pathA, path);
+ // Note: we don't delete the second file.
+ expect_eq_x(S_OK, IPersistFile_Save(pf, path, TRUE));
+ expect_true(check_file_exists(pathA));
+ build_temp_path_aw("winetest_persist3.lnk", pathA, path);
+ expect_eq_x(S_OK, IPersistFile_Save(pf, path, TRUE));
+ expect_true(check_file_exists_and_delete(pathA));
+
+ // SaveCompleted can be also called many times and the path seems to be
+ // ignored.
+ expect_eq_x(S_OK, IPersistFile_SaveCompleted(pf, other_pathW));
+ expect_eq_x(S_OK, IPersistFile_GetCurFile(pf, &string));
+ expect_eq_ws(path, string);
+ expect_eq_x(S_OK, IPersistFile_SaveCompleted(pf, other_pathW));
+ expect_eq_x(S_OK, IPersistFile_SaveCompleted(pf, other_pathW));
+ expect_eq_x(S_OK, IPersistFile_SaveCompleted(pf, other_pathW));
+ expect_eq_x(S_OK, IPersistFile_SaveCompleted(pf, other_pathW));
+ expect_eq_x(S_OK, IPersistFile_GetCurFile(pf, &string));
+ expect_eq_ws(path, string);
+
+ expect_eq_x(S_OK, IShellLinkA_SetHotkey(link, 124));
+ expect_eq_x(S_OK, IPersistFile_IsDirty(pf));
+ // 'winetest_persist2.lnk' was not deleted.
+ build_temp_path_aw("winetest_persist2.lnk", pathA, path);
+ expect_eq_x(S_OK, IPersistFile_Load(pf, path, 0));
+ expect_eq_x(S_FALSE, IPersistFile_IsDirty(pf));
+ expect_eq_x(S_OK, IPersistFile_GetCurFile(pf, &string));
+ expect_eq_ws(path, string);
+ expect_true(check_file_exists_and_delete(pathA));
+
+ expect_eq_x(S_OK, IShellLinkA_SetHotkey(link, 125));
+ expect_eq_x(S_OK, IPersistFile_IsDirty(pf));
+ build_temp_path_aw("winetest_non_existent.lnk", pathA, path);
+ expect_eq_x(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), IPersistFile_Load(pf, path, TRUE));
+ // On load failure, the object got reset.
+ todo_wine expect_eq_x(S_FALSE, IPersistFile_IsDirty(pf));
+ expect_eq_x(S_OK, IShellLinkA_GetHotkey(link, &hotkey));
+ todo_wine expect_eq_x(0, hotkey);
+ // Path is not modified on failure.
+ expect_eq_x(S_OK, IPersistFile_GetCurFile(pf, &string));
+ build_temp_path_aw("winetest_persist2.lnk", pathA, path);
+ expect_eq_ws(path, string);
+
+ IShellLinkA_Release(link);
+ expect_eq_x(0, IPersistFile_Release(pf));
+}
+
static void test_datalink(void)
{
static const WCHAR lnk[] = {
@@ -818,6 +953,7 @@ START_TEST(shelllink)
test_get_set();
test_load_save();
+ test_persist_file();
test_datalink();
test_shdefextracticon();
--
1.6.0.2
--------------030508030303090904090809--
More information about the wine-patches
mailing list