# Embed a dos shell windows into a VB form



## mmxx (Nov 17, 2004)

Dear all
I am a newbie of VB. Actually I am trying to build a very simple interface for a statistical software which usually takes a long execution time to give out some results.
I my case, calculations may be started from the VB interface by using the Shell command. This command opens a DOS shell windows where calculation that take place. 

Now instead of opening a new shell windows for each run I would like to have just one embedded DOS shell in my form. That is rather than openening a Dos shell windows, I would like to display the contents of the shell window in my VB form.
How can I do that?
Thanks a lot
Max


----------



## Mosaic1 (Aug 17, 2001)

Which version of Windows are you using? And is this a 16 ibt or 32 bit application"?


----------



## Mosaic1 (Aug 17, 2001)

That last question was in the event you wanted to pipe the results into a text box using the API and several calls.

But if you just want the command window to be slipped into your form, you can get the Handle to the command window and set your form as its parent.


----------



## mmxx (Nov 17, 2004)

Dear Mosaic
thanks for your responses. I am working on a W2000, 32bit-OS.
Could I see an example of your suggestion??
Many thanks!
Max


----------



## Mosaic1 (Aug 17, 2001)

Hi Max,

You're welcome.

This is the one which would look more professional in my opinion, much better than having a command window inside your form. I got most of the code from another forum (Extreme Visual Basic) and made a few adjustments for your purposes.

Open your form, add two textboxes to it. Make one larger to hold the output from the command program and set its multi line property to true.
Add a vertical scrollbar to that one. Name it txtOut.

The second text box is going to be just large enough to hold your command. Name that txtPrompt

Add a command button. The idea is for you to type a command into txtPrompt and then to press the command button to execute. I changed the caption on the command button to "Execute"

The results will show in the large text box ( the one named txtOut).

Add a module to your project.

Copy and paste this code into that module.

```
Option Explicit

'Declare APIs for shell execution.
Public Declare Function CreatePipe Lib "kernel32" ( _
    phReadPipe As Long, _
    phWritePipe As Long, _
    lpPipeAttributes As Any, _
    ByVal nSize As Long) As Long
Public Declare Function ReadFile Lib "kernel32" ( _
    ByVal hFile As Long, _
    ByVal lpBuffer As String, _
    ByVal nNumberOfBytesToRead As Long, _
    lpNumberOfBytesRead As Long, _
    ByVal lpOverlapped As Any) As Long

Public Declare Function CreateProcessA Lib "kernel32" (ByVal _
    lpApplicationName As Long, ByVal lpCommandLine As String, _
    lpProcessAttributes As Any, lpThreadAttributes As Any, _
    ByVal bInheritHandles As Long, ByVal dwCreationFlags As Long, _
    ByVal lpEnvironment As Long, ByVal lpCurrentDirectory As Long, _
    lpStartupInfo As Any, lpProcessInformation As Any) As Long

Public Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long


'Types related to shell execution.
Public Type SECURITY_ATTRIBUTES
    nLength As Long
    lpSecurityDescriptor As Long
    bInheritHandle As Long
End Type
Public Type STARTUPINFO
    cb As Long
    lpReserved As Long
    lpDesktop As Long
    lpTitle As Long
    dwX As Long
    dwY As Long
    dwXSize As Long
    dwYSize As Long
    dwXCountChars As Long
    dwYCountChars As Long
    dwFillAttribute As Long
    dwFlags As Long
    wShowWindow As Integer
    cbReserved2 As Integer
    lpReserved2 As Long
    hStdInput As Long
    hStdOutput As Long
    hStdError As Long
End Type
Public Type PROCESS_INFORMATION
    hProcess As Long
    hThread As Long
    dwProcessId As Long
    dwThreadID As Long
End Type

'Some constants that we need.
Public Const NORMAL_PRIORITY_CLASS = &H20&
Public Const STARTF_USESTDHANDLES = &H100&
Public Const STARTF_USESHOWWINDOW = &H1
Public Const SW_HIDE = 0
Public strRet$

'This sub executes a command line app and gets its
'output.
Public Function ExecuteApp(sCmdline As String) As String
    Dim proc As PROCESS_INFORMATION, ret As Long
    Dim start As STARTUPINFO
    Dim sa As SECURITY_ATTRIBUTES
    Dim hReadPipe As Long 'The handle used to read from the pipe.
    Dim hWritePipe As Long 'The pipe where StdOutput and StdErr will be redirected to.
    Dim sOutput As String
    Dim lngBytesRead As Long, sBuffer As String * 256

    sa.nLength = Len(sa)
    sa.bInheritHandle = True
      
    ret = CreatePipe(hReadPipe, hWritePipe, sa, 0)
    If ret = 0 Then
        MsgBox "CreatePipe failed. Error: " & Err.LastDllError
        Exit Function
    End If

    start.cb = Len(start)
    start.dwFlags = STARTF_USESTDHANDLES Or STARTF_USESHOWWINDOW
    ' Redirect the standard output and standard error to the same pipe
    start.hStdOutput = hWritePipe
    start.hStdError = hWritePipe
    start.wShowWindow = SW_HIDE
       
    ' Start the shelled application:
    
    ret = CreateProcessA(0&, sCmdline, sa, sa, True, NORMAL_PRIORITY_CLASS, 0&, 0&, start, proc)
    If ret = 0 Then
        MsgBox "CreateProcess failed. Error: " & Err.LastDllError
        Exit Function
    End If
   
    ' The handle wWritePipe has been inherited by the shelled application
    ' so we can close it now
    CloseHandle hWritePipe

    ' Read the characters that the shelled application
    ' has outputed 256 characters at a time
   
    Do
            
        ret = ReadFile(hReadPipe, sBuffer, 256, lngBytesRead, 0&)
            
          'Add the output to the textbox.  
      
  Form1.txtOut.Text = Form1.txtOut.Text & Left$(sBuffer, lngBytesRead)
        strRet = strRet & Left$(sBuffer, lngBytesRead)
            
            'Force the windows message queue to update the screen before
            'Reading more.
        DoEvents
    Loop While ret <> 0 ' if ret = 0 then there is no more characters to read
    
    
    CloseHandle proc.hProcess
    CloseHandle proc.hThread
    CloseHandle hReadPipe

    ExecuteApp = sOutput ' Return the output of shelled program.
    
End Function
```
 Go back to the form's code and add this.


```
Private Sub Command1_Click()
Dim Doit As String
txtOut.Text = ""
strRet = ""
If txtPrompt.Text = "" Then
       Exit Sub
    Else
       Doit = txtPrompt.Text
        
        txtOut.Text = ExecuteApp(Doit)
    End If
    txtOut.Text = strRet & vbCrLf & "Done"
End Sub
```
Test it.

As an example type this into txtPrompt

ping techguy.org

Press the command button.

Watch txtOut to see the results as they come in.

Now see how this works for your original purpose. A text box has a limit of 64kb. How long are the results of your app? Will this all fit?


----------



## mmxx (Nov 17, 2004)

Dear Mosaic , I have tested your code but unfortunately it doesn't work well for my app while works well for "ping" and "cmd" as your examples suggest. My app is a run-time version of a matrix-programming language called Gauss (it is similar to Matlab). The result I get with the code for the VB interface is an infinite repetition of the <gauss> prompt, as you were hitting repeatedly the Enter Key at a dos prompt. 
Moreover my problem is just to display some results that appear in a dos-shell.
So I don't need to give any manual prompt, just displaying the results in the secon d window would be fine. I don't have the sufficient knowledge to deal with this problem, although I tried to understand the code an will try to customise the code by my own. 
But do you have any idea about what is going on?
Thanks a lot
max


----------



## Mosaic1 (Aug 17, 2001)

When I asked you about the bits, that was not the OS, but pertained to the program itself. So what you actually want is to embed a command prompt in a window? That can be done easily for something like notepad. There is code all over the internet for doing this. You will find, however that is seems not to work for the prompt window. In fact it does, but you have to add a pause after you start the prompt. Then it works.


----------



## mmxx (Nov 17, 2004)

Dear Mosaic Thanks for your answer, finally I got a newer version of the Gauss run-time and your code works! But ...I still get an aplication error when the job is done. This is because when the computations end, the Gauss prompt starts again and this generates an application error for the Gauss -run time. I cannot undesrtand why.
Where should I put the pause ? I understand this is a for-next cycle, right?.What do you mean "just before starting the prompt"?
I am near to solve all my problems if this works !
Thanks in advance for your patience.
Cheers
max


----------



## Mosaic1 (Aug 17, 2001)

The pause was for a different approach. You can actually put any window inside your form. Open a notepad and set it's parent (Using API calls) to form1. This works perfectly for most applications. For some reason, no matter which approach ( I used 3 different codes) the cmd window wont go into the form. I found it to be some kind of a slowdown in finding it. So adding a pause gave everything a chance to catch up.

I wish I could be of more help with your other problem. Unfortunately if it's an error generated from the other program, I am not sure what to try. 

It sounds like Gauss isn't exiting properly. What is the exact error?


----------



## mmxx (Nov 17, 2004)

Yes, I think the problem is the Gauss application and no in the VB code. Anyway the error that appears is the following:

Windows Application ERROR:
The instruction at "0x00ef1385" referenced memory at "0x00ef1385" The memory could not be read.

I also realised the following: When I launch the Gauss application at the DOS command prompt I always get dispalyed the following:
1) Gauss Header, like:
GAUSS Run-Time Module 6.0.39 (Nov 18 2004) 32-bit
(C)Copyright 1984-2004 Aptech Systems, Inc.
All Rights Reserved Worldwide.
2) then the app reports the results of my program written in Gauss.

When I try to run it form the VB form I get results displayed in the reverse order (very strange!)

1) First I get my results
2) When the job is done the app displays the header.

Also the output is a text of about 20k , I don't know if this is relevant.
Thanks for your attention
max



Mosaic1 said:


> The pause was for a different approach. You can actually put any window inside your form. Open a notepad and set it's parent (Using API calls) to form1. This works perfectly for most applications. For some reason, no matter which approach ( I used 3 different codes) the cmd window wont go into the form. I found it to be some kind of a slowdown in finding it. So adding a pause gave everything a chance to catch up.
> 
> I wish I could be of more help with your other problem. Unfortunately if it's an error generated from the other program, I am not sure what to try.
> 
> It sounds like Gauss isn't exiting properly. What is the exact error?


----------



## Mosaic1 (Aug 17, 2001)

Do you get any of this type of error message when you rin other programs on your system?

After you get the error message, does VB shut down? Are you running this in the IDE or have you compiled?


----------



## Mosaic1 (Aug 17, 2001)

When the original code was written by others, the results disappeared from the screen as soon as the job was done. In the case of Ping, there waws just a falsh and then the textbox was empty. I found that annoying. What's the point if you cannot read the results? So I added something to keep them on screen. The question is whether you are seeing the full result and then the word Done? No?

EDIT:



> When I try to run it form the VB form I get results displayed in the reverse order (very strange!)


I agree that is odd.

The thing is that if you run ping it shuts itself down after running. Gauss doesn't?


----------



## Mosaic1 (Aug 17, 2001)

When this code was written it was a correction written by others of other code found at another site. I the took that code and made a few adjustments for this particular use. After going over it I see that there are a couple of errors in the original which didn't really seem to do any harm because of the way this is set up, but which should be cleaned up anyway. So I did that.

In your Module here is the cleaned up code:

```
Public Function ExecuteApp(sCmdline As String) As String
    Dim proc As PROCESS_INFORMATION, ret As Long
    Dim start As STARTUPINFO
    Dim sa As SECURITY_ATTRIBUTES
    Dim hReadPipe As Long 'The handle used to read from the pipe.
    Dim hWritePipe As Long 'The pipe where StdOutput and StdErr will be redirected to.
   Dim lngBytesRead As Long, sBuffer As String * 256

    sa.nLength = Len(sa)
    sa.bInheritHandle = True
      
    ret = CreatePipe(hReadPipe, hWritePipe, sa, 0)
    If ret = 0 Then
        MsgBox "CreatePipe failed. Error: " & Err.LastDllError
        Exit Function
    End If

    start.cb = Len(start)
    start.dwFlags = STARTF_USESTDHANDLES Or STARTF_USESHOWWINDOW
    ' Redirect the standard output and standard error to the same pipe
    start.hStdOutput = hWritePipe
    start.hStdError = hWritePipe
    start.wShowWindow = SW_HIDE
       
    ' Start the shelled application:
   
    ret = CreateProcessA(0&, sCmdline, sa, sa, True, NORMAL_PRIORITY_CLASS, 0&, 0&, start, proc)
    If ret = 0 Then
        MsgBox "CreateProcess failed. Error: " & Err.LastDllError
        Exit Function
    End If
   
    
    Do
            'Read the file
        ret = ReadFile(hReadPipe, sBuffer, 256, lngBytesRead, 0&)
            'Change the property to include the new text.
           
        Form1.txtOut.Text = Form1.txtOut.Text & Left$(sBuffer, lngBytesRead)
        strRet = strRet & Left$(sBuffer, lngBytesRead)
        DoEvents
    
   Loop While ret <> 0 ' if ret = 0 then there is no more characters to read
    
    
    CloseHandle proc.hProcess
    CloseHandle proc.hThread
    CloseHandle hReadPipe

    ExecuteApp = strRet
End Function
```
In your form:

```
Private Sub Command1_Click()
Dim Doit As String
txtOut.Text = ""
strRet = ""

If txtPrompt.Text = "" Then
        Exit Sub
    Else
       Doit = txtPrompt.Text
       txtOut.Text = ExecuteApp(Doit) & vbCrLf & "Done!"
    End If
End Sub
```


----------



## Mosaic1 (Aug 17, 2001)

PS When I added the cleaned up code I only posted the changes. So change the Function but be sure to leave the declares as they were.


----------



## mmxx (Nov 17, 2004)

Mosaic1 said:


> Do you get any of this type of error message when you rin other programs on your system?
> 
> After you get the error message, does VB shut down? Are you running this in the IDE or have you compiled?


Dear Mosaic,
no I don't get any of this type error when I run other programs on my system, and I get the error both running it from the IDE and when is it compiled.....


----------



## mmxx (Nov 17, 2004)

Mosaic1 said:


> When the original code was written by others, the results disappeared from the screen as soon as the job was done. In the case of Ping, there waws just a falsh and then the textbox was empty. I found that annoying. What's the point if you cannot read the results? So I added something to keep them on screen. The question is whether you are seeing the full result and then the word Done? No?
> 
> EDIT:
> 
> ...


YES gauss tries to shut down , but it then when the rror appears. I can see my results , but the word "done" doesn't appear.....


----------



## mmxx (Nov 17, 2004)

Mosaic1 said:


> PS When I added the cleaned up code I only posted the changes. So change the Function but be sure to leave the declares as they were.


OK thanks! I will try it and let you now what happens, although I am quite pessimistic ...if the error comes from Gauss the best I can do is to report to Aptech system this bug....

Thank a lot for your help!
best regards
max


----------



## Mosaic1 (Aug 17, 2001)

Right.But it's always a good idea to clean up the VB code to rule any other things out. I'll be interested to hear. I am also wondering if there is a switch you can use with Gauss to force it to close. If there is no Done on screen then it is still running and no data is being generated.


----------



## Mosaic1 (Aug 17, 2001)

Either way, if you cannot get this to work, I can help you write code to put a command window inside your form. That's a bit ugly. But see how that goes.

But when you run it manually and do not use VB it works with no error?


----------



## mmxx (Nov 17, 2004)

Mosaic1 said:


> Either way, if you cannot get this to work, I can help you write code to put a command window inside your form. That's a bit ugly. But see how that goes.
> 
> But when you run it manually and do not use VB it works with no error?


Dear mosaic 
The error also appears when I firs run a line command form the dos prompt like

tgsrun c:\myprograms\progfile.gcg

where gsrun invokes the gauss terminal interface , executes acompiled file progfile.gcg in the myprograms directory.

This is the reason becasue I do not guess that it is a VB problem, but a Gauss problem or a proble with the compiled *.gcg file.

If you are interested in what I did up to this moment you can download the compiled GUI as well as the Gauss run time from the links on my web :

http://personales.ya.com/max_mar ---> the go to academic interest

You'll find two download links one is for RETINA (my app) and the other is for the Gauss run-time. I will send you the source code by mail.

It is a beginner code and it actually contains sub-routines that I plan to use in the further in order to enhance the GUI.

I really appreciate your disponibility to write down the code to embed the shell itself in the form, but I think we would have the same actual problem

Best
max


----------



## Mosaic1 (Aug 17, 2001)

Hi max,

That's the answer I was unsure about. The Gauss is not running properly. Hope you do get it sorted. 


Mo


----------

