At runtime how does one determine which mono version is being used? - c#

On a system multiple mono runtime versions may exist.
For example
/usr/bin/mono
/usr/local/bin/mono
When creating a new managed process from a C# application it can be useful to be explicit about which mono version you want to run it with. (The mono in the path may not be the mono being used to run the current process)
Using the Process class to get the current process name returns the assembly that mono is running not mono itself.
What is the best way to determine which mono runtime is currently being used?

On Linux, the process Id and /proc can be used to find the mono executable.
string monopath = String.Format("/proc/{0}/exe", Process.GetCurrentProcess().Id);
monopath will be a symlink to the current executing mono runtime, and can be used to launch a new process:
Process.Start(monopath);

You can use
Type monoRuntimeType;
MethodInfo getDisplayNameMethod;
if ((monoRuntimeType = typeof(object).Assembly.GetType("Mono.Runtime")) != null &&
(getDisplayNameMethod = monoRuntimeType.GetMethod("GetDisplayName",
BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.DeclaredOnly | BindingFlags.ExactBinding, null,
Type.EmptyTypes, null)) != null)
Console.WriteLine("Mono " + (string)getDisplayNameMethod.Invoke(null, null));

Related

need an optimal way to get a process by means of its executable path

I'm trying to get a process ID by means of the process's execution-path. For that I'm executing the below Powershell command which runs perfectly in Powershell's console:
(Get-Process | Where-Object {$_.Path -eq 'C:\WINDOWS\system32\winlogon.exe'}).Id
But executing the same through C# is giving no results. below is the code snippet I'm following:
string cmd = "(Get-Process | Where-Object {{$_.Path -eq '{0}'}}).Id";
string path = #"C:\WINDOWS\system32\winlogon.exe";
string finalCmd = string.Format(cmd, System.IO.Path.GetFullPath(path));
powershell.Runspace = runspace;
powershell.AddScript(finalCmd);
var result = powershell.Invoke();
I'm using double-culry-braces for escape sequence. But still powershell.Invoke() returns nothing but null. Is there any other way to get the Process Id with its executable path?
My ultimate goal is that I should be able to push an application (MSI installer) to all the PCs in network through Active Directory(irrespective of x86/x64) and I should get the Process Ids for the given executable path. Thanks for the suggestions but in my case I need a generic solution which should work seamlessly for both x86 and x64.
Doesn't seem like you need to use Powershell here. .NET code can query processes directly.
Something like:
Process.GetProcesses().Where(p=>p.MainModule.FileName==path)
should return you an enumerable of all matching processes, from which you can easily retrieve their IDs. And decide what to do if you find more than one!

HttpRuntime CacheInternal NULL reference exception while reading user sessions (Reflection)

After some updates on our windows servers(2008R2 ,2012) Asp.net application throwing error:
var obj_1 = typeof(HttpRuntime).GetProperty("CacheInternal", BindingFlags.NonPublic | BindingFlags.Static);
CacheInternal is coming null, dont know why ?
following solution is not working :(
Solution
I have find the solution. Now HTTPRuntime class doesnt have CacheInternal Property.So to achive the above task I have created a global list adding sessions in that list in Session_Start and removing the sessions in Sessions_end functions of Global.asax.
I have find a solution that maybe is the best for now. If anyone has another, let me know!
object aspNetCacheInternal = null;
var cacheInternalPropInfo = typeof(HttpRuntime).GetProperty("CacheInternal", BindingFlags.NonPublic | BindingFlags.Static);
if (cacheInternalPropInfo == null)
{
// At some point, after some .NET Framework's security update, that internal member disappeared.
// https://stackoverflow.com/a/45045160
//
// We need to look for internal cache otherwise.
//
var cacheInternalFieldInfo = HttpRuntime.Cache.GetType().GetField("_internalCache", BindingFlags.NonPublic | BindingFlags.Static);
if (cacheInternalFieldInfo != null)
{
var httpRuntimeInternalCache = cacheInternalFieldInfo.GetValue(HttpRuntime.Cache);
var httpRuntimeInternalCacheField = httpRuntimeInternalCache.GetType().GetField("_cacheInternal", BindingFlags.NonPublic | BindingFlags.Instance);
if (httpRuntimeInternalCacheField != null)
aspNetCacheInternal = httpRuntimeInternalCacheField.GetValue(httpRuntimeInternalCache);
}
}
else
{
aspNetCacheInternal = cacheInternalPropInfo.GetValue(null, null);
}
return aspNetCacheInternal;
Regards!
That internal member was present in .NET 2.0 and disappeared somewhere between .NET 3.5 and .NET 4.6.1. That's why you shouldn't use reflection to rely on nonpublic members. They can disappear or be renamed any time.
Because .NET is backwards compatible, forcing a certain runtime version will not use older assemblies at runtime, if newer ones are available: .NET 4.6.1 is still an in-place upgrade of all earlier versions down to 4.0.
So I think this update either patched the member away from the System.Web assembly, or it was never in 4.0 to begin with and your application pool somehow changed from .NET 2.0 to .NET 4.0.
Of course it isn't advisable to uninstall updates, but you could try to find the one removing this member. You'd then have to verify it wasn't a security update.
Alternatively force the application to run under .NET 2.0, if that's viable.
You could also try to find a different way to solve the original problem.

How to access PowerShell host from C#

In a PowerShell profile, one can identify the PowerShell host in order to do appropriate setup for that host's environment. For example:
if ($host.Name -eq 'ConsoleHost')
{
Import-Module PSReadline
# differentiate verbose from warnings!
$privData = (Get-Host).PrivateData
$privData.VerboseForegroundColor = "cyan"
}
elseif ($host.Name -like '*ISE Host')
{
Start-Steroids
Import-Module PsIseProjectExplorer
}
I would like to be able to do the equivalent identification from a C# context primarily because PowerShell ISE does not support Console.ReadLine so I want to know if it is safe to use it in the current PS host's environment.
I first explored trying to get the output of the Get-Host cmdlet from within C# (per Invoking a cmdlet within a cmdlet). After I located the Microsoft.PowerShell.Commands.Utility assembly (under C:\Program Files (x86)\Reference Assemblies\Microsoft\WindowsPowerShell\3.0) I could compile this but it yielded null...
var cmd = new Microsoft.PowerShell.Commands.GetHostCommand();
var myHost = cmd.Invoke();
...while this would not compile due to the InternalHost class being (ironically!) internal:
var cmd = new Microsoft.PowerShell.Commands.GetHostCommand();
var myHost = cmd.Invoke<System.Management.Automation.Internal.Host.InternalHost>();
Next, I then modified my cmdlet to inherit from PSCmdlet rather than Cmdlet (to allow access to the SessionState), so I could then access the PS host object like this:
var psVarObject = SessionState.PSVariable.GetValue("Host");
Of course, that returns a pure Object, which I then needed to cast to... oh, wait... it's still internal!... so this would not compile:
string psHost = ((System.Management.Automation.Internal.Host.InternalHost)psVarObject).Name;
Leaving me no alternative but to use reflection on a foreign assembly (horrors!):
string psHost = (string)psVarObject.GetType().GetProperty("Name").GetValue(psVarObject, null);
That works, but is less than ideal, because reflecting upon any 3rd-party assembly is a fragile thing to do.
Any alternative ideas on either (a) identifying the host or, (b) backing up a bit, being able to use the host's own Read-Host cmdlet to get a typed input from a user?
You can just use Host property from PSCmdlet class. And if you want to do Read-Host:
Host.UI.ReadLine()
When getting
var psVarObject = SessionState.PSVariable.GetValue("Host");
You can cast it to System.Management.Automation.Host.PSHost instead of InternalHost

How to recognize dll's target platform [duplicate]

I've got an arbitrary list of .NET assemblies.
I need to programmatically check if each DLL was built for x86 (as opposed to x64 or Any CPU). Is this possible?
Look at System.Reflection.AssemblyName.GetAssemblyName(string assemblyFile).
You can examine assembly metadata from the returned AssemblyName instance:
Using PowerShell:
[36] C:\> [reflection.assemblyname]::GetAssemblyName("${pwd}\Microsoft.GLEE.dll") | fl
Name : Microsoft.GLEE
Version : 1.0.0.0
CultureInfo :
CodeBase : file:///C:/projects/powershell/BuildAnalyzer/...
EscapedCodeBase : file:///C:/projects/powershell/BuildAnalyzer/...
ProcessorArchitecture : MSIL
Flags : PublicKey
HashAlgorithm : SHA1
VersionCompatibility : SameMachine
KeyPair :
FullName : Microsoft.GLEE, Version=1.0.0.0, Culture=neut...
Here, ProcessorArchitecture identifies the target platform.
Amd64: A 64-bit processor based on the x64 architecture.
Arm: An ARM processor.
IA64: A 64-bit Intel Itanium processor only.
MSIL: Neutral with respect to processor and bits-per-word.
X86: A 32-bit Intel processor, either native or in the Windows on Windows environment on a 64-bit platform (WoW64).
None: An unknown or unspecified combination of processor and bits-per-word.
I'm using PowerShell in this example to call the method.
You can use the CorFlags CLI tool (for instance, C:\Program Files\Microsoft SDKs\Windows\v7.0\Bin\CorFlags.exe) to determine the status of an assembly, based on its output and opening an assembly as a binary asset you should be able to determine where you need to seek to determine if the 32BIT flag is set to 1 (x86) or 0 (Any CPU or x64, depending on PE):
Option | PE | 32BIT
----------|-------|---------
x86 | PE32 | 1
Any CPU | PE32 | 0
x64 | PE32+ | 0
The blog post x64 Development with .NET has some information about corflags.
Even better, you can use Module.GetPEKind to determine whether an assembly is PortableExecutableKinds value PE32Plus (64-bit), Required32Bit (32-bit and WoW), or ILOnly (any CPU) along with other attributes.
Just for clarification, CorFlags.exe is part of the .NET Framework SDK. I have the development tools on my machine, and the simplest way for me determine whether a DLL is 32-bit only is to:
Open the Visual Studio Command Prompt (In Windows: menu Start/Programs/Microsoft Visual Studio/Visual Studio Tools/Visual Studio 2008 Command Prompt)
CD to the directory containing the DLL in question
Run corflags like this:
corflags MyAssembly.dll
You will get output something like this:
Microsoft (R) .NET Framework CorFlags Conversion Tool. Version 3.5.21022.8
Copyright (c) Microsoft Corporation. All rights reserved.
Version : v2.0.50727
CLR Header: 2.5
PE : PE32
CorFlags : 3
ILONLY : 1
32BIT : 1
Signed : 0
As per comments the flags above are to be read as following:
Any CPU: PE = PE32 and 32BIT = 0
x86: PE = PE32 and 32BIT = 1
64-bit: PE = PE32+ and 32BIT = 0
Just write your own. The core of the PE architecture hasn't been seriously changed since its implementation in Windows 95.
Here's a C# example:
public static ushort GetPEArchitecture(string pFilePath)
{
ushort architecture = 0;
try
{
using (System.IO.FileStream fStream = new System.IO.FileStream(pFilePath, System.IO.FileMode.Open, System.IO.FileAccess.Read))
{
using (System.IO.BinaryReader bReader = new System.IO.BinaryReader(fStream))
{
// Check the MZ signature
if (bReader.ReadUInt16() == 23117)
{
// Seek to e_lfanew.
fStream.Seek(0x3A, System.IO.SeekOrigin.Current);
// Seek to the start of the NT header.
fStream.Seek(bReader.ReadUInt32(), System.IO.SeekOrigin.Begin);
if (bReader.ReadUInt32() == 17744) // Check the PE\0\0 signature.
{
// Seek past the file header,
fStream.Seek(20, System.IO.SeekOrigin.Current);
// Read the magic number of the optional header.
architecture = bReader.ReadUInt16();
}
}
}
}
}
catch (Exception) { /* TODO: Any exception handling you want
to do, personally I just take 0
as a sign of failure */
}
// If architecture returns 0, there has been an error.
return architecture;
}
}
Now the current constants are:
0x10B - PE32 format.
0x20B - PE32+ format.
But with this method it allows for the possibilities of new constants. Just validate the return as you see fit.
DotPeek from JetBrains provides a quick and easy way to see msil (Any CPU), x86, and x64:
Try to use CorFlagsReader from this project at CodePlex. It has no references to other assemblies and it can be used as is.
[TestMethod]
public void EnsureKWLLibrariesAreAll64Bit()
{
var assemblies = Assembly.GetExecutingAssembly().GetReferencedAssemblies().Where(x => x.FullName.StartsWith("YourCommonProjectName")).ToArray();
foreach (var assembly in assemblies)
{
var myAssemblyName = AssemblyName.GetAssemblyName(assembly.FullName.Split(',')[0] + ".dll");
Assert.AreEqual(ProcessorArchitecture.MSIL, myAssemblyName.ProcessorArchitecture);
}
}
Below is a batch file that will run corflags.exe against all DLL files and EXE files in the current working directory and all sub-directories, parse the results and display the target architecture of each.
Depending on the version of corflags.exe that is used, the line items in the output will either include 32BIT, or 32BITREQ (and 32BITPREF). Whichever of these two is included in the output is the critical line item that must be checked to differentiate between Any CPU and x86. If you are using an older version of corflags.exe (pre Windows SDK v8.0A), then only the 32BIT line item will be present in the output, as others have indicated in past answers. Otherwise 32BITREQ and 32BITPREF replace it.
This assumes corflags.exe is in the %PATH%. The simplest way to ensure this is to use a Developer Command Prompt. Alternatively you could copy it from its default location.
If the batch file below is run against an unmanaged DLL or EXE file, it will incorrectly display it as x86, since the actual output from Corflags.exe will be an error message similar to:
corflags : error CF008 : The specified file does not have a valid managed header
#echo off
echo.
echo Target architecture for all exes and dlls:
echo.
REM For each exe and dll in this directory and all subdirectories...
for %%a in (.exe, .dll) do forfiles /s /m *%%a /c "cmd /c echo #relpath" > testfiles.txt
for /f %%b in (testfiles.txt) do (
REM Dump corflags results to a text file
corflags /nologo %%b > corflagsdeets.txt
REM Parse the corflags results to look for key markers
findstr /C:"PE32+">nul .\corflagsdeets.txt && (
REM `PE32+` indicates x64
echo %%~b = x64
) || (
REM pre-v8 Windows SDK listed only "32BIT" line item,
REM newer versions list "32BITREQ" and "32BITPREF" line items
findstr /C:"32BITREQ : 0">nul /C:"32BIT : 0" .\corflagsdeets.txt && (
REM `PE32` and NOT 32bit required indicates Any CPU
echo %%~b = Any CPU
) || (
REM `PE32` and 32bit required indicates x86
echo %%~b = x86
)
)
del corflagsdeets.txt
)
del testfiles.txt
echo.
I've cloned a super handy tool that adds a context menu entry for assemblies in Windows Explorer to show all available information:
Download from Releases · tebjan/AssemblyInformation.
One more way would be to use dumpbin from the Visual Studio tools on the DLL and look for the appropriate output:
dumpbin.exe /HEADERS <your DLL file path>
FILE HEADER VALUE
14C machine (x86)
4 number of sections
5885AC36 time date stamp Mon Jan 23 12:39:42 2017
0 file pointer to symbol table
0 number of symbols
E0 size of optional header
2102 characteristics
Executable
32 bit word machine
DLL
Note: The above output is for a 32-bit DLL file
One more useful option with dumpbin.exe is /EXPORTS. It will show you the function exposed by the DLL file
dumpbin.exe /EXPORTS <PATH OF THE DLL FILE>
A more generic way - use the file structure to determine bitness and image type:
public static CompilationMode GetCompilationMode(this FileInfo info)
{
if (!info.Exists)
throw new ArgumentException($"{info.FullName} does not exist");
var intPtr = IntPtr.Zero;
try
{
uint unmanagedBufferSize = 4096;
intPtr = Marshal.AllocHGlobal((int)unmanagedBufferSize);
using (var stream = File.Open(info.FullName, FileMode.Open, FileAccess.Read))
{
var bytes = new byte[unmanagedBufferSize];
stream.Read(bytes, 0, bytes.Length);
Marshal.Copy(bytes, 0, intPtr, bytes.Length);
}
// Check DOS header magic number
if (Marshal.ReadInt16(intPtr) != 0x5a4d)
return CompilationMode.Invalid;
// This will get the address for the WinNT header
var ntHeaderAddressOffset = Marshal.ReadInt32(intPtr + 60);
// Check WinNT header signature
var signature = Marshal.ReadInt32(intPtr + ntHeaderAddressOffset);
if (signature != 0x4550)
return CompilationMode.Invalid;
// Determine file bitness by reading magic from IMAGE_OPTIONAL_HEADER
var magic = Marshal.ReadInt16(intPtr + ntHeaderAddressOffset + 24);
var result = CompilationMode.Invalid;
uint clrHeaderSize;
if (magic == 0x10b)
{
clrHeaderSize = (uint)Marshal.ReadInt32(intPtr + ntHeaderAddressOffset + 24 + 208 + 4);
result |= CompilationMode.Bit32;
}
else if (magic == 0x20b)
{
clrHeaderSize = (uint)Marshal.ReadInt32(intPtr + ntHeaderAddressOffset + 24 + 224 + 4);
result |= CompilationMode.Bit64;
}
else return CompilationMode.Invalid;
result |= clrHeaderSize != 0
? CompilationMode.CLR
: CompilationMode.Native;
return result;
}
finally
{
if (intPtr != IntPtr.Zero)
Marshal.FreeHGlobal(intPtr);
}
}
Compilation mode enumeration
[Flags]
public enum CompilationMode
{
Invalid = 0,
Native = 0x1,
CLR = Native << 1,
Bit32 = CLR << 1,
Bit64 = Bit32 << 1
}
Source code with explanation is at GitHub.
Another way to check the target platform of a .NET assembly is inspecting the assembly with .NET Reflector...
##~#€~! I've just realized that the new version is not free! So, correction, if you have a free version of .NET reflector, you can use it to check the target platform.
cfeduke notes the possibility of calling GetPEKind. It's potentially interesting to do this from PowerShell.
Here, for example, is code for a cmdlet that could be used: https://stackoverflow.com/a/16181743/64257
Alternatively, at https://stackoverflow.com/a/4719567/64257 it is noted that "there's also the Get-PEHeader cmdlet in the PowerShell Community Extensions that can be used to test for executable images."
A tool is sigcheck:
sigcheck c:\Windows\winhlp32.exe
Output:
Sigcheck v2.71 - File version and signature viewer
Copyright (C) 2004-2018 Mark Russinovich
Sysinternals - www.sysinternals.com
c:\windows\winhlp32.exe:
Verified: Signed
Signing date: 20:05 02.05.2022
Publisher: Microsoft Windows
Company: Microsoft Corporation
Description: Windows Winhlp32 Stub
Product: Microsoft® Windows® Operating System
Prod version: 10.0.19041.1
File version: 10.0.19041.1 (WinBuild.160101.0800)
MachineType: 32-bit
sigcheck -nobanner c:\Windows\HelpPane.exe
Output:
c:\windows\HelpPane.exe:
Verified: Signed
Signing date: 00:42 23.04.2022
Publisher: Microsoft Windows
Company: Microsoft Corporation
Description: Microsoft Help and Support
Product: Microsoft® Windows® Operating System
Prod version: 10.0.19041.1151
File version: 10.0.19041.1151 (WinBuild.160101.0800)
MachineType: 64-bit
An alternative to already mentioned tools is Telerik JustDecompile (free tool) which will display the information next to the assembly name:
I like the ILSpy tool. It shows not only architecture, but the target framework as well:
// linq2db, Version=3.0.0.0, Culture=neutral, PublicKeyToken=e41013125f9e410a
// Global type: <Module>
// Architecture: AnyCPU (64-bit preferred)
// Runtime: v4.0.30319
// This assembly is signed with a strong name key.
// This assembly was compiled using the /deterministic option.
// Hash algorithm: SHA1
So it is possible to determine if it is .NET Core 2.1, .NET Framework 4.6 or any other one:

Get effective executable filename

A .NET application (managed) runs on Windows 7 64 bit. It is actually running on a 64 bit environment.
The application inspect running process (for example, calc.exe) which is located in c:\windows\syswow64\calc.exe.
So, why the function
Process.MainModule.Filename
returns c:\windows\system32\calc.exe? Is it possible to get the effective executable main module location, when is unredirected from SYSWOW64 directory?
What are possible workarounds? The quickest I wrote is the following snippet:
bool iWindows = pFilename.StartsWith(#"c:\windows\", StringComparison.InvariantCultureIgnoreCase);
bool iWindowsSystem32 = pFilename.StartsWith(#"c:\windows\system32\", StringComparison.InvariantCultureIgnoreCase);
if ((iWindows == true) || (iWindowsSystem32 == true)) {
string pActualFileName;
if (iWindowsSystem32 == true)
pActualFileName = pFilename.Replace(#"c:\windows\system32\", #"c:\windows\syswow64\");
else
pActualFileName = pFilename.Replace(#"c:\windows\", #"c:\windows\syswow64\");
Am I missing something?
Try getting the assembly and then getting the assembly location, such as
System.Reflection.Assembly.GetExecutingAssembly().Location
Try to call Wow64DisableWow64FsRedirection before the usage of Process.MainModule.Filename. Verifying that the program are running on 64-bit operation system with IsWow64Process or Environment.Is64BitOperatingSystem (if you use .NET 4.0) before usage of Wow64DisableWow64FsRedirection is recommended.
UPDATED: I am sure that you have a small error in your code or the problem can be on .NET runtines which you have installed. I tested your problem with respect of the following test code
using System;
using System.Diagnostics;
namespace Win64ProcesPath {
class Program {
static void Main (string[] args) {
Process myProcess = new Process ();
try {
myProcess.StartInfo.UseShellExecute = false;
myProcess.StartInfo.FileName = "calc.exe";
myProcess.StartInfo.CreateNoWindow = true;
myProcess.Start ();
System.Threading.Thread.Sleep (1000);
Console.WriteLine ("{0}", myProcess.MainModule.FileName);
Process p = Process.GetProcessById (myProcess.Id);
Console.WriteLine ("{0}", p.MainModule.FileName);
//Process p32 = Process.GetProcessById (8048);
//Console.WriteLine ("{0}", p32.MainModule.FileName);
}
catch (Exception e) {
Console.WriteLine (e.Message);
}
}
}
}
with .NET 4.0 and Visual Studio 2010 installed on Vindows 7 64-bit (x64). On Vindows 7 64-bit there are two version of calc.exe: one 32-bit under C:\Windows\SysWOW64\calc.exe and another 64-bit under C:\Windows\system32\calc.exe. How can easy verify the files has different file size (776,192 and 918.528 bytes). If I compile the program as 64-bit program it starts C:\Windows\system32\calc.exe and Process.GetProcessById(processId).MainModule.FileName shows also correct file name. One can also use Process.GetProcessById() to get correct path of 32-bit version of calc.exe which are started separately (see commented lines). So 64-bit versin of this program has no problem in my envoronment.
If you do have 32-bit application you will be able to access to the full filesystem after the call of Wow64DisableWow64FsRedirection, but you will not be able to access the memory of 64-bit programs and Process.MainModule will throw the excepion System.ComponentModel.Win32Exception with the code NativeErrorCode: 299 and the Message: "A 32 bit processes cannot access modules of a 64 bit process." To be able to get the full filename of 64-bit application you should use API with get you results produced from a 64-bit operation system component (like WMI and so on). But it's already another probelm, because how you wrote your program is 64-bit program.

Categories

Resources