1 /* 2 KeePass Password Safe - The Open-Source Password Manager 3 Copyright (C) 2003-2021 Dominik Reichl <dominik.reichl@t-online.de> 4 5 This program is free software; you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published by 7 the Free Software Foundation; either version 2 of the License, or 8 (at your option) any later version. 9 10 This program is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with this program; if not, write to the Free Software 17 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 18 */ 19 20 using System; 21 using System.Collections.Generic; 22 using System.Collections.ObjectModel; 23 using System.Diagnostics; 24 using System.Drawing; 25 using System.Runtime.InteropServices; 26 using System.Text; 27 using System.Windows.Forms; 28 29 using KeePass.Native; 30 using KeePass.Resources; 31 using KeePass.Util; 32 33 using KeePassLib.Utility; 34 35 namespace KeePass.UI 36 { 37 [Flags] 38 public enum VtdFlags 39 { 40 None = 0, 41 EnableHyperlinks = 0x0001, 42 UseHIconMain = 0x0002, 43 UseHIconFooter = 0x0004, 44 AllowDialogCancellation = 0x0008, 45 UseCommandLinks = 0x0010, 46 UseCommandLinksNoIcon = 0x0020, 47 ExpandFooterArea = 0x0040, 48 ExpandedByDefault = 0x0080, 49 VerificationFlagChecked = 0x0100, 50 ShowProgressBar = 0x0200, 51 ShowMarqueeProgressBar = 0x0400, 52 CallbackTimer = 0x0800, 53 PositionRelativeToWindow = 0x1000, 54 RtlLayout = 0x2000, 55 NoDefaultRadioButton = 0x4000 56 } 57 58 [Flags] 59 internal enum VtdCommonButtonFlags 60 { 61 None = 0, 62 OkButton = 0x0001, // Return value: IDOK = DialogResult.OK 63 YesButton = 0x0002, // Return value: IDYES 64 NoButton = 0x0004, // Return value: IDNO 65 CancelButton = 0x0008, // Return value: IDCANCEL 66 RetryButton = 0x0010, // Return value: IDRETRY 67 CloseButton = 0x0020 // Return value: IDCLOSE 68 } 69 70 public enum VtdIcon 71 { 72 None = 0, 73 Warning = 0xFFFF, 74 Error = 0xFFFE, 75 Information = 0xFFFD, 76 Shield = 0xFFFC 77 } 78 79 public enum VtdCustomIcon 80 { 81 None = 0, 82 Question = 1 83 } 84 85 internal enum VtdNtf 86 { 87 Created = 0, 88 Navigated = 1, 89 ButtonClicked = 2, 90 HyperlinkClicked = 3, 91 Timer = 4, 92 Destroyed = 5, 93 RadioButtonClicked = 6, 94 DialogConstructed = 7, 95 VerificationClicked = 8, 96 Help = 9, 97 ExpandoButtonClicked = 10 98 } 99 100 // Pack = 4 required for 64-bit compatibility 101 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 4)] 102 internal struct VtdButton 103 { 104 public int ID; 105 106 [MarshalAs(UnmanagedType.LPWStr)] 107 public string Text; 108 VtdButtonKeePass.UI.VtdButton109 public VtdButton(bool bConstruct) 110 { 111 Debug.Assert(bConstruct); 112 113 this.ID = (int)DialogResult.Cancel; 114 this.Text = string.Empty; 115 } 116 } 117 118 // Pack = 4 required for 64-bit compatibility 119 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 4)] 120 internal struct VtdConfig 121 { 122 public uint cbSize; 123 public IntPtr hwndParent; 124 public IntPtr hInstance; 125 126 [MarshalAs(UnmanagedType.U4)] 127 public VtdFlags dwFlags; 128 129 [MarshalAs(UnmanagedType.U4)] 130 public VtdCommonButtonFlags dwCommonButtons; 131 132 [MarshalAs(UnmanagedType.LPWStr)] 133 public string pszWindowTitle; 134 135 public IntPtr hMainIcon; 136 137 [MarshalAs(UnmanagedType.LPWStr)] 138 public string pszMainInstruction; 139 140 [MarshalAs(UnmanagedType.LPWStr)] 141 public string pszContent; 142 143 public uint cButtons; 144 public IntPtr pButtons; 145 public int nDefaultButton; 146 public uint cRadioButtons; 147 public IntPtr pRadioButtons; 148 public int nDefaultRadioButton; 149 150 [MarshalAs(UnmanagedType.LPWStr)] 151 public string pszVerificationText; 152 153 [MarshalAs(UnmanagedType.LPWStr)] 154 public string pszExpandedInformation; 155 156 [MarshalAs(UnmanagedType.LPWStr)] 157 public string pszExpandedControlText; 158 159 [MarshalAs(UnmanagedType.LPWStr)] 160 public string pszCollapsedControlText; 161 162 public IntPtr hFooterIcon; 163 164 [MarshalAs(UnmanagedType.LPWStr)] 165 public string pszFooter; 166 167 public TaskDialogCallbackProc pfCallback; 168 public IntPtr lpCallbackData; 169 public uint cxWidth; 170 VtdConfigKeePass.UI.VtdConfig171 public VtdConfig(bool bConstruct) 172 { 173 Debug.Assert(bConstruct); 174 175 cbSize = (uint)Marshal.SizeOf(typeof(VtdConfig)); 176 hwndParent = IntPtr.Zero; 177 hInstance = IntPtr.Zero; 178 179 dwFlags = VtdFlags.None; 180 if(Program.Translation.Properties.RightToLeft) dwFlags |= VtdFlags.RtlLayout; 181 182 dwCommonButtons = VtdCommonButtonFlags.None; 183 pszWindowTitle = null; 184 hMainIcon = IntPtr.Zero; 185 pszMainInstruction = string.Empty; 186 pszContent = string.Empty; 187 cButtons = 0; 188 pButtons = IntPtr.Zero; 189 nDefaultButton = 0; 190 cRadioButtons = 0; 191 pRadioButtons = IntPtr.Zero; 192 nDefaultRadioButton = 0; 193 pszVerificationText = null; 194 pszExpandedInformation = null; 195 pszExpandedControlText = null; 196 pszCollapsedControlText = null; 197 hFooterIcon = IntPtr.Zero; 198 pszFooter = null; 199 pfCallback = null; 200 lpCallbackData = IntPtr.Zero; 201 cxWidth = 0; 202 } 203 } 204 TaskDialogCallbackProc(IntPtr hwnd, uint uNotification, UIntPtr wParam, IntPtr lParam, IntPtr lpRefData)205 internal delegate int TaskDialogCallbackProc(IntPtr hwnd, uint uNotification, 206 UIntPtr wParam, IntPtr lParam, IntPtr lpRefData); 207 208 public sealed class VistaTaskDialog 209 { 210 private const int VtdConfigSize32 = 96; 211 private const int VtdConfigSize64 = 160; 212 213 private VtdConfig m_cfg = new VtdConfig(true); 214 private int m_iResult = (int)DialogResult.Cancel; 215 private bool m_bVerification = false; 216 217 private List<VtdButton> m_vButtons = new List<VtdButton>(); 218 219 // private IntPtr m_hWnd = IntPtr.Zero; 220 221 public string WindowTitle 222 { 223 get { return m_cfg.pszWindowTitle; } 224 set { m_cfg.pszWindowTitle = value; } 225 } 226 227 public string MainInstruction 228 { 229 get { return m_cfg.pszMainInstruction; } 230 set { m_cfg.pszMainInstruction = value; } 231 } 232 233 internal ReadOnlyCollection<VtdButton> Buttons 234 { 235 get { return m_vButtons.AsReadOnly(); } 236 } 237 238 public string Content 239 { 240 get { return m_cfg.pszContent; } 241 set { m_cfg.pszContent = value; } 242 } 243 244 public bool CommandLinks 245 { 246 get { return ((m_cfg.dwFlags & VtdFlags.UseCommandLinks) != VtdFlags.None); } 247 set 248 { 249 if(value) m_cfg.dwFlags |= VtdFlags.UseCommandLinks; 250 else m_cfg.dwFlags &= ~VtdFlags.UseCommandLinks; 251 } 252 } 253 254 public int DefaultButtonID 255 { 256 get { return m_cfg.nDefaultButton; } 257 set { m_cfg.nDefaultButton = value; } 258 } 259 260 public bool EnableHyperlinks 261 { 262 get { return ((m_cfg.dwFlags & VtdFlags.EnableHyperlinks) != VtdFlags.None); } 263 set 264 { 265 if(value) m_cfg.dwFlags |= VtdFlags.EnableHyperlinks; 266 else m_cfg.dwFlags &= ~VtdFlags.EnableHyperlinks; 267 } 268 } 269 270 public string ExpandedInformation 271 { 272 get { return m_cfg.pszExpandedInformation; } 273 set { m_cfg.pszExpandedInformation = value; } 274 } 275 276 public bool ExpandedByDefault 277 { 278 get { return ((m_cfg.dwFlags & VtdFlags.ExpandedByDefault) != VtdFlags.None); } 279 set 280 { 281 if(value) m_cfg.dwFlags |= VtdFlags.ExpandedByDefault; 282 else m_cfg.dwFlags &= ~VtdFlags.ExpandedByDefault; 283 } 284 } 285 286 public string FooterText 287 { 288 get { return m_cfg.pszFooter; } 289 set { m_cfg.pszFooter = value; } 290 } 291 292 public string VerificationText 293 { 294 get { return m_cfg.pszVerificationText; } 295 set { m_cfg.pszVerificationText = value; } 296 } 297 298 public int Result 299 { 300 get { return m_iResult; } 301 } 302 303 public bool ResultVerificationChecked 304 { 305 get { return m_bVerification; } 306 } 307 308 public event EventHandler<LinkClickedEventArgs> LinkClicked; 309 VistaTaskDialog()310 public VistaTaskDialog() 311 { 312 } 313 AddButton(int iResult, string strCommand, string strDescription)314 public void AddButton(int iResult, string strCommand, string strDescription) 315 { 316 if(strCommand == null) throw new ArgumentNullException("strCommand"); 317 318 VtdButton btn = new VtdButton(true); 319 320 if(strDescription == null) btn.Text = strCommand; 321 else btn.Text = strCommand + "\n" + strDescription; 322 323 btn.ID = iResult; 324 325 m_vButtons.Add(btn); 326 } 327 SetIcon(VtdIcon vtdIcon)328 public void SetIcon(VtdIcon vtdIcon) 329 { 330 m_cfg.dwFlags &= ~VtdFlags.UseHIconMain; 331 m_cfg.hMainIcon = new IntPtr((int)vtdIcon); 332 } 333 SetIcon(VtdCustomIcon vtdIcon)334 public void SetIcon(VtdCustomIcon vtdIcon) 335 { 336 if(vtdIcon == VtdCustomIcon.Question) 337 SetIcon(SystemIcons.Question.Handle); 338 } 339 SetIcon(IntPtr hIcon)340 public void SetIcon(IntPtr hIcon) 341 { 342 m_cfg.dwFlags |= VtdFlags.UseHIconMain; 343 m_cfg.hMainIcon = hIcon; 344 } 345 SetFooterIcon(VtdIcon vtdIcon)346 public void SetFooterIcon(VtdIcon vtdIcon) 347 { 348 m_cfg.dwFlags &= ~VtdFlags.UseHIconFooter; 349 m_cfg.hFooterIcon = new IntPtr((int)vtdIcon); 350 } 351 ButtonsToPtr()352 private void ButtonsToPtr() 353 { 354 if(m_vButtons.Count == 0) { m_cfg.pButtons = IntPtr.Zero; return; } 355 356 int nConfigSize = Marshal.SizeOf(typeof(VtdButton)); 357 m_cfg.pButtons = Marshal.AllocHGlobal(m_vButtons.Count * nConfigSize); 358 m_cfg.cButtons = (uint)m_vButtons.Count; 359 360 for(int i = 0; i < m_vButtons.Count; ++i) 361 { 362 long l = m_cfg.pButtons.ToInt64() + (i * nConfigSize); 363 Marshal.StructureToPtr(m_vButtons[i], new IntPtr(l), false); 364 } 365 } 366 FreeButtonsPtr()367 private void FreeButtonsPtr() 368 { 369 if(m_cfg.pButtons == IntPtr.Zero) return; 370 371 int nConfigSize = Marshal.SizeOf(typeof(VtdButton)); 372 for(int i = 0; i < m_vButtons.Count; ++i) 373 { 374 long l = m_cfg.pButtons.ToInt64() + (i * nConfigSize); 375 Marshal.DestroyStructure(new IntPtr(l), typeof(VtdButton)); 376 } 377 378 Marshal.FreeHGlobal(m_cfg.pButtons); 379 m_cfg.pButtons = IntPtr.Zero; 380 m_cfg.cButtons = 0; 381 } 382 ShowDialog()383 public bool ShowDialog() 384 { 385 return ShowDialog(null); 386 } 387 ShowDialog(Form fParent)388 public bool ShowDialog(Form fParent) 389 { 390 MessageService.ExternalIncrementMessageCount(); 391 392 Form f = fParent; 393 if(f == null) f = MessageService.GetTopForm(); 394 if(f == null) f = GlobalWindowManager.TopWindow; 395 if(f == null) f = Program.MainForm; 396 397 #if DEBUG 398 if(GlobalWindowManager.TopWindow != null) 399 { 400 Debug.Assert(f == GlobalWindowManager.TopWindow); 401 } 402 if(Program.MainForm != null) // Skip check for TrlUtil 403 { 404 Debug.Assert((f == MessageService.GetTopForm()) || (f == Program.MainForm)); 405 } 406 #endif 407 408 bool bResult; 409 if((f == null) || !f.InvokeRequired) 410 bResult = InternalShowDialog(f); 411 else 412 bResult = (bool)f.Invoke(new InternalShowDialogDelegate( 413 this.InternalShowDialog), f); 414 415 MessageService.ExternalDecrementMessageCount(); 416 return bResult; 417 } 418 InternalShowDialogDelegate(Form fParent)419 private delegate bool InternalShowDialogDelegate(Form fParent); 420 InternalShowDialog(Form fParent)421 private bool InternalShowDialog(Form fParent) 422 { 423 if(IntPtr.Size == 4) 424 { Debug.Assert(Marshal.SizeOf(typeof(VtdConfig)) == VtdConfigSize32); } 425 else if(IntPtr.Size == 8) 426 { Debug.Assert(Marshal.SizeOf(typeof(VtdConfig)) == VtdConfigSize64); } 427 else { Debug.Assert(false); } 428 429 m_cfg.cbSize = (uint)Marshal.SizeOf(typeof(VtdConfig)); 430 431 if(fParent == null) m_cfg.hwndParent = IntPtr.Zero; 432 else 433 { 434 try { m_cfg.hwndParent = fParent.Handle; } 435 catch(Exception) 436 { 437 Debug.Assert(false); 438 m_cfg.hwndParent = IntPtr.Zero; 439 } 440 } 441 442 bool bExp = (m_cfg.pszExpandedInformation != null); 443 m_cfg.pszExpandedControlText = (bExp ? KPRes.Details : null); 444 m_cfg.pszCollapsedControlText = (bExp ? KPRes.Details : null); 445 446 int pnButton = 0, pnRadioButton = 0; 447 bool bVerification = false; 448 449 try { ButtonsToPtr(); } 450 catch(Exception) { Debug.Assert(false); return false; } 451 452 m_cfg.pfCallback = this.OnTaskDialogCallback; 453 454 try 455 { 456 using(EnableThemingInScope etis = new EnableThemingInScope(true)) 457 { 458 if(NativeMethods.TaskDialogIndirect(ref m_cfg, out pnButton, 459 out pnRadioButton, out bVerification) != 0) 460 throw new NotSupportedException(); 461 } 462 } 463 catch(Exception) { return false; } 464 finally 465 { 466 try 467 { 468 m_cfg.pfCallback = null; 469 FreeButtonsPtr(); 470 } 471 catch(Exception) { Debug.Assert(false); } 472 } 473 474 m_iResult = pnButton; 475 m_bVerification = bVerification; 476 return true; 477 } 478 OnTaskDialogCallback(IntPtr hwnd, uint uNotification, UIntPtr wParam, IntPtr lParam, IntPtr lpRefData)479 private int OnTaskDialogCallback(IntPtr hwnd, uint uNotification, 480 UIntPtr wParam, IntPtr lParam, IntPtr lpRefData) 481 { 482 try 483 { 484 // if((uNotification == (uint)VtdNtf.Created) || 485 // (uNotification == (uint)VtdNtf.DialogConstructed)) 486 // UpdateHWnd(hwnd); 487 // else if(uNotification == (uint)VtdNtf.Destroyed) 488 // UpdateHWnd(IntPtr.Zero); 489 490 if((uNotification == (uint)VtdNtf.HyperlinkClicked) && this.EnableHyperlinks && 491 (lParam != IntPtr.Zero)) 492 { 493 string str = Marshal.PtrToStringUni(lParam); 494 if(str != null) 495 { 496 if(str.StartsWith("http:", StrUtil.CaseIgnoreCmp) || 497 str.StartsWith("https:", StrUtil.CaseIgnoreCmp)) 498 WinUtil.OpenUrl(str, null); 499 else if(this.LinkClicked != null) 500 { 501 LinkClickedEventArgs e = new LinkClickedEventArgs(str); 502 this.LinkClicked(this, e); 503 } 504 else { Debug.Assert(false); } 505 } 506 else { Debug.Assert(false); } 507 } 508 } 509 catch(Exception) { Debug.Assert(false); } 510 511 return 0; 512 } 513 514 /* private void UpdateHWnd(IntPtr hWnd) 515 { 516 if(hWnd != m_hWnd) { } // Unregister m_hWnd 517 // Register hWnd 518 m_hWnd = hWnd; 519 } */ 520 CreateLink(string strHRef, string strText)521 public static string CreateLink(string strHRef, string strText) 522 { 523 string strH = (strHRef ?? string.Empty); 524 string strT = (strText ?? string.Empty); 525 return ("<A HREF=\"" + strH + "\">" + strT + "</A>"); 526 } 527 Unlink(string strText)528 internal static string Unlink(string strText) 529 { 530 if(string.IsNullOrEmpty(strText)) return string.Empty; 531 532 string str = strText; 533 while(true) 534 { 535 int iS = str.IndexOf("<A HREF=\"", StrUtil.CaseIgnoreCmp); 536 if(iS < 0) break; 537 538 const string strE = "\">"; 539 int iE = str.IndexOf(strE, iS, StrUtil.CaseIgnoreCmp); 540 if(iE < 0) { Debug.Assert(false); break; } 541 542 const string strC = "</A>"; 543 int iC = str.IndexOf(strC, iE, StrUtil.CaseIgnoreCmp); 544 if(iC < 0) { Debug.Assert(false); break; } 545 546 str = str.Remove(iC, strC.Length); 547 str = str.Remove(iS, iE - iS + strE.Length); 548 } 549 550 return str; 551 } 552 ShowMessageBox(string strContent, string strMainInstruction, string strWindowTitle, VtdIcon vtdIcon, Form fParent)553 public static bool ShowMessageBox(string strContent, string strMainInstruction, 554 string strWindowTitle, VtdIcon vtdIcon, Form fParent) 555 { 556 return (ShowMessageBoxEx(strContent, strMainInstruction, strWindowTitle, 557 vtdIcon, fParent, null, 0, null, 0) >= 0); 558 } 559 ShowMessageBoxEx(string strContent, string strMainInstruction, string strWindowTitle, VtdIcon vtdIcon, Form fParent, string strButton1, int iResult1, string strButton2, int iResult2)560 public static int ShowMessageBoxEx(string strContent, string strMainInstruction, 561 string strWindowTitle, VtdIcon vtdIcon, Form fParent, 562 string strButton1, int iResult1, string strButton2, int iResult2) 563 { 564 VistaTaskDialog vtd = new VistaTaskDialog(); 565 566 vtd.CommandLinks = false; 567 568 if(strContent != null) vtd.Content = strContent; 569 if(strMainInstruction != null) vtd.MainInstruction = strMainInstruction; 570 if(strWindowTitle != null) vtd.WindowTitle = strWindowTitle; 571 572 vtd.SetIcon(vtdIcon); 573 574 bool bCustomButton = false; 575 if(!string.IsNullOrEmpty(strButton1)) 576 { 577 vtd.AddButton(iResult1, strButton1, null); 578 bCustomButton = true; 579 } 580 if(!string.IsNullOrEmpty(strButton2)) 581 { 582 vtd.AddButton(iResult2, strButton2, null); 583 bCustomButton = true; 584 } 585 586 if(!vtd.ShowDialog(fParent)) return -1; 587 return (bCustomButton ? vtd.Result : 0); 588 } 589 } 590 } 591