I use 4.7.6 version and in the link below, i posted the problem i am experiencing which is termination of process creation under some circumstances
[Interactive Process Communication][1]
Update Begin
Here is how i create the process and the pipe
#pragma once
#include "Runtime/Core/Public/GenericPlatform/GenericPlatformProcess.h"
#include "Runtime/Core/Public/Misc/Timespan.h"
/**
* Declares a delegate that is executed when a interactive process completed.
*
* The first parameter is the process return code.
*/
DECLARE_DELEGATE_OneParam(FOnInteractiveProcessCompleted, int32)
/**
* Declares a delegate that is executed when a interactive process produces output.
*
* The first parameter is the produced output.
*/
DECLARE_DELEGATE_OneParam(FOnInteractiveProcessOutput, FString)
/**
* Implements an external process that can be interacted.
*/
class CHESSGAME_API FInteractiveProcess
: public FRunnable
{
public:
/**
* Creates a new interactive process.
*
* @param InURL The URL of the executable to launch.
* @param InParams The command line parameters.
* @param InHidden Whether the window of the process should be hidden.
*/
FInteractiveProcess(const FString& InURL, const FString& InParams, bool InHidden);
/** Destructor. */
~FInteractiveProcess();
/**
* Cancels the process.
*
* @param InKillTree Whether to kill the entire process tree when canceling this process.
*/
void Cancel(bool InKillTree = false)
{
bCanceling = true;
bKillTree = InKillTree;
}
/**
* Gets the duration of time that the task has been running.
*
* @return Time duration.
*/
FTimespan GetDuration() const;
/**
* Checks whether the process is still running.
*
* @return true if the process is running, false otherwise.
*/
bool IsRunning() const
{
return (Thread != nullptr);
}
/**
* Launches the process
*
* @return True if succeed
*/
bool Launch();
/**
* Returns a delegate that is executed when the process has been canceled.
*
* @return The delegate.
*/
FSimpleDelegate& OnCanceled()
{
return CanceledDelegate;
}
/**
* Returns a delegate that is executed if the process is terminated without user wanting
*
* @return The delegate
*/
FSimpleDelegate& OnTerminated()
{
return TerminatedDelegate;
}
/**
* Returns a delegate that is executed when a interactive process completed.
*
* @return The delegate.
*/
FOnInteractiveProcessCompleted& OnCompleted()
{
return CompletedDelegate;
}
/**
* Returns a delegate that is executed when a interactive process produces output.
*
* @return The delegate.
*/
FOnInteractiveProcessOutput& OnOutput()
{
return OutputDelegate;
}
/**
* Sends the message when process is ready
*
* @param Message to be send
*/
void WriteWhenReady(const FString& Message);
/**
* Returns the return code from the exited process
*
* @return Process return code
*/
int GetReturnCode() const
{
return ReturnCode;
}
// FRunnable interface
virtual bool Init() override
{
return true;
}
virtual uint32 Run() override;
virtual void Stop() override
{
Cancel();
}
virtual void Exit() override { }
protected:
/**
* Processes the given output string.
*
* @param Output The output string to process.
*/
void ProcessOutput(const FString& Output);
// Write message to process through pipe
bool WriteToPipe();
private:
// Whether the process is being canceled. */
bool bCanceling : 1;
// Whether the window of the process should be hidden. */
bool bHidden : 1;
// Whether to kill the entire process tree when cancelling this process. */
bool bKillTree : 1;
// Holds the URL of the executable to launch. */
FString URL;
// Holds the command line parameters. */
FString Params;
// Holds the handle to the process. */
FProcHandle ProcessHandle;
// Holds the pipe. */
void* ReadPipe;
// Holds the write pipe. */
void* WritePipe;
// Holds the monitoring thread object. */
FRunnableThread* Thread;
// Holds the return code. */
int ReturnCode;
// Holds the time at which the process started. */
FDateTime StartTime;
// Holds the time at which the process ended. */
FDateTime EndTime;
// Message to be written to pipe when ready
FString MessageToProcess;
// Holds a delegate that is executed when the process has been canceled. */
FSimpleDelegate CanceledDelegate;
// Holds a delegate that is executed when the process has been canceled. */
FSimpleDelegate TerminatedDelegate;
// Holds a delegate that is executed when a interactive process completed. */
FOnInteractiveProcessCompleted CompletedDelegate;
// Holds a delegate that is executed when a interactive process produces output. */
FOnInteractiveProcessOutput OutputDelegate;
};
And the source
#include "ChessGame.h"
#include "FInteractiveProcess.h"
#include "Runtime/Core/Public/Misc/Paths.h"
FInteractiveProcess::FInteractiveProcess(const FString& InURL, const FString& InParams, bool InHidden)
: bCanceling(false)
, bHidden(InHidden)
, bKillTree(false)
, URL(InURL)
, Params(InParams)
, ReadPipe(nullptr)
, WritePipe(nullptr)
, Thread(nullptr)
, ReturnCode(0)
, StartTime(0)
, EndTime(0)
{ }
FInteractiveProcess::~FInteractiveProcess()
{
if (IsRunning())
{
Cancel(true);
Thread->WaitForCompletion();
delete Thread;
Thread = nullptr;
}
}
FTimespan FInteractiveProcess::GetDuration() const
{
if (IsRunning())
{
return (FDateTime::UtcNow() - StartTime);
}
return (EndTime - StartTime);
}
bool FInteractiveProcess::Launch()
{
if (IsRunning())
{
return false;
}
if (!FPlatformProcess::CreatePipe(ReadPipe, WritePipe))
{
return false;
}
ProcessHandle = FPlatformProcess::CreateProc(*URL, *Params, false, bHidden, bHidden, nullptr, 0, nullptr, WritePipe);
if (!ProcessHandle.IsValid())
{
UE_LOG(LogTemp, Warning, TEXT("Failed to launch"));
return false;
}
// Creating name for the process
static uint32 tempInteractiveProcessIndex = 0;
ThreadName = FString::Printf(TEXT("FInteractiveProcess %d"), tempInteractiveProcessIndex);
tempInteractiveProcessIndex++;
Thread = FRunnableThread::Create(this, *ThreadName);
return true;
}
void FInteractiveProcess::ProcessOutput(const FString& Output)
{
TArray<FString> LogLines;
Output.ParseIntoArray(&LogLines, TEXT("\n"), false);
for (int32 LogIndex = 0; LogIndex < LogLines.Num(); ++LogIndex)
{
OutputDelegate.ExecuteIfBound(LogLines[LogIndex]);
}
}
bool FInteractiveProcess::WriteToPipe()
{
// If there is not a message
if (MessageToProcess.Len() == 0)
{
return false;
}
if (!IsRunning())
{
return false;
}
if (!ProcessHandle.IsValid())
{
UE_LOG(LogTemp, Warning, TEXT("Process handle is not valid"));
return false;
}
// Convert tempInput to UTF8CHAR
uint32 BytesAvailable = MessageToProcess.Len();
UTF8CHAR* Buffer = new UTF8CHAR[BytesAvailable + 1];
if (!FString::ToBlob(MessageToProcess, Buffer, BytesAvailable))
{
UE_LOG(LogTemp, Warning, TEXT("Failed to convert UTF8CHAR"));
return false;
}
// Empty original FString
MessageToProcess.Empty(0);
// Write pipe UTF8CHAR
uint32 BytesWritten = 0;
// @todo This doesn't works on any OS other than Windows
if (!WriteFile(WritePipe, Buffer, BytesAvailable, (::DWORD*)&BytesWritten, nullptr))
{
UE_LOG(LogTemp, Warning, TEXT("Failed to write"));
return false;
}
if (BytesAvailable > BytesWritten)
{
UE_LOG(LogTemp, Warning, TEXT("Failed to write all of the message"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("Succesfully write through pipe"));
}
return true;
}
void FInteractiveProcess::WriteWhenReady(const FString& Message)
{
MessageToProcess = Message;
}
// FRunnable interface
uint32 FInteractiveProcess::Run()
{
// control and interact with the process
StartTime = FDateTime::UtcNow();
{
bool IsProcRunning = false;
do
{
// 1 millisecond sleep is a problem on Windows platform, dont change this
FPlatformProcess::Sleep(0.0);
// pipe
ProcessOutput(FPlatformProcess::ReadPipe(ReadPipe));
// Write pipe
WriteToPipe();
// If wanted to stop program
if (bCanceling)
{
// close pipes
FPlatformProcess::ClosePipe(ReadPipe, WritePipe);
ReadPipe = WritePipe = nullptr;
FPlatformProcess::TerminateProc(ProcessHandle, bKillTree);
CanceledDelegate.ExecuteIfBound();
// get completion status
if (!FPlatformProcess::GetProcReturnCode(ProcessHandle, &ReturnCode))
{
ReturnCode = -1;
}
CompletedDelegate.ExecuteIfBound(ReturnCode);
}
IsProcRunning = FPlatformProcess::IsProcRunning(ProcessHandle);
} while (IsProcRunning);
}
EndTime = FDateTime::UtcNow();
UE_LOG(LogTemp, Warning, TEXT("%s terminated"), *ThreadName);
// Means the process terminated without user wanting
if (!bCanceling)
{
TerminatedDelegate.ExecuteIfBound();
}
return 0;
}
My problem is whenever i attach a write pipe to process by using function CreateProc(last paramater is for write pipe), the process terminates right after creation. Or whenever i create an FRunnableThread for controlling the process via my class the same happens. The process i create gets terminated right after creation for some reason i couldn’t understand. If i just create the process and dont attach a write pipe to it and if i dont create an FRunnableThread for controlling the process via my class. The process doesn’t gets terminated.
I wondered if this is a bug related to the program i am creating. So i tried the same process creation by using Qt. And it worked quite fine. Not only creation was successful also sending and receiving messages was possible.
By the way i receive the outputs in the time process lives. But right after that the process gets terminated for some reason i dont know.
Here is a glimpse of what happens. The process gets terminated right after i create it(0.00003~ seconds)
Dont mind the part “by an outsider”
You can download stockfish from here, which is free
[StockFish][3]
Update End
After 8 days of working on this problem, i decided to try it with Qt to see if this a bug related to the program that i am using.
Creating and messaging with the same program using Qt works like a charm and i finally come to the understanding that this is a UE related bug. The process terminates right after creation :
- when i attach a write pipe to it
- Or when i create an FRunnableThread to
control the process via class which
inherits FRunnable - If i do both the
same
If FPlatformProcess doesn’t require processes to give outputs or some signals constantly and if FPlatformProcess doesn’t only works with UE based programs… Than this is a bug!