Fix usage of BizHawk within a folder with a semicolon in it

Semicolons are perfectly valid for path names, but windows disallows dll paths to contain such (presumingly internally using them as path separators like with PATH env var)
A workaround for this is to simply use the "short name path" instead, which does not have a semicolon (but these names aren't guaranteed to exist, and in certain cases if they don't,GetShortPathNameW will just return the long name back, so semicolon still needs to be rechecked)
This commit is contained in:
CasualPokePlayer 2025-02-05 22:53:16 -08:00
parent f382ec7590
commit 1f276242ec
3 changed files with 107 additions and 19 deletions

View File

@ -5,6 +5,7 @@ using System.IO;
using System.Windows.Forms;
using BizHawk.Common;
using BizHawk.Common.StringExtensions;
using BizHawk.Emulation.DiscSystem;
namespace BizHawk.Client.DiscoHawk
@ -23,6 +24,7 @@ namespace BizHawk.Client.DiscoHawk
static Program()
{
// http://www.codeproject.com/Articles/310675/AppDomain-AssemblyResolve-Event-Tips
// in case assembly resolution fails, such as if we moved them into the dll subdirectory, this event handler can reroute to them
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
@ -33,18 +35,14 @@ namespace BizHawk.Client.DiscoHawk
return;
}
// http://www.codeproject.com/Articles/310675/AppDomain-AssemblyResolve-Event-Tips
// this will look in subdirectory "dll" to load pinvoked stuff
var dllDir = Path.Combine(AppContext.BaseDirectory, "dll");
SetDllDirectoryW(dllDir);
try
{
// but before we even try doing that, whack the MOTW from everything in that directory (that's a dll)
// before we load anything from the dll dir, whack the MOTW from everything in that directory (that's a dll)
// otherwise, some people will have crashes at boot-up due to .net security disliking MOTW.
// some people are getting MOTW through a combination of browser used to download bizhawk, and program used to dearchive it
static void RemoveMOTW(string path) => DeleteFileW($"{path}:Zone.Identifier");
var todo = new Queue<DirectoryInfo>(new[] { new DirectoryInfo(dllDir) });
var dllDir = Path.Combine(AppContext.BaseDirectory, "dll");
var todo = new Queue<DirectoryInfo>([ new DirectoryInfo(dllDir) ]);
while (todo.Count != 0)
{
var di = todo.Dequeue();
@ -72,6 +70,48 @@ namespace BizHawk.Client.DiscoHawk
WmImports.ChangeWindowMessageFilter(WM_DROPFILES, WmImports.ChangeWindowMessageFilterFlags.Add);
WmImports.ChangeWindowMessageFilter(WM_COPYDATA, WmImports.ChangeWindowMessageFilterFlags.Add);
WmImports.ChangeWindowMessageFilter(WM_COPYGLOBALDATA, WmImports.ChangeWindowMessageFilterFlags.Add);
// this will look in subdirectory "dll" to load pinvoked stuff
var dllDir = Path.Combine(AppContext.BaseDirectory, "dll");
// windows prohibits a semicolon for SetDllDirectoryW, although such paths are fully valid otherwise
// presumingly windows internally has ; used as a path separator, like with PATH
// or perhaps this is just some legacy junk windows keeps around for backwards compatibility reasons
// we can possibly workaround this by using the "short path name" rather (but this isn't guaranteed to exist)
const string SEMICOLON_IN_DIR_MSG =
"DiscoHawk requires no semicolons within its base directory! DiscoHawk will now close.";
if (dllDir.ContainsOrdinal(';'))
{
var dllShortPathLen = Win32Imports.GetShortPathNameW(dllDir, null, 0);
if (dllShortPathLen == 0)
{
MessageBox.Show(SEMICOLON_IN_DIR_MSG);
return;
}
var dllShortPathBuffer = new char[dllShortPathLen];
dllShortPathLen = Win32Imports.GetShortPathNameW(dllDir, dllShortPathBuffer, dllShortPathLen);
if (dllShortPathLen == 0)
{
MessageBox.Show(SEMICOLON_IN_DIR_MSG);
return;
}
dllDir = new string(dllShortPathBuffer, 0, dllShortPathLen);
if (dllDir.ContainsOrdinal(';'))
{
MessageBox.Show(SEMICOLON_IN_DIR_MSG);
return;
}
}
if (!Win32Imports.SetDllDirectoryW(dllDir))
{
MessageBox.Show(
$"SetDllDirectoryW failed with error code {Marshal.GetLastWin32Error()}, this is fatal. DiscoHawk will now close.");
return;
}
}
// Do something for visuals, I guess

View File

@ -20,10 +20,6 @@ namespace BizHawk.Client.EmuHawk
{
// Declared here instead of a more usual place to avoid dependencies on the more usual place
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool SetDllDirectoryW(string lpPathName);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool DeleteFileW(string lpFileName);
@ -59,18 +55,15 @@ namespace BizHawk.Client.EmuHawk
return;
}
// this will look in subdirectory "dll" to load pinvoked stuff
var dllDir = Path.Combine(AppContext.BaseDirectory, "dll");
_ = SetDllDirectoryW(dllDir);
try
{
// but before we even try doing that, whack the MOTW from everything in that directory (that's a dll)
// before we load anything from the dll dir, whack the MOTW from everything in that directory (that's a dll)
// otherwise, some people will have crashes at boot-up due to .net security disliking MOTW.
// some people are getting MOTW through a combination of browser used to download bizhawk, and program used to dearchive it
// We need to do it here too... otherwise people get exceptions when externaltools we distribute try to startup
static void RemoveMOTW(string path) => DeleteFileW($"{path}:Zone.Identifier");
var todo = new Queue<DirectoryInfo>(new[] { new DirectoryInfo(dllDir) });
var dllDir = Path.Combine(AppContext.BaseDirectory, "dll");
var todo = new Queue<DirectoryInfo>([ new DirectoryInfo(dllDir) ]);
while (todo.Count != 0)
{
var di = todo.Dequeue();
@ -123,8 +116,52 @@ namespace BizHawk.Client.EmuHawk
return -1;
}
string dllDir = null;
if (!OSTailoredCode.IsUnixHost)
{
// this will look in subdirectory "dll" to load pinvoked stuff
// declared above to be re-used later on, see second SetDllDirectoryW call
dllDir = Path.Combine(AppContext.BaseDirectory, "dll");
// windows prohibits a semicolon for SetDllDirectoryW, although such paths are fully valid otherwise
// presumingly windows internally has ; used as a path separator, like with PATH
// or perhaps this is just some legacy junk windows keeps around for backwards compatibility reasons
// we can possibly workaround this by using the "short path name" rather (but this isn't guaranteed to exist)
const string SEMICOLON_IN_DIR_MSG =
"EmuHawk requires no semicolons within its base directory! EmuHawk will now close.";
if (dllDir.ContainsOrdinal(';'))
{
var dllShortPathLen = Win32Imports.GetShortPathNameW(dllDir, null, 0);
if (dllShortPathLen == 0)
{
MessageBox.Show(SEMICOLON_IN_DIR_MSG);
return -1;
}
var dllShortPathBuffer = new char[dllShortPathLen];
dllShortPathLen = Win32Imports.GetShortPathNameW(dllDir, dllShortPathBuffer, dllShortPathLen);
if (dllShortPathLen == 0)
{
MessageBox.Show(SEMICOLON_IN_DIR_MSG);
return -1;
}
dllDir = new string(dllShortPathBuffer, 0, dllShortPathLen);
if (dllDir.ContainsOrdinal(';'))
{
MessageBox.Show(SEMICOLON_IN_DIR_MSG);
return -1;
}
}
if (!Win32Imports.SetDllDirectoryW(dllDir))
{
MessageBox.Show(
$"SetDllDirectoryW failed with error code {Marshal.GetLastWin32Error()}, this is fatal. EmuHawk will now close.");
return -1;
}
// Check if we have the C++ VS2015-2022 redist all in one redist be installed
var p = OSTailoredCode.LinkedLibManager.LoadOrZero("vcruntime140_1.dll");
if (p != IntPtr.Zero)
@ -287,8 +324,12 @@ namespace BizHawk.Client.EmuHawk
// It isn't clear whether we need the earlier SetDllDirectory(), but I think we do.
// note: this is pasted instead of being put in a static method due to this initialization code being sensitive to things like that, and not wanting to cause it to break
// pasting should be safe (not affecting the jit order of things)
var dllDir = Path.Combine(AppContext.BaseDirectory, "dll");
_ = SetDllDirectoryW(dllDir);
if (!Win32Imports.SetDllDirectoryW(dllDir))
{
MessageBox.Show(
$"SetDllDirectoryW failed with error code {Marshal.GetLastWin32Error()}, this is fatal. EmuHawk will now close.");
return -1;
}
}
if (!initialConfig.SkipSuperuserPrivsCheck

View File

@ -74,5 +74,12 @@ namespace BizHawk.Common
[DllImport("kernel32.dll", ExactSpelling = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool IsWow64Process(IntPtr hProcess, [MarshalAs(UnmanagedType.Bool)] out bool Wow64Process);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetDllDirectoryW(string lpPathName);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]
public static extern int GetShortPathNameW(string lpszLongPath, char[] lpszShortPath, int cchBuffer);
}
}