How can I clear the CommandLineArgs of an application from code? - c#

We have a 3rd party login dialog which will skip the login prompt if the login data is passed in via command line arguments. This is used when an application is launched from within the main 3rd party software.
The custom app I am writing should provide users with a button to change their login info, however since the app is launched with the login info provided in the command line args, the login dialog never appears when the button is clicked.
Is it possible to clear or reset Environment.GetCommandLineArgs() from the code?
Edit
I ended up simply restarting the application prior to startup if login info existed in the command line. This makes the 3rd party login dialog actually show up instead of automatically using the login info provided in the command line arguments.
I'm accepting Jim's answer because I feel it is the most complete answer to my question, although Oded's answer is also a viable alternative.

What you're asking can't be done in .NET because the Environment class caches the command line and there's no property accessor for setting it. (More correctly, the startup code caches the command line and Environment.CommandLine calls into the runtime to get that cached value.)
In a native Windows application, the GetCommandLine() API function returns a pointer to the command line that the operating system presented to the program. A program can call CommandLineToArgvW to parse the command line into the standard argv and argc parameters familiar to C and C++ programmers.
The Environment class uses something similar. When you call Environment.GetCommandLineArgs, it accesses the Environment.CommandLine property and then calls the windows function CommandLineToArgvW to parse the command line. But Environment.CommandLine doesn't get its value from GetCommandLine(). Instead, the program gets the Windows command line at startup (by calling GetCommandLine()), and then saves it.
This is unfortunate, because you can modify the value that GetCommandLine returns, as demonstrated by this little snippet:
[DllImport("kernel32")]
static extern IntPtr GetCommandLine();
static void DoIt()
{
IntPtr pcmdline = GetCommandLine();
Console.WriteLine("Environment.CommandLine = {0}", Environment.CommandLine);
string realCmdLine = Marshal.PtrToStringAnsi(pcmdline);
Console.WriteLine("realCmdLine = {0}", realCmdLine);
Console.WriteLine("** Modify command line");
// Modify the command line
byte[] bytes = Encoding.ASCII.GetBytes("ham and swiss on rye\0");
Marshal.Copy(bytes, 0, pcmdline, bytes.Length);
Console.WriteLine("Environment.CommandLine = {0}", Environment.CommandLine);
pcmdline = GetCommandLine();
realCmdLine = Marshal.PtrToStringAnsi(pcmdline);
Console.WriteLine("realCmdLine = {0}", realCmdLine);
}
If you run that, you'll find that Environment.CommandLine returns the same string both times, whereas the second time you call GetCommandLine, you'll get back the string ham and swiss on rye.
Even if the above did work, there's no guarantee that it would solve your problem. The 3rd party control might parse the command line, cache the login information, and never parse the command line again.

You can use Process.Start to start a new instance of the application, providing the new credentials as arguments, and exit the current instance.

Add additional command line argument(s) to indicate this secondary condition. For example, an argument could be ShowLogin. If true, then the command line arguments pre-fill the login dialog and the users could update there information as needed. If false, then the arguments are used to auto log in without showing the dialog.

Related

C# Console-Application with no console but winforms

We have an application that is a WinForms-Application. It accepts startup parameters and can perform certain jobs with no UI by using the command line.
Now, we also want some console-output, like for example when using application.exe /help.
The Issue we are now facing is: How to bring both worlds together in a "nice" way:
When setting the Output Type to "Windows Application",all the UI stuff works fine, but Console.WriteLine() doesn't show results when used from cmd.
When setting the Output Type to "Console", generally both things work, BUT: The Application (when used in UI-Mode so to say) raises a console window, that stays open until the application terminates.
Is there a way to bypass the visibility of the console-window?
In the real world, I can find applications using one of three approaches - but I don't like either of them:
Leave the console window open when running in UI-Mode, who cares?
Use user32.dll to hide the window (it still flashes, tho)
Use Windows-Application as Output-Type and show Console-Output as "message boxes", when used from the command line.
I've figured out a solution, thx to #Luaan s comment:
I'm now using the Output Type Windows Application (so, no console window) - but simply registering the Console with the Applications "Parent" Console.
This now behaves as expected:
No console is shown during normal startup.
When used from within an existing console window (ps / cmd) the output is printed, because that consoles are then technically the parent-console of the application.
public class GUIConsoleWriter
{
[System.Runtime.InteropServices.DllImport("kernel32.dll")]
private static extern bool AttachConsole(int dwProcessId);
private const int ATTACH_PARENT_PROCESS = -1;
public static void RegisterGUIConsoleWriter()
{
AttachConsole(ATTACH_PARENT_PROCESS);
}
}
And then, firstline in Main():
static void Main(String[] args)
{
GUIConsoleWriter.RegisterGUIConsoleWriter();
}
Now, using regular Console.WriteLine() works. Ofc. this is limited to writing output, but for the scope of my projects that is sufficent, as input has to be provided with arguments, no interactive console-options.
When another application needs to fetch output programmatically, you ofc. can't do myApp.exe /doSomething but need to do cmd /C myApp.exe /doSomething because the application itself doesn't own a console.
AND you have to manually take care to "exit" your application, after providing output, otherwhise the process won't return.

uglify crashing after running child_process.execFile

EDIT 2
I "solved" the problem, but I don't want to post it as an answer b/c it doesn't explain what actually happened. In the code for the .NET resourceReader.exe I use
Console.OutputEncoding = System.Text.Encoding.UTF8;
to output the internationalized resources to stdout in unicode. If I reset the encoding at the end of my program with
Console.OutputEncoding = System.Text.Encoding.Default;
then I don't get any errors in Node. If I don't reset it, I get the error described in the original question. It seems that .NET is somehow messing up some of the output encoding settings on the cmd.exe and causing the subsequent node run to fail!
EDIT
I narrowed down the error to being caused by resourceReader.exe. It's a.NET program which reads some resource streams out of the .NET assembly and prints them to the stdout using Console.WriteLine. I added Console.OutputEncoding = System.Text.Encoding.UTF8 to resourceReader.exe because some of the resources are in non ASCII letters and that's whats causing the crash in grunt!
If I take that line out, the task doesn't crash, but the resources show up in non printable ASCII characters! Also, the crash only happens if I actually print non-ASCII to sdtout. If I don't print them, it doesn't error.
ORIGINAL
I added a step to my Gruntfile which uses child_process.execFile to run an read some data from an external program and uses it in the build. Now whenever I run my build, it runs fine the first time, but crashes the second time!
Here's the output from the crash (this is during the uglify task):
File build/Scripts/NDB.contacts.min.js created: 16.85 kBevents.js:85
throw er; // Unhandled 'error' event
^
Error: This socket is closed.
at WriteStream.Socket._writeGeneric (net.js:656:19)
at WriteStream.Socket._write (net.js:709:8)
at doWrite (_stream_writable.js:301:12)
at writeOrBuffer (_stream_writable.js:288:5)
at WriteStream.Writable.write (_stream_writable.js:217:11)
at WriteStream.Socket.write (net.js:634:40)
at Log._write (C:\...\node_modules\grunt\node_modules\grunt-legacy-log\index.js:161:26)
at Log.wrapper [as _write] (C:\...\node_modules\grunt\node_modules\grunt-legacy-log\node_modules\lodash\index.js:3095:19)
at Log._writeln (C:\...\node_modules\grunt\node_modules\grunt-legacy-log\index.js:166:8)
at Log.wrapper [as _writeln] (C:\...\node_modules\grunt\node_modules\grunt-legacy-log\node_modules\lodash\index.js:3095:19)
at Log.writeln (C:\...\node_modules\grunt\node_modules\grunt-legacy-log\index.js:177:8)
at Log.wrapper (C:\...\node_modules\grunt\node_modules\grunt-legacy-log\node_modules\lodash\index.js:3095:19)
at writeln (C:\...\node_modules\grunt\lib\grunt\fail.js:30:13)
at Object.fail.fatal (C:\...\node_modules\grunt\lib\grunt\fail.js:46:3)
at process.uncaughtHandler (C:\...\node_modules\grunt\lib\grunt.js:121:10)
at process.emit (events.js:129:20)
at process._fatalException (node.js:236:26)
at Task.runTaskFn (C:\...\node_modules\grunt\lib\util\task.js:250:7)
at Task.<anonymous> (C:\...\node_modules\grunt\lib\util\task.js:293:12)
at C:\...\node_modules\grunt\lib\util\task.js:220:11
at process._tickCallback (node.js:355:11)
Here's the code for the task which uses child_process.
function readAllCultures() {
var readDeferred = q.defer();
childProc.execFile("../tools/resourceReader.exe", function (err, stdout, stderr) {
if (err) throw new Error(err);
var cultures = JSON.parse(stdout);
readDeferred.resolve(cultures);
});
return readDeferred.promise;
}
Here's some things I discovered debugging that might be helpful
If I redirect the output of grunt (using either > filename or | process) it runs fine
When I redirect output, I never see the message from uglify that it created the main output only that it created the source map.
If I close and reopen my command prompt (cmd.exe) it works fine
I added a listener to the exit and close events of the child process using rdr.on("close", function() { console.log("close"); }); and the same for exit. Both events fire as expected in the first run.
Using Process Explorer, I can see node.exe open under the command prompt and close again when my command finishes running. There are no processes visibly "left open" under the command prompt.
The fact that the stack trace is ultimately showing a socket.write error is interesting. Nothing in the code you've provided suggests you are trying to write to a socket.
If we follow the stack down, we can see that it's actually grunt that is trying to write to a socket, as it's attempting to log an uncaught exception.
So, first your grunt task throws a currently unknown error, and then grunt itself errors because it can't tell you about it.
I would first try and log the error that occurs in your child process. Currently if there is an error present, you simply throw it without finding out what it is. It's likely that this is what grunt is trying and failing to log.
Replace
if (err) throw new Error(err);
With
if (err) console.error(err);
This will hopefully avoid the socket.write problem and give you specific information about what error is happening on the child process. What do you see?
Second, I would try using child_process.exec instead of child_process.execFile. This will spawn a shell and run resourceReader.exe inside of it (instead of running it directly). This might help avoid any problems you are encountering with the command still running/failing in the background, which could be the cause of the socket.write error.

C# SendKeys Wait for Program to Load before sending

So long story short, I am trying to automate some things when my computer boots up. I thought I'd write an C# console application to do this and then add it to a schedule task in windows to be executed on bootup. My problem is with one program, it requires a password and has no options to open via the command line. Thus it must be entered manually. My thought was to retrieve my password from a KeePass database and use SendKeys to enter the password and login to the program. The problem I'm having is the time it takes to load; I have no way of detecting when the GUI interface has loaded and is ready for my SendKeys. Is there any way to detect this? I'm assuming all I have to work with is the "Process" class since thats what I used to run the program. Also note that when I run the executable using Process.Start(), the program creates another process for logging in, but it has no associated window that I can see using Windows API calls.
Okay that was long, I can re-cap...
Problem:
From C# detecting when a third party program has loaded (i.e. the splash screen is gone and GUI is ready for user interaction - meaning I can't just rely on if the Process is running or not).
Also, no command line options for the third party program, or I would just run it with the password as an argument.
Goal:
To use SendKeys in order to automate entering a password, but my program must wait for the third party application to finish loading.
Notes:
Using C# .NET 3.5 Console Application
NOT detecting load for my own form but a third party otherwise this would be easy (i.e. form_loaded event...)
Thank you for looking at my question, if you want any more details or anything let me know.
UPDATE:
Problem solved!
The two answers I received combined to give me the solution I wanted. So if anyone comes across this later, here is what I did to make it work.
So this program automates a login for some client software that you must login to. My problem was that the software offered not option or documentation for command line prameters which many other programs offer so you can login with a keyfile or something. This program also disabled copy and paste so the password HAS to be typed in manually, which is a big pain if you use passwords like I do, long complicated ones with no patterns. So I wrote this program for my benefit as well others at work; I just schedule it to run at logon to my windows machine and it opens the client software and performs login automatically.
//
// IMPORTANT Windows API imports....
//
[DllImport("user32.dll", SetLastError = true)]
private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint procId);
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool SetForegroundWindow(IntPtr hWnd);
// When I get to this point in my code, I already had the password and window title...
string password = "password";
string title = "window title";
// This gets a handle to the window I want where "title" is the text in the title
// bar of the window as a string.
// This is a Windows API function that must be imported by DLLImport
// I had to get the handle this way because all I knew about the third party
// window was the title, not the process name or anything...
IntPtr hWnd = FindWindowEx(IntPtr.Zero, IntPtr.Zero, null, title);
// Now that I have a handle to the login window I used another windows API
// call to get the process ID.
// This windows API call gives me the Process ID as an out parameter and returns
// the thread ID of the window. I don't use the thread it, but maybe you can...
uint loginWindowProcId;
uint loginWindowThreadId = GetWindowThreadProcessId(hWnd, out loginWindowProcId);
// now I can just use .NET to find the Process for me...
Process loginWindowProcess = null;
if (0 != loginWindowProcId)
{
// get the process object
loginWindowProcess = Process.GetProcessById((int)loginWindowProcId);
// This right here is why I wanted the Process structure. It takes a
// little while for the client software to load and be ready. So here
// you wait for the window to be idle so you know it has loaded and can
// receive user input, or in this case keys from "SendKeys".
loginWindowProcess.WaitForInputIdle();
// I use yet another windows API call to make sure that the login window
// is currently in the foreground. This ensures that the keys are sent
// to the right window. Use the handle that we started with.
SetForegroundWindow(hWnd);
// Now send the password to the window. In my case, the user name is
// always there from my windows credentials. So normally I would type in the
// password and press ENTER to login. But here I'll use SendKeys to mimic my
// behavior.
SendKeys.SendWait(password); // send password string
SendKeys.SendWait("{ENTER}"); // send ENTER key
// Now the client should be logging in for you! : )
// IMPORTANT NOTE
// If you are using a console application like I am, you must add a reference to
// System.Windows.Forms to your project and put "using System.Windows.Forms;" in
// your code. This is required to use the "SendKeys" function.
//
// Also this code is just for my testing (quick and dirty), you will want to write
// more checks and catch errors and such. You should probably give the
// WaitForInputIdle a timeout etc...
}
You can check with Process.WaitForInputIdle after you start a process, and wait until is fully started, here is the simple example :
http://msdn.microsoft.com/en-us/library/xb73d10t%28v=vs.71%29.aspx
Try looking at that: http://www.acoolsip.com/a-cool-blog/science-and-technology/151-c-sending-commands-to-independent-windows.html
you can check if the window is shown (using the link) and then sending messages (also on the link)

Sending a result with EndDialog Win32API in .NET

I am running some automation in a C# program (.Net 4.0). There is an issue with a modal dialog where I want to click the message away and continue testing. I have tried a few options (SendKey and using Win32 to send a click event with code modified from here: http://msdn.microsoft.com/en-us/magazine/gg309183.aspx. Neither of these have proved to be reliable enough to be considered effective.
My next approach will be to try calling the EndDialog() function from my C# program and simply sending the enumeration/return code to the message box.
EndDialog(HWND hDlg, INT_PTR nResult) is the call where hDlg is the handle to the message box being closed and nResult is the result of the dialog.
Where I am running into an issue is how to send the desired result. An example would be that the return code IDCANCEL has a value of 2. How exactly do I send this value? What variables or constants would I need to declare? I'm just looking for how to get the proper pointer declared to send the desired result to the function.
Further information on these result values can be found here http://msdn.microsoft.com/en-us/library/windows/desktop/ms645505(v=VS.85).aspx
just invoke PostMessage. Here is a sample in c/c++:
::PostMessage(hWnd, WM_COMMAND, MAKEWPARAM(IDCANCEL,BN_CLICKED), 0);

What is the different between API functions AllocConsole and AttachConsole(-1)?

Could you please explain me, what is the different between API functions AllocConsole and AttachConsole(-1) ? I mean if AttachConsole gets ATTACH_PARENT_PROCESS(DWORD)-1.
Well, the fundamental difference is:
AllocConsole() will create a new console (and attach to it)
AttachConsole( ATTACH_PARENT_PROCESS /* -1 */) will not create a new console, it will attach to the existing console of the parent process.
In the first case you get a whole new console window, in the second case, you use an existing console window.
Of course, if you're already attached to a console (ie., you're a console mode program launched from cmd.exe) there's not much difference - you'll get an error with either API.
Also note that just because you detach from a console doesn't mean the detached console will be useful - for example, if you're a console process launched from a cmd window, that window essentially blocks until your process ends.
Some code to play with:
int main( int argc, char* argv[])
{
int ch;
BOOL bResult;
printf( "default console\n");
ch = getchar();
bResult = FreeConsole();
bResult = AllocConsole();
printf( "AllocConsole()\n");
ch = getchar();
bResult = FreeConsole();
bResult = AttachConsole( ATTACH_PARENT_PROCESS);
printf( "AttachConsole( ATTACH_PARENT_PROCESS)\n");
ch = getchar();
return 0;
}
I don't think there's a function called CreateConsole, but there's AllocConsole.
Assuming that's what you meant, I think the difference is that AttachConsole(ATTACH_PARENT_PROCESS) can return ERROR_INVALID_HANDLE if the parent process doesn't have a console.
Try running this code from both a command prompt and Start -> Run:
#include <windows.h>
#pragma comment ( lib, "user32.lib" )
int main()
{
BOOL b;
char msg[1024];
b = FreeConsole();
sprintf(msg, "%d", b);
MessageBox(NULL, msg, "FreeConsole", 0);
b = AttachConsole(ATTACH_PARENT_PROCESS);
sprintf(msg, "%d", b);
MessageBox(NULL, msg, "AttachConsole", 0);
return 0;
}
When run from a command prompt, two message boxes containing a 1 are displayed, meaning both calls succeeded. When run from Start -> Run, the first box contains 1 and the second contains 0, meaning that only the first call succeeded. The second one fails because explorer.exe (which is the parent of a process launched from Start -> Run) doesn't have a console.
On Windows 7, when you execute cmd.exe, CreateProcess will have the CREATE_NEW_CONSOLE flag, which will allocate a new console rather than being attached to the parent console (which is default behaviour when the PE header contains Subsystem = 3 i.e. IMAGE_SUBSYSTEM_WINDOWS_CUI indicating that it is a console application). This means that AllocConsole will be called before the .exe image entry point in the current process address space.
AllocConsole creates a new conhost.exe instance, which paints the GUI window, handles the mouse and keyboard events and maintains and writes to the the input buffer and maintains and reads from the screen buffer, and when there is a keyboard event, it updates the input buffer. AllocConsole also sets the stdin handle in the ParameterBlock in the cmd.exe process PEB to the console input buffer and the stdout and stderr to the the console pseudohandles, and sets the ConsoleHandle in the ParameterBlockto the PID of theconhost.exe` instance that it is attached to.
cmd.exe is a console application written in C, like diskpart.exe or setx.exe, which displays the command prompt to the screen buffer (stdout of cmd.exe) and reads a command from the stdin of cmd.exe and interprets the key press events to display to stdout as well as determine what command to call and what to display to stdout as a result (which might be a file in the handle in the PEB and not sent to conhost). A command itself has an stdin of either nothing, the read side of an anonymous pipe, a file or the con file.
cmd.exe calls functions like WriteFile which will call WriteConsole (which is an alias of WriteConsoleA) if it is writing to a standard handle (otherwise it calls NtWriteFile). This will initiate an ALPC call to conhost.exe PID in ParameterBlock->ConsoleHandle, passing the console pseudohandle and the buffer to write the result to (or the buffer to read from).
When you execute cmd /c from inside cmd.exe, it creates a child cmd.exe, but does not supply CREATE_NEW_CONSOLE, which results in the child cmd.exe attaching to the same visible console i.e. conhost.exe instance and AttachConsole is called before the entry point. This is not the case with a child created with admin /c (admin version of cmd.exe) if done in a non-elevated cmd.exe, which needs to have a new conhost.exe instance because it has different privileges. Similarly, when starting a program, start supplies CREATE_NEW_CONSOLE, which opens a new conhost.exe for its child process, but call and specifying the program filename + extension as a raw command do not open another console window, but attach to the parent. cmd /c diskpart creates a cmd.exe child that attaches to the console that the parent is attached to and then that child creates its own child diskpart.exe, which attaches to the console that the parent is attached to.
When you execute a GUI application, it is not allocated or attached to a console. If you use AllocConsole within the application, it will create a new conhost.exe console window (which can be hidden by the application, i.e. blender has window > toggle system console, and it does this by getting a handle to the conhost.exe console window and then using using ShowWindow with SW_HIDE on it), but will not create a child process, because it is the console window of the current process, so now there will be 2 windows. You can also instead attach to a console (using AttachConsole) belonging to a process of pid. AttachConsole(-1) attaches to the parent pid. This will use the conhost that that process is attached to.
You cannot AttachConsole if you are already attached to a console without using FreeConsole and you can't AttachConsole to connect to the parent's console if you create the process with DETACHED_PROCESS. CREATE_NO_WINDOW has the same effect as DETACHED_PROCESS in that it does not attach to or allocate a console for the console application but does allow it to attach to the parent console.
It has been a while since I used the winapi, but I looked up the MSDN documentation and I was not able to find the CreateConsole API function. So my guess is that CreateConsole is legacy stuff and has been replaced by AttachConsole. So there is probably no difference, but CreateConsole has probably been deprecated.

Categories

Resources