Chapter 11
Windows
Programming

Modified

Download

  1. ConsoleHelloWorld - Console example
  2. HelloWorld - Message box project in Assembler
  3. HelloWorld2 - Graphic Windows project in Assembler
  4. HelloWorldCPP - C++ project
  5. HelloWorldCPPandAssembler - C++ and Assembler project
  6. TicTacToe - C++ project
  7. Windows include files and libraries

Contents

Overview

Windows programming generally involves developing a GUI, many high-level languages are well suited for GUI development by hiding much of the programming complexity using object oriented techniques. Assembly language is not often used for Windows GUI programming but is useful in understanding the basic concepts in graphical operating system function calls.

Invoke/Local/Addr

invoke and proc statements can replace the Call, pushing and removing parameters, and accessing parameters by referring to the eBp register explicitly type checking of parameters is performed.

local definition defines a dynamic variable that is accessible only within the procedure, allocation/deallocation code is generated by the Assembler.

addr operator is equivalent to the offset operator.

Consider the C++ and corresponding prototype and call in Assembly:

int f(int x, int &y, char &z) 
{
   int i;
   i = x+y;
   return i;
}
 f  proc x : dword, y : ptr dword, z : ptr byte
    local i : dword
    mov	  eAx, x
    mov	  eBx, y		; y : ptr
    add	  eAx, [eBx]
    mov	  i, eAx
    ret
 f  endp
c = f(4, a, b);
    invoke  f, 4, offset a, offset b
    mov     c, eAx

 

"Hello World" Windows Console Programming in C++

Windows provides a simple API for console (text) applications requiring reading and writing.

The following is from Microsoft's documentation of the WriteConsole function.

BOOL WINAPI WriteConsole(
  __in        HANDLE hConsoleOutput,
  __in        const VOID *lpBuffer,
  __in        DWORD nNumberOfCharsToWrite,
  __out       LPDWORD lpNumberOfCharsWritten,
  __reserved  LPVOID lpReserved
);

Parameters

hConsoleOutput [in]
A handle to the console screen buffer. The handle must have the GENERIC_WRITE access right. For more information, see Console Buffer Security and Access Rights.
lpBuffer [in]
A pointer to a buffer that contains characters to be written to the console screen buffer. The total size must be less than 64K.
nNumberOfCharsToWrite [in]
The number of TCHARs to write. If the total size of the specified number of characters exceeds 64 KB, the function fails with ERROR_NOT_ENOUGH_MEMORY.
lpNumberOfCharsWritten [out]
A pointer to a variable that receives the number of TCHARs actually written.
lpReserved
Reserved; must be NULL.

Return Value

If the function succeeds, the return value is nonzero.

The following writes Hello World to the console and reads a string of up to 80 characters and echoes to the console.

#include <windows.h>
int main() {
	HANDLE stdOutHandle, stdInHandle;

	char *helloWorld = "Hello world\n\r";
	char buffer[82];

	unsigned long bytesWritten=0, bytesRead=0;

    	stdOutHandle = GetStdHandle(STD_OUTPUT_HANDLE);

	WriteConsole(stdOutHandle, helloWorld, 13, &bytesWritten, NULL);

    	stdInHandle = GetStdHandle(STD_INPUT_HANDLE);

	ReadConsole(stdInHandle, buffer, 80, &bytesRead, NULL);

	WriteConsole(stdOutHandle, buffer, bytesRead, &bytesWritten, NULL);

	return 0;
}

"Hello World" Windows Console Programming in Assembly

Windows provides a simple API for console (text) applications.

The following, similar to Console1.asm example of the text, prints "Hello World" to the console.

There are two Windows calls, from Table 11-2 of the text. The function prototypes are defined in SmallWin.inc file for:

  1. INVOKE GetStdHandle, STD_OUTPUT_HANDLE

    returns a handle to the standard output, the console.

     

  2. INVOKE WriteConsole,
      consoleHandle,
      ADDR message,      
      messageSize,
      ADDR bytesWritten,
      0

writes the message to the handle, standard output.

INCLUDE SmallWin.inc

.data
message BYTE "Hello World", 0dh,0ah
messageSize DWORD ($-message)

consoleHandle HANDLE 0     ; handle to standard output device
bytesWritten  DWORD ?      ; number of bytes written

.code
main PROC
	INVOKE GetStdHandle, STD_OUTPUT_HANDLE
	mov consoleHandle,eax

	INVOKE WriteConsole,
	  consoleHandle,		; console output handle
	  ADDR message,       	; string pointer
	  messageSize,		; string length, 13
	  ADDR bytesWritten,	; returns num bytes written
	  0				; not used

	INVOKE ExitProcess,0
main ENDP
END main

 

Read console and echo in Assembly                                    ConsoleHelloWorld

The following, combines reading and writing the console.

There are two Windows calls, from Table 11-2 of the text. The function prototypes are defined in SmallWin.inc file for:

  1. INVOKE GetStdHandle, STD_INPUT_HANDLE

    returns a handle to the standard input, the console.

     

  2. INVOKE ReadConsole,
            stdInHandle,
      ADDR buffer,
      78,
      ADDR bytesRead,
      0

reads the input to the handle, standard input.

INCLUDE SmallWin.inc

.data
buffer 		BYTE 		80 DUP(?),0,0
stdInHandle 	HANDLE 	?
stdOutHandle 	HANDLE 	0    
bytesRead   	DWORD 	?

.code
main PROC
	INVOKE GetStdHandle, STD_INPUT_HANDLE
	mov	 stdInHandle,eax

	INVOKE ReadConsole, 	
		 stdInHandle,		; console input handle 
		 ADDR buffer,		; string buffer pointer
	  	 78,				; maximum + 2 for CR/LF 
		 ADDR bytesRead,		; actual read
		 0
	
	INVOKE GetStdHandle, STD_OUTPUT_HANDLE
	mov    stdOutHandle,eax

	INVOKE WriteConsole,
	  	 stdOutHandle,		
	  	 ADDR buffer,       	
	  	 bytesRead,			
	  	 ADDR bytesWritten,	
	  	 0						

	INVOKE ExitProcess,0
main ENDP
END main

 

Minimal Windows Graphical Program using C++ and Assembler                        HelloWorld

The following C++ and Assembler programs display a message box on the screen which you can download and run, about the simplest Windows program possible. The message box is produced when calling the MessageBox function with the parameters for text and style to display:

 invoke MessageBox, 0, addr MsgBoxText, addr MsgCaption, MB_OK


Assembling and Executing

Though Visual Studio comes with Masm32 it does not have the necessary include files for Windows programming in Assembler. Also, the text Irvine32.inc file is not consistent with standard Window's definitions and is woefully incomplete.

To be compatible with Window's documentation, we will use include files defined specifically for Windows programming.

#include "stdafx.h"   

char MsgCaption[]="Message Box Caption";
char MsgBoxText[]="Message Box Text";

int __stdcall WinMain(HINSTANCE hInstance, 
                      HINSTANCE hPrevInstance,
                      LPSTR lpCmdLine, 
                      int nCmdShow)
{
	MessageBox(0,
		   MsgBoxText, 
		   MsgCaption, 
		   MB_OK);
	return 0;
}
.386 
.model flat, stdcall 
option casemap:none
include masm32\include\windows.inc
include masm32\include\kernel32.inc
include masm32\include\user32.inc
includelib masm32\lib\user32.lib
includelib masm32\lib\kernel32.lib
.data
MsgCaption  byte "Message Box Caption",0
MsgBoxText  byte "Message Box Text",0

.code
WinMain	 proc	near

	invoke MessageBox, 0, 
		 addr MsgBoxText, 
		 addr MsgCaption, 
		 MB_OK

	invoke ExitProcess, 0

WinMain	 Endp
	End 	 WinMain

"Hello World" Windows Graphical Programming using C++ and Assembler                HelloWorld2

The minimal Windows program to print the familiar "Hello World"  in both C++ and Assembler is more complex due to opening a standard window and handling some of the user events.

Download and run to observe program behavior.

A cursory examination of either program gives the justified impression that Windows programs consist mainly of calls to Windows operating system functions.

The programs are nearly identical in implementation but the C++ executable is over ten times larger than the Assembler. The key functions in each are:

WinMain

  1. Called when program starts.
  2. Constructs and initializes the window.
  3. Iterates a message receive and dispatch loop until the window is closed.
  4. Indirectly calls WndProc by dispatching messages to operating system which calls WinProc when appropriate.

WndProc

Handles messages passed from operating system callback.

WinMain is invoked first, similar to the C++ main function. Its primary function is to initialize the process, open a window, and execute a receive and dispatch message iteration. The messages are sent to the operating system which in turn calls the WndProc function to handle the messages.

WndProc is the heart of a Windows program where the user interface is implemented by handling events (messages) such as mouse movements, key presses, etc. WinMain defines WinProc to be the function to handle messages.

A Windows program executes a loop to get messages (mouse-clicks, window refresh, etc.) and dispatch the messages to the Windows OS.

The OS schedules a call to the program's WinProc function, which then handles any message it chooses. Any messages not handled by WinProc are passed on to the default handler.

Commonly, the messages to be handled are defined in a switch statement labeled by constants defined within the <windows.h> file.

For example, WM_PAINT is the message label for a paint message.

In our simple example only two events are handled:

  1. WM_PAINT event to display the Hello World text
  2. WM_DESTROY event to destroy or close the window process.
#include "stdafx.h"
 
 
 


char szAppName[]="Hello World Window";	// Title
char szClassName[]="Window Class";
char szHelloWorld[]="Hello World";


int WndProc(HWND hWnd,
            UINT message,
            WPARAM wParam,
            LPARAM lParam {
 PAINTSTRUCT ps;
 HDC hdc;

 switch (message) {
  case WM_PAINT:
           // Alloc paint
    hdc = BeginPaint(hWnd, &ps);
    TextOut(hdc,0,0,szHelloWorld,11);
    EndPaint(hWnd, &ps);
  // Deallocate
    break;
  case WM_DESTROY:
    PostQuitMessage(0);
   // Close
    break;
  default:
    return DefWindowProc(hWnd,
                         message,
                         wParam,
                         lParam);
 }
 return 0;
}




int __stdcall WinMain(HINSTANCE hInstance, 
		HINSTANCE hPrevInstance, 
		LPSTR lpCmdLine, int nCmdShow){
 WNDCLASSEX wcex;
 MSG msg;
 HWND hWnd;

 wcex.cbSize        = sizeof(WNDCLASSEX); 
 wcex.style         = CS_HREDRAW | CS_VREDRAW;
 wcex.lpfnWndProc   = (WNDPROC)WndProc;
 wcex.cbClsExtra    = 0;
 wcex.cbWndExtra    = 0;
 wcex.hInstance     = hInstance;
 wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
 wcex.lpszMenuName  = 0;
 wcex.lpszClassName = szClassName;
 wcex.hIcon         = 0;
 wcex.hIconSm       = 0;
 wcex.hCursor       = LoadCursor(NULL,IDC_ARROW);

 RegisterClassEx(&wcex);

 hWnd = CreateWindow(szClassName, szAppName, 
	  WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 
	  0, CW_USEDEFAULT, 0, NULL, NULL, 
        hInstance, NULL);

 
 ShowWindow(hWnd, nCmdShow);
 UpdateWindow(hWnd);
					// Main message loop
 while (GetMessage(&msg, NULL, 0, 0))
 {	TranslateMessage(&msg);
	DispatchMessage(&msg);
 }
 return msg.wParam;
}
.386 
.model flat, stdcall 
option casemap:none
include masm32\include\windows.inc
include masm32\include\user32.inc
include masm32\include\kernel32.inc
include masm32\include\gdi32.inc
includelib masm32\lib\user32.lib
includelib masm32\lib\kernel32.lib
includelib masm32\lib\gdi32.lib
.data
AppName  	byte 	"HelloWorld Window",0
ClassName 	byte 	"SimpleWinClass",0
helloWorld	byte	"Hello World"

.code
WndProc proc hWnd:HWND, uMsg:UINT,
             wParam:WPARAM, lParam:LPARAM
  LOCAL      ps:PAINTSTRUCT

  @@switch:
      cmp uMsg,WM_DESTROY
      je @@destroy
      cmp uMsg, WM_PAINT
      je @@paint
      jmp @@default
    @@paint:                          ; WM_PAINT
      invoke BeginPaint,hWnd, ADDR ps ; Alloc paint
      invoke TextOut,eAx,0,0,ADDR helloWorld, 11
      invoke EndPaint,hWnd, ADDR ps   ; Deallocate
      jmp @@endswitch
    @@destroy:                        ; WM_DESTROY
      invoke PostQuitMessage,0        ; Quit
      jmp  @@endswitch
    @@default:   
      invoke DefWindowProc,hWnd,uMsg,wParam,lParam
      ret
  @@endswitch:
      Mov eAx, 0
      ret
WndProc endp

WinMain proc hInstance:HINSTANCE,
		 hPrevInst:HINSTANCE,
		 CmdLine:LPSTR,CmdShow:DWORD
        LOCAL  wc:WNDCLASSEX
        LOCAL  msg:MSG
        LOCAL  hwnd:HWND

  mov    wc.cbSize,SIZEOF WNDCLASSEX
  mov    wc.style, CS_HREDRAW or CS_VREDRAW
  mov    wc.lpfnWndProc, OFFSET WndProc	; Handler
  mov    wc.cbClsExtra, 0
  mov    wc.cbWndExtra, 0
  push   hInstance
  pop    wc.hInstance
  mov    wc.hbrBackground, COLOR_WINDOW+1
  mov    wc.lpszMenuName, 0
  mov    wc.lpszClassName, OFFSET ClassName
  mov    wc.hIcon, 0			; Default icons
  mov    wc.hIconSm, 0
  invoke LoadCursor, 0, IDC_HAND	; Use hand cursor
  mov    wc.hCursor, eAx

  invoke RegisterClassEx, addr wc

  invoke CreateWindowEx, 0,ADDR ClassName,ADDR AppName,
         WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,
         CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,0,0,
         hInstance,0

  mov    hwnd, eAx

  invoke ShowWindow, hwnd,SW_SHOWNORMAL
  invoke UpdateWindow, hwnd
						; Main message loop
  @@while:	
      invoke GetMessage, ADDR msg,0,0,0
      cmp    eAx, 0	
      Jne    @@do
      Jmp    @@endwhile
   @@do:
      invoke TranslateMessage, ADDR msg
      invoke DispatchMessage, ADDR msg
      Jmp    @@while
   @@endwhile:
      mov    eax,msg.wParam
      ret
WinMain endp

main  proc	 near		; Windows Startup code
     	invoke GetModuleHandle, NULL

     	invoke WinMain, eAx, 0, 0, 0

     	invoke ExitProcess, 0
main  endp

	end main

Handling more Windows events - Key Presses

The WndProc is invoked by the Windows operating system in response to events related to the window in which the event occurred. For example, pressing a key in a window sends a key pressed message to the WndProc for that window (remember that you may have several windows open at one time).

The following example adds handling of key press events and display of the characters to the window.

Only the parts of the program changed from above are included, you'll notice that the WinMain does not change and the WndProc merely adds the code necessary buffering the key pressed in response to WM_CHAR message and displaying the characters buffered in response to the WM_PAINT message.

Download and run to observe the program behavior when keys are pressed.

char buffer[1000];
int  count=0;

int WndProc(HWND hWnd, UINT message, 
            WPARAM wParam, LPARAM lParam)
{
 PAINTSTRUCT ps;
 HDC hdc;

 switch (message) {
   case WM_DESTROY:
	PostQuitMessage(0);		// Close
	break;
   case WM_CHAR:
	buffer[count] = (char) wParam;
	InvalidateRect(hWnd, 0, TRUE);
	count++;
	break;
   case WM_PAINT:
	hdc = BeginPaint(hWnd, &ps);	// Allocate paint 
	TextOut(hdc,0,0,buffer,count);	
	EndPaint(hWnd, &ps);		// Deallocate
	break;
   default:
	return DefWindowProc(hWnd, 
				    message, 
				    wParam, 
				    lParam);
 }
 return 0;
}
.data
  buffer	byte	1000 dup(?)
  count	dd	0

.code
WndProc proc hWnd:HWND, uMsg:UINT, 
	       wParam:WPARAM, lParam:LPARAM
  LOCAL      ps:PAINTSTRUCT

  @@switch:
	cmp 	 uMsg,WM_DESTROY
	je	 @@destroy
	cmp	 uMsg, WM_CHAR
	je	 @@char
	cmp	 uMsg, WM_PAINT
	je	 @@paint
	jmp	 @@default	
    @@destroy:			  	   ; WM_DESTROY event
	invoke PostQuitMessage,0        ; Close
	jmp	 @@endswitch
    @@char:	
	push	 wParam
	pop	 eAx
	mov	 eBx, count
	mov	 buffer[eBx], Al           ; Save key press
	invoke InvalidateRect, hWnd, 0,TRUE
	inc	 count
	jmp	 @@endswitch
    @@paint:			  	    ; WM_PAINT event
	invoke BeginPaint,hWnd, ADDR ps  ; Alloc paint
	invoke TextOut,eAx,0,0,ADDR buffer, count	
	invoke EndPaint,hWnd, ADDR ps	    ; Deallocate
	jmp	@@endswitch
    @@default:				   
	invoke DefWindowProc,hWnd,uMsg,wParam,lParam
	ret
  @@endswitch:
	Mov	eAx, 0
	ret
WndProc endp

Handling even more Windows events - Mouse Moves - Sketch

WndProc is invoked by the Windows operating system in response to events related to the window in which the event occurred.

For example, moving the mouse in a window sends a mouse message to the WndProc for that window.

The following example adds handling of mouse click event to implement a sketch program.

Only the parts of the program changed from above are included, you'll notice that again the WinMain does not change.

WndProc merely adds the code to store the <x,y> points locating the mouse movement across the screen and displaying lines through the array of <x,y> points in response to the WM_PAINT message.

Download and run to observe the program behavior when the mouse is moved.

.data
ClassName 	db 		"SimpleSketchClass",0
AppName  	db 		"Sketch",0
points	POINT		1000 dup (<0,0>)
npoints	dd		0

.code
WndProc proc hWnd:HWND, uMsg:UINT, 
             wParam:WPARAM, lParam:LPARAM
   LOCAL ps:PAINTSTRUCT

   @@switch:
		cmp 	uMsg,WM_DESTROY
		je	@@destroy
		cmp	uMsg, WM_MOUSEMOVE
		je	@@mousemove
		cmp	uMsg, WM_PAINT
		je	@@paint
		jmp	@@default	
	@@destroy:					; WM_DESTROY event
		invoke PostQuitMessage,0
		jmp	 @@endswitch
	@@mousemove:				; WM_MOUSEMOVE event
		mov	 eAx, size POINT		; Left mouse button
		mul	 npoints	
		mov	 eSi, eAx
		inc	 npoints
		mov 	 eAx,lParam			; Extract <x,y> mouse
		and 	 eAx,0ffffh			; location from lParam
		mov 	 points[eSi].x, eAx	; and save in points
		mov 	 eAx,lParam			; array
		shr 	 eAx,16
		mov 	 points[eSi].y, eAx
		invoke InvalidateRect,hWnd,NULL,TRUE
		jmp	 @@endswitch
	@@paint:					; WM_PAINT event
		invoke BeginPaint,hWnd, ADDR ps
		invoke Polyline, eAx, addr points, npoints
		invoke EndPaint,hWnd, ADDR ps		
		jmp	 @@endswitch
	@@default:			
		invoke DefWindowProc,hWnd,uMsg,wParam,lParam		
		ret
    @@endswitch:
	Mov	eAx, 0
	ret
WndProc endp

 

TicTacToe in C++                                                    TicTacToe

Following C++ displays a TicTacToe board, accepts and displays O and X moves when a numbered button is pressed.

Play starts with X and alternates between the two players.

Logic is implemented to prevent cheating (e.g. changing an X to an O).

No other logic is implemented (e.g. game over, winner, etc.)

The underlying structure of a graphic Windows program remains the same. However, the need to send a message to the OS to update the user interface requires a relatively complex call, for example:

SendMessage((HWND) lParam, (UINT) WM_SETTEXT, (WPARAM) wParam,(LPARAM) "X");

Although we could translate into Assembler, we have reached the point of diminishing returns, its probably better to stick with C++ or some other higher-level language.

#include <windows.h>
#include <stdio.h>

#define BUTTON0 100

HINSTANCE app_hInst;

char szTitle[]="X turn";								// Window title text
char szWindowClass[]="Window Class";
char player = 'X';

int WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	PAINTSTRUCT ps;
	HDC hdc;
    	HWND hwndButton;

	switch (message) {
		case WM_DESTROY:
		   PostQuitMessage(0);						// Close window and process
		   break;
        		case WM_CREATE:
		   for(int button=0; button<9; button++) {
			char buffer[2];
			sprintf( buffer, "%d\0", button ); 				/* Create button caption */
			CreateWindowEx(0,						/* more or ''extended'' styles */
                         		TEXT("BUTTON"),                         			/* GUI ''class'' to create */
                         		TEXT(buffer),						/* GUI caption */
                     			WS_CHILD|WS_VISIBLE|BS_DEFPUSHBUTTON,   /* control styles separated by | */
                     			50*(button%3),                          			/* LEFT POSITION */
                     			50*(button/3),                          			/* TOP POSITION  */
                     			50,                                     				/* WIDTH OF CONTROL */
                     			50,                                     				/* HEIGHT OF CONTROL */
                     			hWnd,                                   			/* Parent window handle */
                         		(HMENU)BUTTON0+button,                  		/* control''s ID for WM_COMMAND */
                         		app_hInst,                              			/* application instance */
                         		NULL);
		}
          		break;
        	      case WM_COMMAND:
              	if(((HWND)lParam) && (HIWORD(wParam) == BN_CLICKED))
		    if(player == 'X') {
			SendMessage((HWND) lParam, (UINT) WM_SETTEXT, (WPARAM) wParam,(LPARAM) "X"); 
			SendMessage((HWND) hWnd, (UINT) WM_SETTEXT, (WPARAM) wParam,(LPARAM) "O turn"); 
			player = 'O';
		    }
		    else {
			SendMessage((HWND) lParam, (UINT) WM_SETTEXT, (WPARAM) wParam,(LPARAM) "O"); 
			SendMessage((HWND) hWnd, (UINT) WM_SETTEXT, (WPARAM) wParam,(LPARAM) "X turn"); 
			player = 'X';
		    }
              	break;
	      default:
		return DefWindowProc(hWnd, message, wParam, lParam);
   }
   return 0;
}

int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
 	MSG msg;
	WNDCLASSEX wcex;
    	HWND hWnd;

   	 app_hInst = hInstance;

	wcex.cbSize 			= sizeof(WNDCLASSEX); 
	wcex.style			= CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc		= (WNDPROC)WndProc;
	wcex.cbClsExtra		= 0;
	wcex.cbWndExtra		= 0;
	wcex.hInstance		= hInstance;
	wcex.hIcon			= 0;
	wcex.hCursor			= LoadCursor(NULL,IDC_ARROW);
	wcex.hbrBackground		= (HBRUSH)(COLOR_WINDOW+1);
	wcex.lpszMenuName		= 0;
	wcex.lpszClassName		= szWindowClass;
	wcex.hIconSm		= 0;

	RegisterClassEx(&wcex);

    	hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, 
					    CW_USEDEFAULT, 0, 170, 190, NULL, NULL, hInstance, NULL);

    	ShowWindow(hWnd, nCmdShow);
    	UpdateWindow(hWnd);
	
	while (GetMessage(&msg, NULL, 0, 0))							// Main message loop:
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;
}

Command Prompt - File Copy

The standard library for the C language contains many useful functions for file handling, running other process, math, etc.

These functions can be accessed by defining the PROTO for the C function and then INVOKE the function. 

The following Command Prompt example illustrates using the library by copying a file to the screen.

The C program copies file.C while the Assembler program copies file.asm to the console screen. The commands necessary for Assembler are:

  1. Copy the Assembler program and save as file.asm.
  2. C:"\Program Files\Microsoft Visual Studio 9.0\VC\vcvarsall.bat"
  3. ml /coff /Zi file.asm /link MSVCRT.LIB /subsystem:console
  4. file

int *fopen( const char *filename, 
            const char *mode );
int getc( int *stream );
int putchar( int c );


char filename[7] = "file.c";
char mode[2] = "r";


int main(void) {
	int *fp = fopen(filename, mode);
	int c;

	while ((c=getc(fp)) != -1)
		putchar(c);
	return 0;
}
.386
.model small, c

fopen	  proto	near C, 
        filename:near ptr dword, mode:near ptr dword
getc 	  proto	near C, fileptr : dword
putchar proto	near C, char : dword

.data
filename	byte	"file.asm",0
mode		byte	"r",0

.code

main	proc	public
	local	 	fp : dword
	invoke 	fopen, addr filename, addr mode
	mov	 	fp, eAx
@@while:
	invoke 	getc, fp
	cmp	 	Al, -1
	jne	 	@@do
	jmp	 	@@endwhile
   @@do:
	invoke 	putchar, eAx
	jmp	 	@@while
@@endwhile:

	ret
	
main	endp
	end