Login | Register   
LinkedIn
Google+
Twitter
RSS Feed
Download our iPhone app
TODAY'S HEADLINES  |   ARTICLE ARCHIVE  |   FORUMS  |   TIP BANK
Browse DevX
Sign up for e-mail newsletters from DevX


advertisement
 

Capturing the Output of a MS-DOS Program : Page 2

See how the Shell function really works and how you can replace it with a more flexible CShell class that support synchronous execution of an external program, as well as redirection of the shelled program's output to a file.


advertisement

The Standard Output Device
The program you run will create a window. To establish a channel with this window you could utilize the STARTUPINFO argument. Such a data structure lets you decide the style of the window, its size, the text for the title bar and even the desktop (under Windows NT only). It also makes available three handles to manage the three standard I/O devices: input, output and error. If you want the output of your MS-DOS program to go to a file just pass a handle to that file as the hStdOut field.

Notice that this technique will work only with those program that send their output to the standard output device (aka stdout). This is quite usual with many MS-DOS and Win32 console programs, but is not a rule.



To make use of the hStdOut field you need to add the STARTF_USESTDHANDLES to the dwFlags field:

si.cb = Len(si)
si.dwFlags = STARTF_USESHOWWINDOW Or _
             STARTF_USESTDHANDLES

I've said that you need to pass a file handle to hStdOut. That's true but not all the handles will work fine. The only valid handle is any returned by CreateFile(). There's a great difference between, say, a window handle and what's needed here.

Dim hFile As Long
hFile = CreateFile(
    sTempFile, _
    GENERIC_READ Or GENERIC_WRITE, _
    0, _
    ByVal 0&, _
    CREATE_ALWAYS, _
    0, _
    ByVal 0&)

If hFile Then
    si.hStdOutput = hFile
End If

Once you created your output file and passed its handle to hStdOut everything that the running program sends out to its stdout goes to the file. At the end of the program you have just to close the file with CloseHandle() and read it back in case you want to return a string.

Designing the CShell Class
The CShell class I mentioned earlier will have just an Execute method with a prototype like this:

Public Function Execute( _
   ByVal szProgram As String, _
   Optional ByVal fRedirectOutput As Boolean_
) As String

It takes the name of the program to run, including any needed parameters, and a boolean flag that can explicitly state whether output redirection is needed or not.

Public Function Execute(ByVal szProgram As String, _
     Optional ByVal fRedirectOutput) As String
Dim hProcess As Long
Dim hFile As Long
Dim si As STARTUPINFO
Dim pi As PROCESS_INFORMATION

Execute = ""
If IsMissing(fRedirectOutput) Then
   fRedirectOutput = False
End If

' If redirection needed executes program
sTempFile = GetTempFile
If fRedirectOutput Then
     si.cb = Len(si)
     si.dwFlags = STARTF_USESTDHANDLES Or STARTF_USESHOWWINDOW
     si.wShowWindow = SW_HIDE

     ' Creates a temp file
     hFile = CreateFile(sTempFile, GENERIC_READ Or GENERIC_WRITE, _
                 0, ByVal 0&, CREATE_ALWAYS, 0, ByVal 0&)
     If hFile Then
          si.hStdOutput = hFile
     End If
End If

' Creates the process
bResult = CreateProcess(szProgram, vbNullString, _
         ByVal 0&, ByVal 0&, True, NORMAL_PRIORITY_CLASS, _
         ByVal 0&, vbNullString, si, pi)
If bResult Then
     WaitForSingleObject pi.hProcess, INFINITE
End If

' Closes handles
If hFile And fRedirectOutput Then
    Dim numOfBytes As Long
    Dim buf As String
    numOfBytes = GetFileSize(hFile, ByVal 0&)
    buf = Space(numOfBytes)
    CloseHandle (hFile)

    ' Reads back the file content
    hFile = lopen(sTempFile, 0)
    lread hFile, buf, numOfBytes
    lclose (hFile)

    ' Returns
    Execute = buf
    DeleteFile (sTempFile)
End If
End Function

Private Function GetTempFile() As String
  Dim s As String

  dwSize = GetTempPath(0, s)
  s = Space(dwSize - 1)
  GetTempPath dwSize - 1, s
  If Right$(s, 1) <> "\" Then
     s = s + "\"
  End If
  s = s + "TEMP.tmp"
  GetTempFile = s
End Function

Often you have also the problem of stopping the calling process until the spawned process terminates. This can be easily accomplished via WaitForSingleObject() as shown in the listing above.

Summary
This article demonstrated how to capture the output of MS-DOS program that writes to the standard output device. By making a specialized use of CreateProcess you can redirect the output to a temporary file and then read it back into a string. The source code available includes a cshell.cls file plus a cshell.bas with all the declarations needed.



Dino Esposito is a trainer and consultant based in Rome, Italy. He specializes in Windows and COM development and authored many programming books, among which Visual C++ Windows Shell Programming; and Windows Scripting Host Tutorial, both from Wrox Press. Dino is also a contributing editor to MIND, MSJ and MSDN News, and has written articles for many other magazines. He currently works for Artis srl, a Rome-based consulting company which is active in the system integration area and Web-based architectures. Dino is also a frequent speaker at industry conferences such as Microsoft DevDays and TechEd. You can see a list of his articles in the Magazine Bank.
Comment and Contribute

 

 

 

 

 


(Maximum characters: 1200). You have 1200 characters left.

 

 

Sitemap
Thanks for your registration, follow us on our social networks to keep up-to-date