diff --git a/.github/workflows/makedark.yml b/.github/workflows/makedark.yml
new file mode 100644
index 0000000..80a97fa
--- /dev/null
+++ b/.github/workflows/makedark.yml
@@ -0,0 +1,29 @@
+name: MakeDark
+
+on:
+ release:
+ types: [created]
+ push:
+ branches:
+ - master
+
+jobs:
+ build:
+ runs-on: windows-latest
+
+ steps:
+ - uses: actions/checkout@v3
+
+ - name: Setup .NET
+ uses: actions/setup-dotnet@v2
+ with:
+ dotnet-version: 8.0.x
+
+ - name: Setup Windows SDK
+ uses: GuillaumeFalourd/setup-windows10-sdk-action@v1.5
+
+ - name: Install dependencies
+ run: dotnet restore src\MakeDark.csproj
+
+ - name: Build
+ run: dotnet build src\MakeDark.csproj --configuration Release --no-restore
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..262d668
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,173 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
+
+# User-specific files
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+[Aa][Rr][Mm]/
+[Aa][Rr][Mm]64/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+[Rr]eport/
+
+# Visual Studio cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUNIT
+*.VisualState.xml
+TestResult.xml
+
+*_i.c
+*_p.c
+*_i.h
+*.ilk
+*.meta
+*.obj
+*.pch
+*.pdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*_wpftmp.*
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axocover/
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_/
+.*crunch*.local.xml
+nCrunchTemp_*/
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+
+# Note: This signature is weak since it does not require VisualStudioVersion=12.0 and it may match on files not in solution folders
+*__[Rr]e[Ss]harper.user
+*_*.suo
+*_*.user
+*_*.aps
+*_*.ncb
+*_*.opensdf
+*_*.sdf
+*_*.cachefile
+*_*.VC.db
+*_*.bak
+*__history/
+
+# Backup & report files from converting an old project file to a newer
+# Visual Studio version. Backup files are not needed, because we have git
+_reportMaxUpgradeOrder.numbers.html
+Backup*/Web.config*
+UpgradeLog*.XML
+UpgradeLog*.htm
+UpgradeLog*.html
+UpgradeLog*.txt
+
+# Windows Presentation Foundation (WPF) specific
+*.g.xaml
+*.g.cs
+
+# XAML generated resource dictionaries
+*.baml
+
+# Backup files when performing XAML Binding debugging
+*.backupid-*.BindingExpression
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..0463556
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2024 Lemutec
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/MakeDark.sln b/MakeDark.sln
new file mode 100644
index 0000000..36a5bd5
--- /dev/null
+++ b/MakeDark.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.9.34902.65
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MakeDark", "src\MakeDark.csproj", "{BEBBDDC5-A479-470B-AA88-CDBDF70C8022}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {BEBBDDC5-A479-470B-AA88-CDBDF70C8022}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {BEBBDDC5-A479-470B-AA88-CDBDF70C8022}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {BEBBDDC5-A479-470B-AA88-CDBDF70C8022}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {BEBBDDC5-A479-470B-AA88-CDBDF70C8022}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {E503C867-4D7D-4414-A048-1E90CF07CCD2}
+ EndGlobalSection
+EndGlobal
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..751fb82
--- /dev/null
+++ b/README.md
@@ -0,0 +1,15 @@
+[](https://github.com/lemutec/MakeDark/actions/workflows/makedark.yml) [](https://dotnet.microsoft.com/zh-cn/download/dotnet/latest/runtime)
+
+# MakeDark
+
+Make your windows application launch as dark mode.
+
+## Usage
+
+Drag&drop the `*.lnk` or `*.exe` file to `makedark.exe`.
+
+And then the `*.lnk` file will be edited to launch using MakeDark.
+
+## Effect
+
+
diff --git a/assets/image-20240810143110413.png b/assets/image-20240810143110413.png
new file mode 100644
index 0000000..9c2fb5d
Binary files /dev/null and b/assets/image-20240810143110413.png differ
diff --git a/src/Favicon.ico b/src/Favicon.ico
new file mode 100644
index 0000000..074053e
Binary files /dev/null and b/src/Favicon.ico differ
diff --git a/src/Favicon.png b/src/Favicon.png
new file mode 100644
index 0000000..4798823
Binary files /dev/null and b/src/Favicon.png differ
diff --git a/src/Favicon.svg b/src/Favicon.svg
new file mode 100644
index 0000000..41e0127
--- /dev/null
+++ b/src/Favicon.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/MakeDark.csproj b/src/MakeDark.csproj
new file mode 100644
index 0000000..6d13e80
--- /dev/null
+++ b/src/MakeDark.csproj
@@ -0,0 +1,42 @@
+
+
+
+ WinExe
+ net8.0-windows
+ enable
+ enable
+ true
+ 12.0
+ true
+ false
+ true
+ false
+ Favicon.ico
+ makedark
+ 0.1.0
+ 0.1.0
+ $(VersionPrefix)0.1.0
+ Lemutec
+
+
+
+
+
+
+
+
+
+
+
+
+ tlbimp
+ 0
+ 1
+ f935dc20-1cf0-11d0-adb9-00c04fd58a0b
+ 0
+ false
+ true
+
+
+
+
diff --git a/src/NativeMethods.cs b/src/NativeMethods.cs
new file mode 100644
index 0000000..b6f226b
--- /dev/null
+++ b/src/NativeMethods.cs
@@ -0,0 +1,107 @@
+using System.Runtime.InteropServices;
+
+namespace MakeDark;
+
+internal static class NativeMethods
+{
+ [DllImport("user32.dll", SetLastError = true)]
+ public static extern nint FindWindow(string lpClassName, string lpWindowName);
+
+ [DllImport("user32.dll", SetLastError = true)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ public static extern bool IsWindow(nint hWnd);
+
+ [DllImport("dwmapi.dll", PreserveSig = true)]
+ public static extern int DwmSetWindowAttribute(nint hwnd, DwmWindowAttribute attr, ref int attrValue, int attrSize);
+
+ [DllImport("ntdll.dll", SetLastError = true)]
+ public static extern int RtlGetVersion(ref RTL_OSVERSIONINFOEX lpVersionInformation);
+
+ public static bool IsWindows10Version1809OrAbove()
+ {
+ RTL_OSVERSIONINFOEX versionInfo = new()
+ {
+ dwOSVersionInfoSize = (uint)Marshal.SizeOf(),
+ };
+
+ if (RtlGetVersion(ref versionInfo) == 0)
+ {
+ // Windows 10 1809
+ return versionInfo.dwMajorVersion >= 10 && versionInfo.dwBuildNumber >= 17763;
+ }
+
+ return false;
+ }
+
+ public static bool EnableDarkModeForWindow(nint hWnd, bool enable = true)
+ {
+ if (IsWindows10Version1809OrAbove())
+ {
+ int darkMode = enable ? 1 : 0;
+ int hr = DwmSetWindowAttribute(hWnd, DwmWindowAttribute.UseImmersiveDarkMode, ref darkMode, sizeof(int));
+ return hr >= 0;
+ }
+ return true;
+ }
+
+ public static bool SetRoundedCorners(nint hWnd, bool enable = true)
+ {
+ if (IsWindows10Version1809OrAbove())
+ {
+ int preference = enable ? (int)DwmWindowCornerPreference.DWMWCP_ROUND : (int)DwmWindowCornerPreference.DWMWCP_DONOTROUND;
+ int hr = DwmSetWindowAttribute(hWnd, DwmWindowAttribute.WindowCornerPreference, ref preference, sizeof(int));
+ return hr >= 0;
+ }
+ return true;
+ }
+
+ public enum DwmWindowAttribute : uint
+ {
+ NCRenderingEnabled = 1,
+ NCRenderingPolicy,
+ TransitionsForceDisabled,
+ AllowNCPaint,
+ CaptionButtonBounds,
+ NonClientRtlLayout,
+ ForceIconicRepresentation,
+ Flip3DPolicy,
+ ExtendedFrameBounds,
+ HasIconicBitmap,
+ DisallowPeek,
+ ExcludedFromPeek,
+ Cloak,
+ Cloaked,
+ FreezeRepresentation,
+ PassiveUpdateMode,
+ UseHostBackdropBrush,
+ UseImmersiveDarkMode = 20,
+ WindowCornerPreference = 33,
+ BorderColor,
+ CaptionColor,
+ TextColor,
+ VisibleFrameBorderThickness,
+ SystemBackdropType,
+ Last,
+ }
+
+ public enum DwmWindowCornerPreference : uint
+ {
+ DWMWCP_DEFAULT = 0,
+ DWMWCP_DONOTROUND = 1,
+ DWMWCP_ROUND = 2,
+ DWMWCP_ROUNDSMALL = 3
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct RTL_OSVERSIONINFOEX
+ {
+ public uint dwOSVersionInfoSize;
+ public uint dwMajorVersion;
+ public uint dwMinorVersion;
+ public uint dwBuildNumber;
+ public uint dwPlatformId;
+
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
+ public string szCSDVersion;
+ }
+}
diff --git a/src/Program.cs b/src/Program.cs
new file mode 100644
index 0000000..c1da129
--- /dev/null
+++ b/src/Program.cs
@@ -0,0 +1,71 @@
+using Lnk;
+using System.Diagnostics;
+
+namespace MakeDark;
+
+internal static class Program
+{
+ public static void Main(string[] args)
+ {
+ if (args.Length <= 0)
+ {
+ return;
+ }
+
+ string fileName = args[0];
+
+ if (!File.Exists(fileName))
+ {
+ return;
+ }
+
+ if (Path.GetExtension(fileName)?.ToLower().Equals(".lnk") ?? false)
+ {
+ LnkFile? src = ShortcutHelper.Open(fileName);
+
+ if (src != null)
+ {
+ if (src.LocalPath == Environment.ProcessPath)
+ {
+ // Done.
+ return;
+ }
+
+ LnkFile2 tar = new()
+ {
+ SourceFile = src.SourceFile,
+ LocalPath = Environment.ProcessPath,
+ WorkingDirectory = src.WorkingDirectory,
+ Arguments = ArgumentExtension.ToArguments([src.LocalPath, src.Arguments]),
+ Description = "Make Dark Mode Launcher Lnk",
+ IconLocation = src.LocalPath
+ };
+ _ = ShortcutHelper.Create(tar);
+ }
+ }
+ else
+ {
+ using Process process = new()
+ {
+ StartInfo = new ProcessStartInfo()
+ {
+ FileName = args[0],
+ Arguments = string.Join(" ", args[1..].Select(a => $"\"{a}\""))
+ }
+ };
+
+ process.Start();
+ process.WaitForInputIdle();
+
+ SpinWait.SpinUntil(() => process.HasExited || process.MainWindowHandle != IntPtr.Zero);
+
+ if (process.HasExited)
+ {
+ return;
+ }
+
+ NativeMethods.EnableDarkModeForWindow(process.MainWindowHandle);
+ NativeMethods.SetRoundedCorners(process.MainWindowHandle);
+ }
+ }
+}
diff --git a/src/Properties/PublishProfiles/FolderProfile.pubxml b/src/Properties/PublishProfiles/FolderProfile.pubxml
new file mode 100644
index 0000000..c21f27f
--- /dev/null
+++ b/src/Properties/PublishProfiles/FolderProfile.pubxml
@@ -0,0 +1,18 @@
+
+
+
+
+ Release
+ Any CPU
+ bin\Release\net8.0-windows\publish\win-x64\
+ FileSystem
+ <_TargetId>Folder
+ net8.0-windows
+ false
+ win-x64
+ true
+ false
+
+
\ No newline at end of file
diff --git a/src/Properties/launchSettings.json b/src/Properties/launchSettings.json
new file mode 100644
index 0000000..f0e834b
--- /dev/null
+++ b/src/Properties/launchSettings.json
@@ -0,0 +1,8 @@
+{
+ "profiles": {
+ "MakeDark": {
+ "commandName": "Project",
+ "commandLineArgs": "C:\\Users\\ema\\Desktop\\LocalSend.lnk"
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ShortcutHelper.cs b/src/ShortcutHelper.cs
new file mode 100644
index 0000000..098fd57
--- /dev/null
+++ b/src/ShortcutHelper.cs
@@ -0,0 +1,133 @@
+using Lnk;
+
+namespace MakeDark;
+
+internal static class ShortcutHelper
+{
+ public static LnkFile? Open(string fileName)
+ {
+ if (!File.Exists(fileName))
+ {
+ return null!;
+ }
+
+ byte[] raw = File.ReadAllBytes(fileName);
+
+ if (raw[0] == 0x4c)
+ {
+ LnkFile lnkObj = new(raw, fileName);
+ return lnkObj;
+ }
+ return null!;
+ }
+
+ public static bool Create(LnkFile2 lnkFile)
+ {
+ if (string.IsNullOrWhiteSpace(lnkFile.SourceFile))
+ {
+ return false;
+ }
+
+ FileInfo lnkFileInfo = new(lnkFile.SourceFile);
+
+ if (!Directory.Exists(lnkFileInfo.DirectoryName))
+ {
+ _ = Directory.CreateDirectory(lnkFileInfo.DirectoryName!);
+ }
+
+#if false
+ IWshRuntimeLibrary.WshShell shell = new();
+ IWshRuntimeLibrary.IWshShortcut shortcut = (IWshRuntimeLibrary.IWshShortcut)shell.CreateShortcut(lnkFile.SourceFile);
+ shortcut.TargetPath = lnkFile.LocalPath;
+ shortcut.WorkingDirectory = lnkFile.WorkingDirectory;
+ shortcut.WindowStyle = 1;
+ shortcut.Arguments = lnkFile.Arguments;
+ shortcut.Description = lnkFile.Description;
+ shortcut.IconLocation = lnkFile.IconLocation;
+ shortcut.Save();
+#else
+ dynamic shell = null!;
+ dynamic shortcut = null!;
+
+ try
+ {
+ shell = Activator.CreateInstance(Type.GetTypeFromCLSID(new Guid("72C24DD5-D70A-438B-8A42-98424B88AFB8"))!)!;
+ shortcut = shell.CreateShortcut(lnkFile.SourceFile);
+ shortcut.TargetPath = lnkFile.LocalPath;
+ shortcut.WorkingDirectory = lnkFile.WorkingDirectory;
+ shortcut.WindowStyle = 1;
+ shortcut.Arguments = lnkFile.Arguments;
+ shortcut.Description = lnkFile.Description;
+ shortcut.IconLocation = lnkFile.IconLocation;
+ shortcut.Save();
+ }
+ finally
+ {
+ if (shortcut != null)
+ {
+ _ = System.Runtime.InteropServices.Marshal.FinalReleaseComObject(shortcut);
+ }
+ if (shell != null)
+ {
+ _ = System.Runtime.InteropServices.Marshal.FinalReleaseComObject(shell);
+ }
+ }
+#endif
+ return true;
+ }
+}
+
+public sealed class LnkFile2
+{
+ public string? SourceFile { get; set; }
+ public string? LocalPath { get; set; }
+ public string? WorkingDirectory { get; set; }
+ public string? Arguments { get; set; }
+ public string? Description { get; set; }
+ public string? IconLocation { get; set; }
+ public int WindowStyle { get; set; } = 1;
+}
+
+public static class ArgumentExtension
+{
+ public static string[] ParseArguments(string commandLine)
+ {
+ List args = [];
+ string currentArg = string.Empty;
+ bool inQuotes = false;
+
+ for (int i = 0; i < commandLine.Length; i++)
+ {
+ char c = commandLine[i];
+
+ if (c == '"')
+ {
+ inQuotes = !inQuotes;
+ }
+ else if (c == ' ' && !inQuotes)
+ {
+ if (currentArg != string.Empty)
+ {
+ args.Add(currentArg);
+ currentArg = string.Empty;
+ }
+ }
+ else
+ {
+ currentArg += c;
+ }
+ }
+
+ if (currentArg != string.Empty)
+ {
+ args.Add(currentArg);
+ }
+
+ return [.. args];
+ }
+
+ public static string ToArguments(IEnumerable args)
+ {
+ return string.Join(" ", args?.Select(arg => (arg?.Contains(' ') ?? false) ? $"\"{arg}\"" : arg) ?? []);
+ }
+}