[PATCH] shell32: implements shell link's GetCurFile (with tests, fixes bug #21297)

Mikołaj Zalewski mikolajz at tygrys.dom
Sat Feb 20 15:42:30 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..4b3e3e6 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(winetest_strcmpW((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


--------------040009010709000803040605--



More information about the wine-patches mailing list