Rick Strahl's FoxPro and Web Connection Web Log
White Papers | Products | Message Board | News |

Writing to the Windows Console from Visual FoxPro


Monday, November 7, 2011, 1:21:11 AM

I'm starting this post with a disclaimer: This post doesn't have a full solution to the problem I was aiming to solve. However I'm posting it anyway to give some insights that might prove helpful to others and a partial solution that might address some limited scenarios that only need to generate display output to the console.

Here's the issue: I have a Visual FoxPro application that is a desktop application - Html Help Builder - but that also has command line options for a variety of tasks. When the app starts with no parameters it just runs the desktop app. But if you pass in a few specific parameters it will crunch away at some options and then exit. Along the way it would be nice to provide a little information, especially if something goes wrong. Specifically the process I'm working on is for Help File build automation with command line - you tell Html Help Builder to do a Help File compile and build - this process may take a little while and it be nice to provide some basic status information. For command line options that process is typically handled through Console output.

Alas, FoxPro - or for that matter any Windows application - can't output to Console by default. Windows apps start enter a message loop and exit back to the command prompt, and then continue running. Is it possible to write to the Console anyway? Sort of…

Getting output to the Console

After a bit of research I found a solution to create Console output via some C code in a DLL. Basically you can attach a console or create one for a process and then write to a console.

The code to do this is:

#include <windows.h>
#include <stdio.h>
#include <fcntl.h>
#include <io.h>
#include <iostream>
#include <fstream>

BOOL WINAPI WriteToConsole(char *input)
{    
    BOOL bConsole = AttachConsole(ATTACH_PARENT_PROCESS) != FALSE;

    if (bConsole)
    {
        int fd = 0;
        long lStdOut;
        lStdOut = (long)GetStdHandle( STD_OUTPUT_HANDLE); // STD_ERROR_HANDLE);
        fd = _open_osfhandle(lStdOut, _O_TEXT);
        if (fd > 0)
        {
            *stdout = *_fdopen(fd, "w");
            setvbuf(stdout, NULL, _IONBF, 0 );
        }
    }

    printf(input);    
    FreeConsole();
    return TRUE;
}

I compiled this into wwIPStuff.dll (which includes a ton of other stuff used in our West Wind Client Tools) and I then created a small test program ConsoleOutput.prg in FoxPro that looks like this:

#include wconnect.h

DECLARE integer WriteToConsole IN wwipstuff.dll ;
     string

? WriteToConsole(CRLF)
? WriteToConsole("Hello from FoxPro" + CRLF)

? WriteToConsole("More output" + CRLF)
? WriteToConsole("And still some More output" +CRLF)

? "Output from FoxPro Console"
WAIT window

I then add ConsoleOutput.prg to a project and compile this into an EXE:

BUILD EXE ConsoleOutput from ConsoleOutput

Good News, Bad News

To run this and get Console output to work you have to run the EXE from the Windows Command prompt. If you run from Explorer nothing is going to happen since there's no console active when an app is started from Explorer, the Run box or through code.

So open a command window and run the code. What you'll end up with is something like this:

FoxProWindowsConsole

It's clearly working - output *is* written into the Windows console.

But if you look closely, you'll notice that something funky is going on with the command prompt. Notice that there's an empty command prompt followed by console output, and then after the program is done executing the cursor remains in 'empty space' after the console output.

The command prompt is actually active, so you can type any Console commands and it will work - there's just no prompt.

Why is this happening? Unfortunately, this is how a Windows application is supposed to work. A Windows app like Visual FoxPro (WinMain() entry point) starts, loads up its Windows message loop and then is supposed to exit the console and return to the command prompt. The application runs, but it runs independently of the command prompt. A true Console application however (Main() entry point) runs synchronously until the main function completes at which point the application exits and returns to the command prompt.

This is even more evident if you try to redirect output to a file. If you do:

consoleoutput > consoleoutput.txt

you'll find that a 0 byte file consoleoutput.txt is created, but it's empty and no matter what you write to the console, the file will remain at 0 bytes, because the console redirection expires when the exe returns to the command prompt.

The bad news is that although I've now managed to write to the Windows Console, the output it generates is not all that useful. Yes I can see the output in the console window (even though it looks rather funky because of the writing *after* the prompt), but the output can't be captured using all the common Windows mechanisms for redirecting the output.

For example in Html Help Builder and my compilation output, this is useless because what I need is a mechanism to capture the result from the compilation process so it can be integrated into a build process.

No Native FoxPro Solution

Unfortunately I think this is a problem that can't be solved using FoxPro code because of the way Windows GUI apps work. The immediate exit is not something that I think we can get around especially since the console exit occurs even before any FoxPro runs. I was thinking that maybe with some of Windows internal events you'd be able to hook into the message queue at startup, but even that won't work because by the time even the first line of Fox code runs it's too late.

Workaround: Create a front end Console Application

I did end up with a solution to the problem however, although it's a pretty roundabout way to get there:

I ended up creating a .NET Console application that acts as a front end to the FoxPro app. The .NET app automates the Html Help Builder COM object and then echos output back to the Console. Granted this is a bit more work, especially since the FoxPro app already had a command line interface, but it's a workable solution.

Here's what the .NET Console App looks like:

using System;
using Westwind.wwHelp;
using System.IO;

namespace HelpBuilderConsole
{
    class HelpBuilderConsole
    {
        static void Main(string[] args)
        {

            Console.WriteLine("West Wind Html Html Help Builder Console");

            if (args == null || args.Length == 0)
            {
                Console.WriteLine("\r\n* Available commands:");
                Console.WriteLine("RESET");
                Console.WriteLine("REINDEX \t\t[hbp Project FilePath]");
                Console.WriteLine("BUILDPROJECT \t\t[hbp Project FilePath]");
                Console.WriteLine("ASSEMBLYIMPORT \t\t[AssenblyFilePath] [NewOrExistingprojectFile]"); 

                OnExit();
                return;
            }

            string command = args[0].ToUpper();

            if (command == "BUILDPROJECT")
                BuildProject(args);
            else if (command == "ASSEMBLYIMPORT")
                AssemblyImport(args);
            else if (command == "RESET")
                Reset(args);
            else if (command == "REINDEX")
                Reindex(args);                
            
            OnExit();
            return;
        }

        static void OnExit()
        {
#if DEBUG
            Console.ReadLine();
#endif
        }

        static void  BuildProject(string[] args)
        {
            if (args.Length < 2 || args[1] == null)
            {
                WriteError(3, "Invalid or missing Project Filename");
                return;
            }

            string projFile = args[1];
            if (!IsFile(projFile))
                return;

            
            wwHelp help = wwHelp.CreateInstance();
            if (!help.Open(projFile))
            {
                WriteError(24, "Unable to open: " + projFile + ". " + help.ErrorMsg);
                return;
            }

            Console.WriteLine("Building Html Html Help Builder Project: " + projFile);

            WriteProgress("Generating HTML...");
            help.GenerateHtml();
            WriteProgress("Generating Index...");
            help.GenerateIndex();
            WriteProgress("Generating Toc...");
            help.GenerateToc(0,true,null);
            WriteProgress("Compiling Project...");
            string result = help.CompileProject();
            if (result == null || help.Error)
            {
                WriteError(24, "Error compiling project: " + help.ErrorMsg +  "\r\n\r\n" + result);
                return;
            }

            WriteSuccess("Project built.\r\n\r\n* Compiler Messages:\r\n" + result);

            help.Release();
            help = null;
        }
}
... additional methods omitted

The wwHelp object used here is a wrapper around the Html Html Help Builder COM object. Html Help Builder includes a .NET API that provides the .NET wrapper around the COM object which makes short. With this code in place it's now easy to do:

HelpBuilderConsole.exe RESET

HelpBuilderConsole.exe BUILDPROJECT "c:\HelpBuilder Projects\RazorHosting\RazorHosting.hbp" > BuildOutput.txt

It's not as clean as having the main wwHelp.exe app handle these tasks, but at least the required functionality is in place. It works and gets the job done.

You can build your Console app in any language that can produce one - .NET is probably the easiest, but you can use C++ or Delphi or Java etc. Any environment that can produce a Console app will do.

Resources

Posted in: FoxPro

Feedback for this Weblog Entry


re: Writing to the Windows Console from Visual FoxPro



Phil
I haven't tried this before, but I have you checked out using AllocConsole?

http://www.pinvoke.net/default.aspx/kernel32.allocconsole

re: Writing to the Windows Console from Visual FoxPro


@Phil - AllocConsole will create a new Console window from within a Windows GUI app. This might be useful for other things but the problem is again that you can't capture the console externally from the command line.

re: Writing to the Windows Console from Visual FoxPro


Why don't you change the type of the subsystem in the EXE header from windows to console?

re: Writing to the Windows Console from Visual FoxPro


@Christof - Uhm... how do you do that? And if you did, does the app still run as a Windows app? Basically what I would really need is an app that does both - provide Console output AND still run as a Windows application :-)

re: Writing to the Windows Console from Visual FoxPro


Hello,

i want to use the mplayer in slave-mode with my vfp-application, so i have to write in the mplayer-console. do you have any informations for me.

Klaus

re: Writing to the Windows Console from Visual FoxPro


Thanks for this. Just saved me hours of working through the same path. I'm writing a command line wrapper around FoxUnit so we can do automated unit testing in a CI environment.
 



© Rick Strahl, West Wind Technologies, 2003 - 2018