Solved

Write In Console

Posted on 2002-03-11
9
497 Views
Last Modified: 2007-12-19
Dear Experts

I developed a application which will run continuously. Each and every status of the application will be logged in a log file. I developed my application in such a way that it hides in the task bar when it is minimized. What i want to do is to write each and every status of the application in the dos console. So that the status can be viewed in the console. I am able to write the status in the dos console with the following APIs

Private Declare Function AllocConsole Lib "kernel32" () As Long
Private Declare Function FreeConsole Lib "kernel32" () As Long
Private Declare Function GetStdHandle Lib "kernel32" (ByVal nStdHandle As Long) As Long
Private Declare Function WriteConsole Lib "kernel32" Alias "WriteConsoleA" (ByVal hConsoleOutput As Long, ByVal lpBuffer As Any, ByVal nNumberOfCharsToWrite As Long, lpNumberOfCharsWritten As Long, lpReserved As Any) As Long


The problem here is when the user closes the console using the window close button it is throwing some memory error and terminates the actual application itself. Its not throwing any error if we use FreeConsole API to close the console. How to avoid this error. Can we avoid the user closing the console. How to avoid the memory error.

Cheers!
0
Comment
Question by:naga1979
9 Comments
 
LVL 39

Expert Comment

by:abel
Comment Utility
I don't know why you get the memory error, I need to see more of the code to understand why that happens. But instead of trying to solve the memory error, it's probably much easier to make it impossible to close the window.

You can do that by using SetClassLong, but the window must be in your process space, and I don't know if that is the case. If it is, use it before you create the window, because if you change classes, windows created with that class do not change, only newly created windows do. (note that "class" here has nothing to do with the notion of class most vb-users have).
If you can use it, you should call it with
GCL_STYLE constant and modify the CS_NOCLOSE bit. In pseudocode:

lStyle = GetClassLong(hwnd, GCL_STYLE)
lStyle = lStyle And (Not GCL_STYLE)
SetClassLong(hwnd, GCL_STYLE, lStyle)
CreateWindow(withNewClass)

Hope this helps a bit,
Abel
0
 
LVL 1

Author Comment

by:naga1979
Comment Utility
Dera abel

Here is my code

Private Declare Function AllocConsole Lib "kernel32" () As Long
Private Declare Function FreeConsole Lib "kernel32" () As Long
Private Declare Function GetStdHandle Lib "kernel32" (ByVal nStdHandle As Long) As Long
Private Const STD_INPUT_HANDLE = -10&
Private Const STD_OUTPUT_HANDLE = -11&
Private Const STD_ERROR_HANDLE = -12&

Private Declare Function WriteConsole Lib "kernel32" Alias "WriteConsoleA" (ByVal hConsoleOutput As Long, ByVal lpBuffer As Any, ByVal nNumberOfCharsToWrite As Long, lpNumberOfCharsWritten As Long, lpReserved As Any) As Long

Private Declare Function SetConsoleTitle Lib "kernel32" Alias "SetConsoleTitleA" (ByVal lpConsoleTitle As String) As Long

Private Declare Function SetConsoleActiveScreenBuffer Lib "kernel32" (ByVal hConsoleOutput As Long) As Long



Private Sub Command1_Click()
FreeConsole
End Sub

Private Sub Form_Load()
Dim z As String

AllocConsole
x = GetStdHandle(STD_OUTPUT_HANDLE)
z = "Hai how are you " + vbCrLf
SetConsoleTitle ("Hello World")

For i = 0 To 50
    WriteConsole x, z, Len(z), vbNull, vbNull
Next
End Sub

In this i get error if try to close the console directly.

Cheers!
0
 
LVL 39

Expert Comment

by:abel
Comment Utility
Hi naga,

The method I suggested is not usable in this situation. I'm sorry for that, but that's how it is. My search for any other solution appeared to be fruitless and I was about to give up (enough melodrama?) when I finally saw the light!

Here's what the problem is: a console window is not subclassable, end of story. You cannot change its properties, you cannot change its class or the properties of its class. What you CAN do is sending messages to it, like a WM_CLOSE, but that's not going to help us here. In addition, Microsoft does not issue any message when it creates or destroys the window. It does not have a parent window and it does not belong to your application. It runs in its own thread and that's yet another reason why you can do so little with it. As it turns out, consoles have a special place when it comes to windows, and that's all probably due to the fact that it can run full-screen and act as if it were not a window at all. After all, you can run console applications in it, and they differ significantly in design from GUI applications.

But that's not all. In times of Windows 95/98 there were still some possibilities to peek and poke around the memory areas of other processes, including console processes. In Win2k this is not possible at all, unless you design your own device driver to do the tricks.

Just this morning I thought of two possible solutions for you. One is about creating an out of process COM object (EXE) using VB and encapsulate the whole console-story in that object. If the console quits, the object quits, but nobody really cares, right? And that only raises trappable errors, and that's what we were after.

Second is a solution probably way beyond the current scope, but depending on your needs easy to implement: simulate your own console. If it's only about informing the user, you can create a window similar to a console window and use the OEM font to make it look original.

But these two options all faint when I start talking about the one and only solution that is really to your needs (am I overacting?). The cure to all your illnesses: SetConsoleCtrlhandler!

That function does exactly what we need: it creates a handler (some kind of callback function in this case, that you have to provide yourself) that processes close, ctrl-c and ctrl-break messages. And you probably knew that you could also close it not only with the close-button but with ctrl-c/break, and those we are going to catch as well.

This story ends up way beyond moderate level of programming, and not knowing what your level is, I just provide you with the sample code I used while investigating:


'--MODULE
Option Explicit
Public Const CTRL_BREAK_EVENT = 1
Public Const CTRL_C_EVENT = 0
Public Const CTRL_CLOSE_EVENT = 2
Public Const CTRL_LOGOFF_EVENT = 5
Public Const CTRL_SHUTDOWN_EVENT = 6

Public Function HandlerRoutine(ByVal dwCtrlType As Long) As Long        'ByVal is VERY IMPORTANT!!!
    Select Case dwCtrlType
        Case CTRL_C_EVENT, CTRL_BREAK_EVENT
            HandlerRoutine = 1
           
        Case CTRL_CLOSE_EVENT
            HandlerRoutine = 1
            Form1.tmrShutDownDlg.Enabled = True
           
        Case CTRL_LOGOFF_EVENT, CTRL_SHUTDOWN_EVENT
            HandlerRoutine = 0
           
        Case Else
            MsgBox "A control event unknown and undocumented: " & dwCtrlType
            HandlerRoutine = 0
           
    End Select

End Function



'--FORM or whereever
Private Sub Command4_Click()
    Dim z As String, x As Long, i As Long
    Dim retval As Long
   
    AllocConsole
    x = GetStdHandle(STD_OUTPUT_HANDLE)
    z = "Hai how are you " + vbCrLf
    SetConsoleTitle ("Hello World")
   
   
    For i = 0 To 50
       WriteConsole x, z, Len(z), vbNull, vbNull
    Next
   
    retval = SetConsoleCtrlHandler(AddressOf HandlerRoutine, True)        'Return val nonzero: succes!
    If retval = 0 Then
        MsgBox "Problem"
    End If
End Sub

Private Sub Command3_Click()
    FreeConsole
End Sub

Private Sub tmrShutDownDlg_Timer()
    Static count As Long
    Dim hwnd As Long, str As String
       
    str = "You tried to close it!" & vbCrLf
    count = count + 1
    If count > 100 Then         '5 seconds passed!
        tmrShutDownDlg.Enabled = False
        count = 0
    End If
   
    hwnd = FindWindow("#32770", "End Program - Hello World")
    If hwnd <> 0 Then
        SendMessage hwnd, WM_CLOSE, 0, 0
        WriteConsole GetStdHandle(STD_OUTPUT_HANDLE), str, Len(str), 0, 0
        If FindWindow("#32770", "End Program - Hello World") = 0 Then
            tmrShutDownDlg.Enabled = False
        End If
        count = 0           'There may have been more then one click on the close button, start again!
    End If
       
End Sub



I'll explain the code in the next post.
0
 
LVL 39

Accepted Solution

by:
abel earned 100 total points
Comment Utility
In chapters:

1. Set up your form
Create a new project and add two buttons to it. One with a caption "Allocate console" and one with the caption "Free console". Accidentally I named them Command4 and Command3 respectively.
Add a module to your project for the HandlerRoutine. This one MUST be in a module!
Add a timer to your form and call it tmrShutDownDlg. Copy and paste the code.
If you compile it, there will be some missing declarations, just look them up in the API viewer. For once, these definitions appeared to be correct.

1. SetConsoleCtrlHandler and HandlerRoutine
The heart of the solution. SetConsoleCtrlHandler does nothing more than setting a handler for the console that is attached to the current process.
HandlerRoutine must follow the specific form as I have given it, or crashes will be your share. Ie, I forgot ByVal, which appeared to be very sinfull of me! I should have known that forgetting byval could crash the app and it did. It took me some time to figure out why.

The function itself is pretty straightforward. A non-zero return value means that you have processed a control event, a zero return value means that you want the default handler to take care of it. As you see, I choose to let the system take care of it when it is logging of or shutting down. In all other cases, I'm in charge myself, and I do nothing.

2. A little twirk of the OS
Well, I do almost nothing. But Microsoft decided that when a process creates a console window, then the process itself IS a console process. Meaning, the OS sees everything that happens in the console window as your whole application. And if you close the console window, the application is closed (the OS thinks CUI, not GUI!) and with it, the rest of your windows get destroyed. Now you understand why your application closed without error or message. Just like that. (unless in the IDE of course!).

3. And the OS still doesn't give up.
Remove the line where I call the timer and you'll understand that the OS doesn't like an unclosable CUI application. It shows the (in)famous "End Program" dialog box. Now what! Your users think to close the console and they get a message of the system that the application is not responding anymore! Confusing? You bet!

4. But we fight back!
Because it can take some time for the "End Program" dialog to appear, I considered using a timer to find it and close it. There are neater solutions probably, like hooking system-wide into the WM_CREATE messages and destroying it before it gets created, but that's too much now. I leave that up to you. Again, this functionality is pretty straightforward. I've set the timer's interval to 50 ms and everytime its event is fired, I try to locate the window. Once I find it, I destroy it with WM_CLOSE. Note, DestroyWindow will not work here, because the "End Program" dialog belongs to another process: CRSS in this case.

5. Only one small step to go
But even now the OS has something nasty at hand. If you click more then once on the close-button (which people do when it does not react), the "End Program" dialog box will appear more then once as well. And I just stopped the timer-loop with the count-to-100 variable!
But as you clearly see, this one is easily solved: just look again for the window and go through it until it's finished.
Note that I made a little mistake in the code here. It does not necessarily close all the windows. But I'll leave that small cleanup to you.


Well, that's it! Note that if you search for this SetConsoleCtrlHandler and hope for some VB sample code, you won't find it! At least I couldn't. But this sample is working.

While you are using the console in the debugging environment, do not be tempted to hit Ctrl-Break, because VB will get confused with the handler since it is also the method of breaking into running code. But in the executable it will respond like you want to every effort of closing the window.

A final thought: I haven't tried what would happen if other applications send WM_CLOSE to the console window. But you can't trap then anyway, so it will probably just kill your app then.
0
Maximize Your Threat Intelligence Reporting

Reporting is one of the most important and least talked about aspects of a world-class threat intelligence program. Here’s how to do it right.

 
LVL 39

Expert Comment

by:abel
Comment Utility
Btw, if you use your code on a system other then Win2k: the handler still works, but the reaction of the OS is probably a little different. Like the classname, I guess it won't be "#32770" on all systems. And the searchtext should be adjusted to the name of the caption of the console window you use in the final incarnation of your application.
0
 
LVL 39

Expert Comment

by:abel
Comment Utility
Ping ;-)
Just curious: did it help?
0
 
LVL 1

Author Comment

by:naga1979
Comment Utility
Dear Abel

Give me some time, as i am busy with my office work.

Cheers!
0
 

Expert Comment

by:xyan00
Comment Utility


Hi naga1979,

acctually this is not my comment about your question posted here,I just want to tell you there are maybe some
points for you from my question 20400164 posted at somewhere shown as below:

http://www.experts-exchange.com/Programming/Programming_Languages/Cplusplus/Q_20400164.html

since I found that you mentioned about Philips SpeechMania before by searching.


anyway it will be very appreciated if you can click and view my question mentioned above.

or could you suggest me any experts or links where I can find somebody who can help me?

thanks in advance

xyan00


supplementary description:

I want to display a parameter  which is obtained by hddl from transaction dll,it would be better if the function  

calling for the window that showing the parameter is also invoked in the transaction dll.
0
 
LVL 49

Expert Comment

by:DanRollins
Comment Utility
Hi naga1979,
It appears that you have forgotten this question. I will ask Community Support to close it unless you finalize it within 7 days. I will ask a Community Support Moderator to:

    Accept abel's comment(s) as an answer.

naga1979, if you think your question was not answered at all or if you need help, just post a new comment here; Community Support will help you.  DO NOT accept this comment as an answer.

EXPERTS: If you disagree with that recommendation, please post an explanatory comment.
==========
DanRollins -- EE database cleanup volunteer
0

Featured Post

How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

Join & Write a Comment

When trying to find the cause of a problem in VBA or VB6 it's often valuable to know what procedures were executed prior to the error. You can use the Call Stack for that but it is often inadequate because it may show procedures you aren't intereste…
Since upgrading to Office 2013 or higher installing the Smart Indenter addin will fail. This article will explain how to install it so it will work regardless of the Office version installed.
As developers, we are not limited to the functions provided by the VBA language. In addition, we can call the functions that are part of the Windows operating system. These functions are part of the Windows API (Application Programming Interface). U…
Get people started with the process of using Access VBA to control Outlook using automation, Microsoft Access can control other applications. An example is the ability to programmatically talk to Microsoft Outlook. Using automation, an Access applic…

763 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

11 Experts available now in Live!

Get 1:1 Help Now