Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix WindowsFormsApplicationBase.IsSingleInstance #11258

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ Imports System.IO.Pipes
Imports System.Reflection
Imports System.Runtime.InteropServices
Imports System.Security
Imports System.Security.Cryptography
Imports System.Text
Imports System.Threading
Imports System.Windows.Forms
Imports Microsoft.VisualBasic.CompilerServices
Expand Down Expand Up @@ -354,7 +356,7 @@ Namespace Microsoft.VisualBasic.ApplicationServices
Dim awaitable = SendSecondInstanceArgsAsync(ApplicationInstanceID, commandLine, cancellationToken:=tokenSource.Token).ConfigureAwait(False)
awaitable.GetAwaiter().GetResult()
Catch ex As Exception
Throw New CantStartSingleInstanceException()
Throw New CantStartSingleInstanceException($"{GetResourceString(SR.AppModel_SingleInstanceCantConnect)}: {ApplicationInstanceID}")
End Try
End If
End If 'Single-Instance application
Expand Down Expand Up @@ -1006,28 +1008,35 @@ Namespace Microsoft.VisualBasic.ApplicationServices

''' <summary>
''' Generates the name for the remote singleton that we use to channel multiple instances
''' to the same application model thread.
''' to the same application model thread for the current user.
''' </summary>
''' <returns>A string unique to the application that should be the same for versions of
''' the application that have the same Major and Minor Version Number
''' </returns>
''' <remarks>If GUID Attribute does not exist fall back to unique ModuleVersionId</remarks>
Private Shared Function GetApplicationInstanceID(ByVal Entry As Assembly) As String

Dim guidAttrib As GuidAttribute = Entry.GetCustomAttribute(Of GuidAttribute)()

If guidAttrib IsNot Nothing Then

Dim version As Version = Entry.GetName.Version

Private Shared Function GetApplicationInstanceID(entry As Assembly) As String
Dim guidAttribute As GuidAttribute = entry.GetCustomAttribute(Of GuidAttribute)()
Dim currentUserSID As String = Principal.WindowsIdentity.GetCurrent().User.Value
Dim location As String = entry.Location
Dim pathGuid As Guid = GetFilePathAsGuid(location)
If guidAttribute IsNot Nothing Then
Dim version As Version = entry.GetName.Version
If version IsNot Nothing Then
Return $"{guidAttrib.Value}{version.Major}.{version.Minor}"
Return $"{pathGuid}-{guidAttribute.Value}{version.Major}.{version.Minor}-{currentUserSID}"
Else
Return guidAttrib.Value
Return $"{pathGuid}-{guidAttribute.Value}-{currentUserSID}"
End If
End If

Return Entry.ManifestModule.ModuleVersionId.ToString()
Return $"{pathGuid}-{entry.ManifestModule.ModuleVersionId}-{currentUserSID}"
End Function

#Disable Warning CA5351 ' We want speed over collision resistance.
Private Shared Function GetFilePathAsGuid(filePath As String) As Guid
Dim filePathBytes As Byte() = Encoding.UTF8.GetBytes(filePath)
Dim hashBytes As Byte() = MD5.HashData(filePathBytes)
Return New Guid(hashBytes)
End Function
#Enable Warning CA5351
End Class
End Namespace
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,22 @@ private static string GetAppID(Assembly assembly)
return testAccessor.Dynamic.GetApplicationInstanceID(assembly);
}

private static Guid GetFilePathAsGuid(string filePath)
{
var testAccessor = typeof(WindowsFormsApplicationBase).TestAccessor();
return testAccessor.Dynamic.GetFilePathAsGuid(filePath);
}

[Fact]
public void GetApplicationInstanceID()
{
var assembly = typeof(WindowsFormsApplicationBaseTests).Assembly;
string expectedId = assembly.ManifestModule.ModuleVersionId.ToString();
string currentUserSID = System.Security.Principal.WindowsIdentity.GetCurrent().User.Value;
string expectedId = $"{GetFilePathAsGuid(assembly.Location)}-{assembly.ManifestModule.ModuleVersionId}-{currentUserSID}";
Assert.Equal(expectedId, GetAppID(assembly));
}

private static string GetUniqueIDFromAssembly(string guid, Version version)
private static Assembly CreateDynamicAssembly(string guid, Version version)
{
CustomAttributeBuilder attributeBuilder = new(
typeof(GuidAttribute).GetConstructor([typeof(string)]), new[] { guid });
Expand All @@ -32,27 +39,41 @@ private static string GetUniqueIDFromAssembly(string guid, Version version)
AssemblyBuilderAccess.RunAndCollect,
new[] { attributeBuilder });
assemblyBuilder.DefineDynamicModule(Guid.NewGuid().ToString());
return GetAppID(assemblyBuilder);
return assemblyBuilder;
}

[Fact]
public void GetApplicationInstanceID_GuidAttribute()
{
string guid = Guid.NewGuid().ToString();
Assert.Equal($"{guid}1.2", GetUniqueIDFromAssembly(guid, new Version(1, 2, 3, 4)));
string currentUserSID = System.Security.Principal.WindowsIdentity.GetCurrent().User.Value;
Version version = new Version(1, 2, 3, 4);
var assembly = CreateDynamicAssembly(guid, version);
string location = assembly.Location;
Guid expectedPathGuid = GetFilePathAsGuid(location);
Assert.Equal($"{expectedPathGuid}-{guid}{version.Major}.{version.Minor}-{currentUserSID}", GetAppID(assembly));
}

[Fact]
public void GetApplicationInstanceID_GuidAttributeNewVersion()
{
string guid = Guid.NewGuid().ToString();
Assert.Equal($"{guid}0.0", GetUniqueIDFromAssembly(guid, new Version()));
string currentUserSID = System.Security.Principal.WindowsIdentity.GetCurrent().User.Value;
Version version = new Version();
var assembly = CreateDynamicAssembly(guid, version);
string location = assembly.Location;
Guid expectedPathGuid = GetFilePathAsGuid(location);
Assert.Equal($"{expectedPathGuid}-{guid}0.0-{currentUserSID}", GetAppID(assembly));
}

[Fact]
public void GetApplicationInstanceID_GuidAttributeNullVersion()
{
string guid = Guid.NewGuid().ToString();
Assert.Equal($"{guid}0.0", GetUniqueIDFromAssembly(guid, version: null));
string currentUserSID = System.Security.Principal.WindowsIdentity.GetCurrent().User.Value;
var assembly = CreateDynamicAssembly(guid, null);
string location = assembly.Location;
Guid expectedPathGuid = GetFilePathAsGuid(location);
Assert.Equal($"{expectedPathGuid}-{guid}0.0-{currentUserSID}", GetAppID(assembly));
}
}