I am using c++ and i have a program that works with winmain.
I do not want the system() call to open and close a window.
example:
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
system("dir > nul 2> nul");
return 0;
}
I want to use system calls but I don't want them to open a new window.
Thanks in advance.
edit:
I found this solution:
To execute cmd commands without opening a window i create a bat file (the program does this) then i use this code:
ShellExecute(NULL, "open", "Directory_Of_Bat_File", NULL, NULL, 0);
This opens the bat file and executes the commands.
I also noticed that you don't need system() to delete the bat file but you can delete it by writing:
del Directory_Of_Bat_File
Inside the bat file and this will delete it when you execute the bat file (it will delete itself).
This without opening a new window.
Since system, by its definition, creates a new process with a command interpreter, you can't do that.
From MSDN subject system
The system function passes command to the command interpreter, which
executes the string as an operating-system command.
If you want to do "dir", since that's a built-in command in the "cmd.exe" or whatever command interpreter you are using, it's pretty difficult to "fix" this issue - even using ShellExecute or CreateProcess will not help a whole lot, since you will get a window either way - it may be minimizes or something like that, but it will still be a window there.
Use CreateProcess or ShellExecute to launch the process, there you can pass options related to windows. what system() executes can normally be found in environment, getenv("ComSpec")
I found this solution:
To execute cmd commands without opening a window I create a bat file (the program does this) then I use this code:
ShellExecute(NULL, "open", "Directory_Of_Bat_File", NULL, NULL, 0);
This opens the bat file and executes the commands. I also noticed that you don't need system() to delete the bat file but you can delete it by writing:
del Directory_Of_Bat_File
Inside the bat file and this will delete it when you execute the bat file (it will delete itself). This without opening a new window.
Related
I'd like to create a windows application that, under normal conditions, does not have any connected terminal, but may have one in some conditions. I tried two different routes. Option A, creating a normal console application, and conditionally calling FreeConsole():
int main()
{
if (someCondition) {
HANDLE stdOutHandle = GetStdHandle(STD_OUTPUT_HANDLE);
WriteConsoleA(stdOutHandle, "hello world\n", 12, NULL, NULL);
} else {
FreeConsole();
// normal operation
}
return 0;
}
And option B, creating a WinMain based application, and conditionally calling AllocConsole().
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR pCmdLine, int nCmdShow)
{
if (someCondition) {
AllocConsole();
HANDLE stdOutHandle = GetStdHandle(STD_OUTPUT_HANDLE);
WriteConsoleA(stdOutHandle, "hello world\n", 12, NULL, NULL);
}
else {
// normal operation
}
return 0;
}
The problem with option A, is that it doesn't act exactly like a windows application, in that you can very briefly see a console window open up before normal operation continues. This isn't a huge problem, but I'd prefer the program to act, as I said, exactly like a normal windows application.
The problem with option B is that, if the program is invoked from an existing terminal, it opens up a separate terminal window and outputs to that, instead of outputting to the terminal from which it was invoked. This is a much bigger problem.
What is the appropriate solution to conditionally behave as either a console program or a windows program, without either of the problems described above?
Edit
Based on a suggestion in the comments, I tried to use the AttachConsole function.
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR pCmdLine, int nCmdShow)
{
if (someCondition) {
AttachConsole(ATTACH_PARENT_PROCESS);
HANDLE stdOutHandle = GetStdHandle(STD_OUTPUT_HANDLE);
Sleep(3000);
WriteConsoleA(stdOutHandle, "hello world\n", 12, NULL, NULL);
FreeConsole();
}
else {
// normal operation
}
return 0;
}
This seems to be on the right track, but there is still something missing, because my shell prompt is immediately printed out without waiting for the program to finish.
Impossible.
AttachConsole does not work 100% because cmd.exe actually checks if the process it is about to start is a GUI app or not, and alters its behavior.
The only way to make it work is to have two programs; myapp.com and myapp.exe. %PathExt% lists .com before .exe so you can make a console .exe and rename it .com at it will be executed if the user runs "myapp" (but will not work if the user types "myapp.exe"). If your program is not very big you can just ship two versions. If the program is large you can move most of the code to a .dll or make the .com a small helper that calls the .exe with a command line parameter and some pipes for stdin/stdout. Visual Studio does this (devenv.com).
If you want to check if there is already a console, check for a NULL return from GetConsoleWindow(). If it returns null, then do the AllocConsole procedure and other setup (SetWindowLong, SetConsoleMode etc)
int main() programs don't make a console by default, they happen to attach into a console if run through cmd/ps.
One way is the following:
BOOL WINAPI
AttachOrCreateConsole()
{
if(AttachConsole(ATTACH_PARENT_PROCESS))
{
return TRUE;
}
return AllocConsole();
}
This will use the parent console if available and fall back to creating one if needed.
Note that if yo do this and want to use the standard C/C++ I/O mechanisms (stdin/std::cin, stdout/std::cout) you will need to do the work needed to associate those streams to the console handle. There is plenty of material available on how to do that.
I am trying to make an image viewer, and everything works when I hard code the path of the file, but when I try to get the path so I can open files with it, it doesn't work, nothing happens.
When debugging, I found out that CreateFileA returns INVALID_HANDLE_VALUE.
This leads me to believe that the error should be in these 2 lines of code.
LPSTR FileA = GetCommandLine();
HANDLE FileHandle = CreateFileA(FileA, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
When CreateFileA() returns INVALID_HANDLE_VALUE on failure, use GetLastError() to find out WHY it failed. But in this case, GetCommandLine() is likely not returning the path you are expecting.
Assuming that is actually calling the Win32 GetCommandLineA() function, and not some other function in your program, then that function returns the command line that was used to launch the calling process, ie "C:\<path>\myapp.exe <parameters>". If there are no <parameters> present, then you would be trying to open your own program's EXE, not an image file. And if there are <parameters> present, then you would be passing an invalid file path to CreateFileA().
You need to parse the command line to extract just the file path you actually want, THEN you can try to open the file at that path. Have a look at Parsing C Command-Line Arguments and Parsing C++ Command-Line Arguments . Consider using CommandLineToArgvW() to make that easier.
I'm trying to create a function which opens a PDF in firefox separate from the main process. I believe I am having trouble with the parameters for createProcess... any help is greatly appreciated
EDIT: the batch file is being created, I have tested it several times, and to explain a bit:
The batch file is because I really don't know what I am doing, I am a student in computer science and this is a side project to help me at my job. I work at a law office and file the mail electronically as it comes in. I wanted to make a simple program that would loop through the scans directory, display the scan and prompt the user for information about the document. Therefore I need to be able to build a file path dynamically. Originally I was using "system" to open firefox and display the document. After a bit of trying I got it to work with a batch file. I then learned that system is a blocking command and that I would need to start a separate thread. This is where I ran into createprocess. I simply continued to use the batch file from my old system idea... And the more I think about it I can't remember which professor suggested the batch file or why...
void openPDF(char scansQueue[][MAX_NAME], int index)
{
// build bat file
fstream outfile;
outfile.open("C:\\firefox.bat");
if(outfile.good())cout<<"outfile good!!!!"<<endl;
outfile<<"\"C:\\Program Files (x86)\\Mozilla Firefox\\firefox.exe\" \"C:\\Scans\\" <<scansQueue[index]<<"\"";
STARTUPINFOW si;
PROCESS_INFORMATION pi;
ZeroMemory( &si, sizeof(si) );
si.cb = sizeof(si);
ZeroMemory( &pi, sizeof(pi) );
if(!CreateProcess(NULL, L"C:\\firefox", NULL, NULL, false, 0, NULL, NULL, &si, &pi))cout<<"PROCESS FAILED TO EXECUTE!!!";
CloseHandle( pi.hProcess );
CloseHandle( pi.hThread );
}
There are several problems with this code. A few has already been pointed out in the comments (closing potentially invalid handles on failure, possibility that the batch file can't be created, and the rather questionable command line). Here's a few more issues.
First, you can't run a batch file this way.
The documentation for CreateProcess clearly states:
To run a batch file, you must start the command interpreter; set lpApplicationName to cmd.exe and set lpCommandLine to the following arguments: /c plus the name of the batch file.
Second, you are passing a string literal for lpCommandLine, something that's also explicitly outlawed by the documentation:
lpCommandLine [in, out, optional]
...
The Unicode version of this function, CreateProcessW, can modify the contents of this string. Therefore, this parameter cannot be a pointer to read-only memory (such as a const variable or a literal string). If this parameter is a constant string, the function may cause an access violation.
Finally, why are you creating a temporary batch file to run a single command? You could easily have written the CreateProcess call to start Firefox directly.
I'm having some trouble with a program. My goal is to have it open several .exe files with optional args passed. For example if I wanted to open up a pdf I could type the string below into a cmd window.
// If used in a cmd window it will open up my PDF reader and load MyPDF.pdf file
"c:\Test space\SumatraPDF.exe" "c:\Test space\Sub\MyPDF.pdf"
Here are two tries I used. The first opens the PDF but of course doesn't load the file. The second simply doesn't work.
// Opens the PDF in my program
system("\"C:\\Test space\\SumatraPDF.exe\"");
// Error I get inside of a cmd window is the comment below
// 'C:\Test' is not recognized as an internal or external command, operable program or batch file.
//system("\"C:\\Test space\\SumatraPDF.exe\" \"C:\\Test space\\Sub\\MyPDF.pdf\"");
I'm unsure of the reason why the second one does not work. It could be I'm misunderstanding something about system, or I'm not using delimiters right.
I feel like there is a library out there designed for this rather than creating a long string that uses so many delimiters.
Thanks for any help.
Welcome to Stack Overflow!
The system method works by passing it's argument to cmd /c. So you will need an extra set of quotes around it. See related question posted by sled.
As an alternative to system, take a look at the ShellExecute or ShellExecuteEx Win32 API function. It has more features although it is not as portable.
// ShellExecute needs COM to be initialized
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
SHELLEXECUTEINFO sei = {0};
sei.cbSize = sizeof(sei);
sei.lpFile = prog; // program like c:\Windows\System32\notepad.exe
sei.lpParameters = args; // program arguments like c:\temp\foo.txt
sei.nShow = SW_NORMAL; // app should be visible and not maximized or minimized
ShellExecuteEx(&sei); // launch program
CoUninitialize();
More information here.
I have the following code
void reportResults()
{
wstring env(_wgetenv(L"ProgramFiles"));
env += L"\Internet Explorer\iexplore.exe";
wstringstream url;
url << "\"\"" << env.c_str() << "\" http://yahoo.com\"";
wchar_t arg[BUFSIZE];
url.get(arg, BUFSIZE);
wcout << arg << endl;
_wsystem(arg);
}
Where arg is:
""C:\Program Files\Internet Explorer\iexplore.exe" http://yahoo.com"
The program functions as expected, launching IE and navigating to Yahoo, but the calling function (reportResults) never exits. How do I get the program to exit leaving the browser alive?
Thanks.
You want to use _wspawn() instead of _wsystem(). This will spawn a new process for the browser process. _wsystem() blocks on the command that you create; this is why you're not getting back to your code. _wspawn() creates a new, separate process, which should return to your code immediately.
The _wsystem command will wait for the command in arg to return and returns the return value of the command. If you close the Internet Explorer window it will return command back to your program.
Why not just use ShellExecute to launch the default browser with a given URL?
Synopsis:
LONG r = ShellExecute(NULL, "open", "http://www.microsoft.com", NULL, NULL, SW_SHOWNORMAL);
EDIT:
I suppose since it must be IE, this might work (note, untested code):
LONG r = ShellExecute(NULL, NULL, "iexplore.exe", "http://www.microsoft.com", NULL, SW_SHOWNORMAL);
If you want to use the current implementation, you will have to fork() the process and let a child handle the browser spawning. Thus, the main process will continue and exit the function.
Instead of executing
"C:\Program Files\Internet Explorer\iexplore.exe" "http://yahoo.com"
execute
start "C:\Program Files\Internet Explorer\iexplore.exe" "http://yahoo.com"