WindowImpl.cs 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699
  1. // Copyright (c) The Avalonia Project. All rights reserved.
  2. // Licensed under the MIT license. See licence.md file in the project root for full license information.
  3. using Avalonia.Input;
  4. using System;
  5. using System.Collections.Generic;
  6. using System.ComponentModel;
  7. using System.Diagnostics.CodeAnalysis;
  8. using System.Linq;
  9. using System.Reactive.Disposables;
  10. using System.Reactive.Linq;
  11. using System.Runtime.InteropServices;
  12. using Avalonia.Controls;
  13. using Avalonia.Input.Raw;
  14. using Avalonia.Platform;
  15. using Avalonia.Win32.Input;
  16. using Avalonia.Win32.Interop;
  17. namespace Avalonia.Win32
  18. {
  19. public class WindowImpl : IWindowImpl
  20. {
  21. private static readonly List<WindowImpl> s_instances = new List<WindowImpl>();
  22. private static readonly IntPtr DefaultCursor = UnmanagedMethods.LoadCursor(
  23. IntPtr.Zero, new IntPtr((int)UnmanagedMethods.Cursor.IDC_ARROW));
  24. private UnmanagedMethods.WndProc _wndProcDelegate;
  25. private string _className;
  26. private IntPtr _hwnd;
  27. private IInputRoot _owner;
  28. private bool _trackingMouse;
  29. private bool _isActive;
  30. private bool _decorated = true;
  31. private double _scaling = 1;
  32. private WindowState _showWindowState;
  33. public WindowImpl()
  34. {
  35. CreateWindow();
  36. s_instances.Add(this);
  37. }
  38. public Action Activated { get; set; }
  39. public Action Closed { get; set; }
  40. public Action Deactivated { get; set; }
  41. public Action<RawInputEventArgs> Input { get; set; }
  42. public Action<Rect> Paint { get; set; }
  43. public Action<Size> Resized { get; set; }
  44. public Action<double> ScalingChanged { get; set; }
  45. public Thickness BorderThickness
  46. {
  47. get
  48. {
  49. var style = UnmanagedMethods.GetWindowLong(_hwnd, -16);
  50. var exStyle = UnmanagedMethods.GetWindowLong(_hwnd, -20);
  51. var padding = new UnmanagedMethods.RECT();
  52. if (UnmanagedMethods.AdjustWindowRectEx(ref padding, style, false, exStyle))
  53. {
  54. return new Thickness(-padding.left, -padding.top, padding.right, padding.bottom);
  55. }
  56. else
  57. {
  58. throw new Win32Exception();
  59. }
  60. }
  61. }
  62. public Size ClientSize
  63. {
  64. get
  65. {
  66. UnmanagedMethods.RECT rect;
  67. UnmanagedMethods.GetClientRect(_hwnd, out rect);
  68. return new Size(rect.right, rect.bottom) / Scaling;
  69. }
  70. set
  71. {
  72. if (value != ClientSize)
  73. {
  74. value *= Scaling;
  75. value += BorderThickness;
  76. UnmanagedMethods.SetWindowPos(
  77. _hwnd,
  78. IntPtr.Zero,
  79. 0,
  80. 0,
  81. (int)value.Width,
  82. (int)value.Height,
  83. UnmanagedMethods.SetWindowPosFlags.SWP_RESIZE);
  84. }
  85. }
  86. }
  87. public double Scaling => _scaling;
  88. public IPlatformHandle Handle
  89. {
  90. get;
  91. private set;
  92. }
  93. public bool IsEnabled
  94. {
  95. get { return UnmanagedMethods.IsWindowEnabled(_hwnd); }
  96. set { UnmanagedMethods.EnableWindow(_hwnd, value); }
  97. }
  98. public Size MaxClientSize
  99. {
  100. get
  101. {
  102. return (new Size(
  103. UnmanagedMethods.GetSystemMetrics(UnmanagedMethods.SystemMetric.SM_CXMAXTRACK),
  104. UnmanagedMethods.GetSystemMetrics(UnmanagedMethods.SystemMetric.SM_CYMAXTRACK))
  105. - BorderThickness) / Scaling;
  106. }
  107. }
  108. public WindowState WindowState
  109. {
  110. get
  111. {
  112. var placement = default(UnmanagedMethods.WINDOWPLACEMENT);
  113. UnmanagedMethods.GetWindowPlacement(_hwnd, ref placement);
  114. switch (placement.ShowCmd)
  115. {
  116. case UnmanagedMethods.ShowWindowCommand.Maximize:
  117. return WindowState.Maximized;
  118. case UnmanagedMethods.ShowWindowCommand.Minimize:
  119. return WindowState.Minimized;
  120. default:
  121. return WindowState.Normal;
  122. }
  123. }
  124. set
  125. {
  126. if (UnmanagedMethods.IsWindowVisible(_hwnd))
  127. {
  128. ShowWindow(value);
  129. }
  130. else
  131. {
  132. _showWindowState = value;
  133. }
  134. }
  135. }
  136. public void Activate()
  137. {
  138. UnmanagedMethods.SetActiveWindow(_hwnd);
  139. }
  140. public IPopupImpl CreatePopup()
  141. {
  142. return new PopupImpl();
  143. }
  144. public void Dispose()
  145. {
  146. s_instances.Remove(this);
  147. UnmanagedMethods.DestroyWindow(_hwnd);
  148. }
  149. public void Hide()
  150. {
  151. UnmanagedMethods.ShowWindow(_hwnd, UnmanagedMethods.ShowWindowCommand.Hide);
  152. }
  153. public void SetSystemDecorations(bool value)
  154. {
  155. if (value == _decorated)
  156. return;
  157. var style = (UnmanagedMethods.WindowStyles) UnmanagedMethods.GetWindowLong(_hwnd, -16);
  158. style |= UnmanagedMethods.WindowStyles.WS_OVERLAPPEDWINDOW;
  159. if (!value)
  160. style ^= UnmanagedMethods.WindowStyles.WS_OVERLAPPEDWINDOW;
  161. UnmanagedMethods.RECT windowRect;
  162. UnmanagedMethods.GetWindowRect(_hwnd, out windowRect);
  163. Rect newRect;
  164. var oldThickness = BorderThickness;
  165. UnmanagedMethods.SetWindowLong(_hwnd, -16, (uint) style);
  166. if (value)
  167. {
  168. var thickness = BorderThickness;
  169. newRect = new Rect(
  170. windowRect.left - thickness.Left,
  171. windowRect.top - thickness.Top,
  172. (windowRect.right - windowRect.left) + (thickness.Left + thickness.Right),
  173. (windowRect.bottom - windowRect.top) + (thickness.Top + thickness.Bottom));
  174. }
  175. else
  176. newRect = new Rect(
  177. windowRect.left + oldThickness.Left,
  178. windowRect.top + oldThickness.Top,
  179. (windowRect.right - windowRect.left) - (oldThickness.Left + oldThickness.Right),
  180. (windowRect.bottom - windowRect.top) - (oldThickness.Top + oldThickness.Bottom));
  181. UnmanagedMethods.SetWindowPos(_hwnd, IntPtr.Zero, (int) newRect.X, (int) newRect.Y, (int) newRect.Width,
  182. (int) newRect.Height,
  183. UnmanagedMethods.SetWindowPosFlags.SWP_NOZORDER | UnmanagedMethods.SetWindowPosFlags.SWP_NOACTIVATE);
  184. _decorated = value;
  185. }
  186. public void Invalidate(Rect rect)
  187. {
  188. var f = Scaling;
  189. var r = new UnmanagedMethods.RECT
  190. {
  191. left = (int)(rect.X * f),
  192. top = (int)(rect.Y * f),
  193. right = (int)(rect.Right * f),
  194. bottom = (int)(rect.Bottom * f),
  195. };
  196. UnmanagedMethods.InvalidateRect(_hwnd, ref r, false);
  197. }
  198. public Point PointToClient(Point point)
  199. {
  200. var p = new UnmanagedMethods.POINT { X = (int)point.X, Y = (int)point.Y };
  201. UnmanagedMethods.ScreenToClient(_hwnd, ref p);
  202. return new Point(p.X, p.Y) / Scaling;
  203. }
  204. public Point PointToScreen(Point point)
  205. {
  206. point *= Scaling;
  207. var p = new UnmanagedMethods.POINT { X = (int)point.X, Y = (int)point.Y };
  208. UnmanagedMethods.ClientToScreen(_hwnd, ref p);
  209. return new Point(p.X, p.Y);
  210. }
  211. public void SetInputRoot(IInputRoot inputRoot)
  212. {
  213. _owner = inputRoot;
  214. }
  215. public void SetTitle(string title)
  216. {
  217. UnmanagedMethods.SetWindowText(_hwnd, title);
  218. }
  219. public virtual void Show()
  220. {
  221. ShowWindow(_showWindowState);
  222. }
  223. public void BeginMoveDrag()
  224. {
  225. UnmanagedMethods.DefWindowProc(_hwnd, (int) UnmanagedMethods.WindowsMessage.WM_NCLBUTTONDOWN,
  226. new IntPtr((int)UnmanagedMethods.HitTestValues.HTCAPTION), IntPtr.Zero);
  227. }
  228. static readonly Dictionary<WindowEdge, UnmanagedMethods.HitTestValues> EdgeDic = new Dictionary<WindowEdge, UnmanagedMethods.HitTestValues>
  229. {
  230. {WindowEdge.East, UnmanagedMethods.HitTestValues.HTRIGHT},
  231. {WindowEdge.North, UnmanagedMethods.HitTestValues.HTTOP },
  232. {WindowEdge.NorthEast, UnmanagedMethods.HitTestValues.HTTOPRIGHT },
  233. {WindowEdge.NorthWest, UnmanagedMethods.HitTestValues.HTTOPLEFT },
  234. {WindowEdge.South, UnmanagedMethods.HitTestValues.HTBOTTOM },
  235. {WindowEdge.SouthEast, UnmanagedMethods.HitTestValues.HTBOTTOMRIGHT },
  236. {WindowEdge.SouthWest, UnmanagedMethods.HitTestValues.HTBOTTOMLEFT },
  237. {WindowEdge.West, UnmanagedMethods.HitTestValues.HTLEFT}
  238. };
  239. public void BeginResizeDrag(WindowEdge edge)
  240. {
  241. UnmanagedMethods.DefWindowProc(_hwnd, (int) UnmanagedMethods.WindowsMessage.WM_NCLBUTTONDOWN,
  242. new IntPtr((int) EdgeDic[edge]), IntPtr.Zero);
  243. }
  244. public Point Position
  245. {
  246. get
  247. {
  248. UnmanagedMethods.RECT rc;
  249. UnmanagedMethods.GetWindowRect(_hwnd, out rc);
  250. return new Point(rc.left, rc.top);
  251. }
  252. set
  253. {
  254. UnmanagedMethods.SetWindowPos(
  255. Handle.Handle,
  256. IntPtr.Zero,
  257. (int) value.X,
  258. (int) value.Y,
  259. 0,
  260. 0,
  261. UnmanagedMethods.SetWindowPosFlags.SWP_NOSIZE | UnmanagedMethods.SetWindowPosFlags.SWP_NOACTIVATE);
  262. }
  263. }
  264. public virtual IDisposable ShowDialog()
  265. {
  266. var disabled = s_instances.Where(x => x != this && x.IsEnabled).ToList();
  267. WindowImpl activated = null;
  268. foreach (var window in disabled)
  269. {
  270. if (window._isActive)
  271. {
  272. activated = window;
  273. }
  274. window.IsEnabled = false;
  275. }
  276. Show();
  277. return Disposable.Create(() =>
  278. {
  279. foreach (var window in disabled)
  280. {
  281. window.IsEnabled = true;
  282. }
  283. activated?.Activate();
  284. });
  285. }
  286. public void SetCursor(IPlatformHandle cursor)
  287. {
  288. UnmanagedMethods.SetClassLong(_hwnd, UnmanagedMethods.ClassLongIndex.GCL_HCURSOR,
  289. cursor?.Handle ?? DefaultCursor);
  290. }
  291. protected virtual IntPtr CreateWindowOverride(ushort atom)
  292. {
  293. return UnmanagedMethods.CreateWindowEx(
  294. 0,
  295. atom,
  296. null,
  297. (int)UnmanagedMethods.WindowStyles.WS_OVERLAPPEDWINDOW,
  298. UnmanagedMethods.CW_USEDEFAULT,
  299. UnmanagedMethods.CW_USEDEFAULT,
  300. UnmanagedMethods.CW_USEDEFAULT,
  301. UnmanagedMethods.CW_USEDEFAULT,
  302. IntPtr.Zero,
  303. IntPtr.Zero,
  304. IntPtr.Zero,
  305. IntPtr.Zero);
  306. }
  307. [SuppressMessage("Microsoft.StyleCop.CSharp.NamingRules", "SA1305:FieldNamesMustNotUseHungarianNotation", Justification = "Using Win32 naming for consistency.")]
  308. protected virtual IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
  309. {
  310. bool unicode = UnmanagedMethods.IsWindowUnicode(hWnd);
  311. const double wheelDelta = 120.0;
  312. uint timestamp = unchecked((uint)UnmanagedMethods.GetMessageTime());
  313. RawInputEventArgs e = null;
  314. WindowsMouseDevice.Instance.CurrentWindow = this;
  315. switch ((UnmanagedMethods.WindowsMessage)msg)
  316. {
  317. case UnmanagedMethods.WindowsMessage.WM_ACTIVATE:
  318. var wa = (UnmanagedMethods.WindowActivate)(ToInt32(wParam) & 0xffff);
  319. switch (wa)
  320. {
  321. case UnmanagedMethods.WindowActivate.WA_ACTIVE:
  322. case UnmanagedMethods.WindowActivate.WA_CLICKACTIVE:
  323. _isActive = true;
  324. Activated?.Invoke();
  325. break;
  326. case UnmanagedMethods.WindowActivate.WA_INACTIVE:
  327. _isActive = false;
  328. Deactivated?.Invoke();
  329. break;
  330. }
  331. return IntPtr.Zero;
  332. case UnmanagedMethods.WindowsMessage.WM_DESTROY:
  333. if (Closed != null)
  334. {
  335. UnmanagedMethods.UnregisterClass(_className, Marshal.GetHINSTANCE(GetType().Module));
  336. Closed();
  337. }
  338. return IntPtr.Zero;
  339. case UnmanagedMethods.WindowsMessage.WM_DPICHANGED:
  340. var dpi = ToInt32(wParam) & 0xffff;
  341. var newDisplayRect = (UnmanagedMethods.RECT)Marshal.PtrToStructure(lParam, typeof(UnmanagedMethods.RECT));
  342. Position = new Point(newDisplayRect.left, newDisplayRect.top);
  343. _scaling = dpi / 96.0;
  344. ScalingChanged?.Invoke(_scaling);
  345. return IntPtr.Zero;
  346. case UnmanagedMethods.WindowsMessage.WM_KEYDOWN:
  347. case UnmanagedMethods.WindowsMessage.WM_SYSKEYDOWN:
  348. e = new RawKeyEventArgs(
  349. WindowsKeyboardDevice.Instance,
  350. timestamp,
  351. RawKeyEventType.KeyDown,
  352. KeyInterop.KeyFromVirtualKey(ToInt32(wParam)), WindowsKeyboardDevice.Instance.Modifiers);
  353. break;
  354. case UnmanagedMethods.WindowsMessage.WM_KEYUP:
  355. case UnmanagedMethods.WindowsMessage.WM_SYSKEYUP:
  356. e = new RawKeyEventArgs(
  357. WindowsKeyboardDevice.Instance,
  358. timestamp,
  359. RawKeyEventType.KeyUp,
  360. KeyInterop.KeyFromVirtualKey(ToInt32(wParam)), WindowsKeyboardDevice.Instance.Modifiers);
  361. break;
  362. case UnmanagedMethods.WindowsMessage.WM_CHAR:
  363. // Ignore control chars
  364. if (ToInt32(wParam) >= 32)
  365. {
  366. e = new RawTextInputEventArgs(WindowsKeyboardDevice.Instance, timestamp,
  367. new string((char)ToInt32(wParam), 1));
  368. }
  369. break;
  370. case UnmanagedMethods.WindowsMessage.WM_LBUTTONDOWN:
  371. case UnmanagedMethods.WindowsMessage.WM_RBUTTONDOWN:
  372. case UnmanagedMethods.WindowsMessage.WM_MBUTTONDOWN:
  373. e = new RawMouseEventArgs(
  374. WindowsMouseDevice.Instance,
  375. timestamp,
  376. _owner,
  377. msg == (int)UnmanagedMethods.WindowsMessage.WM_LBUTTONDOWN
  378. ? RawMouseEventType.LeftButtonDown
  379. : msg == (int)UnmanagedMethods.WindowsMessage.WM_RBUTTONDOWN
  380. ? RawMouseEventType.RightButtonDown
  381. : RawMouseEventType.MiddleButtonDown,
  382. DipFromLParam(lParam), GetMouseModifiers(wParam));
  383. break;
  384. case UnmanagedMethods.WindowsMessage.WM_LBUTTONUP:
  385. case UnmanagedMethods.WindowsMessage.WM_RBUTTONUP:
  386. case UnmanagedMethods.WindowsMessage.WM_MBUTTONUP:
  387. e = new RawMouseEventArgs(
  388. WindowsMouseDevice.Instance,
  389. timestamp,
  390. _owner,
  391. msg == (int) UnmanagedMethods.WindowsMessage.WM_LBUTTONUP
  392. ? RawMouseEventType.LeftButtonUp
  393. : msg == (int) UnmanagedMethods.WindowsMessage.WM_RBUTTONUP
  394. ? RawMouseEventType.RightButtonUp
  395. : RawMouseEventType.MiddleButtonUp,
  396. DipFromLParam(lParam), GetMouseModifiers(wParam));
  397. break;
  398. case UnmanagedMethods.WindowsMessage.WM_MOUSEMOVE:
  399. if (!_trackingMouse)
  400. {
  401. var tm = new UnmanagedMethods.TRACKMOUSEEVENT
  402. {
  403. cbSize = Marshal.SizeOf(typeof(UnmanagedMethods.TRACKMOUSEEVENT)),
  404. dwFlags = 2,
  405. hwndTrack = _hwnd,
  406. dwHoverTime = 0,
  407. };
  408. UnmanagedMethods.TrackMouseEvent(ref tm);
  409. }
  410. e = new RawMouseEventArgs(
  411. WindowsMouseDevice.Instance,
  412. timestamp,
  413. _owner,
  414. RawMouseEventType.Move,
  415. DipFromLParam(lParam), GetMouseModifiers(wParam));
  416. break;
  417. case UnmanagedMethods.WindowsMessage.WM_MOUSEWHEEL:
  418. e = new RawMouseWheelEventArgs(
  419. WindowsMouseDevice.Instance,
  420. timestamp,
  421. _owner,
  422. ScreenToClient(DipFromLParam(lParam)),
  423. new Vector(0, (ToInt32(wParam) >> 16) / wheelDelta), GetMouseModifiers(wParam));
  424. break;
  425. case UnmanagedMethods.WindowsMessage.WM_MOUSEHWHEEL:
  426. e = new RawMouseWheelEventArgs(
  427. WindowsMouseDevice.Instance,
  428. timestamp,
  429. _owner,
  430. ScreenToClient(DipFromLParam(lParam)),
  431. new Vector(-(ToInt32(wParam) >> 16) / wheelDelta,0), GetMouseModifiers(wParam));
  432. break;
  433. case UnmanagedMethods.WindowsMessage.WM_MOUSELEAVE:
  434. _trackingMouse = false;
  435. e = new RawMouseEventArgs(
  436. WindowsMouseDevice.Instance,
  437. timestamp,
  438. _owner,
  439. RawMouseEventType.LeaveWindow,
  440. new Point(), WindowsKeyboardDevice.Instance.Modifiers);
  441. break;
  442. case UnmanagedMethods.WindowsMessage.WM_NCLBUTTONDOWN:
  443. case UnmanagedMethods.WindowsMessage.WM_NCRBUTTONDOWN:
  444. case UnmanagedMethods.WindowsMessage.WM_NCMBUTTONDOWN:
  445. e = new RawMouseEventArgs(
  446. WindowsMouseDevice.Instance,
  447. timestamp,
  448. _owner,
  449. msg == (int)UnmanagedMethods.WindowsMessage.WM_NCLBUTTONDOWN
  450. ? RawMouseEventType.NonClientLeftButtonDown
  451. : msg == (int)UnmanagedMethods.WindowsMessage.WM_NCRBUTTONDOWN
  452. ? RawMouseEventType.RightButtonDown
  453. : RawMouseEventType.MiddleButtonDown,
  454. new Point(0, 0), GetMouseModifiers(wParam));
  455. break;
  456. case UnmanagedMethods.WindowsMessage.WM_PAINT:
  457. if (Paint != null)
  458. {
  459. UnmanagedMethods.PAINTSTRUCT ps;
  460. if (UnmanagedMethods.BeginPaint(_hwnd, out ps) != IntPtr.Zero)
  461. {
  462. UnmanagedMethods.RECT r;
  463. UnmanagedMethods.GetUpdateRect(_hwnd, out r, false);
  464. var f = Scaling;
  465. Paint(new Rect(r.left / f, r.top / f, (r.right - r.left) / f, (r.bottom - r.top) / f));
  466. UnmanagedMethods.EndPaint(_hwnd, ref ps);
  467. }
  468. }
  469. return IntPtr.Zero;
  470. case UnmanagedMethods.WindowsMessage.WM_SIZE:
  471. if (Resized != null &&
  472. (wParam == (IntPtr)UnmanagedMethods.SizeCommand.Restored ||
  473. wParam == (IntPtr)UnmanagedMethods.SizeCommand.Maximized))
  474. {
  475. var clientSize = new Size(ToInt32(lParam) & 0xffff, ToInt32(lParam) >> 16);
  476. Resized(clientSize / Scaling);
  477. }
  478. return IntPtr.Zero;
  479. }
  480. if (e != null && Input != null)
  481. {
  482. Input(e);
  483. if (e.Handled)
  484. {
  485. return IntPtr.Zero;
  486. }
  487. }
  488. return UnmanagedMethods.DefWindowProc(hWnd, msg, wParam, lParam);
  489. }
  490. static InputModifiers GetMouseModifiers(IntPtr wParam)
  491. {
  492. var keys = (UnmanagedMethods.ModifierKeys)ToInt32(wParam);
  493. var modifiers = WindowsKeyboardDevice.Instance.Modifiers;
  494. if (keys.HasFlag(UnmanagedMethods.ModifierKeys.MK_LBUTTON))
  495. modifiers |= InputModifiers.LeftMouseButton;
  496. if(keys.HasFlag(UnmanagedMethods.ModifierKeys.MK_RBUTTON))
  497. modifiers |= InputModifiers.RightMouseButton;
  498. if(keys.HasFlag(UnmanagedMethods.ModifierKeys.MK_MBUTTON))
  499. modifiers |= InputModifiers.MiddleMouseButton;
  500. return modifiers;
  501. }
  502. private void CreateWindow()
  503. {
  504. // Ensure that the delegate doesn't get garbage collected by storing it as a field.
  505. _wndProcDelegate = new UnmanagedMethods.WndProc(WndProc);
  506. _className = Guid.NewGuid().ToString();
  507. UnmanagedMethods.WNDCLASSEX wndClassEx = new UnmanagedMethods.WNDCLASSEX
  508. {
  509. cbSize = Marshal.SizeOf(typeof(UnmanagedMethods.WNDCLASSEX)),
  510. style = 0,
  511. lpfnWndProc = _wndProcDelegate,
  512. hInstance = Marshal.GetHINSTANCE(GetType().Module),
  513. hCursor = DefaultCursor,
  514. hbrBackground = IntPtr.Zero,
  515. lpszClassName = _className
  516. };
  517. ushort atom = UnmanagedMethods.RegisterClassEx(ref wndClassEx);
  518. if (atom == 0)
  519. {
  520. throw new Win32Exception();
  521. }
  522. _hwnd = CreateWindowOverride(atom);
  523. if (_hwnd == IntPtr.Zero)
  524. {
  525. throw new Win32Exception();
  526. }
  527. Handle = new PlatformHandle(_hwnd, PlatformConstants.WindowHandleType);
  528. if (UnmanagedMethods.ShCoreAvailable)
  529. {
  530. uint dpix, dpiy;
  531. var monitor = UnmanagedMethods.MonitorFromWindow(
  532. _hwnd,
  533. UnmanagedMethods.MONITOR.MONITOR_DEFAULTTONEAREST);
  534. if (UnmanagedMethods.GetDpiForMonitor(
  535. monitor,
  536. UnmanagedMethods.MONITOR_DPI_TYPE.MDT_EFFECTIVE_DPI,
  537. out dpix,
  538. out dpiy) == 0)
  539. {
  540. _scaling = dpix / 96.0;
  541. }
  542. }
  543. }
  544. private Point DipFromLParam(IntPtr lParam)
  545. {
  546. return new Point((short)(ToInt32(lParam) & 0xffff), (short)(ToInt32(lParam) >> 16)) / Scaling;
  547. }
  548. private Point PointFromLParam(IntPtr lParam)
  549. {
  550. return new Point((short)(ToInt32(lParam) & 0xffff), (short)(ToInt32(lParam) >> 16));
  551. }
  552. private Point ScreenToClient(Point point)
  553. {
  554. var p = new UnmanagedMethods.POINT { X = (int)point.X, Y = (int)point.Y };
  555. UnmanagedMethods.ScreenToClient(_hwnd, ref p);
  556. return new Point(p.X, p.Y);
  557. }
  558. private void ShowWindow(WindowState state)
  559. {
  560. UnmanagedMethods.ShowWindowCommand command;
  561. switch (state)
  562. {
  563. case WindowState.Minimized:
  564. command = UnmanagedMethods.ShowWindowCommand.Minimize;
  565. break;
  566. case WindowState.Maximized:
  567. command = UnmanagedMethods.ShowWindowCommand.Maximize;
  568. break;
  569. case WindowState.Normal:
  570. command = UnmanagedMethods.ShowWindowCommand.Restore;
  571. break;
  572. default:
  573. throw new ArgumentException("Invalid WindowState.");
  574. }
  575. UnmanagedMethods.ShowWindow(_hwnd, command);
  576. }
  577. public void SetIcon(IWindowIconImpl icon)
  578. {
  579. var impl = (IconImpl)icon;
  580. var nativeIcon = impl.IconBitmap;
  581. UnmanagedMethods.PostMessage(_hwnd, (int)UnmanagedMethods.WindowsMessage.WM_SETICON,
  582. new IntPtr((int)UnmanagedMethods.Icons.ICON_BIG), nativeIcon.GetHicon());
  583. }
  584. private static int ToInt32(IntPtr ptr)
  585. {
  586. if (IntPtr.Size == 4) return ptr.ToInt32();
  587. return (int)(ptr.ToInt64() & 0xffffffff);
  588. }
  589. }
  590. }