DLeh
DLeh

Reputation: 24385

C# file not found in System32 when running in 32 bit

I'm trying to run quser and get the resulting string. When I run the code below, I see the following message the the resulting string is empty:

'quser' is not recognized as an internal or external command, operable program or batch file.

Process p = new Process();
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.FileName = "cmd.exe";
p.StartInfo.Arguments = "/c quser /server:SomeServer";
p.Start();

string output = p.StandardOutput.ReadToEnd();

p.WaitForExit();
Console.WriteLine(output);

So the full command that gets run is

cmd.exe /c quser /ser:SomeServer

Which runs fine when I do it directly, but fails from C#.

I found a similar question with no answer here: Executing Quser windows command in C#; Returning result to String. This question doesn't have the quser not recognized message though.

Why would the command not be recognized when running from code?

I tried running the quser command directly like so, but I get a file not found... strange

Process p = new Process();
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.FileName = @"c:\Windows\System32\quser.exe";
p.StartInfo.Arguments = @"/server:SomeServer";
p.Start();

string output = p.StandardOutput.ReadToEnd();

p.WaitForExit();
Console.WriteLine(output);

We found that running it in 64 bit, it finds it. When running as AnyCPU or 32bit, it seems to be looking in SysWOW64, even when I directly tell it to look in System32

Upvotes: 3

Views: 1859

Answers (1)

Der Kommissar
Der Kommissar

Reputation: 5943

Alright, so I found a solution.

Basically, as we found in chat, the System32 folder is redirecting to SysWOW64 on certain builds, causing the quser to appear to not be there.

I have successfully applied a workaround.

To workaround it:

  1. Use the following namespace:

     using System.Runtime.InteropServices;
    
  2. Add the following at the top of your class file.

     [DllImport("kernel32.dll", SetLastError = true)]
     public static extern int Wow64DisableWow64FsRedirection(ref IntPtr ptr);
     [DllImport("kernel32.dll", SetLastError = true)]
     public static extern int Wow64EnableWow64FsRedirection(ref IntPtr ptr);
    
  3. Before making the quser call, make the following calls:

     IntPtr val = IntPtr.Zero;
     Wow64DisableWow64FsRedirection(ref val);
    
  4. After making your quser call, revert the changes:

     Wow64EnableWow64FsRedirection(ref val);
    

Full sample:

using System.Runtime.InteropServices;

...
namespace CSharpTests
{
    public class Program
    {
        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern int Wow64DisableWow64FsRedirection(ref IntPtr ptr);
        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern int Wow64EnableWow64FsRedirection(ref IntPtr ptr);
        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern int Wow64RevertWow64FsRedirection(ref IntPtr ptr);

        static void Main(string[] args)
        {
            IntPtr val = IntPtr.Zero;
            Wow64DisableWow64FsRedirection(ref val);
            Process p = new Process();
            p.StartInfo.UseShellExecute = false;
            p.StartInfo.RedirectStandardOutput = true;
            p.StartInfo.FileName = "cmd.exe";
            p.StartInfo.Arguments = "/c quser";
            p.Start();

            string output = p.StandardOutput.ReadToEnd();

            p.WaitForExit();
            Console.WriteLine(output);
            Wow64RevertWow64FsRedirection(ref val);
            p = new Process();
            p.StartInfo.UseShellExecute = false;
            p.StartInfo.RedirectStandardOutput = true;
            p.StartInfo.FileName = "cmd.exe";
            p.StartInfo.Arguments = "/c quser";
            p.Start();

            output = p.StandardOutput.ReadToEnd();

            p.WaitForExit();
            Console.WriteLine(output);
        }
    }
}

Results:

USERNAME              SESSIONNAME        ID  STATE   IDLE TIME  LOGON TIME
ebrown                console             1  Active      none   05/18/2015 09:2
1
'quser' is not recognized as an internal or external command,
operable program or batch file.

As you can see, the first quser call succeeded, as we told the OS to stop redirecting to SysWOW64, once we re-enabled it, the call failed.

I'm sure there's a reason for this protection, but sometimes you don't need it.

Additional considerations:

It would be prudent of someone implementing this pattern to first detect if the workaround needs applied. Such detection could be done by using the following boolean:

Environment.GetFolderPath(Environment.SpecialFolder.SystemX86).Contains("System32")

In the case of a false boolean, then you would need to check that:

File.Exists(@"c:\windows\System32\FILENAMEHERE")

in this case:

File.Exists(@"c:\windows\System32\qdisk.exe")

Adapted from:

http://blog.airesoft.co.uk/2010/09/wow-disabling-wow64-fs-redirection-can-cause-problems-who-knew/

https://msdn.microsoft.com/en-us/library/windows/desktop/aa384187%28v=vs.85%29.aspx

Upvotes: 6

Related Questions