Situation
I am trying to run a command-line tool, DISM.exe, programmatically. When I run it manually it works, but when I try to spawn it with the following:
var systemPath = Environment.GetFolderPath(Environment.SpecialFolder.System);
var dism = new Process();
dism.StartInfo.FileName = Path.Combine(systemPath, "Dism.exe");
dism.StartInfo.Arguments = "/Online /Get-Features /Format:Table";
dism.StartInfo.Verb = "runas";
dism.StartInfo.UseShellExecute = false;
dism.StartInfo.RedirectStandardOutput = true;
dism.Start();
var result = dism.StandardOutput.ReadToEnd();
dism.WaitForExit();
Then my result comes out as:
Error: 11
You cannot service a running 64-bit operating system with a 32-bit version of DISM.
Please use the version of DISM that corresponds to your computer's architecture.
Problem
I'm actually already aware of what causes this: my project is set up to compile for an x86 platform. (See this question for example, although none of the answers mention this). However, unfortunately it is a requirement at the moment that we continue targeting this platform, I am not able to fix this by switching to Any CPU.
So my question is how to programmatically spawn a process in a way which is independent of the platform of its parent- i.e. keep my project targeting x86, but start a process which will target the correct platform for the machine it is on.
even though I'm running the correct DSIM.exe in System32
But you're not. That's the point. The file system redirector lies to 32-bit processes and so when you ask for System32 from an x86 process, you actually get the file from SysWow64. If you want to access the 64-bit exe, you need to ask for it via %windir%\sysnative
(%windir% being SpecialFolder.Windows)
While it's not answering your question about starting a 64 bit process from a 32 bit, an alternative approach to your underlying problem would be to query WMI to obtain the information you require. You can list optional features or list Server Features
This answer gives general information about performing a WMI query from C#.
You can also check and install windows features from powershell, which you might be able to spawn from your program instead of starting DISM.
Related
So I have built some code, it's quite simple basically it stops all active input from keyboard and mouse until a text file of a certain name appears in the C:\Temp directory. It also has a manifest file to run it as administrator on start up.
So I found something that on the surface looks like it fulfils my needs of being able to do this task however upon running it I found out that the project has been compiled in x86 and does not run on my x64 machine. Here is the reference to the project if anyone would like to look into it, it's a very smartly designed piece of code that does an interesting objective. It also explains clearly enough what I am trying to accomplish.
So after implementing this (and failing) I have setup a couple other avenues to try, one is VBA through excel with the VBA copying itself to and from the machines in a list and running itself, then there is using VBS to write the entire code as a txt file on the target machine change the extension and then execute it remotely. I have just started researching these but I imagine the problems of running as an administrator amongst other things will crop up again to be dealt with. To be honest though I would really prefer to do this in C# only as that is the language I'm trying to go further in so I'm interested in this challenge. If anybody knows of a similar library of code or application I could look into to achieve what I'm trying I would appreciate being pointed in the right direction.
I would try and be more specific about what libraires/API's im trying to implement but the truth is I don't know what libraries I need to even interact with to get what I want. My goal is to have C# executable code on my machine and a tool that can run that executable on another machine.
Thanks
Thanks to the help in comments from #Nick.McDermaid I was able to correctly open and build the project I was trying to download. Unsure what caused the issue previously with me not being able to open and interact with the code but now I have it I shall pursue this avenue further to accomplish my goal.
As an addendum one other avenue I tried for executing code remotely was through VBS where I used
set svcproc=getobject("winmgmts:{impersonationLevel=impersonate}!\\"&MachineName & "\root\cimv2:win32_process")
scmd="""C:\Program Files\Internet Explorer\iexplore.exe"" -framemerging ""https://gifyu.com/images/Boo-Ghost-Gif.gif"""
'scmd="C:\Windows\notepad.exe"
iret=svcproc.create(scmd,null,null,pid)
set svcproc=nothing
to execute something that existed on the remote machine but I ran into a LOT of security policy issues where I could launch the process but I couldn't bring it to the foreground as the Malware tracker on the machine thought it was an attack and quashed it immediately.
Above is a screen shot I took in Windows PE environment while applying an image to a machine. As you can see I have filtered the results of Windows Sysinternals Process Monitor so that only ReadFile operations on the Win8 32-bit image by the Dism.exe process are shown.
I am in need of finding a way to access this same information using C/C#/C++ without the .NET framework (because supposedly the .NET framework doesn't exist in PE environment). I have searched, asked around, and searched again for Namespaces and Classes that would allow me to extract this information from the kernel without luck.
I am most interested in having the ability to access the value "Offset" that you can see in the image. This number is the location offset of the Read operation on the file and I need this value for the program I am required to make.
Does anybody know how I can achieve my goal? This is obviously possible to do in the PE environment because Process Monitor does it, so how can I do the same thing?
You might want to look at file mini-filters. Process Explorer basically installs one on the system when you start it for the first time. The mini-filter can be installed and started without the need for a reboot.
The mini-filter runs inside the kernel like any other device drivers and from there you can monitor any file activity (read, write, create, delete, close, execute, etc.)
You'd have to write this in C. You can also get info from the OSR distribution list.
I want to start the default windows sound record in a Winforms app (c++/cli).In "Run" dialog, the command for Vista/7 is "soundrecorder". So I began with the easiest way:
System::Diagnostics::Process::Start("soundrecorder");
but it throws a System.ComponentModel.Win32Exception (Message: The System Cannot Find the File Specified). So I used the real path:
String^ path = Environment::GetFolderPath(Environment::SpecialFolder::System);
path = System::IO::Path::Combine(path, "soundrecorder.exe");
System::Diagnostics::Process::Start(path);
Same result. Hard-coding full path fail too. Any other command like "mspaint" runs correctly.
I thought that the problem was in my environment/current user/machine. Then I write a stupid program in C#:
public abstract class StupidProgram{
public static void Main(string[] args)
{
Process.Start("soundrecorder");
}
}
It works, and even more: If I run "StupidProgram.exe" from my app, it works too.
The application is coded in C++/CLI as 32-bits program. I'm working in Windows 7 64bits. I tested the app in a Windows 7 32bits and it works, so it seams a 32/64 bits compatibility issue.
Do you have any idea about this behaviour?
Quoting your C++/CLI code:
System::Diagnostics::Process::Start("soundrecord");
Quoting your C# code:
Process.Start("soundrecorder");
^^
Specifying "soundrecord" doesn't work, because it's called "soundrecord er"
Without the exact exception text, I think specifying the full path doesn't work because your application is being compiled as x86, and you're running 64-bit Windows, and the OS is doing filesystem redirection.
I recommend you use the equivalent to your C# command: Windows has the functionality to search the path for the executable, let it do so. Hard-coding the full path will give compatibility issues in the future, when MS decides to move it.
System::Diagnostics::Process::Start("soundrecorder");
^^
I have a program that is doing raw IO to disks within Windows.
All works fine if the target disk is online. However, the default behavior in some Windows OSes is to have new disks initially offline.
I am having a hard time finding the correct API to do this on Windows. The command line equivalent would be something like:
"select disk 2", "online disk" | diskpart
However I need to be able to do this in code. I looked through the DeviceIoControl Win32 API (which I think is right) but cannot determine which control code to use. The fact that I can't find it makes me think I might be missing a better API to use.
For future generations, the answer (on Win 2k3/Vista and later) is the Virtual Disk Service (VDS). There's some work getting it all together, especially if you don't use COM objects within .NET that much.
Disk online/offline is done with IVdsDrive::SetStatus. At least it should; I found that I could solve my problem with simply disabling read-only status on my disk. I was able to do this with IVdsDisk::SetFlags with the appropriate flag value.
This question has a couple useful links to the Windows API, including the DeviceIOControl method.
After looking through all of the enumerations, I could not find anything related to bringing a disk online, or make any interesting change to the disk beyond formatting/partitions. This is likely because only hot-swappable hard drives are supported by this functionality. The market for hot-swappable hard drives is very small, and the vast majority of those situations there are drivers to support any needed operations. Finally the remainder should be able to use the diskpart tool for whatever is necessary.
You need to look again at your requirements I think. You are running a process that has the rights necessary to online a hard disk, but cannot access a command line program? Here are some suggestions for common reasons to not use a command line program:
Can't have a black screen pop up - tons of solutions to this problem available online
Security team won't allow it - you are already running the process as an administrator so you trust it, why wouldn't you trust the built in Windows function
Technical problems preclude calling other processes - I would be interested in how this was managed given the process is running as an administrator
Coding guidelines such as "Always use the API" - there isn't one due to lack of need
Not sure about C#, but I'm using this in C++:
Try calling DeviceIoControl() with IOCTL_DISK_SET_DISK_ATTRIBUTES. The file handle must have read and write access. I think it requires at least Windows 7. It doesn't work on Windows 2003 x64. Windows 8 successfully takes the disk offline and then you can rewrite it from a backup.
BOOL disk_offline(HANDLE h_file, bool enable){
DWORD bytes_returned = 0;
BOOL b_offline = 0;
if(get_size_volume_disk(h_file)){
SET_DISK_ATTRIBUTES disk_attr;
ZeroMemory(&disk_attr, sizeof(disk_attr));
disk_attr.Version = sizeof(SET_DISK_ATTRIBUTES);
disk_attr.Attributes = enable? DISK_ATTRIBUTE_OFFLINE: 0;
disk_attr.AttributesMask = DISK_ATTRIBUTE_OFFLINE;
b_offline = DeviceIoControl(h_file, IOCTL_DISK_SET_DISK_ATTRIBUTES, &disk_attr, disk_attr.Version, NULL, 0, &bytes_returned, NULL);
// Invalidates the cached partition table and re-enumerates the device.
if(!enable) BOOL b_update = DeviceIoControl(h_file, IOCTL_DISK_UPDATE_PROPERTIES, NULL, 0, NULL, 0, &bytes_returned, NULL);
}
return b_offline;
}
Using DeviceIoControl and IOCTL_DISK_IS_WRITABLE control code, it is possible to check if disk is writable. If the disk is offline it returns false. This means that it is possible to determine if disk is offline and it works fine with Windows 2003 and after. However, I could not find any useful IOCTL to bring the disk online on Windows 2003. IOCTL_DISK_SET_DISK_ATTRIBUTES only works with Windows 2008 and after.
I’m trying to launch an application (Operating System, My Application and the application I want to launch are all 32 bits), from .NET 3.51.
The code that launches the Process is used for other applications, but there’s one that is giving us a headache. If we “double click” on the application’s icon, it works as expected, meaning that it works fine as an application in the computer. Double clicking the .exe directly, also works.
The operating system is Windows 7 32Bits (Home and/or Professional).
Our .NET application is compiled with x86 to avoid problems.
The code that launches “Processes” is located inside a DLL (also 32 bits) made by us, basically it’s a simple DLL that holds some “Common Code” across the board, common methods, functions and stuff we use throughout our code. One of those methods look like this:
public static bool FireUpProcess( Process process, string path, bool enableRaisingEvents,
ProcessWindowStyle windowStyle, string arguments )
{
if ( process != null )
{
try
{
process.StartInfo.FileName = #path;
if ( arguments != null )
{
if ( arguments != String.Empty )
{
process.StartInfo.Arguments = arguments;
}
}
process.StartInfo.WindowStyle = windowStyle;
process.EnableRaisingEvents = enableRaisingEvents;
process.Start();
}
catch
{
try
{
process.Kill();
}
catch ( InvalidOperationException )
{
} // The process is not even created
return false;
}
}
else
{
return false;
}
return true;
}
I don’t know who wrote this method, but it has been working for roughly six years with different applications, therefore I assume it’s “ok”. However, we have a customer with a piece of software that won’t launch when passed through that argument.
The arguments are:
process is a System.Diagnostics.Process created with a simple "new Process();”
path is a full path to the .exe “c:/path/to/my.exe”.
enableRaisingEvents is false
windowStyle is Maximized (but have tried others).
It gives a crappy MessageBox… which I have happily immortalized. It’s in spanish but the translation ought to be easy:
It says:
Application Error
An unexpected exception has occurred for the program (0x0eedfade) at …
Googling that 0x0eedfade gives strange results that look scary, but the truth is, if I go to the .exe that I’m trying to launch and double click it, it works perfectly.
For The Record: If I try to launch other things (I.e.: Notepad.exe, Adobe Acrobat Reader) it works, but Firefox doesn’t open and doesn’t show an error.
This “some work, some doesn’t” behavior leads me to believe that there might be a problem with a Windows 7 security mechanism or similar that I don’t know.
What am I missing or doing wrong?
UPDATE: Ok; I’ve gotten a copy of the software. It’s a messy software but it works. Now that I can debug, I see that the program gives an error when launched with my FireUpProcess method.
As suggested I added the WorkingDirectory code, but here’s the code:
public static bool FireUpProcess(Process process, string path, bool enableRaisingEvents, ProcessWindowStyle windowStyle)
{
if (process != null)
{
try
{
if ( !String.IsNullOrEmpty(#path) )
{
process.StartInfo.FileName = #path;
process.StartInfo.WorkingDirectory = System.IO.Path.GetDirectoryName(#path);
process.StartInfo.WindowStyle = windowStyle;
// Suscribe to the exit notification
process.EnableRaisingEvents = enableRaisingEvents;
// Disable to prevent multiple launchs
Framework.Check.LogWarning("LAUNCHING EXTERNAL DEVICE WITH PATH: " + path);
process.Start(); // HERE The program reports the following:
That means, “The program could not be started because ddip.dll is missing… try reinstalling bla bla”.
The thing is, if I execute the same #path from the command line, the program opens perfectly:
That opens the program. And the same happens if I click on the “shortcut” that it’s located in the “programs” menu. There aren’t any parameters in that shortcut, it’s a simple call to the executable file.
So the question is now: What is the difference between my code and the other methods?
There has got to be something different that causes my process not to start.
Any ideas?
UPDATE AND SOLUTION
I made it work by using one of the below provided answers. Turns out that none directly pointed me to the solution, but they all gave me good ideas here and there.
I added an app manifest to our application (should have had it since the age of vista, don’t know why it wasn’t there in the 1st place). The app manifest I added by using VStudio 2008 add file -> app manifest.
In it, I made sure we have this:
<requestedExecutionLevel level=“asInvoker” uiAccess=“false” />
We don’t need admin or anything like that, but apparently Vista/7 need to know it.
After that was added, the process is correctly launched.
note: UseShellExecute is true by default (as suggested by some), you have to explicitly turn it to false if that’s what you want.
You are not setting the process.StartInfo.WorkingDirectory property. There's plenty of poorly written software out there that assumes the working directory will be the directory in which the EXE is stored. At least add this line:
process.StartInfo.WorkingDirectory = System.IO.Path.GetDirectoryName(#path);
The exception is however rather strange. I'd definitely recommend you tell the customer to update their anti-malware tools.
If the exe has a manifest, you should set UseShellExecute to true on the process object before you call Start. It's not a bad idea in any case.
As Kate Gregory pointed out, if you want to "emulate" the user double clicking on the icon, you have to set UseShellExecute to true. Setting this flags make the code use a totally different path, using the underlying windows ShellExecute function.
Now, I will add to this, that if you're running on a UAC-equipped Windows (Vista, 7, 2008, ...) you maybe should also try to use the runas verb as explained here and here.
With .NET, that would be:
if (System.Environment.OSVersion.Version.Major >= 6) // UAC's around...
{
processStartInfo.Verb = "runas";
}
I've had similar problems in the past. I resolved it by executing the cmd app as follows:
public static bool FireUpProcess(Process process, string path, bool enableRaisingEvents, ProcessWindowStyle windowStyle)
{
//if path contains " ", surround it with quotes.
//add /c and the path as parameters to the cmd process.
//Any other parameters can be added after the path.
ProcessStartInfo psi = new ProcessStartInfo("cmd", "/c" + path ));
psi.WorkingDirectory = System.IO.Path.GetDirectoryName(#path);
psi.WindowStyle = windowStyle;
// Suscribe to the exit notification
process.EnableRaisingEvents = enableRaisingEvents;
// Disable to prevent multiple launchs
Framework.Check.LogWarning("LAUNCHING EXTERNAL DEVICE WITH PATH: " + path);
process.Start(); ...}
If it is possible I would try to use Process Monitor from Sysinternals. When you start it up you can deselect Registry and Network Activity on the toolbar (the 5 icons on the right side). Then you only see Process and Disk activity. Since it looks like a file not found problem you should use the Filter dialog (6. icon from the left) select Process Name from the Drop down list (Architecture is the default) and enter your failing executable name. This will greatly limit the captured output so you can see what is going on. Then start the exectuable and check in the Result Column for NAME NOT FOUND result. This are the locations where a file was searched but not found. If you know the offending dll name you can search for it with Ctrl+F as usual to dig it out. Then you can compare the different search paths from your working application and when it was started from your application.
Could it be that the environment variable PATH has a different value inside your process? It could be that adding . (the current directory) helps to fix the dll search path. Or is the application started from a different user account? It could also be the new feature that when an application is installing things into Programm Files but has no rights (only administrator can do this) Windows will redirect the writes into the user profile. This is a secure and transparent way to get more secure. But this could cause e.g. during first application startup some e.g. config file to be deployed into the Administrators Profile when he is running the application not with consent from the UAC dialog. Then other users might also start the application but fail because the additional config file is located in the Administrators profile and not in Program Files as expected for everyone.
I believe Hans Passant is on the right track. In addition to what he said, check to ensure that ddip.dll and the exe are in the same directory. This is not always the case as there are other ways to bind assemblies outside the bin. Namely, the GAC and AssemblyResolve event. Considering your situation I see no reason the GAC is involved. Check the exe's code that is launched for any hooks into the AssemblyResolve event. If it's hooked into you may need to update the implementation to allow another process to launch it.
Because you are getting an exception regarding a missing DLL, I have little confidence in the answers regarding path delimiter issues. Nonetheless, you have the application code, so verify that it references ddip.dll. This will give you a good deal of confidence that you are in fact referencing the correct .exe and therefore it's not just a path delimiter problem with the command prompt (E.G. misinterpreted spaces).