Click here to Skip to main content
Click here to Skip to main content

DWinLib - Visual Studio Express Setup and a Minimal Wrapper

, 14 Feb 2013 CPOL
Rate this:
Please Sign up or sign in to vote.
Setting up Visual Studio 2008 (and possibly newer versions) to build a simple Windows Wrapper!

The following writing is part of a series of articles on DWinLib, a semi-simple wrapper for the Windows API. These include:

  1. DWinLib - An Overview
  2. Setting up Visual Studio and building a minimal Windows’ wrapper - You are here
  3. Compiling a bare DWinLib program
  4. DWinLib - The Guts
  5. Modal Dialogs - A DWinLib Approach

Overview

After you finish this article, and have worked through the examples, you will be able to set up Visual Studio C++ Express (VSE) and compile and understand a simple, non-DWinLib Windows program. Some of the information is useful for those wishing to use VSE without any wrapper framework.

Perhaps I should qualify the previous paragraph and say “as long as you are using VSE 2008.” The following was first written for VSE 2005 with an older version of DWinLib. I am leaving the original instructions semi in-tact for those who still use that platform. For programmers in that camp, the 2.00 edition of DWinLib, and the application developed for it are necessary. Changes too numerous to list here have occurred since then, and I won’t attempt to fit the instructions into one set. Nor will I reinstall 2005 and redo this writing for the newer library, even though doing so would be technically feasible. I just have more important things on my plate.

I suspect the steps to build this example are the same in VSE 2010 and VSE 2012 as they are in VSE 2008, but I could be wrong. Comments about this are welcomed.

Realistically, no reason exists to keep the 2005 instructions, because anyone still using that tool knows how to work with it by this point. I cannot make myself erase good information, as numerous memories exist of needing something immediately after taking such an action. Therefore I will simply organize everything to the best of my ability.

For those who use VSE, the 2008 iteration (and I’m certain the newer ones as well) made creating Win32 programs much simpler because downloading and setting up the platform SDK is now unnecessary.

If you are familiar with Visual Studio the following may not be so helpful, but the completed project might interest you, as it shows everything necessary to create an extremely minimal wrapper. Tearing the code apart, you will see how to wrap some common controls and how a program can manage and respond to multiple windows in order to implement a simple game of Tic-Tac-Toe.

Once completed, the bare application is easily extended to create your own, although the framework given leaves many tasks to manual coding. Let us get started!

Visual Studio Setup

Preliminaries

The first step, of course, is to download and install VSE. VSE 2005 isn’t available any more, nor is 2008, but VSE 2010 and 2012 may be obtained from that link. If you still have a copy of VSE 2005 you will also need the Platform SDK, which can be downloaded from the Windows® Server 2003 SP1 Platform SDK Web Install page. I won’t go over setting these up other than to say I remember the process being pretty much ‘pick a directory and follow the instructions.’ You may also download the ISO, in which case a little more is involved. Google can elaborate on the necessary steps.

The rest of this ‘Visual Studio Setup’ section is primarily devoted to items required for VSE 2005. This isn’t because I’ve forgotten to write the 2008 instructions. The steps simply are not needed due to the improvements Microsoft made in the newer versions.

Setting up the default linker settings

This section only necessary for VSE 2005:

When you create a console window project in VSE, just ‘kernel32.lib’ is linked to. Until you make some changes to VSE itself, you must manually add many standard Windows libraries into the dependencies. The following eliminates this step, although any non-standard libraries, such as DirectX, will still need linked in.

First, find the ‘default.js’ file inside ‘VC\VCWizards\AppWiz\Generic\Application\scripts\1033’ Visual Studio directory. Look for the two lines containing the word ‘kernel32’. Insert “user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib” between the ‘kernel32.lib’ and the ‘$(NoInherit)’ in both lines.

Now open the ‘corewin_express.vsprops’ files in the ‘VC\VCProjectDefaults’ Visual Studio directory. Again, find the line with ‘kernel32,’ and insert the same string behind ‘kernel32.lib’, before the ending quotation mark.

That’s everything for this step. All your future console projects will automatically list these default libraries in their dependencies.

Setting up the help files inside VSE

This section only necessary for VSE 2005:

(I wish something similar was available in VSE 2008, because the help system for standard C++ Windows library functions is vastly inferior to the 2005 version. If anyone knows any tips for improving the built-in help functionality: please post in the comments. Until then Google is often a better alternative to pressing ‘F1.’)

Visual Studio has the ability to give context sensitive help based upon the word at the cursor when F1 is pressed. To add the Platform SDK help files to the ones Visual Studio looks in, open Visual Studio and press F1. After the Document Explorer opens, paste the following link into the URL location: ms-help://MS.VSExpressCC.v80/dv_vsexpcc/local/CollectionManagerExpress.htm.

At the bottom of the page which appears exists an option to add the Platform SDK help files to the help system. Select it and press OK when you are prompted to restart everything. Close the Document Explorer and Visual Studio. Reopen VSE and press F1 again. The Document Explorer will say it is reconfiguring itself with the new help files.

From experience, Microsoft is placing more emphasis on the .Net framework help topics than the WinAPI ones in the newer versions of Visual Studio. Many items you would assume should open up as API topics default to their .Net counterpart. Google will often work better in those cases.

I have also heard it is possible to decrease the opening time of the Document Explorer when F1 is pressed in VSE 2005. The method which has been given is to click on the ‘Tools -> Visual Studio 2005 Command Prompt’ menu items, navigate to the "C:\Program Files\Common Files\Microsoft Shared\Help 8" directory in the command prompt window that appears, and type ‘for /r %f in (*.dll,*.exe) do ngen "%f"’. When I did this I just got a bunch of error messages like the following:

Failed to find dependencies of image C:\Program Files\Common Files\Microsoft Shared\Help 8\dexplmnu.dll because this image or one of its dependencies is not a valid Win32 application.
Compiling 1 assembly:
Compiling assembly C:\Program Files\Common Files\Microsoft Shared\Help 8\dexplmnu.dll ...
Error compiling C:\Program Files\Common Files\Microsoft Shared\Help 8\dexplmnu.dll: An attempt was made to load a program with an incorrect format. (Exception from HRESULT: 0x8007000B)

It also failed to load any faster. (The Document Explorer takes about thirty seconds to open after VSE is first booted up and F1 is pressed on my system. Future openings occur almost instantaneously, until VSE is closed.)

Setting up the default directory settings

This section only necessary for VSE 2005:

Now is the time to point VSE to the ‘Include’ and ‘Lib’ directories which were installed in the platform SDK installation step. To do so, open VSE and select the ‘Tools -> Options’ menu item. Press the the ‘Projects and Solutions -> VC++ Directories’ item in the left frame, and the ‘Show directories for:’ drop-down combo box on the right. From the items which appear, click on ‘Include files’, and select the ‘New Line (Ctrl-Insert)’ icon. Then choose the ‘...’ button to the right of it and navigate to the ‘Include’ directory where the Platform SDK is installed. Press ‘Open’ in the directory selection window, and change the ‘Show directories for:’ dropdown combo box to ‘Library files’, and repeat the process, this time selecting the ‘Lib’ directory off the Platform SDK installation directory. When finished, press the ‘OK’ button in the options window.

This step only needs to be performed once. All future projects will use this setting.

VSE 2005 & 2008: While you are in the Options window, you might want to enable Build Timing, so VSE notifies you how long compilation takes. That option is in the ‘Projects and Solutions -> VC++ Project Settings’ section.

Create a blank project

Next, make an empty C++ project in VSE. Select the ‘File -> New Project’ menu items, and then ‘General’ in the left pane, and ‘Empty Project’ in the right pane. (This is easier than selecting ‘Win32’ for several reasons I won’t discuss.)

VSE makes part of this step a little perplexing. You must enter some text in the ‘Name:’ portion of the New Project dialog. VSE also prompts you to create a new subdirectory for the solution. We won’t use the option, so uncheck that menu item. The ‘Solution:’ box becomes inactive, but VSE STILL makes a subdirectory under the browsing location, and places your ‘solution’ in it.

Therefore, if your goal is to create the project in ‘...Programs\MinimalWrapper’, and the directory already exists, you must browse to inside of ‘Programs,’ type ‘MinimalWrapper’ in the ‘Name:’ box, and press ‘OK.’ If you want the solution to have a different name than the parent subdirectory, you will need to rename the solution after it is created, and this can only be done by right clicking on the “Solution ‘MinimalWrapper’” line in the Solution Explorer to highlight it, and then selecting the ‘File -> Save MinimalWrapper.sln As...’ menu items, as far as I can tell.

So create a new project in a subdirectory of your choice, or, if you have already created one with nothing in it, enter the subdirectory’s name while being in the parent directory.

Regarding folder heirarchy, for our work you can place the project into any subdirectory on your hard drive you want. But after creating a few programs you might wish for a more optimal directory structure. After having done this several times, I recommend the following as a starting point. A benefit which won’t be obvious without experience is there are far fewer steps to find folders when adding libraries and utilities to your projects.

So if you follow my recommendation you will have a folder inside ‘MyProgs’ called ‘MinimalWrapper’ which contains everything for this example.

Wrapping a Window

Yay! We are ready to code a minimum wrapper! To do so we must first add a .cpp file to our project. I don’t remember what was required in VSE 2005, but I believe they were the same basic steps as in VSE 2008. After the previous instructions your screen should look something like this:

Select the ‘File -> New -> New File’ menu items (or type ‘Ctrl + N’), choose ‘Visual C++’ in the left pane, and ‘C++ File (.cpp)’ in the right one, and select ‘Open.’ An empty file will appear in the right, previously greyed-out area of VSE. Save this to the program directory. ‘File -> Save Source2.cpp As...’ are the required menu item steps. Navigate to the ‘MinimalWrapper’ subdirectory and name it ‘MinimalWrapper.cpp.’

I am doing this a little bit of the long way around. These instructions show you the general steps needed to add any file to a project, and give you a grasp of the fundamentals. Earlier, when we made the solution, we could have selected another type, such as Win32 Console Application, and some base files would automatically have been added to the project. DWinLib doesn’t use ‘stdafx.h’ and the rest which those choices create. In other words, I always do the longer method because it is shorter: I don’t have to remove things from the project afterwards.

Having followed the above process, the file we created is not associated to the solution. We need to change this, so right click on the ‘Source Files’ filter in the left hand pane of the Visual Studio window (shown above) and choose ‘Add -> Existing Item.’ Finally, select our .cpp file from its hard drive location.

If you press the little green ‘Start Debugging’ arrow in the previous graphic (to the left of the word ‘Debug’ in the upper right-hand corner) you will receive a ‘fatal error LNK1561: entry point must be defined.’ Although this seems kinda dismal, we are actually very close to having a working program! Copy and paste the following in the file editor:

#include <windows.h>

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HINSTANCE hInst;

// **************************************
// Step 1: Create main program entrance:
// **************************************
int WINAPI WinMain(HINSTANCE instance, HINSTANCE , PSTR , int show) {
   // ***********************************
   // Step 2: Register the Window Class:
   // ***********************************
   static TCHAR appName[] = TEXT("MinApplication");
   hInst = instance;
   WNDCLASS wndclass;
   wndclass.style         = CS_HREDRAW | CS_VREDRAW;
   wndclass.lpfnWndProc   = WndProc;
   wndclass.cbClsExtra    = 0;
   wndclass.cbWndExtra    = 0;
   wndclass.hInstance     = instance;
   wndclass.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
   wndclass.hCursor       = LoadCursor(NULL, IDC_ARROW);
   wndclass.hbrBackground = (HBRUSH) GetStockObject(GRAY_BRUSH);
   wndclass.lpszMenuName  = NULL;
   wndclass.lpszClassName = appName;

   if (!RegisterClass(&wndclass)) {
      MessageBox(NULL, TEXT("Oh no, Mr. Bill...."), appName, MB_ICONERROR);
      return 0;
      }

   // ***************************
   // Step 3: Create the window:
   // ***************************
   HWND hwnd = CreateWindowEx(WS_EX_APPWINDOW, appName, TEXT("MinApplication"),
               WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_OVERLAPPEDWINDOW | WS_BORDER,
               CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
               NULL, NULL, instance, NULL);

   ShowWindow(hwnd, show);
   UpdateWindow(hwnd);

   // ********************************
   // Step 4: Enter the message loop:
   // ********************************
   MSG msg;
   while (GetMessage(&msg, NULL, 0, 0)) {
      TranslateMessage(&msg);
      DispatchMessage(&msg);
      }
   return msg.wParam;
   }


// ******************************************
// Step 5: Handle the messages from Windows:
// ******************************************
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
   switch (message) {
      case WM_DESTROY :
         PostQuitMessage(0);
         return 0;
      }
   return DefWindowProc(hwnd, message, wParam, lParam);
   }

Press the green button again and if all of the previous instructions were followed, Visual Studio should do a little processing, and a window will appear on your screen:

This doesn’t use a wrapper of any nature. Perusing the code, five distinct steps are outlined in the comments. All visible, non-modal-box, non-trivial applications in Windows require these.

The basic problem encountered when wrapping a Window procedure in an object oriented language is that of going from a static function (the ‘WndProc’ routine) to a non-static member function. This is necessitated by Windows itself, and its ‘C’ legacy. Windows can only tell your executable to take action on user input or other events through a static routine inside the program.

One of the difficulties in programming a non-trivial application in the above manner is a great deal of time is spent creating and managing the static WndProc procedures. You must also tie the application’s logic and data into the WndProc procedures, and this can be quite a task for a complicated application.

Below is the previous program re-written using the same basic mechanism behind DWinLib. It is almost four times larger than the preceding, code-wise, although at one time the executable compiled to 52 Kb, vs. 48 Kb for the non-wrapped version. To compile, select and delete everything in the editor, then copy and paste the new text. Or comment out the existing writing by surrounding it with ‘/*’ and ‘*/’ block markers, and insert the new code afterwards. (I recommend the last way because you can more easily play with the two alternatives. For that reason I also suggest using single-line comments throughout your work whenever possible so the block comments are easier to use in this manner. This applies to all programs you write, regardless of the framework.)

As you peruse the following, keep this in mind:

The code works by creating a WinApp to hold the main static callback. WinBaseO is defined as a virtual base class from which to derive actual windows. (No member function screams out to be made into a pure virtual, therefore only common sense keeps plain WinBaseO objects from being instantiated.)

When a WinBaseO object is constructed it registers its existence with the WinApp. Derived classes override WinBaseO functions however required, in order to process the Windows messages (WM_PAINT, etc.) in the desired manner. Other routines can also be added, to handle items unrelated to the window logic.

In this example only a WinMainO is derived from WinBaseO. A single WinMainO, which contains the application’s main window, is required in each DWinLib application. In a non-trivial program you may have many more windows derived from WinBaseO.

All in all, this is a simple approach, and works well.

You will note the same five steps have been commented as were previously pointed out, although a couple additional comments exist.

You will also see the fifth step has been broken into three parts. The first (5 - just as in the previous code) is a static message handler which is the heart of the WinApp unit. The second portion (5a) is a non-static, member function of WinBaseO, used to define default processing for Window messages. The third part (5b) overrides any window message handlers for specific use by your window, when the base routine will not do the needed task.

The other comments are thrown in to indicate program structure unrelated to the outlined steps.

#define WIN32_LEAN_AND_MEAN
#define _WIN32_WINNT 0x0500
#define _WIN32_IE 0x0400

#include <tchar.h>
#include <windows.h>
#include <string>

class WinBaseO;

// *************************************************************************
// Define a 'WinApp' class to handle the main static window procedure.
// There will only be one of these instantiated in the program as a global.
// *************************************************************************
class WinApp {
   private:
      static WinBaseO * winBeingCreatedC;
      HINSTANCE hInstC;
   public:
      WinApp(HINSTANCE hInst_, HINSTANCE, LPSTR, int);
 
      LRESULT static CALLBACK WindowProc(HWND window, unsigned int msg,
                  unsigned int wParam, long lParam);
      HINSTANCE hInst() { return hInstC; }
      void   setWinBeingCreated(WinBaseO * win) { winBeingCreatedC = win; }
      WPARAM run();
   };


// **********************************************************
// Define a base window class for deriving real windows from
// **********************************************************
class WinBaseO {
   //For dealing with the window handle and window procedure
      protected:
         HWND hwndC;
      public:
         WinBaseO();
         ~WinBaseO();
         
         //winProc can be overridden for each window, but it is easiest to just
         //override the individual functions.
         virtual LRESULT winProc(HWND window, UINT msg, WPARAM wParam, LPARAM lParam);
         HWND hwnd() { return hwndC; }
         
         void hwnd(HWND hwnd) { hwndC = hwnd; } //Only needed by WinApp::WindowProc

   //Routines to be inherited by derived windows
      public:
         virtual LRESULT wClose() { return DefWindowProc(hwndC, WM_CLOSE, 0, 0); }
   };


WinBaseO * WinApp::winBeingCreatedC = NULL;
WinApp * gWinApp; //A Global WinApp object for the entire program


// ************************************
// Now fill in the WinBaseO definition
// ************************************
WinBaseO::WinBaseO() : hwndC(NULL) {
   gWinApp->setWinBeingCreated(this);
   }


WinBaseO::~WinBaseO() {
   if (IsWindow(hwndC) == TRUE) DestroyWindow(hwndC);
   }
   
   
// ***************************************************************************
// Step 5a: Handle the messages from Windows in a non-static member function:
// ***************************************************************************
LRESULT WinBaseO::winProc(HWND window, UINT msg, WPARAM wParam, LPARAM lParam) {
   switch (msg) {
      case WM_CLOSE:
         wClose();
         return 0;
      }
   return DefWindowProc(window, msg, wParam, lParam);
   }


// ***************************************************
// Now define and fill in the guts to a 'real' window
// that uses the above WinBaseO definitions.
// ***************************************************
class WinMainO : public WinBaseO {
   //Regular class stuff
      private:
         void registerWinClass();

      public:
         WinMainO();
         
   //WinBaseO overrides:
      public:
         virtual LRESULT wClose();
   };


namespace { //An unnamed namespace for this unit to use
   TCHAR * winCaption = TEXT("Bare Windows Wrapper Example");
   TCHAR * winClassName = TEXT("BareWindowApp");
   }


// ***********************************
// Step 2: Register the Window Class:
// ***********************************
void WinMainO::registerWinClass() {
   WNDCLASS wc;
   wc.style         = CS_HREDRAW | CS_VREDRAW;
   wc.lpfnWndProc   = gWinApp->WindowProc;
   wc.cbClsExtra    = 0;
   wc.cbWndExtra    = 0;
   wc.hInstance     = gWinApp->hInst();
   wc.hIcon         = NULL;
   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
   wc.hbrBackground = (HBRUSH)GetStockObject (GRAY_BRUSH);
   wc.lpszMenuName  = NULL;
   wc.lpszClassName = winClassName;
   if (!RegisterClass (&wc)) abort();
   }
 

WinMainO::WinMainO() {
   static bool registered = false;
   if (!registered) {
      registerWinClass();
      registered = true;
      }

   // ***************************
   // Step 3: Create the window:
   // ***************************
   hwndC = CreateWindow(winClassName, winCaption, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT,
               CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, gWinApp->hInst(), 
               NULL);
   if (!hwndC) abort();

   ShowWindow(hwndC, SW_SHOW);
   UpdateWindow(hwndC);
   }


// *************************************************
// Step 5b: Override any message handlers you want
// to handle differently than the WinBaseO handler:
// *************************************************
LRESULT WinMainO::wClose() {
   PostQuitMessage(0);
   return DefWindowProc(hwndC, WM_CLOSE, 0, 0);
   }
 

// **********************************
// Now fill in the WinApp definition
// **********************************
WinApp::WinApp(HINSTANCE hInst_, HINSTANCE, LPSTR, int) :
            hInstC(hInst_) {

   gWinApp = this;
   }
 

WPARAM WinApp::run() {
   WinMainO win;
   // ********************************
   // Step 4: Enter the message loop:
   // ********************************
   MSG msg;
   while (GetMessage(&msg, NULL, 0, 0)) {
      TranslateMessage(&msg);
      DispatchMessage(&msg);
      }
   return msg.wParam;
   } 


// ******************************************
// Step 5: Handle the messages from Windows:
// ******************************************
LRESULT CALLBACK WinApp::WindowProc(HWND window, unsigned int msg, unsigned int wParam,
            long lParam) {

   WinBaseO * win = reinterpret_cast<WinBaseO*>(GetWindowLong(window, GWL_USERDATA));
   if (win) return win->winProc(window, msg, wParam, lParam);
   SetWindowLong(window, GWL_USERDATA, reinterpret_cast<long>(winBeingCreatedC));
   winBeingCreatedC->hwnd(window);
   return winBeingCreatedC->winProc(window, msg, wParam, lParam);
   }


// ***************************************************************
// And finally, a WinMain that takes advantage of the above work:
// ***************************************************************
// **************************************
// Step 1: Create main program entrance:
// **************************************
int WINAPI WinMain(HINSTANCE inst1, HINSTANCE inst2, LPSTR str, int show) {
   WinApp app(inst1, inst2, str, show);
   return app.run();
   }

The above code has been kept to a minimal state: error handling is almost non-existent, and all extraneous messages are ignored. The previous routines are just enough to create a basic Windows wrapper.

Examining WinApp::WindowProc, you will see that WinBaseO’s constructor tells the global WinApp object it is being constructed, and the incoming Window messages are routed to the appropriate member functions. Much thought was given to that aspect of the functioning of DWinLib.

As stated earlier, tying your application’s logic and data into the WndProc procedures of a pure API program can be quite painful. Using this wrapper removes the difficulty from the task: simply put the functionality in the definition of the window derived from WinBaseO and use it like data in any C++ class. The application logic also flows much cleaner when using a wrapper, if you are conversant in object oriented techniques.

If you want to see another example of this method in action, examine the attached zip file and investigate how the Tic-Tac-Toe program is put together modularly. The internals are significantly bigger than the previous. Many more messages are handled, exception handling has been included, and it is not as trivial as the above code. The Tic-Tac-Toe program also wraps a couple of Windows Common Controls, and does so in one of the cleanest examples I’ve ever seen. (I’m tooting my own horn here, as extracting the information and getting everything to work from the examples I found was kinda painful. Microsoft does not make great examples for some of the common controls, and I did not find much better in my limited search at the time I constructed that application back in about ’02.)

Even though this is the framework DWinLib is based upon at its core, DWinLib is far advanced from the preceding code. There will be more on the internals of DWinLib, and what it does in addition to the above, in an upcoming article.

The End

History

2/13/2013 - Although this stems from earlier writings, I’m considering it to be brand new because of all the necessary modifications, including the changes to properly tuck the presentation into the DWinLib overview series.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

David O'Neil
Software Developer www.randommonkeyworks.com
United States United States
I am the author of Laughing at the Devil: One Man’s Religious Discoveries. For those who are “ready to look at the world - religion, science, spirituality - differently,” LATD is the book to turn to.
 
In about 1994 I began studying and documenting the astronomy of our ancestors. A hint lead to many years of partial understanding, before a profound breakthrough occurred and some old myths finally made sense.
 
The greatest of my discoveries is the celestial observations behind the biblical tale of Samson, which was created 3,000 years ago. That find casts a profound new light on the roots of Western religion, as well as the foundation of modern science. To learn more, visit my website.
 
Trained as a mechanical engineer, I learned C++ programming on my own in order to create a MIDI program. I am delighted to say I also succeeded in that goal. Happy coding, everybody!

Comments and Discussions

 
-- There are no messages in this forum --
| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.1411023.1 | Last Updated 14 Feb 2013
Article Copyright 2013 by David O'Neil
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid