Skip to content

Instantly share code, notes, and snippets.

@jxqu3
Last active March 24, 2026 02:02
Show Gist options
  • Select an option

  • Save jxqu3/a38d06598cc8716b7882a5b3e46af61d to your computer and use it in GitHub Desktop.

Select an option

Save jxqu3/a38d06598cc8716b7882a5b3e46af61d to your computer and use it in GitHub Desktop.
This is a class to make a window using the Win32 API. It needs to have Microsoft's CsWin32 set up, and works like this.
User32.*
Kernel32.*
WM_DESTROY
IDC_ARROW
BS_DEFPUSHBUTTON
using Windows.Win32;
using Windows.Win32.UI.WindowsAndMessaging;
using Windows.Win32.Foundation;
using System.Text;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
namespace NativeWindowApp;
internal class Window : Control, IDisposable
{
public string Title { get; set; } = "Native Window";
public Window(int width, int height, string title = "Native Window", string className = "MyNativeWindowClass")
{
Width = width;
Height = height;
Title = title;
ClassName = className;
Initialize();
}
public override void Initialize()
{
unsafe
{
hInstance = PInvoke.GetModuleHandle(null);
fixed (char* pClassName = ClassName)
{
WINDOW_EX_STYLE style = WINDOW_EX_STYLE.WS_EX_APPWINDOW | WINDOW_EX_STYLE.WS_EX_WINDOWEDGE;
var wndClass = new WNDCLASSW
{
lpfnWndProc = &WindowProc,
lpszClassName = new PCWSTR(pClassName),
hInstance = new HINSTANCE(hInstance.DangerousGetHandle()),
hCursor = PInvoke.LoadCursor(HINSTANCE.Null, PInvoke.IDC_ARROW),
};
_ = PInvoke.RegisterClass(wndClass);
_hwnd = PInvoke.CreateWindowEx(style, ClassName, Title, WINDOW_STYLE.WS_OVERLAPPEDWINDOW, 100, 100, Width, Height, HWND.Null, new FreeLibrarySafeHandle(), hInstance, null);
PInvoke.ShowWindow(_hwnd, SHOW_WINDOW_CMD.SW_SHOW);
}
}
}
public void Dispose()
{
if (_hwnd != HWND.Null)
{
PInvoke.DestroyWindow(_hwnd);
_hwnd = HWND.Null;
}
}
public void InitLoop()
{
while (PInvoke.GetMessage(out MSG msg, HWND.Null, 0, 0) > 0)
{
PInvoke.TranslateMessage(msg);
PInvoke.DispatchMessage(msg);
Thread.Sleep(10);
}
}
[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })]
static LRESULT WindowProc(HWND hWnd, uint msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case PInvoke.WM_DESTROY:
PInvoke.PostQuitMessage(0);
return (LRESULT)0;
}
return PInvoke.DefWindowProc(hWnd, msg, wParam, lParam);
}
}
using NativeWindowApp;
internal class Program
{
private static void Main(string[] args)
{
Console.WriteLine("Starting Native Window Application...");
using (var window = new Window(1024, 768, "My Native Window"))
{
var button = new Button(window);
window.InitLoop();
}
Console.WriteLine("Adding Native Button...");
//var button = new Button(window);
}
}
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net10.0-windows</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Nullable>enable</Nullable>
<PublishAot>true</PublishAot>
<CsWin32RunAsBuildTask>true</CsWin32RunAsBuildTask>
<DisableRuntimeMarshalling>true</DisableRuntimeMarshalling>
</PropertyGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.3.264">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.Windows.SDK.Win32Metadata" Version="69.0.7-preview" />
</ItemGroup>
</Project>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment