Pipe ReadFile blockiert



  • Hallo, ich habe ein C++-Programm, was die Standardausgabe von net users auslesen soll. Unglücklicherweise blockiert ReadFile mit dem Lesehandle meiner Pipe.
    Es besteht aus folgenden Bestandteilen:
    -drei Klassen
    -eine Funktion, die die drei Klassen verwendet
    Wäre jemand so freundlich, meinen Code zu kontrollieren und mir zu sagen, wo das Problem liegt und wie man es behebt?
    Bei entsprechender Antwort werde ich meinen Code posten.


  • Mod

    @philipp95 sagte in Pipe ReadFile blockiert:

    Wäre jemand so freundlich, meinen Code zu kontrollieren und mir zu sagen, wo das Problem liegt und wie man es behebt?

    Vielleicht? Woher soll man das wissen, wenn du deinen Code nicht zeigst? Wir sind hier in einem Hilfeforum, da stehen die Chancen eigentlich recht gut, dass dir jemand helfen würde. Wenn man dir aber alles mühsam aus der Nase ziehen muss, sinken deine Chancen jedoch ganz schnell.



  • PathRetriever.h:

    #pragma once
    
    #include <Windows.h>
    #include <Shlwapi.h>
    #pragma comment(lib,"Shlwapi.lib")
    #include <string>
    
    class PathRetriever
    {
    public:
    	PathRetriever(void);
    	bool RetrievePath(std::wstring& Target);
    	~PathRetriever(void);
    };
    

    MyPipe.h:

    #pragma once
    #include <Windows.h>
    #include <string>
    
    class MyPipe
    {
    private:
    	HANDLE hRead;
    	HANDLE hWrite;
    	bool bCreateSuccessful;
    	void CreatePipeInternal();
    public:
    	MyPipe(void);
    	bool ReturnCreateSucessfull();
    	HANDLE ReturnReadHandle();
    	HANDLE ReturnWriteHandle();
    	void ReadLoop(std::wstring& ResultString);
    	~MyPipe(void);
    };
    

    MyProcess.h:

    [#pragma once
    
    #include <Windows.h>
    #include <string>
    #include "MyPipe.h"
    
    class MyProcess
    {
    private:
    	bool bSuceeded;
    	STARTUPINFO StartupInfo;
    	PROCESS_INFORMATION ProcessInformation;
    	void ZeroStructures();
    	void ConstructInternal(wchar_t* Path,wchar_t* Arguments,HANDLE hOutput);
    	bool bCreateSuccessful;
    public:
    	MyProcess(std::wstring Path);
    	MyProcess(std::wstring Path,std::wstring Arguments);
    	MyProcess(std::wstring Path,std::wstring Arguments,MyPipe OutputPipe);
    	bool ReturnCreateSucessfull();
    	void WaitForExit();
    	~MyProcess(void);
    };]
    

    MyPipe.h:

    #pragma once
    #include <Windows.h>
    #include <string>
    class MyPipe
    {
    private:
    	HANDLE hRead;
    	HANDLE hWrite;
    	bool bCreateSuccessful;
    	void CreatePipeInternal();
    public:
    	MyPipe(void);
    	bool ReturnCreateSucessfull();
    	HANDLE ReturnReadHandle();
    	HANDLE ReturnWriteHandle();
    	void ReadLoop(std::wstring& ResultString);
    	~MyPipe(void);
    };
    

    MyPipe.cpp:

    #include "MyPipe.h"
    
    MyPipe::MyPipe(void)
    {
    	CreatePipeInternal();
    }
    void MyPipe::CreatePipeInternal() {
    	bCreateSuccessful=false;
    	SECURITY_ATTRIBUTES saAttr={0};
    	saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); 
    	saAttr.bInheritHandle = TRUE; 
    	saAttr.lpSecurityDescriptor = NULL; 
    	if(CreatePipe(&this->hRead,&this->hWrite,&saAttr,0)) {
    		bCreateSuccessful=true;
    	}
    	;
    }
    HANDLE MyPipe::ReturnReadHandle() {
    	return hRead;
    }
    HANDLE MyPipe::ReturnWriteHandle() {
    	return hWrite;
    }
    bool MyPipe::ReturnCreateSucessfull() {
    	return bCreateSuccessful;
    }
    void MyPipe::ReadLoop(std::wstring& ResultString) {
    	DWORD dwRead=0;
    	bool bResult=true;
    	wchar_t Buf[2]={0};
    	do {
    		ZeroMemory(&Buf,sizeof(wchar_t)*2);
    		dwRead=0;
    		if(ReadFile(this->ReturnReadHandle(),Buf,sizeof(wchar_t)*2,&dwRead,0)) {
    			bResult=true;
    		}
    		else {
    			DWORD dwLast=GetLastError();
    		}
    		ResultString+=std::wstring(Buf);
    	}
    	while(bResult&&dwRead==sizeof(wchar_t)*2);
    }
    MyPipe::~MyPipe(void)
    {
    }
    

    MyProcess.cpp:

    #include "MyProcess.h"
    
    void MyProcess::ZeroStructures(void) {
    	ZeroMemory(&this->StartupInfo,sizeof(STARTUPINFO));
    	ZeroMemory(&this->ProcessInformation,sizeof(ProcessInformation));
    }
    void MyProcess::ConstructInternal(wchar_t* Path,wchar_t* Arguments,HANDLE hOutput){
    	ZeroStructures();
    	bCreateSuccessful=false;
    	if(hOutput) {
    		this->StartupInfo.hStdOutput=hOutput;
    		this->StartupInfo.dwFlags|=STARTF_USESTDHANDLES;
    	}
    	if(CreateProcess(Path,Arguments,0,0,TRUE,NORMAL_PRIORITY_CLASS,0,0,&this->StartupInfo,&this->ProcessInformation)) {
    		bCreateSuccessful=true;
    	}
    }
    MyProcess::MyProcess(std::wstring Path) {
    	ConstructInternal(const_cast<wchar_t*>(Path.c_str()),0,0);
    }
    
    MyProcess::MyProcess(std::wstring Path,std::wstring Arguments)
    {
    	ConstructInternal(const_cast<wchar_t*>(Path.c_str()),const_cast<wchar_t*>(Arguments.c_str()),0);
    }
    MyProcess::MyProcess(std::wstring Path,std::wstring Arguments,MyPipe OutputPipe)
    {
    	ConstructInternal(const_cast<wchar_t*>(Path.c_str()),const_cast<wchar_t*>(Arguments.c_str()),OutputPipe.ReturnWriteHandle());
    }
    bool MyProcess::ReturnCreateSucessfull() {
    	return bCreateSuccessful;
    }
    void MyProcess::WaitForExit() {
    	WaitForSingleObject(this->ProcessInformation.hProcess,INFINITE);
    }
    MyProcess::~MyProcess(void)
    {
    	CloseHandle(this->ProcessInformation.hThread);
    	CloseHandle(this->ProcessInformation.hProcess);
    }
    

    PathRetriever.cpp:

    #include "PathRetriever.h"
    
    PathRetriever::PathRetriever(void)
    {
    }
    bool PathRetriever::RetrievePath(std::wstring& Target) {
    	bool bReturn=false;
    	WCHAR Path[MAX_PATH+1]={0};
    	if(GetSystemDirectory(Path,MAX_PATH+1)) {
    		if(PathAppend(Path,L"net.exe")) {
    			Target=std::wstring(Path);
    			bReturn=true;
    		}
    	}
    	return bReturn;
    }
    PathRetriever::~PathRetriever(void)
    {
    }
    

    Die Funktion:

    void RunNetAndDisplayResult() {
    	MyPipe Pipe;
    	if(Pipe.ReturnCreateSucessfull()) {
    		SetHandleInformation(Pipe.ReturnWriteHandle(),HANDLE_FLAG_INHERIT,HANDLE_FLAG_INHERIT);
    		std::wstring Path=L"";
    		PathRetriever Retriever;
    		Retriever.RetrievePath(Path);
    		MyProcess Process(Path,L"users",Pipe);
    		if(Process.ReturnCreateSucessfull()) {
    			Process.WaitForExit();
    			std::wstring Result=L"";
    			Pipe.ReadLoop(Result);
    			MessageBox(0,const_cast<wchar_t*>(Result.c_str()),L"Ergebnis",MB_ICONINFORMATION);
    		}
    	}
    }
    


  • ist jetzt geraten aber wird die Process.WaitForExit() denn überhaupt beendet?



  • @Wade1234 Ja, Process.WaitForExit() wird beendet. Ich habe enen Breakpoint bei "std::wstring Result=L"";" gesetzt. Der Breakpoint wird erreicht.



  • @philipp95 probier mal SetHandleInformation(Pipe.ReturnWriteHandle(),HANDLE_FLAG_INHERIT,HANDLE_FLAG_INHERIT); in SetHandleInformation(Pipe.ReturnWriteHandle(),HANDLE_FLAG_INHERIT,0); zu ändern. ansonsten gibt es im msdn ein sehr schönes beispiel für die verwendung von pipes.



  • @Wade1234 Ich habe die Zeile in SetHandleInformation(Pipe.ReturnWriteHandle(),HANDLE_FLAG_INHERIT,0); geändert. Dennoch blockiert ReadFile.



  • und ConstructInternal(const_cast<wchar_t*>(Path.c_str()),const_cast<wchar_t*>(Arguments.c_str()),OutputPipe.ReturnWriteHandle()); funktioniert? also eigentlich kannst du char nicht so einfach nach wchar casten.



  • @Wade1234 std::wstring Path,std::wstring Arguments



  • Nur mal eine Vermutung: Du kannst vor ReadFile mit PeekNamedPipe schauen, ob überhaupt etwas zu holen ist (auch ohne zu kopieren). Und anschließend eben solange ReadFile aufrufen, bis die verfügbaren Bytes (lpTotalBytesAvail) abgeholt sind.
    Irgendwas war da nämlich...



  • @philipp95 sagte in Pipe ReadFile blockiert:

    @Wade1234 std::wstring Path,std::wstring Arguments

    ja aber arguments.c_str()? wieso liest du eigentlich immer 2* sizeof(wchar_t)? wird überhaupt etwas aus der pipe gelesen?

    @yahendrik sagte in Pipe ReadFile blockiert:

    Nur mal eine Vermutung: Du kannst vor ReadFile mit PeekNamedPipe schauen, ob überhaupt etwas zu holen ist (auch ohne zu kopieren). Und anschließend eben solange ReadFile aufrufen, bis die verfügbaren Bytes (lpTotalBytesAvail) abgeholt sind.
    Irgendwas war da nämlich...

    das ist aber eine anonymous pipe, bei der readfile eigentlich mit eof bzw. dem entsprechenden äquivalent returnen sollte, wenn keine daten mehr vorhanden sind.

    jedenfalls ist das mal ein schönes beispiel, warum ich lieber mit C bzw. funktionsorientierung programmiere.


  • Mod

    Pipe? Warum Pipe? Das ist eine normale Datei...

    Halte Dich an dieses offizielle Beispiel und es sollte gehen:
    https://docs.microsoft.com/en-us/windows/win32/procthread/creating-a-child-process-with-redirected-input-and-output



  • @philipp95 sagte in Pipe ReadFile blockiert:

    Hallo, ich habe ein C++-Programm, was die Standardausgabe von net users auslesen soll.

    So was hab ich auch mal gemacht:
    https://www.c-plusplus.net/forum/topic/248213/system-befehl-ausgabe-umleiten/10



  • Oder einfach popen/_popen/_wpopen verwenden. Geht noch einfacher und ist sogar fast plattformunabhängig. Ob das für dich sinnvoll ist, sei dahingestellt, es ist aber zweifelsohne kürzer.

    #include <cstdio>
    //...
    static int closePipe(FILE *p)
    {
    #if _WIN32
        return _pclose(p);
    #else
        return pclose(p);
    #endif
    };
    
    //eine Klasse, deren Objekt eine Pipe enthält und diese automatisch mit closePipe schließt.
    using pipeptr = std::unique_ptr<FILE, decltype(&closePipe)>;
    
    //führt command aus und gibt dessen errorcode + rückgabewert zurück
    std::pair<int, std::string> exec(const std::string &command)
    {
    #ifdef _WIN32
        //2>&1 sorgt dafür, dass stderr nach stdout fließt
        pipeptr pipe(_popen((command + " 2>&1").c_str(), L"r"), &closePipe);
    #else
        pipeptr pipe(popen((command + " 2>&1").c_str(), &closePipe);
    #endif
        if(!pipe)
            ...//fehler, den du aus errno auslesen kannst.
    
        std::string output;
    
        char buf[512];
        while(fgets(buf, sizeof(buf), pipe.get()))
            output += buf;
    
        auto ret = closePipe(pipe.release());
        return { ret, output };
    }