improve Program startup, remove the OSTailoredCode duplication hack, use RuntimeInformation.IsOSPlatform for figuring out OS, use AppContext.BaseDirectory to get exe location (apparently even works with bundled assemblies in .net5+)
This commit is contained in:
parent
eca0ca41a9
commit
859ea8e249
|
@ -22,7 +22,7 @@ namespace BizHawk.Bizware.Test
|
|||
{
|
||||
var firstAsm = Array.Find(AppDomain.CurrentDomain.GetAssemblies(), asm => asm.FullName == args.Name);
|
||||
if (firstAsm is not null) return firstAsm;
|
||||
var guessFilename = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!, "dll", $"{new AssemblyName(args.Name).Name}.dll");
|
||||
var guessFilename = Path.Combine(AppContext.BaseDirectory, "dll", $"{new AssemblyName(args.Name).Name}.dll");
|
||||
return File.Exists(guessFilename) ? Assembly.LoadFile(guessFilename) : null;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
<PropertyGroup>
|
||||
<ApplicationIcon>discohawk.ico</ApplicationIcon>
|
||||
<Nullable>disable</Nullable>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
|
|
|
@ -3,74 +3,82 @@ using System.Runtime.InteropServices;
|
|||
using System.Reflection;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using BizHawk.Emulation.DiscSystem;
|
||||
using System.Windows.Forms;
|
||||
|
||||
using OSTC = EXE_PROJECT.OSTailoredCode;
|
||||
using BizHawk.Common;
|
||||
using BizHawk.Emulation.DiscSystem;
|
||||
|
||||
namespace BizHawk.Client.DiscoHawk
|
||||
{
|
||||
internal static class Program
|
||||
{
|
||||
// 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);
|
||||
|
||||
static Program()
|
||||
{
|
||||
if (OSTC.IsUnixHost)
|
||||
// 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;
|
||||
|
||||
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
// for Unix, skip everything else and just wire up the event handler
|
||||
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
|
||||
// Windows needs extra considerations for the dll directory
|
||||
// we can skip all this on non-Windows platforms
|
||||
return;
|
||||
}
|
||||
|
||||
// http://www.codeproject.com/Articles/310675/AppDomain-AssemblyResolve-Event-Tips
|
||||
// this will look in subdirectory "dll" to load pinvoked stuff
|
||||
string dllDir = Path.Combine(GetExeDirectoryAbsolute(), "dll");
|
||||
SetDllDirectory(dllDir);
|
||||
var dllDir = Path.Combine(AppContext.BaseDirectory, "dll");
|
||||
SetDllDirectoryW(dllDir);
|
||||
|
||||
// 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;
|
||||
|
||||
// but before we even try doing that, 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) });
|
||||
while (todo.Count != 0)
|
||||
try
|
||||
{
|
||||
var di = todo.Dequeue();
|
||||
foreach (var diSub in di.GetDirectories()) todo.Enqueue(diSub);
|
||||
foreach (var fi in di.GetFiles("*.dll")) RemoveMOTW(fi.FullName);
|
||||
foreach (var fi in di.GetFiles("*.exe")) RemoveMOTW(fi.FullName);
|
||||
// but before we even try doing that, 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) });
|
||||
while (todo.Count != 0)
|
||||
{
|
||||
var di = todo.Dequeue();
|
||||
foreach (var disub in di.GetDirectories()) todo.Enqueue(disub);
|
||||
foreach (var fi in di.GetFiles("*.dll")) RemoveMOTW(fi.FullName);
|
||||
foreach (var fi in di.GetFiles("*.exe")) RemoveMOTW(fi.FullName);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine($"MotW remover failed: {e}");
|
||||
}
|
||||
}
|
||||
|
||||
[STAThread]
|
||||
private static void Main(string[] args)
|
||||
{
|
||||
SubMain(args);
|
||||
}
|
||||
|
||||
// NoInlining should keep this code from getting jammed into Main() which would create dependencies on types which haven't been setup by the resolver yet... or something like that
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
public static extern bool ChangeWindowMessageFilterEx(IntPtr hWnd, uint msg, ChangeWindowMessageFilterExAction action, ref CHANGEFILTERSTRUCT changeInfo);
|
||||
|
||||
private static void SubMain(string[] args)
|
||||
{
|
||||
if (!OSTC.IsUnixHost)
|
||||
if (!OSTailoredCode.IsUnixHost)
|
||||
{
|
||||
// MICROSOFT BROKE DRAG AND DROP IN WINDOWS 7. IT DOESN'T WORK ANYMORE
|
||||
// WELL, OBVIOUSLY IT DOES SOMETIMES. I DON'T REMEMBER THE DETAILS OR WHY WE HAD TO DO THIS SHIT
|
||||
// BUT THE FUNCTION WE NEED DOESN'T EXIST UNTIL WINDOWS 7, CONVENIENTLY
|
||||
// SO CHECK FOR IT
|
||||
IntPtr lib = OSTC.LinkedLibManager.LoadOrThrow("user32.dll");
|
||||
IntPtr proc = OSTC.LinkedLibManager.GetProcAddrOrZero(lib, "ChangeWindowMessageFilterEx");
|
||||
if (proc != IntPtr.Zero)
|
||||
{
|
||||
ChangeWindowMessageFilter(WM_DROPFILES, ChangeWindowMessageFilterFlags.Add);
|
||||
ChangeWindowMessageFilter(WM_COPYDATA, ChangeWindowMessageFilterFlags.Add);
|
||||
ChangeWindowMessageFilter(0x0049, ChangeWindowMessageFilterFlags.Add);
|
||||
}
|
||||
OSTC.LinkedLibManager.FreeByPtr(lib);
|
||||
const uint WM_DROPFILES = 0x0233;
|
||||
const uint WM_COPYDATA = 0x004A;
|
||||
const uint WM_COPYGLOBALDATA = 0x0049;
|
||||
WmImports.ChangeWindowMessageFilter(WM_DROPFILES, WmImports.ChangeWindowMessageFilterFlags.Add);
|
||||
WmImports.ChangeWindowMessageFilter(WM_COPYDATA, WmImports.ChangeWindowMessageFilterFlags.Add);
|
||||
WmImports.ChangeWindowMessageFilter(WM_COPYGLOBALDATA, WmImports.ChangeWindowMessageFilterFlags.Add);
|
||||
}
|
||||
|
||||
// Do something for visuals, I guess
|
||||
Application.EnableVisualStyles();
|
||||
Application.SetCompatibleTextRenderingDefault(false);
|
||||
|
||||
if (args.Length == 0)
|
||||
{
|
||||
using var dialog = new MainDiscoForm();
|
||||
|
@ -88,61 +96,26 @@ namespace BizHawk.Client.DiscoHawk
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
public static string GetExeDirectoryAbsolute()
|
||||
{
|
||||
return Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
|
||||
}
|
||||
|
||||
/// <remarks>http://www.codeproject.com/Articles/310675/AppDomain-AssemblyResolve-Event-Tips</remarks>
|
||||
private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
|
||||
{
|
||||
var requested = args.Name;
|
||||
|
||||
lock (AppDomain.CurrentDomain)
|
||||
{
|
||||
var asms = AppDomain.CurrentDomain.GetAssemblies();
|
||||
foreach (var asm in asms)
|
||||
if (asm.FullName == args.Name)
|
||||
return asm;
|
||||
|
||||
//load missing assemblies by trying to find them in the dll directory
|
||||
string dllName = $"{new AssemblyName(args.Name).Name}.dll";
|
||||
string directory = Path.Combine(GetExeDirectoryAbsolute(), "dll");
|
||||
string fname = Path.Combine(directory, dllName);
|
||||
return File.Exists(fname) ? Assembly.LoadFile(fname) : null;
|
||||
var firstAsm = Array.Find(AppDomain.CurrentDomain.GetAssemblies(), asm => asm.FullName == requested);
|
||||
if (firstAsm != null)
|
||||
{
|
||||
return firstAsm;
|
||||
}
|
||||
|
||||
// load missing assemblies by trying to find them in the dll directory
|
||||
var dllname = $"{new AssemblyName(requested).Name}.dll";
|
||||
var directory = Path.Combine(AppContext.BaseDirectory, "dll");
|
||||
var fname = Path.Combine(directory, dllname);
|
||||
// it is important that we use LoadFile here and not load from a byte array; otherwise mixed (managed/unmanaged) assemblies can't load
|
||||
return File.Exists(fname) ? Assembly.LoadFile(fname) : null;
|
||||
}
|
||||
}
|
||||
|
||||
//declared here instead of a more usual place to avoid dependencies on the more usual place
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
private static extern bool SetDllDirectory(string lpPathName);
|
||||
|
||||
[DllImport("kernel32.dll", EntryPoint = "DeleteFileW", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true)]
|
||||
private static extern bool DeleteFileW([MarshalAs(UnmanagedType.LPWStr)]string lpFileName);
|
||||
|
||||
private const uint WM_DROPFILES = 0x0233;
|
||||
private const uint WM_COPYDATA = 0x004A;
|
||||
[DllImport("user32")]
|
||||
public static extern bool ChangeWindowMessageFilter(uint msg, ChangeWindowMessageFilterFlags flags);
|
||||
public enum ChangeWindowMessageFilterFlags : uint
|
||||
{
|
||||
Add = 1, Remove = 2
|
||||
}
|
||||
public enum MessageFilterInfo : uint
|
||||
{
|
||||
None = 0, AlreadyAllowed = 1, AlreadyDisAllowed = 2, AllowedHigher = 3
|
||||
}
|
||||
|
||||
public enum ChangeWindowMessageFilterExAction : uint
|
||||
{
|
||||
Reset = 0, Allow = 1, DisAllow = 2
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct CHANGEFILTERSTRUCT
|
||||
{
|
||||
public uint size;
|
||||
public MessageFilterInfo info;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
{
|
||||
try
|
||||
{
|
||||
string execPath = Assembly.GetEntryAssembly().Location;
|
||||
var execPath = AppContext.BaseDirectory;
|
||||
dynamic ji = Activator.CreateInstance(JumpTask);
|
||||
|
||||
ji.ApplicationPath = execPath;
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows.Forms;
|
||||
|
@ -13,60 +14,54 @@ using BizHawk.Common.PathExtensions;
|
|||
using BizHawk.Client.Common;
|
||||
using BizHawk.Client.EmuHawk.CustomControls;
|
||||
|
||||
using OSTC = EXE_PROJECT.OSTailoredCode;
|
||||
|
||||
namespace BizHawk.Client.EmuHawk
|
||||
{
|
||||
internal static class Program
|
||||
{
|
||||
// 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);
|
||||
|
||||
static Program()
|
||||
{
|
||||
//this needs to be done before the warnings/errors show up
|
||||
// This needs to be done before the warnings/errors show up
|
||||
Application.EnableVisualStyles();
|
||||
Application.SetCompatibleTextRenderingDefault(false);
|
||||
|
||||
// quickly check if the user is running this as a 32 bit process somehow
|
||||
// Quickly check if the user is running this as a 32 bit process somehow
|
||||
// TODO: We may want to remove this sometime, EmuHawk should be able to run somewhat as 32 bit if the user really wants to
|
||||
// (There are no longer any hard 64 bit deps, i.e. SlimDX is no longer around)
|
||||
if (!Environment.Is64BitProcess)
|
||||
{
|
||||
using (var box = new ExceptionBox($"EmuHawk requires a 64 bit environment in order to run! EmuHawk will now close.")) box.ShowDialog();
|
||||
Process.GetCurrentProcess().Kill();
|
||||
return;
|
||||
}
|
||||
|
||||
if (OSTC.IsUnixHost)
|
||||
{
|
||||
// for Unix, skip everything else and just wire up the event handler
|
||||
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var (dllToLoad, desc) in new[]
|
||||
{
|
||||
("vcruntime140_1.dll", "Microsoft Visual C++ Redistributable for Visual Studio 2015, 2017 and 2019 (x64)"),
|
||||
("msvcr100.dll", "Microsoft Visual C++ 2010 SP1 Runtime (x64)"), // for Mupen64Plus, and some others
|
||||
})
|
||||
{
|
||||
var p = OSTC.LinkedLibManager.LoadOrZero(dllToLoad);
|
||||
if (p != IntPtr.Zero)
|
||||
{
|
||||
OSTC.LinkedLibManager.FreeByPtr(p);
|
||||
continue;
|
||||
}
|
||||
// else it's missing or corrupted
|
||||
using (ExceptionBox box = new($"EmuHawk needs {desc} in order to run! See the readme on GitHub for more info. (EmuHawk will now close.) Internal error message: {OSTC.LinkedLibManager.GetErrorMessage()}"))
|
||||
using (var box = new ExceptionBox(
|
||||
"EmuHawk requires a 64 bit environment in order to run! EmuHawk will now close."))
|
||||
{
|
||||
box.ShowDialog();
|
||||
}
|
||||
|
||||
Process.GetCurrentProcess().Kill();
|
||||
return;
|
||||
}
|
||||
|
||||
// In case assembly resolution fails, such as if we moved them into the dll subdiretory, this event handler can reroute to them
|
||||
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
|
||||
|
||||
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
// Windows needs extra considerations for the dll directory
|
||||
// we can skip all this on non-Windows platforms
|
||||
return;
|
||||
}
|
||||
|
||||
// this will look in subdirectory "dll" to load pinvoked stuff
|
||||
var dllDir = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "dll");
|
||||
_ = SetDllDirectory(dllDir);
|
||||
|
||||
//in case assembly resolution fails, such as if we moved them into the dll subdiretory, this event handler can reroute to them
|
||||
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
|
||||
var dllDir = Path.Combine(AppContext.BaseDirectory, "dll");
|
||||
_ = SetDllDirectoryW(dllDir);
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -94,7 +89,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
private static int Main(string[] args)
|
||||
{
|
||||
var exitCode = SubMain(args);
|
||||
if (OSTC.IsUnixHost)
|
||||
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
Console.WriteLine("BizHawk has completed its shutdown routines, killing process...");
|
||||
Process.GetCurrentProcess().Kill();
|
||||
|
@ -102,37 +97,60 @@ namespace BizHawk.Client.EmuHawk
|
|||
return exitCode;
|
||||
}
|
||||
|
||||
//NoInlining should keep this code from getting jammed into Main() which would create dependencies on types which havent been setup by the resolver yet... or something like that
|
||||
// NoInlining should keep this code from getting jammed into Main() which would create dependencies on types which havent been setup by the resolver yet... or something like that
|
||||
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
|
||||
private static int SubMain(string[] args)
|
||||
{
|
||||
// this check has to be done VERY early. i stepped through a debug build with wrong .dll versions purposely used,
|
||||
// and there was a TypeLoadException before the first line of SubMain was reached (some static ColorType init?)
|
||||
var thisAsmVer = EmuHawk.ReflectionCache.AsmVersion;
|
||||
foreach (var asmVer in new[]
|
||||
{
|
||||
BizInvoke.ReflectionCache.AsmVersion,
|
||||
Bizware.Audio.ReflectionCache.AsmVersion,
|
||||
Bizware.BizwareGL.ReflectionCache.AsmVersion,
|
||||
Bizware.Graphics.ReflectionCache.AsmVersion,
|
||||
Bizware.Graphics.Controls.ReflectionCache.AsmVersion,
|
||||
Bizware.Input.ReflectionCache.AsmVersion,
|
||||
Client.Common.ReflectionCache.AsmVersion,
|
||||
Common.ReflectionCache.AsmVersion,
|
||||
Emulation.Common.ReflectionCache.AsmVersion,
|
||||
Emulation.Cores.ReflectionCache.AsmVersion,
|
||||
Emulation.DiscSystem.ReflectionCache.AsmVersion,
|
||||
WinForms.Controls.ReflectionCache.AsmVersion,
|
||||
})
|
||||
{
|
||||
if (asmVer != thisAsmVer)
|
||||
var thisAsmVer = ReflectionCache.AsmVersion;
|
||||
if (new[]
|
||||
{
|
||||
MessageBox.Show("One or more of the BizHawk.* assemblies have the wrong version!\n(Did you attempt to update by overwriting an existing install?)");
|
||||
BizInvoke.ReflectionCache.AsmVersion,
|
||||
Bizware.Audio.ReflectionCache.AsmVersion,
|
||||
Bizware.BizwareGL.ReflectionCache.AsmVersion,
|
||||
Bizware.Graphics.ReflectionCache.AsmVersion,
|
||||
Bizware.Graphics.Controls.ReflectionCache.AsmVersion,
|
||||
Bizware.Input.ReflectionCache.AsmVersion,
|
||||
Client.Common.ReflectionCache.AsmVersion,
|
||||
Common.ReflectionCache.AsmVersion,
|
||||
Emulation.Common.ReflectionCache.AsmVersion,
|
||||
Emulation.Cores.ReflectionCache.AsmVersion,
|
||||
Emulation.DiscSystem.ReflectionCache.AsmVersion,
|
||||
WinForms.Controls.ReflectionCache.AsmVersion,
|
||||
}.Any(asmVer => asmVer != thisAsmVer))
|
||||
{
|
||||
MessageBox.Show("One or more of the BizHawk.* assemblies have the wrong version!\n(Did you attempt to update by overwriting an existing install?)");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!OSTailoredCode.IsUnixHost)
|
||||
{
|
||||
// TODO: We optimally should only require the new VS2015-2022 all in one redist be installed
|
||||
// None of this old 2010 runtime garbage
|
||||
// (It's probably as simple as just recompiling old mupen .dlls against newer VS and probably just removing speex completely)
|
||||
foreach (var (dllToLoad, desc) in new[]
|
||||
{
|
||||
("vcruntime140_1.dll", "Microsoft Visual C++ Redistributable for Visual Studio 2015, 2017, 2019, and 2022 (x64)"),
|
||||
// for libspeexdsp.dll, mupen64plus-audio-bkm.dll, mupen64plus-video-glide64.dll, mupen64plus-video-glide64mk2.dll, mupen64plus-video-rice.dll
|
||||
("msvcr100.dll", "Microsoft Visual C++ 2010 SP1 Runtime (x64)"),
|
||||
})
|
||||
{
|
||||
var p = OSTailoredCode.LinkedLibManager.LoadOrZero(dllToLoad);
|
||||
if (p != IntPtr.Zero)
|
||||
{
|
||||
OSTailoredCode.LinkedLibManager.FreeByPtr(p);
|
||||
continue;
|
||||
}
|
||||
|
||||
// else it's missing or corrupted
|
||||
MessageBox.Show($"EmuHawk needs {desc} in order to run! See the readme on GitHub for more info. (EmuHawk will now close.) " +
|
||||
$"Internal error message: {OSTailoredCode.LinkedLibManager.GetErrorMessage()}");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
typeof(Form).GetField(OSTC.IsUnixHost ? "default_icon" : "defaultIcon", BindingFlags.NonPublic | BindingFlags.Static)
|
||||
typeof(Form).GetField(OSTailoredCode.IsUnixHost ? "default_icon" : "defaultIcon", BindingFlags.NonPublic | BindingFlags.Static)!
|
||||
.SetValue(null, Properties.Resources.Logo);
|
||||
|
||||
TempFileManager.Start();
|
||||
|
@ -195,8 +213,8 @@ namespace BizHawk.Client.EmuHawk
|
|||
{
|
||||
// try to fallback on the faster option on Windows
|
||||
// if we're on a Unix platform, there's only 1 fallback here...
|
||||
1 when OSTC.IsUnixHost => (EDispMethod.GdiPlus, "GDI+"),
|
||||
1 or 2 when !OSTC.IsUnixHost => dispMethod == EDispMethod.D3D9
|
||||
1 when OSTailoredCode.IsUnixHost => (EDispMethod.GdiPlus, "GDI+"),
|
||||
1 or 2 when !OSTailoredCode.IsUnixHost => dispMethod == EDispMethod.D3D9
|
||||
? (EDispMethod.OpenGL, "OpenGL")
|
||||
: (EDispMethod.D3D9, "Direct3D9"),
|
||||
_ => (EDispMethod.GdiPlus, "GDI+")
|
||||
|
@ -210,16 +228,16 @@ namespace BizHawk.Client.EmuHawk
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var fallback = ChooseFallback();
|
||||
new ExceptionBox(new Exception($"Initialization of Display Method failed; falling back to {fallback.Name}", ex)).ShowDialog();
|
||||
return TryInitIGL(initialConfig.DispMethod = fallback.Method);
|
||||
var (method, name) = ChooseFallback();
|
||||
new ExceptionBox(new Exception($"Initialization of Display Method failed; falling back to {name}", ex)).ShowDialog();
|
||||
return TryInitIGL(initialConfig.DispMethod = method);
|
||||
}
|
||||
}
|
||||
|
||||
switch (dispMethod)
|
||||
{
|
||||
case EDispMethod.D3D9:
|
||||
if (OSTC.IsUnixHost || OSTC.IsWine)
|
||||
if (OSTailoredCode.IsUnixHost || OSTailoredCode.IsWine)
|
||||
{
|
||||
// possibly sharing config w/ Windows, assume the user wants the not-slow method (but don't change the config)
|
||||
return TryInitIGL(EDispMethod.OpenGL);
|
||||
|
@ -230,17 +248,17 @@ namespace BizHawk.Client.EmuHawk
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var fallback = ChooseFallback();
|
||||
new ExceptionBox(new Exception($"Initialization of Direct3D9 Display Method failed; falling back to {fallback.Name}", ex)).ShowDialog();
|
||||
return TryInitIGL(initialConfig.DispMethod = fallback.Method);
|
||||
var (method, name) = ChooseFallback();
|
||||
new ExceptionBox(new Exception($"Initialization of Direct3D9 Display Method failed; falling back to {name}", ex)).ShowDialog();
|
||||
return TryInitIGL(initialConfig.DispMethod = method);
|
||||
}
|
||||
case EDispMethod.OpenGL:
|
||||
if (!IGL_OpenGL.Available)
|
||||
{
|
||||
// too old to use, need to fallback to something else
|
||||
var fallback = ChooseFallback();
|
||||
new ExceptionBox(new Exception($"Initialization of OpenGL Display Method failed; falling back to {fallback.Name}")).ShowDialog();
|
||||
return TryInitIGL(initialConfig.DispMethod = fallback.Method);
|
||||
var (method, name) = ChooseFallback();
|
||||
new ExceptionBox(new Exception($"Initialization of OpenGL Display Method failed; falling back to {name}")).ShowDialog();
|
||||
return TryInitIGL(initialConfig.DispMethod = method);
|
||||
}
|
||||
var igl = new IGL_OpenGL();
|
||||
// need to have a context active for checking renderer, will be disposed afterwards
|
||||
|
@ -265,24 +283,25 @@ namespace BizHawk.Client.EmuHawk
|
|||
|
||||
Sound globalSound = null;
|
||||
|
||||
if (!OSTC.IsUnixHost)
|
||||
if (!OSTailoredCode.IsUnixHost)
|
||||
{
|
||||
//WHY do we have to do this? some intel graphics drivers (ig7icd64.dll 10.18.10.3304 on an unknown chip on win8.1) are calling SetDllDirectory() for the process, which ruins stuff.
|
||||
//The relevant initialization happened just before in "create IGL context".
|
||||
//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(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "dll");
|
||||
_ = SetDllDirectory(dllDir);
|
||||
// WHY do we have to do this? some intel graphics drivers (ig7icd64.dll 10.18.10.3304 on an unknown chip on win8.1) are calling SetDllDirectory() for the process, which ruins stuff.
|
||||
// The relevant initialization happened just before in "create IGL context".
|
||||
// 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 (OSTC.HostWindowsVersion is null || OSTC.HostWindowsVersion.Value.Version >= OSTC.WindowsVersion._10) // "windows isn't capable of being useful for non-administrators until windows 10" --zeromus
|
||||
if (OSTailoredCode.HostWindowsVersion is null ||
|
||||
OSTailoredCode.HostWindowsVersion.Value.Version >= OSTailoredCode.WindowsVersion._10) // "windows isn't capable of being useful for non-administrators until windows 10" --zeromus
|
||||
{
|
||||
if (EmuHawkUtil.CLRHostHasElevatedPrivileges)
|
||||
{
|
||||
using MsgBox dialog = new(
|
||||
title: "This EmuHawk is privileged",
|
||||
message: $"EmuHawk detected it {(OSTC.IsUnixHost ? "is running as root (Superuser)" : "has Administrator privileges")}.\n"
|
||||
message: $"EmuHawk detected it {(OSTailoredCode.IsUnixHost ? "is running as root (Superuser)" : "has Administrator privileges")}.\n"
|
||||
+ "This is a bad idea.",
|
||||
boxIcon: MessageBoxIcon.Warning);
|
||||
dialog.ShowDialog();
|
||||
|
@ -321,11 +340,10 @@ namespace BizHawk.Client.EmuHawk
|
|||
}
|
||||
initialConfig = ConfigService.Load<Config>(iniPath);
|
||||
initialConfig.ResolveDefaults();
|
||||
// ReSharper disable once AccessToDisposedClosure
|
||||
mf.Config = initialConfig;
|
||||
};
|
||||
// var title = mf.Text;
|
||||
mf.Show();
|
||||
// mf.Text = title;
|
||||
try
|
||||
{
|
||||
exitCode = mf.ProgramRunLoop();
|
||||
|
@ -357,17 +375,9 @@ namespace BizHawk.Client.EmuHawk
|
|||
Input.Instance?.Adapter?.DeInitAll();
|
||||
}
|
||||
|
||||
//return 0 assuming things have gone well, non-zero values could be used as error codes or for scripting purposes
|
||||
// return 0 assuming things have gone well, non-zero values could be used as error codes or for scripting purposes
|
||||
return exitCode;
|
||||
} //SubMain
|
||||
|
||||
//declared here instead of a more usual place to avoid dependencies on the more usual place
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
private static extern uint SetDllDirectory(string lpPathName);
|
||||
|
||||
[DllImport("kernel32.dll", EntryPoint = "DeleteFileW", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true)]
|
||||
private static extern bool DeleteFileW([MarshalAs(UnmanagedType.LPWStr)]string lpFileName);
|
||||
}
|
||||
|
||||
/// <remarks>http://www.codeproject.com/Articles/310675/AppDomain-AssemblyResolve-Event-Tips</remarks>
|
||||
private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
|
||||
|
@ -377,14 +387,16 @@ namespace BizHawk.Client.EmuHawk
|
|||
lock (AppDomain.CurrentDomain)
|
||||
{
|
||||
var firstAsm = Array.Find(AppDomain.CurrentDomain.GetAssemblies(), asm => asm.FullName == requested);
|
||||
if (firstAsm != null) return firstAsm;
|
||||
if (firstAsm != null)
|
||||
{
|
||||
return firstAsm;
|
||||
}
|
||||
|
||||
//load missing assemblies by trying to find them in the dll directory
|
||||
// load missing assemblies by trying to find them in the dll directory
|
||||
var dllname = $"{new AssemblyName(requested).Name}.dll";
|
||||
var directory = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "dll");
|
||||
var simpleName = new AssemblyName(requested).Name;
|
||||
var directory = Path.Combine(AppContext.BaseDirectory, "dll");
|
||||
var fname = Path.Combine(directory, dllname);
|
||||
//it is important that we use LoadFile here and not load from a byte array; otherwise mixed (managed/unmanaged) assemblies can't load
|
||||
// it is important that we use LoadFile here and not load from a byte array; otherwise mixed (managed/unmanaged) assemblies can't load
|
||||
return File.Exists(fname) ? Assembly.LoadFile(fname) : null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
|
||||
using BizHawk.Common.StringExtensions;
|
||||
|
||||
|
@ -200,7 +199,7 @@ namespace BizHawk.Common.PathExtensions
|
|||
}
|
||||
if (OSTailoredCode.IsUnixHost)
|
||||
{
|
||||
var dirPath = ReadPathFromEnvVar("BIZHAWK_HOME") ?? Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
|
||||
var dirPath = ReadPathFromEnvVar("BIZHAWK_HOME") ?? AppContext.BaseDirectory;
|
||||
ExeDirectoryPath = string.IsNullOrEmpty(dirPath) || dirPath == "/" ? string.Empty : dirPath;
|
||||
DllDirectoryPath = Path.Combine(ExeDirectoryPath == string.Empty ? "/" : ExeDirectoryPath, "dll");
|
||||
// yes, this is a lot of extra code to make sure BizHawk can run in `/` on Unix, but I've made up for it by caching these for the program lifecycle --yoshi
|
||||
|
@ -208,8 +207,8 @@ namespace BizHawk.Common.PathExtensions
|
|||
}
|
||||
else
|
||||
{
|
||||
var dirPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
|
||||
DataDirectoryPath = ExeDirectoryPath = string.IsNullOrEmpty(dirPath) ? throw new Exception("failed to get location of executable, very bad things must have happened") : dirPath.RemoveSuffix('\\');
|
||||
var dirPath = AppContext.BaseDirectory;
|
||||
DataDirectoryPath = ExeDirectoryPath = string.IsNullOrEmpty(dirPath) ? throw new("failed to get location of executable, very bad things must have happened") : dirPath.RemoveSuffix('\\');
|
||||
DllDirectoryPath = Path.Combine(ExeDirectoryPath, "dll");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace BizHawk.Common
|
||||
{
|
||||
/// <summary>
|
||||
/// Imports of functions in dlfcn.h
|
||||
/// For Linux, these come from libdl historically
|
||||
/// (Although since glibc 2.34 these are just in libc, with libdl just calling into libc)
|
||||
/// </summary>
|
||||
public static class LinuxDlfcnImports
|
||||
{
|
||||
public const int RTLD_NOW = 2;
|
||||
|
||||
[DllImport("libdl.so.2")]
|
||||
public static extern IntPtr dlopen(string fileName, int flags);
|
||||
|
||||
[DllImport("libdl.so.2")]
|
||||
public static extern int dlclose(IntPtr handle);
|
||||
|
||||
[DllImport("libdl.so.2")]
|
||||
public static extern IntPtr dlsym(IntPtr handle, string symbol);
|
||||
|
||||
[DllImport("libdl.so.2")]
|
||||
public static extern IntPtr dlerror();
|
||||
}
|
||||
}
|
|
@ -1,25 +1,43 @@
|
|||
#pragma warning disable IDE0240
|
||||
#nullable enable
|
||||
#pragma warning restore IDE0240
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
using BizHawk.Common.StringExtensions;
|
||||
|
||||
#if EXE_PROJECT
|
||||
namespace EXE_PROJECT // Use a different namespace so the executable can still use this class' members without an implicit dependency on the BizHawk.Common library, and without resorting to code duplication.
|
||||
#else
|
||||
using static BizHawk.Common.LoaderApiImports;
|
||||
|
||||
namespace BizHawk.Common
|
||||
#endif
|
||||
{
|
||||
public static class OSTailoredCode
|
||||
{
|
||||
/// <remarks>macOS doesn't use <see cref="PlatformID.MacOSX">PlatformID.MacOSX</see></remarks>
|
||||
public static readonly DistinctOS CurrentOS = Environment.OSVersion.Platform == PlatformID.Unix
|
||||
? SimpleSubshell("uname", "-s", "Can't determine OS") == "Darwin" ? DistinctOS.macOS : DistinctOS.Linux
|
||||
: DistinctOS.Windows;
|
||||
public static readonly DistinctOS CurrentOS;
|
||||
public static readonly bool IsUnixHost;
|
||||
|
||||
static OSTailoredCode()
|
||||
{
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
||||
{
|
||||
CurrentOS = DistinctOS.Linux;
|
||||
}
|
||||
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||
{
|
||||
CurrentOS = DistinctOS.macOS;
|
||||
}
|
||||
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
CurrentOS = DistinctOS.Windows;
|
||||
}
|
||||
else if (RuntimeInformation.OSDescription.ToUpperInvariant().Contains("BSD", StringComparison.Ordinal))
|
||||
{
|
||||
CurrentOS = DistinctOS.BSD;
|
||||
}
|
||||
else
|
||||
{
|
||||
CurrentOS = DistinctOS.Unknown;
|
||||
}
|
||||
|
||||
IsUnixHost = CurrentOS != DistinctOS.Windows;
|
||||
}
|
||||
|
||||
private static readonly Lazy<(WindowsVersion, Version?)?> _HostWindowsVersion = new(() =>
|
||||
{
|
||||
|
@ -65,8 +83,6 @@ namespace BizHawk.Common
|
|||
|
||||
public static (WindowsVersion Version, Version? Win10PlusVersion)? HostWindowsVersion => _HostWindowsVersion.Value;
|
||||
|
||||
public static readonly bool IsUnixHost = CurrentOS != DistinctOS.Windows;
|
||||
|
||||
public static bool IsWSL => _isWSL.Value;
|
||||
|
||||
private static readonly Lazy<bool> _isWine = new(() =>
|
||||
|
@ -89,11 +105,13 @@ namespace BizHawk.Common
|
|||
|
||||
public static bool IsWine => _isWine.Value;
|
||||
|
||||
private static readonly Lazy<ILinkedLibManager> _LinkedLibManager = new Lazy<ILinkedLibManager>(() => CurrentOS switch
|
||||
private static readonly Lazy<ILinkedLibManager> _LinkedLibManager = new(() => CurrentOS switch
|
||||
{
|
||||
DistinctOS.Linux => new UnixMonoLLManager(),
|
||||
DistinctOS.macOS => new UnixMonoLLManager(),
|
||||
DistinctOS.Linux => new LinuxLLManager(),
|
||||
DistinctOS.macOS => new PosixLLManager(),
|
||||
DistinctOS.Windows => new WindowsLLManager(),
|
||||
DistinctOS.BSD => new PosixLLManager(),
|
||||
DistinctOS.Unknown => throw new NotSupportedException("Cannot link libraries with Unknown OS"),
|
||||
_ => throw new InvalidOperationException()
|
||||
});
|
||||
|
||||
|
@ -117,65 +135,72 @@ namespace BizHawk.Common
|
|||
string GetErrorMessage();
|
||||
}
|
||||
|
||||
private class UnixMonoLLManager : ILinkedLibManager
|
||||
private class LinuxLLManager : ILinkedLibManager
|
||||
{
|
||||
private const int RTLD_NOW = 2;
|
||||
public int FreeByPtr(IntPtr hModule) => LinuxDlfcnImports.dlclose(hModule);
|
||||
|
||||
[DllImport("libdl.so.2")]
|
||||
private static extern int dlclose(IntPtr handle);
|
||||
|
||||
[DllImport("libdl.so.2")]
|
||||
private static extern IntPtr dlerror();
|
||||
|
||||
[DllImport("libdl.so.2")]
|
||||
private static extern IntPtr dlopen(string fileName, int flags);
|
||||
|
||||
[DllImport("libdl.so.2")]
|
||||
private static extern IntPtr dlsym(IntPtr handle, string symbol);
|
||||
|
||||
public int FreeByPtr(IntPtr hModule) => dlclose(hModule);
|
||||
|
||||
public IntPtr GetProcAddrOrZero(IntPtr hModule, string procName) => dlsym(hModule, procName);
|
||||
public IntPtr GetProcAddrOrZero(IntPtr hModule, string procName) => LinuxDlfcnImports.dlsym(hModule, procName);
|
||||
|
||||
public IntPtr GetProcAddrOrThrow(IntPtr hModule, string procName)
|
||||
{
|
||||
_ = dlerror(); // the Internet said to do this
|
||||
_ = LinuxDlfcnImports.dlerror(); // the Internet said to do this
|
||||
var p = GetProcAddrOrZero(hModule, procName);
|
||||
if (p != IntPtr.Zero) return p;
|
||||
var errCharPtr = dlerror();
|
||||
throw new InvalidOperationException($"error in {nameof(dlsym)}{(errCharPtr == IntPtr.Zero ? string.Empty : $": {Marshal.PtrToStringAnsi(errCharPtr)}")}");
|
||||
throw new InvalidOperationException($"error in dlsym: {GetErrorMessage()}");
|
||||
}
|
||||
|
||||
public IntPtr LoadOrZero(string dllToLoad) => dlopen(dllToLoad, RTLD_NOW);
|
||||
public IntPtr LoadOrZero(string dllToLoad) => LinuxDlfcnImports.dlopen(dllToLoad, LinuxDlfcnImports.RTLD_NOW);
|
||||
|
||||
public IntPtr LoadOrThrow(string dllToLoad)
|
||||
{
|
||||
var ret = LoadOrZero(dllToLoad);
|
||||
return ret != IntPtr.Zero ? ret : throw new InvalidOperationException($"got null pointer from {nameof(dlopen)}, error: {Marshal.PtrToStringAnsi(dlerror())}");
|
||||
return ret != IntPtr.Zero
|
||||
? ret
|
||||
: throw new InvalidOperationException($"got null pointer from dlopen, error: {GetErrorMessage()}");
|
||||
}
|
||||
|
||||
public string GetErrorMessage()
|
||||
{
|
||||
var errCharPtr = dlerror();
|
||||
return errCharPtr == IntPtr.Zero ? "No error present" : Marshal.PtrToStringAnsi(errCharPtr)!;
|
||||
var errCharPtr = LinuxDlfcnImports.dlerror();
|
||||
return errCharPtr == IntPtr.Zero ? "dlerror reported no error" : Marshal.PtrToStringAnsi(errCharPtr)!;
|
||||
}
|
||||
}
|
||||
|
||||
// this is just a copy paste of LinuxLLManager using PosixDlfcnImports instead of LinuxDlfcnImports
|
||||
// TODO: probably could do some OOP magic so there isn't just a copy paste here
|
||||
private class PosixLLManager : ILinkedLibManager
|
||||
{
|
||||
public int FreeByPtr(IntPtr hModule) => PosixDlfcnImports.dlclose(hModule);
|
||||
|
||||
public IntPtr GetProcAddrOrZero(IntPtr hModule, string procName) => PosixDlfcnImports.dlsym(hModule, procName);
|
||||
|
||||
public IntPtr GetProcAddrOrThrow(IntPtr hModule, string procName)
|
||||
{
|
||||
_ = PosixDlfcnImports.dlerror(); // the Internet said to do this
|
||||
var p = GetProcAddrOrZero(hModule, procName);
|
||||
if (p != IntPtr.Zero) return p;
|
||||
throw new InvalidOperationException($"error in dlsym: {GetErrorMessage()}");
|
||||
}
|
||||
|
||||
public IntPtr LoadOrZero(string dllToLoad) => PosixDlfcnImports.dlopen(dllToLoad, PosixDlfcnImports.RTLD_NOW);
|
||||
|
||||
public IntPtr LoadOrThrow(string dllToLoad)
|
||||
{
|
||||
var ret = LoadOrZero(dllToLoad);
|
||||
return ret != IntPtr.Zero
|
||||
? ret
|
||||
: throw new InvalidOperationException($"got null pointer from dlopen, error: {GetErrorMessage()}");
|
||||
}
|
||||
|
||||
public string GetErrorMessage()
|
||||
{
|
||||
var errCharPtr = PosixDlfcnImports.dlerror();
|
||||
return errCharPtr == IntPtr.Zero ? "dlerror reported no error" : Marshal.PtrToStringAnsi(errCharPtr)!;
|
||||
}
|
||||
}
|
||||
|
||||
private class WindowsLLManager : ILinkedLibManager
|
||||
{
|
||||
// functions taken from LoaderApiImports
|
||||
// TODO: Should we apply the same EXE_PROJECT hack to that file?
|
||||
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true, ExactSpelling = true)]
|
||||
private static extern IntPtr LoadLibraryW(string lpLibFileName);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static extern bool FreeLibrary(IntPtr hLibModule);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
|
||||
private static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName);
|
||||
|
||||
public int FreeByPtr(IntPtr hModule) => FreeLibrary(hModule) ? 0 : 1;
|
||||
|
||||
public IntPtr GetProcAddrOrZero(IntPtr hModule, string procName) => GetProcAddress(hModule, procName);
|
||||
|
@ -194,18 +219,12 @@ namespace BizHawk.Common
|
|||
return ret != IntPtr.Zero ? ret : throw new InvalidOperationException($"got null pointer from {nameof(LoadLibraryW)}, {GetErrorMessage()}");
|
||||
}
|
||||
|
||||
[DllImport("kernel32.dll", ExactSpelling = true)]
|
||||
private static extern uint GetLastError();
|
||||
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, ExactSpelling = true)]
|
||||
private static extern unsafe int FormatMessageW(int flags, IntPtr source, uint messageId, uint languageId, char* outMsg, int size, IntPtr args);
|
||||
|
||||
public unsafe string GetErrorMessage()
|
||||
{
|
||||
var errCode = GetLastError();
|
||||
var errCode = Win32Imports.GetLastError();
|
||||
var buffer = stackalloc char[1024];
|
||||
const int FORMAT_MESSAGE_FROM_SYSTEM = 0x1000;
|
||||
var sz = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, IntPtr.Zero, errCode, 0, buffer, 1024, IntPtr.Zero);
|
||||
var sz = Win32Imports.FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, IntPtr.Zero, errCode, 0, buffer, 1024, IntPtr.Zero);
|
||||
return $"error code: 0x{errCode:X8}, error message: {new string(buffer, 0, sz)}";
|
||||
}
|
||||
}
|
||||
|
@ -214,7 +233,9 @@ namespace BizHawk.Common
|
|||
{
|
||||
Linux,
|
||||
macOS,
|
||||
Windows
|
||||
Windows,
|
||||
BSD, // covering all the *BSDs
|
||||
Unknown,
|
||||
}
|
||||
|
||||
public enum WindowsVersion
|
||||
|
@ -234,8 +255,10 @@ namespace BizHawk.Common
|
|||
/// <param name="checkStderr">stderr is discarded if false</param>
|
||||
/// <remarks>OS is implicit and needs to be checked at callsite. Returned <see cref="Process"/> has not been started.</remarks>
|
||||
public static Process ConstructSubshell(string cmd, string args, bool checkStdout = true, bool checkStderr = false)
|
||||
=> new Process {
|
||||
StartInfo = new ProcessStartInfo {
|
||||
=> new()
|
||||
{
|
||||
StartInfo = new()
|
||||
{
|
||||
Arguments = args,
|
||||
CreateNoWindow = true,
|
||||
FileName = cmd,
|
||||
|
@ -257,7 +280,7 @@ namespace BizHawk.Common
|
|||
using var proc = ConstructSubshell(cmd, args);
|
||||
proc.Start();
|
||||
var stdout = proc.StandardOutput;
|
||||
if (stdout.EndOfStream) throw new Exception($"{noOutputMsg} ({cmd} wrote nothing to stdout)");
|
||||
if (stdout.EndOfStream) throw new($"{noOutputMsg} ({cmd} wrote nothing to stdout)");
|
||||
return stdout.ReadLine()!;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace BizHawk.Common
|
||||
{
|
||||
/// <summary>
|
||||
/// Imports of functions in dlfcn.h
|
||||
/// For most POSIX systems, these come from libc
|
||||
/// Linux is a partial exception, as they weren't in libc until glibc 2.34
|
||||
/// (For reference, Debian 10, the current Debian oldoldstable, is on glibc 2.28)
|
||||
/// </summary>
|
||||
public static class PosixDlfcnImports
|
||||
{
|
||||
public const int RTLD_NOW = 2;
|
||||
|
||||
[DllImport("libc")]
|
||||
public static extern IntPtr dlopen(string fileName, int flags);
|
||||
|
||||
[DllImport("libc")]
|
||||
public static extern int dlclose(IntPtr handle);
|
||||
|
||||
[DllImport("libc")]
|
||||
public static extern IntPtr dlsym(IntPtr handle, string symbol);
|
||||
|
||||
[DllImport("libc")]
|
||||
public static extern IntPtr dlerror();
|
||||
}
|
||||
}
|
|
@ -14,13 +14,13 @@ namespace BizHawk.Common
|
|||
Execute = 0x4
|
||||
}
|
||||
|
||||
[DllImport("libc.so.6")]
|
||||
[DllImport("libc")]
|
||||
public static extern IntPtr mmap(IntPtr addr, UIntPtr length, MemoryProtection prot, int flags, int fd, IntPtr offset);
|
||||
|
||||
[DllImport("libc.so.6")]
|
||||
[DllImport("libc")]
|
||||
public static extern int mprotect(IntPtr addr, UIntPtr len, MemoryProtection prot);
|
||||
|
||||
[DllImport("libc.so.6")]
|
||||
[DllImport("libc")]
|
||||
public static extern int munmap(IntPtr addr, UIntPtr length);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
|
||||
using BizHawk.Common.StringExtensions;
|
||||
|
||||
|
@ -24,7 +24,7 @@ namespace BizHawk.Common
|
|||
static VersionInfo()
|
||||
{
|
||||
var path = Path.Combine(
|
||||
Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location)?.RemoveSuffix(Path.DirectorySeparatorChar) ?? string.Empty,
|
||||
AppContext.BaseDirectory.RemoveSuffix(Path.DirectorySeparatorChar),
|
||||
"dll",
|
||||
"custombuild.txt"
|
||||
);
|
||||
|
|
|
@ -51,6 +51,12 @@ namespace BizHawk.Common
|
|||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static extern bool DestroyMenu(IntPtr hMenu);
|
||||
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, ExactSpelling = true)]
|
||||
public static extern unsafe int FormatMessageW(int flags, IntPtr source, uint messageId, uint languageId, char* outMsg, int size, IntPtr args);
|
||||
|
||||
[DllImport("kernel32.dll", ExactSpelling = true)]
|
||||
public static extern uint GetLastError();
|
||||
|
||||
[DllImport("user32.dll", CharSet = CharSet.Unicode, ExactSpelling = true)]
|
||||
public static extern uint MapVirtualKeyW(uint uCode, uint uMapType);
|
||||
|
||||
|
|
|
@ -13,6 +13,12 @@ namespace BizHawk.Common
|
|||
public static readonly IntPtr HWND_MESSAGE = new(-3);
|
||||
public const int GWLP_USERDATA = -21;
|
||||
|
||||
public enum ChangeWindowMessageFilterFlags : uint
|
||||
{
|
||||
Add = 1,
|
||||
Remove = 2,
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct MSG
|
||||
{
|
||||
|
@ -44,6 +50,9 @@ namespace BizHawk.Common
|
|||
public string lpszClassName;
|
||||
}
|
||||
|
||||
[DllImport("user32.dll", ExactSpelling = true)]
|
||||
public static extern bool ChangeWindowMessageFilter(uint msg, ChangeWindowMessageFilterFlags flags);
|
||||
|
||||
[DllImport("user32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]
|
||||
public static extern IntPtr CreateWindowExW(int dwExStyle, IntPtr lpClassName, string lpWindowName,
|
||||
int dwStyle, int X, int Y, int nWidth, int nHeight, IntPtr hWndParent, IntPtr hMenu, IntPtr hInstance, IntPtr lpParam);
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
<Project>
|
||||
<Import Project="MainSlnCommon.props" />
|
||||
<PropertyGroup>
|
||||
<DefineConstants>$(DefineConstants);EXE_PROJECT</DefineConstants>
|
||||
<OutputPath>$(MSBuildProjectDirectory)/../../output</OutputPath>
|
||||
<AssemblyName>$(MSBuildProjectName.Substring($([MSBuild]::Add($(MSBuildProjectName.LastIndexOf('.')), 1))))</AssemblyName>
|
||||
</PropertyGroup>
|
||||
|
@ -11,9 +10,6 @@
|
|||
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
||||
<OutputType>WinExe</OutputType>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="$(MSBuildProjectDirectory)/../BizHawk.Common/OSTailoredCode.cs" />
|
||||
</ItemGroup>
|
||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||
<ItemGroup>
|
||||
<NotExecFilesFromExecProj Include="$(OutputPath)*.deps.json" />
|
||||
|
|
Loading…
Reference in New Issue