Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 16 additions & 4 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ concurrency:
cancel-in-progress: false

env:
RELEASE_TAG: v1.0.0-beta.1.1
RELEASE_TAG: v1.0.0-beta.1.2

jobs:
build-release:
Expand All @@ -35,6 +35,9 @@ jobs:
- name: Restore
run: dotnet restore .\LocaleGameHub\LocaleGameHub.csproj

- name: Test Windows taskbar and shortcut identities
run: dotnet run --project .\tests\VNAR.ShellTests\VNAR.ShellTests.csproj -c Release

- name: Publish self-contained build
shell: pwsh
run: |
Expand Down Expand Up @@ -135,9 +138,18 @@ jobs:
run: |
$tag = "${{ env.RELEASE_TAG }}"
$notes = @"
## VNAR Beta 1.1
## VNAR Beta 1.2

Fixes the launcher taskbar icon being replaced by a previously opened game shortcut.

- Stable, separate taskbar identity for the VNAR library window.
- Unique persistent identity for each game shortcut, independent of the admin setting.
- Installer shortcuts use the same launcher identity.
- Windows regression tests check process identities and saved shortcut properties.

First public GitHub beta package.
Recreate old game shortcuts to add their explicit identity. If an old pinned VNAR
icon stays cached, unpin it and pin the running Beta 1.2 launcher again.
Existing library data and game launch profiles are unchanged.

### Downloads
- **VNAR-Setup.exe** — self-contained Windows installer.
Expand All @@ -156,7 +168,7 @@ jobs:
.\dist\VNAR-Setup.exe `
.\dist\VNAR-Portable-win-x64.zip `
.\dist\SHA256SUMS.txt `
--title "VNAR Beta 1.1" `
--title "VNAR Beta 1.2" `
--notes $notes `
--prerelease --draft `
--target "${{ github.sha }}"
Expand Down
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
# Changelog

## 1.0.0-beta.1.2

- Fix taskbar icon collisions between the VNAR launcher and game shortcuts targeting the same executable.
- Assign a stable launcher AppUserModelID and a distinct ID per game, before creating windows.
- Persist game IDs in shortcut properties without changing their icons, targets, or arguments.
- Set the launcher ID on installer-created shortcuts.
- Add Windows integration checks for process identity and shortcut persistence, with normal/admin game settings.


## 1.0.0-beta.1.1

First public GitHub beta package.
Expand Down
7 changes: 6 additions & 1 deletion LocaleGameHub/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,15 @@ protected override void OnStartup(StartupEventArgs e)

try
{
var isGameLaunch = TryGetLaunchGameId(e.Args, out var gameId);
// Set the identity before creating any windows, including error dialogs.
// Old shortcuts without an explicit AppID cannot claim the launcher UI.
TaskbarIdentityService.SetCurrentProcessIdentity(isGameLaunch ? gameId : null);

var preferences = new LibraryService();
LocalizationService.SetLanguage(preferences.Settings.Language);

if (TryGetLaunchGameId(e.Args, out var gameId))
if (isGameLaunch)
{
LaunchFromShortcut(preferences, gameId);
Shutdown(0);
Expand Down
2 changes: 1 addition & 1 deletion LocaleGameHub/LocaleGameHub.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<ImplicitUsings>enable</ImplicitUsings>
<AssemblyName>VNAR</AssemblyName>
<RootNamespace>LocaleGameHub</RootNamespace>
<Version>1.0.0-beta.1.1</Version>
<Version>1.0.0-beta.1.2</Version>
<ApplicationManifest>app.manifest</ApplicationManifest>
<ApplicationIcon>Resources\VNAR.ico</ApplicationIcon>
</PropertyGroup>
Expand Down
1 change: 1 addition & 0 deletions LocaleGameHub/Services/ShortcutService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ public static string CreateShortcut(GameEntry game, string shortcutName, string
ReleaseCom(shellObject);
}

TaskbarIdentityService.SetShortcutIdentity(shortcutPath, game.Id);
return shortcutPath;
}

Expand Down
103 changes: 103 additions & 0 deletions LocaleGameHub/Services/TaskbarIdentityService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;

namespace LocaleGameHub.Services;

/// <summary>
/// Prevents Explorer from inferring one shared taskbar identity from VNAR.exe
/// for the library UI and every "--launch" shortcut.
/// </summary>
public static class TaskbarIdentityService
{
// Keep this stable across versions, and in sync with installer/VNAR.iss.
public const string LauncherAppId = "JAVCIF.VNAR.Launcher";

public static string GetGameAppId(Guid gameId) => $"JAVCIF.VNAR.Game.{gameId:N}";

public static void SetCurrentProcessIdentity(Guid? gameId = null)
{
var appId = gameId.HasValue ? GetGameAppId(gameId.Value) : LauncherAppId;
Marshal.ThrowExceptionForHR(SetCurrentProcessExplicitAppUserModelID(appId));
}

public static void SetShortcutIdentity(string shortcutPath, Guid gameId)
{
// WScript.Shell exposes the icon/arguments but not System.AppUserModel.ID.
// Reopen the saved link and change only that property, preserving its icon,
// target, arguments and any existing Shell Link flags.
var linkType = Type.GetTypeFromCLSID(new Guid("00021401-0000-0000-C000-000000000046"), true)!;
var link = Activator.CreateInstance(linkType)
?? throw new InvalidOperationException("Could not open the Shell Link property store.");

try
{
var file = (IPersistFile)link;
file.Load(shortcutPath, 2); // STGM_READWRITE
var store = (IPropertyStore)link;
var key = new PropertyKey
{
FormatId = new Guid("9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3"),
PropertyId = 5 // PKEY_AppUserModel_ID
};
var value = new PropVariant
{
ValueType = 31, // VT_LPWSTR
StringValue = Marshal.StringToCoTaskMemUni(GetGameAppId(gameId))
};

try
{
Marshal.ThrowExceptionForHR(store.SetValue(ref key, ref value));
Marshal.ThrowExceptionForHR(store.Commit());
file.Save(shortcutPath, true);
}
finally
{
Marshal.FreeCoTaskMem(value.StringValue);
}
}
finally
{
Marshal.FinalReleaseComObject(link);
}
}

[DllImport("shell32.dll", CharSet = CharSet.Unicode, ExactSpelling = true)]
private static extern int SetCurrentProcessExplicitAppUserModelID(string appId);

[StructLayout(LayoutKind.Sequential, Pack = 4)]
private struct PropertyKey
{
public Guid FormatId;
public uint PropertyId;
}

// PROPVARIANT's union contains a counted pointer: 16 bytes on x86,
// 24 on x64. Include that member so the native layout is correct on both.
[StructLayout(LayoutKind.Sequential)]
private struct CountedPointer
{
public uint Count;
public IntPtr Pointer;
}

[StructLayout(LayoutKind.Explicit)]
private struct PropVariant
{
[FieldOffset(0)] public ushort ValueType;
[FieldOffset(8)] public IntPtr StringValue;
[FieldOffset(8)] public CountedPointer ArrayValue;
}

[ComImport]
[Guid("886D8EEB-8CF2-4446-8D02-CDBA1DBDCF99")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
private interface IPropertyStore
{
[PreserveSig] int GetCount(out uint count);
[PreserveSig] int GetAt(uint index, out PropertyKey key);
[PreserveSig] int GetValue(ref PropertyKey key, out PropVariant value);
[PreserveSig] int SetValue(ref PropertyKey key, ref PropVariant value);
[PreserveSig] int Commit();
}
}
20 changes: 16 additions & 4 deletions README.es.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

Un launcher ligero para Windows enfocado en visual novels, con integración con Locale Emulator, metadatos de VNDB, edición de portadas, navegación por desarrolladores, favoritos, soporte WebP y accesos directos inteligentes.

[English](README.md) · [Descargar Beta 1.1](https://github.com/JAVCIF/vnar/releases/tag/v1.0.0-beta.1.1)
[English](README.md) · [Descargar Beta 1.2](https://github.com/JAVCIF/vnar/releases/tag/v1.0.0-beta.1.2)
</div>

## Descargas
Expand All @@ -16,8 +16,8 @@ Un launcher ligero para Windows enfocado en visual novels, con integración con

| Paquete | Descripción | Descarga |
| --- | --- | --- |
| Instalador | Instalación autocontenida, acceso en el menú Inicio y acceso de escritorio opcional | [VNAR-Setup.exe](https://github.com/JAVCIF/vnar/releases/download/v1.0.0-beta.1.1/VNAR-Setup.exe) |
| Portable | Versión autocontenida; descomprime y ejecuta `VNAR.exe` | [VNAR-Portable-win-x64.zip](https://github.com/JAVCIF/vnar/releases/download/v1.0.0-beta.1.1/VNAR-Portable-win-x64.zip) |
| Instalador | Instalación autocontenida, acceso en el menú Inicio y acceso de escritorio opcional | [VNAR-Setup.exe](https://github.com/JAVCIF/vnar/releases/download/v1.0.0-beta.1.2/VNAR-Setup.exe) |
| Portable | Versión autocontenida; descomprime y ejecuta `VNAR.exe` | [VNAR-Portable-win-x64.zip](https://github.com/JAVCIF/vnar/releases/download/v1.0.0-beta.1.2/VNAR-Portable-win-x64.zip) |

Locale Emulator **no viene incluido** dentro de VNAR. En el primer arranque puedes seleccionar un `LEProc.exe` existente o dejar que VNAR descargue la versión oficial de Locale Emulator.

Expand Down Expand Up @@ -81,9 +81,21 @@ dotnet run --project .\LocaleGameHub\LocaleGameHub.csproj

También se incluyen `build_portable.bat` y `build_small.bat` para compilaciones locales.

## Iconos de la barra de tareas (Beta 1.2)

VNAR ahora declara su propio AppUserModelID de Windows. Cada acceso de juego tiene
una identidad distinta y estable para que su icono no se asocie a la ventana del
launcher. La opción de administrador sigue cambiando únicamente el perfil de
Locale Emulator.

Los accesos antiguos siguen funcionando. Vuelve a crearlos desde VNAR para añadir
la nueva identidad. Si Windows conserva el icono de un VNAR anclado anteriormente,
desancla ese acceso, abre el `VNAR.exe` actualizado y ancla la ventana nueva.
No necesitas borrar la caché global de iconos ni tus datos.

## Estado del proyecto

Versión pública actual: **Beta 1.1 (`1.0.0-beta.1.1`)**.
Versión pública actual: **Beta 1.2 (`1.0.0-beta.1.2`)**.

Puedes usar GitHub Issues para reportar errores o proponer mejoras.

Expand Down
24 changes: 20 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

A lightweight Windows launcher for visual novels with Locale Emulator integration, VNDB metadata, cover editing, developer browsing, favorites, WebP support, and smart desktop shortcuts.

[Español](README.es.md) · [Download Beta 1.1](https://github.com/JAVCIF/vnar/releases/tag/v1.0.0-beta.1.1)
[Español](README.es.md) · [Download Beta 1.2](https://github.com/JAVCIF/vnar/releases/tag/v1.0.0-beta.1.2)
</div>

## Downloads
Expand All @@ -16,8 +16,8 @@ A lightweight Windows launcher for visual novels with Locale Emulator integratio

| Package | Description | Download |
| --- | --- | --- |
| Installer | Self-contained installation with Start Menu integration and optional desktop shortcut | [VNAR-Setup.exe](https://github.com/JAVCIF/vnar/releases/download/v1.0.0-beta.1.1/VNAR-Setup.exe) |
| Portable | Self-contained portable build; extract and run `VNAR.exe` | [VNAR-Portable-win-x64.zip](https://github.com/JAVCIF/vnar/releases/download/v1.0.0-beta.1.1/VNAR-Portable-win-x64.zip) |
| Installer | Self-contained installation with Start Menu integration and optional desktop shortcut | [VNAR-Setup.exe](https://github.com/JAVCIF/vnar/releases/download/v1.0.0-beta.1.2/VNAR-Setup.exe) |
| Portable | Self-contained portable build; extract and run `VNAR.exe` | [VNAR-Portable-win-x64.zip](https://github.com/JAVCIF/vnar/releases/download/v1.0.0-beta.1.2/VNAR-Portable-win-x64.zip) |

Locale Emulator is **not bundled** with VNAR. On first launch, VNAR can use an existing `LEProc.exe` or download the official Locale Emulator release for you.

Expand Down Expand Up @@ -87,9 +87,25 @@ dotnet publish .\LocaleGameHub\LocaleGameHub.csproj -c Release -r win-x64 --self

The repository also includes `build_portable.bat` and `build_small.bat` for local Windows builds.

## Taskbar icons (Beta 1.2)

VNAR now declares its own Windows AppUserModelID. Game shortcuts use a separate,
stable ID per game, so opening one cannot supply its icon to the VNAR library window.
The administrator option still only selects the Locale Emulator profile.

Existing shortcuts keep working. Recreate them in VNAR to add the new shortcut
identity. If an old pinned VNAR icon remains cached, unpin it, open the updated
`VNAR.exe`, and pin that running window again. No icon-cache reset is required.

Windows shell regression checks:

```powershell
dotnet run --project .\tests\VNAR.ShellTests\VNAR.ShellTests.csproj -c Release
```

## Project status

Current public build: **Beta 1.1 (`1.0.0-beta.1.1`)**.
Current public build: **Beta 1.2 (`1.0.0-beta.1.2`)**.

Bug reports and suggestions are welcome through GitHub Issues.

Expand Down
6 changes: 3 additions & 3 deletions installer/VNAR.iss
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#define MyAppName "VNAR"
#define MyAppVersion "1.0.0-beta.1.1"
#define MyAppVersion "1.0.0-beta.1.2"
#define MyAppPublisher "JAVCIF"
#define MyAppExeName "VNAR.exe"

Expand Down Expand Up @@ -35,8 +35,8 @@ Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{
Source: "..\dist\portable\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs

[Icons]
Name: "{autoprograms}\VNAR"; Filename: "{app}\{#MyAppExeName}"; WorkingDir: "{app}"
Name: "{autodesktop}\VNAR"; Filename: "{app}\{#MyAppExeName}"; WorkingDir: "{app}"; Tasks: desktopicon
Name: "{autoprograms}\VNAR"; Filename: "{app}\{#MyAppExeName}"; WorkingDir: "{app}"; AppUserModelID: "JAVCIF.VNAR.Launcher"
Name: "{autodesktop}\VNAR"; Filename: "{app}\{#MyAppExeName}"; WorkingDir: "{app}"; Tasks: desktopicon; AppUserModelID: "JAVCIF.VNAR.Launcher"

[Run]
Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,VNAR}"; Flags: nowait postinstall skipifsilent
Loading
Loading