Hypermotion.se
MainDevinfoDownloadEmailLinksArticles


Here's the article section. This is the place where different thoughts, discoveries and other stuff is posted
if not connected to the current development. It might be overkill to call it articles since it can be just short notes
or code snippets........but here it is anyway! I hope it is useful!


Downloading and using the correct Windows SDK


I discovered the other day that I was using an old Windows SDK during my development. I thought
I was using the latest one, because I have installed it, but that may not be true at all. I thought the installation
process handled the switch automagically but at least I was using an old version. It seemed like the Windows
SDK used environment variables to control the version because in Visual Studio the directories for includes and
such are controlled by a $(WindowsSdkDir) variable. Simple! I'll change that in a snap! Well, at least I
thought so...But it isn't that simple, things are stored in the registry as well.

But first you have to download the correct SDK. I was in a bit of hurry and of course I downloaded the incorrect
one, the 32-bit one instead of the 64-bit version I needed. So, Microsoft actually have a good page here that
helps you to choose the correct version. I prefer to download the ISO version and burn it to a DVD so I have
it handy for other computers if I need it.

When it comes to choosing the SDK version to use, you can run the Windows SDK Configuration Tool
(WindowsSdkVer.exe) in the  Setup folder of your SDK installation after installation to be sure that you use that
version in Visual Studio, as shown below:

Windows SDK Version

But it doesn't stop there. If you have installed Visual Studio 2008 service pack 1 on the non-Express
Visual Studio version the tool will not work. Then you'll need to have a look at a post here.

Happy coding!


Using your own symbol server without Internet connection


Your own symbol server can be valuable if you ever debug using a debugger.
It's pretty easy to setup your own symbol server if you want to, and you can add both Microsoft public
symbols as well as your own. I'm going to show how to setup a symbol server with the public Microsoft symbols
without an existing Internet connection on your developer computer, and how to use it with a debugger such as WinDbg.

First download the Debugging tools for Windows and install it. If you are going to debug both 32-bit and 64-bit
applications you will need to download and install both packages, because the 32-bit WinDbg can only debug
32-bit applications and vice versa.

Create a target directory where local copies of used symbols can be stored, in this example we will use D:\Symbols.
Download any public Microsoft symbols you'll need, they are currently available here. If you plan to install other
operating system symbols then the ones for Windows 7 you'll need a temporary directory for symbol storage as well,
since the pre-Windows 7 symbols are stored in another directory structure. We will call this directory D:\symin in this
example. Remember that this symbol storage can use some space, I have a symbol server set up for operating systems
XP, Vista, Win 7, Windows Server 2003 and 2008 (both x86 and x64 with servicepacks) and this takes something like
15 Gb, and then I still don't count all the hotfixes that has been released!

Start installing one of the symbol packages you downloaded from Microsoft. If it is a Windows 7 symbol package you
can extract it to the target directory directly (D:\Symbols in my case).A Windows 7 symbol package is already in the correct
format, so you don't have to do anything else with it. If it is a pre-Windows 7 symbol package you'll extract it to the temporary
directory first (D:\symin in my case) and run the following command from a command prompt in the debugging tools
installation directory:

symstore add /r /f d:\symin\*.* /s D:\Symbols /t "xxx"

An explanation of the symstore command-line options can be found here.

NOTE: The "xxx" string after /t tells you what kind of symbols you're addding to the symbol store. So change this string
to anything you can identify the symbols with. If you're adding the XP SP3 x86 symbols it's a good thing to write it here.

The computer works for a while and when it has completed the symbols have been added to the symbol server store at
D:\Symbols. You can now clean up the temporary storage at D:\symin and continue with any remaining symbol installations
by repeating the procedure over again.

NOTE: Symstore does not support simultaneous transactions from multiple users. It is recommended that one user is
designated administrator of the symbol store and responsible for all transactions.

NOTE: Both public and private symbols of the same file can not be stored in the same symbol store because they both
contain the same signature and age.

To use a debugger such as WinDbg you just point the symbol search path to your symbol directory:

Start WinDbg. Click File | Symbol File Path... and type in:
"srv*D:\Symbols"

Don't forget to save your workspace so you don't have to type it in every time. Happy debugging!


Using your own symbol server with Internet connection


Your own symbol server can be valuable if you ever debug using a debugger.
It's pretty easy to setup your own symbol server if you want to, and you can add both Microsoft public
symbols as well as your own. I'm going to show how to setup WinDbg with the public Microsoft symbol server.

First download the Debugging tools for Windows and install it. If you are going to debug both 32-bit and 64-bit
applications you will need to download and install both packages, because the 32-bit WinDbg can only debug
32-bit applications and vice versa.

Create a target directory where local copies of used symbols can be stored, in this example we will use D:\Symbols.
After the debugging tools installation you start WinDbg. Click File | Symbol File Path... and type in:
"srv*D:\Symbols*http://msdl.microsoft.com/download/symbols" as shown below:

Add symbol server path

The first part is where on your computer the symbol files will be stored when they are downloaded from Microsoft
and the second part is the download path to the Microsoft public symbols. Any symbol files downloaded in this
example are stored at D:\Symbols for future use. Whenever a symbol file is needed the local directory is checked first,
and if it not was found it is downloaded from the public Microsoft symbol server. Don't forget to save your workspace so
you don't have to type it in every time. Happy debugging!


Add your own symbol files to a symbol server store


You may not know it, but symbol files are created by the compiler. So it's an easy step to add your own symbol files
to a symbol store.

First download the Debugging tools for Windows and install it. If you are going to debug both 32-bit and 64-bit
applications you will need to download and install both packages, because the 32-bit WinDbg can only debug
32-bit applications and vice versa.

Run the following command from a command prompt in the debugging tools installation directory. In this example
we assume that your project/solution is located in D:\MyApp and that the symbol store is located in D:\Symbols:

symstore add /r /f D:\MyApp\*.* /s D:\Symbols /t "Product Name" /v "Product Version" /c "Transaction comment"

As you may guess the strings "Product Name", "Product Version" and "Transaction coment" should match the actual
product name, version and comment you may want to use in your specific case. Happy debugging!


Connecting Visual Studio to a symbol server


Microsoft Visual Studio 2008 SP1 connects to the Microsoft public symbol servers automatically when you click Load symbols
from Microsoft symbol servers
in the Options dialog box (Debugging category, Symbols page) or the shortcut menu (in the
Modules
Window of Call Stack Window).

To set a path to a symbol server in Visual Studio you follow these steps:

Click Tools | Options.

Open the Debugging node and click Symbols.

Add the new path to the symbol server. If you want to use the Microsoft public symbol server you type:

http://msdl.microsoft.com/download/symbols

If you have a UNC path to a server on the network just type:

\\server\share

If you have the symbol server on your local computer just type the location of them, like:

D:\Symbols

Happy debugging!

Using the HTML5 video tag


Recently I started to investigate how I could use the HTML5 video tag to show my animations directly on
the download page without using any plugin. Not in full size ofcourse, more like a kind of thumbnail.
It was quite simple to accomplish, but I still want to share some tips.

Check if you can create Ogg Theora video files (.ogv, the ogm doesn't seem to be the same format so you can't
just rename it). If you need a converter you can download a command-line converter for Linux, MacOS and
Windows with the name Ffmpeg2Theora from here. Then I just use it to convert my animation to an Ogg Theora
video file (ogv). To create the thumbnail video in the downloads section I just typed:

ffmpeg2theora -x 176 -y 144 input.avi

This command converted my video input.avi to the size 176 * 144 from it's original size and called it input.ogv.
I recommend using sizes with multiples of 16 to keep the stream optimized. Ffmpeg2Theora has lot of options
to use so check it out on the command line, you can also find some examples here. Upload the ogv file to your site.
Then you have to modify your .htaccess file. If you don't have one you can create one in the same directory as the
ogv file and put this in it:

AddType video/ogg          .ogv
AddType application/ogg    .ogg

The last thing to do is to actually add the HTML5 video tag in your home page where you want it, and with
the options you like. There are also options to use a fallback if you want to, but I have chosen not to do it.
Good luck!


More on DirectX application memory debugging


I noticed the other day that my application had memory leaks so it had to be dealt with. I am going to show
this real-world case as an example on how to use different tools for DirectX debugging. Several of them have
been mentioned earlier like DbgView and PIX. They are both going to action in this case. Before you start debugging
DirectX applications you need to do these things:

Turn on DirectX debug runtime in the DirectX Control Panel:

DirectX Control Panel Proprties

Set the radio button on the debug version and crank up the output level to the max. As you
may notice there are some other interesting options here as well, like the break on memory
leaks, errors and AllocID, but I am not going to show them this time. For example you can
use the AllocID to set a breakpoint on a specific memory allocation, that's useful.

You also have to add a few things to the source code. Add these includes:

#define D3D_DEBUG_INFO                          //Enable enhanced D3D debugging in debug mode

#define _CRTDBG_MAP_ALLOC                       //Enable memory leak detection

 

#if defined (DEBUG) | defined (_DEBUG)         //If debug build is used

   #include <stdlib.h>

   #include <crtdbg.h>                          //Include declaration for _CrtSetDbgFlag

#endif


Also add these lines at the start of your WinMain:

// Add general-purpose memory leak detection for debug builds

 

#if defined (DEBUG) | defined (_DEBUG)         //If debug version is used

   _CrtSetDbgFlag (_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); //Set debug flags

#endif


Compile and link your application with the Debug configuration. Start up DbgView then run your application.
Let it run and then exit it. Return to DbgView where you should find the debug output from your application.
It can look something like this:

DbgView Log

One thing to notice here is line #43 where we can see that we have 151 allocations that not are handled correctly!
Wow, that's not too good. Actually, this is not as bad as it seems. When something isn't released correctly that in turn
stops other things to be released which in turn stops other things from being released....Well, you get the picture.

Other things to notice here is lAllocID on #47 and the size of the allocated data in dwSize on the same row. The
size can be a clue what we have to look for and the lAllocID is the same AllocID as we saw previously in the
DirectX Control Panel. So if we change the 0 in the DirectX Control Panel to break on AllocID 1 and run
a debug on the application in Visual Studio we get a breakpoint on the allocation. Sweet! Unfortunately this will
not help us much in this case, since the AllocID is very early in the code, it is the DirectX device that still has references
and can not be closed, so this is not what we're looking for. Something else we notice is redundant render states
on #29 and 31. This is useful when we start optimizing. But back to the memory errors...

So what are we going to do? We fire up PIX. This is an excellent tool for debugging and optimizing/profiling your
DirectX application. I recommend that you check out what it has to offer, it's very capable. So we start up PIX and
clicks on the new icon:

PIX Start Experiment Screen

Here we set the application we want to profile, your DirectX application. Select a replayable call stream and set a name.
Notice where the file is going to be saved since it may be quite big so you may want to delete it when done. Then just click
the Start Experiment button and your application should start. Run it as before and quit. After a while you will get a screen
similar to this:

PIX Profile Data Screen
Here we have loads of information. For now the most interesting stuff is in the Objects window in the middle. There
we have all objects we use and in the Destruction field we can see some entries marked Never: Depth stencil, render target, a swap
chain and the device. Things are clearing up a bit but we still don't know for sure. The events window to the left show all
frames with calls made and references to the objects in the objects window are shown when you click on the different frames.
So we simply go to the last recorded frame and it looks like this after I filtered the object window on Destruction: Never:

PIX Show Object References Last Frame
Here you can notice that the highlighted row in the objects window is the only object that still have a reference in the App
Refs column. We have found it! With this information it is quite easy to fix the bug: A surface pointer that allocated a surface
twice, by mistake. This single mistake caused the 151 leaks....

This is just a small piece of what PIX have to offer, make sure you check it out!


Using reference tones to synchronize sound


If you want to synchronize a sound effect with the length of a tiled animation you quickly notice that it
isn't always easy to estimate the length of the sound. My solution was to create a number of reference sounds
in 440 Hz with lengths from 0.1 seconds to 3.0 seconds. You can quickly insert a reference sound and switch
to a longer or shorter one until the correct length is found. If you want to use them they are available in the
download section.


Visual studio effect file integration


As you might now the fixed graphics pipeline is moving out from DirectX, and is replaced with shaders.
Maybe you have already started working with them. Have you ever wished you had integration with them
in Visual Studio? Well, you can fix that with a rule file located here.


Directx 9 Fixed graphics pipeline


I know, the fixed graphics pipeline is moving out from DirectX, but I still use it from time to time.
Sometimes I wished I had a graphics pipeline overview. Luckily the author of the book "The
Direct3D Graphics Pipeline" Richard Thomson has published an excellent overview on his site.
You can find the DirectX 9 pipeline poster here.


Playing an avi movie located in memory


There may be situations where you would like to play an animation already loaded into
memory to the screen. The following code shows how to play an animation from memory
using MMIO functions. It might not be an optimal solution at all times, but it works. We
make it possible by writing a custom MMIO handler and install it. This custom handler
passes the data for the streams and at the end we uninstall the custom handler. Let's begin
with the "main" code. Note that g_pAnim is a global pointer to the memory location and
dwAnimSize is the size of the animation:

UPDATE: I tested this code on Windows 7 x64 and it behave somewhat different then on XP that
it was written for in the beginning. It still work but the window isn't maximized as on XP. This is probably
easy to fix, but I haven't really tried to do it.

// The PlayAnimation function below plays an animation pointed by g_pAnim

// until the end of animation. The function uses windows multimedia and installs

// a custom MMIO procedure for reading the AVI file from the pointer in memory.

 

void FX_PlayAnimation (void)

{

MCIERROR MCIErr;                                //Result returned from play

LPMMIOPROC lpMMIOProc;                          //Pointer to the custom installed MMIO procedure

char szMCICommand[100];                        //MCI command string

 

if (g_pAnim==NULL) return;                     //Check for NULL pointer

 

// Prepare the window handle command, which have to include the window handle that

// we redirect video output to. By default MCI opens a window of it own, but I can't

// manage to play the animation in fullscreen in it.

 

sprintf_s ((char*)&szMCICommand,_countof (szMCICommand),

   "window test handle %d\0",Init.hwnd);

 

lpMMIOProc=mmioInstallIOProc                    //Install a custom MMIO procedure

   (mmioFOURCC ('Z','Y','K',' '),               //Four character code for installed procedure

   (LPMMIOPROC)FX_AnimIO,                       //Pointer to callback procedure

   MMIOFLAGS);                                 //Flags for installing the procedure

 

if (lpMMIOProc==NULL)                           //If custom MMIO procedure install failed

   AppError (10,0);                             //Call error function

 

MCIErr=mciSendString                           //Send an open "file" call to the avi file

   ((LPCSTR)"open test.ZYK+ type avivideo alias test\0", //Open with custom procedure with alias

   NULL,                                       //No buffer with returning info is needed

   0,                                          //Buffersize for return buffer

   NULL);                                       //No callback is needed

 

if (MCIErr) AppError (11,0);                    //Call error function if failed

 

MCIErr=mciSendString                           //Send a window command

   ((LPCSTR)&szMCICommand,                     //Window command with HWND handle

   NULL,                                       //No buffer with returning info is needed

   0,                                          //Buffersize for return buffer

   NULL);                                       //No callback is needed

 

if (MCIErr) AppError (48,0);                    //Call error function if failed

 

SetCursor (NULL);                              //Remove mouse cursor from animation window

 

MCIErr=mciSendString                           //Send a cue command

   ((LPCSTR)"cue test output\0",               //Send a cue command to prepare output

   NULL,                                       //No return buffer with returning info is needed

   0,                                          //Buffer size for return buffer

   NULL);                                       //No callback is needed

 

if (MCIErr) AppError (61,0);                    //Call error function if failed

 

MCIErr=mciSendString                           //Send a play command to the opened avi "file"

   ((LPCSTR)"play test wait\0",                 //Play command, wait until finished. Fullscreen?

   NULL,                                       //No buffer with returning info is needed

   0,                                          //Buffersize for return buffer

   NULL);                                       //No callback is needed

 

if (MCIErr) AppError (13,0);                    //Call error function if failed

 

MCIErr=mciSendString                           //Send close command to the avi "file"

   ((LPCSTR)"close test\0",                     //Close command

   NULL,                                       //No buffer with returning info is needed

   0,                                          //Buffersize for return buffer

   NULL);                                       //No callback is needed

 

if (MCIErr) AppError (16,0);                    //Call error function if failed

 

lpMMIOProc=mmioInstallIOProc                    //Remove the custom MMIO procedure

   (mmioFOURCC ('Z','Y','K',' '),               //Four character code for installed procedure

   NULL,                                       //Pointer is NULL, we are removing procedure

   MMIO_REMOVEPROC);                           //Flags for removing procedure

 

if (lpMMIOProc==NULL)                           //If custom MMIO procedure remove failed

   AppError (17,0);                             //Call error function

}


And the callback that passes the data looks like this:

// The function below is the callback routine for reading

// AVI files from memory through Windows multimedia. The

// normal Windows Multimedia functions are simulated, and

// all operations with the specified type are handled through

// this callback routine.

 

LRESULT CALLBACK FX_AnimIO (

   LPMMIOINFO lpMMIOInfo,                       //MMIO structure

   UINT uiMessage,                              //MMIO command to process by callback

   LPARAM lParam1,                              //Parameter 1

   LPARAM lParam2)                              //Parameter 2

{

static bool bAlreadyOpen=FALSE;                 //Keep track if already opened

 

switch (uiMessage)                              //Check what type of operation to do

   {

   case MMIOM_OPEN:                             //Open avi memory file

      if (bAlreadyOpen) return 0;               //Check if already opened

      bAlreadyOpen=TRUE;                        //Set open flag

      if (lpMMIOInfo==NULL) return -1;         //Check for NULL pointer

      lpMMIOInfo->lDiskOffset=0;               //Set offset to start of "file"

      return 0;                                 //We are done, return

   break;

 

   case MMIOM_READ:                             //Reading avi "file"

      memcpy ((void *)lParam1,g_pAnim+lpMMIOInfo->lDiskOffset,lParam2); //Copy the data

      lpMMIOInfo->lDiskOffset=lpMMIOInfo->lDiskOffset+lParam2;         //Set new disk offset

      return (lParam2);                        //We are done, return

   break;

 

   case MMIOM_CLOSE:                           //Closing the "file"

      return 0;                                 //Return to caller

   break;

 

   case MMIOM_SEEK:                             //Seek within the "file"

      switch (lParam2)                          //Check what type of seek

         {

         if (lpMMIOInfo==NULL) return -1;      //Check for NULL pointer

 

         case SEEK_SET:                        //Seek set position

            lpMMIOInfo->lDiskOffset=(long)lParam1; //Set new position

         break;

 

         case SEEK_CUR:                        //Seek from current position

            lpMMIOInfo->lDiskOffset+=(long)lParam1; //Set new position

         break;

 

         case SEEK_END:                        //Seek to end

            lpMMIOInfo->lDiskOffset=((long)dwAnimSize)-lParam1-1;

         break;

         }

 

      if (lpMMIOInfo==NULL) return -1;         //Check for NULL pointer

      return lpMMIOInfo->lDiskOffset;           //Return the new offset

   break;

 

   default: AppError (22,0);                    //Call error function, unknown command

   }

 

return -1;                                      //Return error if reached here

}


Last but not least we have an include for the MMIO flags as well:

// Flags for installing custom MMIO procedure:

#define MMIOFLAGS   MMIO_INSTALLPROC | MMIO_GLOBALPROC



Timing is all!


Measuring of the elapsed time is an important step in a game. Its is used to control the speed
of the game and it's different components. Note that not all components have to use the same
speed, different tasks can be performed with different time intervals. There are several ways to
check for time in a Windows system, and some are more suited for games then others.
I recommend using the QueryPerformanceFrequency and QueryPerformanceCounter calls
for your timing. They are easy to use, exist on Pentium and forward processors and give a high
resolution suitable for your needs. The following code snippet shows how to check for the
availability of this timer:

// The Timer_QuerySpeed function checks how many timer ticks the

// computer makes per second. It is stored in the timing structure

// and is used for general game timing.

 

void Timer_QuerySpeed (void)

{

BOOL bSupported;                                //Set if the hardware supports a QueryPerformanceFrequency call

 

bSupported=QueryPerformanceFrequency ((LARGE_INTEGER*)&(Timing.liCountPerSec)); //Query speed

 

if (!bSupported)                                //The hardware didn't support a QueryPerformanceFrequency call

   AppError (51,0);                             //Call error function

 

Timing.fSecsPerCount=1.0f/(float)Timing.liCountPerSec; //Calculate seconds per timer tick

}



Optimizing DirectX drawing throughput


It is not always simple to push the most of your DirectX application, but there are a few guidelines
to keep in mind during development. I am going to give a few tips on optimization:

Debugging DirectX applications


Debugging DirectX applications is not always an easy task. There are a few different ways to debug,
and they are chosen depending on the situation. A simple way is to print data you are interested in to
the screen or a file, but it may be very tedious. You can also step in the debugger or set breakpoints
of course. Another way is to turn on debug information on in DirectX. Set debug output to maximum
in the DirectX control panel. This debug information is printed in the output window in the IDE (at least
for Visual Studio). Microsoft has also released a good standalone application for getting these debug
messages called DbgView. I like it and use it from time to time. You can even connect to a remote
computer. Start it, start the logging and then start your application. The messages are now logged.

Another really good tool from Microsoft included in the SDK is PIX. Use it to get different performance
counters from your application. You can even trig user defined events in PIX by using the D3DPERF_BeginEvent
and D3DPERF_EndEvent calls. Don't forget to check out the other tools included in the DirectX SDK.

Good luck in your debugging!


Using custom font resources without installing them


When you're making a game you want it's appearance to be as good as possible, including the text that is
displayed. There are a number of ways to display text, like bitmapped fonts or TrueType fonts used in many
"ordinary" applications of today. But wait, if I make a TrueType font for my game I have to install it on the
players computer and then use it, I don't want that. No you don't have to. You can load your fonts into memory and
use a call to AddFontMemResourceEx. Just take a look at the code below:


Fonts.hMainFont=AddFontMemResourceEx (         //Add font resource from memory to the system

   pFont,                                      //Pointer to font resource in memory

   Fonts.dwMainFontSize,                       //Font size of font to add

   0,                                          //Reserved, must be zero

   &dwNumFonts);                               //Pointer to number of fonts to install



Simple, huh? Just don't forget to call RemoveFontMemResourceEx after you finished using it. Also note that this
call is only included in Windows 2000 and later, so you have to build your binary with with WINVER and
_WIN32_WINNT defined to 0x0500 minimum.


Mapping virtual key codes to clear text


In my current game under development I wanted a routine where the user chooses key combinations and the
pressed keys are presented on screen as clear text. If the player pressed space for jump I wanted it to write out
"Space" on the screen. How should I solve that? It was clear that I needed a virtual key to clear text conversion
table. And here it is:


// The cKeytable is a string table to keep track of what string to write on screen

// when the user presses a key with the corresponding virtal key code value when

// choosing keys for controls. This means that virtual keycode x is described by

// cKeytable[x] string.

 

char cKeytable[256][25]={

   "\0", "LEFT MOUSE BUTTON\0", "RIGHT MOUSE BUTTON\0", "\0", "MIDDLE MOUSE BUTTON\0",

   "X1 MOUSE BUTTON\0", "X2 MOUSE BUTTON\0", "\0", "BACKSPACE\0", "TAB\0",

   "\0", "\0", "CLEAR\0", "ENTER\0", "\0", "\0", "SHIFT\0", "CTRL\0", "ALT\0",

   "PAUSE\0", "CAPS LOCK\0", "\0", "\0", "\0", "\0", "\0", "\0", "ESCAPE\0", "\0",

   "\0", "\0", "\0", "SPACEBAR\0", "PAGE UP\0", "PAGE DOWN\0", "END\0", "HOME\0",

   "LEFT ARROW\0", "UP ARROW\0", "RIGHT ARROW\0", "DOWN ARROW\0", "SELECT\0", "PRINT\0",

   "EXECUTE\0", "PRINT SCREEN\0", "INSERT\0", "DELETE\0", "HELP\0", "0\0", "1\0", "2\0",

   "3\0", "4\0", "5\0", "6\0", "7\0", "8\0", "9\0", "\0", "\0", "\0", "\0", "\0", "\0",

   "\0", "A\0", "B\0", "C\0", "D\0", "E\0", "F\0", "G\0", "H\0", "I\0", "J\0", "K\0",

   "L\0", "M\0", "N\0", "O\0", "P\0", "Q\0", "R\0", "S\0", "T\0", "U\0", "V\0", "W\0",

   "X\0", "Y\0", "Z\0", "LEFT WIN KEY\0", "RIGHT WIN KEY\0", "APPLICATIONS KEY", "\0",

   "COMPUTER SLEEP\0", "KEYPAD 0\0", "KEYPAD 1\0", "KEYPAD 2\0", "KEYPAD 3\0", "KEYPAD 4\0",

   "KEYPAD 5\0", "KEYPAD 6\0", "KEYPAD 7\0", "KEYPAD 8\0", "KEYPAD 9\0", "MULTIPLY\0",

   "ADD\0", "SEPARATOR\0", "SUBTRACT\0", "DECIMAL\0", "DIVIDE\0", "F1\0", "F2\0", "F3\0",

   "F4\0", "F5\0", "F6\0", "F7\0", "F8\0", "F9\0", "F10\0", "F11\0", "F12\0", "F13\0",

   "F14\0", "F15\0", "F16\0", "F17\0", "F18\0", "F19\0", "F20\0", "F21\0", "F22\0",

   "F23\0", "F24\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "NUM LOCK\0",

   "SCROLL LOCK\0", "OEM SPECIFIC\0", "OEM SPECIFIC\0", "OEM SPECIFIC\0", "OEM SPECIFIC\0",

   "OEM SPECIFIC\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0",

   "LEFT SHIFT\0", "RIGHT SHIFT\0", "LEFT CONTROL\0", "RIGHT CONTROL\0", "LEFT MENU\0",

   "RIGHT MENU\0", "BROWSER BACK\0", "BROWSER FORWARD\0", "BROWSER REFRESH\0",

   "BROWSER STOP\0", "BROWSER SEARCH\0", "BROWSER FAVOURITES\0", "BROWSER HOME\0",

   "VOLUME MUTE\0", "VOLUME DOWN\0", "VOLUME UP\0", "NEXT TRACK\0", "PREVIOUS TRACK\0",

   "STOP MEDIA", "PLAY PAUSE MEDIA\0", "START MAIL\0", "SELECT MEDIA\0", "START APP 1",

   "START APP 2", "\0", "\0", "OEM 1\0", "PLUS\0", "COMMA\0", "MINUS\0", "PERIOD\0",

   "OEM 2\0", "OEM 3\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0",

   "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0",

   "\0", "\0", "OEM 4\0", "OEM 5\0", "OEM 6\0", "OEM 7\0", "OEM 8\0", "\0",

   "OEM SPECIFIC\0", "OEM 102\0", "OEM SPECIFIC\0", "OEM SPECIFIC\0", "PROCESS\0",

   "OEM SPECIFIC\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0",

   "\0", "\0", "\0", "\0", "ATTN\0", "CRSEL\0", "EXSEL\0", "ERASE EOF\0", "PLAY\0",

   "ZOOM\0", "\0", "PA1\0", "CLEAR\0", "\0" };



As you can see there are some old key codes there as well as some called OEM something, which are different for
different languages, so they are hard to set anything specific on. When I started to use the table for conversion I
noticed that some key defines were missing so I added these as well:


// The virtual keys for volume control doesn't seem to be defined,

// so I'm adding them manually here.

 

#define VK_VOLUME_MUTE     0xAD                 //Virtual keycode for sound mute

#define VK_VOLUME_UP       0xAF                 //Virtual keycode for volume up

#define VK_VOLUME_DOWN     0xAE                 //Virtual keycode for volume down



Well, that's all about that! Happy key conversion!


The Windows Main Game Loop


After reading some books on game programming I have noticed that several of them seems to have an
inefficient main game loop. Most of them only process one Windows message each frame and some of them
even uses the GetMessage call for message processing. Processing the complete message queue every frame is
a very quick operation and I haven't seen any problems with it. The GetMessage call waits for a message if the
queue is empty, and will put the thread to sleep if no message is there. In my opinion you'll have a better main game
loop if you do like this:


MSG msg;                                       //Windows message structure

 

PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE);   //Read message in queue without removing it

 

while (msg.message!=WM_QUIT)                    //Process messages until we quit

   {

   while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) //While messages in queue, first remove

      {

      TranslateMessage (&msg);                  //Then translate the message...

      DispatchMessage (&msg);                   //And dispatch it to the callback function

      }

 

   // Here we do our game stuff

   }