Calling Python file from c++ using CreateProcess - python

I would like to run python script file on MFC Button click using CreateProcess(), so that I can capture the output of a process in realtime using a pipes.
CString sCmd = "Cmd.Exe";
CString sParms = " /c python.exe C:\\WinApp\\Debug\\printx.py";
BOOL fRet = ExecAndProcessOutput(sCmd, sParms);
BOOL ExecAndProcessOutput(LPCSTR szCmd, LPCSTR szParms)
{
SECURITY_ATTRIBUTES rSA = { 0 };
rSA.nLength = sizeof(SECURITY_ATTRIBUTES);
rSA.bInheritHandle = TRUE;
HANDLE hReadPipe, hWritePipe;
CreatePipe(&hReadPipe, &hWritePipe, &rSA, 25000);
PROCESS_INFORMATION rPI = { 0 };
STARTUPINFO rSI = { 0 };
rSI.cb = sizeof(rSI);
rSI.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
rSI.wShowWindow = SW_SHOWNORMAL;// SW_HIDE; // or SW_SHOWNORMAL or SW_MINIMIZE
rSI.hStdOutput = hWritePipe;
rSI.hStdError = hWritePipe;
CString sCmd;
sCmd.Format("\"%s\" %s", (LPCSTR)szCmd, (LPCSTR)szParms);
BOOL fRet = CreateProcess(NULL, (LPSTR)(LPCSTR)sCmd, NULL,NULL, TRUE, 0, 0, 0, &rSI, &rPI);
if (!fRet)
{
return(FALSE);
}
}
In summary, passing parameter to the 2nd argument for the Createprocess is:
"Cmd.Exe" /c python.exe C:\WinApp\Debug\printx.py
some reason when I run this code createprocess I don't get any error as a return argument. But python file named - printx.py not running.
Appreciates any help on this.
Thanks,

Related

.py file executed by C# process not waiting to finish

I want to run .py file from my C# project, and get the result. The python script is making an API request, and returns an auth_key token, which I want to use in my C# code. The only problem is that, for some reason the C# code doesn't wait for the process to finish, and thus that not every account has auth_key. Here is my C# code.
private static void GenerateTokens()
{
var url = ConfigurationManager.AppSetting[GeSettingsNode() + ":ip"];
for (int i = 0; i < accounts.Count; i++)
{
ProcessStartInfo start = new ProcessStartInfo();
start.FileName = ConfigurationManager.AppSetting["PythonPath"];
start.Arguments = string.Format($"python_operation_processor.py {accounts[i].client_key_id} {accounts[i].key_sercret_part} {url}");
start.UseShellExecute = false;
start.RedirectStandardOutput = true;
Process process = Process.Start(start);
using (StreamReader reader = process.StandardOutput)
{
accounts[i].auth_key = reader.ReadToEnd().Trim();
}
}
}
And here is my Python script ( python_operation_processor.py )that's making the API requests.
if __name__ == '__main__':
client_key_id = sys.argv[1]
client_secret = sys.argv[2]
API_URL = sys.argv[3]
nonce = str(uuid.uuid4())
d = datetime.datetime.now() - datetime.timedelta(hours=3)
timestamp = d.strftime('%Y-%m-%dT%H:%M:%S.%f')[:-3] + 'Z'
signature = b64encode(hmac.new(b64decode(client_secret), msg=bytes(client_key_id + nonce + timestamp, 'utf-8'),
digestmod=hashlib.sha256).digest()).decode('utf-8')
r = requests.post(API_URL + '/v1/authenticate',
json={'client_key_id': client_key_id, 'timestamp': timestamp, 'nonce': nonce,
'signature': signature})
if r.status_code != 200:
raise Exception('Failed to authenticate: ' + r.text)
auth_token = r.json()['token']
print(auth_token)
Do you have any idea, how I can wait for the execution of every process, and get the token for every account ?
I recently created something similar and ended up with this because, whilst waiting for the process is easy, it is tricky to get the output stream filled correctly.
The method presented also allow you to display the output into a textblock or similar in your application.
If you use it like this, the token will be written to the StringBuilder, and used as return value.
private async Task<string> RunCommand(string fileName, string args)
{
var timeoutSignal = new CancellationTokenSource(TimeSpan.FromMinutes(3));
ProcessStartInfo start = new ProcessStartInfo();
start.FileName = fileName;
start.Arguments = string.Format("{0}", args);
start.RedirectStandardOutput = true;
start.RedirectStandardError = true;
start.UseShellExecute = false;
start.CreateNoWindow = true;
var sb = new StringBuilder();
using (Process process = new Process())
{
process.StartInfo = start;
process.OutputDataReceived += (sender, eventArgs) =>
{
sb.AppendLine(eventArgs.Data); //allow other stuff as well
};
process.ErrorDataReceived += (sender, eventArgs) => {};
if (process.Start())
{
process.EnableRaisingEvents = true;
process.BeginOutputReadLine();
process.BeginErrorReadLine();
await process.WaitForExitAsync(timeoutSignal.Token);
//allow std out to be flushed
await Task.Delay(100);
}
}
return sb.ToString();
}
To render this to a textblock in a UI application, you'll need to:
implement an event which signals a new line has been read, which means forwarding the process.OutputDataReceived event.
if your thinking about a live feed, make sure you flush the stdio buffer in python setting flush to true: print(""hello world"", flush=True)
If you're using an older .net version; you can implement the WaitForExitAsync as described here: https://stackoverflow.com/a/17936541/2416958 as an extention method:
public static class ProcessHelpers
{
public static Task<bool> WaitForExitAsync(this Process process, TimeSpan timeout)
{
ManualResetEvent processWaitObject = new ManualResetEvent(false);
processWaitObject.SafeWaitHandle = new SafeWaitHandle(process.Handle, false);
TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
RegisteredWaitHandle registeredProcessWaitHandle = null;
registeredProcessWaitHandle = ThreadPool.RegisterWaitForSingleObject(
processWaitObject,
delegate(object state, bool timedOut)
{
if (!timedOut)
{
registeredProcessWaitHandle.Unregister(null);
}
processWaitObject.Dispose();
tcs.SetResult(!timedOut);
},
null /* state */,
timeout,
true /* executeOnlyOnce */);
return tcs.Task;
}
}

Empty output when calling a python script in C# project

I would like to call a python script in my C# project , I'm using this function to do the job but unfortunately I didn't get any result and the result variable shows always an empty output. I would like to know what's the reason of this
public string RunFromCmd(string rCodeFilePath, string args)
{
string file = rCodeFilePath;
string result = string.Empty;
try
{
var info = new ProcessStartInfo(pythonPath);
info.Arguments = #"C:\Users\MyPc\ExternalScripts\HelloWorld.py" + " " + args;
info.RedirectStandardInput = false;
info.RedirectStandardOutput = true;
info.UseShellExecute = false;
info.CreateNoWindow = true;
using (var proc = new Process())
{
proc.StartInfo = info;
proc.Start();
proc.WaitForExit();
if (proc.ExitCode == 0)
{
result = proc.StandardOutput.ReadToEnd();
}
}
return result;
}
catch (Exception ex)
{
throw new Exception("R Script failed: " + result, ex);
}
}
Click Event ( Calling funtion )
private void Button1_Click(object sender, RoutedEventArgs e)
{
pythonPath = Environment.GetEnvironmentVariable("PYTHON_PATH");
RunFromCmd(pythonPath, "");
}
Python Script :
import sys
def main():
text = "Hello World"
return text
result = main()
I've fixed the issue by setting Copy if newer instead of Do Not Copy to HelloWorld.py Script

How to retrieve the full path for the foreground window in Windows 10 using the Python ctypes module?

I've decided to bite more than I could chew here. I've spent the past day attempting this through trial and error however I have yet to figure out the correct way to interact with the Windows API.
I would like to retrieve the full path for the focused window on my Windows 10 PC using Python. So far I have hot glued this together:
from ctypes import *
import sys
import time
try:
while True:
# Retrieve the handle for the foreground window
hwnd = windll.user32.GetForegroundWindow()
# Create a buffer for the GetWindowThreadProcessId function
buffer = create_string_buffer(260)
# Retrieve the full path and file name of the foreground window
windll.user32.GetWindowModuleFileName(hwnd, buffer, sizeof(buffer))
# Print the full path and file name of the foreground window
print(buffer.value)
# Sleep for 100 milliseconds
time.sleep(0.1)
except KeyboardInterrupt:
sys.exit(0)
Unfortunatly this doesn't have my desired output. When I have Command Prompt open I would expect the path to be C:\Windows\system32\cmd.exe however I get C:\Users\John\AppData\Local\Programs\Python\Python39\python.exe instead. When I open any other window I get an empty output.
GetWindowModuleFileName calls GetModuleFileName from which the MSDN doc says :
The module must have been loaded by the current process.
So you can't get the full path you want directly by calling GetWindowModuleFileName.
You can refer to this thread : How to get the Executable name of a window.
And here is an example implemented in C++, you can refer to it:
#include <Windows.h>
#include <psapi.h>
#include <iostream>
#include <tlhelp32.h>
BOOL SetPrivilege(HANDLE hToken, LPCTSTR Privilege,BOOL bEnablePrivilege)
{
TOKEN_PRIVILEGES tp = { 0 };
// Initialize everything to zero
LUID luid;
DWORD cb = sizeof(TOKEN_PRIVILEGES);
if (!LookupPrivilegeValue(NULL, Privilege, &luid))
{
return FALSE;
}
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
if (bEnablePrivilege) {
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
}
else {
tp.Privileges[0].Attributes = 0;
}
AdjustTokenPrivileges(hToken, FALSE, &tp, cb, NULL, NULL);
if (GetLastError() != ERROR_SUCCESS)
{
std::cout << "err = " << GetLastError() << std::endl;
return FALSE;
}
return TRUE;
}
int main()
{
HANDLE curHandle = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, GetCurrentProcessId());
OpenProcessToken(curHandle, TOKEN_ADJUST_PRIVILEGES, &curHandle);
SetPrivilege(curHandle, SE_DEBUG_NAME, TRUE);
while (1)
{
TCHAR buf[MAX_PATH] = L"";
HWND hwnd = GetForegroundWindow();
DWORD pid = 0;
GetWindowThreadProcessId(hwnd, &pid);
HANDLE handle = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid);
if (handle)
{
GetModuleFileNameEx(handle, 0, buf, MAX_PATH);
std::wcout << buf << std::endl;
}
else
{
std::cout << "error = " << GetLastError() << std::endl;
}
if (handle) CloseHandle(handle);
Sleep(100);
}
return 0;
}

ShellExecuteEx and getexitcodeprocess

Using ShellExecuteEx(..) to lunch a python script and python script returning a value from python main using sys.exit(0) on success or some other error value. How to read a python script exit code?
After launching application waited to complete script by using MsgWaitForMultipleObjects (...) and then calling GetExitCodeProcess(...) some reason I always read value 1 from getExitCodeprocess(..)
Python Code:
def main():
time.sleep(10)
logger.info("************The End**********")
return (15)
if __name__ == "__main__":
sys.exit(main())
C++ Code:
SHELLEXECUTEINFO rSEI = { 0 };
rSEI.cbSize = sizeof(rSEI);
//rSEI.lpVerb = "runas";
rSEI.lpVerb = "open";
rSEI.lpFile = "python.Exe";
rSEI.lpParameters = LPCSTR(path.c_str());
rSEI.nShow = SW_NORMAL;
rSEI.fMask = SEE_MASK_NOCLOSEPROCESS;
if (ShellExecuteEx(&rSEI)) // you should check for an error here
;
else
errorMessageID = GetLastError(); //MessageBox("Error", "Status", 0);
WORD nStatus;
MSG msg; // else process some messages while waiting...
while (TRUE)
{
nStatus = MsgWaitForMultipleObjects(1, &rSEI.hProcess, FALSE, INFINITE, QS_ALLINPUT); // drop through on user activity
if (nStatus == WAIT_OBJECT_0)
{ // done: the program has ended
break;
}
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
DispatchMessage(&msg);
//MessageBox("Wait...", "Status", 0);
}
} // launched process has exited
DWORD dwCode=0;
if (!GetExitCodeProcess(rSEI.hProcess, &dwCode)) //errorvalue
{
DWORD lastError = GetLastError();
}
In this code as Python script exiting with 15, I am expecting to read 15 from dwCode from GetExitCodeProcess(rSEI.hProcess, &dwCode)?
Appreciates all of your help on this...
As the comments metioned, your python script fails.
Python Code Sample:
import sys
import time
import logging
import logging.handlers
logger = logging.getLogger("logger")
logger.setLevel(logging.DEBUG)
handler = logging.StreamHandler()
handler.setLevel(logging.INFO)
logger.addHandler(handler)
def main():
time.sleep(10)
logger.info("************The End**********")
return (15)
if __name__ == "__main__":
sys.exit(main())
Name your Python script file with .py as the suffix instead of .Exe, such as "python.py"
Give path value to SHELLEXECUTEINFO.lpDirectory, and set SHELLEXECUTEINFO.lpParameters to NULL here.
Or Give the path and file combination to SHELLEXECUTEINFO.lpVerb, like "Path\\python.py"
C++ Code Sample:
#include <windows.h>
#include <iostream>
#include <string>
void main()
{
int errorMessageID = 0;
std::string path = "Path";
SHELLEXECUTEINFO rSEI = { 0 };
rSEI.cbSize = sizeof(rSEI);
//rSEI.lpVerb = "runas";
rSEI.lpVerb = "open";
rSEI.lpFile = "python.py";
rSEI.lpParameters = NULL;
rSEI.lpDirectory = path.c_str();
rSEI.nShow = SW_NORMAL;
rSEI.fMask = SEE_MASK_NOCLOSEPROCESS;
if (!ShellExecuteEx(&rSEI)) // you should check for an error here
errorMessageID = GetLastError(); //MessageBox("Error", "Status", 0);
WORD nStatus;
MSG msg; // else process some messages while waiting...
while (TRUE)
{
nStatus = MsgWaitForMultipleObjects(1, &rSEI.hProcess, FALSE, INFINITE, QS_ALLINPUT); // drop through on user activity
if (nStatus == WAIT_OBJECT_0)
{ // done: the program has ended
break;
}
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
DispatchMessage(&msg);
//MessageBox("Wait...", "Status", 0);
}
} // launched process has exited
DWORD dwCode = 0;
if (!GetExitCodeProcess(rSEI.hProcess, &dwCode)) //errorvalue
{
DWORD lastError = GetLastError();
}
}

fgetc causes a segfault after running the second time

I have an application that tries to read a specific key file and this can happen multiple times during the program's lifespan. Here is the function for reading the file:
__status
_read_key_file(const char * file, char ** buffer)
{
FILE * pFile = NULL;
long fsize = 0;
pFile = fopen(file, "rb");
if (pFile == NULL) {
_set_error("Could not open file: ", 1);
return _ERROR;
}
// Get the filesize
while(fgetc(pFile) != EOF) {
++fsize;
}
*buffer = (char *) malloc(sizeof(char) * (fsize + 1));
// Read the file and write it to the buffer
rewind(pFile);
size_t result = fread(*buffer, sizeof(char), fsize, pFile);
if (result != fsize) {
_set_error("Reading error", 0);
fclose(pFile);
return _ERROR;
}
fclose(pFile);
pFile = NULL;
return _OK;
}
Now the problem is that for a single open/read/close it works just fine, except when I run the function the second time - it will always segfault at this line: while(fgetc(pFile) != EOF)
Tracing with gdb, it shows that the segfault occurs deeper within the fgetc function itself.
I am a bit lost, but obviously am doing something wrong, since if I try to tell the size with fseek/ftell, I always get a 0.
Some context:
Language: C
System: Linux (Ubuntu 16 64bit)
Please ignore functions
and names with underscores as they are defined somewhere else in the
code.
Program is designed to run as a dynamic library to load in Python via ctypes
EDIT
Right, it seems there's more than meets the eye. Jean-François Fabre spawned an idea that I tested and it worked, however I am still confused to why.
Some additional context:
Suppose there's a function in C that looks something like this:
_status
init(_conn_params cp) {
_status status = _NONE;
if (!cp.pkey_data) {
_set_error("No data, open the file", 0);
if(!cp.pkey_file) {
_set_error("No public key set", 0);
return _ERROR;
}
status = _read_key_file(cp.pkey_file, &cp.pkey_data);
if (status != _OK) return status;
}
/* SOME ADDITIONAL WORK AND CHECKING DONE HERE */
return status;
}
Now in Python (using 3.5 for testing), we generate those conn_params and then call the init function:
from ctypes import *
libCtest = CDLL('./lib/lib.so')
class _conn_params(Structure):
_fields_ = [
# Some params
('pkey_file', c_char_p),
('pkey_data', c_char_p),
# Some additonal params
]
#################### PART START #################
cp = _conn_params()
cp.pkey_file = "public_key.pem".encode('utf-8')
status = libCtest.init(cp)
status = libCtest.init(cp) # Will cause a segfault
##################### PART END ###################
# However if we do
#################### PART START #################
cp = _conn_params()
cp.pkey_file = "public_key.pem".encode('utf-8')
status = libCtest.init(cp)
# And then
cp = _conn_params()
cp.pkey_file = "public_key.pem".encode('utf-8')
status = libCtest.init(cp)
##################### PART END ###################
The second PART START / PART END will not cause the segfault in this context.
Would anyone know a reason to why?

Categories