Skip to content

Commit fbc0c39

Browse files
committed
Fix taskbar icon (#1271)
1 parent 1da19b2 commit fbc0c39

File tree

5 files changed

+64
-3
lines changed

5 files changed

+64
-3
lines changed

Daybreak.Shared/Utils/NativeMethods.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ namespace Daybreak.Shared.Utils;
66

77
public static class NativeMethods
88
{
9+
public const uint WM_SETICON = 0x0080;
10+
public const nint ICON_BIG = 1; // 32x32 (taskbar icon)
11+
public const nint ICON_SMALL = 0; // 16x16 (title bar icon)
12+
913
public const int STD_OUTPUT_HANDLE = -11;
1014
public const int ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004;
1115
public const int ENABLE_PROCESSED_OUTPUT = 0x0001;

Daybreak/Daybreak.csproj

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,16 @@
129129
<DaybreakInjectorSources Include="..\Daybreak.Shared\**\*.cs" />
130130
</ItemGroup>
131131

132+
<ItemGroup>
133+
<None Remove="Daybreak.ico" />
134+
</ItemGroup>
135+
136+
<ItemGroup>
137+
<EmbeddedResource Include="Daybreak.ico">
138+
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
139+
</EmbeddedResource>
140+
</ItemGroup>
141+
132142
<!--
133143
This is a workaround to determine where to place the dependency projects
134144
It will first check if PublishDir is set.

Daybreak/Launch/Launcher.Main.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using Daybreak.Shared.Services.Screens;
2+
using Daybreak.Shared.Utils;
23
using Daybreak.Views;
34
using Microsoft.Extensions.DependencyInjection;
45
using Microsoft.Extensions.Logging;
@@ -39,6 +40,7 @@ private static PhotinoBlazorApp CreateMainApp(PhotinoBlazorAppBuilder mainBuilde
3940
.SetSmoothScrollingEnabled(true)
4041
.SetChromeless(true);
4142
app.MainWindow.SetLogVerbosity(0);
43+
app.MainWindow.RegisterWindowCreatedHandler((_, __) => SetupWindowIcon(app));
4244
app.MainWindow.RegisterWindowCreatedHandler((_, __) => SetupRoundedWindows(app));
4345
app.MainWindow.RegisterWindowCreatedHandler((_, __) => SetupBorderless(app));
4446
app.MainWindow.RegisterWindowCreatedHandler((_, __) => StartHostedServices(app, cts));

Daybreak/Launch/Launcher.Shared.cs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,22 @@
88
using Serilog;
99
using Serilog.Events;
1010
using Serilog.Sinks.SystemConsole.Themes;
11+
using System.Drawing;
1112
using System.Extensions.Core;
13+
using System.Reflection;
1214
using System.Runtime.InteropServices;
1315

1416
namespace Daybreak.Launch;
1517

1618
public partial class Launcher
1719
{
20+
private const string IconResourceName = "Daybreak.Daybreak.ico";
21+
1822
public const string OutputTemplate = "[{Timestamp:yyyy-MM-dd HH:mm:ss}] {Level:u4}: [{EnvironmentName}] [{ThreadId}:{ThreadName}] [{SourceContext}]{NewLine}{Message:lj}{NewLine}{Exception}";
1923

2024
private static nint OriginalWndProc;
2125
private static NativeMethods.WndProcDelegate? WndProcDelegate;
26+
private static Icon? WindowIcon;
2227

2328
public static void SetupLogging(IServiceCollection services)
2429
{
@@ -130,6 +135,49 @@ private static void SetupBorderless(PhotinoBlazorApp app)
130135
0x0001 | 0x0002 | 0x0004 | 0x0020); // SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED
131136
}
132137

138+
private static void SetupWindowIcon(PhotinoBlazorApp app)
139+
{
140+
var hwnd = app.MainWindow.WindowHandle;
141+
if (hwnd == IntPtr.Zero)
142+
{
143+
return;
144+
}
145+
146+
var scopedLogger = app.Services.GetRequiredService<ILogger<Launcher>>().CreateScopedLogger();
147+
try
148+
{
149+
var assembly = Assembly.GetExecutingAssembly();
150+
if (assembly.GetManifestResourceInfo(IconResourceName) is not ManifestResourceInfo info)
151+
{
152+
scopedLogger.LogWarning("Icon resource '{IconResourceName}' not found in assembly.", IconResourceName);
153+
return;
154+
}
155+
156+
var embeddedIconStream = Assembly.GetExecutingAssembly().GetManifestResourceStream(IconResourceName);
157+
if (embeddedIconStream is null)
158+
{
159+
scopedLogger.LogWarning("Icon resource '{IconResourceName}' stream is null.", IconResourceName);
160+
return;
161+
}
162+
163+
WindowIcon = new Icon(embeddedIconStream);
164+
var hIcon = WindowIcon.Handle;
165+
if (hIcon is 0)
166+
{
167+
scopedLogger.LogWarning("Failed to get handle for window icon.");
168+
return;
169+
}
170+
171+
NativeMethods.SendMessage(hwnd, NativeMethods.WM_SETICON, NativeMethods.ICON_BIG, hIcon);
172+
NativeMethods.SendMessage(hwnd, NativeMethods.WM_SETICON, NativeMethods.ICON_SMALL, hIcon);
173+
scopedLogger.LogDebug("Window icon set successfully.");
174+
}
175+
catch(Exception ex)
176+
{
177+
scopedLogger.LogError(ex, "Failed to set window icon.");
178+
}
179+
}
180+
133181
private static nint WndProc(nint hwnd, uint msg, nint wParam, nint lParam)
134182
{
135183
if (msg == NativeMethods.WM_NCCALCSIZE && wParam != 0)

Daybreak/Launch/Launcher.cs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,6 @@ public partial class Launcher
2222
[STAThread]
2323
public static void Main(string[] args)
2424
{
25-
#if DEBUG
26-
AllocateAnsiConsole();
27-
#endif
2825
var bootstrap = SetupBootstrap();
2926
LaunchSequence(args, bootstrap);
3027
}

0 commit comments

Comments
 (0)