Specify DLLs for an exe launched with Process.Start? - c#

I'm trying to launch an executable with Process.Start(). When the exe has no DLL dependencies, it works fine. However, when I need to include 2 DLLs, it doesn't work. I've tried setting the WorkingDirectory, and have verified that the 2 required DLLs are present there. Any ideas?
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.CreateNoWindow = false;
startInfo.UseShellExecute = false;
startInfo.FileName = "memcached.exe";
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
startInfo.Arguments = arguments; //not shown
startInfo.WorkingDirectory = Environment.CurrentDirectory;
try
{
using (Process exeProcess = Process.Start(startInfo))
{
exeProcess.WaitForExit();
}
}
catch (Exception ex)
{
Trace.TraceError(ex.Message); // never gets here
}
This is code based on the Windows Azure Memcached Solution Accelerator. When memcached can't launch, a dialog box is displayed. Unfortunately you can't see this when the code is running remotely in the cloud.

I had similar problem trying to start another process that needed a DLL and couldn't find it. The solution was pretty simple in my case, a missing '\'.
procInfo.WorkingDirectory = #"C:\filedir"; //won't work
procInfo.WorkingDirectory = #"C:\filedir\" ; //would do the trick
procInfo.WorkingDirectory = Enviroment.CurrentDirectory; //== "C:\filedir", that won't work either
procInfo.WorkingDirectory = Enviroment.CurrentDirectory + '\\'; // would work.
Hope that helps you.

The problem might be that you are setting the WorkingDirectory to the current directory of the current process (which could be anywhere, not necessarily the directory containing your program). Try setting the working directory to the directory containing the exe you want to start.
Also, have you verified that the DLLs are with memcached.exe (or in the place required by the memcached.exe)?

Try to place your .EXE file and that referenced assemblies in same place, and to define your WorkingDirectory.WorkingDirectory to that folder. This probably will work fine.
One extreme alternative is to strong name that references assemblies (DLL) and to register them into GAC.
You should exhaust all other alternatives before think about this option.

Related

How do you register COM assemblies programmatically in C#?

I guess technically, it doesn't have to be a COM assembly specifically. But, I wanted to include that because that's how I searched for it myself.
Currently, we use a batch file that registers them with the following line:
c:\windows\microsoft.net\framework\v4.0.30319\regasm.exe c:\Path\To\AssemblyToRegister.dll /codebase /s /tlb
How would you register it in C# code so we can get rid of the batch files and automate this process a little better?
Note: The following code requires the application to be run as administrator!
Assembly asm = Assembly.LoadFrom(#"c:\Path\To\AssemblyToRegister.dll");
RegistrationServices regAsm = new RegistrationServices();
bool bResult = regAsm.RegisterAssembly(asm, AssemblyRegistrationFlags.SetCodeBase);
I found this answer originally from this page. However, we ran into an issue where it threw an exception on the third line with a certain dll and found that the "LoadFile" form line 1 should have been "LoadFrom". Here is the link to the Stack Overflow question where we found the fix.
Here is my first attempt which is not the best way of doing this. I am adding in-case it helps someone in the future.
This code assumes an absolute path to the dll is provided in the dllLocation variable.
You might also have to change the regasm location value to wherever it's installed on your machine.
string regasmLocation = #"c:\windows\microsoft.net\framework\v4.0.30319\regasm.exe";
try
{
Process p = new Process();
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.FileName = regasmLocation;
// If you want the tlb (type library) created use the following.
//p.StartInfo.Arguments = dllLocation + #" /codebase /s /tlb";
p.StartInfo.Arguments = dllLocation + #" /codebase /s";
p.Start();
// To avoid deadlocks, always read the output stream first and then wait. https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.process.standarderror?view=net-6.0
string standardError = p.StandardError.ReadToEnd();
string standardOutput = p.StandardOutput.ReadToEnd();
p.WaitForExit();
Console.WriteLine($"Successfully registered '{dllLocation}'.");
if (!string.IsNullOrWhiteSpace(standardOutput))
{
Console.WriteLine($"Standard Output: \n{standardOutput}");
}
if (!string.IsNullOrWhiteSpace(standardError))
{
Console.WriteLine($"Standard Error: \n{standardError}");
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
return false;
}
return true;
This is limited because it runs regasm through a console window. This means that you can't really handle exceptions. I would suggest doing it programmatically with my other answer.

Cmd Command (ewfmgr C: -commit) not executed when start it from C# Process

When I try to run cmd command 'efwmgr C: -commit' from C#, got empty log file without any information, and when check manually 'ewfmgr C:', got 'Boot Command NO_CMD', so commit not run.
Same code just changed Arguments = "/C chkdsk C:" it runs and works well, inserted whole output into my log.
Method which I used.
public static void StartProcess()
{
var procStartInfo = new ProcessStartInfo
{
CreateNoWindow = false,
WindowStyle = ProcessWindowStyle.Normal,
FileName = "cmd",
Arguments = "/C ewfmgr C: -commit",
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardInput = true,
RedirectStandardError = true
};
var process = new Process { StartInfo = procStartInfo, EnableRaisingEvents = true };
using (StreamWriter writer = new StreamWriter(#"D:\commitFile.txt"))
{
process.OutputDataReceived += (sender, e) =>
{
writer.WriteLine(e.Data);
};
process.Start();
process.BeginOutputReadLine();
process.WaitForExit();
}
}
This is the nearly example I found on https://social.msdn.microsoft.com/Forums/vstudio/en-US/4e014365-8e8f-4f93-998a-156f2e55ebab/how-to-get-and-write-ewf-current-to-text-file-using-c?forum=csharpgeneral
You probably getting an error in process error output stream. Append your log in ErrorDataReceived event handler. And for the 'ewfmgr' is not recognized as an internal or external command you should edit process environment variable or specify full path to your application.
This is how you code should look like:
var procStartInfo = new ProcessStartInfo
{
CreateNoWindow = false,
WindowStyle = ProcessWindowStyle.Normal,
FileName = "cmd",
//Append PATH environment variable bellow if you use this
Arguments = "/C ewfmgr C: -commit",
//Or use full path to application without changing environment variable
//Arguments = "/C c:\full\path\to\application\ewfmgr C: -commit",
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardInput = true,
RedirectStandardError = true
};
procStartInfo.EnvironmentVariables["PATH"] += #";c:\full\path\to\application\";
var process = new Process { StartInfo = procStartInfo, EnableRaisingEvents = true };
using (StreamWriter writer = new StreamWriter(#"D:\commitFile.txt"))
{
process.OutputDataReceived += (sender, e) =>
{
writer.WriteLine(e.Data);
};
process.ErrorDataReceived+= (sender, e) =>
{
writer.WriteLine(e.Data);
};
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
process.WaitForExit();
}
Just wanted to point out an update regarding this issue for anyone that might encounter this, or any other file "missing" from Windows/System32 directory:
First things to check is your system architecture, and your process architecture:
There are several posts about this feature (although I prefer to call it issue), I can safely say that this one explains it correclty, and that ewfmgr.exe works just fine if you set your architecture correclty.
In order not to rely on another post/link, I'll rewrite/copy the answer from David there:
There is one explanation that makes sense:
You are executing the program on a 64 bit machine.
Your C# program is built as x86.
The bcdedit.exe file exists in C:\Windows\System32.
Although C:\Windows\System32 is on your system path, in an x86 process you are subject to the File System Redirector. Which means that C:\Windows\System32 actually resolves to C:\Windows\SysWOW64.
There is no 32 bit version of bcdedit.exe in C:\Windows\SysWOW64.
The solution is to change your C# program to target AnyCPU or x64.
As a side note I'd like to also add that by default, VS projects for C# are set as "Any CPU" configuration, however there's checkbox ticked in project properties on the build tab, that says "Prefer 32-bit": This needs to be unchecked/disabled, or "Any cpu" build will result in 32 bit application as well.
Other than that, I've successfully implemented fully working EWF manager into our service application. Unfortunatelly I haven't had any luck using pInvoke and ewfapi dll, since it didn't seem to return correct results. I'm not sure yet whether there was an issue in implementation, or whether the EWF api itself is broken (to be honest, its really buggy and unreliable. For us specifically, the "Disable" command does nothing - after reboot, ewf is still enabled. The only way to disable it is to Disable and Commit, or use CommitAndDisableLive, which both unfortunatelly commit current state onto the drive, so I had to use fallback and reboot the machine before disabling protection, to be sure its in clean state), so I went the route to call it using command line, and parse response parameters to control it. Unfortunatelly it was time critical and I needed production ready solution, so I had to make a workaround, but I'll post a separate question about the pInvoke, and then put on GitHub altogether as a final solution.
I'll come back and edit this post to include the GitHub link once I have the time to upload it. Should someone need it right away, I'm happy to send it over as-is.
Hope it helps.

Launching an OSX process without a dock icon

I have two Unity3D applications - one launched by the other, with a -batchmode argument on the launched one so it has no graphics.
On Mac OSX the launched process still gets a dock icon that sits there bouncing forever; clicking it does nothing since it's non-graphical, and I'd really like to remove it.
I've tried modifying the Info.plist with a LSUIElement entry to get rid of the dock icon. That works perfectly if I launch the application myself, but it still gets a dock icon when I launch it as a process.
My process launching code is a little unusual which mightn't be helping. This works on Windows and Linux but not OSX and I'm not sure why (C#, mono):
ProcessStartInfo proc = new ProcessStartInfo();
proc.FileName = path + filename;
proc.WorkingDirectory = path;
proc.Arguments = commandlineFlags;
process = Process.Start(proc);
I've only got it to launch on OSX with this specific setup:
ProcessStartInfo startInfo = new ProcessStartInfo("open", "-a '" + path + filename + "' -n --args " + commandlineFlags);
startInfo.UseShellExecute = false;
process = Process.Start(startInfo);
You will need MonoMac for this if you are not already using it, either the older open-source version or the commercial version (Xamarin.Mac).
In the Unity app that you are launching as a 'sub-process' from the first app add a project reference to MonoMac and add a using clause for MonoMac:
using MonoMac;
Then in your static Main function:
MonoMac.AppKit.NSApplication.Init ();
MonoMac.AppKit.NSApplication.SharedApplication.ActivationPolicy = MonoMac.AppKit.NSApplicationActivationPolicy.Accessory;
That will hide the application/process from dock and task switcher... Of course you can conditional skip that code if you are running on Windows/Linux.
Answering my own question, but adding these to the ProcessStartInfo removed the dock icon:
startInfo.CreateNoWindow = true;
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
I'm not sure if both of those is actually needed, but there doesn't seem to be any harm.
A PList edit seems to be needed as well. Specifically I'm adding:
<key>LSBackgroundOnly</key>
<string>1</string>
As found here.

Execute uninstall with C#

I've been looking around the internet for this, but I couldn't find it.
Is there a way to trigger an uninstaller (from the Programs and Features screen) via C#? Or is this blocked by Windows for security purposes?
You can use msiexec.exe. You can simply uninstall an application with its product code. Using command you can set whether to show UI during the uninstallation or make it a silent uninstallation,
string UninstallCommandString = "/x {0} /qn";
/qn: Set user interface level: None
/qb: Set user interface level: Basic UI
/qr: Set user interface level: Reduced UI
/qf: Set user interface level: Full UI (default)
C# code
string UninstallCommandString = "/x {0} /qn";
Process process = new Process();
ProcessStartInfo startInfo = new ProcessStartInfo();
process.StartInfo = startInfo;
startInfo.UseShellExecute = false;
startInfo.RedirectStandardError = true;
startInfo.FileName = "msiexec.exe";
startInfo.Arguments = string.Format(UninstallCommandString, "Product Code");
process.Start();
Have a look at C# - Installing and uninstalling software and Programmatically Uninstall Programs With C#
You could invoke the executable file for the uninstaller using system.diagnostics.
Something like the following should do the trick:
System.Diagnostics.Process.Start("/path/to/uninstall.exe", "arguments for uninstaller if needed, else don't bother with this arg");
It's quick and dirty and /should/ work. Hope that helps.
edit- Just realised you want to do this from the add remove software screen. I'll leave this here anyway but my mistake.

Visual C # process arguments not working

ok this was working the other day and now it's not... i don't remember changing anything, but i can run this reg.exe command with the arguments below from CMD and it works fine and creates the output file. but running it in VC# it does not create the file test_output.txt???
System.Diagnostics.Process proc_cmd = new System.Diagnostics.Process();
proc_cmd.StartInfo.FileName = #"c:\windows\system32\reg.exe";
proc_cmd.StartInfo.Arguments = #"query ""HKLM\Software\test\test software"" /v BuildNumber >c:\test\test_output.txt";
proc_cmd.StartInfo.CreateNoWindow = true;
proc_cmd.StartInfo.RedirectStandardError = true;
proc_cmd.StartInfo.RedirectStandardOutput = true;
proc_cmd.StartInfo.RedirectStandardInput = true;
proc_cmd.StartInfo.UseShellExecute = false;
proc_cmd.Start();
proc_cmd.Close();
You should use the Registry class instead.
Your >output.txt is an instruction to the command interpreter (cmd.exe). That won't work calling reg.exe. Consider calling cmd.exe instead, or redirecting the stdout and writing it to the file yourself. See this SO answer link.
Of course, if there's no compelling reason to shell out to the Reg.exe, you should use the Registry class.

Categories

Resources