Using WriteConsoleA from a Clarion application

Today I was inspired to respond to a StackOverflow question and pulled this example together from pieces I found on the newsgroup :slight_smile:

Compile the program, run it from a command line and mash the button!

image

  PROGRAM

HANDLE    EQUATE(UNSIGNED)
DWORD     EQUATE(ULONG)
BOOL      EQUATE(BYTE)
VOID      EQUATE(LONG)
LPVOID    EQUATE(ULONG)
LPDWORD   EQUATE(ULONG)
ATTACH_PARENT_PROCESS EQUATE(-1)
STD_OUTPUT_HANDLE EQUATE(-11)

  MAP
    MODULE('API')
      AttachConsole(DWORD dwProcessId),BYTE,PASCAL,RAW
      FreeConsole(),BYTE,PROC,PASCAL,RAW
      GetStdHandle(DWORD nStdHandle),LONG,PASCAL,RAW
      WriteConsoleA( |
        HANDLE  hConsoleOutput, |
        VOID    lpBuffer, |
        DWORD   nNumberOfCharsToWrite, |
        *LPDWORD lpNumberOfCharsWritten, |
        LPVOID  lpReserved),BYTE,PASCAL,RAW,PROC
    END
WriteLine PROCEDURE(STRING pMessage)
  END

Window    WINDOW('Test WriteConsole'),AT(,,101,43),GRAY,FONT('Microsoft Sans Serif',8)
            BUTTON('Write To Stdout'),AT(17,12),USE(?ButtonTest)
          END
  CODE

  Open(Window)
  
  ACCEPT
    IF Event() = EVENT:Accepted AND Accepted() = ?ButtonTest
      WriteLine('Hi!')
    END
  END
  
WriteLine PROCEDURE(STRING pMessage)
conHandle   HANDLE
outLen      LPDWORD
bufferStr   &CSTRING
  CODE
  bufferStr &= New(CSTRING(Len(pMessage)+2))
  bufferStr = pMessage & '<10><0>'
  IF AttachConsole(ATTACH_PARENT_PROCESS)
    conHandle = GetStdHandle(STD_OUTPUT_HANDLE)
    WriteConsoleA(conHandle,Address(bufferStr),LEN(bufferStr),outLen,0)
    FreeConsole()
  END
  Dispose(bufferStr)
  RETURN
3 Likes

Hi Brahn,

Interesting, note you don’t need the string reference with it’s new and dispose you can just use LEN to create a cstring of the required length, as in …

WriteLine           PROCEDURE(STRING pMessage)
conHandle               HANDLE
outLen                  LPDWORD
bufferStr               CSTRING(LEN(pMessage)+2)
	CODE
		  bufferStr = pMessage & '<10><0>'
		  IF AttachConsole(ATTACH_PARENT_PROCESS)
		         	conHandle = GetStdHandle(STD_OUTPUT_HANDLE)
			        WriteConsoleA(conHandle,Address(bufferStr),LEN(bufferStr),outLen,0)
			        FreeConsole()
		  END
		  RETURN
1 Like

Oh neat, I forgot about that!
I’m not sure how necessary the FreeConsole() is either but I was trying it out.

FreeConsole() is needed, i tried to remove it but then there was no output.

n.b. Used it in a small app writing a few lines to the console and exiting directly after. it does not return to show the command prompt again, but it’s ready and you can type a new command.

In case anyone is interested: this example will compile just fine as-is under Clarion 11. Clarion 9.1 must have a bit of a bug.

Under Clarion 9.1:

  • If you are compiling in DLL mode, you have to use LibMaker.exe to create a .lib file that contains just the AttachConsole entry point from C:\WINDOWS\System32\kernel32.dll
  • If you’re compiling in LIB mode, you do not need to make this custom .lib file, or do anything special to include the AttachConsole entry point lib

Thanks to Rick Martin for helping me solve that one.

There has been some maneuvering of stuff in the shipping WIN32.LIB and other libs. AttachConsole is in there now, but was not in 9.1.

Used this today. Works great.

1 Like

Brahn did create a ConsoleSupport Class and an example on GitHub. IIRC on my Laptop I have an enhanced version I’ll try to find.

This is the Console INC file Class to give an idea of methods in the CLW file.

ConsoleSupport  Class(),Type,Module('ConsoleSupport.Clw'),LINK('ConsoleSupport.Clw',1)
! Properties
inputHandle   HANDLE
outputHandle  HANDLE
textBuffer    CSTRING(2048)
outBuffer     CSTRING(2048)
inBuffer      CSTRING(2048)
bytesWritten  LONG
bytesRead     LONG
prefix        CSTRING(21)
! Methods
Construct     PROCEDURE
Destruct      PROCEDURE(),VIRTUAL
Init          PROCEDURE(),BYTE,PROC,VIRTUAL
WriteLine     PROCEDURE(STRING pText),BYTE,PROC,VIRTUAL
ReadKey       PROCEDURE(),STRING,PROC,VIRTUAL
GetLastSystemError  PROCEDURE ( LONG pLastErr=0 ),STRING
              END

One important setting is the Project EXP file must have the First line end with CUI not GUI. You’ll see this in his Console Test project EXP file.

NAME 'CONSOLETEST' CUI

It’s probably appropriate for someone to fork and maintain that repo given that fushnisoft was @brahnp / @brahn and he has passed away.

GitHub - enercalc/ClarionClasses: Some handy classes that I use. I guess I did this sometime last year:)