Matt Gerrans
Posts: 1153
Nickname: matt
Registered: Feb, 2002
|
|
Re: How is this executed?
|
Posted: Apr 24, 2002 5:00 PM
|
|
It is a funny thing -- programming in languages like Java remove you from a lot of the "bare metal" stuff that goes on below, but you have to kind of accept these kind of mysteries. Even in C++, using MFC or Borland's C++ Builder, you are kind of removed from the lower-level things (unless you choose to seek them out).
Even though writing a Windows program in the old-fashioned bare-bones C API is a lot more painful than using any of the modern tools and techniques, it does allow you to better understand what is going on. What you do is this: you write a main function (called WinMain()). In this, you set up some data structures and whatnot and register a callback function (pointer to a function in C, analogous to passing an implementation of some interface in Java or a delegate in C#). Then you start a (kind of) infinite loop where you call GetMessage() and DispatchMessage(), like this:
// The event handler callback.
WindowEventHandler( HWND windowHandle, Event event... )
{
switch( event )
{
case WM_RESIZE:
recalcControPositions();
break;
//...
}
}
// The main entry point.
WinMain()
{
WNDCLASS windowClass;
windowClass.lpfnWndProc = WindowEventHandler;
HWND windowHandle = CreateWindow( &windowClass, ... );
while( GetMessage( &msg, ... ) )
{
DispatchMessage( &msg );
}
}
(for those old Windows programmers, yes I know there is a TranslateMessage() in there, but I've omitted it for simplicity. You'll also notice that this is very much pseudo code and paraphrased.)
Since you've already registered your callback, you can imagine that it works kind of like this: - GetMessage() looks at the various input (mouse, keyboard, modem, etc.) devices and the system for messages and puts them in a queue. If the next message is anything other than "quit" it will return "true" (meaning "keep on running") and put the message in the msg parameter. - Inside the loop, you call DispatchMessage(), which among other things, will call into your callback function with the message.
This is probably clear as mud, but the idea is that you can see how the thread of execution goes back and forth between your code and the Windows code. If you never called DispatchMessage(), your callback would never get called.
Similar things happen in a Java program, it is just that you are more insulated from it. I don't know for sure about the Java internals, but I'd guess that when you create an Applet or JFrame, it starts up all this stuff in a new Thread in its constructor and that Thread's run() contains some sort of message loop. Instead of calling registered callbacks, it just calls its own methods (like paint()), which can be overridden by classes which extend it.
Here is the source of a raw Windows program, for complete accuracy:
//--------------------------------------------------------------------
// Matt Gerrans
// December 1997
//
// Skeleton app with Tray Icon capability.
//--------------------------------------------------------------------
#include <shlobj.h>
#include "Skeleton.h"
#include <windowsx.h>
#include "resource.h"
//--------------------------------------------------------------------
// Local functions:
//--------------------------------------------------------------------
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpszCmdLine, int nCmdShow);
long FAR PASCAL MainWindowMessageHandler( HWND, UINT, UINT, LONG );
#define MYWM_NOTIFYICON (WM_APP+100)
//--------------------------------------------------------------------
// Globals (ugh!):
//--------------------------------------------------------------------
HINSTANCE _hinst;
static char _pszIniFile[MAXLEN+1];
static UINT _nOptions;
static char _pszAppName[MAXLEN+1];
//--------------------------------------------------------------------
// WinMain()
//--------------------------------------------------------------------
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, //)
LPSTR lpszCmdLine, int nCmdShow)
{
MSG msg;
HWND hwnd;
WNDCLASS wndclass;
_hinst = hInstance;
ProcessCommandLine( lpszCmdLine );
_hinst = hInstance; // Is there a better way???
// Yes there is a better way: pack in in the window extra data!
LoadString( _hinst, IDS_APPNAME, _pszAppName, MAXLEN );
// Prevent multiple instances:
HANDLE hMutex = CreateMutex( NULL, TRUE, _pszAppName );
if( GetLastError() == ERROR_ALREADY_EXISTS )
{
// Could show an "I'm already running" message here.
return 0;
}
if (!hPrevInstance)
{
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = MainWindowMessageHandler;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = _hinst;
wndclass.hIcon = LoadIcon(_hinst, _pszAppName);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName = _pszAppName;
wndclass.lpszClassName = _pszAppName;
RegisterClass(&wndclass) ;
}
hwnd = CreateWindow( _pszAppName, _pszAppName,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, _hinst, NULL) ;
// ShowWindow(hwnd, SW_HIDE ) ; // mfg: Invisible app!
UpdateWindow(hwnd);
while( GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg) ;
DispatchMessage(&msg) ;
}
ReleaseMutex( hMutex ); // Important!
CloseHandle( hMutex ); // This one is not strictly necessary as the mutex
// will be automatically closed upon the app's
// termination.
return msg.wParam;
}
//--------------------------------------------------------------------
// MainWindowMessageHandler()
//
// A well-designed message-handler function is lean and mean; it
// calls other functions and procedures to do most work. This way,
// it doesn't become too large and cluttered.
//--------------------------------------------------------------------
long FAR PASCAL MainWindowMessageHandler( HWND hwnd, UINT wMessage, //)
UINT wParam, LONG lParam )
{
#ifdef _DEBUG
char sMsg[256];
#endif
switch(wMessage)
{
case WM_CREATE:
//_nOptions = GetOptions( _hinst );
//SetupTrayIcon( hwnd, _hinst );
/*
char sMsg[256];
wsprintf( sMsg, "sizeof(WPARAM) is %d, sizeof(LPARAM) is %d",
(int)sizeof(WPARAM), (int)sizeof(LPARAM) );
MessageBox( NULL, sMsg, "Info", MB_OK|MB_SYSTEMMODAL);
// ...Both are 4 bytes!!!
*/
return 0;
case WM_COMMAND:
switch(wParam)
{
default:
break;
}
break;
case MYWM_NOTIFYICON:
switch (lParam)
{
case WM_LBUTTONDBLCLK:
break;
case WM_LBUTTONDOWN:
break;
case WM_RBUTTONDOWN:
// *** Show the menu! ***
break;
case WM_RBUTTONDBLCLK:
break;
default:
break;
}
break;
case WM_CLOSE:
if( CloseConfirmed(_hinst) )
{
DestroyWindow(hwnd);
return 0;
}
else
return 0;
case WM_DESTROY:
//RemoveIconFromTray( hwnd );
PostQuitMessage(0);
return 0;
}
return DefWindowProc( hwnd, wMessage, wParam, lParam );
}
//--------------------------------------------------------------------
// SetupTrayIcon()
//--------------------------------------------------------------------
void SetupTrayIcon( HWND hwnd, HINSTANCE hinst )
{
NOTIFYICONDATA icondata;
icondata.cbSize = sizeof(NOTIFYICONDATA);
icondata.hWnd = hwnd;
icondata.uID = IDI_SKELETON;
icondata.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
icondata.uCallbackMessage = MYWM_NOTIFYICON;
icondata.hIcon = LoadIcon( hinst, _pszAppName );
LoadString( hinst, IDS_TOOLTIP, icondata.szTip, MAXLEN );
Shell_NotifyIcon( NIM_ADD, &icondata );
// What's this?
if( icondata.hIcon )
DestroyIcon( icondata.hIcon );
}
//--------------------------------------------------------------------
// RemoveIconFromTray()
//--------------------------------------------------------------------
void RemoveIconFromTray( HWND hwnd )
{
NOTIFYICONDATA icondata;
// Delete the icon from the tray!
icondata.cbSize = sizeof(NOTIFYICONDATA);
icondata.hWnd = hwnd;
icondata.uID = IDI_SKELETON;
Shell_NotifyIcon( NIM_DELETE, &icondata) ;
}
//--------------------------------------------------------------------
// GetOptions()
//--------------------------------------------------------------------
UINT GetOptions( HINSTANCE hinst, SAppOptions & sAppOptions )
{
char pszSection[MAXLEN+1],
// pszItem[MAXLEN+1],
pszIniFile[MAXLEN+1];
GetIniFile( hinst, pszIniFile );
LoadString( hinst, IDS_APPINFO_SECTION, pszSection, MAXLEN );
// for each option...
//LoadString( hinst, IDS_OPTION, pszItem, MAXLEN );
//GetPrivateProfileInt( pszSection, pszItem, 0, pszIniFile );
return 0;
}
//--------------------------------------------------------------------
// SetOptions()
//--------------------------------------------------------------------
void SetOptions( HINSTANCE hinst, SAppOptions sAppOptions )
{
char pszSection[MAXLEN+1],
// pszItem[MAXLEN+1],
// pszOptions[20],
pszIniFile[MAXLEN+1];
GetIniFile( hinst, pszIniFile );
LoadString( hinst, IDS_APPINFO_SECTION, pszSection, MAXLEN );
// for each option...
//LoadString( hinst, IDS_TRAY, pszItem, MAXLEN );
//WritePrivateProfileString( pszSection, pszItem, pszOptions, pszIniFile );
}
//--------------------------------------------------------------------
// GetIniFile()
//
// If there is a command line specifying an ini file, it will be used.
// Otherwise it is assumed that the ini file will be in the same
// directory as the exe and will have the same filename with an
// extension of "INI" rather than "EXE." Yes, that does mean that
// if you rename "SKELETON.EXE," to "BUD.EXE," it will look for
// "BUD.INI." This nifty feature allows you to run multple icons
// in the tray from the same directory without specifying command
// line parameters.
//--------------------------------------------------------------------
void GetIniFile( HINSTANCE hinst, char * pszIniFile )
{
if( strlen( _pszIniFile ) )
strcpy( pszIniFile, _pszIniFile );
else
{
// Get the ini file name with these assumptions:
// 1. The ini file is in the same directory as the program.
// 2. The ini files has the same name as the exe ("Skeleton" -
// unless it was renamed).
GetModuleFileName( hinst, pszIniFile, MAXLEN );
}
// Now, change the EXE extension to INI:
strcpy( pszIniFile+strlen(pszIniFile)-3, "INI" );
#ifdef _DEBUG
MessageBox( NULL, pszIniFile, "Here's the ini file name:", MB_OK );
#endif
}
//--------------------------------------------------------------------
// CloseConfirmed()
//--------------------------------------------------------------------
BOOL CloseConfirmed( HINSTANCE hinst )
{
char pszAreYouSure[MAXLEN+1],
pszTitle[MAXLEN+1];
UINT uResponse = IDNO,
uMsgOptions = MB_YESNO | MB_ICONQUESTION | MB_SYSTEMMODAL;
LoadString( hinst, IDS_CONFIRMCLOSE, pszAreYouSure, MAXLEN );
LoadString( hinst, IDS_CLOSETITLE, pszTitle, MAXLEN );
uResponse = MessageBox( NULL, pszAreYouSure, pszTitle, uMsgOptions );
return (uResponse == IDYES ? TRUE : FALSE );
}
//--------------------------------------------------------------------
// ProcessCommandLine()
//--------------------------------------------------------------------
int ProcessCommandLine( const char * lpszCmdLine )
{
char sCmdLine[MAXLEN+1], * pCmd;
strcpy( sCmdLine, lpszCmdLine );
pCmd = strtok( sCmdLine, " " );
while( pCmd )
{
// Build an array of command line items:
// MyStringArrary += pCmd;
pCmd = strtok( NULL, " " );
}
// Process the array...
return 0;
}
|
|