SPINCUBE.C
/******************************************************************************\ 
* 
*  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; 
}