/******************************************************************************\
*
* MODULE: SPINCUBE.C
*
*
* PURPOSE: To provide a generic Windows NT dynamic link library
* sample demonstrating the use of DLL entry points, exported
* variables, using C runtime in a DLL, etc...
*
* This module also provides a functional example of how
* to create a custom control library which may be used by
* applications (i.e. SPINTEST.EXE) and the Dialog Editor.
*
*
* FUNCTIONS: DllMain() - Registers spincube class when a
* process loads this DLL.
* CustomControlInfoA() - Called by DLGEDIT to initialize
* a CCINFO structure(s).
* SpincubeStyle() - Brings up dialog box which allows
* user to modify control style.
* SpincubeSizeToText() - Called by DLGEDIT if user requests
* that control be sized to fit text.
* SpincubeWndProc() - Window procedure for spincube
* control.
* SpincubeDlgProc() - Procedure for control style dialog.
*
*
* COMMMENTS: The dialog editor interface has changed since Win 3.0.
* Recommend browsing the NT CUSTCNTL.H file to get an
* idea of the new interface.
*
*
* Microsoft Developer Support
* Copyright 1992 - 1998 Microsoft Corporation
*
\******************************************************************************/
#include <windows.h>
#include <stdlib.h>
#include "spincube.h"
//
// function prototype for C runtime initialization routine
//
BOOL WINAPI _CRT_INIT (HINSTANCE hDLL, DWORD dwReason, LPVOID lpReserved);
//
// function prototype for Paint() in PAINT.C
//
void Paint (HWND);
//
// function prototype for looking up string resources
//
LPTSTR GetStringRes (int);
//
// Declared below are the module's 2 exported variables.
//
// giNumSpincubesThisProcess is an instance variable that contains
// the number of (existing) Spincube controls created by the
// current process.
//
// giNumSpincubesAllProcesses is a shared (between processes) variable
// which contains the total number of (existing) Spincube controls
// created by all processes in the system.
//
//
int giNumSpincubesThisProcess = 0;
#pragma data_seg(".MYSEG")
int giNumSpincubesAllProcesses = 0;
#pragma data_seg()
//
// Some global vars for this module
//
HANDLE ghMod; // DLL's module handle
LPCCSTYLE gpccs; // global pointer to a CCSTYLE structure
CCSTYLEFLAGA aSpincubeStyleFlags[] = { { SS_ERASE, 0, "SS_ERASE" },
{ SS_INMOTION, 0, "SS_INMOTION" } };
/******************************************************************************\
*
* FUNCTION: DllMain
*
* INPUTS: hDLL - DLL module handle
* dwReason - reason being called (e.g. process attaching)
* lpReserved - reserved
*
* RETURNS: TRUE if initialization passed, or
* FALSE if initialization failed.
*
* COMMENTS: On DLL_PROCESS_ATTACH registers the SPINCUBECLASS
*
* DLL initialization serialization is guaranteed within a
* process (if multiple threads then DLL entry points are
* serialized), but is not guaranteed across processes.
*
* When synchronization objects are created, it is necesaary
* to check the return code of GetLastError even if the create
* call succeeded. If the object existed, ERROR_ALREADY_EXISTED
* will be returned.
*
* If your DLL uses any C runtime functions then you should
* always call _CRT_INIT so that the C runtime can initialize
* itself appropriately. Failure to do this may result in
* indeterminate behavior. When the DLL entry point is called
* for DLL_PROCESS_ATTACH & DLL_THREAD_ATTACH circumstances,
* _CRT_INIT should be called before any other initilization
* is performed. When the DLL entry point is called for
* DLL_PROCESS_DETACH & DLL_THREAD_DETACH circumstances,
* _CRT_INIT should be called after all cleanup has been
* performed, i.e. right before the function returns.
*
\******************************************************************************/
BOOL WINAPI DllMain (HANDLE hDLL, DWORD dwReason, LPVOID lpReserved)
{
ghMod = hDLL;
switch (dwReason)
{
case DLL_PROCESS_ATTACH:
{
WNDCLASS wc;
if (!_CRT_INIT (hDLL, dwReason, lpReserved))
return FALSE;
wc.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS | CS_OWNDC |
CS_GLOBALCLASS ;
wc.lpfnWndProc = (WNDPROC) SpincubeWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = SPINCUBE_EXTRA;
wc.hInstance = hDLL;
wc.hIcon = NULL;
wc.hCursor = LoadCursor (NULL, IDC_ARROW);
wc.hbrBackground = NULL;
wc.lpszMenuName = (LPSTR) NULL;
wc.lpszClassName = (LPSTR) SPINCUBECLASS;
if (!RegisterClass (&wc))
{
MessageBox (NULL,
GetStringRes (IDS_REGCLASSFAIL),
(LPCTSTR) "SPINCUBE.DLL",
MB_OK | MB_ICONEXCLAMATION);
return FALSE;
}
break;
}
case DLL_PROCESS_DETACH:
{
if (!_CRT_INIT (hDLL, dwReason, lpReserved))
return FALSE;
if (!UnregisterClass ((LPSTR) SPINCUBECLASS, hDLL ))
{
MessageBox (NULL,
GetStringRes (IDS_UNREGFAIL),
(LPCTSTR) "SPINCUBE.DLL",
MB_OK | MB_ICONEXCLAMATION);
return FALSE;
}
break;
}
default:
if (!_CRT_INIT (hDLL, dwReason, lpReserved))
return FALSE;
break;
}
return TRUE;
}
/******************************************************************************\
*
* FUNCTION: CustomControlInfoA
*
* INPUTS: acci - pointer to an array od CCINFOA structures
*
* RETURNS: Number of controls supported by this DLL
*
* COMMENTS: See CUSTCNTL.H for more info
*
\******************************************************************************/
UINT CALLBACK CustomControlInfoA (LPCCINFOA acci)
{
//
// Dlgedit is querying the number of controls this DLL supports, so return 1.
// Then we'll get called again with a valid "acci"
//
if (!acci)
return 1;
//
// Fill in the constant calues.
//
acci[0].flOptions = 0;
acci[0].cxDefault = 40; // default width (dialog units)
acci[0].cyDefault = 40; // default height (dialog units)
acci[0].flStyleDefault = WS_CHILD |
WS_VISIBLE |
SS_INMOTION;
acci[0].flExtStyleDefault = 0;
acci[0].flCtrlTypeMask = 0;
acci[0].cStyleFlags = NUM_SPINCUBE_STYLES;
acci[0].aStyleFlags = aSpincubeStyleFlags;
acci[0].lpfnStyle = SpincubeStyle;
acci[0].lpfnSizeToText = SpincubeSizeToText;
acci[0].dwReserved1 = 0;
acci[0].dwReserved2 = 0;
//
// Copy the strings
//
// NOTE: MAKE SURE THE STRINGS COPIED DO NOT EXCEED THE LENGTH OF
// THE BUFFERS IN THE CCINFO STRUCTURE!
//
lstrcpy (acci[0].szClass, SPINCUBECLASS);
lstrcpy (acci[0].szDesc, SPINCUBEDESCRIPTION);
lstrcpy (acci[0].szTextDefault, SPINCUBEDEFAULTTEXT);
//
// Return the number of controls that the DLL supports
//
return 1;
}
/******************************************************************************\
*
* FUNCTION: SpincubeStyle
*
* INPUTS: hWndParent - handle of parent window (dialog editor)
* pccs - pointer to a CCSTYLE structure
*
* RETURNS: TRUE if success,
* FALSE if error occured
*
* LOCAL VARS: rc - return code from DialogBox
*
\******************************************************************************/
BOOL CALLBACK SpincubeStyle (HWND hWndParent, LPCCSTYLE pccs)
{
int rc;
gpccs = pccs;
if ((rc = DialogBox (ghMod, "SpincubeStyle", hWndParent,
(DLGPROC)SpincubeDlgProc)) == -1)
{
MessageBox (hWndParent,
GetStringRes (IDS_DLGBOXFAIL),
(LPCTSTR) "Spincube.dll",
MB_OK | MB_ICONEXCLAMATION | MB_APPLMODAL);
rc = 0;
}
return (BOOL) rc;
}
/******************************************************************************\
*
* FUNCTION: SpincubeSizeToText
*
* INPUTS: flStyle - control style
* flExtStyle - control extended style
* hFont - handle of font used to draw text
* pszText - control text
*
* RETURNS: Width (in pixels) control must be to accomodate text, or
* -1 if an error occurs.
*
* COMMENTS: Just no-op here (since we never actually display text in
* the control it doesn't need to be resized).
*
\******************************************************************************/
INT CALLBACK SpincubeSizeToText (DWORD flStyle, DWORD flExtStyle,
HFONT hFont, LPSTR pszText)
{
return -1;
}
/******************************************************************************\
*
* FUNCTION: SpincubeWndProc (standard window procedure INPUTS/RETURNS)
*
* COMMENTS: This is the window procedure for our custom control. At
* creation we alloc a SPINCUBEINFO struct, initialize it,
* and associate it with this particular control. We also
* start a timer which will invalidate the window every so
* often; this causes a repaint, and the cube gets drawn in
* a new position. Left button clicks will turn toggle the
* erase option, causing a "trail" of cubes to be left when
* off. Right button clicks will toggle the motion state of
* the control (and turn the timer on/off).
*
\******************************************************************************/
LRESULT CALLBACK SpincubeWndProc (HWND hwnd, UINT msg, WPARAM wParam,
LPARAM lParam)
{
switch (msg)
{
case WM_CREATE:
{
//
// Alloc & init a SPINCUBEINFO struct for this particular control
//
HDC hdc;
LPCREATESTRUCT lpcs = (LPCREATESTRUCT) lParam;
PSPINCUBEINFO pSCI = (PSPINCUBEINFO) LocalAlloc (LPTR,
sizeof(SPINCUBEINFO));
if (!pSCI)
{
MessageBox (NULL,
GetStringRes (IDS_ALLOCFAIL),
(LPCTSTR) "SPINCUBE.DLL",
MB_OK | MB_ICONEXCLAMATION);
return -1;
}
//
// Alloc the compatible DC for this control.
//
hdc = GetDC (hwnd);
if ((pSCI->hdcCompat = CreateCompatibleDC (hdc)) == NULL)
{
MessageBox (NULL,
GetStringRes (IDS_CREATEDCFAIL),
(LPCTSTR) "SPINCUBE.DLL",
MB_OK | MB_ICONEXCLAMATION);
return -1;
}
ReleaseDC (hwnd, hdc);
//
// Initialize this instance structure
//
pSCI->fCurrentXRotation =
pSCI->fCurrentYRotation =
pSCI->fCurrentZRotation = (float) 0.0;
pSCI->fCurrentXRotationInc =
pSCI->fCurrentYRotationInc =
pSCI->fCurrentZRotationInc = (float) 0.2617; // a random # (15 degrees)
pSCI->iCurrentXTranslation =
pSCI->iCurrentYTranslation =
pSCI->iCurrentZTranslation = 0;
//
// All these calculations so the cubes start out with random movements.
//
if ((pSCI->iCurrentXTranslationInc = (rand() % 10) + 2) > 7)
pSCI->iCurrentXTranslationInc = -pSCI->iCurrentXTranslationInc;
if ((pSCI->iCurrentYTranslationInc = (rand() % 10) + 2) <= 7)
pSCI->iCurrentYTranslationInc = -pSCI->iCurrentYTranslationInc;
if ((pSCI->iCurrentZTranslationInc = (rand() % 10) + 2) > 7)
pSCI->iCurrentZTranslationInc = -pSCI->iCurrentZTranslationInc;
pSCI->rcCubeBoundary.left =
pSCI->rcCubeBoundary.top = 0;
pSCI->rcCubeBoundary.right = lpcs->cx;
pSCI->rcCubeBoundary.bottom = lpcs->cy;
pSCI->iOptions = SPINCUBE_REPAINT_BKGND;
pSCI->hbmCompat = NULL;
SetWindowLong (hwnd, GWL_SPINCUBEDATA, (LONG) pSCI);
SetTimer (hwnd, SPIN_EVENT, SPIN_INTERVAL, NULL);
//
// Increment the count vars
//
giNumSpincubesThisProcess++;
giNumSpincubesAllProcesses++;
break;
}
case WM_PAINT:
Paint (hwnd);
break;
case WM_TIMER:
switch (wParam)
{
case SPIN_EVENT:
{
PSPINCUBEINFO pSCI = (PSPINCUBEINFO) GetWindowLong (hwnd,
GWL_SPINCUBEDATA);
InvalidateRect (hwnd, &pSCI->rcCubeBoundary, FALSE);
break;
}
}
break;
case WM_LBUTTONDBLCLK:
{
//
// Toggle the erase state of the control
//
if (DO_ERASE(hwnd))
SetWindowLong (hwnd, GWL_STYLE,
GetWindowLong (hwnd, GWL_STYLE) & ~SS_ERASE);
else
{
//
// Repaint the entire control to get rid of the (cube trails) mess
//
PSPINCUBEINFO pSCI = (PSPINCUBEINFO) GetWindowLong (hwnd,
GWL_SPINCUBEDATA);
SetWindowLong (hwnd, GWL_STYLE,
GetWindowLong (hwnd, GWL_STYLE) | SS_ERASE);
pSCI->iOptions |= SPINCUBE_REPAINT_BKGND;
InvalidateRect (hwnd, NULL, FALSE);
SendMessage (hwnd, WM_PAINT, 0, 0);
}
break;
}
case WM_RBUTTONDBLCLK:
{
//
// Toggle the motion state of the control
//
if (IN_MOTION(hwnd))
{
KillTimer (hwnd, SPIN_EVENT);
SetWindowLong (hwnd, GWL_STYLE,
GetWindowLong (hwnd, GWL_STYLE) & ~SS_INMOTION);
}
else
{
SetTimer (hwnd, SPIN_EVENT, SPIN_INTERVAL, NULL);
SetWindowLong (hwnd, GWL_STYLE,
GetWindowLong (hwnd, GWL_STYLE) | SS_INMOTION);
}
break;
}
case WM_SIZE:
if (wParam == SIZE_MAXIMIZED || wParam == SIZE_RESTORED)
{
PSPINCUBEINFO pSCI = (PSPINCUBEINFO) GetWindowLong (hwnd,
GWL_SPINCUBEDATA);
//
// Get a new bitmap which is the new size of our window
//
HDC hdc = GetDC (hwnd);
HBITMAP hbmTemp = CreateCompatibleBitmap (hdc,
(int) LOWORD (lParam),
(int) HIWORD (lParam));
if (!hbmTemp)
{
//
// Scream, yell, & committ an untimely demise...
//
MessageBox (NULL,
GetStringRes (IDS_CREATEBITMAPFAIL),
(LPCTSTR) "SPINCUBE.DLL",
MB_OK | MB_ICONEXCLAMATION);
DestroyWindow (hwnd);
}
pSCI->hbmSave = SelectObject (pSCI->hdcCompat, hbmTemp);
if (pSCI->hbmCompat)
DeleteObject (pSCI->hbmCompat);
ReleaseDC (hwnd, hdc);
pSCI->hbmCompat = hbmTemp;
//
// Reset the translation so the cube doesn't go spinning off into
// space somewhere- we'd never see it again!
//
pSCI->iCurrentXTranslation =
pSCI->iCurrentYTranslation =
pSCI->iCurrentZTranslation = 0;
//
// All these calculations so the cube starts out with random movements,
//
if ((pSCI->iCurrentXTranslationInc = (rand() % 10) + 2) > 7)
pSCI->iCurrentXTranslationInc = -pSCI->iCurrentXTranslationInc;
if ((pSCI->iCurrentYTranslationInc = (rand() % 10) + 2) <= 7)
pSCI->iCurrentYTranslationInc = -pSCI->iCurrentYTranslationInc;
if ((pSCI->iCurrentZTranslationInc = (rand() % 10) + 2) > 7)
pSCI->iCurrentZTranslationInc = -pSCI->iCurrentZTranslationInc;
pSCI->rcCubeBoundary.left =
pSCI->rcCubeBoundary.top = 0;
pSCI->rcCubeBoundary.right = (int) LOWORD (lParam);
pSCI->rcCubeBoundary.bottom = (int) HIWORD (lParam);
pSCI->iOptions |= SPINCUBE_REPAINT_BKGND;
InvalidateRect (hwnd, NULL, FALSE);
}
break;
case WM_DESTROY:
{
PSPINCUBEINFO pSCI = (PSPINCUBEINFO) GetWindowLong (hwnd,
GWL_SPINCUBEDATA);
//
// Clean up all the resources used for this control
//
if (IN_MOTION(hwnd))
KillTimer (hwnd, SPIN_EVENT);
SelectObject (pSCI->hdcCompat, pSCI->hbmSave);
DeleteObject (pSCI->hbmCompat);
DeleteDC (pSCI->hdcCompat);
LocalFree (LocalHandle ((LPVOID) pSCI));
//
// Decrement the global count vars
//
giNumSpincubesThisProcess--;
giNumSpincubesAllProcesses--;
break;
}
default:
return (DefWindowProc(hwnd, msg, wParam, lParam));
}
return ((LONG) TRUE);
}
/******************************************************************************\
*
* FUNCTION: SpincubeDlgProc (standard dialog procedure INPUTS/RETURNS)
*
* COMMENTS: This dialog comes up in response to a user requesting to
* modify the control style. This sample allows for changing
* the control's text, and this is done by modifying the
* CCSTYLE structure pointed at by "gpccs" (a pointer
* that was passed to us by dlgedit).
*
\******************************************************************************/
LRESULT CALLBACK SpincubeDlgProc (HWND hDlg, UINT msg, WPARAM wParam,
LPARAM lParam)
{
switch (msg)
{
case WM_INITDIALOG :
{
if (gpccs->flStyle & SS_ERASE)
CheckDlgButton (hDlg, DID_ERASE, 1);
if (gpccs->flStyle & SS_INMOTION)
CheckDlgButton (hDlg, DID_INMOTION, 1);
break;
}
case WM_COMMAND:
switch (LOWORD(wParam))
{
case DID_ERASE:
if (IsDlgButtonChecked (hDlg, DID_ERASE))
gpccs->flStyle |= SS_ERASE;
else
gpccs->flStyle &= ~SS_ERASE;
break;
case DID_INMOTION:
if (IsDlgButtonChecked (hDlg, DID_INMOTION))
gpccs->flStyle |= SS_INMOTION;
else
gpccs->flStyle &= ~SS_INMOTION;
break;
case DID_OK:
EndDialog (hDlg, 1);
break;
}
break;
}
return FALSE;
}
/******************************************************************************\
*
* FUNCTION: GetStringRes (int id INPUT ONLY)
*
* COMMENTS: Load the resource string with the ID given, and return a
* pointer to it. Notice that the buffer is common memory so
* the string must be used before this call is made a second time.
*
\******************************************************************************/
LPTSTR GetStringRes (int id)
{
static TCHAR buffer[MAX_PATH];
buffer[0]=0;
LoadString (GetModuleHandle (NULL), id, buffer, MAX_PATH);
return buffer;
}