Pipe creation terminates Process

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” :smiley:

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 :

  1. when i attach a write pipe to it
  2. Or when i create an FRunnableThread to
    control the process via class which
    inherits FRunnable
  3. 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!

Hey Corpse0327-

Could you elaborate on what exactly is happening on your end? I’m having a hard time understanding exactly what the issue is. It sounds as though you’re trying to communicate between UE4 and another program? Any additional information you can provide will help me understand and reproduce the issue.

Cheers

I updated the post

If you need any more info, just tell me :slight_smile:

Hey Corpse0327-

There are a couple of things I noticed when looking over the code you posted. In line 25 of the source file, using the “delete” keyword on a pointer to an object not allocated with the “new” keyword can have unpredictable results.

I’m not certain exactly what you’re trying to do with pipe creation however if it is something along the line of drainage pipes you may want to look into using splines to set it up.

Lastly I’m not sure what you’re using StockFish for however using another engine in conjunction with UE4 is not supported.

Ohhh, the delete was a foolish reflex :slight_smile:

StockFish is not a game engine. It is a chess engine. Which is a 600 kb program that is designed to be the AI of chess games. You simply give it some commands and it will give you answers.

For example “RKBQKBKR/PPPPPPPP/8/8/8/8/pppppppp/rkbqkbkr” tells engine that chess pieces are at their start positions and some more, but i think i shouldn’t go into more details as it is unnecessery.

Many other game engines support using chess engines as they are just simple AI, not actual engines. If UE is actually blocking, this should change for real. But i don’t think that is the case. I think the word ‘engine’ mislead you :slight_smile:

Also this is the first time i am using IPC, and i don’t know what are drainage pipes and splines. Can you give me some links about them :slight_smile:

Hey Corpse0327-

If you are trying to use StockFish alongside the project you’re working on you may want to look at setting it up as a plugin for the engine. You can check the wiki for information on creating and setting up plugins (A new, community-hosted Unreal Engine Wiki - Announcements - Epic Developer Community Forums). Additionally you can add it as a third party library to your project (A new, community-hosted Unreal Engine Wiki - Announcements - Epic Developer Community Forums).

For drainage pipes I was referring to the circular metal piping seen in industrial areas. Splines are a good way of setting up these kinds of pipes. Here is a video made by a community member of setting up pipes using splines inside blueprints:

Sorry, i think i failed to explain the problem well.

This is a bug, because it only fails when in editor. It works fine when in game.

For the help i thank you. I will look at the other methods and will use them for now :slight_smile: