diff --git a/src/BizHawk.Client.DiscoHawk/Program.cs b/src/BizHawk.Client.DiscoHawk/Program.cs index cd4ed36bee..f85d66f0a7 100644 --- a/src/BizHawk.Client.DiscoHawk/Program.cs +++ b/src/BizHawk.Client.DiscoHawk/Program.cs @@ -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(new[] { new DirectoryInfo(dllDir) }); + var dllDir = Path.Combine(AppContext.BaseDirectory, "dll"); + var todo = new Queue([ 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 diff --git a/src/BizHawk.Client.EmuHawk/Program.cs b/src/BizHawk.Client.EmuHawk/Program.cs index 094deac620..dda7f39da6 100644 --- a/src/BizHawk.Client.EmuHawk/Program.cs +++ b/src/BizHawk.Client.EmuHawk/Program.cs @@ -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(new[] { new DirectoryInfo(dllDir) }); + var dllDir = Path.Combine(AppContext.BaseDirectory, "dll"); + var todo = new Queue([ 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 diff --git a/src/BizHawk.Common/Win32/Win32Imports.cs b/src/BizHawk.Common/Win32/Win32Imports.cs index 870f545a86..7bdc80a496 100644 --- a/src/BizHawk.Common/Win32/Win32Imports.cs +++ b/src/BizHawk.Common/Win32/Win32Imports.cs @@ -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); } } \ No newline at end of file