Progrmr
Progrmr

Reputation: 1621

x86 assembly - how to use Windows API _WriteConsole@4 - masm32 syntax

In result to my post Can I use int21h on windows xp to print things?, I have seen an article about using Windows API's and in this article was a reference to using the _WriteConsole@4 API to print a message to the console. The article is at http://cs.lmu.edu/~ray/notes/x86assembly/.

Here is my code so far:

.386P
.model  flat
extern  _ExitProcess@4:near
extern  _GetStdHandle@4:near
extern  _WriteConsoleA@20:near
public  _go

.data
      msg     byte    'if you get this, it worked.', 10
      handle  dword   ?
      written dword   ?
.code 
start:
_go:    
      push    -11
      call    _GetStdHandle@4
      mov     handle, eax
      push    0
      push    offset written
      push    13
      push    offset msg
      push    handle
      call    _WriteConsoleA@20
      push    0
      call    _ExitProcess@4
end start

I am using this syntax to compile the code: ML:

ml (the file is called test.asm) test.asm /c

Link:

link test.obj C:\masm32\lib\kernel32.lib /SUBSYSTEM:CONSOLE /entry:go

I have gotten it to compile and link, but when I run the .exe that is produced, it does absolutely nothing, not even an error return. The console is just black. Why is this?

Any help would be greatly appreciated. And to the users of this forum, I apologize for bombarding stackoverflow.com every day, it is just that I have very few resources to learn with.

Thanks in advance,

Progrmr

Upvotes: 4

Views: 6407

Answers (3)

Gunner
Gunner

Reputation: 5884

First off, the link you are getting your sample code is from a NASM user and he probably never used MASM from the way he speaks of it. He also writes his MASM sample like NASM format. The fact that you want to use Assembly means you have to be an advanced computer user. You need to know how to use batch files, how to set system paths and other things. When something goes wrong, you learn by researching. So you got an error saying it can't find masm32rt.inc but you say you are using MASM32. I use batch files and an IDE for Assembly and I have my system paths pointing to various directories in MASM32.

Add the absolute path to your masm32\include directory before masm32rt.inc. While you are at it, open masm32rt.inc in a text editor and see what is in it - error fixed.

You start your source file with:
.586
.option casemap:NONE
.model flat, stdcall

include yourincludeshere and it could be a bunch
includelib yourlibshere same here a bunch

masm32rt.inc contains that already and it contains include and includelib for libs and includes that are widely used. We use it to save a bunch of typing.

Now open any include file in \masm32\include the includes are just setting up the protos for the API calls so you can use invoke for parameter checking, it also aliases the API calls so we don't have to type WriteConsoleA instead we just write WriteConsole hell, you could even do YoConsole equ and in your code you would write YoConsole for WriteConsole.

That being said, we don't use - extern _WriteConsoleA@20:near as NASM would nor do we need to set our entry label to public MASM knows your entry point by this:

.code
yourentrypointname:

end yourentrypointname

we also don't have to specify the libs to the linker since we use includelib in our source.

Also, DO NOT, I repeat DO NOT get into the habit of using hard coded numbers in your parameters. Get out of that habit NOW!! Windows header files use DEFINES for a reason - code readability, We (the folks that help you) don't need to lookup what -11 means for that API call, I used the define and you know what that parameter means. Plus what if you have that WriteConsole call 40 times? If you use an equate, you need to change just the equate instead of search and replace. If you want decent MASM tutorials search for Iczelion they are old and contain a few errors but they will get you started, many of us in the early days have used those tutorials.

Upvotes: 3

Gunner
Gunner

Reputation: 5884

This works without problem:

include masm32rt.inc

.data
szMsg       db  "I am in the console!", 0
MSG_LEN     equ $ - szMsg

.data?
BytesWriten dd  ?

.code
start:
    push    STD_OUTPUT_HANDLE
    call    GetStdHandle

    push    NULL
    push    offset BytesWriten
    push    MSG_LEN
    push    offset szMsg
    push    eax
    call    WriteConsole

    push    0
    call    ExitProcess
end start

your entry label is _go yet you tell the linker is is go - /entry:go so it creates the console but does not execute any code! You don't need to tell the linker the entry point in this case, your entry point is start... How does the linker know? The end start

Upvotes: 2

Antonio Bakula
Antonio Bakula

Reputation: 20693

You could try MASM32, here is a hello world example for console application :

; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««« *

    .486
    .model flat, stdcall
    option casemap :none   ; case sensitive

; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««

    include \masm32\include\windows.inc
    include \masm32\include\masm32.inc
    include \masm32\include\kernel32.inc
    include \masm32\macros\macros.asm

    includelib \masm32\lib\masm32.lib
    includelib \masm32\lib\kernel32.lib

    .code

start:

; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««

    print "Hello world"

    exit

; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««

end start

But if you want to stick with your current assembler, we can look under macros.asm there is a print Macro :

print MACRO arg1:REQ,varname:VARARG      ;; display zero terminated string
    invoke StdOut,reparg(arg1)
  IFNB <varname>
    invoke StdOut,chr$(varname)
  ENDIF
ENDM

So you want StdOut, in MASM32 it looks like this :

StdOut proc lpszText:DWORD

    LOCAL hOutPut  :DWORD
    LOCAL bWritten :DWORD
    LOCAL sl       :DWORD

    invoke GetStdHandle,STD_OUTPUT_HANDLE
    mov hOutPut, eax

    invoke StrLen,lpszText
    mov sl, eax

    invoke WriteFile,hOutPut,lpszText,sl,ADDR bWritten,NULL

    mov eax, bWritten
    ret

StdOut endp

So at the end of this journey, you have to use WriteFile not WriteConsole :)

Upvotes: 1

Related Questions