Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Fix get process architecture #3726

Merged
merged 7 commits into from
Jun 8, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,14 @@ internal class NativeMethods
public const ushort IMAGE_FILE_MACHINE_UNKNOWN = 0;

[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool IsWow64Process2(IntPtr process, out ushort processMachine, out ushort nativeMachine);
public static extern bool IsWow64Process2([In] IntPtr process, [Out] out ushort processMachine, [Out] out ushort nativeMachine);

// A pointer to a value that is set to TRUE if the process is running under WOW64.
// If the process is running under 32-bit Windows, the value is set to FALSE.
// If the process is a 64-bit application running under 64-bit Windows, the value is also set to FALSE.
[DllImport("kernel32.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi)]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can add CallingConvention = CallingConvention.Winapi and the return annotation also above

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added.

[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool IsWow64Process([In] IntPtr process, [Out] out bool wow64Process);
}

#endif
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ namespace Microsoft.VisualStudio.TestPlatform.PlatformAbstractions;
public partial class ProcessHelper : IProcessHelper
{
private static readonly string Arm = "arm";
private readonly Process _currentProcess = Process.GetCurrentProcess();

/// <inheritdoc/>
public object LaunchProcess(string processPath, string arguments, string workingDirectory, IDictionary<string, string> envVariables, Action<object, string> errorCallback, Action<object> exitCallBack, Action<object, string> outputCallBack)
Expand Down Expand Up @@ -139,7 +140,7 @@ void InitializeAndStart()
/// <inheritdoc/>
public string GetCurrentProcessFileName()
{
return Process.GetCurrentProcess().MainModule.FileName;
return _currentProcess.MainModule.FileName;
}

/// <inheritdoc/>
Expand All @@ -151,12 +152,17 @@ public string GetTestEngineDirectory()
/// <inheritdoc/>
public int GetCurrentProcessId()
{
return Process.GetCurrentProcess().Id;
return _currentProcess.Id;
}

/// <inheritdoc/>
public string GetProcessName(int processId)
{
if (processId == _currentProcess.Id)
{
return _currentProcess.ProcessName;
}

return Process.GetProcessById(processId).ProcessName;
}

Expand Down Expand Up @@ -184,7 +190,7 @@ public void SetExitCallback(int processId, Action<object> callbackAction)
{
try
{
var process = Process.GetProcessById(processId);
var process = processId == _currentProcess.Id ? _currentProcess : Process.GetProcessById(processId);
process.EnableRaisingEvents = true;
process.Exited += (sender, args) => callbackAction?.Invoke(sender);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@ namespace Microsoft.VisualStudio.TestPlatform.PlatformAbstractions;
/// <inheritdoc />
public class PlatformEnvironment : IEnvironment
{
private PlatformArchitecture? _architecture;

/// <inheritdoc />
public PlatformArchitecture Architecture
{
get
{
return Environment.Is64BitOperatingSystem
return _architecture ??= Environment.Is64BitOperatingSystem
? IsArm64()
? PlatformArchitecture.ARM64
: PlatformArchitecture.X64
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,37 +23,69 @@ public string GetCurrentProcessLocation()
=> Path.GetDirectoryName(GetCurrentProcessFileName());

/// <inheritdoc/>
public IntPtr GetProcessHandle(int processId)
=> Process.GetProcessById(processId).Handle;
public IntPtr GetProcessHandle(int processId) =>
processId == _currentProcess.Id
? _currentProcess.Handle
: Process.GetProcessById(processId).Handle;

/// <inheritdoc/>
public PlatformArchitecture GetCurrentProcessArchitecture()
=> _currentProcessArchitecture ??= GetProcessArchitecture(Process.GetCurrentProcess().Id);

{
_currentProcessArchitecture ??= GetProcessArchitecture(_currentProcess.Id);
return _currentProcessArchitecture.Value;
}

public PlatformArchitecture GetProcessArchitecture(int processId)
=> IntPtr.Size == 8
? IsArm64(processId)
? PlatformArchitecture.ARM64
: PlatformArchitecture.X64
: PlatformArchitecture.X86;

private static bool IsArm64(int processId)
{
if (_currentProcess.Id == processId)
{
// If we already cached the current process architecture, no need to figure it out again.
if (_currentProcessArchitecture != null)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (_currentProcessArchitecture != null)
if (_currentProcessArchitecture is not null)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added.

{
return _currentProcessArchitecture.Value;
}

// When this is current process, we can just check if IntPointer size to get if we are 64-bit or 32-bit.
// When it is 32-bit we can just return, if it is 64-bit we need to clarify if x64 or arm64.
if (IntPtr.Size == 4)
{
return PlatformArchitecture.X86;
}
}

// If the current process is 64-bit, or this is any remote process, we need to query it via native api.
var process = processId == _currentProcess.Id ? _currentProcess : Process.GetProcessById(processId);
try
{
var process = Process.GetProcessById(processId);
if (!NativeMethods.IsWow64Process2(process.Handle, out ushort processMachine, out ushort nativeMachine))
{
throw new Win32Exception();
}

// If processMachine is IMAGE_FILE_MACHINE_UNKNOWN mean that we're not running using WOW64 x86 emulation.
if (processMachine != NativeMethods.IMAGE_FILE_MACHINE_UNKNOWN)
{
// The process is running using WOW64, which suggests it is 32-bit (or any of the other machines, that we cannot
// handle, so we just assume x86).
return PlatformArchitecture.X86;
}

// If processMachine is IMAGE_FILE_MACHINE_UNKNOWN mean that we're not running using WOW64 emulation.
// If nativeMachine is IMAGE_FILE_MACHINE_ARM64 mean that we're running on ARM64 architecture device.
if (processMachine == NativeMethods.IMAGE_FILE_MACHINE_UNKNOWN && nativeMachine == NativeMethods.IMAGE_FILE_MACHINE_ARM64)
{
// To distinguish between ARM64 and x64 emulated on ARM64 we check the PE header of the current running executable.
return IsArm64Executable(process.MainModule.FileName);
if (IsArm64Executable(process.MainModule.FileName))
{
return PlatformArchitecture.ARM64;
}
else
{
return PlatformArchitecture.X64;
}
}
else
{
return PlatformArchitecture.X64;
}
}
catch
Expand All @@ -64,9 +96,33 @@ private static bool IsArm64(int processId)
// we loaded runner version of Microsoft.TestPlatform.PlatformAbstractions but newer version Microsoft.TestPlatform.ObjectModel(the one close
// to the test container) and the old PlatformAbstractions doesn't contain the methods expected by the new ObjectModel throwing
// a MissedMethodException.
}

return false;
try
{
if (!Environment.Is64BitOperatingSystem)
{
// When we know this is not 64-bit operating system, then all processes are running as 32-bit, both
// the current process and other processes.
return PlatformArchitecture.X86;
}

var isWow64Process = NativeMethods.IsWow64Process(process.Handle, out var isWow64);
if (!isWow64Process)
{
// Do nothing we cannot log errors here.
}
Evangelink marked this conversation as resolved.
Show resolved Hide resolved

// The process is running using WOW64, which suggests it is 32-bit (or any of the other machines, that we cannot
// handle, so we just assume x86). If it is not wow, we assume x64, because we failed the call to more advanced api
// that can tell us if this is arm64, so we are probably on older version of OS which is x64.
// We could call PlatformArchitecture.Architecture, but that uses the same api that we just failed to invoke.
return isWow64 ? PlatformArchitecture.X86 : PlatformArchitecture.X64;
}
catch
{
// Do nothing we cannot log errors here.
}
}
}

private static bool IsArm64Executable(string path)
Expand Down