C++ CreateprocessA Failed Error - c++

I use this code but i get a failed error: 2 i know that means the file doesn't exist but no idea what am doing wrong.
DWORD size = 1024;
TCHAR buff[1024];
int err = AssocQueryString(ASSOCF_INIT_IGNOREUNKNOWN, ASSOCSTR_EXECUTABLE, _T(".html"), NULL, buff, &size);
STARTUPINFOA si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
LPCSTR BrowLoc = (LPCSTR)buff;
if (!CreateProcessA(BrowLoc, // No module name (use command line)
NULL,
NULL, // Process handle not inheritable
NULL, // Thread handle not inhberitable
FALSE, // Set handle inheritance to FALSE
0, // No creation flags
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si, // Pointer to STARTUPINFO structure
&pi) // Pointer to PROCESS_INFORMATION structure
)
{
printf("CreateProcess failed (%d).\n", GetLastError());
getchar();
return 0;
}

I guess you are using unicode. In that case, force using multi-byte character for AssocQueryString().
Use the code below for the first 3 lines.
DWORD size = 1024;
char buff[1024];
int err = AssocQueryStringA(ASSOCF_INIT_IGNOREUNKNOWN, ASSOCSTR_EXECUTABLE, ".html", NULL, buff, &size);

Related

How to execute commands from an attached console

I'm coding a WinAPI GUI program that needs calling ftp and possibly other console programs while getting their console output to act accordingly ie. waiting for ftp to complete execution before reading all its output wouldn't do.
My current approach is calling CreateProcess() to create a cmd.exe process potentially hiding the ugly console window, AttachConsole() to make it my own, GetStdHandle() to get input and output handles, SetConsoleCursorPosition() to the end of the console buffer, and WriteConsole() with commands such as ftp\n or dir\n. Yet this commands are written but not executed. However, I can manually use the same console ( using CreateProcess() with CREATE_NEW_CONSOLE flag ) to type ftp press enter and get it executed.
Previous approaches involved:
Calling ftp directly with CreateProcess() and redirected inputs/outputs.
Couldn't get ftp output until the CreateProcess() process had already ended.
Using system().
Was advised against its usage before getting any output.
My current stripped down code:
// Next two structures might be a bit misleading, they were used for the 1. previous
// approach
PROCESS_INFORMATION piProcInfo;
ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION));
STARTUPINFO siStartInfo;
ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );
siStartInfo.cb = sizeof(STARTUPINFO);
siStartInfo.hStdError = g_hChildStd_OUT_Wr;
siStartInfo.hStdOutput = g_hChildStd_OUT_Wr;
siStartInfo.hStdInput = g_hChildStd_IN_Rd;
siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
SECURITY_ATTRIBUTES security;
security.nLength = sizeof(SECURITY_ATTRIBUTES);
security.lpSecurityDescriptor = NULL;
security.bInheritHandle = FALSE;
CreateProcess( NULL, "cmd", &security, &security, FALSE, NORMAL_PRIORITY_CLASS |
CREATE_NEW_CONSOLE, NULL, NULL, &siStartInfo, &piProcInfo);
uint32_t pidConsole = piProcInfo.dwProcessId;
while ( ! AttachConsole(pidConsole) ){};
HANDLE myConsoleIn, myConsoleOut;
myConsoleIn = GetStdHandle(STD_INPUT_HANDLE);
myConsoleOut = GetStdHandle(STD_OUTPUT_HANDLE);
Sleep(100);
CONSOLE_SCREEN_BUFFER_INFO myConsoleCursorInformation = {};
GetConsoleScreenBufferInfo(myConsoleOut,&myConsoleCursorInformation);
SetConsoleCursorPosition(myConsoleOut,myConsoleCursorInformation.dwSize);
CHAR myConsoleBuffer[200]="dir\n";
DWORD myConsoleProcessed;
WriteConsole( myConsoleOut, myConsoleBuffer, 4, &myConsoleProcessed, NULL);
How can I get a command written in the console to execute? Is there an alternative to my attempt of ending commands with a trailing \n ie. using WriteConsole() with a dir\n or ftp\n argument.
I thought about sending a keypress to the process in question after typing the desired command. Yet the created console needs not only to manually press the enter key but also having dir, ftp or whatever command to be manually typed.
Please feel free to point out any missing information !
How can I get a command written in the console to execute? Is there an
alternative to my attempt of ending commands with a trailing \n ie.
using WriteConsole() with a dir\n or ftp\n argument.
Try the following code to see if it works:
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
const wchar_t *cmdPath = L"C:\\Windows\\System32\\cmd.exe";
wchar_t *cmdArgs = (wchar_t *)L"C:\\Windows\\System32\\cmd.exe /k dir";
BOOL result = CreateProcess(cmdPath, cmdArgs, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
DWORD errCode = GetLastError();
if (!result)
{
std::cout << "Create Process failed: " << GetLastError() << std::endl;
}
/K Run Command and then return to the CMD prompt.
This is useful for testing, to examine variables
Use /C if you want "Run Command and then terminate".
Update: Complete code for communicating with a child process(cmd.exe) using pipes.
HANDLE g_hChildStd_IN_Rd = NULL;
HANDLE g_hChildStd_IN_Wr = NULL;
HANDLE g_hChildStd_OUT_Rd = NULL;
HANDLE g_hChildStd_OUT_Wr = NULL;
#define BUFSIZE 1024
void ErrorExit(LPCTSTR lpszFunction)
{
LPVOID lpMsgBuf;
LPVOID lpDisplayBuf;
DWORD dw = GetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&lpMsgBuf,
0, NULL);
lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT,
(lstrlen((LPCTSTR)lpMsgBuf) + lstrlen((LPCTSTR)lpszFunction) + 40) * sizeof(TCHAR));
StringCchPrintf((LPTSTR)lpDisplayBuf,
LocalSize(lpDisplayBuf) / sizeof(TCHAR),
TEXT("%s failed with error %d: %s"),
lpszFunction, dw, lpMsgBuf);
MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK);
LocalFree(lpMsgBuf);
LocalFree(lpDisplayBuf);
ExitProcess(1);
}
void ReadFromPipe(void)
{
DWORD dwRead, dwWritten;
CHAR chBuf[BUFSIZE];
BOOL bSuccess = FALSE;
HANDLE hParentStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
for (;;)
{
DWORD bytesAvail = 0;
if (!PeekNamedPipe(g_hChildStd_OUT_Rd, NULL, 0, NULL, &bytesAvail, NULL)) {
std::cout << "Failed to call PeekNamedPipe" << std::endl;
}
if (bytesAvail) {
DWORD n;
BOOL success = ReadFile(g_hChildStd_OUT_Rd, chBuf, BUFSIZE, &n, NULL);
if (!success || n == 0) {
}
bSuccess = WriteFile(hParentStdOut, chBuf,n, &dwWritten, NULL);
}
else
{
break;
}
}
}
void WriteToPipe(void)
{
DWORD dwWritten;
BOOL bSuccess = FALSE;
CHAR buf[] = "dir\n";
bSuccess = WriteFile(g_hChildStd_IN_Wr, buf, sizeof(buf)-1, &dwWritten, NULL);
}
int main()
{
STARTUPINFO si;
PROCESS_INFORMATION pi;
SECURITY_ATTRIBUTES saAttr;
printf("\n->Start of parent execution.\n");
// Set the bInheritHandle flag so pipe handles are inherited.
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
// Create a pipe for the child process's STDOUT.
if (!CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0))
ErrorExit(TEXT("StdoutRd CreatePipe"));
// Ensure the read handle to the pipe for STDOUT is not inherited.
if (!SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0))
ErrorExit(TEXT("Stdout SetHandleInformation"));
// Create a pipe for the child process's STDIN.
if (!CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0))
ErrorExit(TEXT("Stdin CreatePipe"));
// Ensure the write handle to the pipe for STDIN is not inherited.
if (!SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0))
ErrorExit(TEXT("Stdin SetHandleInformation"));
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
si.cb = sizeof(STARTUPINFO);
si.hStdError = g_hChildStd_OUT_Wr;
si.hStdOutput = g_hChildStd_OUT_Wr;
si.hStdInput = g_hChildStd_IN_Rd;
si.dwFlags |= STARTF_USESTDHANDLES;
TCHAR cmdPath[] = TEXT("C:\\Windows\\System32\\cmd.exe");
BOOL result = CreateProcess(cmdPath, NULL, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);
DWORD errCode = GetLastError();
if (!result)
{
std::cout << "Create Process failed: " << GetLastError() << std::endl;
}
for (;;)
{
ReadFromPipe();
WriteToPipe();
}
}

Create Process default browser

I'm currently using ShellExecute "open" to open a URL in the user's browser, but running into a bit of trouble in Win7 and Vista because the program runs elevated as a service.
I want to get thread id so ShellExecute is not possible to get thread id so i started using "CreateProcess" But i don't see any help on opening default browser whichever is it from CreateProcess.
You can use other features of the Shell API to figure out which browser is the default.
For instance, you can ask the shell for the executable name associated with .html files using AssocQueryString, and launch that via CreateProcess.
Here you can open the URL with your default browser.
STARTUPINFOA si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
CStringA command_line;
command_line.Format("cmd.exe /c start \"link\" \"%s\"", "http://www.google.com");
if (!CreateProcessA(NULL, // No module name (use command line)
command_line.GetBuffer(),
NULL, // Process handle not inheritable
NULL, // Thread handle not inhberitable
FALSE, // Set handle inheritance to FALSE
NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW, // No creation flags
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si, // Pointer to STARTUPINFO structure
&pi) // Pointer to PROCESS_INFORMATION structure
)
{
TRACE("CreateProcess failed (%d).\n", GetLastError());
return;
}
Here is another answer as you wanted. As far as you want it open as a new window. You can expand it for IE, opera, and so on...
DWORD size = MAX_PATH;
char buff[MAX_PATH];
int err = AssocQueryStringA(ASSOCF_INIT_IGNOREUNKNOWN, ASSOCSTR_EXECUTABLE, ".html", NULL, buff, &size);
STARTUPINFOA si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
CStringA command_line;
CStringA target_url( "http://google.com/" );
if( strcmp( "chrome.exe", PathFindFileNameA(buff)) == 0 )
command_line.Format("%s --new-window %s", buff, target_url);
else if( strcmp( "firefox.exe", PathFindFileNameA(buff)) == 0 )
command_line.Format("%s -new-instance %s", buff, target_url);
else
command_line.Format("%s %s", buff, target_url);
if (!CreateProcessA(NULL, // No module name (use command line)
command_line.GetBuffer(),
NULL, // Process handle not inheritable
NULL, // Thread handle not inhberitable
FALSE, // Set handle inheritance to FALSE
0, // No creation flags
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si, // Pointer to STARTUPINFO structure
&pi) // Pointer to PROCESS_INFORMATION structure
)
{
//... error handling
return;
}

C++ process class error

Following the tutorial here, I decided to make a process class for C++ so that I did not have to constantly keep writing out the same code for starting a process. It does work starting the process, but when I pass a command line function, it does nothing. Example ("c:\\windows\\notepad.exe", "c:\\windows\\PFRO.txt"). What is the problem?
Note: format is just a basic formatting function, using vsprintf
class process
{
public:
static BOOL __stdcall start(LPCSTR _Proc_name, LPSTR _Command_line = NULL, LPSECURITY_ATTRIBUTES _Proc_attrib = NULL,
LPSECURITY_ATTRIBUTES _Thread_attrib = NULL, BOOL _Inherits_handles = FALSE, DWORD _Creation_flags = NULL,
LPVOID _Environment = NULL, LPCSTR _Cur_directory = NULL)
{
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
if (!CreateProcess(_Proc_name, _Command_line, _Proc_attrib, _Thread_attrib,
_Inherits_handles, _Creation_flags, _Environment, _Cur_directory, &si, &pi))
{
fputs(format("process::start(...) failed [%d]\n", GetLastError()), stderr);
return false;
}
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
return true;
}
};
int main()
{
process::start("c:\\windows\\notepad.exe", "c:\\windows\\PFRO.txt");
getchar();
}
When the command line parameter is parsed to provide arguments for a main function, the first token is taken to be the executable file. A called program might well try to open the second token as its file argument, and of course you there wasn't one.
The usual practice is to repeat the program name as the first token in the command line. For example
process::start("c:\\windows\\notepad.exe", "notepad c:\\windows\\PFRO.txt");

CreateProcess can not run a application

I just want to create a process to run a application on windows my code as below:
//init the structure
STARTUPINFOW StartupInfo;
ZeroMemory(&StartupInfo,sizeof(StartupInfo));
StartupInfo.cb = sizeof(StartupInfo);
StartupInfo.dwFlags = STARTF_USESHOWWINDOW;
StartupInfo.wShowWindow = true ;
PROCESS_INFORMATION ProcessInfo;
ZeroMemory(&ProcessInfo,sizeof(ProcessInfo));
DWORD dwExitCode = 0;
LPCWSTR cmdFormat = "xxxxxx"; // this is the applocation's path
LPWSTR cmd = new wchar_t[256*sizeof(wchar_t)];
wcscpy_s(cmd, wcslen(cmdFormat)+1,cmdFormat);
int ret = CreateProcessW(cmd,
NULL,
NULL,
NULL,
false,
NORMAL_PRIORITY_CLASS,
NULL,
NULL,
&StartupInfo,
&ProcessInfo);
if(ret)
{
CloseHandle(ProcessInfo.hThread);
WaitForSingleObject(ProcessInfo.hProcess, INFINITE);
GetExitCodeProcess(ProcessInfo.hProcess, &dwExitCode);
CloseHandle(ProcessInfo.hProcess);
}
if(dwExitCode==0)
{
DWORD errorcode = GetLastError();
std::cout<<"ERROR: "<<errorcode<<std::endl;
}
I use this function that I can create new process to run notepad.exe and some other applications
Q1: but when I close the application the dwExitCode = 0 and the errorcode 1803
Q2: some application can not run just exit immediately
Following function always works for me:
static int createProcess(string cmdLine, bool isWait, LPDWORD pExitCode)
{
STARTUPINFOA si;
PROCESS_INFORMATION pi;
::ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
::ZeroMemory(&pi, sizeof(pi));
// reset last error
::SetLastError(0);
// Start the child process.
BOOL bCreateProcess = ::CreateProcessA(NULL, // No module name (use command line)
(LPSTR) cmdLine.c_str(), // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Set handle inheritance to FALSE
CREATE_NO_WINDOW, // No creation flags
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si, // Pointer to STARTUPINFO structure
&pi); // Pointer to PROCESS_INFORMATION structure
if(!bCreateProcess)
{
// create process failed,
//Logger::trace(error, getClassName(), "createProcess", getFormattedStringA("create process failed with error:%d, Commad line:'%s',isWait:%d",GetLastError(), cmdLine.c_str(), isWait),"CreateProcess Failed");
return 0;
}
//Logger::trace(info, getClassName(), "createProcess", getFormattedStringA("created process,Commad line:'%s',isWait:%d,Result:%d", cmdLine.c_str(), isWait,bCreateProcess),"Launched Process");
// Wait until child process exits.
if(isWait)
{
::WaitForSingleObject(pi.hProcess, INFINITE);
if(pExitCode)
{
::GetExitCodeProcess(pi.hProcess, pExitCode);
}
}
::CloseHandle( pi.hProcess );
pi.hProcess = NULL;
::CloseHandle( pi.hThread );
pi.hThread = NULL;
return 1; // return non zero. function succeeded
}
Yes I find the root cause some application need some local resource ,so maybe need the parent's starting directory

Small issue with creating a process ( CreateProcess or ShellExecuteEx) with parameters

Related question: CreateProcess doesn't pass command line arguments.
Is there a difference between passing an argument vs. passing a parameter to an EXE when using CreateProcess (and/or ShellExecuteEx)?
I'm trying to call something like:
myExe.exe /myparam
with the code like :
TCHAR Buffer[MAX_PATH];
DWORD dwRet;
dwRet = GetCurrentDirectory(MAX_PATH, Buffer);
CString sCmd;
sCmd.Format ( "%s\\%s", Buffer, command);
CString sParam( "/myparam" );
sCmd += " " + sParam;
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory( &si, sizeof(si) );
si.cb = sizeof(si);
ZeroMemory( &pi, sizeof(pi) );
if (CreateProcess( NULL, sCmd.GetBuffer() , NULL, NULL, TRUE, 0, NULL, Buffer, &si, &pi))
{
::WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
else
{
LPVOID lpMsgBuf = NULL;
DWORD dw = GetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0, NULL );
CString msg;
msg.Format("Failed to start command line (error: %s) : %s\n",lpMsgBuf,sCmd);
AfxMessageBox(msg);
LocalFree(lpMsgBuf);
}
From what I understand from the other thread and MSDN is that it should be working properly and call the EXE with the parameter; doing the above code without adding the "/myparam" works like it should.
I've tried the EXE from the command line and from Explorer (by creating a shortcut and adding /myparam to the target name) and it's working alright.
Try this in case there are spaces in the path:
CString sCmd;
sCmd.Format ( "\"%s\\%s\"", Buffer, command);
Or else pass the parameters via the parameters argument.