Skip to content
This repository was archived by the owner on Jul 26, 2023. It is now read-only.

Commit b755726

Browse files
authored
Merge pull request #338 from AArnott/fix329
Add Kernel32 and User32 methods, and tests #329
2 parents 8de2db0 + 0ac63a4 commit b755726

File tree

10 files changed

+212
-0
lines changed

10 files changed

+212
-0
lines changed

src/Kernel32.Tests/Kernel32Facts.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System;
55
using System.IO;
6+
using System.Runtime.InteropServices;
67
using PInvoke;
78
using Xunit;
89
using static PInvoke.Kernel32;
@@ -22,4 +23,11 @@ public void GetTickCount64_Nonzero()
2223
ulong result = GetTickCount64();
2324
Assert.NotEqual(0ul, result);
2425
}
26+
27+
[Fact]
28+
public void SetLastError_ImpactsMarshalGetLastWin32Error()
29+
{
30+
SetLastError(2);
31+
Assert.Equal(2, Marshal.GetLastWin32Error());
32+
}
2533
}

src/Kernel32/Kernel32.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ public static partial class Kernel32
4343
private const string api_ms_win_core_libraryloader_l1_1_1 = ApiSets.api_ms_win_core_libraryloader_l1_1_1;
4444
private const string api_ms_win_core_sysinfo_l1_2_1 = ApiSets.api_ms_win_core_sysinfo_l1_2_1;
4545
private const string api_ms_win_core_sysinfo_l1_2_0 = ApiSets.api_ms_win_core_sysinfo_l1_2_0;
46+
private const string api_ms_win_core_errorhandling_l1_1_1 = ApiSets.api_ms_win_core_errorhandling_l1_1_1;
4647
#else
4748
private const string api_ms_win_core_localization_l1_2_0 = nameof(Kernel32);
4849
private const string api_ms_win_core_processthreads_l1_1_1 = nameof(Kernel32);
@@ -57,6 +58,7 @@ public static partial class Kernel32
5758
private const string api_ms_win_core_libraryloader_l1_1_1 = nameof(Kernel32);
5859
private const string api_ms_win_core_sysinfo_l1_2_1 = nameof(Kernel32);
5960
private const string api_ms_win_core_sysinfo_l1_2_0 = nameof(Kernel32);
61+
private const string api_ms_win_core_errorhandling_l1_1_1 = nameof(Kernel32);
6062
#endif
6163
#pragma warning restore SA1303 // Const field names must begin with upper-case letter
6264

@@ -661,6 +663,13 @@ public static extern unsafe bool DeviceIoControl(
661663
[DllImport(api_ms_win_core_libraryloader_l1_1_1, CharSet = CharSet.Unicode, SetLastError = true)]
662664
public static unsafe extern void* LockResource(IntPtr hResData);
663665

666+
/// <summary>
667+
/// Sets the last-error code for the calling thread.
668+
/// </summary>
669+
/// <param name="dwErrCode">The last-error code for the thread.</param>
670+
[DllImport(api_ms_win_core_errorhandling_l1_1_1, SetLastError = true)]
671+
public static extern void SetLastError(uint dwErrCode);
672+
664673
/// <summary>
665674
/// Closes a file search handle opened by the FindFirstFile, FindFirstFileEx, FindFirstFileNameW,
666675
/// FindFirstFileNameTransactedW, FindFirstFileTransacted, FindFirstStreamTransactedW, or FindFirstStreamW functions.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
static extern PInvoke.Kernel32.SetLastError(uint dwErrCode) -> void

src/User32.Tests/User32Facts.cs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,4 +58,38 @@ public unsafe void INPUT_UnionMemoryAlignment()
5858
Assert.Equal(IntPtr.Size, (long)&input.Inputs.mi - (long)&input);
5959
Assert.Equal(IntPtr.Size, (long)&input.Inputs.ki - (long)&input);
6060
}
61+
62+
[Fact]
63+
public void GetWindowTextHelper_WithNonzeroLastError()
64+
{
65+
IntPtr hwnd = CreateWindow(
66+
"BUTTON",
67+
string.Empty, // empty window name
68+
WindowStyles.WS_OVERLAPPED,
69+
0,
70+
0,
71+
0,
72+
0,
73+
IntPtr.Zero,
74+
IntPtr.Zero,
75+
Process.GetCurrentProcess().Handle,
76+
IntPtr.Zero);
77+
if (hwnd == IntPtr.Zero)
78+
{
79+
throw new Win32Exception();
80+
}
81+
82+
try
83+
{
84+
Kernel32.SetLastError(2);
85+
Assert.Equal(string.Empty, GetWindowText(hwnd));
86+
}
87+
finally
88+
{
89+
if (!DestroyWindow(hwnd))
90+
{
91+
throw new Win32Exception();
92+
}
93+
}
94+
}
6195
}

src/User32/PublicAPI.Unshipped.txt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
PInvoke.User32.GetNextWindowCommands
2+
PInvoke.User32.GetNextWindowCommands.GW_HWNDNEXT = 2 -> PInvoke.User32.GetNextWindowCommands
3+
PInvoke.User32.GetNextWindowCommands.GW_HWNDPREV = 3 -> PInvoke.User32.GetNextWindowCommands
4+
PInvoke.User32.GetWindowCommands
5+
PInvoke.User32.GetWindowCommands.GW_CHILD = 5 -> PInvoke.User32.GetWindowCommands
6+
PInvoke.User32.GetWindowCommands.GW_ENABLEDPOPUP = 6 -> PInvoke.User32.GetWindowCommands
7+
PInvoke.User32.GetWindowCommands.GW_HWNDFIRST = 0 -> PInvoke.User32.GetWindowCommands
8+
PInvoke.User32.GetWindowCommands.GW_HWNDLAST = 1 -> PInvoke.User32.GetWindowCommands
9+
PInvoke.User32.GetWindowCommands.GW_HWNDNEXT = 2 -> PInvoke.User32.GetWindowCommands
10+
PInvoke.User32.GetWindowCommands.GW_HWNDPREV = 3 -> PInvoke.User32.GetWindowCommands
11+
PInvoke.User32.GetWindowCommands.GW_OWNER = 4 -> PInvoke.User32.GetWindowCommands
12+
static PInvoke.User32.GetNextWindow(System.IntPtr hWnd, PInvoke.User32.GetNextWindowCommands wCmd) -> System.IntPtr
13+
static extern PInvoke.User32.DestroyWindow(System.IntPtr hWnd) -> bool
14+
static extern PInvoke.User32.GetTopWindow(System.IntPtr hWnd) -> System.IntPtr
15+
static extern PInvoke.User32.GetWindow(System.IntPtr hWnd, PInvoke.User32.GetWindowCommands wCmd) -> System.IntPtr
16+
static extern PInvoke.User32.SetLastErrorEx(uint dwErrCode, uint dwType) -> void
17+
static extern PInvoke.User32.SetWindowText(System.IntPtr hWnd, string lpString) -> bool
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Copyright (c) to owners found in https://github.com/AArnott/pinvoke/blob/master/COPYRIGHT.md. All rights reserved.
2+
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
3+
4+
namespace PInvoke
5+
{
6+
/// <content>
7+
/// Contains the <see cref="GetNextWindowCommands"/> nested type.
8+
/// </content>
9+
public partial class User32
10+
{
11+
/// <summary>The commands that can be used as arguments to <see cref="GetNextWindow" />.</summary>
12+
public enum GetNextWindowCommands
13+
{
14+
/// <summary>Returns a handle to the window below the given window.</summary>
15+
GW_HWNDNEXT = GetWindowCommands.GW_HWNDNEXT,
16+
17+
/// <summary>Returns a handle to the window above the given window.</summary>
18+
GW_HWNDPREV = GetWindowCommands.GW_HWNDPREV,
19+
}
20+
}
21+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Copyright (c) to owners found in https://github.com/AArnott/pinvoke/blob/master/COPYRIGHT.md. All rights reserved.
2+
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
3+
4+
namespace PInvoke
5+
{
6+
/// <content>
7+
/// Contains the <see cref="GetWindowCommands"/> nested type.
8+
/// </content>
9+
public partial class User32
10+
{
11+
/// <summary>The commands that can be used as arguments to <see cref="GetWindow" />.</summary>
12+
public enum GetWindowCommands
13+
{
14+
/// <summary>
15+
/// The retrieved handle identifies the window of the same type that is highest in the Z order.
16+
/// 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.
17+
/// </summary>
18+
GW_HWNDFIRST = 0,
19+
20+
/// <summary>
21+
/// The retrieved handle identifies the window of the same type that is lowest in the Z order.
22+
/// 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.
23+
/// </summary>
24+
GW_HWNDLAST = 1,
25+
26+
/// <summary>Returns a handle to the window below the given window.</summary>
27+
GW_HWNDNEXT = 2,
28+
29+
/// <summary>Returns a handle to the window above the given window.</summary>
30+
GW_HWNDPREV = 3,
31+
32+
/// <summary>The retrieved handle identifies the specified window's owner window, if any. For more information, see Owned Windows.</summary>
33+
GW_OWNER = 4,
34+
35+
/// <summary>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.</summary>
36+
GW_CHILD = 5,
37+
38+
/// <summary>The retrieved handle identifies the enabled popup window owned by the specified window (the search uses the first such window found using <see cref="GW_HWNDNEXT" />); otherwise, if there are no enabled popup windows, the retrieved handle is that of the specified window.</summary>
39+
GW_ENABLEDPOPUP = 6,
40+
}
41+
}
42+
}

src/User32/User32.cs

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,12 @@ public static extern unsafe int GetClassName(
237237
[Friendly(FriendlyFlags.Array)] char* lpClassName,
238238
int nMaxCount);
239239

240+
/// <summary>
241+
/// Retrieves the identifier of the thread that created the specified window and, optionally, the identifier of the process that created the window.
242+
/// </summary>
243+
/// <param name="hWnd">A handle to the window. </param>
244+
/// <param name="lpdwProcessId">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.</param>
245+
/// <returns>The return value is the identifier of the thread that created the window. </returns>
240246
[DllImport(nameof(User32), SetLastError = true)]
241247
public static extern int GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId);
242248

@@ -2362,6 +2368,51 @@ public static extern unsafe int GetWindowText(
23622368
[Friendly(FriendlyFlags.Array)] char* lpString,
23632369
int nMaxCount);
23642370

2371+
/// <summary>
2372+
/// 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.
2373+
/// </summary>
2374+
/// <param name="hWnd">A handle to the window or control whose text is to be changed. </param>
2375+
/// <param name="lpString">The new title or control text. </param>
2376+
/// <returns>
2377+
/// If the function succeeds, the return value is nonzero.
2378+
/// If the function fails, the return value is zero. To get extended error information, call GetLastError.
2379+
/// </returns>
2380+
[DllImport(nameof(User32), CharSet = CharSet.Unicode, SetLastError = true)]
2381+
[return: MarshalAs(UnmanagedType.Bool)]
2382+
public static extern unsafe bool SetWindowText(
2383+
IntPtr hWnd,
2384+
string lpString);
2385+
2386+
/// <summary>
2387+
/// 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.
2388+
/// </summary>
2389+
/// <param name="hWnd">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.</param>
2390+
/// <returns>
2391+
/// 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.
2392+
/// </returns>
2393+
[DllImport(nameof(User32), SetLastError = true)]
2394+
public static extern IntPtr GetTopWindow(IntPtr hWnd);
2395+
2396+
/// <summary>
2397+
/// Retrieves a handle to a window that has the specified relationship (Z-Order or owner) to the specified window.
2398+
/// </summary>
2399+
/// <param name="hWnd">A handle to a window. The window handle retrieved is relative to this window, based on the value of the wCmd parameter. </param>
2400+
/// <param name="wCmd">The relationship between the specified window and the window whose handle is to be retrieved.</param>
2401+
/// <returns>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.</returns>
2402+
[DllImport(nameof(User32), SetLastError = true)]
2403+
public static extern IntPtr GetWindow(
2404+
IntPtr hWnd,
2405+
GetWindowCommands wCmd);
2406+
2407+
/// <summary>
2408+
/// 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.
2409+
/// 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.
2410+
/// </summary>
2411+
/// <param name="hWnd">A handle to a window. The window handle retrieved is relative to this window, based on the value of the wCmd parameter. </param>
2412+
/// <param name="wCmd">Indicates whether the function returns a handle to the next window or the previous window.</param>
2413+
/// <returns>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.</returns>
2414+
public static IntPtr GetNextWindow(IntPtr hWnd, GetNextWindowCommands wCmd) => GetWindow(hWnd, (GetWindowCommands)wCmd);
2415+
23652416
/// <summary>
23662417
/// Moves the cursor to the specified screen coordinates. If the new coordinates are not within the screen
23672418
/// rectangle set by the most recent ClipCursor function call, the system automatically adjusts the coordinates so that the
@@ -2548,6 +2599,20 @@ public static unsafe extern IntPtr CreateWindowEx(
25482599
IntPtr hInstance,
25492600
void* lpParam);
25502601

2602+
/// <summary>
2603+
/// 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).
2604+
/// 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.
2605+
/// DestroyWindow also destroys modeless dialog boxes created by the CreateDialog function.
2606+
/// </summary>
2607+
/// <param name="hWnd">A handle to the window to be destroyed. </param>
2608+
/// <returns>
2609+
/// If the function succeeds, the return value is nonzero.
2610+
/// If the function fails, the return value is zero. To get extended error information, call GetLastError.
2611+
/// </returns>
2612+
[DllImport(nameof(User32), SetLastError = true)]
2613+
[return: MarshalAs(UnmanagedType.Bool)]
2614+
public static extern bool DestroyWindow(IntPtr hWnd);
2615+
25512616
[DllImport(nameof(User32), SetLastError = true)]
25522617
public static extern IntPtr DispatchMessage(ref MSG lpmsg);
25532618

@@ -2671,6 +2736,15 @@ public static extern unsafe bool SetWindowPlacement(
26712736
[DllImport(nameof(User32), SetLastError = true)]
26722737
public static extern unsafe void keybd_event(byte bVk, byte bScan, KEYEVENTF dwFlags, void* dwExtraInfo);
26732738

2739+
/// <summary>
2740+
/// Sets the last-error code for the calling thread.
2741+
/// Currently, this function is identical to the SetLastError function. The second parameter is ignored.
2742+
/// </summary>
2743+
/// <param name="dwErrCode">The last-error code for the thread.</param>
2744+
/// <param name="dwType">This parameter is ignored.</param>
2745+
[DllImport(nameof(User32), SetLastError = true)]
2746+
public static extern void SetLastErrorEx(uint dwErrCode, uint dwType);
2747+
26742748
/// <summary>
26752749
/// The BeginPaint function prepares the specified window for painting and fills a <see cref="PAINTSTRUCT"/> structure with information about the painting.
26762750
/// </summary>

src/Windows.Core/ApiSets.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,5 +118,10 @@ public static class ApiSets
118118
/// The "api-ms-win-core-libraryloader-l1-1-1.dll" constant.
119119
/// </summary>
120120
public const string api_ms_win_core_libraryloader_l1_1_1 = "api-ms-win-core-libraryloader-l1-1-1.dll";
121+
122+
/// <summary>
123+
/// The "api-ms-win-core-errorhandling-l1-1-1.dll" constant.
124+
/// </summary>
125+
public const string api_ms_win_core_errorhandling_l1_1_1 = "api-ms-win-core-errorhandling-l1-1-1.dll";
121126
}
122127
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
const PInvoke.ApiSets.api_ms_win_core_errorhandling_l1_1_1 = "api-ms-win-core-errorhandling-l1-1-1.dll" -> string

0 commit comments

Comments
 (0)