Sign in to follow this  

CreatePipe homework problems

This topic is 3594 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hey guys, I've got a homework problem that's absolutely giving me fits. I'm trying to create a pipe and redirect the standard input to a child process. We were given the sourcecode for an app (called Turtle) that we have to communicate with with an anonymous pipe using Win32 api's CreatePipe function. Turtle is a simple window that reads a couple of commands from the standard input. Type in "Up" and the window moves up a few pixels. Or "Left" a few, "Down" a few, etc. The assignment is to create a child process (the Turtle app) and redirect the standard input for the child process so you can type in commands to the child from the parent processes standard in. It seems so simple, but I just can't get this to work. It should have taken me only a couple of hours to finish this off, but there's some concept about the CreatePipe function that I'm just not getting. I'm including my sourcecode below, but be warned, I have swapped about my read and write handles so much, and gotten so confused on this that this will be ugly. Thanks to anyone who can point me in the right direction:
#include <iostream>
#include <windows.h>

#define BUF_SIZE 20

int main()
{
  HANDLE hPipeRead;
  HANDLE hPipeWrite;
  char buf[BUF_SIZE];
  STARTUPINFO startup;
  PROCESS_INFORMATION process;
  DWORD   byteswritten = 0;
  SECURITY_ATTRIBUTES sa;
  sa.nLength = sizeof(SECURITY_ATTRIBUTES);
  sa.bInheritHandle = TRUE;
  sa.lpSecurityDescriptor = 0;

  ZeroMemory(&startup,sizeof(STARTUPINFO));
  ZeroMemory(&process,sizeof(PROCESS_INFORMATION));

  hPipeRead = GetStdHandle(STD_INPUT_HANDLE);

  CreateProcess("turtle.exe",NULL,0,0,TRUE,0,0,0,&startup,&process);

  while(0 == strstr(buf,"QUIT"))
  {
    ZeroMemory(buf,BUF_SIZE);
    std::cin >> buf;
    if(!(CreatePipe(&hPipeRead,&hPipeWrite,&sa,0)))
      printf("Couldn't create the pipe\n");
    WriteFile(hPipeRead,buf,BUF_SIZE,&byteswritten,NULL);
    CloseHandle(hPipeRead);
    CloseHandle(hPipeWrite);
  }

  TerminateProcess(process.hProcess,0);
  CloseHandle(process.hProcess);
  return 0;
}

Share this post


Link to post
Share on other sites
Quote:
Original post by Goishin
It seems so simple, but I just can't get this to work.

Can you elaborate? Have you determined where the failure occurs? Is your process creating correctly? Is the pipe creating? Where is the problem?

Share this post


Link to post
Share on other sites
Whoa! That was fast!.

I'm creating the process correctly, it's just not reading the input I'm trying to pass to it. The Turtle window is showing up, and it's going away when the program exits, and I'm also reading the input from the standard in correctly, because when I type "QUIT" from the console window the parent and the child are both terminated properly. But for some reason the commands are not being passed to the child process.

- Goishin

Share this post


Link to post
Share on other sites
Hey Smitty,

Sorry about that. You're right. But that's not the problem. I had just gotten desperate by that point and I've swapped everything around to the point that it's just confusing. Before responding to you I swapped it back to the hPipeWrite handle and double checked . That's not it yet. There's just something I'm not getting about this.

- Goishin

Share this post


Link to post
Share on other sites
Quote:
Original post by Goishin
Hey Smitty,

Sorry about that. You're right. But that's not the problem. I had just gotten desperate by that point and I've swapped everything around to the point that it's just confusing. Before responding to you I swapped it back to the hPipeWrite handle and double checked . That's not it yet. There's just something I'm not getting about this.

- Goishin


In other words, the code posted is useless for anyone trying to help.

But still...

char buf[BUF_SIZE];
...
if (0 == strstr(buf,"QUIT")


strstr is using unitialized buffer. Anything can happen here, including buffer overrun, crash, or other weird stuff.



hPipeRead = GetStdHandle(STD_INPUT_HANDLE);

if(!(CreatePipe(&hPipeRead,&hPipeWrite,&sa,0)))

What is the purpose of assigning console to read handle, when you're just going to overwrite it when you create the pipe?

Why is pipe created and closed in a loop?

What are the commands you want to send to child process? How is the process reading them? Do they need to be followed by enter?
std::cin >> buf;
Even if the user needs to hit enter for this line, the \n will not be present in buf.

Share this post


Link to post
Share on other sites
Have you used a debugger to step through the code? Have you looked at the contents of "buf" at the point where you invoke WriteFile, to make sure it has what you think it has?

Also, Antheus is right... you need to edot your code at the top to whatever isn't working so that we can see what you're seeing, or maybe just a new post with the code... I have to leave in a few minutes, but that will help other people help you.

Share this post


Link to post
Share on other sites
Ok, I've changed the code to implement what you guys suggested. Hopefully, it will be a little clearer. That's reposted below. And I'm also posting the code for the turtle app itself. This was given to me by my professor and cannot be changed, but will let you see exactly what it is and does and what commands it understands.

Thanks for the help with this, I've been tearing my hair out over it.


#include <iostream>
#include <windows.h>

#define BUF_SIZE 20

int main()
{
int index = 0;
HANDLE hPipeRead;
HANDLE hPipeWrite;
char buf[BUF_SIZE];
STARTUPINFO startup;
PROCESS_INFORMATION process;
DWORD byteswritten = 0;
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.bInheritHandle = TRUE;
sa.lpSecurityDescriptor = 0;

ZeroMemory(&startup,sizeof(STARTUPINFO));
ZeroMemory(&process,sizeof(PROCESS_INFORMATION));
ZeroMemory(buf,BUF_SIZE);

if(!(CreatePipe(&hPipeRead,&hPipeWrite,&sa,0)))
printf("Couldn't create the pipe\n");

CreateProcess("turtle.exe",NULL,0,0,TRUE,0,0,0,&startup,&process);

while(0 == strstr(buf,"Quit"))
{
ZeroMemory(buf,BUF_SIZE);
std::cin >> buf;
for(index = 0; index < BUF_SIZE; index++)
{
if(buf[index] == 0)
{
buf[index] = '\n';
break;
}
}
WriteFile(hPipeWrite,buf,BUF_SIZE,&byteswritten,NULL);
}
CloseHandle(hPipeRead);
CloseHandle(hPipeWrite);

TerminateProcess(process.hProcess,0);
CloseHandle(process.hProcess);
return 0;
}




And the code for the turtle program

// turtle.cpp
// -- window controlled by standard input (for hw #2)
// cs180 1/08
//
// compile with:
// cl /W3 /EHsc turtle.cpp user32.lib gdi32.lib

#include <iostream>
#include <string>
#include <windows.h>

class Turtle {
public:
Turtle(HINSTANCE, int);
void Draw(const RECT&, HDC);
~Turtle(void);
private:
const char *name;
static LRESULT CALLBACK WinProc(HWND, UINT, WPARAM, LPARAM);
static DWORD WINAPI InputThread(LPVOID);
HINSTANCE hinstance;
HWND window;
HANDLE thread;
int window_dx, window_dy;
};


int WINAPI WinMain(HINSTANCE hinst, HINSTANCE, LPSTR, int show) {
Turtle *turtle = new Turtle(hinst,show);

MSG msg;
while (GetMessage(&msg,0,0,0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}

delete turtle;
return msg.wParam;
}


Turtle::Turtle(HINSTANCE hinst, int show)
: hinstance(hinst), name("Turtle Window"), thread(0) {

window_dx = GetSystemMetrics(SM_CXSCREEN)/50;
window_dy = GetSystemMetrics(SM_CYSCREEN)/50;

WNDCLASS wc;
wc.style = 0;
wc.lpfnWndProc = WinProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hinstance;
wc.hIcon = LoadIcon(0,IDI_APPLICATION);
wc.hCursor = LoadCursor(0,IDC_ARROW);
wc.hbrBackground = 0;
wc.lpszMenuName = 0;
wc.lpszClassName = name;
RegisterClass(&wc);
window = CreateWindow(name,name,WS_BORDER|WS_CAPTION|WS_SYSMENU,
(2*GetSystemMetrics(SM_CXSCREEN))/5,
(2*GetSystemMetrics(SM_CYSCREEN))/5,
GetSystemMetrics(SM_CXSCREEN)/5,
GetSystemMetrics(SM_CYSCREEN)/5,
0,0,hinstance,this);
ShowWindow(window,show);
thread = CreateThread(0,0,InputThread,this,0,0);
}


Turtle::~Turtle(void) {
std::cin.setstate(std::ios_base::failbit);
WaitForSingleObject(thread,INFINITE);
CloseHandle(thread);
UnregisterClass(name,hinstance);
}


LRESULT CALLBACK Turtle::WinProc(HWND win, UINT msg, WPARAM wp, LPARAM lp) {
static Turtle *turtle = 0;

switch (msg) {

case WM_CREATE: {
CREATESTRUCT *csp = reinterpret_cast<CREATESTRUCT*>(lp);
turtle = reinterpret_cast<Turtle*>(csp->lpCreateParams);
return 0; }

case WM_PAINT: {
PAINTSTRUCT ps;
HDC dc = BeginPaint(win,&ps);
turtle->Draw(ps.rcPaint,dc);
EndPaint(win,&ps);
return 0; }

case WM_DESTROY:
PostQuitMessage(0);
return 0;

}
return DefWindowProc(win,msg,wp,lp);
}


void Turtle::Draw(const RECT& rect, HDC dc) {
FillRect(dc,&rect,HBRUSH(GetStockObject(WHITE_BRUSH)));
}


DWORD WINAPI Turtle::InputThread(LPVOID vp) {
Turtle *turtle = reinterpret_cast<Turtle*>(vp);
while (std::cin) {
std::string command;
std::cin >> command;
if (std::cin) {
if (command == "Quit")
SendMessage(turtle->window,WM_CLOSE,0,0);
else {
int dx = 0, dy = 0;
if (command == "Left")
dx = -turtle->window_dx;
else if (command == "Right")
dx = turtle->window_dx;
else if (command == "Up")
dy = -turtle->window_dy;
else if (command == "Down")
dy = turtle->window_dy;
RECT rect;
GetWindowRect(turtle->window,&rect);
MoveWindow(turtle->window,rect.left+dx,rect.top+dy,
rect.right-rect.left,rect.bottom-rect.top,TRUE);
}
}
}
return 0;
}




- Goishin

Share this post


Link to post
Share on other sites
Antheus,

Thanks for the link. So I've gotten it to work once now. And I've got to tell you, I was so ecstatic I jumped around the room for a bit ( I really have been working on this assignment for about eight hours now ). The problem I was having was I hadn't set the startup.dwFlags to STARTF_USESTDHANDLES.

But now it only works once. It's supposed to work over and over until I type Quit. Am I supposed to be closing the pipe and recreating it each time? If so, how do I pass the child process the new read handle after I've created it? Or is there something else I'm supposed to be doing to make it read again? I'm zeroing out the buffer I'm passing to the WriteFile each time through my while loop, so I don't think it has to do with that. But is there something else I'm missing?


#include <iostream>
#include <windows.h>

#define BUF_SIZE 20

int main()
{
int index = 0;
HANDLE hPipeRead;
HANDLE hPipeWrite;
char buf[BUF_SIZE];
STARTUPINFO startup;
PROCESS_INFORMATION process;
DWORD byteswritten = 0;
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.bInheritHandle = TRUE;
sa.lpSecurityDescriptor = 0;

ZeroMemory(&startup,sizeof(STARTUPINFO));
ZeroMemory(&process,sizeof(PROCESS_INFORMATION));
ZeroMemory(buf,BUF_SIZE);


if(!(CreatePipe(&hPipeRead,&hPipeWrite,&sa,0)))
printf("Couldn't create the pipe\n");

startup.hStdInput = hPipeRead;
startup.dwFlags |= STARTF_USESTDHANDLES;

CreateProcess("turtle.exe",NULL,0,0,TRUE,0,0,0,&startup,&process);
std::cin >> buf;
while(0 == strstr(buf,"Quit"))
{
ZeroMemory(buf,BUF_SIZE);
std::cin >> buf;
for(index = 0; index < BUF_SIZE; index++)
{
if(buf[index] == 0)
{
buf[index] = '\n';
break;
}
}
WriteFile(hPipeWrite,buf,BUF_SIZE,&byteswritten,NULL);
}
CloseHandle(hPipeRead);
CloseHandle(hPipeWrite);

TerminateProcess(process.hProcess,0);
CloseHandle(process.hProcess);
return 0;
}



- Goishin

Share this post


Link to post
Share on other sites
Quote:
Original post by Goishin
It's supposed to work over and over until I type Quit. Am I supposed to be closing the pipe and recreating it each time?
I don't know that you are "supposed" to be doing it one way or the other, but I think most people would find it a bit odd that you are creating and destroying it each time. In fact, Antheus questioned you about that point in his original post. I would move it outside of the loop... have you tried it yet to see if it fixed your problem?


Share this post


Link to post
Share on other sites
Smitty,

Weird, when I reposted, it reposted the old code. I must have screwed up when I reposted. But yeah, I actually already have moved the CreatePipe and CloseHandle calls outside of the while loop. This one is the correct code where it works only once:


#include <iostream>
#include <windows.h>

#define BUF_SIZE 20

int main()
{
int index = 0;
HANDLE hPipeRead;
HANDLE hPipeWrite;
char buf[BUF_SIZE];
STARTUPINFO startup;
PROCESS_INFORMATION process;
DWORD byteswritten = 0;
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.bInheritHandle = TRUE;
sa.lpSecurityDescriptor = 0;

ZeroMemory(&startup,sizeof(STARTUPINFO));
ZeroMemory(&process,sizeof(PROCESS_INFORMATION));
ZeroMemory(buf,BUF_SIZE);


if(!(CreatePipe(&hPipeRead,&hPipeWrite,&sa,0)))
printf("Couldn't create the pipe\n");

startup.hStdInput = hPipeRead;
startup.dwFlags |= STARTF_USESTDHANDLES;

CreateProcess("turtle.exe",NULL,0,0,TRUE,0,0,0,&startup,&process);
std::cin >> buf;
while(0 == strstr(buf,"Quit"))
{
ZeroMemory(buf,BUF_SIZE);
std::cin >> buf;
for(index = 0; index < BUF_SIZE; index++)
{
if(buf[index] == 0)
{
buf[index] = '\n';
break;
}
}
WriteFile(hPipeWrite,buf,BUF_SIZE,&byteswritten,NULL);
}
CloseHandle(hPipeRead);
CloseHandle(hPipeWrite);

TerminateProcess(process.hProcess,0);
CloseHandle(process.hProcess);
return 0;
}



Sorry for the confusion,

- Goishin

Share this post


Link to post
Share on other sites

This topic is 3594 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this