diff --git a/.gitignore b/.gitignore
index eda54ed08..744d70fe9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,7 +9,6 @@ tags
*.pc
.*.swp
.sw?
-*.rc
*.gcno
*.gcda
*.gcov
diff --git a/build-aux/meson.build b/build-aux/meson.build
new file mode 100644
index 000000000..7f3f2ffda
--- /dev/null
+++ b/build-aux/meson.build
@@ -0,0 +1 @@
+subdir('win32')
diff --git a/build-aux/win32/app.c b/build-aux/win32/app.c
new file mode 100644
index 000000000..e158c3b4d
--- /dev/null
+++ b/build-aux/win32/app.c
@@ -0,0 +1,162 @@
+/*
+ * Copyright © 2025 Luca Bacci
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see .
+ *
+ * Author: Luca Bacci
+ */
+
+#include "config.h"
+
+#include
+
+#include
+#include
+#include
+#include
+
+#ifdef _MSC_VER
+#include
+#endif
+
+static void
+set_process_wide_settings (void)
+{
+#if defined (_M_IX86) || defined (__i386__)
+ /* https://learn.microsoft.com/en-us/archive/blogs/michael_howard/faq-about-heapsetinformation-in-windows-vista-and-heap-based-buffer-overruns */
+ /* https://web.archive.org/web/20080825034220/https://blogs.msdn.com/sdl/archive/2008/06/06/corrupted-heap-termination-redux.aspx */
+ HeapSetInformation (NULL, HeapEnableTerminationOnCorruption, NULL, 0);
+
+ /* https://learn.microsoft.com/en-us/archive/blogs/michael_howard/new-nx-apis-added-to-windows-vista-sp1-windows-xp-sp3-and-windows-server-2008 */
+ SetProcessDEPPolicy (PROCESS_DEP_ENABLE | PROCESS_DEP_DISABLE_ATL_THUNK_EMULATION);
+#endif
+
+ SetErrorMode (GetErrorMode () | SEM_FAILCRITICALERRORS);
+}
+
+static void
+set_crt_non_interactive (void)
+{
+ /* The Debug CRT may show UI dialogs even in console applications.
+ * Direct to stderr instead.
+ */
+ _CrtSetReportFile (_CRT_ASSERT, _CRTDBG_FILE_STDERR);
+ _CrtSetReportMode (_CRT_ASSERT, _CRTDBG_MODE_FILE);
+
+ _CrtSetReportFile (_CRT_ERROR, _CRTDBG_FILE_STDERR);
+ _CrtSetReportMode (_CRT_ERROR, _CRTDBG_MODE_FILE);
+
+ _CrtSetReportFile (_CRT_WARN, _CRTDBG_FILE_STDERR);
+ _CrtSetReportMode (_CRT_WARN, _CRTDBG_MODE_FILE);
+}
+
+static void
+set_stderr_unbuffered_mode (void)
+{
+ /* MSVCRT.DLL can open stderr in full-buffering mode. That depends on
+ * the type of output device; for example, it's fully buffered for
+ * named pipes but not for console devices.
+ *
+ * Having a fully buffered stderr is not a good default since we can
+ * loose important messages before a crash. Moreover, POSIX forbids
+ * full buffering on stderr. So here we set stderr to unbuffered mode.
+ *
+ * Note: line buffering mode would be good enough, but the Windows C
+ * RunTime library implements it the same as full buffering:
+ *
+ * "for some systems, _IOLBF provides line buffering. However, for
+ * Win32, the behavior is the same as _IOFBF: Full Buffering"
+ *
+ * https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/setvbuf#remarks
+ *
+ * References:
+ *
+ * - https://sourceforge.net/p/mingw/mailman/message/27121137/
+ */
+#if !defined (_UCRT)
+ int ret = setvbuf (stderr, NULL, _IONBF, 0);
+ assert (ret == 0);
+#endif
+}
+
+static void
+early_flush_exit_handler (void)
+{
+ /* There are two ways to flush open streams: calling fflush with NULL
+ * argument and calling _flushall. The former flushes output streams
+ * only, the latter flushes both input and output streams.
+ * We should not do anything with input streams here since flushing
+ * means * discarding * data.
+ */
+ fflush (NULL);
+}
+
+static void
+register_early_flush_at_exit (void)
+{
+ /* Implement the two-phase flushing at process exit.
+ *
+ * The C RunTime library flushes open streams within its DllMain handler.
+ * This goes against the rules for DllMain, as each stream is protected
+ * by a lock and locks must not be acquired in DllMain.
+ *
+ * So we flush from app code using an atexit handler. The handler runs when
+ * the application is in a fully working state and thus is completely safe.
+ *
+ * This ensures that all important data is flushed. Anything that is written
+ * after exit will be flushed lately by the C RunTime library (and therefore
+ * may be skipped).
+ *
+ * See comments in "%ProgramFiles(x86)%\Windows Kits\10\Source\\
+ * \ucrt\stdio\fflush.cpp" for more informations.
+ *
+ * References:
+ *
+ * - https://devblogs.microsoft.com/oldnewthing/20070503-00/?p=27003
+ * - https://devblogs.microsoft.com/oldnewthing/20100122-00/?p=15193
+ * - https://learn.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-best-practices
+ */
+ int ret = atexit (early_flush_exit_handler);
+ assert (ret == 0);
+}
+
+/* Boilerplate for CRT constructor */
+
+#ifdef _MSC_VER
+static void startup (void);
+
+__pragma (section (".CRT$XCT", long, read))
+
+__declspec (allocate (".CRT$XCT"))
+const void (*ptr_startup) (void) = startup;
+
+#ifdef _M_IX86
+__pragma (comment (linker, "/INCLUDE:" "_ptr_startup"))
+#else
+__pragma (comment (linker, "/INCLUDE:" "ptr_startup"))
+#endif
+#else
+static void __attribute__((constructor)) startup (void);
+#endif
+
+static void
+startup (void)
+{
+ set_crt_non_interactive ();
+ set_process_wide_settings ();
+ set_stderr_unbuffered_mode ();
+ register_early_flush_at_exit ();
+}
diff --git a/build-aux/win32/app.manifest.xml b/build-aux/win32/app.manifest.xml
new file mode 100644
index 000000000..57ec3d31d
--- /dev/null
+++ b/build-aux/win32/app.manifest.xml
@@ -0,0 +1,40 @@
+
+
+
+
+ UTF-8
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/build-aux/win32/app.rc b/build-aux/win32/app.rc
new file mode 100644
index 000000000..ce9785df1
--- /dev/null
+++ b/build-aux/win32/app.rc
@@ -0,0 +1,6 @@
+#pragma code_page(65001)
+
+#define WIN32_LEAN_AND_MEAN
+#include
+
+CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "app.manifest.xml"
diff --git a/build-aux/win32/meson.build b/build-aux/win32/meson.build
new file mode 100644
index 000000000..820b6514f
--- /dev/null
+++ b/build-aux/win32/meson.build
@@ -0,0 +1,14 @@
+
+if host_system == 'windows'
+ static_lib = static_library('os-profile-app-static-lib',
+ sources: ['app.c'],
+ include_directories: [configinc])
+
+ resources = windows.compile_resources('app.rc',
+ depend_files: ['app.manifest.xml'])
+
+ app_profile_dep = declare_dependency(link_whole: [static_lib],
+ sources: [resources])
+else
+ app_profile_dep = declare_dependency()
+endif