Files
Open-Shell-Menu/Src/ClassicExplorer/ClassicCopy.cpp
Xenhat f4dd56155b Rebrand to Open-Shell (#36) (#58)
* Rebrand to Open-Shell

* Slight installer branding improvement
2018-08-05 15:22:10 -04:00

777 lines
23 KiB
C++

// Classic Shell (c) 2009-2017, Ivo Beltchev
// Open-Shell (c) 2017-2018, The Open-Shell Team
// Confidential information of Ivo Beltchev. Not for disclosure or distribution without prior written consent from the author
#include "stdafx.h"
#include <commctrl.h>
#include <oleacc.h>
#include <atlcomcli.h>
#include <dwmapi.h>
#include <utility>
#include "Translations.h"
#include "Settings.h"
#include "ResourceHelper.h"
#include "ComHelper.h"
#include "dllmain.h"
static wchar_t g_TitleMove[256];
static wchar_t g_TitleCopy[256];
static wchar_t g_TitleFolder[256];
static wchar_t g_ButtonMove[256];
static wchar_t g_ButtonDontMove[256];
static wchar_t g_ButtonCopy[256];
static wchar_t g_ButtonDontCopy[256];
static wchar_t g_ButtonCancel[256];
static wchar_t g_ButtonMore[256];
static HMODULE g_hShell32;
static void LogPrint( CString *pLog, const wchar_t *format, ... )
{
wchar_t buf[256];
va_list args;
va_start(args,format);
int len=Vsprintf(buf,_countof(buf),format,args);
va_end(args);
*pLog+=buf;
}
// CClassicCopyFile - this is the implementation of the Copy UI dialog box for files
class CClassicCopyFile
{
public:
CClassicCopyFile( void );
~CClassicCopyFile( void );
bool Run( HWND hWnd, IAccessible *pAcc, CString *pLog );
private:
void EnumAccChildren( IAccessible *pAcc, CString *pLog );
void AddAccChild( IAccessible *pAcc, const VARIANT &id, CString *pLog );
void GetFileInfo( IAccessible *pAcc, bool bSrc );
CString m_FileName;
bool m_bSystem;
bool m_bReadOnly;
HICON m_Icon;
CString m_SrcSize;
CString m_SrcTime;
HICON m_SrcIcon;
CString m_DstSize;
CString m_DstTime;
HICON m_DstIcon;
typedef std::pair<CComPtr<IAccessible>,int> CControl;
CControl m_YesButton;
CControl m_NoButton;
CControl m_CheckBox;
CControl m_Cancel;
bool m_bCopyMultiLast; // the last of a series of multi-file boxes
static INT_PTR CALLBACK DialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam );
static void PumpMessages( void );
};
CClassicCopyFile::CClassicCopyFile( void )
{
m_Icon=m_SrcIcon=m_DstIcon=NULL;
m_bCopyMultiLast=false;
m_bSystem=m_bReadOnly=false;
}
CClassicCopyFile::~CClassicCopyFile( void )
{
if (m_Icon) DestroyIcon(m_Icon);
if (m_SrcIcon) DestroyIcon(m_SrcIcon);
if (m_DstIcon) DestroyIcon(m_DstIcon);
}
// Show the dialog box. Returns true to suppress the original task dialog box
bool CClassicCopyFile::Run( HWND hWnd, IAccessible *pAcc, CString *pLog )
{
// find all interesting controls
EnumAccChildren(pAcc,pLog);
if (!m_YesButton.first || m_YesButton.second!=CHILDID_SELF || !m_NoButton.first || m_NoButton.second!=CHILDID_SELF || !m_Cancel.first)
return false; // something is wrong, do nothing
if (pLog) pLog->Empty(); // success - no need to log anything
// get the info for the source and the destination file (file name, icon, properties)
m_bReadOnly=false;
m_bSystem=false;
GetFileInfo(m_YesButton.first,true);
GetFileInfo(m_NoButton.first,false);
TlsData *pTlsData=GetTlsData();
if (m_CheckBox.first)
pTlsData->bCopyMultiFile=true;
else if (pTlsData->bCopyMultiFile)
m_bCopyMultiLast=true;
// pick the correct dialog template (for single and multiple files, for LTR and RTL)
int dlg=pTlsData->bCopyMultiFile?(IsLanguageRTL()?IDD_FILEMULTIR:IDD_FILEMULTI):(IsLanguageRTL()?IDD_FILER:IDD_FILE);
HWND parent=GetWindow(GetAncestor(hWnd,GA_ROOT),GW_OWNER);
int res=(int)DialogBoxParam(g_Instance,MAKEINTRESOURCE(dlg),parent,DialogProc,(LPARAM)this);
if (res==IDOK || (res==IDYES && m_bCopyMultiLast))
{
// Yes was pressed, proceed with the operation
m_YesButton.first->accDoDefaultAction(CComVariant(CHILDID_SELF));
}
else if (res==IDNO)
{
// No
if (m_CheckBox.first && GetKeyState(VK_SHIFT)<0)
m_CheckBox.first->accDoDefaultAction(CComVariant(CHILDID_SELF)); // Shift+No = No to All
PumpMessages(); // messages need to be pumped after every accessibility action. otherwise the next action doesn't work
m_NoButton.first->accDoDefaultAction(CComVariant(CHILDID_SELF));
}
else if (res==IDYES)
{
// Yes to All
m_CheckBox.first->accDoDefaultAction(CComVariant(CHILDID_SELF));
PumpMessages(); // messages need to be pumped after every accessibility action. otherwise the next action doesn't work
m_YesButton.first->accDoDefaultAction(CComVariant(CHILDID_SELF));
}
if (res==IDCANCEL)
{
// Cancel
if (GetKeyState(VK_SHIFT)<0 || GetKeyState(VK_CONTROL)<0)
return false; // // Shift+Cancel or Ctrl+Cancel was clicked - show the original dialog box
m_Cancel.first->accDoDefaultAction(CComVariant(CHILDID_SELF));
}
if (res==IDC_LINKMORE)
{
// More... was clicked - show the original dialog box
return false;
}
return true;
}
void CClassicCopyFile::PumpMessages( void )
{
MSG msg;
while (PeekMessage(&msg,NULL,0,0,PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
void CClassicCopyFile::AddAccChild( IAccessible *pAcc, const VARIANT &id, CString *pLog )
{
CComVariant state;
pAcc->get_accState(id,&state);
if (state.intVal&(STATE_SYSTEM_UNAVAILABLE|STATE_SYSTEM_INVISIBLE)) return;
CComVariant role;
pAcc->get_accRole(id,&role);
if (role.intVal==ROLE_SYSTEM_PUSHBUTTON)
{
CComBSTR name;
if (SUCCEEDED(pAcc->get_accName(id,&name)) && name)
{
if (pLog)
LogPrint(pLog,L"button found: '%s', %d\r\n",name,id.intVal);
if (_wcsicmp(name,g_ButtonCopy)==0 || _wcsicmp(name,g_ButtonMove)==0)
{
m_YesButton.first=pAcc;
m_YesButton.second=id.intVal;
if (pLog)
LogPrint(pLog,L"YES button: %p, %d\r\n",pAcc,id.intVal);
}
else if (_wcsicmp(name,g_ButtonDontCopy)==0 || _wcsicmp(name,g_ButtonDontMove)==0)
{
m_NoButton.first=pAcc;
m_NoButton.second=id.intVal;
if (pLog)
LogPrint(pLog,L"NO button: %p, %d\r\n",pAcc,id.intVal);
}
else if (_wcsicmp(name,g_ButtonCancel)==0)
{
m_Cancel.first=pAcc;
m_Cancel.second=id.intVal;
if (pLog)
LogPrint(pLog,L"CANCEL button: %p, %d\r\n",pAcc,id.intVal);
}
}
}
if (role.intVal==ROLE_SYSTEM_CHECKBUTTON)
{
// hopefully there is only one checkbox
m_CheckBox.first=pAcc;
m_CheckBox.second=id.intVal;
}
}
void CClassicCopyFile::EnumAccChildren( IAccessible *pAcc, CString *pLog )
{
AddAccChild(pAcc,CComVariant(CHILDID_SELF),pLog);
long count;
pAcc->get_accChildCount(&count);
CComVariant children[20];
AccessibleChildren(pAcc,0,count,children,&count);
for (int i=0;i<count;i++)
{
if (children[i].vt==VT_DISPATCH)
{
CComQIPtr<IAccessible> pChild=children[i].pdispVal;
if (pChild)
EnumAccChildren(pChild,pLog);
}
else
AddAccChild(pAcc,children[i],pLog);
}
}
void CClassicCopyFile::GetFileInfo( IAccessible *pAcc, bool bSrc )
{
long count;
pAcc->get_accChildCount(&count);
CComVariant children[20];
AccessibleChildren(pAcc,0,count,children,&count);
wchar_t fname[_MAX_PATH]=L"";
wchar_t dir[_MAX_PATH]=L"";
CString size;
CString date;
// get the file name, directory, size and date
for (int i=0;i<count;i++)
{
CComBSTR name;
if (children[i].vt==VT_DISPATCH)
{
CComQIPtr<IAccessible> pChild=children[i].pdispVal;
if (pChild)
pChild->get_accName(CComVariant(CHILDID_SELF),&name);
}
else
{
pAcc->get_accName(children[i],&name);
}
switch (i)
{
case 2: if (wcslen(name)<_countof(fname)) wcscpy_s(fname,name); break;
case 3: if (wcslen(name)<_countof(dir)) wcscpy_s(dir,name); break;
case 4: size=name; break;
case 5: date=name; break;
}
}
if (bSrc)
{
m_FileName=fname;
m_SrcSize=size;
m_SrcTime=date;
}
else
{
m_DstSize=size;
m_DstTime=date;
}
if (!fname[0] || !dir[0]) return;
wchar_t fname2[_MAX_PATH];
memcpy(fname2,fname,sizeof(fname2));
*PathFindExtension(fname2)=0;
int len1=Strlen(fname2);
// the directory text is something like "filename (directory)". we need to parse out the real directory name
int len2=Strlen(dir);
if (dir[0]==0x202A) len1++; // for Arabic the first character is some RTL marker. needs to be skipped
if (dir[len1]==0x1E) len1++; // for Hebrew there is an extra control character after the file name
if (len1+1>=len2 || dir[len1]!=L' ' || dir[len1+1]!=L'(' || dir[len2-1]!=L')') return;
if (dir[len1+2]==0x202A) len1++; // for Hebrew the first character is some RTL marker. needs to be skipped
if (dir[len2-2]==0x202C) len2--; // for Hebrew the last character is some RTL marker. needs to be skipped
dir[len2-1]=0;
// construct the full file name
wchar_t path[_MAX_PATH];
_wmakepath_s(path,NULL,dir+len1+2,fname,NULL);
if (!bSrc)
{
DWORD attrib=GetFileAttributes(path);
if (attrib!=INVALID_FILE_ATTRIBUTES)
{
if (attrib&FILE_ATTRIBUTE_READONLY) m_bReadOnly=true;
if (attrib&FILE_ATTRIBUTE_SYSTEM) m_bSystem=true;
}
}
// get file icon
HICON hIcon=NULL;
CAbsolutePidl pidl;
if (SUCCEEDED(SHParseDisplayName(path,NULL,&pidl,0,NULL)) && pidl)
{
int iconSize=GetSystemMetrics(SM_CXICON);
HBITMAP hBitmap=NULL;
CComPtr<IShellItemImageFactory> pFactory;
if (SUCCEEDED(SHCreateItemFromIDList(pidl,IID_IShellItemImageFactory,(void**)&pFactory)) && pFactory)
{
SIZE size={iconSize,iconSize};
if (FAILED(pFactory->GetImage(size,SIIGBF_ICONONLY,&hBitmap)))
hBitmap=NULL;
}
if (hBitmap)
{
HBITMAP hMonoBitmap=CreateBitmap(iconSize,iconSize,1,1,NULL);
ICONINFO info={TRUE,0,0,hMonoBitmap,hBitmap};
hIcon=CreateIconIndirect(&info);
DeleteObject(hMonoBitmap);
DeleteObject(hBitmap);
}
}
if (!hIcon) return;
if (bSrc)
m_SrcIcon=hIcon;
else
m_DstIcon=hIcon;
}
const int WM_BRINGFOREGROUND=WM_USER+11;
INT_PTR CALLBACK CClassicCopyFile::DialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
if (uMsg==WM_INITDIALOG)
{
SetWindowText(hwndDlg,FindTranslation(L"Copy.Title",L"Confirm File Replace"));
CClassicCopyFile *pThis=(CClassicCopyFile*)lParam;
wchar_t text[_MAX_PATH*2];
if (pThis->m_bSystem)
{
Sprintf(text,_countof(text),FindTranslation(L"Copy.SubtitleSys",L"This folder already contains a system file named '%s'."),pThis->m_FileName);
if (GetSettingInt(L"OverwriteAlertLevel")>=1)
PlaySound(L".Default",NULL,SND_APPLICATION|SND_ALIAS|SND_ASYNC|SND_NODEFAULT|SND_SYSTEM);
}
else if (pThis->m_bReadOnly)
{
Sprintf(text,_countof(text),FindTranslation(L"Copy.SubtitleRO",L"This folder already contains a read-only file named '%s'."),pThis->m_FileName);
if (GetSettingInt(L"OverwriteAlertLevel")>=2)
PlaySound(L".Default",NULL,SND_APPLICATION|SND_ALIAS|SND_ASYNC|SND_NODEFAULT|SND_SYSTEM);
}
else
Sprintf(text,_countof(text),FindTranslation(L"Copy.Subtitle",L"This folder already contains a file named '%s'."),pThis->m_FileName);
SetDlgItemText(hwndDlg,IDC_STATICFNAME,text);
// load icon for file conflict (146) from Shell32.dll
HMODULE hShell32=GetModuleHandle(L"Shell32.dll");
pThis->m_Icon=LoadIcon(hShell32,MAKEINTRESOURCE(146));
if (pThis->m_Icon)
SendDlgItemMessage(hwndDlg,IDC_STATICICON1,STM_SETICON,(LPARAM)pThis->m_Icon,0);
// set the localized text
SetDlgItemText(hwndDlg,IDC_STATICPROMPT1,FindTranslation(L"Copy.Prompt1",L"Do you want to replace the existing file:"));
SetDlgItemText(hwndDlg,IDC_STATICDSTSIZE,pThis->m_DstSize);
SetDlgItemText(hwndDlg,IDC_STATICDSTTIME,pThis->m_DstTime);
SetDlgItemText(hwndDlg,IDC_STATICPROMPT2,FindTranslation(L"Copy.Prompt2",L"with this one?"));
SendDlgItemMessage(hwndDlg,IDC_STATICDSTICON,STM_SETICON,(LPARAM)pThis->m_DstIcon,0);
SetDlgItemText(hwndDlg,IDC_STATICSRCSIZE,pThis->m_SrcSize);
SetDlgItemText(hwndDlg,IDC_STATICSRCTIME,pThis->m_SrcTime);
SendDlgItemMessage(hwndDlg,IDC_STATICSRCICON,STM_SETICON,(LPARAM)pThis->m_SrcIcon,0);
SetDlgItemText(hwndDlg,IDOK,FindTranslation(L"Copy.Yes",L"&Yes"));
SetDlgItemText(hwndDlg,IDNO,FindTranslation(L"Copy.No",L"&No"));
if (GetDlgItem(hwndDlg,IDYES))
SetDlgItemText(hwndDlg,IDYES,FindTranslation(L"Copy.YesAll",L"Yes to &All"));
if (GetDlgItem(hwndDlg,IDCANCEL))
SetDlgItemText(hwndDlg,IDCANCEL,FindTranslation(L"Copy.Cancel",L"Cancel"));
Sprintf(text,_countof(text),L"<a>%s</a>",FindTranslation(L"Copy.More",L"&More..."));
SetDlgItemText(hwndDlg,IDC_LINKMORE,text);
PostMessage(hwndDlg,WM_BRINGFOREGROUND,0,0);
return TRUE;
}
if (uMsg==WM_BRINGFOREGROUND)
{
// bring window to front (sometimes on Windows7 it shows up behind Explorer)
SetForegroundWindow(hwndDlg);
return TRUE;
}
if (uMsg==WM_COMMAND && (wParam==IDOK || wParam==IDYES || wParam==IDNO || wParam==IDCANCEL))
{
EndDialog(hwndDlg,wParam);
return TRUE;
}
if (uMsg==WM_NOTIFY)
{
NMHDR *pHdr=(NMHDR*)lParam;
if (pHdr->idFrom==IDC_LINKMORE && (pHdr->code==NM_CLICK || pHdr->code==NM_RETURN))
{
EndDialog(hwndDlg,IDC_LINKMORE);
return TRUE;
}
}
return FALSE;
}
///////////////////////////////////////////////////////////////////////////////
// CClassicCopyFolder - this is the implementation of the Copy UI dialog box for folders
class CClassicCopyFolder
{
public:
CClassicCopyFolder( void ) { m_Icon=NULL; m_Original=NULL; }
~CClassicCopyFolder( void );
bool Run( HWND hWnd );
private:
HICON m_Icon;
HWND m_Original;
static INT_PTR CALLBACK DialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam );
};
CClassicCopyFolder::~CClassicCopyFolder( void )
{
if (m_Icon) DestroyIcon(m_Icon);
}
// Show the dialog box. Returns true to suppress the original task dialog box
bool CClassicCopyFolder::Run( HWND hWnd )
{
m_Original=hWnd;
const int ID_ALLCHECK=16663;
HWND check=GetDlgItem(hWnd,ID_ALLCHECK);
bool bMulti=(check && (GetWindowLong(check,GWL_STYLE)&WS_VISIBLE));
// pick the correct dialog template (for single and multiple files, for LTR and RTL)
int dlg=bMulti?(IsLanguageRTL()?IDD_FOLDERMULTIR:IDD_FOLDERMULTI):(IsLanguageRTL()?IDD_FOLDERR:IDD_FOLDER);
HWND parent=GetWindow(GetAncestor(hWnd,GA_ROOT),GW_OWNER);
int res=(int)DialogBoxParam(g_Instance,MAKEINTRESOURCE(dlg),parent,DialogProc,(LPARAM)this);
if (res==IDOK) // Yes button for single folder
{
// Yes was pressed, proceed with the operation
PostMessage(hWnd,WM_COMMAND,IDYES,(LPARAM)GetDlgItem(hWnd,IDYES));
}
else if (res==IDNO)
{
// No
if (bMulti)
{
if (GetKeyState(VK_SHIFT)<0)
{
CheckDlgButton(hWnd,ID_ALLCHECK,BST_CHECKED);
SendMessage(hWnd,WM_COMMAND,ID_ALLCHECK,(LPARAM)check);
}
PostMessage(hWnd,WM_COMMAND,IDNO,(LPARAM)GetDlgItem(hWnd,IDNO)); // Skip
}
else
PostMessage(hWnd,WM_COMMAND,IDCANCEL,(LPARAM)GetDlgItem(hWnd,IDCANCEL)); // No
}
else if (res==IDYES)
{
// Yes to All
CheckDlgButton(hWnd,ID_ALLCHECK,BST_CHECKED);
SendMessage(hWnd,WM_COMMAND,ID_ALLCHECK,(LPARAM)check);
PostMessage(hWnd,WM_COMMAND,IDYES,(LPARAM)GetDlgItem(hWnd,IDYES));
}
if (res==IDCANCEL)
{
// Cancel
if (GetKeyState(VK_SHIFT)<0 || GetKeyState(VK_CONTROL)<0)
return false; // // Shift+Cancel or Ctrl+Cancel was clicked - show the original dialog box
PostMessage(hWnd,WM_COMMAND,IDCANCEL,(LPARAM)GetDlgItem(hWnd,IDCANCEL)); // No
}
if (res==IDC_LINKMORE)
{
// More... was clicked - show the original dialog box
return false;
}
return true;
}
INT_PTR CALLBACK CClassicCopyFolder::DialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
if (uMsg==WM_INITDIALOG)
{
SetWindowText(hwndDlg,FindTranslation(L"Folder.Title",L"Confirm Folder Replace"));
CClassicCopyFolder *pThis=(CClassicCopyFolder*)lParam;
wchar_t text[2048];
// find the link control and get its text
HWND link=FindWindowEx(pThis->m_Original,NULL,WC_LINK,NULL);
if (link)
GetWindowText(link,text,_countof(text));
else
text[0]=0;
Strcat(text,_countof(text),L"\r\n\r\n");
Strcat(text,_countof(text),FindTranslation(L"Folder.Prompt",L"Do you still want to move or copy the folder?"));
SetDlgItemText(hwndDlg,IDC_STATICFNAME,text);
// load icon for file conflict (146) from Shell32.dll
HMODULE hShell32=GetModuleHandle(L"Shell32.dll");
pThis->m_Icon=LoadIcon(hShell32,MAKEINTRESOURCE(146));
if (pThis->m_Icon)
SendDlgItemMessage(hwndDlg,IDC_STATICICON1,STM_SETICON,(LPARAM)pThis->m_Icon,0);
// set the localized text
SetDlgItemText(hwndDlg,IDOK,FindTranslation(L"Copy.Yes",L"&Yes"));
SetDlgItemText(hwndDlg,IDNO,FindTranslation(L"Copy.No",L"&No"));
if (GetDlgItem(hwndDlg,IDYES))
SetDlgItemText(hwndDlg,IDYES,FindTranslation(L"Copy.YesAll",L"Yes to &All"));
if (GetDlgItem(hwndDlg,IDCANCEL))
SetDlgItemText(hwndDlg,IDCANCEL,FindTranslation(L"Copy.Cancel",L"Cancel"));
Sprintf(text,_countof(text),L"<a>%s</a>",FindTranslation(L"Copy.More",L"&More..."));
SetDlgItemText(hwndDlg,IDC_LINKMORE,text);
PostMessage(hwndDlg,WM_BRINGFOREGROUND,0,0);
return TRUE;
}
if (uMsg==WM_BRINGFOREGROUND)
{
// bring window to front (sometimes on Windows7 it shows up behind Explorer)
SetForegroundWindow(hwndDlg);
return TRUE;
}
if (uMsg==WM_COMMAND && (wParam==IDOK || wParam==IDYES || wParam==IDNO || wParam==IDCANCEL))
{
EndDialog(hwndDlg,wParam);
return TRUE;
}
if (uMsg==WM_NOTIFY)
{
NMHDR *pHdr=(NMHDR*)lParam;
if (pHdr->idFrom==IDC_LINKMORE && (pHdr->code==NM_CLICK || pHdr->code==NM_RETURN))
{
EndDialog(hwndDlg,IDC_LINKMORE);
return TRUE;
}
}
return FALSE;
}
///////////////////////////////////////////////////////////////////////////////
static CComPtr<IAccessible> FindMoreButton( IAccessible *pAcc )
{
CComVariant state;
CComVariant self(CHILDID_SELF);
pAcc->get_accState(self,&state);
if (!(state.intVal&(STATE_SYSTEM_UNAVAILABLE|STATE_SYSTEM_INVISIBLE)))
{
CComVariant role;
pAcc->get_accRole(self,&role);
if (role.intVal==ROLE_SYSTEM_PUSHBUTTON)
{
CComBSTR name;
if (SUCCEEDED(pAcc->get_accName(self,&name)) && name && _wcsicmp(name,g_ButtonMore)==0)
return pAcc;
}
}
CComVariant children[30];
long count;
AccessibleChildren(pAcc,0,_countof(children),children,&count);
for (int i=0;i<count;i++)
{
if (children[i].vt==VT_DISPATCH)
{
CComQIPtr<IAccessible> pChild=children[i].pdispVal;
if (pChild)
{
CComPtr<IAccessible> pRes=FindMoreButton(pChild);
if (pRes) return pRes;
}
}
}
return NULL;
}
///////////////////////////////////////////////////////////////////////////////
static LRESULT CALLBACK WindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData )
{
if (uMsg==WM_WINDOWPOSCHANGING)
{
WINDOWPOS *pos=(WINDOWPOS*)lParam;
if (pos->flags&SWP_SHOWWINDOW)
{
wchar_t title[256];
GetWindowText(hWnd,title,_countof(title));
if (_wcsicmp(title,g_TitleMove)==0 || _wcsicmp(title,g_TitleCopy)==0)
{
// file UI
if (GetSettingBool(L"ReplaceFileUI"))
{
CString log;
CComPtr<IAccessible> pAcc;
HRESULT h=AccessibleObjectFromWindow(hWnd,OBJID_WINDOW,IID_IAccessible,(void**)&pAcc);
if (SUCCEEDED(h) && pAcc)
{
CClassicCopyFile copy;
if (copy.Run(hWnd,pAcc,g_bLogLevel?&log:NULL))
{
pos->x=pos->y=-20000;
pos->flags&=~(SWP_SHOWWINDOW|SWP_NOMOVE);
}
}
else if (g_bLogLevel)
LogPrint(&log,L"AccessibleObjectFromWindow: error=0x%X, hWnd=0x%p",h,hWnd);
if (g_bLogLevel)
{
wchar_t fname[_MAX_PATH]=L"%LOCALAPPDATA%\\OpenShell";
DoEnvironmentSubst(fname,_MAX_PATH);
SHCreateDirectory(NULL,fname);
Strcat(fname,_countof(fname),L"\\ExplorerLog.txt");
if (!log.IsEmpty())
{
FILE *f;
if (_wfopen_s(&f,fname,L"wb")==0)
{
fwprintf(f,L"\xFEFF");
OSVERSIONINFOEX ver={sizeof(ver)};
#pragma warning(suppress:4996)
GetVersionEx((OSVERSIONINFO*)&ver);
fwprintf(f,L"version = %d.%d.%d - %d.%d\r\n\r\n",ver.dwMajorVersion,ver.dwMinorVersion,ver.dwBuildNumber,ver.wServicePackMajor,ver.wServicePackMinor);
wchar_t languages[100]={0};
ULONG size=4; // up to 4 languages
ULONG len=_countof(languages);
GetUserPreferredUILanguages(MUI_LANGUAGE_NAME,&size,languages,&len);
for (const wchar_t *lang=languages;*lang;lang+=wcslen(lang)+1)
fwprintf(f,L"language = %s\r\n",lang);
fwprintf(f,L"\r\n");
fwprintf(f,L"g_ButtonMove = '%s'\r\n",g_ButtonMove);
fwprintf(f,L"g_ButtonDontMove = '%s'\r\n",g_ButtonDontMove);
fwprintf(f,L"g_ButtonCopy = '%s'\r\n",g_ButtonCopy);
fwprintf(f,L"g_ButtonDontCopy = '%s'\r\n",g_ButtonDontCopy);
fwprintf(f,L"g_ButtonCancel = '%s'\r\n",g_ButtonCancel);
fwprintf(f,L"\r\n");
fwrite((const wchar_t*)log,log.GetLength(),2,f);
fclose(f);
}
}
else
DeleteFile(fname);
}
}
}
else if (_wcsicmp(title,g_TitleFolder)==0)
{
// folder UI
if (GetSettingBool(L"ReplaceFolderUI"))
{
CClassicCopyFolder copy;
if (copy.Run(hWnd))
{
pos->x=pos->y=-20000;
pos->flags&=~(SWP_SHOWWINDOW|SWP_NOMOVE);
}
}
}
else
{
// look for progress bar
if (GetSettingBool(L"EnableMore"))
{
HWND progress=FindChildWindow(hWnd,PROGRESS_CLASS);
if (progress)
{
bool bDef;
int delay=GetSettingInt(L"MoreProgressDelay",bDef);
if (bDef)
{
delay=0;
if (GetWinVersion()>=WIN_VER_WIN7)
{
BOOL comp;
if (SUCCEEDED(DwmIsCompositionEnabled(&comp)) && comp)
delay=500;
}
}
SetTimer(hWnd,'CLEX',delay,NULL);
return DefSubclassProc(hWnd,uMsg,wParam,lParam);
}
}
}
LRESULT res=DefSubclassProc(hWnd,uMsg,wParam,lParam);
RemoveWindowSubclass(hWnd,WindowProc,uIdSubclass);
return res;
}
}
if (uMsg==WM_TIMER && wParam=='CLEX')
{
KillTimer(hWnd,wParam);
CComPtr<IAccessible> pAcc;
HRESULT h=AccessibleObjectFromWindow(hWnd,OBJID_WINDOW,IID_IAccessible,(void**)&pAcc);
if (SUCCEEDED(h) && pAcc)
{
CComPtr<IAccessible> pMore=FindMoreButton(pAcc);
if (pMore) pMore->accDoDefaultAction(CComVariant(CHILDID_SELF));
}
LRESULT res=DefSubclassProc(hWnd,uMsg,wParam,lParam);
RemoveWindowSubclass(hWnd,WindowProc,uIdSubclass);
return res;
}
return DefSubclassProc(hWnd,uMsg,wParam,lParam);
}
LRESULT CALLBACK ClassicCopyHook( int nCode, WPARAM wParam, LPARAM lParam )
{
if (nCode==HCBT_CREATEWND)
{
HWND hWnd=(HWND)wParam;
CBT_CREATEWND *create=(CBT_CREATEWND*)lParam;
HINSTANCE hInst=(HINSTANCE)GetWindowLongPtr(hWnd,GWLP_HINSTANCE);
if (create->lpcs->lpszName && (intptr_t)create->lpcs->lpszClass==32770 && hInst==g_hShell32)
{
WaitDllInitThread();
static LONG id;
int i=InterlockedIncrement(&id);
SetWindowSubclass(hWnd,WindowProc,i,0);
}
}
TlsData *pTlsData=GetTlsData();
return CallNextHookEx(pTlsData->copyHook,nCode,wParam,lParam);
}
void InitClassicCopyProcess( void )
{
// load UI text from shell32.dll
// the text is used to locate controls in the copy dialog by name
g_hShell32=GetModuleHandle(L"shell32.dll");
LoadString(g_hShell32,17027,g_TitleMove,256);
LoadString(g_hShell32,17024,g_TitleCopy,256);
LoadString(g_hShell32,16705,g_TitleFolder,256);
LoadString(g_hShell32,13610,g_ButtonMove,256);
LoadString(g_hShell32,13623,g_ButtonDontMove,256);
LoadString(g_hShell32,13604,g_ButtonCopy,256);
LoadString(g_hShell32,13606,g_ButtonDontCopy,256);
LoadString(g_hShell32,13588,g_ButtonCancel,256);
LoadString(g_hShell32,32992,g_ButtonMore,256);
}
void InitClassicCopyThread( void )
{
TlsData *pTlsData=GetTlsData();
if (!pTlsData->copyHook)
pTlsData->copyHook=SetWindowsHookEx(WH_CBT,ClassicCopyHook,g_Instance,GetCurrentThreadId());
}
void FreeClassicCopyThread( void )
{
TlsData *pTlsData=GetTlsData();
if (pTlsData->copyHook)
{
UnhookWindowsHookEx(pTlsData->copyHook);
pTlsData->copyHook=NULL;
}
}