mirror of
https://github.com/velopack/velopack.git
synced 2025-10-25 15:19:22 +00:00
Refactor NativeMethods into PlatformUtil class which only exposes helper functions
This commit is contained in:
@@ -102,7 +102,7 @@ namespace Squirrel.CommandLine
|
||||
|
||||
protected static void InvokeAndThrowIfNonZero(string exePath, IEnumerable<string> args, string workingDir)
|
||||
{
|
||||
var result = ProcessUtil.InvokeProcess(exePath, args, workingDir, CancellationToken.None);
|
||||
var result = PlatformUtil.InvokeProcess(exePath, args, workingDir, CancellationToken.None);
|
||||
if (result.ExitCode != 0) {
|
||||
throw new Exception(
|
||||
$"Command failed:\n{result.Command}\n\n" +
|
||||
|
||||
@@ -99,7 +99,7 @@ namespace Squirrel.CommandLine.OSX
|
||||
throw new OptionValidationException("--exeName is required when generating a new app bundle.");
|
||||
|
||||
var mainExePath = Path.Combine(options.packDirectory, exeName);
|
||||
if (!File.Exists(mainExePath) || !Utility.IsMachOImage(mainExePath))
|
||||
if (!File.Exists(mainExePath) || !PlatformUtil.IsMachOImage(mainExePath))
|
||||
throw new OptionValidationException($"--exeName '{mainExePath}' does not exist or is not a mach-o executable.");
|
||||
|
||||
var appleId = $"com.{options.packAuthors ?? options.packId}.{options.packId}";
|
||||
|
||||
@@ -54,10 +54,10 @@ namespace Squirrel.CommandLine.Windows
|
||||
|
||||
List<string> args = new List<string>();
|
||||
args.Add("sign");
|
||||
args.AddRange(NativeMethods.CommandLineToArgvW(signArguments));
|
||||
args.AddRange(PlatformUtil.CommandLineToArgvW(signArguments));
|
||||
args.Add(filePath);
|
||||
|
||||
var result = ProcessUtil.InvokeProcess(SignToolPath, args, null, CancellationToken.None);
|
||||
var result = PlatformUtil.InvokeProcess(SignToolPath, args, null, CancellationToken.None);
|
||||
if (result.ExitCode != 0) {
|
||||
var cmdWithPasswordHidden = new Regex(@"\/p\s+?[^\s]+").Replace(result.Command, "/p ********");
|
||||
throw new Exception(
|
||||
@@ -74,12 +74,12 @@ namespace Squirrel.CommandLine.Windows
|
||||
if (CheckIsAlreadySigned(filePath)) return;
|
||||
|
||||
var command = signTemplate.Replace("\"{{file}}\"", "{{file}}").Replace("{{file}}", $"\"{filePath}\"");
|
||||
var args = NativeMethods.CommandLineToArgvW(command);
|
||||
var args = PlatformUtil.CommandLineToArgvW(command);
|
||||
|
||||
if (args.Length < 2)
|
||||
throw new OptionValidationException("Invalid signing template");
|
||||
|
||||
var result = ProcessUtil.InvokeProcess(args[0], args.Skip(1), null, CancellationToken.None);
|
||||
var result = PlatformUtil.InvokeProcess(args[0], args.Skip(1), null, CancellationToken.None);
|
||||
if (result.ExitCode != 0) {
|
||||
var cmdWithPasswordHidden = new Regex(@"\/p\s+?[^\s]+").Replace(result.Command, "/p ********");
|
||||
throw new Exception(
|
||||
|
||||
@@ -108,8 +108,7 @@ namespace Squirrel
|
||||
}
|
||||
|
||||
if (force) {
|
||||
this.Log().Info($"Killing running processes in '{RootAppDir}'.");
|
||||
Utility.KillProcessesInDirectory(RootAppDir);
|
||||
PlatformUtil.KillProcessesInDirectory(RootAppDir);
|
||||
}
|
||||
|
||||
// 'current' does exist, and it's wrong, so lets get rid of it
|
||||
@@ -192,7 +191,7 @@ namespace Squirrel
|
||||
args.Add(arguments);
|
||||
}
|
||||
|
||||
return ProcessUtil.StartNonBlocking(UpdateExePath, args, Path.GetDirectoryName(UpdateExePath));
|
||||
return PlatformUtil.StartProcessNonBlocking(UpdateExePath, args, Path.GetDirectoryName(UpdateExePath));
|
||||
}
|
||||
|
||||
internal VersionDirInfo GetLatestVersion()
|
||||
@@ -375,11 +374,11 @@ namespace Squirrel
|
||||
/// <inheritdoc />
|
||||
public override bool IsUpdateExe { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string CurrentVersionDir => RootAppDir;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override SemanticVersion CurrentlyInstalledVersion { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string CurrentVersionDir => RootAppDir;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string AppTempDir => CreateSubDirIfDoesNotExist(Utility.GetDefaultTempBaseDirectory(), AppId);
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Versioning;
|
||||
|
||||
namespace Squirrel
|
||||
{
|
||||
[SupportedOSPlatform("osx")]
|
||||
internal static class NativeMac
|
||||
{
|
||||
private const string SystemLib = "libSystem.dylib";
|
||||
|
||||
[DllImport(SystemLib)]
|
||||
public static extern int getppid();
|
||||
|
||||
[DllImport(SystemLib, SetLastError = true)]
|
||||
private static extern int chmod(string pathname, int mode);
|
||||
|
||||
public static void ChmodAsExe(string filePath)
|
||||
{
|
||||
var filePermissionOctal = Convert.ToInt32("777", 8);
|
||||
const int EINTR = 4;
|
||||
int chmodReturnCode = 0;
|
||||
|
||||
do
|
||||
{
|
||||
chmodReturnCode = chmod(filePath, filePermissionOctal);
|
||||
}
|
||||
while (chmodReturnCode == -1 && Marshal.GetLastWin32Error() == EINTR);
|
||||
|
||||
if (chmodReturnCode == -1)
|
||||
{
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error(), $"Could not set file permission {filePermissionOctal} for {filePath}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,391 +0,0 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Versioning;
|
||||
using System.Text;
|
||||
|
||||
namespace Squirrel
|
||||
{
|
||||
[SupportedOSPlatform("windows")]
|
||||
internal static class NativeMethods
|
||||
{
|
||||
public static int GetParentProcessId()
|
||||
{
|
||||
var pbi = new PROCESS_BASIC_INFORMATION();
|
||||
|
||||
//Get a handle to our own process
|
||||
IntPtr hProc = OpenProcess((ProcessAccess) 0x001F0FFF, false, Process.GetCurrentProcess().Id);
|
||||
|
||||
try {
|
||||
int sizeInfoReturned;
|
||||
int queryStatus = NtQueryInformationProcess(hProc, (PROCESSINFOCLASS) 0, ref pbi, pbi.Size, out sizeInfoReturned);
|
||||
} finally {
|
||||
if (!hProc.Equals(IntPtr.Zero)) {
|
||||
//Close handle and free allocated memory
|
||||
CloseHandle(hProc);
|
||||
hProc = IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
return (int) pbi.InheritedFromUniqueProcessId;
|
||||
}
|
||||
|
||||
[DllImport("kernel32.dll", EntryPoint = "LocalFree", SetLastError = true)]
|
||||
private static extern IntPtr _LocalFree(IntPtr hMem);
|
||||
|
||||
[DllImport("shell32.dll", EntryPoint = "CommandLineToArgvW", CharSet = CharSet.Unicode)]
|
||||
private static extern IntPtr _CommandLineToArgvW([MarshalAs(UnmanagedType.LPWStr)] string cmdLine, out int numArgs);
|
||||
|
||||
public static string[] CommandLineToArgvW(string cmdLine)
|
||||
{
|
||||
IntPtr argv = IntPtr.Zero;
|
||||
try {
|
||||
int numArgs = 0;
|
||||
|
||||
argv = _CommandLineToArgvW(cmdLine, out numArgs);
|
||||
if (argv == IntPtr.Zero) {
|
||||
throw new Win32Exception();
|
||||
}
|
||||
var result = new string[numArgs];
|
||||
|
||||
for (int i = 0; i < numArgs; i++) {
|
||||
IntPtr currArg = Marshal.ReadIntPtr(argv, i * Marshal.SizeOf(typeof(IntPtr)));
|
||||
result[i] = Marshal.PtrToStringUni(currArg);
|
||||
}
|
||||
|
||||
return result;
|
||||
} finally {
|
||||
_LocalFree(argv);
|
||||
}
|
||||
}
|
||||
|
||||
[DllImport("shell32.dll", SetLastError = true)]
|
||||
public static extern void SetCurrentProcessExplicitAppUserModelID([MarshalAs(UnmanagedType.LPWStr)] string AppID);
|
||||
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
public static extern IntPtr LoadLibraryEx(string lpModuleName, IntPtr hFile, uint dwFlags);
|
||||
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
public static extern IntPtr FindResource(IntPtr hModule, string lpName, string lpType);
|
||||
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
public static extern IntPtr FindResource(IntPtr hModule, IntPtr lpName, IntPtr lpType);
|
||||
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
public static extern IntPtr FindResource(IntPtr hModule, IntPtr lpName, string lpType);
|
||||
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
public static extern IntPtr FindResourceEx(IntPtr hModule, IntPtr lpType, IntPtr lpName, ushort wLanguage);
|
||||
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
public static extern IntPtr FindResourceEx(IntPtr hModule, string lpType, IntPtr lpName, ushort wLanguage);
|
||||
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
public static extern IntPtr FindResourceEx(IntPtr hModule, string lpType, string lpName, ushort wLanguage);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
public static extern uint SizeofResource(IntPtr hModule, IntPtr handle);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
public static extern IntPtr LoadResource(IntPtr hModule, IntPtr handle);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
public static extern IntPtr LockResource(IntPtr hglobal);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
public static extern bool FreeLibrary(IntPtr hModule);
|
||||
|
||||
[DllImport("version.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool GetFileVersionInfo(
|
||||
string lpszFileName,
|
||||
int dwHandleIgnored,
|
||||
int dwLen,
|
||||
[MarshalAs(UnmanagedType.LPArray)] byte[] lpData);
|
||||
|
||||
[DllImport("version.dll", SetLastError = true)]
|
||||
internal static extern int GetFileVersionInfoSize(
|
||||
string lpszFileName,
|
||||
IntPtr dwHandleIgnored);
|
||||
|
||||
[DllImport("version.dll")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool VerQueryValue(
|
||||
byte[] pBlock,
|
||||
string pSubBlock,
|
||||
out IntPtr pValue,
|
||||
out int len);
|
||||
|
||||
[DllImport("psapi.dll", SetLastError = true)]
|
||||
internal static extern bool EnumProcesses(
|
||||
IntPtr pProcessIds, // pointer to allocated DWORD array
|
||||
int cb,
|
||||
out int pBytesReturned);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
internal static extern bool QueryFullProcessImageName(
|
||||
IntPtr hProcess,
|
||||
[In] int justPassZeroHere,
|
||||
[Out] StringBuilder lpImageFileName,
|
||||
[In][MarshalAs(UnmanagedType.U4)] ref int nSize);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
internal static extern IntPtr OpenProcess(
|
||||
ProcessAccess processAccess,
|
||||
bool bInheritHandle,
|
||||
int processId);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
internal static extern bool CloseHandle(IntPtr hHandle);
|
||||
|
||||
[DllImport("NTDLL.DLL", SetLastError = true)]
|
||||
internal static extern int NtQueryInformationProcess(IntPtr hProcess, PROCESSINFOCLASS pic, ref PROCESS_BASIC_INFORMATION pbi, int cb, out int pSize);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
internal static extern UInt32 WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds);
|
||||
|
||||
[DllImport("kernel32.dll", EntryPoint = "GetStdHandle")]
|
||||
internal static extern IntPtr GetStdHandle(StandardHandles nStdHandle);
|
||||
|
||||
[DllImport("kernel32.dll", EntryPoint = "AllocConsole")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool AllocConsole();
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
internal static extern bool AttachConsole(int pid);
|
||||
|
||||
[DllImport("Kernel32.dll", SetLastError = true)]
|
||||
internal static extern IntPtr BeginUpdateResource(string pFileName, bool bDeleteExistingResources);
|
||||
|
||||
[DllImport("Kernel32.dll", SetLastError = true)]
|
||||
internal static extern bool UpdateResource(IntPtr handle, string pType, IntPtr pName, short language, [MarshalAs(UnmanagedType.LPArray)] byte[] pData, int dwSize);
|
||||
|
||||
[DllImport("Kernel32.dll", SetLastError = true)]
|
||||
internal static extern bool EndUpdateResource(IntPtr handle, bool discard);
|
||||
|
||||
#nullable enable
|
||||
/// <summary>
|
||||
/// The ApplyDelta function use the specified delta and source files to create a new copy of the target file.
|
||||
/// </summary>
|
||||
/// <param name="applyFlags">Either DELTA_FLAG_NONE or DELTA_APPLY_FLAG_ALLOW_PA19.</param>
|
||||
/// <param name="sourceName">The name of the source file to which the delta is to be applied.</param>
|
||||
/// <param name="deltaName">The name of the delta to be applied to the source file.</param>
|
||||
/// <param name="targetName">The name of the target file that is to be created.</param>
|
||||
/// <returns>
|
||||
/// Returns TRUE on success or FALSE otherwise.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// http://msdn.microsoft.com/en-us/library/bb417345.aspx#applydeltaaw
|
||||
/// </remarks>
|
||||
[DllImport("msdelta.dll", CharSet = CharSet.Unicode, SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool ApplyDelta(
|
||||
[MarshalAs(UnmanagedType.I8)] ApplyFlags applyFlags,
|
||||
string sourceName,
|
||||
string deltaName,
|
||||
string targetName);
|
||||
|
||||
/// <summary>
|
||||
/// The CreateDelta function creates a delta from the specified source and target files and write the output delta to the designated file name.
|
||||
/// </summary>
|
||||
/// <param name="fileTypeSet">The file type set used for Create.</param>
|
||||
/// <param name="setFlags">The file type set used for Create.</param>
|
||||
/// <param name="resetFlags">The file type set used for Create.</param>
|
||||
/// <param name="sourceName">The file type set used for Create.</param>
|
||||
/// <param name="targetName">The name of the target against which the source is compared.</param>
|
||||
/// <param name="sourceOptionsName">Reserved. Pass NULL.</param>
|
||||
/// <param name="targetOptionsName">Reserved. Pass NULL.</param>
|
||||
/// <param name="globalOptions">Reserved. Pass a DELTA_INPUT structure with lpStart set to NULL and uSize set to 0.</param>
|
||||
/// <param name="targetFileTime">The time stamp set on the target file after delta Apply. If NULL, the timestamp of the target file during delta Create will be used.</param>
|
||||
/// <param name="hashAlgId">ALG_ID of the algorithm to be used to generate the target signature.</param>
|
||||
/// <param name="deltaName">The name of the delta file to be created.</param>
|
||||
/// <returns>
|
||||
/// Returns TRUE on success or FALSE otherwise.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// http://msdn.microsoft.com/en-us/library/bb417345.aspx#createdeltaaw
|
||||
/// </remarks>
|
||||
[DllImport("msdelta.dll", CharSet = CharSet.Unicode, SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool CreateDelta(
|
||||
[MarshalAs(UnmanagedType.I8)] FileTypeSet fileTypeSet,
|
||||
[MarshalAs(UnmanagedType.I8)] CreateFlags setFlags,
|
||||
[MarshalAs(UnmanagedType.I8)] CreateFlags resetFlags,
|
||||
string sourceName,
|
||||
string targetName,
|
||||
string? sourceOptionsName,
|
||||
string? targetOptionsName,
|
||||
DeltaInput globalOptions,
|
||||
IntPtr targetFileTime,
|
||||
[MarshalAs(UnmanagedType.U4)] HashAlgId hashAlgId,
|
||||
string deltaName);
|
||||
#nullable restore
|
||||
}
|
||||
|
||||
[Flags]
|
||||
internal enum ProcessAccess : uint
|
||||
{
|
||||
All = 0x001F0FFF,
|
||||
Terminate = 0x00000001,
|
||||
CreateThread = 0x00000002,
|
||||
VirtualMemoryOperation = 0x00000008,
|
||||
VirtualMemoryRead = 0x00000010,
|
||||
VirtualMemoryWrite = 0x00000020,
|
||||
DuplicateHandle = 0x00000040,
|
||||
CreateProcess = 0x000000080,
|
||||
SetQuota = 0x00000100,
|
||||
SetInformation = 0x00000200,
|
||||
QueryInformation = 0x00000400,
|
||||
QueryLimitedInformation = 0x00001000,
|
||||
Synchronize = 0x00100000
|
||||
}
|
||||
|
||||
internal enum PROCESSINFOCLASS : int
|
||||
{
|
||||
ProcessBasicInformation = 0, // 0, q: PROCESS_BASIC_INFORMATION, PROCESS_EXTENDED_BASIC_INFORMATION
|
||||
ProcessQuotaLimits, // qs: QUOTA_LIMITS, QUOTA_LIMITS_EX
|
||||
ProcessIoCounters, // q: IO_COUNTERS
|
||||
ProcessVmCounters, // q: VM_COUNTERS, VM_COUNTERS_EX
|
||||
ProcessTimes, // q: KERNEL_USER_TIMES
|
||||
ProcessBasePriority, // s: KPRIORITY
|
||||
ProcessRaisePriority, // s: ULONG
|
||||
ProcessDebugPort, // q: HANDLE
|
||||
ProcessExceptionPort, // s: HANDLE
|
||||
ProcessAccessToken, // s: PROCESS_ACCESS_TOKEN
|
||||
ProcessLdtInformation, // 10
|
||||
ProcessLdtSize,
|
||||
ProcessDefaultHardErrorMode, // qs: ULONG
|
||||
ProcessIoPortHandlers, // (kernel-mode only)
|
||||
ProcessPooledUsageAndLimits, // q: POOLED_USAGE_AND_LIMITS
|
||||
ProcessWorkingSetWatch, // q: PROCESS_WS_WATCH_INFORMATION[]; s: void
|
||||
ProcessUserModeIOPL,
|
||||
ProcessEnableAlignmentFaultFixup, // s: BOOLEAN
|
||||
ProcessPriorityClass, // qs: PROCESS_PRIORITY_CLASS
|
||||
ProcessWx86Information,
|
||||
ProcessHandleCount, // 20, q: ULONG, PROCESS_HANDLE_INFORMATION
|
||||
ProcessAffinityMask, // s: KAFFINITY
|
||||
ProcessPriorityBoost, // qs: ULONG
|
||||
ProcessDeviceMap, // qs: PROCESS_DEVICEMAP_INFORMATION, PROCESS_DEVICEMAP_INFORMATION_EX
|
||||
ProcessSessionInformation, // q: PROCESS_SESSION_INFORMATION
|
||||
ProcessForegroundInformation, // s: PROCESS_FOREGROUND_BACKGROUND
|
||||
ProcessWow64Information, // q: ULONG_PTR
|
||||
ProcessImageFileName, // q: UNICODE_STRING
|
||||
ProcessLUIDDeviceMapsEnabled, // q: ULONG
|
||||
ProcessBreakOnTermination, // qs: ULONG
|
||||
ProcessDebugObjectHandle, // 30, q: HANDLE
|
||||
ProcessDebugFlags, // qs: ULONG
|
||||
ProcessHandleTracing, // q: PROCESS_HANDLE_TRACING_QUERY; s: size 0 disables, otherwise enables
|
||||
ProcessIoPriority, // qs: ULONG
|
||||
ProcessExecuteFlags, // qs: ULONG
|
||||
ProcessResourceManagement,
|
||||
ProcessCookie, // q: ULONG
|
||||
ProcessImageInformation, // q: SECTION_IMAGE_INFORMATION
|
||||
ProcessCycleTime, // q: PROCESS_CYCLE_TIME_INFORMATION
|
||||
ProcessPagePriority, // q: ULONG
|
||||
ProcessInstrumentationCallback, // 40
|
||||
ProcessThreadStackAllocation, // s: PROCESS_STACK_ALLOCATION_INFORMATION, PROCESS_STACK_ALLOCATION_INFORMATION_EX
|
||||
ProcessWorkingSetWatchEx, // q: PROCESS_WS_WATCH_INFORMATION_EX[]
|
||||
ProcessImageFileNameWin32, // q: UNICODE_STRING
|
||||
ProcessImageFileMapping, // q: HANDLE (input)
|
||||
ProcessAffinityUpdateMode, // qs: PROCESS_AFFINITY_UPDATE_MODE
|
||||
ProcessMemoryAllocationMode, // qs: PROCESS_MEMORY_ALLOCATION_MODE
|
||||
ProcessGroupInformation, // q: USHORT[]
|
||||
ProcessTokenVirtualizationEnabled, // s: ULONG
|
||||
ProcessConsoleHostProcess, // q: ULONG_PTR
|
||||
ProcessWindowInformation, // 50, q: PROCESS_WINDOW_INFORMATION
|
||||
ProcessHandleInformation, // q: PROCESS_HANDLE_SNAPSHOT_INFORMATION // since WIN8
|
||||
ProcessMitigationPolicy, // s: PROCESS_MITIGATION_POLICY_INFORMATION
|
||||
ProcessDynamicFunctionTableInformation,
|
||||
ProcessHandleCheckingMode,
|
||||
ProcessKeepAliveCount, // q: PROCESS_KEEPALIVE_COUNT_INFORMATION
|
||||
ProcessRevokeFileHandles, // s: PROCESS_REVOKE_FILE_HANDLES_INFORMATION
|
||||
MaxProcessInfoClass
|
||||
};
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
internal struct PROCESS_BASIC_INFORMATION
|
||||
{
|
||||
public IntPtr ExitStatus;
|
||||
public IntPtr PebBaseAddress;
|
||||
public IntPtr AffinityMask;
|
||||
public IntPtr BasePriority;
|
||||
public UIntPtr UniqueProcessId;
|
||||
public IntPtr InheritedFromUniqueProcessId;
|
||||
|
||||
public int Size {
|
||||
get { return (int) Marshal.SizeOf(typeof(PROCESS_BASIC_INFORMATION)); }
|
||||
}
|
||||
}
|
||||
|
||||
internal enum StandardHandles : int
|
||||
{
|
||||
STD_INPUT_HANDLE = -10,
|
||||
STD_OUTPUT_HANDLE = -11,
|
||||
STD_ERROR_HANDLE = -12,
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
/// http://msdn.microsoft.com/en-us/library/bb417345.aspx#deltaflagtypeflags
|
||||
/// </remarks>
|
||||
internal enum ApplyFlags : long
|
||||
{
|
||||
/// <summary>Indicates no special handling.</summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>Allow MSDelta to apply deltas created using PatchAPI.</summary>
|
||||
AllowLegacy = 1,
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
/// http://msdn.microsoft.com/en-us/library/bb417345.aspx#filetypesets
|
||||
/// </remarks>
|
||||
[Flags]
|
||||
internal enum FileTypeSet : long
|
||||
{
|
||||
/// <summary>
|
||||
/// File type set that includes I386, IA64 and AMD64 Portable Executable (PE) files. Others are treated as raw.
|
||||
/// </summary>
|
||||
Executables = 0x0FL,
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
/// http://msdn.microsoft.com/en-us/library/bb417345.aspx#deltaflagtypeflags
|
||||
/// </remarks>
|
||||
internal enum CreateFlags : long
|
||||
{
|
||||
/// <summary>Indicates no special handling.</summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>Allow the source, target and delta files to exceed the default size limit.</summary>
|
||||
IgnoreFileSizeLimit = 1 << 17,
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
/// http://msdn.microsoft.com/en-us/library/bb417345.aspx#deltainputstructure
|
||||
/// </remarks>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct DeltaInput
|
||||
{
|
||||
/// <summary>Memory address non-editable input buffer.</summary>
|
||||
public IntPtr Start;
|
||||
|
||||
/// <summary>Size of the memory buffer in bytes.</summary>
|
||||
public IntPtr Size;
|
||||
|
||||
/// <summary>
|
||||
/// Defines whether MSDelta is allowed to edit the input buffer. If you make the input editable, the buffer will
|
||||
/// be zeroed at function return. However this will cause most MSDelta functions to use less memory.
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.Bool)] public bool Editable;
|
||||
}
|
||||
|
||||
internal enum HashAlgId
|
||||
{
|
||||
/// <summary>No signature.</summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>32-bit CRC defined in msdelta.dll.</summary>
|
||||
Crc32 = 32,
|
||||
}
|
||||
}
|
||||
529
src/Squirrel/Internal/PlatformUtil.cs
Normal file
529
src/Squirrel/Internal/PlatformUtil.cs
Normal file
@@ -0,0 +1,529 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Versioning;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Squirrel.SimpleSplat;
|
||||
|
||||
namespace Squirrel
|
||||
{
|
||||
internal static class PlatformUtil
|
||||
{
|
||||
static IFullLogger Log => SquirrelLocator.Current.GetService<ILogManager>().GetLogger(typeof(PlatformUtil));
|
||||
|
||||
private const string OSX_CSTD_LIB = "libSystem.dylib";
|
||||
private const string NIX_CSTD_LIB = "libc";
|
||||
private const string WIN_KERNEL32 = "kernel32.dll";
|
||||
private const string WIN_SHELL32 = "shell32.dll";
|
||||
private const string WIN_NTDLL = "NTDLL.DLL";
|
||||
private const string WIN_PSAPI = "psapi.dll";
|
||||
|
||||
[SupportedOSPlatform("linux")]
|
||||
[DllImport(NIX_CSTD_LIB, EntryPoint = "getppid")]
|
||||
private static extern int nix_getppid();
|
||||
|
||||
[SupportedOSPlatform("osx")]
|
||||
[DllImport(OSX_CSTD_LIB, EntryPoint = "getppid")]
|
||||
private static extern int osx_getppid();
|
||||
|
||||
[SupportedOSPlatform("windows")]
|
||||
[DllImport(WIN_KERNEL32)]
|
||||
private static extern IntPtr GetCurrentProcess();
|
||||
|
||||
[SupportedOSPlatform("windows")]
|
||||
[DllImport(WIN_NTDLL, SetLastError = true)]
|
||||
private static extern int NtQueryInformationProcess(IntPtr hProcess, int pic, ref PROCESS_BASIC_INFORMATION pbi, int cb, out int pSize);
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
private struct PROCESS_BASIC_INFORMATION
|
||||
{
|
||||
public nint ExitStatus;
|
||||
public nint PebBaseAddress;
|
||||
public nint AffinityMask;
|
||||
public nint BasePriority;
|
||||
public nuint UniqueProcessId;
|
||||
public nint InheritedFromUniqueProcessId;
|
||||
}
|
||||
|
||||
public static Process GetParentProcess()
|
||||
{
|
||||
int parentId;
|
||||
|
||||
if (SquirrelRuntimeInfo.IsWindows) {
|
||||
var pbi = new PROCESS_BASIC_INFORMATION();
|
||||
NtQueryInformationProcess(GetCurrentProcess(), 0, ref pbi, Marshal.SizeOf(typeof(PROCESS_BASIC_INFORMATION)), out _);
|
||||
parentId = (int) pbi.InheritedFromUniqueProcessId;
|
||||
} else if (SquirrelRuntimeInfo.IsLinux) {
|
||||
parentId = nix_getppid();
|
||||
} else if (SquirrelRuntimeInfo.IsOSX) {
|
||||
parentId = osx_getppid();
|
||||
} else {
|
||||
throw new PlatformNotSupportedException();
|
||||
}
|
||||
|
||||
// the parent process has exited (nix/osx)
|
||||
if (parentId <= 1)
|
||||
return null;
|
||||
|
||||
try {
|
||||
var p = Process.GetProcessById(parentId);
|
||||
|
||||
// the retrieved process is not our parent, the pid has been reused
|
||||
if (p.StartTime > Process.GetCurrentProcess().StartTime)
|
||||
return null;
|
||||
|
||||
return p;
|
||||
} catch (ArgumentException) {
|
||||
// the process has exited (windows)
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static void WaitForParentProcessToExit()
|
||||
{
|
||||
var p = GetParentProcess();
|
||||
if (p == null) {
|
||||
Log.Warn("Will not wait. Parent process has already exited.");
|
||||
return;
|
||||
}
|
||||
|
||||
Log.Info($"Waiting for PID {p.Id} to exit (60s timeout)...");
|
||||
var exited = p.WaitForExit(60_000);
|
||||
if (!exited) {
|
||||
throw new Exception("Parent wait timed out.");
|
||||
}
|
||||
|
||||
Log.Info($"PID {p.Id} has exited.");
|
||||
}
|
||||
|
||||
[SupportedOSPlatform("osx")]
|
||||
[DllImport(OSX_CSTD_LIB, SetLastError = true)]
|
||||
private static extern int osx_chmod(string pathname, int mode);
|
||||
|
||||
[SupportedOSPlatform("linux")]
|
||||
[DllImport(NIX_CSTD_LIB, SetLastError = true)]
|
||||
private static extern int nix_chmod(string pathname, int mode);
|
||||
|
||||
public static void ChmodFileAsExecutable(string filePath)
|
||||
{
|
||||
Func<string, int, int> chmod;
|
||||
|
||||
if (SquirrelRuntimeInfo.IsOSX) chmod = osx_chmod;
|
||||
else if (SquirrelRuntimeInfo.IsLinux) chmod = nix_chmod;
|
||||
else return; // no-op on windows, all .exe files can be executed.
|
||||
|
||||
var filePermissionOctal = Convert.ToInt32("777", 8);
|
||||
const int EINTR = 4;
|
||||
int chmodReturnCode;
|
||||
|
||||
do {
|
||||
chmodReturnCode = chmod(filePath, filePermissionOctal);
|
||||
} while (chmodReturnCode == -1 && Marshal.GetLastWin32Error() == EINTR);
|
||||
|
||||
if (chmodReturnCode == -1) {
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error(), $"Could not set file permission {filePermissionOctal} for {filePath}.");
|
||||
}
|
||||
}
|
||||
|
||||
private enum MagicMachO : uint
|
||||
{
|
||||
MH_MAGIC = 0xfeedface,
|
||||
MH_CIGAM = 0xcefaedfe,
|
||||
MH_MAGIC_64 = 0xfeedfacf,
|
||||
MH_CIGAM_64 = 0xcffaedfe
|
||||
}
|
||||
|
||||
public static bool IsMachOImage(string filePath)
|
||||
{
|
||||
using (BinaryReader reader = new BinaryReader(File.OpenRead(filePath))) {
|
||||
if (reader.BaseStream.Length < 256) // Header size
|
||||
return false;
|
||||
|
||||
uint magic = reader.ReadUInt32();
|
||||
return Enum.IsDefined(typeof(MagicMachO), magic);
|
||||
}
|
||||
}
|
||||
|
||||
[SupportedOSPlatform("windows")]
|
||||
[DllImport(WIN_PSAPI, SetLastError = true)]
|
||||
private static extern bool EnumProcesses(
|
||||
IntPtr pProcessIds, // pointer to allocated DWORD array
|
||||
int cb,
|
||||
out int pBytesReturned);
|
||||
|
||||
[SupportedOSPlatform("windows")]
|
||||
[DllImport(WIN_KERNEL32, SetLastError = true)]
|
||||
private static extern bool QueryFullProcessImageName(
|
||||
IntPtr hProcess,
|
||||
[In] int justPassZeroHere,
|
||||
[Out] StringBuilder lpImageFileName,
|
||||
[In] [MarshalAs(UnmanagedType.U4)] ref int nSize);
|
||||
|
||||
[Flags]
|
||||
private enum ProcessAccess : uint
|
||||
{
|
||||
All = 0x001F0FFF,
|
||||
Terminate = 0x00000001,
|
||||
CreateThread = 0x00000002,
|
||||
VirtualMemoryOperation = 0x00000008,
|
||||
VirtualMemoryRead = 0x00000010,
|
||||
VirtualMemoryWrite = 0x00000020,
|
||||
DuplicateHandle = 0x00000040,
|
||||
CreateProcess = 0x000000080,
|
||||
SetQuota = 0x00000100,
|
||||
SetInformation = 0x00000200,
|
||||
QueryInformation = 0x00000400,
|
||||
QueryLimitedInformation = 0x00001000,
|
||||
Synchronize = 0x00100000
|
||||
}
|
||||
|
||||
[SupportedOSPlatform("windows")]
|
||||
[DllImport(WIN_KERNEL32, SetLastError = true)]
|
||||
private static extern IntPtr OpenProcess(
|
||||
ProcessAccess processAccess,
|
||||
bool bInheritHandle,
|
||||
int processId);
|
||||
|
||||
[SupportedOSPlatform("windows")]
|
||||
[DllImport(WIN_KERNEL32, SetLastError = true)]
|
||||
private static extern bool CloseHandle(IntPtr hHandle);
|
||||
|
||||
[SupportedOSPlatform("windows")]
|
||||
private static List<(string ProcessExePath, int ProcessId)> GetRunningProcessesWindows()
|
||||
{
|
||||
var pids = new int[2048];
|
||||
var gch = GCHandle.Alloc(pids, GCHandleType.Pinned);
|
||||
try {
|
||||
if (!EnumProcesses(gch.AddrOfPinnedObject(), sizeof(int) * pids.Length, out var bytesReturned))
|
||||
throw new Win32Exception("Failed to enumerate processes");
|
||||
|
||||
if (bytesReturned < 1)
|
||||
throw new Exception("Failed to enumerate processes");
|
||||
|
||||
List<(string ProcessExePath, int ProcessId)> ret = new();
|
||||
|
||||
for (int i = 0; i < bytesReturned / sizeof(int); i++) {
|
||||
IntPtr hProcess = IntPtr.Zero;
|
||||
try {
|
||||
hProcess = OpenProcess(ProcessAccess.QueryLimitedInformation, false, pids[i]);
|
||||
if (hProcess == IntPtr.Zero)
|
||||
continue;
|
||||
|
||||
var sb = new StringBuilder(256);
|
||||
var capacity = sb.Capacity;
|
||||
if (!QueryFullProcessImageName(hProcess, 0, sb, ref capacity))
|
||||
continue;
|
||||
|
||||
var exePath = sb.ToString();
|
||||
if (String.IsNullOrWhiteSpace(exePath) || !File.Exists(exePath))
|
||||
continue;
|
||||
|
||||
ret.Add((sb.ToString(), pids[i]));
|
||||
} catch (Exception) {
|
||||
// don't care
|
||||
} finally {
|
||||
if (hProcess != IntPtr.Zero)
|
||||
CloseHandle(hProcess);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
} finally {
|
||||
gch.Free();
|
||||
}
|
||||
}
|
||||
|
||||
public static List<(string ProcessExePath, int ProcessId)> GetRunningProcesses()
|
||||
{
|
||||
IEnumerable<(string ProcessExePath, int ProcessId)> processes = SquirrelRuntimeInfo.IsWindows
|
||||
? GetRunningProcessesWindows()
|
||||
: Process.GetProcesses().Select(p => (p.MainModule?.FileName, p.Id));
|
||||
|
||||
return processes
|
||||
.Where(x => !String.IsNullOrWhiteSpace(x.ProcessExePath)) // Processes we can't query will have an empty process name
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public static void KillProcessesInDirectory(string directoryToKill)
|
||||
{
|
||||
Log.Info("Killing all processes in " + directoryToKill);
|
||||
|
||||
int c = 0;
|
||||
foreach (var x in GetRunningProcesses()) {
|
||||
if (!Utility.IsFileInDirectory(x.ProcessExePath, directoryToKill)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Utility.FullPathEquals(SquirrelRuntimeInfo.EntryExePath, x.ProcessExePath)) {
|
||||
Log.Info($"Skipping '{x.ProcessExePath}' (is current process)");
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
Process.GetProcessById(x.ProcessId).Kill();
|
||||
c++;
|
||||
} catch (Exception ex) {
|
||||
Log.WarnException($"Unable to terminate process (pid.{x.ProcessId})", ex);
|
||||
}
|
||||
}
|
||||
|
||||
Log.Info($"Terminated {c} processes successfully.");
|
||||
}
|
||||
|
||||
[SupportedOSPlatform("windows")]
|
||||
[DllImport(WIN_KERNEL32, EntryPoint = "LocalFree", SetLastError = true)]
|
||||
private static extern IntPtr _LocalFree(IntPtr hMem);
|
||||
|
||||
[SupportedOSPlatform("windows")]
|
||||
[DllImport(WIN_SHELL32, EntryPoint = "CommandLineToArgvW", CharSet = CharSet.Unicode)]
|
||||
private static extern IntPtr _CommandLineToArgvW([MarshalAs(UnmanagedType.LPWStr)] string cmdLine, out int numArgs);
|
||||
|
||||
[SupportedOSPlatform("windows")]
|
||||
public static string[] CommandLineToArgvW(string cmdLine)
|
||||
{
|
||||
IntPtr argv = IntPtr.Zero;
|
||||
try {
|
||||
argv = _CommandLineToArgvW(cmdLine, out var numArgs);
|
||||
if (argv == IntPtr.Zero) {
|
||||
throw new Win32Exception();
|
||||
}
|
||||
var result = new string[numArgs];
|
||||
|
||||
for (int i = 0; i < numArgs; i++) {
|
||||
IntPtr currArg = Marshal.ReadIntPtr(argv, i * Marshal.SizeOf(typeof(IntPtr)));
|
||||
result[i] = Marshal.PtrToStringUni(currArg);
|
||||
}
|
||||
|
||||
return result;
|
||||
} finally {
|
||||
_LocalFree(argv);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* caesay — 09/12/2021 at 12:10 PM
|
||||
* yeah
|
||||
* can I steal this for squirrel?
|
||||
* Roman — 09/12/2021 at 12:10 PM
|
||||
* sure :)
|
||||
* reference CommandRunner.cs on the github url as source? :)
|
||||
* https://github.com/RT-Projects/RT.Util/blob/ef660cd693f66bc946da3aaa368893b03b74eed7/RT.Util.Core/CommandRunner.cs#L327
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// Given a number of argument strings, constructs a single command line string with all the arguments escaped
|
||||
/// correctly so that a process using standard Windows API for parsing the command line will receive exactly the
|
||||
/// strings passed in here. See Remarks.</summary>
|
||||
/// <remarks>
|
||||
/// The string is only valid for passing directly to a process. If the target process is invoked by passing the
|
||||
/// process name + arguments to cmd.exe then further escaping is required, to counteract cmd.exe's interpretation
|
||||
/// of additional special characters. See <see cref="EscapeCmdExeMetachars"/>.</remarks>
|
||||
[SupportedOSPlatform("windows")]
|
||||
private static string ArgsToCommandLine(IEnumerable<string> args)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
foreach (var arg in args) {
|
||||
if (arg == null)
|
||||
continue;
|
||||
if (sb.Length != 0)
|
||||
sb.Append(' ');
|
||||
// For details, see https://web.archive.org/web/20150318010344/http://blogs.msdn.com/b/twistylittlepassagesallalike/archive/2011/04/23/everyone-quotes-arguments-the-wrong-way.aspx
|
||||
// or https://devblogs.microsoft.com/oldnewthing/?p=12833
|
||||
if (arg.Length != 0 && arg.IndexOfAny(_cmdChars) < 0)
|
||||
sb.Append(arg);
|
||||
else {
|
||||
sb.Append('"');
|
||||
for (int c = 0; c < arg.Length; c++) {
|
||||
int backslashes = 0;
|
||||
while (c < arg.Length && arg[c] == '\\') {
|
||||
c++;
|
||||
backslashes++;
|
||||
}
|
||||
if (c == arg.Length) {
|
||||
sb.Append('\\', backslashes * 2);
|
||||
break;
|
||||
} else if (arg[c] == '"') {
|
||||
sb.Append('\\', backslashes * 2 + 1);
|
||||
sb.Append('"');
|
||||
} else {
|
||||
sb.Append('\\', backslashes);
|
||||
sb.Append(arg[c]);
|
||||
}
|
||||
}
|
||||
sb.Append('"');
|
||||
}
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
private static readonly char[] _cmdChars = new[] { ' ', '"', '\n', '\t', '\v' };
|
||||
|
||||
/// <summary>
|
||||
/// Escapes all cmd.exe meta-characters by prefixing them with a ^. See <see cref="ArgsToCommandLine"/> for more
|
||||
/// information.</summary>
|
||||
[SupportedOSPlatform("windows")]
|
||||
private static string EscapeCmdExeMetachars(string command)
|
||||
{
|
||||
var result = new StringBuilder();
|
||||
foreach (var ch in command) {
|
||||
switch (ch) {
|
||||
case '(':
|
||||
case ')':
|
||||
case '%':
|
||||
case '!':
|
||||
case '^':
|
||||
case '"':
|
||||
case '<':
|
||||
case '>':
|
||||
case '&':
|
||||
case '|':
|
||||
result.Append('^');
|
||||
break;
|
||||
}
|
||||
result.Append(ch);
|
||||
}
|
||||
return result.ToString();
|
||||
}
|
||||
|
||||
private static string ArgsToCommandLineUnix(IEnumerable<string> args)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
foreach (var arg in args) {
|
||||
if (arg == null)
|
||||
continue;
|
||||
if (sb.Length != 0)
|
||||
sb.Append(' ');
|
||||
|
||||
// there are just too many 'command chars' in unix, so we play it
|
||||
// super safe here and escape the string if there are any non-alpha-numeric
|
||||
if (System.Text.RegularExpressions.Regex.IsMatch(arg, @"$[\w]^")) {
|
||||
sb.Append(arg);
|
||||
} else {
|
||||
// https://stackoverflow.com/a/33949338/184746
|
||||
// single quotes are 'strong quotes' and can contain everything
|
||||
// except never other single quotes.
|
||||
sb.Append("'");
|
||||
sb.Append(arg.Replace("'", @"'\''"));
|
||||
sb.Append("'");
|
||||
}
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private static ProcessStartInfo CreateProcessStartInfo(string fileName, string workingDirectory)
|
||||
{
|
||||
var psi = new ProcessStartInfo(fileName);
|
||||
psi.UseShellExecute = false;
|
||||
psi.WindowStyle = ProcessWindowStyle.Hidden;
|
||||
psi.ErrorDialog = false;
|
||||
psi.CreateNoWindow = true;
|
||||
psi.RedirectStandardOutput = true;
|
||||
psi.RedirectStandardError = true;
|
||||
psi.WorkingDirectory = workingDirectory ?? Environment.CurrentDirectory;
|
||||
return psi;
|
||||
}
|
||||
|
||||
private static (ProcessStartInfo StartInfo, string CommandDisplayString) CreateProcessStartInfo(string fileName, IEnumerable<string> args, string workingDirectory)
|
||||
{
|
||||
var psi = CreateProcessStartInfo(fileName, workingDirectory);
|
||||
|
||||
string displayArgs;
|
||||
|
||||
#if NET5_0_OR_GREATER
|
||||
foreach (var a in args) psi.ArgumentList.Add(a);
|
||||
displayArgs = $"['{String.Join("', '", args)}']";
|
||||
#else
|
||||
psi.Arguments = displayArgs = SquirrelRuntimeInfo.IsWindows ? ArgsToCommandLine(args) : ArgsToCommandLineUnix(args);
|
||||
#endif
|
||||
|
||||
return (psi, fileName + " " + displayArgs);
|
||||
}
|
||||
|
||||
private static (int ExitCode, string StdOutput) InvokeProcess(ProcessStartInfo psi, CancellationToken ct)
|
||||
{
|
||||
var pi = Process.Start(psi);
|
||||
while (!ct.IsCancellationRequested) {
|
||||
if (pi.WaitForExit(500)) break;
|
||||
}
|
||||
|
||||
if (ct.IsCancellationRequested && !pi.HasExited) {
|
||||
pi.Kill();
|
||||
ct.ThrowIfCancellationRequested();
|
||||
}
|
||||
|
||||
string output = pi.StandardOutput.ReadToEnd();
|
||||
string error = pi.StandardError.ReadToEnd();
|
||||
var all = (output ?? "") + Environment.NewLine + (error ?? "");
|
||||
|
||||
return (pi.ExitCode, all.Trim());
|
||||
}
|
||||
|
||||
public static Process StartProcessNonBlocking(string fileName, IEnumerable<string> args, string workingDirectory)
|
||||
{
|
||||
var (psi, cmd) = CreateProcessStartInfo(fileName, args, workingDirectory);
|
||||
return Process.Start(psi);
|
||||
}
|
||||
|
||||
public static (int ExitCode, string StdOutput, string Command) InvokeProcess(string fileName, IEnumerable<string> args, string workingDirectory, CancellationToken ct)
|
||||
{
|
||||
var (psi, cmd) = CreateProcessStartInfo(fileName, args, workingDirectory);
|
||||
var p = InvokeProcess(psi, ct);
|
||||
return (p.ExitCode, p.StdOutput, cmd);
|
||||
}
|
||||
|
||||
//public static (int ExitCode, string StdOutput, string Command) InvokeProcess(string fileName, string args, string workingDirectory, CancellationToken ct)
|
||||
//{
|
||||
// var psi = CreateProcessStartInfo(fileName, workingDirectory);
|
||||
// psi.Arguments = args;
|
||||
// var p = InvokeProcess(psi, ct);
|
||||
// return (p.ExitCode, p.StdOutput, fileName + " " + args);
|
||||
//}
|
||||
|
||||
public static Task<(int ExitCode, string StdOutput, string Command)> InvokeProcessAsync(string fileName, IEnumerable<string> args, string workingDirectory, CancellationToken ct)
|
||||
{
|
||||
return Task.Run(() => InvokeProcess(fileName, args, workingDirectory, ct));
|
||||
}
|
||||
|
||||
private enum StandardHandles : int
|
||||
{
|
||||
STD_INPUT_HANDLE = -10,
|
||||
STD_OUTPUT_HANDLE = -11,
|
||||
STD_ERROR_HANDLE = -12,
|
||||
}
|
||||
|
||||
[SupportedOSPlatform("windows")]
|
||||
[DllImport(WIN_KERNEL32, EntryPoint = "GetStdHandle")]
|
||||
private static extern IntPtr GetStdHandle(StandardHandles nStdHandle);
|
||||
|
||||
[SupportedOSPlatform("windows")]
|
||||
[DllImport(WIN_KERNEL32, EntryPoint = "AllocConsole")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static extern bool AllocConsole();
|
||||
|
||||
[SupportedOSPlatform("windows")]
|
||||
[DllImport(WIN_KERNEL32)]
|
||||
private static extern bool AttachConsole(int pid);
|
||||
|
||||
static int consoleCreated = 0;
|
||||
|
||||
[SupportedOSPlatform("windows")]
|
||||
public static void AttachConsole()
|
||||
{
|
||||
if (Environment.OSVersion.Platform != PlatformID.Win32NT) return;
|
||||
|
||||
if (Interlocked.CompareExchange(ref consoleCreated, 1, 0) == 1) return;
|
||||
|
||||
if (!AttachConsole(-1)) {
|
||||
AllocConsole();
|
||||
}
|
||||
|
||||
GetStdHandle(StandardHandles.STD_ERROR_HANDLE);
|
||||
GetStdHandle(StandardHandles.STD_OUTPUT_HANDLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,197 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Runtime.Versioning;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Squirrel
|
||||
{
|
||||
internal static class ProcessUtil
|
||||
{
|
||||
/*
|
||||
* caesay — 09/12/2021 at 12:10 PM
|
||||
* yeah
|
||||
* can I steal this for squirrel?
|
||||
* Roman — 09/12/2021 at 12:10 PM
|
||||
* sure :)
|
||||
* reference CommandRunner.cs on the github url as source? :)
|
||||
* https://github.com/RT-Projects/RT.Util/blob/ef660cd693f66bc946da3aaa368893b03b74eed7/RT.Util.Core/CommandRunner.cs#L327
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// Given a number of argument strings, constructs a single command line string with all the arguments escaped
|
||||
/// correctly so that a process using standard Windows API for parsing the command line will receive exactly the
|
||||
/// strings passed in here. See Remarks.</summary>
|
||||
/// <remarks>
|
||||
/// The string is only valid for passing directly to a process. If the target process is invoked by passing the
|
||||
/// process name + arguments to cmd.exe then further escaping is required, to counteract cmd.exe's interpretation
|
||||
/// of additional special characters. See <see cref="EscapeCmdExeMetachars"/>.</remarks>
|
||||
[SupportedOSPlatform("windows")]
|
||||
private static string ArgsToCommandLine(IEnumerable<string> args)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
foreach (var arg in args) {
|
||||
if (arg == null)
|
||||
continue;
|
||||
if (sb.Length != 0)
|
||||
sb.Append(' ');
|
||||
// For details, see https://web.archive.org/web/20150318010344/http://blogs.msdn.com/b/twistylittlepassagesallalike/archive/2011/04/23/everyone-quotes-arguments-the-wrong-way.aspx
|
||||
// or https://devblogs.microsoft.com/oldnewthing/?p=12833
|
||||
if (arg.Length != 0 && arg.IndexOfAny(_cmdChars) < 0)
|
||||
sb.Append(arg);
|
||||
else {
|
||||
sb.Append('"');
|
||||
for (int c = 0; c < arg.Length; c++) {
|
||||
int backslashes = 0;
|
||||
while (c < arg.Length && arg[c] == '\\') {
|
||||
c++;
|
||||
backslashes++;
|
||||
}
|
||||
if (c == arg.Length) {
|
||||
sb.Append('\\', backslashes * 2);
|
||||
break;
|
||||
} else if (arg[c] == '"') {
|
||||
sb.Append('\\', backslashes * 2 + 1);
|
||||
sb.Append('"');
|
||||
} else {
|
||||
sb.Append('\\', backslashes);
|
||||
sb.Append(arg[c]);
|
||||
}
|
||||
}
|
||||
sb.Append('"');
|
||||
}
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
private static readonly char[] _cmdChars = new[] { ' ', '"', '\n', '\t', '\v' };
|
||||
|
||||
/// <summary>
|
||||
/// Escapes all cmd.exe meta-characters by prefixing them with a ^. See <see cref="ArgsToCommandLine"/> for more
|
||||
/// information.</summary>
|
||||
[SupportedOSPlatform("windows")]
|
||||
private static string EscapeCmdExeMetachars(string command)
|
||||
{
|
||||
var result = new StringBuilder();
|
||||
foreach (var ch in command) {
|
||||
switch (ch) {
|
||||
case '(':
|
||||
case ')':
|
||||
case '%':
|
||||
case '!':
|
||||
case '^':
|
||||
case '"':
|
||||
case '<':
|
||||
case '>':
|
||||
case '&':
|
||||
case '|':
|
||||
result.Append('^');
|
||||
break;
|
||||
}
|
||||
result.Append(ch);
|
||||
}
|
||||
return result.ToString();
|
||||
}
|
||||
|
||||
private static string ArgsToCommandLineUnix(IEnumerable<string> args)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
foreach (var arg in args) {
|
||||
if (arg == null)
|
||||
continue;
|
||||
if (sb.Length != 0)
|
||||
sb.Append(' ');
|
||||
|
||||
// there are just too many 'command chars' in unix, so we play it
|
||||
// super safe here and escape the string if there are any non-alpha-numeric
|
||||
if (System.Text.RegularExpressions.Regex.IsMatch(arg, @"$[\w]^")) {
|
||||
sb.Append(arg);
|
||||
} else {
|
||||
// https://stackoverflow.com/a/33949338/184746
|
||||
// single quotes are 'strong quotes' and can contain everything
|
||||
// except never other single quotes.
|
||||
sb.Append("'");
|
||||
sb.Append(arg.Replace("'", @"'\''"));
|
||||
sb.Append("'");
|
||||
}
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private static ProcessStartInfo CreateProcessStartInfo(string fileName, string workingDirectory)
|
||||
{
|
||||
var psi = new ProcessStartInfo(fileName);
|
||||
psi.UseShellExecute = false;
|
||||
psi.WindowStyle = ProcessWindowStyle.Hidden;
|
||||
psi.ErrorDialog = false;
|
||||
psi.CreateNoWindow = true;
|
||||
psi.RedirectStandardOutput = true;
|
||||
psi.RedirectStandardError = true;
|
||||
psi.WorkingDirectory = workingDirectory ?? Environment.CurrentDirectory;
|
||||
return psi;
|
||||
}
|
||||
|
||||
private static (ProcessStartInfo StartInfo, string CommandDisplayString) CreateProcessStartInfo(string fileName, IEnumerable<string> args, string workingDirectory)
|
||||
{
|
||||
var psi = CreateProcessStartInfo(fileName, workingDirectory);
|
||||
|
||||
string displayArgs;
|
||||
|
||||
#if NET5_0_OR_GREATER
|
||||
foreach (var a in args) psi.ArgumentList.Add(a);
|
||||
displayArgs = $"['{String.Join("', '", args)}']";
|
||||
#else
|
||||
psi.Arguments = displayArgs = SquirrelRuntimeInfo.IsWindows ? ArgsToCommandLine(args) : ArgsToCommandLineUnix(args);
|
||||
#endif
|
||||
|
||||
return (psi, fileName + " " + displayArgs);
|
||||
}
|
||||
|
||||
private static (int ExitCode, string StdOutput) InvokeProcess(ProcessStartInfo psi, CancellationToken ct)
|
||||
{
|
||||
var pi = Process.Start(psi);
|
||||
while (!ct.IsCancellationRequested) {
|
||||
if (pi.WaitForExit(500)) break;
|
||||
}
|
||||
|
||||
if (ct.IsCancellationRequested && !pi.HasExited) {
|
||||
pi.Kill();
|
||||
ct.ThrowIfCancellationRequested();
|
||||
}
|
||||
|
||||
string output = pi.StandardOutput.ReadToEnd();
|
||||
string error = pi.StandardError.ReadToEnd();
|
||||
var all = (output ?? "") + Environment.NewLine + (error ?? "");
|
||||
|
||||
return (pi.ExitCode, all.Trim());
|
||||
}
|
||||
|
||||
public static Process StartNonBlocking(string fileName, IEnumerable<string> args, string workingDirectory)
|
||||
{
|
||||
var (psi, cmd) = CreateProcessStartInfo(fileName, args, workingDirectory);
|
||||
return Process.Start(psi);
|
||||
}
|
||||
|
||||
public static (int ExitCode, string StdOutput, string Command) InvokeProcess(string fileName, IEnumerable<string> args, string workingDirectory, CancellationToken ct)
|
||||
{
|
||||
var (psi, cmd) = CreateProcessStartInfo(fileName, args, workingDirectory);
|
||||
var p = InvokeProcess(psi, ct);
|
||||
return (p.ExitCode, p.StdOutput, cmd);
|
||||
}
|
||||
|
||||
//public static (int ExitCode, string StdOutput, string Command) InvokeProcess(string fileName, string args, string workingDirectory, CancellationToken ct)
|
||||
//{
|
||||
// var psi = CreateProcessStartInfo(fileName, workingDirectory);
|
||||
// psi.Arguments = args;
|
||||
// var p = InvokeProcess(psi, ct);
|
||||
// return (p.ExitCode, p.StdOutput, fileName + " " + args);
|
||||
//}
|
||||
|
||||
public static Task<(int ExitCode, string StdOutput, string Command)> InvokeProcessAsync(string fileName, IEnumerable<string> args, string workingDirectory, CancellationToken ct)
|
||||
{
|
||||
return Task.Run(() => InvokeProcess(fileName, args, workingDirectory, ct));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,13 +2,45 @@
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Versioning;
|
||||
using static Squirrel.NativeMethods;
|
||||
|
||||
namespace Squirrel.Lib
|
||||
{
|
||||
[SupportedOSPlatform("windows")]
|
||||
internal class ResourceReader : IDisposable
|
||||
{
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
private static extern IntPtr LoadLibraryEx(string lpModuleName, IntPtr hFile, uint dwFlags);
|
||||
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
private static extern IntPtr FindResource(IntPtr hModule, string lpName, string lpType);
|
||||
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
private static extern IntPtr FindResource(IntPtr hModule, IntPtr lpName, IntPtr lpType);
|
||||
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
private static extern IntPtr FindResource(IntPtr hModule, IntPtr lpName, string lpType);
|
||||
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
private static extern IntPtr FindResourceEx(IntPtr hModule, IntPtr lpType, IntPtr lpName, ushort wLanguage);
|
||||
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
private static extern IntPtr FindResourceEx(IntPtr hModule, string lpType, IntPtr lpName, ushort wLanguage);
|
||||
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
private static extern IntPtr FindResourceEx(IntPtr hModule, string lpType, string lpName, ushort wLanguage);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
private static extern uint SizeofResource(IntPtr hModule, IntPtr handle);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
private static extern IntPtr LoadResource(IntPtr hModule, IntPtr handle);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
private static extern IntPtr LockResource(IntPtr hglobal);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
private static extern bool FreeLibrary(IntPtr hModule);
|
||||
|
||||
private IntPtr hModule;
|
||||
const uint LOAD_LIBRARY_AS_DATAFILE = 2;
|
||||
private bool _disposed;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics.Contracts;
|
||||
@@ -747,92 +747,6 @@ namespace Squirrel
|
||||
guid[right] = temp;
|
||||
}
|
||||
|
||||
[SupportedOSPlatform("windows")]
|
||||
private static List<(string ProcessExePath, int ProcessId)> EnumerateProcessesWindows()
|
||||
{
|
||||
var pids = new int[2048];
|
||||
var gch = GCHandle.Alloc(pids, GCHandleType.Pinned);
|
||||
try {
|
||||
if (!NativeMethods.EnumProcesses(gch.AddrOfPinnedObject(), sizeof(int) * pids.Length,
|
||||
out var bytesReturned))
|
||||
throw new Win32Exception("Failed to enumerate processes");
|
||||
|
||||
if (bytesReturned < 1)
|
||||
throw new Exception("Failed to enumerate processes");
|
||||
|
||||
List<(string ProcessExePath, int ProcessId)> ret = new();
|
||||
|
||||
for (int i = 0; i < bytesReturned / sizeof(int); i++) {
|
||||
IntPtr hProcess = IntPtr.Zero;
|
||||
try {
|
||||
hProcess = NativeMethods.OpenProcess(ProcessAccess.QueryLimitedInformation, false, pids[i]);
|
||||
if (hProcess == IntPtr.Zero)
|
||||
continue;
|
||||
|
||||
var sb = new StringBuilder(256);
|
||||
var capacity = sb.Capacity;
|
||||
if (!NativeMethods.QueryFullProcessImageName(hProcess, 0, sb, ref capacity))
|
||||
continue;
|
||||
|
||||
var exePath = sb.ToString();
|
||||
if (String.IsNullOrWhiteSpace(exePath) || !File.Exists(exePath))
|
||||
continue;
|
||||
|
||||
ret.Add((sb.ToString(), pids[i]));
|
||||
} catch (Exception) {
|
||||
// don't care
|
||||
} finally {
|
||||
if (hProcess != IntPtr.Zero)
|
||||
NativeMethods.CloseHandle(hProcess);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
} finally {
|
||||
gch.Free();
|
||||
}
|
||||
}
|
||||
|
||||
public static List<(string ProcessExePath, int ProcessId)> EnumerateProcesses()
|
||||
{
|
||||
IEnumerable<(string ProcessExePath, int ProcessId)> allRunningProcesses = SquirrelRuntimeInfo.IsWindows
|
||||
? EnumerateProcessesWindows()
|
||||
: Process.GetProcesses().Select(p => (p.MainModule?.FileName, p.Id));
|
||||
|
||||
return allRunningProcesses
|
||||
.Where(x => !String.IsNullOrWhiteSpace(x.ProcessExePath)) // Processes we can't query will have an empty process name
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public static List<(string ProcessExePath, int ProcessId)> EnumerateProcessesInDirectory(string directory)
|
||||
{
|
||||
return EnumerateProcesses()
|
||||
.Where(x => IsFileInDirectory(x.ProcessExePath, directory))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public static void KillProcessesInDirectory(string directoryToKill)
|
||||
{
|
||||
EnumerateProcessesInDirectory(directoryToKill)
|
||||
.Where(x => {
|
||||
// Never kill our own EXE
|
||||
if (FullPathEquals(SquirrelRuntimeInfo.EntryExePath, x.ProcessExePath))
|
||||
return false;
|
||||
|
||||
var name = Path.GetFileName(x.ProcessExePath).ToLowerInvariant();
|
||||
if (name == "squirrel.exe" || name == "update.exe") return false;
|
||||
|
||||
return true;
|
||||
})
|
||||
.ForEach(x => {
|
||||
try {
|
||||
Process.GetProcessById(x.ProcessId).Kill();
|
||||
} catch (Exception ex) {
|
||||
Log().WarnException($"Unable to terminate process (pid.{x.ProcessId})", ex);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public const string SpecVersionFileName = "sq.version";
|
||||
|
||||
public static NuspecManifest ReadManifestFromVersionDir(string appVersionDir)
|
||||
@@ -858,24 +772,5 @@ namespace Squirrel
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private enum Magic : uint
|
||||
{
|
||||
MH_MAGIC = 0xfeedface,
|
||||
MH_CIGAM = 0xcefaedfe,
|
||||
MH_MAGIC_64 = 0xfeedfacf,
|
||||
MH_CIGAM_64 = 0xcffaedfe
|
||||
}
|
||||
|
||||
public static bool IsMachOImage(string filePath)
|
||||
{
|
||||
using (BinaryReader reader = new BinaryReader(File.OpenRead(filePath))) {
|
||||
if (reader.BaseStream.Length < 256) // Header size
|
||||
return false;
|
||||
|
||||
uint magic = reader.ReadUInt32();
|
||||
return Enum.IsDefined(typeof(Magic), magic);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Versioning;
|
||||
|
||||
namespace Squirrel
|
||||
@@ -9,6 +10,126 @@ namespace Squirrel
|
||||
[SupportedOSPlatform("windows")]
|
||||
internal class MsDeltaCompression
|
||||
{
|
||||
/// <summary>
|
||||
/// The ApplyDelta function use the specified delta and source files to create a new copy of the target file.
|
||||
/// </summary>
|
||||
/// <param name="applyFlags">Either DELTA_FLAG_NONE or DELTA_APPLY_FLAG_ALLOW_PA19.</param>
|
||||
/// <param name="sourceName">The name of the source file to which the delta is to be applied.</param>
|
||||
/// <param name="deltaName">The name of the delta to be applied to the source file.</param>
|
||||
/// <param name="targetName">The name of the target file that is to be created.</param>
|
||||
/// <returns>
|
||||
/// Returns TRUE on success or FALSE otherwise.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// http://msdn.microsoft.com/en-us/library/bb417345.aspx#applydeltaaw
|
||||
/// </remarks>
|
||||
[DllImport("msdelta.dll", CharSet = CharSet.Unicode, SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static extern bool ApplyDelta(
|
||||
[MarshalAs(UnmanagedType.I8)] ApplyFlags applyFlags,
|
||||
string sourceName,
|
||||
string deltaName,
|
||||
string targetName);
|
||||
|
||||
/// <summary>
|
||||
/// The CreateDelta function creates a delta from the specified source and target files and write the output delta to the designated file name.
|
||||
/// </summary>
|
||||
/// <param name="fileTypeSet">The file type set used for Create.</param>
|
||||
/// <param name="setFlags">The file type set used for Create.</param>
|
||||
/// <param name="resetFlags">The file type set used for Create.</param>
|
||||
/// <param name="sourceName">The file type set used for Create.</param>
|
||||
/// <param name="targetName">The name of the target against which the source is compared.</param>
|
||||
/// <param name="sourceOptionsName">Reserved. Pass NULL.</param>
|
||||
/// <param name="targetOptionsName">Reserved. Pass NULL.</param>
|
||||
/// <param name="globalOptions">Reserved. Pass a DELTA_INPUT structure with lpStart set to NULL and uSize set to 0.</param>
|
||||
/// <param name="targetFileTime">The time stamp set on the target file after delta Apply. If NULL, the timestamp of the target file during delta Create will be used.</param>
|
||||
/// <param name="hashAlgId">ALG_ID of the algorithm to be used to generate the target signature.</param>
|
||||
/// <param name="deltaName">The name of the delta file to be created.</param>
|
||||
/// <returns>
|
||||
/// Returns TRUE on success or FALSE otherwise.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// http://msdn.microsoft.com/en-us/library/bb417345.aspx#createdeltaaw
|
||||
/// </remarks>
|
||||
[DllImport("msdelta.dll", CharSet = CharSet.Unicode, SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static extern bool CreateDelta(
|
||||
[MarshalAs(UnmanagedType.I8)] FileTypeSet fileTypeSet,
|
||||
[MarshalAs(UnmanagedType.I8)] CreateFlags setFlags,
|
||||
[MarshalAs(UnmanagedType.I8)] CreateFlags resetFlags,
|
||||
string sourceName,
|
||||
string targetName,
|
||||
string? sourceOptionsName,
|
||||
string? targetOptionsName,
|
||||
DeltaInput globalOptions,
|
||||
IntPtr targetFileTime,
|
||||
[MarshalAs(UnmanagedType.U4)] HashAlgId hashAlgId,
|
||||
string deltaName);
|
||||
|
||||
private enum HashAlgId
|
||||
{
|
||||
/// <summary>No signature.</summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>32-bit CRC defined in msdelta.dll.</summary>
|
||||
Crc32 = 32,
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
/// http://msdn.microsoft.com/en-us/library/bb417345.aspx#deltaflagtypeflags
|
||||
/// </remarks>
|
||||
private enum ApplyFlags : long
|
||||
{
|
||||
/// <summary>Indicates no special handling.</summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>Allow MSDelta to apply deltas created using PatchAPI.</summary>
|
||||
AllowLegacy = 1,
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
/// http://msdn.microsoft.com/en-us/library/bb417345.aspx#filetypesets
|
||||
/// </remarks>
|
||||
[Flags]
|
||||
private enum FileTypeSet : long
|
||||
{
|
||||
/// <summary>
|
||||
/// File type set that includes I386, IA64 and AMD64 Portable Executable (PE) files. Others are treated as raw.
|
||||
/// </summary>
|
||||
Executables = 0x0FL,
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
/// http://msdn.microsoft.com/en-us/library/bb417345.aspx#deltaflagtypeflags
|
||||
/// </remarks>
|
||||
private enum CreateFlags : long
|
||||
{
|
||||
/// <summary>Indicates no special handling.</summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>Allow the source, target and delta files to exceed the default size limit.</summary>
|
||||
IgnoreFileSizeLimit = 1 << 17,
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
/// http://msdn.microsoft.com/en-us/library/bb417345.aspx#deltainputstructure
|
||||
/// </remarks>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
private struct DeltaInput
|
||||
{
|
||||
/// <summary>Memory address non-editable input buffer.</summary>
|
||||
public IntPtr Start;
|
||||
|
||||
/// <summary>Size of the memory buffer in bytes.</summary>
|
||||
public IntPtr Size;
|
||||
|
||||
/// <summary>
|
||||
/// Defines whether MSDelta is allowed to edit the input buffer. If you make the input editable, the buffer will
|
||||
/// be zeroed at function return. However this will cause most MSDelta functions to use less memory.
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.Bool)] public bool Editable;
|
||||
}
|
||||
|
||||
//public void CreateDelta(string oldFilePath, string newFilePath, string deltaFilePath)
|
||||
//{
|
||||
// const string? sourceOptionsName = null;
|
||||
@@ -26,7 +147,7 @@ namespace Squirrel
|
||||
|
||||
public static void ApplyDelta(string deltaFilePath, string oldFilePath, string newFilePath)
|
||||
{
|
||||
if (!NativeMethods.ApplyDelta(ApplyFlags.AllowLegacy, oldFilePath, deltaFilePath, newFilePath))
|
||||
if (!ApplyDelta(ApplyFlags.AllowLegacy, oldFilePath, deltaFilePath, newFilePath))
|
||||
throw new Win32Exception();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,8 +138,8 @@ namespace Squirrel.NuGet
|
||||
Directory.CreateDirectory(fullTargetFile);
|
||||
} else {
|
||||
reader.WriteEntryToFile(fullTargetFile);
|
||||
if (Utility.IsMachOImage(fullTargetFile)) {
|
||||
NativeMac.ChmodAsExe(fullTargetFile);
|
||||
if (PlatformUtil.IsMachOImage(fullTargetFile)) {
|
||||
PlatformUtil.ChmodFileAsExecutable(fullTargetFile);
|
||||
}
|
||||
}
|
||||
}, 5);
|
||||
|
||||
@@ -92,7 +92,7 @@ namespace Squirrel
|
||||
var args = new string[] { "/passive", "/norestart", "/showrmui" };
|
||||
var quietArgs = new string[] { "/q", "/norestart" };
|
||||
Log.Info($"Running {Id} installer '{pathToInstaller} {string.Join(" ", args)}'");
|
||||
var p = await ProcessUtil.InvokeProcessAsync(pathToInstaller, isQuiet ? quietArgs : args, null, CancellationToken.None).ConfigureAwait(false);
|
||||
var p = await PlatformUtil.InvokeProcessAsync(pathToInstaller, isQuiet ? quietArgs : args, null, CancellationToken.None).ConfigureAwait(false);
|
||||
|
||||
// https://johnkoerner.com/install/windows-installer-error-codes/
|
||||
|
||||
|
||||
@@ -97,7 +97,7 @@ namespace Squirrel
|
||||
cts.CancelAfter(10 * 1000);
|
||||
try {
|
||||
var args = new string[] { "--squirrel-uninstall", currentVersion.ToString() };
|
||||
await ProcessUtil.InvokeProcessAsync(exe, args, Path.GetDirectoryName(exe), cts.Token).ConfigureAwait(false);
|
||||
await PlatformUtil.InvokeProcessAsync(exe, args, Path.GetDirectoryName(exe), cts.Token).ConfigureAwait(false);
|
||||
} catch (Exception ex) {
|
||||
this.Log().ErrorException("Failed to run cleanup hook, continuing: " + exe, ex);
|
||||
}
|
||||
@@ -248,7 +248,7 @@ namespace Squirrel
|
||||
cts.CancelAfter(30 * 1000);
|
||||
|
||||
try {
|
||||
await ProcessUtil.InvokeProcessAsync(exe, args, Path.GetDirectoryName(exe), cts.Token).ConfigureAwait(false);
|
||||
await PlatformUtil.InvokeProcessAsync(exe, args, Path.GetDirectoryName(exe), cts.Token).ConfigureAwait(false);
|
||||
} catch (Exception ex) {
|
||||
this.Log().ErrorException("Couldn't run Squirrel hook, continuing: " + exe, ex);
|
||||
}
|
||||
@@ -311,7 +311,7 @@ namespace Squirrel
|
||||
using (var cts = new CancellationTokenSource()) {
|
||||
cts.CancelAfter(10 * 1000);
|
||||
try {
|
||||
await ProcessUtil.InvokeProcessAsync(exe, args, Path.GetDirectoryName(exe), cts.Token).ConfigureAwait(false);
|
||||
await PlatformUtil.InvokeProcessAsync(exe, args, Path.GetDirectoryName(exe), cts.Token).ConfigureAwait(false);
|
||||
} catch (Exception ex) {
|
||||
this.Log().ErrorException("Coudln't run Squirrel hook, continuing: " + exe, ex);
|
||||
}
|
||||
@@ -324,7 +324,7 @@ namespace Squirrel
|
||||
markAppFolderAsDead(v.DirectoryPath);
|
||||
}
|
||||
|
||||
var runningProcesses = Utility.EnumerateProcesses();
|
||||
var runningProcesses = PlatformUtil.GetRunningProcesses();
|
||||
|
||||
foreach (var dir in toDelete) {
|
||||
// skip any directories with running processes
|
||||
|
||||
@@ -178,7 +178,7 @@ namespace Squirrel
|
||||
/// <inheritdoc/>
|
||||
public void KillAllExecutablesBelongingToPackage()
|
||||
{
|
||||
Utility.KillProcessesInDirectory(_config.RootAppDir);
|
||||
PlatformUtil.KillProcessesInDirectory(_config.RootAppDir);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -204,12 +204,10 @@ namespace Squirrel
|
||||
/// <param name="arguments">Arguments to start the exe with</param>
|
||||
/// <remarks>See <see cref="RestartAppWhenExited(string, string)"/> for a version which does not
|
||||
/// exit the current process immediately, but instead allows you to exit the current process
|
||||
/// however you'd like.</remarks>
|
||||
/// however you'd like after cleaning up resources.</remarks>
|
||||
public static void RestartApp(string exeToStart = null, string arguments = null)
|
||||
{
|
||||
AppDesc.GetCurrentPlatform().StartRestartingProcess(exeToStart, arguments);
|
||||
// NB: We have to give update.exe some time to grab our PID
|
||||
Thread.Sleep(1000);
|
||||
Environment.Exit(0);
|
||||
}
|
||||
|
||||
@@ -224,28 +222,9 @@ namespace Squirrel
|
||||
public static Process RestartAppWhenExited(string exeToStart = null, string arguments = null)
|
||||
{
|
||||
var process = AppDesc.GetCurrentPlatform().StartRestartingProcess(exeToStart, arguments);
|
||||
// NB: We have to give update.exe some time to grab our PID
|
||||
Thread.Sleep(1000);
|
||||
return process;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Launch Update.exe and ask it to wait until this process exits before starting
|
||||
/// a new process. Used to re-start your app with the latest version after an update.
|
||||
/// </summary>
|
||||
/// <param name="exeToStart">The file *name* (not full path) of the exe to start, or null to re-launch
|
||||
/// the current executable. </param>
|
||||
/// <param name="arguments">Arguments to start the exe with</param>
|
||||
/// <returns>The Update.exe process that is waiting for this process to exit</returns>
|
||||
public static async Task<Process> RestartAppWhenExitedAsync(string exeToStart = null, string arguments = null)
|
||||
{
|
||||
var process = AppDesc.GetCurrentPlatform().StartRestartingProcess(exeToStart, arguments);
|
||||
// NB: We have to give update.exe some time to grab our PID
|
||||
await Task.Delay(1000).ConfigureAwait(false);
|
||||
return process;
|
||||
}
|
||||
|
||||
|
||||
private static IUpdateSource CreateSource(string urlOrPath, IFileDownloader urlDownloader = null)
|
||||
{
|
||||
if (String.IsNullOrWhiteSpace(urlOrPath)) {
|
||||
|
||||
@@ -46,7 +46,7 @@ namespace Squirrel.Update
|
||||
|
||||
static void ProcessStart(string exeName, string arguments, bool shouldWait, bool forceLatest)
|
||||
{
|
||||
if (shouldWait) waitForParentToExit();
|
||||
if (shouldWait) PlatformUtil.WaitForParentProcessToExit();
|
||||
|
||||
// todo https://stackoverflow.com/questions/51441576/how-to-run-app-as-sudo
|
||||
// https://stackoverflow.com/questions/10283062/getting-sudo-to-ask-for-password-via-the-gui
|
||||
@@ -62,26 +62,7 @@ namespace Squirrel.Update
|
||||
args.Add(arguments);
|
||||
}
|
||||
|
||||
ProcessUtil.StartNonBlocking("/usr/bin/open", args, null);
|
||||
}
|
||||
|
||||
static void waitForParentToExit()
|
||||
{
|
||||
var parentPid = NativeMac.getppid();
|
||||
if (parentPid <= 1) {
|
||||
Log.Warn("Cannot wait for parent to exit, it has already exited.");
|
||||
return;
|
||||
}
|
||||
|
||||
var proc = Process.GetProcessById(parentPid);
|
||||
|
||||
Log.Info($"Waiting for PID {parentPid} to exit (30s timeout)...");
|
||||
var exited = proc.WaitForExit(30_000);
|
||||
if (!exited) {
|
||||
throw new Exception("Parent wait timed out.");
|
||||
}
|
||||
|
||||
Log.Info($"PID {parentPid} has exited.");
|
||||
PlatformUtil.StartProcessNonBlocking("/usr/bin/open", args, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -319,7 +319,7 @@ namespace Squirrel.Update
|
||||
|
||||
static async Task UpdateSelf()
|
||||
{
|
||||
waitForParentToExit();
|
||||
PlatformUtil.WaitForParentProcessToExit();
|
||||
var src = SquirrelRuntimeInfo.EntryExePath;
|
||||
var updateDotExeForOurPackage = Path.Combine(
|
||||
Path.GetDirectoryName(src),
|
||||
@@ -434,7 +434,7 @@ namespace Squirrel.Update
|
||||
return;
|
||||
}
|
||||
|
||||
if (shouldWait) waitForParentToExit();
|
||||
if (shouldWait) PlatformUtil.WaitForParentProcessToExit();
|
||||
|
||||
var config = new AppDescWindows();
|
||||
var latestAppDir = config.UpdateAndRetrieveCurrentFolder(forceLatest);
|
||||
@@ -493,30 +493,10 @@ namespace Squirrel.Update
|
||||
|
||||
static void ShowHelp()
|
||||
{
|
||||
ensureConsole();
|
||||
PlatformUtil.AttachConsole();
|
||||
opt.WriteOptionDescriptions();
|
||||
}
|
||||
|
||||
static void waitForParentToExit()
|
||||
{
|
||||
// Grab a handle the parent process
|
||||
var parentPid = NativeMethods.GetParentProcessId();
|
||||
var handle = default(IntPtr);
|
||||
|
||||
// Wait for our parent to exit
|
||||
try {
|
||||
handle = NativeMethods.OpenProcess(ProcessAccess.Synchronize, false, parentPid);
|
||||
if (handle != IntPtr.Zero) {
|
||||
Log.Info("About to wait for parent PID {0}", parentPid);
|
||||
NativeMethods.WaitForSingleObject(handle, 0xFFFFFFFF /*INFINITE*/);
|
||||
} else {
|
||||
Log.Info("Parent PID {0} no longer valid - ignoring", parentPid);
|
||||
}
|
||||
} finally {
|
||||
if (handle != IntPtr.Zero) NativeMethods.CloseHandle(handle);
|
||||
}
|
||||
}
|
||||
|
||||
static string getMissingRuntimesMessage(string appname, Runtimes.RuntimeInfo[] missingFrameworks)
|
||||
{
|
||||
return missingFrameworks.Length > 1
|
||||
@@ -602,21 +582,6 @@ namespace Squirrel.Update
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int consoleCreated = 0;
|
||||
static void ensureConsole()
|
||||
{
|
||||
if (Environment.OSVersion.Platform != PlatformID.Win32NT) return;
|
||||
|
||||
if (Interlocked.CompareExchange(ref consoleCreated, 1, 0) == 1) return;
|
||||
|
||||
if (!NativeMethods.AttachConsole(-1)) {
|
||||
NativeMethods.AllocConsole();
|
||||
}
|
||||
|
||||
NativeMethods.GetStdHandle(StandardHandles.STD_ERROR_HANDLE);
|
||||
NativeMethods.GetStdHandle(StandardHandles.STD_OUTPUT_HANDLE);
|
||||
}
|
||||
}
|
||||
|
||||
public class ProgressSource
|
||||
|
||||
@@ -178,7 +178,7 @@ namespace Squirrel.Tests
|
||||
[Fact]
|
||||
public void WeCanFetchAllProcesses()
|
||||
{
|
||||
var result = Utility.EnumerateProcesses();
|
||||
var result = PlatformUtil.GetRunningProcesses();
|
||||
Assert.True(result.Count > 1);
|
||||
Assert.True(result.Count != 2048);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user