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.Diagnostics; 22 using System.Drawing; 23 using System.IO; 24 using System.Runtime.InteropServices; 25 using System.Security; 26 using System.Text; 27 using System.Windows.Forms; 28 29 using KeePass.UI; 30 using KeePass.Util; 31 32 using KeePassLib.Utility; 33 34 using NativeLib = KeePassLib.Native.NativeLib; 35 36 namespace KeePass.Native 37 { 38 internal static partial class NativeMethods 39 { GetWindowText(IntPtr hWnd, bool bTrim)40 internal static string GetWindowText(IntPtr hWnd, bool bTrim) 41 { 42 // cc may be greater than the actual length; 43 // https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-getwindowtextlengthw 44 int cc = GetWindowTextLength(hWnd); 45 if(cc <= 0) return string.Empty; 46 47 // StringBuilder sb = new StringBuilder(cc + 2); 48 // int ccReal = GetWindowText(hWnd, sb, cc + 1); 49 // if(ccReal <= 0) { Debug.Assert(false); return string.Empty; } 50 // // The text isn't always NULL-terminated; trim garbage 51 // if(ccReal < sb.Length) 52 // sb.Remove(ccReal, sb.Length - ccReal); 53 // string strWindow = sb.ToString(); 54 55 string strWindow; 56 IntPtr p = IntPtr.Zero; 57 try 58 { 59 int cbChar = Marshal.SystemDefaultCharSize; 60 int cb = (cc + 2) * cbChar; 61 p = Marshal.AllocCoTaskMem(cb); 62 if(p == IntPtr.Zero) { Debug.Assert(false); return string.Empty; } 63 64 byte[] pbZero = new byte[cb]; 65 Marshal.Copy(pbZero, 0, p, cb); 66 67 int ccReal = GetWindowText(hWnd, p, cc + 1); 68 if(ccReal <= 0) { Debug.Assert(false); return string.Empty; } 69 70 if(ccReal <= cc) 71 { 72 // Ensure correct termination (in case GetWindowText 73 // copied too much) 74 int ibZero = ccReal * cbChar; 75 for(int i = 0; i < cbChar; ++i) 76 Marshal.WriteByte(p, ibZero + i, 0); 77 } 78 else { Debug.Assert(false); return string.Empty; } 79 80 strWindow = (Marshal.PtrToStringAuto(p) ?? string.Empty); 81 } 82 finally { if(p != IntPtr.Zero) Marshal.FreeCoTaskMem(p); } 83 84 return (bTrim ? strWindow.Trim() : strWindow); 85 } 86 87 /* internal static string GetWindowClassName(IntPtr hWnd) 88 { 89 try 90 { 91 StringBuilder sb = new StringBuilder(260); 92 93 if(GetClassName(hWnd, sb, 258) > 0) 94 return sb.ToString(); 95 else { Debug.Assert(false); } 96 97 return string.Empty; 98 } 99 catch(Exception) { Debug.Assert(false); } 100 101 return null; 102 } */ 103 GetForegroundWindowHandle()104 internal static IntPtr GetForegroundWindowHandle() 105 { 106 if(!NativeLib.IsUnix()) 107 return GetForegroundWindow(); // Windows API 108 109 try 110 { 111 return new IntPtr(long.Parse(RunXDoTool( 112 "getactivewindow").Trim())); 113 } 114 catch(Exception) { Debug.Assert(false); } 115 return IntPtr.Zero; 116 } 117 118 private static readonly char[] g_vWindowNL = new char[] { '\r', '\n' }; GetForegroundWindowInfo(out IntPtr hWnd, out string strWindowText, bool bTrimWindow)119 internal static void GetForegroundWindowInfo(out IntPtr hWnd, 120 out string strWindowText, bool bTrimWindow) 121 { 122 hWnd = GetForegroundWindowHandle(); 123 124 if(!NativeLib.IsUnix()) // Windows 125 strWindowText = GetWindowText(hWnd, bTrimWindow); 126 else // Unix 127 { 128 strWindowText = RunXDoTool("getactivewindow getwindowname"); 129 if(!string.IsNullOrEmpty(strWindowText)) 130 { 131 if(bTrimWindow) strWindowText = strWindowText.Trim(); 132 else strWindowText = strWindowText.Trim(g_vWindowNL); 133 } 134 } 135 } 136 IsWindowEx(IntPtr hWnd)137 internal static bool IsWindowEx(IntPtr hWnd) 138 { 139 if(!NativeLib.IsUnix()) // Windows 140 return IsWindow(hWnd); 141 142 return true; 143 } 144 GetWindowStyle(IntPtr hWnd)145 internal static int GetWindowStyle(IntPtr hWnd) 146 { 147 return GetWindowLong(hWnd, GWL_STYLE); 148 } 149 GetClassLongPtrEx(IntPtr hWnd, int nIndex)150 internal static IntPtr GetClassLongPtrEx(IntPtr hWnd, int nIndex) 151 { 152 if(IntPtr.Size == 4) return GetClassLong(hWnd, nIndex); 153 return GetClassLongPtr(hWnd, nIndex); 154 } 155 SetForegroundWindowEx(IntPtr hWnd)156 internal static bool SetForegroundWindowEx(IntPtr hWnd) 157 { 158 if(!NativeLib.IsUnix()) 159 return SetForegroundWindow(hWnd); 160 161 return (RunXDoTool("windowactivate " + 162 hWnd.ToInt64().ToString()).Trim().Length == 0); 163 } 164 EnsureForegroundWindow(IntPtr hWnd)165 internal static bool EnsureForegroundWindow(IntPtr hWnd) 166 { 167 if(!IsWindowEx(hWnd)) return false; 168 169 IntPtr hWndInit = GetForegroundWindowHandle(); 170 171 if(!SetForegroundWindowEx(hWnd)) 172 { 173 Debug.Assert(false); 174 return false; 175 } 176 177 int nStartMS = Environment.TickCount; 178 while((Environment.TickCount - nStartMS) < 1000) 179 { 180 IntPtr h = GetForegroundWindowHandle(); 181 if(h == hWnd) return true; 182 183 // Some applications (like Microsoft Edge) have multiple 184 // windows and automatically redirect the focus to other 185 // windows, thus also break when a different window gets 186 // focused (except when h is zero, which can occur while 187 // the focus transfer occurs) 188 if((h != IntPtr.Zero) && (h != hWndInit)) return true; 189 190 Application.DoEvents(); 191 } 192 193 return false; 194 } 195 196 // Workaround for .NET/Windows TopMost/WS_EX_TOPMOST desynchronization bug; 197 // https://sourceforge.net/p/keepass/discussion/329220/thread/d45a3b38e8/ SyncTopMost(Form f)198 internal static void SyncTopMost(Form f) 199 { 200 if(f == null) { Debug.Assert(false); return; } 201 if(NativeLib.IsUnix()) return; 202 203 try 204 { 205 if(!f.TopMost) return; // Managed state 206 207 IntPtr h = f.Handle; 208 if(h == IntPtr.Zero) return; 209 210 int s = GetWindowLong(h, GWL_EXSTYLE); // Unmanaged state 211 if((s & WS_EX_TOPMOST) == 0) 212 { 213 f.TopMost = true; // Calls SetWindowPos (if TopLevel) 214 #if DEBUG 215 Trace.WriteLine("Synchronized TopMost/WS_EX_TOPMOST."); 216 #endif 217 } 218 } 219 catch(Exception) { Debug.Assert(false); } 220 } 221 FindWindow(string strTitle)222 internal static IntPtr FindWindow(string strTitle) 223 { 224 if(strTitle == null) { Debug.Assert(false); return IntPtr.Zero; } 225 226 if(!NativeLib.IsUnix()) 227 return FindWindowEx(IntPtr.Zero, IntPtr.Zero, null, strTitle); 228 229 // Not --onlyvisible (due to not finding minimized windows) 230 string str = RunXDoTool("search --name \"" + strTitle + "\"").Trim(); 231 if(str.Length == 0) return IntPtr.Zero; 232 233 long l; 234 if(long.TryParse(str, out l)) return new IntPtr(l); 235 return IntPtr.Zero; 236 } 237 LoseFocus(Form fCurrent, bool bSkipOwnWindows)238 internal static bool LoseFocus(Form fCurrent, bool bSkipOwnWindows) 239 { 240 if(NativeLib.IsUnix()) return LoseFocusUnix(fCurrent); 241 242 try 243 { 244 IntPtr hWnd = ((fCurrent != null) ? fCurrent.Handle : IntPtr.Zero); 245 246 while(true) 247 { 248 IntPtr hWndPrev = hWnd; 249 hWnd = GetWindow(hWnd, GW_HWNDNEXT); 250 251 if(hWnd == IntPtr.Zero) return false; 252 if(hWnd == hWndPrev) { Debug.Assert(false); return false; } 253 254 int nStyle = GetWindowStyle(hWnd); 255 if((nStyle & WS_VISIBLE) == 0) continue; 256 257 if(GetWindowTextLength(hWnd) == 0) continue; 258 259 if(bSkipOwnWindows && GlobalWindowManager.HasWindowMW(hWnd)) 260 continue; 261 262 // Skip the taskbar window (required for Windows 7, 263 // when the target window is the only other window 264 // in the taskbar) 265 if(IsTaskBar(hWnd)) continue; 266 267 break; 268 } 269 270 Debug.Assert(GetWindowText(hWnd, true) != "Start"); 271 return EnsureForegroundWindow(hWnd); 272 } 273 catch(Exception) { Debug.Assert(false); } 274 275 return false; 276 } 277 IsTaskBar(IntPtr hWnd)278 internal static bool IsTaskBar(IntPtr hWnd) 279 { 280 Process p = null; 281 try 282 { 283 string strText = GetWindowText(hWnd, true); 284 if(strText == null) return false; 285 if(!strText.Equals("Start", StrUtil.CaseIgnoreCmp)) return false; 286 287 uint uProcessId; 288 NativeMethods.GetWindowThreadProcessId(hWnd, out uProcessId); 289 290 p = Process.GetProcessById((int)uProcessId); 291 string strExe = UrlUtil.GetFileName(p.MainModule.FileName).Trim(); 292 293 return strExe.Equals("Explorer.exe", StrUtil.CaseIgnoreCmp); 294 } 295 catch(Exception) { Debug.Assert(false); } 296 finally 297 { 298 try { if(p != null) p.Dispose(); } 299 catch(Exception) { Debug.Assert(false); } 300 } 301 302 return false; 303 } 304 305 /* internal static bool IsMetroWindow(IntPtr hWnd) 306 { 307 if(hWnd == IntPtr.Zero) { Debug.Assert(false); return false; } 308 if(NativeLib.IsUnix() || !WinUtil.IsAtLeastWindows8) 309 return false; 310 311 try 312 { 313 uint uProcessId; 314 NativeMethods.GetWindowThreadProcessId(hWnd, out uProcessId); 315 if(uProcessId == 0) { Debug.Assert(false); return false; } 316 317 IntPtr h = NativeMethods.OpenProcess(NativeMethods.PROCESS_QUERY_INFORMATION, 318 false, uProcessId); 319 if(h == IntPtr.Zero) return false; // No assert 320 321 bool bRet = NativeMethods.IsImmersiveProcess(h); 322 323 NativeMethods.CloseHandle(h); 324 return bRet; 325 } 326 catch(Exception) { Debug.Assert(false); } 327 328 return false; 329 } */ 330 IsInvalidHandleValue(IntPtr p)331 public static bool IsInvalidHandleValue(IntPtr p) 332 { 333 long h = p.ToInt64(); 334 if(h == -1) return true; 335 if(h == 0xFFFFFFFF) return true; 336 337 return false; 338 } 339 GetHeaderHeight(ListView lv)340 public static int GetHeaderHeight(ListView lv) 341 { 342 if(lv == null) { Debug.Assert(false); return 0; } 343 344 try 345 { 346 if((lv.View == View.Details) && (lv.HeaderStyle != 347 ColumnHeaderStyle.None) && (lv.Columns.Count > 0) && 348 !NativeLib.IsUnix()) 349 { 350 IntPtr hHeader = NativeMethods.SendMessage(lv.Handle, 351 NativeMethods.LVM_GETHEADER, IntPtr.Zero, IntPtr.Zero); 352 if(hHeader != IntPtr.Zero) 353 { 354 NativeMethods.RECT rect = new NativeMethods.RECT(); 355 if(NativeMethods.GetWindowRect(hHeader, ref rect)) 356 return (rect.Bottom - rect.Top); 357 else { Debug.Assert(false); } 358 } 359 else { Debug.Assert(false); } 360 } 361 } 362 catch(Exception) { Debug.Assert(false); } 363 364 return 0; 365 } 366 367 // Workaround for only partially visible list view items 368 /* public static void EnsureVisible(ListView lv, int nIndex, bool bPartialOK) 369 { 370 Debug.Assert(lv != null); if(lv == null) return; 371 Debug.Assert(nIndex >= 0); if(nIndex < 0) return; 372 Debug.Assert(nIndex < lv.Items.Count); if(nIndex >= lv.Items.Count) return; 373 374 int nPartialOK = (bPartialOK ? 1 : 0); 375 try 376 { 377 NativeMethods.SendMessage(lv.Handle, LVM_ENSUREVISIBLE, 378 new IntPtr(nIndex), new IntPtr(nPartialOK)); 379 } 380 catch(Exception) { Debug.Assert(false); } 381 } */ 382 GetScrollPosY(IntPtr hWnd)383 public static int GetScrollPosY(IntPtr hWnd) 384 { 385 if(NativeLib.IsUnix()) return 0; 386 387 try 388 { 389 SCROLLINFO si = new SCROLLINFO(); 390 si.cbSize = (uint)Marshal.SizeOf(typeof(SCROLLINFO)); 391 si.fMask = (uint)ScrollInfoMask.SIF_POS; 392 393 if(GetScrollInfo(hWnd, (int)ScrollBarDirection.SB_VERT, ref si)) 394 return si.nPos; 395 396 Debug.Assert(false); 397 } 398 catch(Exception) { Debug.Assert(false); } 399 400 return 0; 401 } 402 Scroll(ListView lv, int dx, int dy)403 public static void Scroll(ListView lv, int dx, int dy) 404 { 405 if(lv == null) throw new ArgumentNullException("lv"); 406 if(NativeLib.IsUnix()) return; 407 408 try { SendMessage(lv.Handle, LVM_SCROLL, (IntPtr)dx, (IntPtr)dy); } 409 catch(Exception) { Debug.Assert(false); } 410 } 411 412 /* public static void ScrollAbsY(IntPtr hWnd, int y) 413 { 414 try 415 { 416 SCROLLINFO si = new SCROLLINFO(); 417 si.cbSize = (uint)Marshal.SizeOf(typeof(SCROLLINFO)); 418 si.fMask = (uint)ScrollInfoMask.SIF_POS; 419 si.nPos = y; 420 421 SetScrollInfo(hWnd, (int)ScrollBarDirection.SB_VERT, ref si, true); 422 } 423 catch(Exception) { Debug.Assert(false); } 424 } */ 425 426 /* public static void Scroll(IntPtr h, int dx, int dy) 427 { 428 if(h == IntPtr.Zero) { Debug.Assert(false); return; } // No throw 429 430 try 431 { 432 ScrollWindowEx(h, dx, dy, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, 433 IntPtr.Zero, SW_INVALIDATE); 434 } 435 catch(Exception) { Debug.Assert(false); } 436 } */ 437 438 /* internal static void ClearIconicBitmaps(IntPtr hWnd) 439 { 440 // TaskbarList.SetThumbnailClip(hWnd, new Rectangle(0, 0, 1, 1)); 441 // TaskbarList.SetThumbnailClip(hWnd, null); 442 443 try { DwmInvalidateIconicBitmaps(hWnd); } 444 catch(Exception) { Debug.Assert(!WinUtil.IsAtLeastWindows7); } 445 } */ 446 GetLastInputTime()447 internal static uint? GetLastInputTime() 448 { 449 try 450 { 451 LASTINPUTINFO lii = new LASTINPUTINFO(); 452 lii.cbSize = (uint)Marshal.SizeOf(typeof(LASTINPUTINFO)); 453 454 if(!GetLastInputInfo(ref lii)) { Debug.Assert(false); return null; } 455 456 return lii.dwTime; 457 } 458 catch(Exception) 459 { 460 Debug.Assert(NativeLib.IsUnix() || WinUtil.IsWindows9x); 461 } 462 463 return null; 464 } 465 SHGetFileInfo(string strPath, int dxImg, int dyImg, out Image img, out string strDisplayName)466 internal static bool SHGetFileInfo(string strPath, int dxImg, int dyImg, 467 out Image img, out string strDisplayName) 468 { 469 img = null; 470 strDisplayName = null; 471 472 try 473 { 474 SHFILEINFO fi = new SHFILEINFO(); 475 476 IntPtr p = SHGetFileInfo(strPath, 0, ref fi, (uint)Marshal.SizeOf(typeof( 477 SHFILEINFO)), SHGFI_ICON | SHGFI_SMALLICON | SHGFI_DISPLAYNAME); 478 if(p == IntPtr.Zero) return false; 479 480 if(fi.hIcon != IntPtr.Zero) 481 { 482 using(Icon ico = Icon.FromHandle(fi.hIcon)) // Doesn't take ownership 483 { 484 img = UIUtil.IconToBitmap(ico, dxImg, dyImg); 485 } 486 487 if(!DestroyIcon(fi.hIcon)) { Debug.Assert(false); } 488 } 489 490 strDisplayName = fi.szDisplayName; 491 return true; 492 } 493 catch(Exception) { Debug.Assert(false); } 494 495 return false; 496 } 497 498 /// <summary> 499 /// Method for testing whether a file exists or not. Also 500 /// supports NTFS alternate data streams. 501 /// </summary> 502 /// <param name="strFilePath">Path of the file or stream.</param> 503 /// <returns><c>true</c> if the file exists.</returns> FileExists(string strFilePath)504 public static bool FileExists(string strFilePath) 505 { 506 if(strFilePath == null) throw new ArgumentNullException("strFilePath"); 507 508 try 509 { 510 // https://sourceforge.net/p/keepass/discussion/329221/thread/65244cc9/ 511 if(!NativeLib.IsUnix()) 512 return (GetFileAttributes(strFilePath) != INVALID_FILE_ATTRIBUTES); 513 } 514 catch(Exception) { Debug.Assert(false); } 515 516 // Fallback to .NET method (for Unix-like systems) 517 try { return File.Exists(strFilePath); } 518 catch(Exception) { Debug.Assert(false); } // Invalid path 519 520 return false; 521 } 522 523 /* internal static LVGROUP GetGroupInfoByIndex(ListView lv, uint uIndex) 524 { 525 if(lv == null) throw new ArgumentNullException("lv"); 526 if(uIndex >= (uint)lv.Groups.Count) 527 throw new ArgumentOutOfRangeException("uIndex"); 528 529 const int nStrLen = 1024; 530 531 LVGROUP g = new LVGROUP(); 532 g.cbSize = (uint)Marshal.SizeOf(typeof(LVGROUP)); 533 534 g.mask = ...; 535 536 g.pszHeader = new StringBuilder(nStrLen); 537 g.cchHeader = nStrLen - 1; 538 g.pszFooter = new StringBuilder(nStrLen); 539 g.cchFooter = nStrLen - 1; 540 g.pszSubtitle = new StringBuilder(nStrLen); 541 g.cchSubtitle = (uint)(nStrLen - 1); 542 g.pszTask = new StringBuilder(nStrLen); 543 g.cchTask = (uint)(nStrLen - 1); 544 g.pszDescriptionTop = new StringBuilder(nStrLen); 545 g.cchDescriptionTop = (uint)(nStrLen - 1); 546 g.pszDescriptionBottom = new StringBuilder(nStrLen); 547 g.cchDescriptionBottom = (uint)(nStrLen - 1); 548 g.pszSubsetTitle = new StringBuilder(nStrLen); 549 g.cchSubsetTitle = (uint)(nStrLen - 1); 550 551 SendMessageLVGroup(lv.Handle, LVM_GETGROUPINFOBYINDEX, 552 new IntPtr((int)uIndex), ref g); 553 return g; 554 } */ 555 556 /* internal static uint GetGroupStateByIndex(ListView lv, uint uIndex, 557 uint uStateMask, out int iGroupID) 558 { 559 if(lv == null) throw new ArgumentNullException("lv"); 560 if(uIndex >= (uint)lv.Groups.Count) 561 throw new ArgumentOutOfRangeException("uIndex"); 562 563 LVGROUP g = new LVGROUP(); 564 g.cbSize = (uint)Marshal.SizeOf(typeof(LVGROUP)); 565 566 g.mask = (LVGF_STATE | LVGF_GROUPID); 567 g.stateMask = uStateMask; 568 569 SendMessageLVGroup(lv.Handle, LVM_GETGROUPINFOBYINDEX, 570 new IntPtr((int)uIndex), ref g); 571 572 iGroupID = g.iGroupId; 573 return g.state; 574 } 575 576 internal static void SetGroupState(ListView lv, int iGroupID, 577 uint uStateMask, uint uState) 578 { 579 if(lv == null) throw new ArgumentNullException("lv"); 580 581 LVGROUP g = new LVGROUP(); 582 g.cbSize = (uint)Marshal.SizeOf(typeof(LVGROUP)); 583 584 g.mask = LVGF_STATE; 585 g.stateMask = uStateMask; 586 g.state = uState; 587 588 SendMessageLVGroup(lv.Handle, LVM_SETGROUPINFO, 589 new IntPtr(iGroupID), ref g); 590 } */ 591 592 /* internal static int GetGroupIDByIndex(ListView lv, uint uIndex) 593 { 594 if(lv == null) { Debug.Assert(false); return 0; } 595 596 LVGROUP g = new LVGROUP(); 597 g.cbSize = (uint)Marshal.SizeOf(typeof(LVGROUP)); 598 g.AssertSize(); 599 600 g.mask = NativeMethods.LVGF_GROUPID; 601 602 if(SendMessageLVGroup(lv.Handle, LVM_GETGROUPINFOBYINDEX, 603 new IntPtr((int)uIndex), ref g) == IntPtr.Zero) 604 { 605 Debug.Assert(false); 606 } 607 608 return g.iGroupId; 609 } 610 611 internal static void SetGroupTask(ListView lv, int iGroupID, 612 string strTask) 613 { 614 if(lv == null) { Debug.Assert(false); return; } 615 616 LVGROUP g = new LVGROUP(); 617 g.cbSize = (uint)Marshal.SizeOf(typeof(LVGROUP)); 618 g.AssertSize(); 619 620 g.mask = LVGF_TASK; 621 622 g.pszTask = strTask; 623 g.cchTask = (uint)((strTask != null) ? strTask.Length : 0); 624 625 if(SendMessageLVGroup(lv.Handle, LVM_SETGROUPINFO, 626 new IntPtr(iGroupID), ref g) == (new IntPtr(-1))) 627 { 628 Debug.Assert(false); 629 } 630 } */ 631 632 /* internal static void SetListViewGroupInfo(ListView lv, int iGroupID, 633 string strTask, bool? obCollapsible) 634 { 635 if(lv == null) { Debug.Assert(false); return; } 636 if(!WinUtil.IsAtLeastWindowsVista) return; 637 638 LVGROUP g = new LVGROUP(); 639 g.cbSize = (uint)Marshal.SizeOf(typeof(LVGROUP)); 640 g.AssertSize(); 641 642 if(strTask != null) 643 { 644 g.mask |= LVGF_TASK; 645 646 g.pszTask = strTask; 647 g.cchTask = (uint)strTask.Length; 648 } 649 650 if(obCollapsible.HasValue) 651 { 652 g.mask |= LVGF_STATE; 653 654 g.stateMask = LVGS_COLLAPSIBLE; 655 g.state = (obCollapsible.Value ? LVGS_COLLAPSIBLE : 0); 656 } 657 658 if(g.mask == 0) return; 659 if(SendMessageLVGroup(lv.Handle, LVM_SETGROUPINFO, 660 new IntPtr(iGroupID), ref g) == (new IntPtr(-1))) 661 { 662 Debug.Assert(false); 663 } 664 } 665 666 internal static int GetListViewGroupID(ListViewGroup lvg) 667 { 668 if(lvg == null) { Debug.Assert(false); return -1; } 669 670 Type t = typeof(ListViewGroup); 671 PropertyInfo pi = t.GetProperty("ID", (BindingFlags.Instance | 672 BindingFlags.NonPublic)); 673 if(pi == null) { Debug.Assert(false); return -1; } 674 if(pi.PropertyType != typeof(int)) { Debug.Assert(false); return -1; } 675 676 return (int)pi.GetValue(lvg, null); 677 } */ 678 GetDesktopName(IntPtr hDesk, out string strAnsi, out string strUni)679 private static bool GetDesktopName(IntPtr hDesk, out string strAnsi, 680 out string strUni) 681 { 682 strAnsi = null; 683 strUni = null; 684 685 const uint cbZ = 12; // Minimal number of terminating zeros 686 const uint uBufSize = 64 + cbZ; 687 IntPtr pBuf = Marshal.AllocCoTaskMem((int)uBufSize); 688 byte[] pbZero = new byte[uBufSize]; 689 Marshal.Copy(pbZero, 0, pBuf, pbZero.Length); 690 691 try 692 { 693 uint uReqSize = uBufSize - cbZ; 694 bool bSuccess = GetUserObjectInformation(hDesk, 2, pBuf, 695 uBufSize - cbZ, ref uReqSize); 696 if(uReqSize > (uBufSize - cbZ)) 697 { 698 Marshal.FreeCoTaskMem(pBuf); 699 pBuf = Marshal.AllocCoTaskMem((int)(uReqSize + cbZ)); 700 pbZero = new byte[uReqSize + cbZ]; 701 Marshal.Copy(pbZero, 0, pBuf, pbZero.Length); 702 703 bSuccess = GetUserObjectInformation(hDesk, 2, pBuf, 704 uReqSize, ref uReqSize); 705 Debug.Assert((uReqSize + cbZ) == (uint)pbZero.Length); 706 } 707 708 if(bSuccess) 709 { 710 try { strAnsi = Marshal.PtrToStringAnsi(pBuf).Trim(); } 711 catch(Exception) { } 712 713 try { strUni = Marshal.PtrToStringUni(pBuf).Trim(); } 714 catch(Exception) { } 715 716 return true; 717 } 718 } 719 finally { Marshal.FreeCoTaskMem(pBuf); } 720 721 Debug.Assert(false); 722 return false; 723 } 724 725 // The GetUserObjectInformation function apparently returns the 726 // desktop name using ANSI encoding even on Windows 7 systems. 727 // As the encoding is not documented, we test both ANSI and 728 // Unicode versions of the name. DesktopNameContains(IntPtr hDesk, string strName)729 internal static bool? DesktopNameContains(IntPtr hDesk, string strName) 730 { 731 if(string.IsNullOrEmpty(strName)) { Debug.Assert(false); return false; } 732 733 string strAnsi, strUni; 734 if(!GetDesktopName(hDesk, out strAnsi, out strUni)) return null; 735 if((strAnsi == null) && (strUni == null)) return null; 736 737 try 738 { 739 if((strAnsi != null) && (strAnsi.IndexOf(strName, 740 StringComparison.OrdinalIgnoreCase) >= 0)) 741 return true; 742 } 743 catch(Exception) { Debug.Assert(false); } 744 745 try 746 { 747 if((strUni != null) && (strUni.IndexOf(strName, 748 StringComparison.OrdinalIgnoreCase) >= 0)) 749 return true; 750 } 751 catch(Exception) { Debug.Assert(false); } 752 753 return false; 754 } 755 IsKeyDownMessage(ref Message m)756 private static bool? IsKeyDownMessage(ref Message m) 757 { 758 if(m.Msg == NativeMethods.WM_KEYDOWN) return true; 759 if(m.Msg == NativeMethods.WM_KEYUP) return false; 760 if(m.Msg == NativeMethods.WM_SYSKEYDOWN) return true; 761 if(m.Msg == NativeMethods.WM_SYSKEYUP) return false; 762 return null; 763 } 764 GetKeyMessageState(ref Message m, out bool bDown)765 internal static bool GetKeyMessageState(ref Message m, out bool bDown) 766 { 767 bool? obKeyDown = IsKeyDownMessage(ref m); 768 if(!obKeyDown.HasValue) 769 { 770 Debug.Assert(false); 771 bDown = false; 772 return false; 773 } 774 775 bDown = obKeyDown.Value; 776 return true; 777 } 778 779 /* internal static string GetKeyboardLayoutNameEx() 780 { 781 StringBuilder sb = new StringBuilder(KL_NAMELENGTH + 1); 782 if(GetKeyboardLayoutName(sb)) 783 { 784 Debug.Assert(sb.Length == (KL_NAMELENGTH - 1)); 785 return sb.ToString(); 786 } 787 else { Debug.Assert(false); } 788 789 return null; 790 } */ 791 792 /// <summary> 793 /// PRIMARYLANGID macro. 794 /// </summary> GetPrimaryLangID(ushort uLangID)795 internal static ushort GetPrimaryLangID(ushort uLangID) 796 { 797 return (ushort)(uLangID & 0x3FFU); 798 } 799 MapVirtualKey3(uint uCode, uint uMapType, IntPtr hKL)800 internal static uint MapVirtualKey3(uint uCode, uint uMapType, IntPtr hKL) 801 { 802 if(hKL == IntPtr.Zero) return MapVirtualKey(uCode, uMapType); 803 return MapVirtualKeyEx(uCode, uMapType, hKL); 804 } 805 VkKeyScan3(char ch, IntPtr hKL)806 internal static ushort VkKeyScan3(char ch, IntPtr hKL) 807 { 808 if(hKL == IntPtr.Zero) return VkKeyScan(ch); 809 return VkKeyScanEx(ch, hKL); 810 } 811 812 /// <returns> 813 /// Null, if there exists no translation or an error occured. 814 /// An empty string, if the key is a dead key. 815 /// Otherwise, the generated Unicode string (typically 1 character, 816 /// but can be more when a dead key is stored in the keyboard layout). 817 /// </returns> ToUnicode3(int vKey, byte[] pbKeyState, IntPtr hKL)818 internal static string ToUnicode3(int vKey, byte[] pbKeyState, IntPtr hKL) 819 { 820 const int cbState = 256; 821 IntPtr pState = IntPtr.Zero; 822 try 823 { 824 uint uScanCode = MapVirtualKey3((uint)vKey, MAPVK_VK_TO_VSC, hKL); 825 826 pState = Marshal.AllocHGlobal(cbState); 827 if(pState == IntPtr.Zero) { Debug.Assert(false); return null; } 828 829 if(pbKeyState != null) 830 { 831 if(pbKeyState.Length == cbState) 832 Marshal.Copy(pbKeyState, 0, pState, cbState); 833 else { Debug.Assert(false); return null; } 834 } 835 else 836 { 837 // Windows' GetKeyboardState function does not return 838 // the current virtual key array; as a workaround, 839 // calling GetKeyState is mentioned sometimes, but 840 // this doesn't work reliably either; 841 // http://pinvoke.net/default.aspx/user32/GetKeyboardState.html 842 843 // GetKeyState(VK_SHIFT); 844 // if(!GetKeyboardState(pState)) { Debug.Assert(false); return null; } 845 846 Debug.Assert(false); 847 return null; 848 } 849 850 const int cchUni = 30; 851 StringBuilder sbUni = new StringBuilder(cchUni + 2); 852 853 int r; 854 if(hKL == IntPtr.Zero) 855 r = ToUnicode((uint)vKey, uScanCode, pState, sbUni, 856 cchUni, 0); 857 else 858 r = ToUnicodeEx((uint)vKey, uScanCode, pState, sbUni, 859 cchUni, 0, hKL); 860 861 if(r < 0) return string.Empty; // Dead key 862 if(r == 0) return null; // No translation 863 864 string str = sbUni.ToString(); 865 if(string.IsNullOrEmpty(str)) { Debug.Assert(false); return null; } 866 867 // Extra characters may be returned, but are invalid 868 // and should be ignored; 869 // https://msdn.microsoft.com/en-us/library/windows/desktop/ms646320.aspx 870 if(r < str.Length) str = str.Substring(0, r); 871 872 return str; 873 } 874 catch(Exception) { Debug.Assert(false); } 875 finally { if(pState != IntPtr.Zero) Marshal.FreeHGlobal(pState); } 876 877 return null; 878 } 879 } 880 } 881