Hello everyone. Short introduction: I've been programming for a long time. However, most of those years have been spent in Linux with C (and some ASM). Now I'm trying to learn some Win32 programming.

Here is my first attempt at a program:


%define STD_INPUT_HANDLE -10
%define STD_OUTPUT_HANDLE -11

segment .data
hello db "Hello World!"

segment .bss
out_handle resd 1
in_handle resd 1


segment .text
global _WinMainCRTStartup

extern _AllocConsole@0
extern _GetStdHandle@4
extern _WriteConsoleA@20
extern _ExitProcess@4

_WinMainCRTStartup:
push ebp
mov ebp, esp

call _AllocConsole@0

push dword STD_OUTPUT_HANDLE
call _GetStdHandle@4

mov , eax

push dword STD_INPUT_HANDLE
call _GetStdHandle@4

mov , eax

push dword
push hello
push 13
push 0
push 0
call _WriteConsoleA@20

; so that the window does not close right away!
B:
jmp B

push 0
call _ExitProcess@4

mov esp, ebp
pop ebp
ret


To assemble:
nasm -f win32 hello.asm

To link:
link.exe /subsystem:windows hello.obj kernel32.lib

The problem is that nothing is printed to the new console. A new console DOES open up but nothing is printed. Any ideas why? I've been searching for quite a bit of time but I can't seem to get it.

Thanks.
Posted on 2006-09-23 02:00:35 by lithium
Hello Lithium,

Welcome on board. As you are building a console application, you need to use the swicth /SUBSYSTEM:CONSOLE

link.exe /subsystem:console hello.obj kernel32.lib
Posted on 2006-09-23 02:50:22 by Vortex
Vortex,

Thank you for your reply. However, I am NOT trying to build a console application. I am trying to build a Win32 application that just so happens to open a console window and perform I/O on it. Later it will show message boxes, etc. This would just be the first part of the program.

If I do change the switch to /subsystem:console (and replace _WinMainCRTStartup with _mainCRTStartup) then, when the executable is run, absolutely nothing happens. No console window is opened, nothing. Strange.

Regardless, I want a Win32 application and not a console application.
Posted on 2006-09-23 03:16:46 by lithium
Lithium,

I found why it doesn't work. WriteConsole is a STDCALL function and it expects the parameters to be passed in reverse order. If you push the parameters in "normal" order, the function will fail :

.code

start:

    call    AllocConsole
    push STD_OUTPUT_HANDLE
    call GetStdHandle
    mov    out_handle,eax
    push    STD_INPUT_HANDLE
    call    GetStdHandle
    mov    in_handle,eax
    push    0
    push    OFFSET pNumberOfCharsWritten
    push    12                  ; no need to include the NULL string terminator
    push    OFFSET hello
    push    out_handle
    call    WriteConsole

; so that the window does not close right away!

    call    wait_key
    push 0
    call    ExitProcess


Instead of using an infinite loop preventing the console window from disappearing, you can select a method more flexible like waiting for keyboard input before exiting.
Attachments:
Posted on 2006-09-23 05:32:32 by Vortex

I am NOT trying to build a console application. I am trying to build a Win32 application that just so happens to open a console window and perform I/O on it.


In Windows, console applications generally are Win32 applications that simply utilize a console window instead of a graphical one.

Since you are using NASM, you may want to check out the NASM32 Project. The package comes many include files and examples of both GUI and Console programs written in NASM.

The big thing that will help you specifically is the "INVOKE" macro that was developed, which automatically resolves API calls so you don't have to worry about which are STDCALL and which are CCALL :)
Posted on 2006-09-23 12:59:34 by SpooK
Vortex,

Thanks a lot. That worked out nicely. A question about the code: what exactly is wait_key? Which library is it located in?

SpooK,

NASM32 looks great. It's a wonder I haven't heard of it before.

Thanks for the help.
Posted on 2006-09-23 16:35:43 by lithium
No problem. NASM32 is fairly new and not that popular.

"wait_key" is something found in the MASM32 library, which would require using MASM. He probably didn't notice that you mentioned NASM.

Using _getch, provided by the already existing and massively supported MSVCRT library, is probably the equivalent you would wish to use in NASM32.
Posted on 2006-09-23 16:52:06 by SpooK
How would I link to msvcrt.lib?

If I call __getch from my code and link with:

link.exe /subsystem:windows hello.obj kernel32.lib msvcrt.lib

I get an error message that says MSVCR80.dll was not found. Of course, if I move the dll to the same directory as the EXE file I get an error message stating that I am attempting to load the C runtime library incorrectly.

Is there a way to do this without linking into the C runtime library? If I do link into msvcrt that then I may as well use printf, scanf, etc... and the whole point here is for me to learn the Win32 API as much as possible.
Posted on 2006-09-23 17:19:19 by lithium
You might not have MSVCRT installed (properly), which is rare these days on Windows. Which version of Windows (or emulator) are you using?

As for __getch, just about every example I see utilizes this MSVCRT function... even the "wait_key" one that Vortex mentioned. It doesn't surprise me as the rest of the Win32 API is dedicated to more Graphical I/O as the Console I/O (CONIO) functions, which are in MSVCRT under Win32, just work(TM).

I'll look into it a bit and see if I can find an alternative :)
Posted on 2006-09-23 18:03:08 by SpooK
OK, good news. I searched through some of my Win32 "console interface source" and was reminded of certain techniques.

Summary
-GetStdHandle to get the Handle of the Console Window (STD_OUTPUT_HANDLE)
-WriteFile to write the "Press any key to continue..." message
-GetStdHandle to get the Handle of the Console Window (STD_INPUT_HANDLE)
-GetConsoleMode to get the current mode of the console
-Mask unwanted characteristics of the current console mode (like disabling the wait for carriage return/pressing enter), done in reference to MSDN (see Get/SetConsoleMode's lpMode)
-SetConsoleMode to set the newly modified console mode
-ReadFile to wait for a key to be pressed
-ExitProcess with "0" to terminate the program with no error indication

Here is example code of a pure Win32 (Kernel32) approach in NASM32... please search MSDN (like the links above) for any clarification of the API calls in question...


;nasmw -f win32 demo.asm -o demo.obj
;polink.exe /SUBSYSTEM:CONSOLE /ENTRY:main /LIBPATH:\nasm32\lib demo.obj kernel32.lib

%include "\nasm32\inc\win32\windows.inc"
%include "\nasm32\inc\win32\kernel32.inc"
%include "\nasm32\inc\nasm32.inc"

entry demo


msg DB 'Press any key to continue...'
.len


hConsole RESD 1
hBuffer RESD 1
hNum RESD 1
hMode RESD 1


proc demo
invoke GetStdHandle, STD_OUTPUT_HANDLE
invoke WriteFile, eax, DWORD msg, DWORD msg.len-msg, DWORD hNum, DWORD 0
invoke GetStdHandle, STD_INPUT_HANDLE
mov DWORD, eax
invoke GetConsoleMode, eax, DWORD hMode
mov eax, DWORD
and al, 1 ;MASK unwanted characteristics of the current console mode
invoke SetConsoleMode, DWORD, eax
invoke ReadFile, DWORD, DWORD hBuffer, DWORD 1, DWORD hNum, DWORD 0
invoke ExitProcess, DWORD 0
endproc


I will probably include this as an example in the next NASM32 release.

You (lithium) should be able to easily integrate it into your current code. Have fun with it :)
Posted on 2006-09-23 19:19:36 by SpooK
Thanks a lot for that. You've been really helpful.

Your code works well but I'm having a slight problem with it. I modified the program to ask for the person's name and give the person a greeting.

The maximum length of a person's name is X characters and there are no buffer overflows in the program. However, if a person enters X+N characters and I call ReadFile to get a keypress for the "Press any key to continue" the remaining N characters remain on the input buffer and the ReadFile immediately treats them as a keypress and closes the window. So, in other words, the extra characters in the name remain on the input buffer. I tried calling FlushConsoleInputBuffer but that does nothing. Any ideas?

EDIT:
Also, for the MSVCRT error. I am using Windows XP Pro and I have Visual Studio 2005 installed.
Posted on 2006-09-23 19:53:41 by lithium
lithium,

Regarding the MSVCRT error, read this: Visual Studio 2005 DLL Hell.

Your msvcrt.lib file must be referring to msvcr80.dll, which is from Visual C++ 2005.

Essentially, if you dynamically link to msvcr80.dll you need to have the .NET Framework 2.0 or the Visual C++ 2005 Redistributable Package installed for your program to run.

If you have Visual Studio 2005 installed you don't need the items above.

Posted on 2006-09-23 20:13:46 by Greg
It would be easier to use LIB.EXE (from the visual studio) to create msvcrt.lib from msvcrt.dll (There is "_getch" inside it). It's nearly impossible to find a Windows without msvcrt.dll.
Posted on 2006-09-23 20:54:21 by ti_mo_n
Greg,

I do have Visual Studio 2005 installed. Nevertheless, I installed the redistributable package. The same errors occur.


This application has failed to start because MSVCR80.dll was not found. Re-installing the application may fix this problem


ti_mo_n,

How would I do this and why is this necessary?
Posted on 2006-09-23 22:40:59 by lithium

Thanks a lot for that. You've been really helpful.

Your code works well but I'm having a slight problem with it. I modified the program to ask for the person's name and give the person a greeting.

The maximum length of a person's name is X characters and there are no buffer overflows in the program. However, if a person enters X+N characters and I call ReadFile to get a keypress for the "Press any key to continue" the remaining N characters remain on the input buffer and the ReadFile immediately treats them as a keypress and closes the window. So, in other words, the extra characters in the name remain on the input buffer. I tried calling FlushConsoleInputBuffer but that does nothing. Any ideas?

EDIT:
Also, for the MSVCRT error. I am using Windows XP Pro and I have Visual Studio 2005 installed.


Sounds like an issue with buffer synchronization.

Throw in a "invoke WaitForSingleObject, DWORD, DWORD 0xFFFFFFFF", to tell your program to wait indefinitely (0xFFFFFFFF) for the "FlushConsoleInputBuffer" operation to be complete and the buffer clear.

To tell you the truth, and along the same lines, you might not even need to use "FlushConsoleInputBuffer". Instead, you should be able to simply use "WaitForSingleObject" before the last ReadFile operation (the one that triggers the exit) to achieve the result of the buffer being "clear" (i.e. no delayed input).

Let me know which one of these methods works for you ;)

As for the MSVCRT issue, the link that ti_mo_n posted suggests that you may need the .NET framework (2.0) installed. I have both that and VS2K5 installed and I have no issues with using MSVCRT with the NASM32 package.
Posted on 2006-09-23 22:57:47 by SpooK
Genius! Pure Genius!

It worked. I'm just not sure why it worked. Like you said, I didn't even need to call FlushInputBuffer. Is the problem that Windows was in the process of flushing the input buffer but the last ReadFile obtained access to the handle before the flushing operation was complete? Or is this completely wrong?

Thanks again.
Posted on 2006-09-23 23:09:18 by lithium
I honestly can't tell you why it works that way. My best guess is that as there is probably some delay or carry-over from the last key being pressed that is not being taken care of until this form of synchronization is achieved. Almost like it forgot to say "we are done taking input" until interrogated about it :P

According to MSDN on WaitForSingleObject, "The function modifies the state of some types of synchronization objects"... and shortly after that it mentions that it also applies directly to Console Input, among other things.

To illustrate this, imagine that when dealing with more input than it is willing accept (your example), the function returns while leaving other things in an unknown state. Maybe when you call "ReadFile" the next time, it picks-up from this last state because of the lack of synchronization (i.e. "I am done accepting input from last time")... and "WaitForSingleObject" forces that signal to occur.

Please don't take my absolute word on this, as it is my best guess with the given material and examples. It is a sizeable amount of code just to replace "_getch", but it is worth it when it helps you to avoid including MSVCRT just to use that one function... plus it is on an exit routine anyhow... no love-loss for efficiency there :)

Whatever the reason... welcome to the world of Windows programming ;)

BTW: Here is what the new code looks like for all those who are interested... I'll probably make it "DEMO12" in NASM32 for the next release...


%include "\nasm32\inc\win32\windows.inc"
%include "\nasm32\inc\win32\kernel32.inc"
%include "\nasm32\inc\nasm32.inc"

entry demo12


msg DB 'Press any key to continue...',13,10
.len


hConsole RESD 1
hBuffer RESD 1
hNum RESD 1
hMode RESD 1


proc demo12
invoke GetStdHandle, STD_OUTPUT_HANDLE
invoke WriteFile, eax, DWORD msg, DWORD msg.len-msg, DWORD hNum, DWORD 0
invoke GetStdHandle, STD_INPUT_HANDLE
mov DWORD, eax
invoke GetConsoleMode, eax, DWORD hMode
mov eax, DWORD
and al, 1
invoke SetConsoleMode, DWORD, eax
invoke  WaitForSingleObject, DWORD, DWORD 0xFFFFFFFF
invoke ReadFile, DWORD, DWORD hBuffer, DWORD 1, DWORD hNum, DWORD 0
invoke ExitProcess, DWORD 0
endproc


Enjoy :)
Posted on 2006-09-24 01:23:55 by SpooK