[SHELL32] Rewrite SHAddToRecentDocs (#2333)
authorKatayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
Fri, 14 Feb 2020 02:05:21 +0000 (11:05 +0900)
committerGitHub <noreply@github.com>
Fri, 14 Feb 2020 02:05:21 +0000 (11:05 +0900)
Rewrite shell32!SHAddToRecentDocs and use it in some applications.
Wine's SHAddToRecentDocs was not Unicode supported and unusable. I will dare to rewrite.
CORE-3588

base/applications/mspaint/registry.cpp
base/applications/notepad/main.c
base/applications/wordpad/wordpad.c
dll/win32/shell32/wine/shellord.c
dll/win32/shimgvw/CMakeLists.txt
dll/win32/shimgvw/shimgvw.c

index cd1250b..369d8ea 100644 (file)
@@ -4,13 +4,15 @@
  * FILE:        base/applications/mspaint/registry.cpp
  * PURPOSE:     Offering functions dealing with registry values
  * PROGRAMMERS: Benedikt Freisen
+ *              Katayama Hirofumi MZ
  */
 
 /* INCLUDES *********************************************************/
 
 #include "precomp.h"
-
 #include <winreg.h>
+#include <wincon.h>
+#include <shlobj.h>
 
 /* FUNCTIONS ********************************************************/
 static DWORD ReadDWORD(CRegKey &key, LPCTSTR lpName, DWORD &dwValue, BOOL bCheckForDef)
@@ -143,6 +145,9 @@ void RegistrySettings::Store()
 
 void RegistrySettings::SetMostRecentFile(LPCTSTR szPathName)
 {
+    if (szPathName && szPathName[0])
+        SHAddToRecentDocs(SHARD_PATHW, szPathName);
+
     if (strFile1 == szPathName)
     {
         // do nothing
index c424449..dee2777 100644 (file)
@@ -5,6 +5,7 @@
  *  Copyright 1997,98 Marcel Baur <mbaur@g26.ethz.ch>
  *  Copyright 2002 Sylvain Petreolle <spetreolle@yahoo.fr>
  *  Copyright 2002 Andriy Palamarchuk
+ *  Copyright 2020 Katayama Hirofumi MZ
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -24,6 +25,7 @@
 
 #include "notepad.h"
 
+#include <shlobj.h>
 #include <strsafe.h>
 
 NOTEPAD_GLOBALS Globals;
@@ -48,6 +50,9 @@ VOID SetFileName(LPCTSTR szFileName)
     StringCchCopy(Globals.szFileName, ARRAY_SIZE(Globals.szFileName), szFileName);
     Globals.szFileTitle[0] = 0;
     GetFileTitle(szFileName, Globals.szFileTitle, ARRAY_SIZE(Globals.szFileTitle));
+
+    if (szFileName && szFileName[0])
+        SHAddToRecentDocs(SHARD_PATHW, szFileName);
 }
 
 /***********************************************************************
index acc8aa0..ba4cf53 100644 (file)
@@ -3,6 +3,7 @@
  *
  * Copyright 2004 by Krzysztof Foltman
  * Copyright 2007-2008 by Alexander N. Sørnes <alex@thehandofagony.com>
+ * Copyright 2020 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -32,6 +33,7 @@
 #include <commctrl.h>
 #include <commdlg.h>
 #include <shellapi.h>
+#include <shlobj.h>
 #include <wine/unicode.h>
 
 #include "wordpad.h"
@@ -811,6 +813,8 @@ static void DoOpenFile(LPCWSTR szOpenFileName)
     SetFocus(hEditorWnd);
 
     set_caption(szOpenFileName);
+    if (szOpenFileName[0])
+        SHAddToRecentDocs(SHARD_PATHW, szOpenFileName);
 
     lstrcpyW(wszFileName, szOpenFileName);
     SendMessageW(hEditorWnd, EM_SETMODIFY, FALSE, 0);
@@ -883,6 +887,9 @@ static BOOL DoSaveFile(LPCWSTR wszSaveFileName, WPARAM format)
 
     lstrcpyW(wszFileName, wszSaveFileName);
     set_caption(wszFileName);
+    if (wszFileName[0])
+        SHAddToRecentDocs(SHARD_PATHW, wszFileName);
+
     SendMessageW(hEditorWnd, EM_SETMODIFY, FALSE, 0);
     set_fileformat(format);
 
index 9b4c6ab..f634c33 100644 (file)
@@ -45,6 +45,9 @@
 WINE_DEFAULT_DEBUG_CHANNEL(shell);
 WINE_DECLARE_DEBUG_CHANNEL(pidl);
 
+#ifdef __REACTOS__
+#include <comctl32_undoc.h>
+#else
 /* FIXME: !!! move CREATEMRULIST and flags to header file !!! */
 /*        !!! it is in both here and comctl32undoc.c      !!! */
 typedef struct tagCREATEMRULIST
@@ -67,7 +70,7 @@ extern VOID WINAPI FreeMRUList(HANDLE hMRUList);
 extern INT    WINAPI AddMRUData(HANDLE hList, LPCVOID lpData, DWORD cbData);
 extern INT    WINAPI FindMRUData(HANDLE hList, LPCVOID lpData, DWORD cbData, LPINT lpRegNum);
 extern INT    WINAPI EnumMRUListA(HANDLE hList, INT nItemPos, LPVOID lpBuffer, DWORD nBufferSize);
-
+#endif
 
 /*************************************************************************
  * ParseFieldA                                 [internal]
@@ -596,9 +599,59 @@ static INT SHADD_get_policy(LPCSTR policy, LPDWORD type, LPVOID buffer, LPDWORD
  */
 static INT CALLBACK SHADD_compare_mru(LPCVOID data1, LPCVOID data2, DWORD cbData)
 {
+#ifdef __REACTOS__
+    LPCWSTR psz1, psz2;
+    INT iCmp = lstrcmpiW(data1, data2);
+    if (iCmp != 0)
+        return iCmp;
+    psz1 = data1;
+    psz2 = data2;
+    psz1 += lstrlenW(psz1) + 1;
+    psz2 += lstrlenW(psz2) + 1;
+    return lstrcmpiW(psz1, psz2);
+#else
     return lstrcmpiA(data1, data2);
+#endif
 }
 
+#ifdef __REACTOS__
+static BOOL
+DoStoreMRUData(LPBYTE pbBuffer, LPDWORD pcbBuffer,
+               LPCWSTR pszTargetTitle, LPCWSTR pszTargetPath, LPCWSTR pszLinkTitle)
+{
+    DWORD ib = 0, cb;
+    INT cchTargetTitle = lstrlenW(pszTargetTitle);
+    INT cchTargetPath = lstrlenW(pszTargetPath);
+    INT cchLinkTitle = lstrlenW(pszLinkTitle);
+
+    cb = (cchTargetTitle + 1 + cchTargetPath + 1 + cchLinkTitle + 2) * sizeof(WCHAR);
+    if (cb > *pcbBuffer)
+        return FALSE;
+
+    ZeroMemory(pbBuffer, *pcbBuffer);
+
+    cb = (cchTargetTitle + 1) * sizeof(WCHAR);
+    if (ib + cb > *pcbBuffer)
+        return FALSE;
+    CopyMemory(&pbBuffer[ib], pszTargetTitle, cb);
+    ib += cb;
+
+    cb = (cchTargetPath + 1) * sizeof(WCHAR);
+    if (ib + cb > *pcbBuffer)
+        return FALSE;
+    CopyMemory(&pbBuffer[ib], pszTargetPath, cb);
+    ib += cb;
+
+    cb = (cchLinkTitle + 1) * sizeof(WCHAR);
+    if (ib + cb > *pcbBuffer)
+        return FALSE;
+    CopyMemory(&pbBuffer[ib], pszLinkTitle, cb);
+    ib += cb;
+
+    *pcbBuffer = ib;
+    return TRUE;
+}
+#else
 /*************************************************************************
  * SHADD_create_add_mru_data - helper function for SHAddToRecentDocs
  *
@@ -650,6 +703,7 @@ static INT SHADD_create_add_mru_data(HANDLE mruhandle, LPCSTR doc_name, LPCSTR n
      */
     return AddMRUData(mruhandle, buffer, *len);
 }
+#endif
 
 /*************************************************************************
  * SHAddToRecentDocs                           [SHELL32.@]
@@ -668,6 +722,268 @@ static INT SHADD_create_add_mru_data(HANDLE mruhandle, LPCSTR doc_name, LPCSTR n
  */
 void WINAPI SHAddToRecentDocs (UINT uFlags,LPCVOID pv)
 {
+#ifdef __REACTOS__
+    static const WCHAR szExplorerKey[] = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer";
+    INT ret;
+    WCHAR szTargetPath[MAX_PATH], szLinkDir[MAX_PATH], szLinkFile[MAX_PATH], szDescription[80];
+    WCHAR szPath[MAX_PATH];
+    DWORD cbBuffer, data[64], datalen, type;
+    HANDLE hFind;
+    WIN32_FIND_DATAW find;
+    HKEY hExplorerKey;
+    LONG error;
+    LPWSTR pchDotExt, pchTargetTitle, pchLinkTitle;
+    MRUINFOW mru;
+    HANDLE hMRUList = NULL;
+    IShellLinkW *psl = NULL;
+    IPersistFile *pPf = NULL;
+    HRESULT hr;
+    BYTE Buffer[(MAX_PATH + 64) * sizeof(WCHAR)];
+
+    TRACE("%04x %p\n", uFlags, pv);
+
+    /* check policy */
+    ret = SHADD_get_policy("NoRecentDocsHistory", &type, data, &datalen);
+    if (ret > 0 && ret != ERROR_FILE_NOT_FOUND)
+    {
+        ERR("Error %d getting policy \"NoRecentDocsHistory\"\n", ret);
+    }
+    else if (ret == ERROR_SUCCESS)
+    {
+        if (!(type == REG_DWORD || (type == REG_BINARY && datalen == 4)))
+        {
+            ERR("Error policy data for \"NoRecentDocsHistory\" not formatted correctly, type=%d, len=%d\n",
+                type, datalen);
+            return;
+        }
+
+        TRACE("policy value for NoRecentDocsHistory = %08x\n", data[0]);
+        /* now test the actual policy value */
+        if (data[0] != 0)
+            return;
+    }
+
+    /* store to szTargetPath */
+    szTargetPath[0] = 0;
+    if (pv)
+    {
+        switch (uFlags)
+        {
+            case SHARD_PATHA:
+                MultiByteToWideChar(CP_ACP, 0, pv, -1, szLinkDir, ARRAYSIZE(szLinkDir));
+                GetFullPathNameW(szLinkDir, ARRAYSIZE(szTargetPath), szTargetPath, NULL);
+                break;
+
+            case SHARD_PATHW:
+                GetFullPathNameW(pv, ARRAYSIZE(szTargetPath), szTargetPath, NULL);
+                break;
+
+            case SHARD_PIDL:
+                SHGetPathFromIDListW(pv, szLinkDir);
+                GetFullPathNameW(szLinkDir, ARRAYSIZE(szTargetPath), szTargetPath, NULL);
+                break;
+
+            default:
+                FIXME("Unsupported flags: %u\n", uFlags);
+                return;
+        }
+    }
+
+    /* get recent folder */
+    if (!SHGetSpecialFolderPathW(NULL, szLinkDir, CSIDL_RECENT, FALSE))
+    {
+        ERR("serious issues 1\n");
+        return;
+    }
+    TRACE("Users Recent dir %S\n", szLinkDir);
+
+    /* open Explorer key */
+    error = RegCreateKeyExW(HKEY_CURRENT_USER, szExplorerKey, 0, NULL, 0,
+                            KEY_READ | KEY_WRITE, NULL, &hExplorerKey, NULL);
+    if (error)
+    {
+        ERR("Failed to RegCreateKeyExW: 0x%08X\n", error);
+        return;
+    }
+
+    if (!pv)
+    {
+        TRACE("pv is NULL, so delete all shortcut files in %S\n", szLinkDir);
+
+        lstrcpynW(szLinkFile, szLinkDir, ARRAYSIZE(szLinkFile));
+        PathAppendW(szLinkFile, L"*.lnk");
+
+        hFind = FindFirstFileW(szLinkFile, &find);
+        if (hFind != INVALID_HANDLE_VALUE)
+        {
+            do
+            {
+                lstrcpynW(szLinkFile, szLinkDir, ARRAYSIZE(szLinkFile));
+                PathAppendW(szLinkFile, find.cFileName);
+                DeleteFileW(szLinkFile);
+            } while (FindNextFile(hFind, &find));
+            FindClose(hFind);
+        }
+
+        SHDeleteKeyW(hExplorerKey, L"RecentDocs");
+        RegCloseKey(hExplorerKey);
+        return;
+    }
+
+    if (szTargetPath[0] == 0 || !PathFileExistsW(szTargetPath) ||
+        PathIsDirectoryW(szTargetPath))
+    {
+        /* path is not normal file */
+        RegCloseKey(hExplorerKey);
+        return;
+    }
+
+    hr = CoInitialize(NULL);
+    if (FAILED(hr))
+    {
+        ERR("CoInitialize: %08X\n", hr);
+        RegCloseKey(hExplorerKey);
+        return;
+    }
+
+    /* check if file is a shortcut */
+    ret = 0;
+    pchDotExt = PathFindExtensionW(szTargetPath);
+    while (lstrcmpiW(pchDotExt, L".lnk") == 0)
+    {
+        hr = IShellLink_ConstructFromPath(szTargetPath, &IID_IShellLinkW, (LPVOID*)&psl);
+        if (FAILED(hr))
+        {
+            ERR("IShellLink_ConstructFromPath: 0x%08X\n", hr);
+            goto Quit;
+        }
+
+        IShellLinkW_GetPath(psl, szPath, ARRAYSIZE(szPath), NULL, 0);
+        IShellLinkW_Release(psl);
+        psl = NULL;
+
+        lstrcpynW(szTargetPath, szPath, ARRAYSIZE(szTargetPath));
+        pchDotExt = PathFindExtensionW(szTargetPath);
+
+        if (++ret >= 8)
+        {
+            ERR("Link loop?\n");
+            goto Quit;
+        }
+    }
+    if (!lstrcmpiW(pchDotExt, L".exe"))
+    {
+        /* executables are not added */
+        goto Quit;
+    }
+
+    /* ***  JOB 0: Build strings *** */
+
+    pchTargetTitle = PathFindFileNameW(szTargetPath);
+
+    lstrcpyW(szDescription, L"Shortcut to ");
+    StrCatBuffW(szDescription, pchTargetTitle, ARRAYSIZE(szDescription));
+
+    lstrcpynW(szLinkFile, szLinkDir, ARRAYSIZE(szLinkFile));
+    PathAppendW(szLinkFile, pchTargetTitle);
+    StrCatBuffW(szLinkFile, L".lnk", ARRAYSIZE(szLinkFile));
+    pchLinkTitle = PathFindFileNameW(szLinkFile);
+
+    /* ***  JOB 1: Update registry for ...\Explorer\RecentDocs list  *** */
+
+    /* store MRU data */
+    cbBuffer = sizeof(Buffer);
+    ret = DoStoreMRUData(Buffer, &cbBuffer, pchTargetTitle, szTargetPath, pchLinkTitle);
+    if (!ret)
+    {
+        ERR("DoStoreMRUData failed: %d\n", ret);
+        goto Quit;
+    }
+
+    /* create MRU list */
+    mru.cbSize = sizeof(mru);
+    mru.uMax = 16;
+    mru.fFlags = MRU_BINARY | MRU_CACHEWRITE;
+    mru.hKey = hExplorerKey;
+    mru.lpszSubKey = L"RecentDocs";
+    mru.lpfnCompare = (MRUCMPPROCW)SHADD_compare_mru;
+    hMRUList = CreateMRUListW(&mru);
+    if (!hMRUList)
+    {
+        ERR("CreateMRUListW failed\n");
+        goto Quit;
+    }
+
+    /* already exists? */
+    ret = FindMRUData(hMRUList, Buffer, cbBuffer, NULL);
+    if (ret >= 0)
+    {
+        /* Just touch for speed */
+        HANDLE hFile;
+        hFile = CreateFileW(szLinkFile, GENERIC_READ | GENERIC_WRITE,
+                            FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
+        if (hFile != INVALID_HANDLE_VALUE)
+        {
+            TRACE("Just touch file '%S'.\n", szLinkFile);
+            CloseHandle(hFile);
+            goto Quit;
+        }
+    }
+
+    /* add MRU data */
+    ret = AddMRUData(hMRUList, Buffer, cbBuffer);
+    if (ret < 0)
+    {
+        ERR("AddMRUData failed: %d\n", ret);
+        goto Quit;
+    }
+
+    /* ***  JOB 2: Create shortcut in user's "Recent" directory  *** */
+
+    hr = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
+                          &IID_IShellLinkW, (LPVOID *)&psl);
+    if (FAILED(hr))
+    {
+        ERR("CoInitialize for IID_IShellLinkW: %08X\n", hr);
+        goto Quit;
+    }
+
+    hr = IShellLinkW_QueryInterface(psl, &IID_IPersistFile, (LPVOID *)&pPf);
+    if (FAILED(hr))
+    {
+        ERR("IShellLinkW_QueryInterface: %08X\n", hr);
+        goto Quit;
+    }
+
+    if (uFlags == SHARD_PIDL)
+        hr = IShellLinkW_SetIDList(psl, pv);
+    else
+        hr = IShellLinkW_SetPath(psl, pv);
+
+    IShellLinkW_SetDescription(psl, szDescription);
+
+    hr = IPersistFile_Save(pPf, szLinkFile, TRUE);
+    if (FAILED(hr))
+    {
+        ERR("IPersistFile_Save: 0x%08X\n", hr);
+    }
+
+    hr = IPersistFile_SaveCompleted(pPf, szLinkFile);
+    if (FAILED(hr))
+    {
+        ERR("IPersistFile_SaveCompleted: 0x%08X\n", hr);
+    }
+
+Quit:
+    if (hMRUList)
+        FreeMRUList(hMRUList);
+    if (pPf)
+        IPersistFile_Release(pPf);
+    if (psl)
+        IShellLinkW_Release(psl);
+    CoUninitialize();
+    RegCloseKey(hExplorerKey);
+#else
 /* If list is a string list lpfnCompare has the following prototype
  * int CALLBACK MRUCompareString(LPCSTR s1, LPCSTR s2)
  * for binary lists the prototype is
@@ -822,30 +1138,6 @@ void WINAPI SHAddToRecentDocs (UINT uFlags,LPCVOID pv)
 
     TRACE("full document name %s\n", debugstr_a(doc_name));
 
-#ifdef __REACTOS__
-    /* check if file is a shortcut */
-    ext = strrchr(doc_name, '.');
-    if (!lstrcmpiA(ext, ".lnk"))
-    {
-        WCHAR doc_nameW[MAX_PATH];
-        IShellLinkA* ShellLink;
-        int nLength = MultiByteToWideChar(CP_ACP, 0, doc_name, -1, doc_nameW, MAX_PATH);
-        if (nLength == 0)
-            return;
-
-        IShellLink_ConstructFromPath(doc_nameW, &IID_IShellLinkA, (LPVOID*)&ShellLink);
-        IShellLinkA_GetPath(ShellLink, doc_name, MAX_PATH, NULL, 0);
-        IShellLinkA_Release(ShellLink);
-    }
-
-    ext = strrchr(doc_name, '.');
-    if (!lstrcmpiA(ext, ".exe"))
-    {
-        /* executables are not added */
-        return;
-    }
-#endif
-
     PathStripPathA(doc_name);
     TRACE("stripped document name %s\n", debugstr_a(doc_name));
 
@@ -1032,6 +1324,7 @@ void WINAPI SHAddToRecentDocs (UINT uFlags,LPCVOID pv)
     /* all done */
     RegCloseKey(HCUbasekey);
     return;
+#endif
 }
 
 /*************************************************************************
index e26d54f..fefd3d1 100644 (file)
@@ -11,5 +11,5 @@ list(APPEND SOURCE
 add_library(shimgvw MODULE ${SOURCE})
 set_module_type(shimgvw win32dll)
 target_link_libraries(shimgvw wine)
-add_importlibs(shimgvw advapi32 comctl32 user32 gdi32 gdiplus comdlg32 shlwapi msvcrt kernel32 ntdll)
+add_importlibs(shimgvw advapi32 comctl32 user32 gdi32 shell32 gdiplus comdlg32 shlwapi msvcrt kernel32 ntdll)
 add_cd_file(TARGET shimgvw DESTINATION reactos/system32 FOR all)
index 7f2d039..de8dc0e 100644 (file)
 #include <winnls.h>
 #include <winreg.h>
 #include <wingdi.h>
+#include <wincon.h>
 #include <windowsx.h>
 #include <objbase.h>
 #include <commctrl.h>
 #include <commdlg.h>
 #include <gdiplus.h>
 #include <tchar.h>
+#include <shlobj.h>
 #include <strsafe.h>
 #include <shlwapi.h>
 
@@ -304,6 +306,9 @@ static void pLoadImage(LPWSTR szOpenFileName)
     }
     Anime_LoadInfo();
 
+    if (szOpenFileName && szOpenFileName[0])
+        SHAddToRecentDocs(SHARD_PATHW, szOpenFileName);
+
     /* reset zoom */
     ResetZoom();