Category: window


Remember the main function from our standard C programs or the win32 console programs. It used to have two arguments argc and argv as follows

int main(int argc, char * argv[]) ...

These parameters helped us send command line parameters to our programs. ‘argc’ would hold the number of arguments passed and argv would be an array of   the parameters passed. But when we are doing a GUI based win32 application in visual studio, we would use the WinMain function as our entry point. This function doesnt have an argc or an argv argument. So how do we pass command line parameters to Windows programs, and how do we access them in our programs.

lpCmdLine argument

One solution is the WinMain function itself. Let us look at the typical declaration of WinMain

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)..

As we can see in the declaration, we have an argument called lpCmdLine of type LPSTR (char *). This variable stores the entire command line minus the name of the program. For eg if we had a program called ‘test.exe’ and if we called it as

test.exe Some arguments here

The variable lpCmdLine would hold the value “Some arguments here”. This is one way of accessing command line parameters,  although not as helpful as the classic argc and argv. We would have to write the code to parse the lpCmdLine variable ourselves, thus increasing the complexity of our program. Also, as we can see lpCmdLine is actually an ANSI string and thus would not work with Unicode characters being passed at commandline.

GetCommandLine()

Another method is to use GetCommandLine() api. This function returns the entire command line, i.e. it includes the program name (which could include the absolute path) and also all the parameters in a single string. This is similar to directly accessing lpCmdLine, but the advantage is that it is mapped to either the ANSI GetCommandLineA() function or the unicode GetCommandLineW() function depending on your projects settings. This solves the problem of unicode input thru command line, but it still doesnt provide a count of arguments neither the arguments as seperate variables like argv.

CommandLineToArgvW()

The last method that I am going to discuss is CommandLineToArgvW This function only works with the Unicode/Wide character set and there seem to be no CommandLineToArgvA ANSI equivalent function. The prototype is as follows

LPWSTR *CommandLineToArgvW(LPCWSTR lpCmdLine, int *pNumArgs)

This function comes closest to the simplicity of argc and argv, or rather it gives us access to our argc and argv in the windows program. As we can see the function accepts two arguments, one is a unicode commandline string which needs to be parsed, the second is a pointer to an int variable. The function stores the number of arguments in this variable when it returns.

The function returns an array of strings, which is similar to argv. Lets look at an example.

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE prevInstance, LPSTR lpCmdLine, int nShowCmd)
{
    LPWSTR *szArgList;
    int argCount;

     szArgList = CommandLineToArgvW(GetCommandLine(), &argCount);
     if (szArgList == NULL)
     {
         MessageBox(NULL, L"Unable to parse command line", L"Error", MB_OK);
         return 10;
     }

     for(int i = 0; i < argCount; i++)
     {
         MessageBox(NULL, szArgList[i], L"Arglist contents", MB_OK);
     }

     LocalFree(szArgList);
     return 0;
}

As we can see above by using this function, we get the commandline as the number of arguments(argc) and a list of strings (argv). The only thing to remember here is that, the function allocates a block of memory for the argument list. We have to manually delete this memory whenever we are done with it, or we will have a leaking application.

EDIT: Variables __argc and __argv

As pointed below by anonymous, Microsoft compiler provides us with the variables __argc and __argv. These variables are automatically populated at runtime by windows. The variables are available in two flavors, the ASCII version and the Unicode version. __argv is the ascii version, whereas __wargv is the unicode version. To use them we have to include stdlib.h, which is automatically included whenever you include windows.h. To let the project setting decide which version of the variable to use, we can access __targv, by including TCHAR.h.

This method is the simplest of them all, but the caveat is that we have to link to the VC++ runtime library i.e. msvcrt.dll (which we would for almost 99% of windows programs).

I hope this helps everyone, would love to hear everyone’s comments regarding the same.

I wanted to implement a double click method to close the display window. That is, whenever the user double clicked on the client window, my window should close.


case WM_LBUTTONDBLCLK:
 ShowWindow(hWnd, SW_HIDE);
 break;

To implement this I decided to process the WM_LBUTTONDBLCLK message in my window procedure, as follows.

Short and simple, but I was wrong.  I ran this code and tried to double click the window, nothing happend. I changed a few things and tried again, still nothing happening. I finally put a break point inside the WM_LBUTTONDBLCLK case, and realized, that no matter how fast or slow I double clicked, the window never recieves the WM_LBUTTONDBLCLK message.

After digging about in msdn( yes the WM_LBUTTONDBLCLK page), I found out that, for our window to recieve the WM_LBUTTONDBLCLK message, we have to define our window class as having the style CS_DBLCLKS.

ie when we define WNDCLASS or WNDLCASSEX, we have to include CS_DBLCLKS as a style. For example, my window class defination now looks like

WNDCLASSEX wcex;
wcex.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS
.....

On normal windows class, whenever we double click on the client area, windows generates a series of WM_LBUTTONDOWN and WM_LBUTTONUP. i.e. we may recieve something like the following.

WM_LBUTTONDOWN, WM_LBUTTONUP, WM_LBUTTONDOWN, WM_LBUTTONUP .....

There is also no guarentee, that we would recieve all of them simultanously, i.e. there might be other messages present between them. So we cant rely on the sequence to determine a doubleclick. Whenever, we define our window class to have the style of CS_DBLCLKS, Windows replaces the second WM_LBUTTONDOWN message with the WM_LBUTTONDBLCLK, whenever the left button is clicked twice with the system defined intervel. But we will still recieve four mouse messages as follows.

WM_LBUTTONDOWN, WM_LBUTTONUP, WM_LBUTTONDBLCLK, WM_LBUTTONUP ...

The lesson to learn  is that, we will recieve a buttondown and a buttonup message before our dblclk message.  Thus whatever processing we are doing for the down and up events will be executed along with the dblclk logic.

As I have mentioned before, I am creating a stop watch application using Visual C++ express and win32. One of the features I wanted to implement, was to be able to drag/move the display window using mouse.  I had implemented this window using the WS_EX_TOOLWINDOW and WS_POPUPWINDOW style. This meant that the window would not have a caption bar, and hence no way to drag/move the window. Even the ‘Move’ command of system menu would fail, as there was no caption bar. There could be many ways to overcome this problem. I am going to list here the method that I used.

Here are some basic steps to solve this :

  1. Indicate to our window, that we want to drag or move. This can be done thru a menu option,  mouse event, a button, a key etc I chose to do it whenever the left button is held down. This would mean processing the WM_LBUTTONDOWN event on the main window.
  2. Capture the mouse, so that Windows will pass all the mouse events to our window.
  3. Process the WM_MOUSEMOVE event to actually move the window.
  4. Indicate to the Window,  that we are done moving. Again, this can be done thru a menu option,  mouse event, a button, a key etc I chose to do it whenever the left button is released and process WM_LBUTTONUP. This would mimic the typical drag and drop behavior, ensuring consistency with windows.

So let us see an example of the same. As I mentioned earlier, I was going to mimic the typical drag and drop behaviour. Whenever the user would press and hold the left mouse button, I would enter the drag state and capture the mouse to the window. This can be achived by processing the WM_LBUTTONDOWN command in our Window Procedure.

case WM_LBUTTONDOWN:
     dragWindow = true;
     SetCapture(hWnd);
     break;

Windows will always send our window procedure the WM_MOUSEMOVE command, irrespective of any button press etc.  If we were to just process the WM_MOUSEMOVE command and move our window, it would move everytime we moved the mouse, irrespective of any buttons being pressed. To overcome this we use a variable, which I called ‘dragWindow’. We want to move our window in the WM_MOUSEMOVE message only if this variable is true. Thus we set it to true whenever the left mouse button is pressed.

Also the SetCapture(HWND hWnd) function captures the mouse for us. This will ensure that our window recieves each and every mouse event, irrespective of whether it is within our client window or anywhere else. We should release the mouse whenever we are done with our movements. To do this we use the function called ReleaseCapture() as follows

case WM_LBUTTONUP:
     ReleaseCapture();
     dragWindow = false;
     break;

The WM_LBUTTONUP message is sent whenever the left button is released, thus indicating that our drag operation is complete. Thus we also set the dragWindow variable to false. Now to implement the actual movement code.

To acutally move the window we have to process the WM_MOUSEMOVE messages. Everytime we recieve the message, we set a new position for our window. This could be done in many ways, like using SetWindowPos() API or destroying and recreating our window or using MoveWindow() api. I chose to use the MoveWindow API, because it seems to exist  just to facilitate  the dragging and moving of windows.

case WM_MOUSEMOVE:
     if (dragWindow == true)
     {
         RECT mainWindowRect;
         POINT pos;
         int windowWidth, windowHeight;

         pos.x = (int)(short) LOWORD(lParam);
         pos.y = (int)(short) HIWORD(lParam);

         GetWindowRect(hWnd,&mainWindowRect);
         windowHeight = mainWindowRect.bottom - mainWindowRect.top;
         windowWidth = mainWindowRect.right - mainWindowRect.left;

         ClientToScreen(hWnd, &pos);
         MoveWindow(hWnd, pos.x, pos.y, windowWidth, windowHeight, TRUE);
     }
     break;

The WM_MOUSEMOVE message is sent whenever the mouse moves inside our window, but as we have captured the mouse, it would be recieved irrespective of mouse position. The lParam variable contains the new ‘X’ and ‘Y’ value of the cursor as the LOWORD and the HIWORD. The position returned is in the client space and not in screen space. To convert this position to actual screen space co-ordinates, we use the ClientToScreen(HWND, *RECT) function.  Once we have this information, we calculate the window widht and height.

Uptill now we haven’t moved the window. To move the window, we invoke the MoveWindow() API.  The function takes six parameters, the window handle which we are moving, new x and y co-ordinates, new width and height. The last variable indicates whether we want to refresh the window while moving. If it is set to TRUE, then the function will send out a WM_PAINT message, otherwise we will have to explicitly redraw the window after MoveWindow.

Well that is about it. Please do leave a comment if you find this useful or if there is anything missing.