From: Lauri Kenttä Subject: [PATCH 1/3] comdlg32: Optimize FILEDLG95_FILENAME_FillFromSelection. Message-Id: <20160623112956.5621-1-lauri.kentta@gmail.com> Date: Thu, 23 Jun 2016 14:29:54 +0300 Related to bug 26803. Previously the function would loop the files twice, first to count, then to create a string from the names. This patch removes the first pass and instead allocates a buffer that is always big enough. This trades some memory for speed, but since currently selecting 1000 files takes over a minute but the required memory for those 1000 filenames is only 262 kB (worst case), this seems a reasonable trade-off. This gives 20% speed-up for selecting 253 files with shift-click. Signed-off-by: Lauri Kenttä --- dlls/comdlg32/filedlg.c | 114 +++++++++++++++++++----------------------------- 1 file changed, 44 insertions(+), 70 deletions(-) diff --git a/dlls/comdlg32/filedlg.c b/dlls/comdlg32/filedlg.c index 282582b..8e64c1e 100644 --- a/dlls/comdlg32/filedlg.c +++ b/dlls/comdlg32/filedlg.c @@ -3626,86 +3626,60 @@ static void FILEDLG95_LOOKIN_Clean(HWND hwnd) */ void FILEDLG95_FILENAME_FillFromSelection (HWND hwnd) { - FileOpenDlgInfos *fodInfos; - LPITEMIDLIST pidl; - UINT nFiles = 0, nFileToOpen, nFileSelected, nLength = 0; - WCHAR lpstrTemp[MAX_PATH]; - LPWSTR lpstrAllFile, lpstrCurrFile; + FileOpenDlgInfos *fodInfos; + LPITEMIDLIST pidl; + UINT nFiles = 0, nFileToOpen, nFileSelected, nLength = 0, nTempLength; + WCHAR lpstrTemp[MAX_PATH]; + LPWSTR lpstrAllFiles; - TRACE("\n"); - fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr); + TRACE("\n"); + fodInfos = GetPropA(hwnd,FileOpenDlgInfosStr); - /* Count how many files we have */ - nFileSelected = GetNumSelected( fodInfos->Shell.FOIDataObject ); + /* Count how many files we have */ + nFileSelected = GetNumSelected( fodInfos->Shell.FOIDataObject ); - /* calculate the string length, count files */ - if (nFileSelected >= 1) + /* Allocate a buffer that is surely big enough; n * (MAX_PATH + quotes) + extra space at the end */ + lpstrAllFiles = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (nFileSelected * (MAX_PATH + 2) + 1) * sizeof(WCHAR)); + if (!lpstrAllFiles) + return; + + /* Loop through the selection, handle only files (not folders) */ + for ( nFileToOpen = 0; nFileToOpen < nFileSelected; nFileToOpen++ ) + { + pidl = GetPidlFromDataObject( fodInfos->Shell.FOIDataObject, nFileToOpen+1 ); + if (pidl) { - nLength += 3; /* first and last quotes, trailing \0 */ - for ( nFileToOpen = 0; nFileToOpen < nFileSelected; nFileToOpen++ ) + if (!IsPidlFolder(fodInfos->Shell.FOIShellFolder, pidl)) { - pidl = GetPidlFromDataObject( fodInfos->Shell.FOIDataObject, nFileToOpen+1 ); - - if (pidl) - { - /* get the total length of the selected file names */ - lpstrTemp[0] = '\0'; - GetName( fodInfos->Shell.FOIShellFolder, pidl, SHGDN_INFOLDER|SHGDN_FORPARSING, lpstrTemp ); - - if ( ! IsPidlFolder(fodInfos->Shell.FOIShellFolder, pidl) ) /* Ignore folders */ - { - nLength += lstrlenW( lpstrTemp ) + 3; - nFiles++; - } - COMDLG32_SHFree( pidl ); - } + nFiles += 1; + lpstrTemp[0] = '\0'; + GetName( fodInfos->Shell.FOIShellFolder, pidl, SHGDN_INFOLDER|SHGDN_FORPARSING, lpstrTemp ); + nTempLength = lstrlenW( lpstrTemp ); + + lpstrAllFiles[nLength++] = '"'; + lstrcpyW( lpstrAllFiles + nLength, lpstrTemp ); + nLength += nTempLength; + lpstrAllFiles[nLength++] = '"'; + lpstrAllFiles[nLength++] = ' '; + lpstrAllFiles[nLength] = 0; } + COMDLG32_SHFree( pidl ); } + } - /* allocate the buffer */ - if (nFiles <= 1) nLength = MAX_PATH; - lpstrAllFile = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, nLength * sizeof(WCHAR)); - - /* Generate the string for the edit control */ - if(nFiles >= 1) - { - lpstrCurrFile = lpstrAllFile; - for ( nFileToOpen = 0; nFileToOpen < nFileSelected; nFileToOpen++ ) - { - pidl = GetPidlFromDataObject( fodInfos->Shell.FOIDataObject, nFileToOpen+1 ); + /* If there's only one file, use the name as-is without quotes */ + if (nFiles == 1) + lstrcpyW( lpstrAllFiles, lpstrTemp ); - if (pidl) - { - /* get the file name */ - lpstrTemp[0] = '\0'; - GetName( fodInfos->Shell.FOIShellFolder, pidl, SHGDN_INFOLDER|SHGDN_FORPARSING, lpstrTemp ); + if (nFiles != 0) + { + SetWindowTextW( fodInfos->DlgInfos.hwndFileName, lpstrAllFiles ); + /* Select the file name like Windows does */ + if (filename_is_edit( fodInfos )) + SendMessageW(fodInfos->DlgInfos.hwndFileName, EM_SETSEL, 0, -1); + } - if (! IsPidlFolder(fodInfos->Shell.FOIShellFolder, pidl)) /* Ignore folders */ - { - if ( nFiles > 1) - { - *lpstrCurrFile++ = '\"'; - lstrcpyW( lpstrCurrFile, lpstrTemp ); - lpstrCurrFile += lstrlenW( lpstrTemp ); - *lpstrCurrFile++ = '\"'; - *lpstrCurrFile++ = ' '; - *lpstrCurrFile = 0; - } - else - { - lstrcpyW( lpstrAllFile, lpstrTemp ); - } - } - COMDLG32_SHFree( pidl ); - } - } - SetWindowTextW( fodInfos->DlgInfos.hwndFileName, lpstrAllFile ); - - /* Select the file name like Windows does */ - if (filename_is_edit( fodInfos )) - SendMessageW(fodInfos->DlgInfos.hwndFileName, EM_SETSEL, 0, -1); - } - HeapFree(GetProcessHeap(),0, lpstrAllFile ); + HeapFree(GetProcessHeap(), 0, lpstrAllFiles); } -- 2.9.0