OSDN Git Service

Update of Lithuanian translation (#496)
[winmerge-jp/winmerge-jp.git] / Src / PropShell.cpp
1 /** 
2  * @file  PropShell.cpp
3  *
4  * @brief Implementation file for Shell Options dialog.
5  *
6  */
7
8 #include "stdafx.h"
9 #include "PropShell.h"
10 #include "RegKey.h"
11 #include "OptionsMgr.h"
12 #include "OptionsPanel.h"
13 #include "Constants.h"
14 #include "Environment.h"
15 #include "paths.h"
16
17 #ifdef _DEBUG
18 #define new DEBUG_NEW
19 #endif
20
21 /// Flags for enabling and mode of extension
22 #define CONTEXT_F_ENABLED 0x01
23 #define CONTEXT_F_ADVANCED 0x02
24
25 // registry values
26 static LPCTSTR f_RegValueEnabled = _T("ContextMenuEnabled");
27 static LPCTSTR f_RegValuePath = _T("Executable");
28
29 static bool IsShellExtensionRegistered(bool peruser)
30 {
31         HKEY hKey;
32 #ifdef _WIN64
33         DWORD ulOptions = KEY_QUERY_VALUE;
34 #else
35         auto Is64BitWindows = []() { BOOL f64 = FALSE; return IsWow64Process(GetCurrentProcess(), &f64) && f64; };
36         DWORD ulOptions = KEY_QUERY_VALUE | (Is64BitWindows() ? KEY_WOW64_64KEY : 0);
37 #endif
38         if (ERROR_SUCCESS == RegOpenKeyEx(peruser ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE, _T("Software\\Classes\\CLSID\\{4E716236-AA30-4C65-B225-D68BBA81E9C2}"), 0, ulOptions, &hKey))
39         {
40                 RegCloseKey(hKey);
41                 return true;
42         }
43         return false;
44 }
45
46 static bool RegisterShellExtension(bool unregister, bool peruser)
47 {
48         TCHAR szSystem32[260] = { 0 };
49         TCHAR szSysWow64[260] = { 0 };
50         GetSystemDirectory(szSystem32, sizeof(szSystem32) / sizeof(szSystem32[0]));
51         GetSystemWow64Directory(szSysWow64, sizeof(szSysWow64) / sizeof(szSysWow64[0]));
52
53         String progpath = env::GetProgPath();
54         String regsvr32 = paths::ConcatPath(szSystem32, _T("regsvr32.exe"));
55         String args;
56         String options = (unregister ? _T("/s /u") : _T("/s"));
57         options += peruser ? _T(" /n /i:user") : _T("");
58         SHELLEXECUTEINFO sei = { sizeof(sei) };
59         if (!peruser)
60                 sei.lpVerb = _T("runas");
61         if (szSysWow64[0])
62         {
63                 args = options + _T(" \"") + paths::ConcatPath(progpath, _T("ShellExtensionX64.dll")) + _T("\"");
64
65                 sei.lpFile = regsvr32.c_str();
66                 sei.lpParameters = args.c_str();
67                 ShellExecuteEx(&sei);
68
69                 regsvr32 = paths::ConcatPath(szSysWow64, _T("regsvr32.exe"));
70                 args = options + _T("\"") + paths::ConcatPath(progpath, _T("ShellExtensionU.dll")) + _T("\"");
71                 sei.lpFile = regsvr32.c_str();
72                 sei.lpParameters = args.c_str();
73                 return !!ShellExecuteEx(&sei);
74         }
75         else
76         {
77                 args = options + _T(" \"") + paths::ConcatPath(progpath, _T("ShellExtensionU.dll")) + _T("\"");
78                 sei.lpFile = regsvr32.c_str();
79                 sei.lpParameters = args.c_str();
80                 return !!ShellExecuteEx(&sei);
81         }
82 }
83
84 PropShell::PropShell(COptionsMgr *optionsMgr) 
85 : OptionsPanel(optionsMgr, PropShell::IDD)
86 , m_bContextAdded(false)
87 , m_bContextAdvanced(false)
88 {
89 }
90
91 BOOL PropShell::OnInitDialog()
92 {
93         OptionsPanel::OnInitDialog();
94
95 #ifndef BCM_SETSHIELD
96 #define BCM_SETSHIELD            (0x1600/*BCM_FIRST*/ + 0x000C)
97 #endif
98
99         SendDlgItemMessage(IDC_REGISTER_SHELLEXTENSION, BCM_SETSHIELD, 0, TRUE);
100         SendDlgItemMessage(IDC_UNREGISTER_SHELLEXTENSION, BCM_SETSHIELD, 0, TRUE);
101
102         // Update shell extension checkboxes
103         UpdateButtons();
104         GetContextRegValues();
105         AdvancedContextMenuCheck();
106         UpdateData(FALSE);
107
108         SetTimer(0, 1000, nullptr);
109
110         return TRUE;  // return TRUE  unless you set the focus to a control
111 }
112
113 void PropShell::DoDataExchange(CDataExchange* pDX)
114 {
115         CPropertyPage::DoDataExchange(pDX);
116         //{{AFX_DATA_MAP(PropShell)
117         DDX_Check(pDX, IDC_EXPLORER_CONTEXT, m_bContextAdded);
118         DDX_Check(pDX, IDC_EXPLORER_ADVANCED, m_bContextAdvanced);
119         //}}AFX_DATA_MAP
120 }
121
122 BEGIN_MESSAGE_MAP(PropShell, OptionsPanel)
123         //{{AFX_MSG_MAP(PropShell)
124         ON_BN_CLICKED(IDC_EXPLORER_CONTEXT, OnAddToExplorer)
125         ON_BN_CLICKED(IDC_REGISTER_SHELLEXTENSION, OnRegisterShellExtension)
126         ON_BN_CLICKED(IDC_UNREGISTER_SHELLEXTENSION, OnUnregisterShellExtension)
127         ON_BN_CLICKED(IDC_REGISTER_SHELLEXTENSION_PERUSER, OnRegisterShellExtensionPerUser)
128         ON_BN_CLICKED(IDC_UNREGISTER_SHELLEXTENSION_PERUSER, OnUnregisterShellExtensionPerUser)
129         ON_WM_TIMER()
130         //}}AFX_MSG_MAP
131 END_MESSAGE_MAP()
132
133 /** 
134  * @brief Reads options values from storage to UI.
135  */
136 void PropShell::ReadOptions()
137 {
138         GetContextRegValues();
139 }
140
141 /** 
142  * @brief Writes options values from UI to storage.
143  */
144 void PropShell::WriteOptions()
145 {
146         SaveMergePath(); // saves context menu settings as well
147 }
148
149 /// Get registry values for ShellExtension
150 void PropShell::GetContextRegValues()
151 {
152         CRegKeyEx reg;
153         LONG retVal = 0;
154         retVal = reg.Open(HKEY_CURRENT_USER, RegDir);
155         if (retVal != ERROR_SUCCESS)
156         {
157                 String msg = strutils::format(_T("Failed to open registry key HKCU/%s:\n\t%d : %s"),
158                         RegDir, retVal, GetSysError(retVal));
159                 LogErrorString(msg);
160                 return;
161         }
162
163         // Read bitmask for shell extension settings
164         DWORD dwContextEnabled = reg.ReadDword(f_RegValueEnabled, 0);
165
166         if (dwContextEnabled & CONTEXT_F_ENABLED)
167                 m_bContextAdded = true;
168
169         if (dwContextEnabled & CONTEXT_F_ADVANCED)
170                 m_bContextAdvanced = true;
171 }
172
173 /// Set registry values for ShellExtension
174 void PropShell::OnAddToExplorer()
175 {
176         AdvancedContextMenuCheck();
177         UpdateButtons();
178 }
179
180 /// Saves given path to registry for ShellExtension, and Context Menu settings
181 void PropShell::SaveMergePath()
182 {
183         TCHAR temp[MAX_PATH] = {0};
184         LONG retVal = 0;
185         GetModuleFileName(AfxGetInstanceHandle(), temp, MAX_PATH);
186
187         CRegKeyEx reg;
188         retVal = reg.Open(HKEY_CURRENT_USER, RegDir);
189         if (retVal != ERROR_SUCCESS)
190         {
191                 String msg = strutils::format(_T("Failed to open registry key HKCU/%s:\n\t%d : %s"),
192                         RegDir, retVal, GetSysError(retVal));
193                 LogErrorString(msg);
194                 return;
195         }
196
197         // Save path to WinMerge(U).exe
198         retVal = reg.WriteString(f_RegValuePath, temp);
199         if (retVal != ERROR_SUCCESS)
200         {
201                 String msg = strutils::format(_T("Failed to set registry value %s:\n\t%d : %s"),
202                         f_RegValuePath, retVal, GetSysError(retVal));
203                 LogErrorString(msg);
204         }
205
206         // Determine bitmask for shell extension
207         DWORD dwContextEnabled = reg.ReadDword(f_RegValueEnabled, 0);
208         if (m_bContextAdded)
209                 dwContextEnabled |= CONTEXT_F_ENABLED;
210         else
211                 dwContextEnabled &= ~CONTEXT_F_ENABLED;
212
213         if (m_bContextAdvanced)
214                 dwContextEnabled |= CONTEXT_F_ADVANCED;
215         else
216                 dwContextEnabled &= ~CONTEXT_F_ADVANCED;
217
218         retVal = reg.WriteDword(f_RegValueEnabled, dwContextEnabled);
219         if (retVal != ERROR_SUCCESS)
220         {
221                 String msg = strutils::format(_T("Failed to set registry value %s to %d:\n\t%d : %s"),
222                         f_RegValueEnabled, dwContextEnabled, retVal, GetSysError(retVal));
223                 LogErrorString(msg);
224         }
225 }
226
227 /// Enable/Disable "Advanced menu" checkbox.
228 void PropShell::AdvancedContextMenuCheck()
229 {
230         if (!IsDlgButtonChecked(IDC_EXPLORER_CONTEXT))
231         {
232                 CheckDlgButton(IDC_EXPLORER_ADVANCED, FALSE);
233                 m_bContextAdvanced = false;
234         }
235 }
236
237 void PropShell::UpdateButtons()
238 {
239         bool registered = IsShellExtensionRegistered(false);
240         bool registeredPerUser = IsShellExtensionRegistered(true);
241         EnableDlgItem(IDC_EXPLORER_CONTEXT, registered || registeredPerUser);
242         EnableDlgItem(IDC_REGISTER_SHELLEXTENSION, !registered);
243         EnableDlgItem(IDC_UNREGISTER_SHELLEXTENSION, registered);
244         EnableDlgItem(IDC_REGISTER_SHELLEXTENSION_PERUSER, !registeredPerUser);
245         EnableDlgItem(IDC_UNREGISTER_SHELLEXTENSION_PERUSER, registeredPerUser);
246         EnableDlgItem(IDC_EXPLORER_ADVANCED, 
247                 (registered || registeredPerUser) && IsDlgButtonChecked(IDC_EXPLORER_CONTEXT));
248 }
249
250 void PropShell::OnRegisterShellExtension()
251 {
252         RegisterShellExtension(false, false);
253 }
254
255 void PropShell::OnUnregisterShellExtension()
256 {
257         RegisterShellExtension(true, false);
258 }
259
260 void PropShell::OnRegisterShellExtensionPerUser()
261 {
262         RegisterShellExtension(false, true);
263 }
264
265 void PropShell::OnUnregisterShellExtensionPerUser()
266 {
267         RegisterShellExtension(true, true);
268 }
269
270 void PropShell::OnTimer(UINT_PTR nIDEvent)
271 {
272         UpdateButtons();
273 }