do anonymous pipes normally work on NT?

Georg Mischler schorsch at schorsch.com
Sat Jun 12 15:03:11 EDT 1999


'''
Hi all,

I seem to be unable to do anonymous pipes right on NT4sp3.
This may be more of a Windows than a Python related
question, but I'll try it here for a start anyway...

The code below was created with the help of an example
posted here some time ago by Bill Tut, a C++ example by
someone else, and after studying the docs to the win32
extensions by Mark Hammond, as well as the documentation
in VisualStudio for VC++5.

To my uninitiated eyes, everything looks as if it would
make sense, and corresponds to similar code for unix,
using fork() and execv().

The problem is, that in the case here, I can write
to the stdin pipe, but reading from stdout or stderr
will get no results at all. All I see is an exception
eventually, telling me:
  '(109, 'ReadFile', 'Die Pipe wurde beendet')
'The pipe was terminated'.

When calling a program from cygwin (b19), instead
of listening to lines of input and answering with
its results, it will just die and nothing else
will happen. This is normally the same if I call a
program from windows, except for example with ftp.exe,
which starts an infinite loop and eats all cpu. I don't
know if this is a related problem or just a ftp.exe
weirdness.

An example invocation would be:

d:\somewhere\> python
>>> import ntprocess
>>> p = ntprocess.Coprocess('c:\\winnt\\system32\\ping.exe -t
localhost')
>>> print p.read()
# nothing happens until I kill ping from the task manager:
Traceback (innermost last):
  File "<stdin>", line 1, in ?
  File "ntprocess.py", line 111, in read
    res, str = win32file.ReadFile(self.hStdout, 0)
pywintypes.api_error: (109, 'ReadFile', 'Die Pipe wurde beendet.')

Now it may be that the nt console programs don't
write to stdout but just directly to the console.
But my cygwin stuff certainly *does* write to stdout
and stderr. Are there any problems known when
combining native NT Python with cygwin binaries?
Am I doing aything else wrong?

Since I have *no* idea where to look, I can't help
but just include the complete source below.
If anyone knowing more about this stuff could
take a short look at it and point me to the blatant
error I made, I'd be extremely happy.


Thanks for any insights!

-schorsch

'''
# ntprocess.py

import types
import os
import sys
import string

import msvcrt
import winnt
import win32api
import win32pipe
import win32file
import win32process
import win32event
import win32security


class Coprocess:
    def __init__ (self, cmd, env={}):
        self.running = 0
        self.exitstatus = None

        if type(cmd) == types.StringType:
            cmd = string.split(cmd)
        executable = cmd[0]
        self.args = cmd[1:]
        self.executable = os.path.normpath(executable)

        # security attributes for created pipes
        sAttrs = win32security.SECURITY_ATTRIBUTES()
        sAttrs.bInheritHandle = 1

        # redirecting child STDOUT:
        # XXX redirection should actually be checked before
        # cmd is split.
        # XXX Right now, the '>' must be framed by whitespace
        # in a string.
        if '>' in self.args:
            redirpos = self.args.index('>')
            if len(self.args) < redirpos + 2:
                raise IOError, 'Empty Redirect.'
            if len(self.args) > redirpos + 2:
                raise IOError, 'Ambiguous Redirect.'
            outfn = self.args[-1]
            outfd = os.open(outfn,
                    os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0664)
            hStdout_w = msvcrt.get_osfhandle(outfd)
            self.args = self.args[:-2]
            hStdout_r = None
            self.hStdout = hStdout_r
        elif '>>' in self.args:
            redirpos = self.args.index('>>')
            if len(self.args) < redirpos + 2:
                raise IOError, 'Empty Redirect.'
            if len(self.args) > redirpos + 2:
                raise IOError, 'Ambiguous Redirect.'
            outfn = self.args[-1]
            outfd = os.open(outfn,
                    os.O_WRONLY | os.O_CREAT | os.O_APPEND, 0664)
            hStdout_w = msvcrt.get_osfhandle(outfd)
            self.args = self.args[:-2]
            hStdout_r = None
            self.hStdout = hStdout_r
        else:
            hStdout_r, hStdout_w = win32pipe.CreatePipe(sAttrs, 0)
            self.hStdout = hStdout_r

        # child STDIN:
        hStdin_r, hStdin_w = win32pipe.CreatePipe(sAttrs, 0)
        self.hStdin = hStdin_w

        # child STDERR:
        hStderr_r, hStderr_w = win32pipe.CreatePipe(sAttrs, 0)
        self.hStderr = hStderr_r

        # create environment for new process.
        newenv = os.environ.copy() # use parent environment.
        newenv.update(env) # add/override from caller data.

        # set up the command line.
        sCmdLine = self.executable + ' ' + string.join(self.args)

        # set the info structure for the new process.
        StartupInfo = win32process.STARTUPINFO()
        StartupInfo.hStdInput  = hStdin_r
        StartupInfo.hStdOutput = hStdout_w
        StartupInfo.hStdError  = hStderr_w
        StartupInfo.dwFlags = win32process.STARTF_USESTDHANDLES

        # start the process.
        hProcess, hThread, dwPid, dwTid = win32process.CreateProcess(
                self.executable, sCmdLine, # what to start
                None,    # process security attributes
                None,    # thread attributes
                1,       # inherit handles, or USESTDHANDLES won't work.
                         # creation flags. Don't access the console.
                win32process.DETACHED_PROCESS,
                newenv,  # new environment
                None,    # current directory (stay where we are)
                StartupInfo)
        #print hProcess, hThread, dwPid, dwTid
        self.hProcess = hProcess
        self.hThread = hThread
        self.pid = dwPid
        self.tid = dwTid
        self.running = 1

    def write(self, str):
        err, bytes = win32file.WriteFile(self.hStdin, str)
        return bytes

    def read(self):
        if not self.hStdout: raise IOError, 'Error: output redirected.'
        res, str = win32file.ReadFile(self.hStdout, 0)
        if res == 0: return str
        else: return ''

    def stderr_read(self):
        res, str = win32file.ReadFile(self.hStderr, 0)
        if res == 0: return str
        else: return ''

    #XXX we'd probably need to implement line buffering ourselves
    #def readline(self):
    #    return ''

    #def stderr_readline(self):
    #    return ''

    def flush(self):
        win32file.FlushFileBuffers(self.hStdin)

    def waitpid (self, opt=0):
        wres = win32event.WaitForSingleObject(self.hProcess, opt)
        # XXX do we want to check the status if we timed out?
        status = win32process.GetExitCodeProcess(self.hProcess)
        self.exitstatus = status/256
        self.running = 0
        return (self.pid, status)

    def kill (self, signal=9):
        self.close()
        if self.running:
            win32process.TerminateProcess(self.hProcess, -9)
            return self.waitpid(0)
        else:
            return 0

    def __del__(self):
        '''close all handles when our instance is garbage collected.'''
        self.close()

    def close(self):
        # close handles defensively, in case we fail early...
        if hasattr(self, 'self.hStdin'):
            win32api.CloseHandle(self.hStdin)
        if hasattr(self, 'self.hStdout'):
            win32api.CloseHandle(self.hStdout)
        if hasattr(self, 'self.hStderr'):
            win32api.CloseHandle(self.hStderr)
        if hasattr(self, 'self.hProcess'):
            win32api.CloseHandle(self.hProcess)
        if hasattr(self, 'self.hThread'):
            win32api.CloseHandle(self.hThread)


--
Georg Mischler  --  simulation developper  --  schorsch at schorsch.com
+schorsch.com+  --  lighting design tools  --  http://www.schorsch.com/


Sent via Deja.com http://www.deja.com/
Share what you know. Learn what you don't.




More information about the Python-list mailing list