C# works on 32bit but not 64bit - c#

The code below works perfectly on my 32bit machine but I have tested the code now on my 64bit machine, I expected it to work as I was calling the 64bit version of the cscript.exe.
Instead the code gets to the point where it runs the script and then waits for exactly 30seconds then exits the script and continues the rest of the program. The script however appears not to run, (it works fine if I run it manually).
using (var ServerProcess = new System.Diagnostics.Process())
{
var fileInformation = new FileInfo(VBScriptToRun);
string processFileName = IntPtr.Size == 8 ? #"c:\windows\sysWOW64\cscript.exe " : #"c:\windows\system32\cscript.exe ";
string processWorkDir = IntPtr.Size == 8 ? #"c:\windows\sysWOW64\" : #"c:\windows\system32\";
string processArguments = fileInformation.FullName;
ServerProcess.StartInfo.FileName = processFileName;
ServerProcess.StartInfo.WorkingDirectory = processWorkDir;
ServerProcess.StartInfo.Arguments = processArguments;
ServerProcess.StartInfo.CreateNoWindow = false;
ServerProcess.StartInfo.UseShellExecute = false;
ServerProcess.StartInfo.RedirectStandardOutput = true;
ServerProcess.StartInfo.LoadUserProfile = true;
EventLogger.Instance.WriteInformation("Total Integration Service Processing File: Starting to launch the specified program");
try
{
ServerProcess.Start();
ServerProcess.WaitForExit();
}catch(Exception e)
{
EventLogger.Instance.WriteInforamtion("Error running script: " + e)
}

// Sample for the Environment.GetFolderPath method
using System;
class Sample
{
public static void Main()
{
Console.WriteLine();
Console.WriteLine("GetFolderPath: {0}",
Environment.GetFolderPath(Environment.SpecialFolder.System));
}
}
/*
This example produces the following results:
GetFolderPath: C:\WINNT\System32
*/
You should not be trying to access the sysWOW64 folder that is the location of 32-bit windows assemblies. Since you indicated that cscript.exe is a 64-bit process the location of cscript.exe on Windows 7 x64 installation would be the System directory
Source: http://msdn.microsoft.com/en-us/library/system.environment.specialfolder
You should also use the following to determine if the operating system is 64-bit or not.
public static bool Is64BitOperatingSystem { get; }
http://msdn.microsoft.com/en-us/library/system.environment.is64bitoperatingsystem.aspx
I should point out that your current method failing because its trying to [based on the lack information this is only a guess] start the 32-bit process. IntPtr.Size is dependant on the process rather than machine.
If you want to use your method your limited to using the following code to do so.
[DllImport("kernel32.dll", SetLastError=true)]
[return:MarshalAs(UnmanagedType.Bool)]
extern static bool IsWow64Process(IntPtr hProcess, [MarshalAs(UnmanagedType.Bool)] out bool isWow64);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError=true)]
extern static IntPtr GetCurrentProcess();
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
extern static IntPtr GetModuleHandle(string moduleName);
[DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError=true)]
extern static IntPtr GetProcAddress(IntPtr hModule, string methodName);
You could use
System.Environment.GetEnvironmentVariable( "PROCESSOR_ARCHITECTURE" )
Except that it will return x86 if the process is a 32-bit process.
You are better of using the .NET 4.0 methods.
You could also just use this:
public static bool Is64BitProcess { get; }
This way you know which cscript.exe to actually launch. If your process is 64-bit you should only communicate with a 64-bit process. If its 32-bit then launch only the 32-bit process.
I do believe Windows 7 x64 keeps multiple versions for this exact perhaps in the System and sysWOW64 system directories.
If the process is not actually a 64-bit process then it won't be located at c:\windows\system32 on a 64-bit installation. Looking into it [ why am I forced to research this instead of you? ] Environment.SpecialFolder.SystemX86 will point to the correct location.

Related

How to reliably start a process in C#?

I'm trying to start .lnk shortcuts from my application. However, I'm struggling with the infamous automatic filesystem redirection for 32/64-bit processes.
I'm searching for a way to simply start an application from the shortcut and I don't care, what happens to that process later. Effectively I'd like to start the shortcut the same way as if user doubleclicked it in the Explorer.
Currently I'm using the following method, but it still doesn't work (ie. I'm unable to start Word this way):
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool Wow64DisableWow64FsRedirection(ref IntPtr ptr);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool Wow64RevertWow64FsRedirection(IntPtr ptr);
private static void Start(string shortcut)
{
IntPtr temp = IntPtr.Zero;
try
{
Wow64DisableWow64FsRedirection(ref temp);
var error = Marshal.GetLastWin32Error();
ProcessStartInfo info = new ProcessStartInfo(shortcut);
info.UseShellExecute = true;
Process.Start(info);
}
finally
{
Wow64RevertWow64FsRedirection(temp);
}
}
How can I reliably start an application, knowing its direct location on the drive in C#?
Edit:
I used SysInternals' ProcMon to check, what is done behind scenes. It looks like the proper path is searched, but then system for some reason still falls back to the forced 32-bit one.

How do I detect if the current Windows is installed in UEFI or legacy mode

My Goal is to simply get, if the current Windows installation is in UEFI or legacy mode. Manually I could just run msinfo32 and look at the "Bios-Mode" row. My current approach is by checking if the "HKLM\SYSTEM\CurrentControlSet\Control\SecureBoot" key exists, but I'm not sure if that's a good approach.
I have already tried looking at quite some WMI classes and properties but neither of them had the information I needed. I read that you could use GetFirmwareType() but it seems like that had been introduced in Windows 8 and I want it to run on Windows 7 as well. I also tried the method microsoft gave me, calling GetFirmwareEnvironmentVariableA with a dummy variable and a dummy namespace.
public const int ERROR_INVALID_FUNCTION = 1;
[DllImport("kernel32.dll",
EntryPoint = "GetFirmwareEnvironmentVariableA",
SetLastError = true,
CharSet = CharSet.Unicode,
ExactSpelling = true,
CallingConvention = CallingConvention.StdCall)]
public static extern int GetFirmwareType(string lpName, string lpGUID, IntPtr pBuffer, uint size);
public static bool IsWindowsUEFI()
{
// Call the function with a dummy variable name and a dummy variable namespace (function will fail because these don't exist.)
GetFirmwareType("", "{00000000-0000-0000-0000-000000000000}", IntPtr.Zero, 0);
if (Marshal.GetLastWin32Error() == ERROR_INVALID_FUNCTION)
{
// Calling the function threw an ERROR_INVALID_FUNCTION win32 error, which gets thrown if either
// - The mainboard doesn't support UEFI and/or
// - Windows is installed in legacy BIOS mode
return false;
}
else
{
// If the system supports UEFI and Windows is installed in UEFI mode it doesn't throw the above error, but a more specific UEFI error
return true;
}
}
It told me when I get ERROR_INVALID_FUNCTION, I am running legacy, otherwise it will return a different, more specific error. All I ever got from that code was ERROR_INVALID_PARAMETER on any type of system and I don't know where my mistake is.
That was just a Typo.
3 [DllImport("kernel32.dll",
4 > EntryPoint = "GetFirmwareEnvironmentVariableA",
5 SetLastError = true, ^
Replace the A pointed by the > and the ^ with a W.

Windows Defender Antivirus scan from C# [AccessViolation exception]

We are writing a code to do on-demand scan of a file from C# using Windows Defender APIs.
[DllImport(#"C:\Program Files\Windows Defender\MpClient.dll")]
public static extern int WDStatus(out bool pfEnabled);
[DllImport(#"C:\Program Files\Windows Defender\MpClient.dll")]
public static extern int MpManagerOpen(uint dwReserved, out IntPtr phMpHandle);
[DllImport(#"C:\Program Files\Windows Defender\MpClient.dll")]
public static extern int MpScanStart(IntPtr hMpHandle, uint ScanType, uint dwScanOptions, IntPtr pScanResources, IntPtr pCallbackInfo, out IntPtr phScanHandle);
[DllImport(#"C:\Program Files\Windows Defender\MpClient.dll")]
public static extern int MpHandleClose(IntPtr hMpHandle);
private void DoDefenderScan_Click(object sender, EventArgs e)
{
try
{
bool pfEnabled;
int result = WDStatus(out pfEnabled); //Returns the defender status - It's working properly.
ErrorHandler.ThrowOnFailure(result, VSConstants.S_OK);
IntPtr phMpHandle;
uint dwReserved = 0;
IntPtr phScanHandle;
MpManagerOpen(dwReserved, out phMpHandle); //Opens Defender and returns the handle in phMpHandle.
tagMPRESOURCE_INFO mpResourceInfo = new tagMPRESOURCE_INFO();
mpResourceInfo.Path = "eicar.com";
mpResourceInfo.Scheme = "file";
mpResourceInfo.Class = IntPtr.Zero;
tagMPRESOURCE_INFO[] pResourceList = new tagMPRESOURCE_INFO[1];
pResourceList.SetValue(mpResourceInfo, 0);
tagMPSCAN_RESOURCES scanResource = new tagMPSCAN_RESOURCES();
scanResource.dwResourceCount = 1;
scanResource.pResourceList = pResourceList;
IntPtr resourcePointer = StructToPtr(scanResource);
result = MpScanStart(phMpHandle, 3, 0, resourcePointer, IntPtr.Zero, out phScanHandle); **//Getting Access violation exception here**.
MpHandleClose(phMpHandle);
MpHandleClose(phScanHandle);
Marshal.FreeHGlobal(resourcePointer);
}
catch (Exception)
{ }
}
And the structure is defined here.
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct tagMPSCAN_RESOURCES
{
public uint dwResourceCount;
[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.Struct, SizeConst = 1)]
public tagMPRESOURCE_INFO[] pResourceList;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct tagMPRESOURCE_INFO
{
[MarshalAs(UnmanagedType.LPWStr)]
public String Scheme;
[MarshalAs(UnmanagedType.LPWStr)]
public String Path;
public IntPtr Class;
}
public class MPRESOURCE_CLASS
{
public uint Value;
}
private static IntPtr StructToPtr(object obj)
{
var ptr = Marshal.AllocHGlobal(Marshal.SizeOf(obj));
Marshal.StructureToPtr(obj, ptr, false);
return ptr;
}
The code is written based on the documentation available at
https://msdn.microsoft.com/en-us/library/vs/alm/dn920144(v=vs.85).aspx
We are getting this exception
Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
at
result = MpScanStart(phMpHandle, 3, 0, resourcePointer, IntPtr.Zero, out phScanHandle); **//Getting Access violation exception here**.
What could be the problem? Is the format of struct is correct?
P.S - No information about MPRESOURCE_CLASS is available in msdn.
I'm not sure, whether this line of code is correct.
mpResourceInfo.Class = IntPtr.Zero;
Update:
Quick scan is working fine with this code:
result = MpScanStart(phMpHandle, 1, 0, IntPtr.Zero, IntPtr.Zero, out phScanHandle);
Defender logs in the event viewer [ Applications and Services Logs-Microsoft-Windows-Windows Defender/Operational ] as
Windows Defender scan has started.
Scan ID:{CDC2AC0D-7648-4313-851C-4D8B7B5EB5CD}
Scan Type:AntiSpyware
Scan Parameters:Quick Scan
I couldn't identify the problem here. So I ended up with Antimalware Scan Interface (AMSI) available starting from Windows 10.
I have written a sample C# code here.
One thing I found is AMSI requires Windows defender/any antivirus to be turned on to verify the file passed to API. But triggering a scan through MpClient.dllwill trigger a defender scan even if defender is turned off.
Also ensure your project targets x64 platform.
public enum AMSI_RESULT
{
AMSI_RESULT_CLEAN = 0,
AMSI_RESULT_NOT_DETECTED = 1,
AMSI_RESULT_DETECTED = 32768
}
[DllImport("Amsi.dll", EntryPoint = "AmsiInitialize", CallingConvention = CallingConvention.StdCall)]
public static extern int AmsiInitialize([MarshalAs(UnmanagedType.LPWStr)]string appName, out IntPtr amsiContext);
[DllImport("Amsi.dll", EntryPoint = "AmsiUninitialize", CallingConvention = CallingConvention.StdCall)]
public static extern void AmsiUninitialize(IntPtr amsiContext);
[DllImport("Amsi.dll", EntryPoint = "AmsiOpenSession", CallingConvention = CallingConvention.StdCall)]
public static extern int AmsiOpenSession(IntPtr amsiContext, out IntPtr session);
[DllImport("Amsi.dll", EntryPoint = "AmsiCloseSession", CallingConvention = CallingConvention.StdCall)]
public static extern void AmsiCloseSession(IntPtr amsiContext, IntPtr session);
[DllImport("Amsi.dll", EntryPoint = "AmsiScanString", CallingConvention = CallingConvention.StdCall)]
public static extern int AmsiScanString(IntPtr amsiContext, [InAttribute()] [MarshalAsAttribute(UnmanagedType.LPWStr)]string #string, [InAttribute()] [MarshalAsAttribute(UnmanagedType.LPWStr)]string contentName, IntPtr session, out AMSI_RESULT result);
[DllImport("Amsi.dll", EntryPoint = "AmsiScanBuffer", CallingConvention = CallingConvention.StdCall)]
public static extern int AmsiScanBuffer(IntPtr amsiContext, [In] [MarshalAs(UnmanagedType.LPArray)] byte[] buffer, uint length, [In()] [MarshalAs(UnmanagedType.LPWStr)] string contentName, IntPtr session, out AMSI_RESULT result);
//This method apparently exists on MSDN but not in AMSI.dll (version 4.9.10586.0)
[DllImport("Amsi.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
public static extern bool AmsiResultIsMalware(AMSI_RESULT result);
private void CallAntimalwareScanInterface()
{
IntPtr amsiContext;
IntPtr session;
AMSI_RESULT result = 0;
int returnValue;
returnValue = AmsiInitialize("VirusScanAPI", out amsiContext); //appName is the name of the application consuming the Amsi.dll. Here my project name is VirusScanAPI.
returnValue = AmsiOpenSession(amsiContext, out session);
returnValue = AmsiScanString(amsiContext, #"X5O!P%#AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*", "EICAR", session, out result); //I've used EICAR test string.
AmsiCloseSession(amsiContext, session);
AmsiUninitialize(amsiContext);
}
I've been searching about problem and I've read this as one of the possible causes:
"You often see differences between debug and release builds because
debug builds contain extra metadata to assist in debugging."
here: https://social.msdn.microsoft.com/Forums/vstudio/en-US/4f48c152-68cd-45ec-a11e-baa7de7f79c3/attempted-to-read-or-write-protected-memory?forum=csharpgeneral
Also you should check this answer to "Is it possible to catch an access violation exception in .NET?" and the further details that are explained in the article Handling Corrupted State Exceptions in MSDN magazine
...
So, according to that answers and articles I'd try:
1st Double check signatures and COM interop thunks for all unmanaged code to verify that they're correct.
2nd Set Visual Studio Debugger to bypass this exception:
Tools menu ->Options -> Debugging -> General -> Uncheck this option "Suppress JIT optimization on module load"
3rd Try-Catch the exception
(note: if you are using .Net 4 then in App.config, within the tag modify runtime to include legacyCorruptedStateExceptionsPolicy enabled="true"like:
<runtime>
<legacyCorruptedStateExceptionsPolicy enabled="true"/>
</runtime>
)
In addition, here, I've found that some .net framework versions (latest comment point to 4.6.1 in one of the answer's comments) has a bug related with this exception and the solution, in the past, has been upgrading the framework.
Also, in the one of that answers I've read:
Hi There are two possible reasons.
1.We have un-managed code and we are calling it from managed code. that is preventing to run this code. try running these commands and
restart your pc
cmd: netsh winsock reset
open cmd.exe and run command "netsh winsock reset catalog"
2.Anti-virus is considering un-managed code as harmful and restricting to run this code disable anti-virus and then check
I'd like to know if some of these approaches helps you to solve your issue.
I really hope this helps.
KR,
Juan
You may use Antimalware Scan Interface to check file for malware.
The Antimalware Scan Interface (AMSI) is a generic interface standard that allows applications and services to integrate with any antimalware product present on a machine. It provides enhanced malware protection for users and their data, applications, and workloads.
It's available starting from Windows 10.
Windows Defender comes with CLI tool 'MpCmdRun' - it's not a full-sized antivirus app, but an API interface to the actual Windows Defender that's always (?) running in background.
Saving to a temporary file via Path.GetTempFileName() and then running a scan like this
MpCmdRun.exe -Scan -ScanType 3 -File "c:\path\to\temp\file" -DisableRemediation
works fine even in an ASP.NET (Core) app, that runs under app-pool identity
I've actually written a small (40 lines of code) C# helper that does everything for you (saves temp file, runs a scan, cleans up)
https://github.com/jitbit/WinDefender/blob/main/WinDefender.cs

How To Add Resources Without Compatibility Error?

The code below creates a copy of the application and adds resources to the copy. When you run the copy that has resources in it, it does it's job first. But when it exits, it exits with Program Compatibility Assistant error:
Image is from Google.
class Program
{
[DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
public static extern IntPtr BeginUpdateResource([MarshalAs(UnmanagedType.LPStr)] string filename, bool deleteExistingResources);
[DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
public static extern bool UpdateResource(IntPtr resource, [MarshalAs(UnmanagedType.LPStr)] string type, [MarshalAs(UnmanagedType.LPStr)] string name, ushort language, IntPtr data, uint dataSize);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool EndUpdateResource(IntPtr resource, bool discardChanges);
private static void modifyResources(string filename)
{
IntPtr handle = BeginUpdateResource(filename, true);
UpdateResource(handle, "10", "1", 0, Marshal.StringToHGlobalAnsi("hello world"), (uint) 11);
EndUpdateResource(handle, false);
}
static void Main(string[] args)
{
string exeFilename = Process.GetCurrentProcess().MainModule.FileName;
string filename = Path.GetFileName(exeFilename);
string anotherFilename = exeFilename.Replace(filename, "_" + filename);
File.Copy(exeFilename, anotherFilename, true);
modifyResources(anotherFilename);
}
}
I don't get it. What mistakes do I make ?
More infos: Win 7 64x, App 86x
Notes (some of these made me think the error was gone):
maybe cleaning up imported libraries might help
maybe Assemblyname or namespace
seems the Compatibility Assistant checks too much and thinks something is wrong when the program does something different than the assistant expects.
project as single exe, no extra dlls (since extinguishing my extra dll, no error occurred)
definitely: error is not running-code related (empty main method)
definitely: error is filename related
Problem is the acquisitiveness of people! There are file names that are unwanted on Windows. Microsoft seems to prevent people from writing new installers. They spawn compatibility errors to prevent certain software from becoming popular and spreading.
Example:
If you use Resource Hacker (tested on Win7):
Go to the installation directory of Resource Hacker.
Run it, close it. No problem,
Rename ResHacker.exe to ResH Installer acker.exe
Run it, close it, see the problem.
Rename it to ResH 4kj545ui45kj4 acker.exe
Run it, close it. No problem.
Rename it back to ResHacker.exe
Punchline:
if (exeFilename.Contains("Installer") && exeFile.isCapableOfResourceManipulation())
makeProblem();
else
ignore();
// assembly info is checked too
// confirmed: after removing all unwanted keywords the error stays away
// even in my old project

32 bit or 64 bit using c# [duplicate]

In a .NET 2.0 C# application I use the following code to detect the operating system platform:
string os_platform = System.Environment.OSVersion.Platform.ToString();
This returns "Win32NT". The problem is that it returns "Win32NT" even when running on Windows Vista 64-bit.
Is there any other method to know the correct platform (32 or 64 bit)?
Note that it should also detect 64 bit when run as a 32 bit application on Windows 64 bit.
.NET 4 has two new properties in the Environment class, Is64BitProcess and Is64BitOperatingSystem. Interestingly, if you use Reflector you can see they are implemented differently in the 32-bit & 64-bit versions of mscorlib. The 32-bit version returns false for Is64BitProcess and calls IsWow64Process via P/Invoke for Is64BitOperatingSystem. The 64-bit version just returns true for both.
UPDATE: As Joel Coehoorn and others suggest, starting at .NET Framework 4.0, you can just check Environment.Is64BitOperatingSystem.
IntPtr.Size won't return the correct value if running in 32-bit .NET Framework 2.0 on 64-bit Windows (it would return 32-bit).
As Microsoft's Raymond Chen describes, you have to first check if running in a 64-bit process (I think in .NET you can do so by checking IntPtr.Size), and if you are running in a 32-bit process, you still have to call the Win API function IsWow64Process. If this returns true, you are running in a 32-bit process on 64-bit Windows.
Microsoft's Raymond Chen:
How to detect programmatically whether you are running on 64-bit Windows
My solution:
static bool is64BitProcess = (IntPtr.Size == 8);
static bool is64BitOperatingSystem = is64BitProcess || InternalCheckIsWow64();
[DllImport("kernel32.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool IsWow64Process(
[In] IntPtr hProcess,
[Out] out bool wow64Process
);
public static bool InternalCheckIsWow64()
{
if ((Environment.OSVersion.Version.Major == 5 && Environment.OSVersion.Version.Minor >= 1) ||
Environment.OSVersion.Version.Major >= 6)
{
using (Process p = Process.GetCurrentProcess())
{
bool retVal;
if (!IsWow64Process(p.Handle, out retVal))
{
return false;
}
return retVal;
}
}
else
{
return false;
}
}
If you're using .NET Framework 4.0, it's easy:
Environment.Is64BitOperatingSystem
See Environment.Is64BitOperatingSystem Property (MSDN).
This is just an implementation of what's suggested above by Bruno Lopez, but works on Win2k + all WinXP service packs. Just figured I'd post it so other people didn't have roll it by hand. (would have posted as a comment, but I'm a new user!)
[DllImport("kernel32", SetLastError = true, CallingConvention = CallingConvention.Winapi)]
public extern static IntPtr LoadLibrary(string libraryName);
[DllImport("kernel32", SetLastError = true, CallingConvention = CallingConvention.Winapi)]
public extern static IntPtr GetProcAddress(IntPtr hwnd, string procedureName);
private delegate bool IsWow64ProcessDelegate([In] IntPtr handle, [Out] out bool isWow64Process);
public static bool IsOS64Bit()
{
if (IntPtr.Size == 8 || (IntPtr.Size == 4 && Is32BitProcessOn64BitProcessor()))
{
return true;
}
else
{
return false;
}
}
private static IsWow64ProcessDelegate GetIsWow64ProcessDelegate()
{
IntPtr handle = LoadLibrary("kernel32");
if ( handle != IntPtr.Zero)
{
IntPtr fnPtr = GetProcAddress(handle, "IsWow64Process");
if (fnPtr != IntPtr.Zero)
{
return (IsWow64ProcessDelegate)Marshal.GetDelegateForFunctionPointer((IntPtr)fnPtr, typeof(IsWow64ProcessDelegate));
}
}
return null;
}
private static bool Is32BitProcessOn64BitProcessor()
{
IsWow64ProcessDelegate fnDelegate = GetIsWow64ProcessDelegate();
if (fnDelegate == null)
{
return false;
}
bool isWow64;
bool retVal = fnDelegate.Invoke(Process.GetCurrentProcess().Handle, out isWow64);
if (retVal == false)
{
return false;
}
return isWow64;
}
The full answer is this (taken from both stefan-mg, ripper234 and BobbyShaftoe's answer):
[DllImport("kernel32.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool IsWow64Process([In] IntPtr hProcess, [Out] out bool lpSystemInfo);
private bool Is64Bit()
{
if (IntPtr.Size == 8 || (IntPtr.Size == 4 && Is32BitProcessOn64BitProcessor()))
{
return true;
}
else
{
return false;
}
}
private bool Is32BitProcessOn64BitProcessor()
{
bool retVal;
IsWow64Process(Process.GetCurrentProcess().Handle, out retVal);
return retVal;
}
First check if you're in a 64 bit process. If you're not, check if the 32 bit process is a Wow64Process.
Microsoft has put a code sample for this:
http://1code.codeplex.com/SourceControl/changeset/view/39074#842775
It looks like this:
/// <summary>
/// The function determines whether the current operating system is a
/// 64-bit operating system.
/// </summary>
/// <returns>
/// The function returns true if the operating system is 64-bit;
/// otherwise, it returns false.
/// </returns>
public static bool Is64BitOperatingSystem()
{
if (IntPtr.Size == 8) // 64-bit programs run only on Win64
{
return true;
}
else // 32-bit programs run on both 32-bit and 64-bit Windows
{
// Detect whether the current process is a 32-bit process
// running on a 64-bit system.
bool flag;
return ((DoesWin32MethodExist("kernel32.dll", "IsWow64Process") &&
IsWow64Process(GetCurrentProcess(), out flag)) && flag);
}
}
/// <summary>
/// The function determins whether a method exists in the export
/// table of a certain module.
/// </summary>
/// <param name="moduleName">The name of the module</param>
/// <param name="methodName">The name of the method</param>
/// <returns>
/// The function returns true if the method specified by methodName
/// exists in the export table of the module specified by moduleName.
/// </returns>
static bool DoesWin32MethodExist(string moduleName, string methodName)
{
IntPtr moduleHandle = GetModuleHandle(moduleName);
if (moduleHandle == IntPtr.Zero)
{
return false;
}
return (GetProcAddress(moduleHandle, methodName) != IntPtr.Zero);
}
[DllImport("kernel32.dll")]
static extern IntPtr GetCurrentProcess();
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
static extern IntPtr GetModuleHandle(string moduleName);
[DllImport("kernel32", CharSet = CharSet.Auto, SetLastError = true)]
static extern IntPtr GetProcAddress(IntPtr hModule,
[MarshalAs(UnmanagedType.LPStr)]string procName);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool IsWow64Process(IntPtr hProcess, out bool wow64Process);
There is a WMI version available as well (for testing remote machines).
You can also check for the PROCESSOR_ARCHITECTURE environment variable.
It either doesn't exist or is set to "x86" on 32-bit Windows.
private int GetOSArchitecture()
{
string pa =
Environment.GetEnvironmentVariable("PROCESSOR_ARCHITECTURE");
return ((String.IsNullOrEmpty(pa) ||
String.Compare(pa, 0, "x86", 0, 3, true) == 0) ? 32 : 64);
}
From Chriz Yuen blog
C# .Net 4.0 Introduced two new environment property
Environment.Is64BitOperatingSystem;
Environment.Is64BitProcess;
Please be careful when you use these both property.
Test on Windows 7 64bits Machine
//Workspace: Target Platform x86
Environment.Is64BitOperatingSystem True
Environment.Is64BitProcess False
//Workspace: Target Platform x64
Environment.Is64BitOperatingSystem True
Environment.Is64BitProcess True
//Workspace: Target Platform Any
Environment.Is64BitOperatingSystem True
Environment.Is64BitProcess True
Quickest way:
if(IntPtr.Size == 8) {
// 64 bit machine
} else if(IntPtr.Size == 4) {
// 32 bit machine
}
Note: this is very direct and works correctly on 64-bit only if the program does not force execution as a 32-bit process (e.g. through <Prefer32Bit>true</Prefer32Bit> in the project settings).
Try this:
Environment.Is64BitOperatingSystem
Environment.Is64BitProcess
#foobar: You are right, it is too easy ;)
In 99% of the cases, developers with weak system administrator backgrounds ultimately fail to realize the power Microsoft has always provided for anyone to enumerate Windows.
System administrators will always write better and simpler code when it comes to such a point.
Nevertheless, one thing to note, build configuration must be AnyCPU for this environment variable to return the correct values on the correct systems:
System.Environment.GetEnvironmentVariable("PROCESSOR_ARCHITECTURE")
This will return "X86" on 32-bit Windows, and "AMD64" on 64-bit Windows.
Using dotPeek helps to see how the framework actually does it. With that in mind, here's what I've come up with:
public static class EnvironmentHelper
{
[DllImport("kernel32.dll")]
static extern IntPtr GetCurrentProcess();
[DllImport("kernel32.dll")]
static extern IntPtr GetModuleHandle(string moduleName);
[DllImport("kernel32")]
static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
[DllImport("kernel32.dll")]
static extern bool IsWow64Process(IntPtr hProcess, out bool wow64Process);
public static bool Is64BitOperatingSystem()
{
// Check if this process is natively an x64 process. If it is, it will only run on x64 environments, thus, the environment must be x64.
if (IntPtr.Size == 8)
return true;
// Check if this process is an x86 process running on an x64 environment.
IntPtr moduleHandle = GetModuleHandle("kernel32");
if (moduleHandle != IntPtr.Zero)
{
IntPtr processAddress = GetProcAddress(moduleHandle, "IsWow64Process");
if (processAddress != IntPtr.Zero)
{
bool result;
if (IsWow64Process(GetCurrentProcess(), out result) && result)
return true;
}
}
// The environment must be an x86 environment.
return false;
}
}
Example usage:
EnvironmentHelper.Is64BitOperatingSystem();
Use these two environment variables (pseudo code):
if (PROCESSOR_ARCHITECTURE = x86 &&
isDefined(PROCESSOR_ARCHITEW6432) &&
PROCESSOR_ARCHITEW6432 = AMD64) {
//64 bit OS
}
else
if (PROCESSOR_ARCHITECTURE = AMD64) {
//64 bit OS
}
else
if (PROCESSOR_ARCHITECTURE = x86) {
//32 bit OS
}
Refer to the blog post HOWTO: Detect Process Bitness.
I used this check with success on many operating systems:
private bool Is64BitSystem
{
get
{
return Directory.Exists(Environment.ExpandEnvironmentVariables(#"%windir%\SysWOW64"));
}
}
This folder is always named "SysWOW64", no matter of the language of the operating system. This works for .NET Framework 1.1 or above.
I need to do this, but I also need to be able as an admin do it remotely, either case this seems to work quite nicely for me:
public static bool is64bit(String host)
{
using (var reg = RegistryKey.OpenRemoteBaseKey(RegistryHive.LocalMachine, host))
using (var key = reg.OpenSubKey(#"Software\Microsoft\Windows\CurrentVersion\"))
{
return key.GetValue("ProgramFilesDir (x86)") !=null;
}
}
This is a solution based on Microsoft's code at http://1code.codeplex.com/SourceControl/changeset/view/39074#842775. It uses extension methods for easy code reuse.
Some possible usage is shown below:
bool bIs64BitOS = System.Environment.OSVersion.IsWin64BitOS();
bool bIs64BitProc = System.Diagnostics.Process.GetCurrentProcess().Is64BitProc();
//Hosts the extension methods
public static class OSHelperTools
{
/// <summary>
/// The function determines whether the current operating system is a
/// 64-bit operating system.
/// </summary>
/// <returns>
/// The function returns true if the operating system is 64-bit;
/// otherwise, it returns false.
/// </returns>
public static bool IsWin64BitOS(this OperatingSystem os)
{
if (IntPtr.Size == 8)
// 64-bit programs run only on Win64
return true;
else// 32-bit programs run on both 32-bit and 64-bit Windows
{ // Detect whether the current process is a 32-bit process
// running on a 64-bit system.
return Process.GetCurrentProcess().Is64BitProc();
}
}
/// <summary>
/// Checks if the process is 64 bit
/// </summary>
/// <param name="os"></param>
/// <returns>
/// The function returns true if the process is 64-bit;
/// otherwise, it returns false.
/// </returns>
public static bool Is64BitProc(this System.Diagnostics.Process p)
{
// 32-bit programs run on both 32-bit and 64-bit Windows
// Detect whether the current process is a 32-bit process
// running on a 64-bit system.
bool result;
return ((DoesWin32MethodExist("kernel32.dll", "IsWow64Process") && IsWow64Process(p.Handle, out result)) && result);
}
/// <summary>
/// The function determins whether a method exists in the export
/// table of a certain module.
/// </summary>
/// <param name="moduleName">The name of the module</param>
/// <param name="methodName">The name of the method</param>
/// <returns>
/// The function returns true if the method specified by methodName
/// exists in the export table of the module specified by moduleName.
/// </returns>
static bool DoesWin32MethodExist(string moduleName, string methodName)
{
IntPtr moduleHandle = GetModuleHandle(moduleName);
if (moduleHandle == IntPtr.Zero)
return false;
return (GetProcAddress(moduleHandle, methodName) != IntPtr.Zero);
}
[DllImport("kernel32.dll")]
static extern IntPtr GetCurrentProcess();
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
static extern IntPtr GetModuleHandle(string moduleName);
[DllImport("kernel32", CharSet = CharSet.Auto, SetLastError = true)]
static extern IntPtr GetProcAddress(IntPtr hModule, [MarshalAs(UnmanagedType.LPStr)]string procName);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool IsWow64Process(IntPtr hProcess, out bool wow64Process);
}
Here is the direct approach in C# using DllImport from this page.
[DllImport("kernel32.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool IsWow64Process([In] IntPtr hProcess, [Out] out bool lpSystemInfo);
public static bool Is64Bit()
{
bool retVal;
IsWow64Process(Process.GetCurrentProcess().Handle, out retVal);
return retVal;
}
I'm using the followin code. Note: It's made for an AnyCPU project.
public static bool Is32bitProcess(Process proc) {
if (!IsThis64bitProcess()) return true; // We're in 32-bit mode, so all are 32-bit.
foreach (ProcessModule module in proc.Modules) {
try {
string fname = Path.GetFileName(module.FileName).ToLowerInvariant();
if (fname.Contains("wow64")) {
return true;
}
} catch {
// What on earth is going on here?
}
}
return false;
}
public static bool Is64bitProcess(Process proc) {
return !Is32bitProcess(proc);
}
public static bool IsThis64bitProcess() {
return (IntPtr.Size == 8);
}
I found this to be the best way to check for the platform of the system and the process:
bool 64BitSystem = Environment.Is64BitOperatingSystem;
bool 64BitProcess = Environment.Is64BitProcess;
The first property returns true for 64-bit system, and false for 32-bit.
The second property returns true for 64-bit process, and false for 32-bit.
The need for these two properties is because you can run 32-bit processes on 64-bit system, so you will need to check for both the system and the process.
All fine, but this should also work from env:
PROCESSOR_ARCHITECTURE=x86
..
PROCESSOR_ARCHITECTURE=AMD64
Too easy, maybe ;-)
Here's a Windows Management Instrumentation (WMI) approach:
string _osVersion = "";
string _osServicePack = "";
string _osArchitecture = "";
ManagementObjectSearcher searcher = new ManagementObjectSearcher("select * from Win32_OperatingSystem");
ManagementObjectCollection collection = searcher.Get();
foreach (ManagementObject mbo in collection)
{
_osVersion = mbo.GetPropertyValue("Caption").ToString();
_osServicePack = string.Format("{0}.{1}", mbo.GetPropertyValue("ServicePackMajorVersion").ToString(), mbo.GetPropertyValue("ServicePackMinorVersion").ToString());
try
{
_osArchitecture = mbo.GetPropertyValue("OSArchitecture").ToString();
}
catch
{
// OSArchitecture only supported on Windows 7/Windows Server 2008
}
}
Console.WriteLine("osVersion : " + _osVersion);
Console.WriteLine("osServicePack : " + _osServicePack);
Console.WriteLine("osArchitecture: " + _osArchitecture);
/////////////////////////////////////////
// Test on Windows 7 64-bit
//
// osVersion : Microsoft Windows 7 Professional
// osservicePack : 1.0
// osArchitecture: 64-bit
/////////////////////////////////////////
// Test on Windows Server 2008 64-bit
// --The extra r's come from the registered trademark
//
// osVersion : Microsoftr Windows Serverr 2008 Standard
// osServicePack : 1.0
// osArchitecture: 64-bit
/////////////////////////////////////////
// Test on Windows Server 2003 32-bit
// --OSArchitecture property not supported on W2K3
//
// osVersion : Microsoft(R) Windows(R) Server 2003, Standard Edition
// osServicePack : 2.0
// osArchitecture:
OSInfo.Bits
using System;
namespace CSharp411
{
class Program
{
static void Main( string[] args )
{
Console.WriteLine( "Operation System Information" );
Console.WriteLine( "----------------------------" );
Console.WriteLine( "Name = {0}", OSInfo.Name );
Console.WriteLine( "Edition = {0}", OSInfo.Edition );
Console.WriteLine( "Service Pack = {0}", OSInfo.ServicePack );
Console.WriteLine( "Version = {0}", OSInfo.VersionString );
Console.WriteLine( "Bits = {0}", OSInfo.Bits );
Console.ReadLine();
}
}
}
Include the following code into a class in your project:
[DllImport("kernel32.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool IsWow64Process([In] IntPtr hProcess, [Out] out bool wow64Process);
public static int GetBit()
{
int MethodResult = "";
try
{
int Architecture = 32;
if ((Environment.OSVersion.Version.Major == 5 && Environment.OSVersion.Version.Minor >= 1) || Environment.OSVersion.Version.Major >= 6)
{
using (Process p = Process.GetCurrentProcess())
{
bool Is64Bit;
if (IsWow64Process(p.Handle, out Is64Bit))
{
if (Is64Bit)
{
Architecture = 64;
}
}
}
}
MethodResult = Architecture;
}
catch //(Exception ex)
{
//ex.HandleException();
}
return MethodResult;
}
Use it like so:
string Architecture = "This is a " + GetBit() + "bit machine";
Use this to get the installed Windows architecture:
string getOSArchitecture()
{
string architectureStr;
if (Directory.Exists(Environment.GetFolderPath(
Environment.SpecialFolder.ProgramFilesX86))) {
architectureStr ="64-bit";
}
else {
architectureStr = "32-bit";
}
return architectureStr;
}
Given that the accepted answer is very complex. There are simpler ways. Mine is a variation of alexandrudicu's anaswer.
Given that 64-bit windows install 32-bit applications in Program Files (x86) you can check if that folder exists, using environment variables (to make up for different localizations)
e.g.
private bool Is64BitSystem
{
get
{
return Directory.Exists(Environment.ExpandEnvironmentVariables(#"%PROGRAMFILES(X86)%"));
}
}
This for me is faster and simpler. Given that I also wish to access a specific path under that folder based on OS version.
This question is for .NET 2.0 but still comes up in a google search, and nobody here mentionned that since .NET standard 1.1 / .NET core 1.0, there is now a better way to know the CPU architecture:
System.Runtime.InteropServices.RuntimeInformation.ProcessArchitecture
This should theoretically be able to differenciate between x64 and Arm64, though I didn't test it myself.
See the documentation.
Sorry for code of VB.NET, but it easy to port to C#.
It works ok on Win7, x64 with .Net Framework 4.8:
Dim r As String = ""
Using searcher As ManagementObjectSearcher = New System.Management.ManagementObjectSearcher("SELECT * FROM Win32_OperatingSystem")
Dim Information As ManagementObjectCollection = searcher.Get()
If Information IsNot Nothing Then
For Each obj As ManagementObject In Information
r = obj("Caption").ToString() & _
" - " & _
obj("OSArchitecture").ToString() ' <--- !!! "64-bit" shown here
Next
End If
MessageBox.Show(r)
End Using
Enjoy ;-)
Function Is64Bit() As Boolean
Return My.Computer.FileSystem.SpecialDirectories.ProgramFiles.Contains("Program Files (x86)")
End Function
Just see if the "C:\Program Files (x86)" exists. If not, then you are on a 32 bit OS. If it does, then the OS is 64 bit (Windows Vista or Windows 7). It seems simple enough...
I use:
Dim drivelet As String = Application.StartupPath.ToString
If Directory.Exists(drivelet(0) & ":\Program Files (x86)") Then
MsgBox("64bit")
Else
MsgBox("32bit")
End if
This gets the path where your application is launched in case you have it installed in various places on the computer. Also, you could just do the general C:\ path since 99.9% of computers out there have Windows installed in C:\.

Categories

Resources