From 5b77fe58cc98af24f163353d50690df735d32c1b Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Sat, 1 Jul 2017 10:21:58 -0700 Subject: [PATCH 1/7] Add SetLastError --- src/Kernel32/Kernel32.cs | 9 +++++++++ src/Windows.Core/ApiSets.cs | 5 +++++ 2 files changed, 14 insertions(+) diff --git a/src/Kernel32/Kernel32.cs b/src/Kernel32/Kernel32.cs index ec20b0dd..8a459e10 100644 --- a/src/Kernel32/Kernel32.cs +++ b/src/Kernel32/Kernel32.cs @@ -43,6 +43,7 @@ public static partial class Kernel32 private const string api_ms_win_core_libraryloader_l1_1_1 = ApiSets.api_ms_win_core_libraryloader_l1_1_1; private const string api_ms_win_core_sysinfo_l1_2_1 = ApiSets.api_ms_win_core_sysinfo_l1_2_1; private const string api_ms_win_core_sysinfo_l1_2_0 = ApiSets.api_ms_win_core_sysinfo_l1_2_0; + private const string api_ms_win_core_errorhandling_l1_1_1 = ApiSets.api_ms_win_core_errorhandling_l1_1_1; #else private const string api_ms_win_core_localization_l1_2_0 = nameof(Kernel32); private const string api_ms_win_core_processthreads_l1_1_1 = nameof(Kernel32); @@ -57,6 +58,7 @@ public static partial class Kernel32 private const string api_ms_win_core_libraryloader_l1_1_1 = nameof(Kernel32); private const string api_ms_win_core_sysinfo_l1_2_1 = nameof(Kernel32); private const string api_ms_win_core_sysinfo_l1_2_0 = nameof(Kernel32); + private const string api_ms_win_core_errorhandling_l1_1_1 = nameof(Kernel32); #endif #pragma warning restore SA1303 // Const field names must begin with upper-case letter @@ -661,6 +663,13 @@ public static extern unsafe bool DeviceIoControl( [DllImport(api_ms_win_core_libraryloader_l1_1_1, CharSet = CharSet.Unicode, SetLastError = true)] public static unsafe extern void* LockResource(IntPtr hResData); + /// + /// Sets the last-error code for the calling thread. + /// + /// The last-error code for the thread. + [DllImport(api_ms_win_core_errorhandling_l1_1_1, SetLastError = true)] + public static extern void SetLastError(uint dwErrCode); + /// /// Closes a file search handle opened by the FindFirstFile, FindFirstFileEx, FindFirstFileNameW, /// FindFirstFileNameTransactedW, FindFirstFileTransacted, FindFirstStreamTransactedW, or FindFirstStreamW functions. diff --git a/src/Windows.Core/ApiSets.cs b/src/Windows.Core/ApiSets.cs index 57851ca1..0ffae8aa 100644 --- a/src/Windows.Core/ApiSets.cs +++ b/src/Windows.Core/ApiSets.cs @@ -118,5 +118,10 @@ public static class ApiSets /// The "api-ms-win-core-libraryloader-l1-1-1.dll" constant. /// public const string api_ms_win_core_libraryloader_l1_1_1 = "api-ms-win-core-libraryloader-l1-1-1.dll"; + + /// + /// The "api-ms-win-core-errorhandling-l1-1-1.dll" constant. + /// + public const string api_ms_win_core_errorhandling_l1_1_1 = "api-ms-win-core-errorhandling-l1-1-1.dll"; } } From 5041252c60a1776745447fe7224c84a5c87b8de2 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Sat, 1 Jul 2017 10:35:59 -0700 Subject: [PATCH 2/7] Add GetWindow and GetNextWindow --- src/User32/User32+GetNextWindowCommands.cs | 21 +++++++++++ src/User32/User32+GetWindowCommands.cs | 42 ++++++++++++++++++++++ src/User32/User32.cs | 20 +++++++++++ 3 files changed, 83 insertions(+) create mode 100644 src/User32/User32+GetNextWindowCommands.cs create mode 100644 src/User32/User32+GetWindowCommands.cs diff --git a/src/User32/User32+GetNextWindowCommands.cs b/src/User32/User32+GetNextWindowCommands.cs new file mode 100644 index 00000000..51609939 --- /dev/null +++ b/src/User32/User32+GetNextWindowCommands.cs @@ -0,0 +1,21 @@ +// Copyright (c) to owners found in https://github.com/AArnott/pinvoke/blob/master/COPYRIGHT.md. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. + +namespace PInvoke +{ + /// + /// Contains the nested type. + /// + public partial class User32 + { + /// The commands that can be used as arguments to . + public enum GetNextWindowCommands + { + /// Returns a handle to the window below the given window. + GW_HWNDNEXT = GetWindowCommands.GW_HWNDNEXT, + + /// Returns a handle to the window above the given window. + GW_HWNDPREV = GetWindowCommands.GW_HWNDPREV, + } + } +} diff --git a/src/User32/User32+GetWindowCommands.cs b/src/User32/User32+GetWindowCommands.cs new file mode 100644 index 00000000..70d876b9 --- /dev/null +++ b/src/User32/User32+GetWindowCommands.cs @@ -0,0 +1,42 @@ +// Copyright (c) to owners found in https://github.com/AArnott/pinvoke/blob/master/COPYRIGHT.md. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. + +namespace PInvoke +{ + /// + /// Contains the nested type. + /// + public partial class User32 + { + /// The commands that can be used as arguments to . + public enum GetWindowCommands + { + /// + /// The retrieved handle identifies the window of the same type that is highest in the Z order. + /// If the specified window is a topmost window, the handle identifies a topmost window. If the specified window is a top-level window, the handle identifies a top-level window. If the specified window is a child window, the handle identifies a sibling window. + /// + GW_HWNDFIRST = 0, + + /// + /// The retrieved handle identifies the window of the same type that is lowest in the Z order. + /// If the specified window is a topmost window, the handle identifies a topmost window. If the specified window is a top-level window, the handle identifies a top-level window. If the specified window is a child window, the handle identifies a sibling window. + /// + GW_HWNDLAST = 1, + + /// Returns a handle to the window below the given window. + GW_HWNDNEXT = 2, + + /// Returns a handle to the window above the given window. + GW_HWNDPREV = 3, + + /// The retrieved handle identifies the specified window's owner window, if any. For more information, see Owned Windows. + GW_OWNER = 4, + + /// The retrieved handle identifies the child window at the top of the Z order, if the specified window is a parent window; otherwise, the retrieved handle is NULL. The function examines only child windows of the specified window. It does not examine descendant windows. + GW_CHILD = 5, + + /// The retrieved handle identifies the enabled popup window owned by the specified window (the search uses the first such window found using ); otherwise, if there are no enabled popup windows, the retrieved handle is that of the specified window. + GW_ENABLEDPOPUP = 6, + } + } +} diff --git a/src/User32/User32.cs b/src/User32/User32.cs index e4b3cbae..12db699c 100644 --- a/src/User32/User32.cs +++ b/src/User32/User32.cs @@ -2362,6 +2362,26 @@ public static extern unsafe int GetWindowText( [Friendly(FriendlyFlags.Array)] char* lpString, int nMaxCount); + /// + /// Retrieves a handle to a window that has the specified relationship (Z-Order or owner) to the specified window. + /// + /// A handle to a window. The window handle retrieved is relative to this window, based on the value of the wCmd parameter. + /// The relationship between the specified window and the window whose handle is to be retrieved. + /// If the function succeeds, the return value is a handle to the next (or previous) window. If there is no next (or previous) window, the return value is NULL. To get extended error information, call GetLastError. + [DllImport(nameof(User32), SetLastError = true)] + public static extern IntPtr GetWindow( + IntPtr hWnd, + GetWindowCommands wCmd); + + /// + /// Retrieves a handle to the next or previous window in the Z-Order. The next window is below the specified window; the previous window is above. + /// If the specified window is a topmost window, the function searches for a topmost window. If the specified window is a top-level window, the function searches for a top-level window. If the specified window is a child window, the function searches for a child window. + /// + /// A handle to a window. The window handle retrieved is relative to this window, based on the value of the wCmd parameter. + /// Indicates whether the function returns a handle to the next window or the previous window. + /// If the function succeeds, the return value is a handle to the next (or previous) window. If there is no next (or previous) window, the return value is NULL. To get extended error information, call GetLastError. + public static IntPtr GetNextWindow(IntPtr hWnd, GetNextWindowCommands wCmd) => GetWindow(hWnd, (GetWindowCommands)wCmd); + /// /// Moves the cursor to the specified screen coordinates. If the new coordinates are not within the screen /// rectangle set by the most recent ClipCursor function call, the system automatically adjusts the coordinates so that the From 7bcf429aec771c534d0a65fb1a8eafaf4a4f4718 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Sat, 1 Jul 2017 12:06:24 -0700 Subject: [PATCH 3/7] Document GetWindowThreadProcessId --- src/User32/User32.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/User32/User32.cs b/src/User32/User32.cs index 12db699c..bf0f83c3 100644 --- a/src/User32/User32.cs +++ b/src/User32/User32.cs @@ -237,6 +237,12 @@ public static extern unsafe int GetClassName( [Friendly(FriendlyFlags.Array)] char* lpClassName, int nMaxCount); + /// + /// Retrieves the identifier of the thread that created the specified window and, optionally, the identifier of the process that created the window. + /// + /// A handle to the window. + /// A pointer to a variable that receives the process identifier. If this parameter is not NULL, GetWindowThreadProcessId copies the identifier of the process to the variable; otherwise, it does not. + /// The return value is the identifier of the thread that created the window. [DllImport(nameof(User32), SetLastError = true)] public static extern int GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId); From 6ff58acfe312dadafe59d5309c6d739cbbdd3c21 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Sat, 1 Jul 2017 12:06:32 -0700 Subject: [PATCH 4/7] Add DestroyWindow --- src/User32/User32.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/User32/User32.cs b/src/User32/User32.cs index bf0f83c3..ae5c237b 100644 --- a/src/User32/User32.cs +++ b/src/User32/User32.cs @@ -2574,6 +2574,20 @@ public static unsafe extern IntPtr CreateWindowEx( IntPtr hInstance, void* lpParam); + /// + /// Destroys the specified window. The function sends WM_DESTROY and WM_NCDESTROY messages to the window to deactivate it and remove the keyboard focus from it. The function also destroys the window's menu, flushes the thread message queue, destroys timers, removes clipboard ownership, and breaks the clipboard viewer chain (if the window is at the top of the viewer chain). + /// If the specified window is a parent or owner window, DestroyWindow automatically destroys the associated child or owned windows when it destroys the parent or owner window. The function first destroys child or owned windows, and then it destroys the parent or owner window. + /// DestroyWindow also destroys modeless dialog boxes created by the CreateDialog function. + /// + /// A handle to the window to be destroyed. + /// + /// If the function succeeds, the return value is nonzero. + /// If the function fails, the return value is zero. To get extended error information, call GetLastError. + /// + [DllImport(nameof(User32), SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool DestroyWindow(IntPtr hWnd); + [DllImport(nameof(User32), SetLastError = true)] public static extern IntPtr DispatchMessage(ref MSG lpmsg); From 0ba5ec656f5a649b04e91c61c085dad1303b0430 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Sat, 1 Jul 2017 10:55:23 -0700 Subject: [PATCH 5/7] Add SetWindowText, GetTopWindow, SetLastErrorEx --- src/User32/User32.cs | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/User32/User32.cs b/src/User32/User32.cs index ae5c237b..20c50d2a 100644 --- a/src/User32/User32.cs +++ b/src/User32/User32.cs @@ -2368,6 +2368,31 @@ public static extern unsafe int GetWindowText( [Friendly(FriendlyFlags.Array)] char* lpString, int nMaxCount); + /// + /// Changes the text of the specified window's title bar (if it has one). If the specified window is a control, the text of the control is changed. However, SetWindowText cannot change the text of a control in another application. + /// + /// A handle to the window or control whose text is to be changed. + /// The new title or control text. + /// + /// If the function succeeds, the return value is nonzero. + /// If the function fails, the return value is zero. To get extended error information, call GetLastError. + /// + [DllImport(nameof(User32), CharSet = CharSet.Unicode, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern unsafe bool SetWindowText( + IntPtr hWnd, + string lpString); + + /// + /// Examines the Z order of the child windows associated with the specified parent window and retrieves a handle to the child window at the top of the Z order. + /// + /// A handle to the parent window whose child windows are to be examined. If this parameter is NULL, the function returns a handle to the window at the top of the Z order. + /// + /// If the function succeeds, the return value is a handle to the child window at the top of the Z order. If the specified window has no child windows, the return value is NULL. To get extended error information, use the GetLastError function. + /// + [DllImport(nameof(User32), SetLastError = true)] + public static extern IntPtr GetTopWindow(IntPtr hWnd); + /// /// Retrieves a handle to a window that has the specified relationship (Z-Order or owner) to the specified window. /// @@ -2711,6 +2736,15 @@ public static extern unsafe bool SetWindowPlacement( [DllImport(nameof(User32), SetLastError = true)] public static extern unsafe void keybd_event(byte bVk, byte bScan, KEYEVENTF dwFlags, void* dwExtraInfo); + /// + /// Sets the last-error code for the calling thread. + /// Currently, this function is identical to the SetLastError function. The second parameter is ignored. + /// + /// The last-error code for the thread. + /// This parameter is ignored. + [DllImport(nameof(User32), SetLastError = true)] + public static extern void SetLastErrorEx(uint dwErrCode, uint dwType); + /// /// The BeginPaint function prepares the specified window for painting and fills a structure with information about the painting. /// From 0294c8ddf31a6934eee5d7bcdcb88676f378ae9d Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Sat, 1 Jul 2017 10:55:23 -0700 Subject: [PATCH 6/7] Add tests to try to repro #329 no success -- the test passes --- src/Kernel32.Tests/Kernel32Facts.cs | 8 +++++++ src/User32.Tests/User32Facts.cs | 34 +++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/src/Kernel32.Tests/Kernel32Facts.cs b/src/Kernel32.Tests/Kernel32Facts.cs index 93e9d8f6..abb7ad04 100644 --- a/src/Kernel32.Tests/Kernel32Facts.cs +++ b/src/Kernel32.Tests/Kernel32Facts.cs @@ -3,6 +3,7 @@ using System; using System.IO; +using System.Runtime.InteropServices; using PInvoke; using Xunit; using static PInvoke.Kernel32; @@ -22,4 +23,11 @@ public void GetTickCount64_Nonzero() ulong result = GetTickCount64(); Assert.NotEqual(0ul, result); } + + [Fact] + public void SetLastError_ImpactsMarshalGetLastWin32Error() + { + SetLastError(2); + Assert.Equal(2, Marshal.GetLastWin32Error()); + } } diff --git a/src/User32.Tests/User32Facts.cs b/src/User32.Tests/User32Facts.cs index 39d6da17..9dddac2c 100644 --- a/src/User32.Tests/User32Facts.cs +++ b/src/User32.Tests/User32Facts.cs @@ -58,4 +58,38 @@ public unsafe void INPUT_UnionMemoryAlignment() Assert.Equal(IntPtr.Size, (long)&input.Inputs.mi - (long)&input); Assert.Equal(IntPtr.Size, (long)&input.Inputs.ki - (long)&input); } + + [Fact] + public void GetWindowTextHelper_WithNonzeroLastError() + { + IntPtr hwnd = CreateWindow( + "BUTTON", + string.Empty, // empty window name + WindowStyles.WS_OVERLAPPED, + 0, + 0, + 0, + 0, + IntPtr.Zero, + IntPtr.Zero, + Process.GetCurrentProcess().Handle, + IntPtr.Zero); + if (hwnd == IntPtr.Zero) + { + throw new Win32Exception(); + } + + try + { + Kernel32.SetLastError(2); + Assert.Equal(string.Empty, GetWindowText(hwnd)); + } + finally + { + if (!DestroyWindow(hwnd)) + { + throw new Win32Exception(); + } + } + } } From 0ac63a4d1f2c6f8c5753cbed64581b9b35507b59 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Sat, 1 Jul 2017 15:46:13 -0700 Subject: [PATCH 7/7] Update PublicAPI files --- src/Kernel32/PublicAPI.Unshipped.txt | 1 + src/User32/PublicAPI.Unshipped.txt | 17 +++++++++++++++++ src/Windows.Core/PublicAPI.Unshipped.txt | 1 + 3 files changed, 19 insertions(+) diff --git a/src/Kernel32/PublicAPI.Unshipped.txt b/src/Kernel32/PublicAPI.Unshipped.txt index e69de29b..7e7ee73a 100644 --- a/src/Kernel32/PublicAPI.Unshipped.txt +++ b/src/Kernel32/PublicAPI.Unshipped.txt @@ -0,0 +1 @@ +static extern PInvoke.Kernel32.SetLastError(uint dwErrCode) -> void \ No newline at end of file diff --git a/src/User32/PublicAPI.Unshipped.txt b/src/User32/PublicAPI.Unshipped.txt index e69de29b..700d1edf 100644 --- a/src/User32/PublicAPI.Unshipped.txt +++ b/src/User32/PublicAPI.Unshipped.txt @@ -0,0 +1,17 @@ +PInvoke.User32.GetNextWindowCommands +PInvoke.User32.GetNextWindowCommands.GW_HWNDNEXT = 2 -> PInvoke.User32.GetNextWindowCommands +PInvoke.User32.GetNextWindowCommands.GW_HWNDPREV = 3 -> PInvoke.User32.GetNextWindowCommands +PInvoke.User32.GetWindowCommands +PInvoke.User32.GetWindowCommands.GW_CHILD = 5 -> PInvoke.User32.GetWindowCommands +PInvoke.User32.GetWindowCommands.GW_ENABLEDPOPUP = 6 -> PInvoke.User32.GetWindowCommands +PInvoke.User32.GetWindowCommands.GW_HWNDFIRST = 0 -> PInvoke.User32.GetWindowCommands +PInvoke.User32.GetWindowCommands.GW_HWNDLAST = 1 -> PInvoke.User32.GetWindowCommands +PInvoke.User32.GetWindowCommands.GW_HWNDNEXT = 2 -> PInvoke.User32.GetWindowCommands +PInvoke.User32.GetWindowCommands.GW_HWNDPREV = 3 -> PInvoke.User32.GetWindowCommands +PInvoke.User32.GetWindowCommands.GW_OWNER = 4 -> PInvoke.User32.GetWindowCommands +static PInvoke.User32.GetNextWindow(System.IntPtr hWnd, PInvoke.User32.GetNextWindowCommands wCmd) -> System.IntPtr +static extern PInvoke.User32.DestroyWindow(System.IntPtr hWnd) -> bool +static extern PInvoke.User32.GetTopWindow(System.IntPtr hWnd) -> System.IntPtr +static extern PInvoke.User32.GetWindow(System.IntPtr hWnd, PInvoke.User32.GetWindowCommands wCmd) -> System.IntPtr +static extern PInvoke.User32.SetLastErrorEx(uint dwErrCode, uint dwType) -> void +static extern PInvoke.User32.SetWindowText(System.IntPtr hWnd, string lpString) -> bool \ No newline at end of file diff --git a/src/Windows.Core/PublicAPI.Unshipped.txt b/src/Windows.Core/PublicAPI.Unshipped.txt index e69de29b..6d3031b9 100644 --- a/src/Windows.Core/PublicAPI.Unshipped.txt +++ b/src/Windows.Core/PublicAPI.Unshipped.txt @@ -0,0 +1 @@ +const PInvoke.ApiSets.api_ms_win_core_errorhandling_l1_1_1 = "api-ms-win-core-errorhandling-l1-1-1.dll" -> string \ No newline at end of file