mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2403 lines
73 KiB
2403 lines
73 KiB
/** FILE: control.c ******** Module Header ********************************
|
|
*
|
|
* Control Panel main window functions and procedures.
|
|
*
|
|
* History:
|
|
* 12:30 on Tues 23 Apr 1991 -by- Steve Cathcart [stevecat]
|
|
* Took base code from Win 3.1 source
|
|
* 10:30 on Tues 04 Feb 1992 -by- Steve Cathcart [stevecat]
|
|
* Updated code to latest Win 3.1 sources
|
|
* 21 Sept 1993 -by- Steve Cathcart [stevecat]
|
|
* Added Caching of Control Panel modules
|
|
*
|
|
*
|
|
* Copyright (C) 1990-1993 Microsoft Corporation
|
|
*
|
|
*************************************************************************/
|
|
//==========================================================================
|
|
// Include files
|
|
//==========================================================================
|
|
// Windows SDK
|
|
#include <windows.h>
|
|
#include <shellapi.h>
|
|
|
|
// Application specific
|
|
#include "cpl.h"
|
|
#include "lb.h"
|
|
#include "cpl_defs.h"
|
|
#include "uniconv.h"
|
|
|
|
//==========================================================================
|
|
// Local Definitions
|
|
//==========================================================================
|
|
#define ItemHeight (iIconH + ifontH + (ifontH>>1))
|
|
|
|
#define MAX_APPLET_NAME_LEN 32
|
|
|
|
// Private Control Panel message. This works exactly the same way as the
|
|
// WM_CPL_LAUNCH message except the wParam is ProcessId instead of an HWND.
|
|
|
|
#define WM_CPL_LAUNCHEX (WM_CPL_LAUNCH-1)
|
|
|
|
typedef INT (APIENTRY *SHELL_ABOUT)(HWND hWnd, LPTSTR szApp,
|
|
LPTSTR szOtherStuff, HICON hIcon);
|
|
|
|
//==========================================================================
|
|
// External Declarations
|
|
//==========================================================================
|
|
// External functions
|
|
|
|
extern BOOL bAppletActive;
|
|
extern BOOL bRebuildCache;
|
|
|
|
//==========================================================================
|
|
// Local Data Declarations
|
|
//==========================================================================
|
|
TCHAR aszControlIni[] = TEXT("control.ini");
|
|
TCHAR aszControlIniPath[MAX_PATH];
|
|
|
|
TCHAR szMAINCPL[] = TEXT("MAIN.CPL");
|
|
TCHAR szClass[] = TEXT("CtlPanelClass"); // some apps depend on this!
|
|
TCHAR szMMCPL[] = TEXT("MMCPL");
|
|
TCHAR szNULL[] = TEXT("");
|
|
TCHAR szCPL[] = TEXT("*.CPL");
|
|
TCHAR szDots[] = TEXT("...");
|
|
TCHAR szStatic[] = TEXT("static");
|
|
TCHAR szHelpFile[] = TEXT("control.hlp");
|
|
TCHAR szText[] = TEXT("Text");
|
|
TCHAR cBackslash = TEXT('\\');
|
|
TCHAR szlistbox[] = TEXT("lb");
|
|
TCHAR szMaxWidth[] = TEXT("MaxWidth");
|
|
|
|
TCHAR szErrorText[1024]; // Used in ASSERT and Debug macros
|
|
|
|
BYTE szCPlApplet[] = "CPlApplet"; // procedure name
|
|
|
|
LPTSTR pStrings[NUMCPLSTRINGS];
|
|
|
|
TCHAR szUsername[MAX_PATH] = TEXT("GetUserName failed::");
|
|
|
|
HINSTANCE hCPlInst; // MMCPL instance handle
|
|
HWND hCPlLB, hCPlTB; // list box and text box handles
|
|
HFONT hCPlFont; // handle to MMCPl font for applet names
|
|
HBRUSH hCPlBk; // handle to MMCPl background brush
|
|
PCPLMODULE CPlMods[CPL_MAXMODS]; // array of ptrs to CPLMODULE struct
|
|
HWND hCPlWnd = 0; // MMCPL main window handle
|
|
RECT CPlRect; // last valid window x,y,w,h
|
|
WORD numApps = 0; // number of lData's in lCPlApps[]
|
|
int ifontH; // height of font
|
|
int iIconX; // X offset to draw icons
|
|
int iIconY; // Y offset to draw icons
|
|
int iIconW; // icon width in pixels
|
|
int iIconH; // icon height " "
|
|
int iWidth = 62; // width of the owner-draw columns
|
|
TCHAR *szWndDim[4] = { TEXT("X"), TEXT("Y"), TEXT("W"), TEXT("H") };
|
|
TCHAR szNumApps[] = TEXT("NumApps");
|
|
UINT wHelpMessage; // stuff for help
|
|
WORD wMenuID = 0;
|
|
DWORD dwMenuBits = 0L;
|
|
HHOOK hhkMsgFilter = NULL;
|
|
HWND hSetup = 0; // means we are running under setup
|
|
BOOL bSetup = FALSE; // means we were run by Setup
|
|
|
|
|
|
//==========================================================================
|
|
// Local Function Prototypes
|
|
//==========================================================================
|
|
LRESULT APIENTRY fnText (HWND hWnd, UINT message, WPARAM wParam, LONG lParam);
|
|
LRESULT APIENTRY CPlWndProc (HWND hWnd, UINT message, WPARAM wParam, LONG lParam);
|
|
|
|
|
|
BOOL AlreadyLoaded (HANDLE, int);
|
|
void AskApps (PCPLMODULE, int);
|
|
int ChngSelApp (int, TCHAR);
|
|
void CPHelp (HWND, LPTSTR, DWORD);
|
|
BOOL CreatChildWindows (void);
|
|
int FindApplet (LPTSTR);
|
|
BOOL GetCPLStrings (void);
|
|
void GetCPlWndPos (int *);
|
|
HWND GetRealParent (HWND);
|
|
BOOL LoadApplets (void);
|
|
void MakeWinPath (LPTSTR, LPTSTR);
|
|
int MessageFilter (int, DWORD, LPMSG);
|
|
BOOL RunCommandLineApplet (int, LPTSTR, LPTSTR, HANDLE);
|
|
PCPLAPPLET SendAppMsg (int, DWORD);
|
|
void SetAppItems (PCPLMODULE, LPCPLINFO, LPNEWCPLINFO, PCPLAPPLET, int);
|
|
void SetCPlWndPos (HWND);
|
|
void ConvertCplInfoA (LPNEWCPLINFO);
|
|
|
|
//==========================================================================
|
|
// Functions
|
|
//==========================================================================
|
|
|
|
void ConvertCplInfoA (LPNEWCPLINFO lpCPlInfoW)
|
|
{
|
|
NEWCPLINFOA CplInfoA;
|
|
|
|
memcpy ((LPBYTE) &CplInfoA, lpCPlInfoW, sizeof(NEWCPLINFOA));
|
|
MultiByteToWideChar (CP_ACP, MB_PRECOMPOSED, CplInfoA.szName, 32,
|
|
(LPWSTR) lpCPlInfoW->szName, 32);
|
|
MultiByteToWideChar (CP_ACP, MB_PRECOMPOSED, CplInfoA.szInfo, 64,
|
|
(LPWSTR) lpCPlInfoW->szInfo, 64);
|
|
MultiByteToWideChar (CP_ACP, MB_PRECOMPOSED, CplInfoA.szHelpFile, 128,
|
|
(LPWSTR) lpCPlInfoW->szHelpFile, 128);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// -added- 4/13/91 to strip the "&" because DrawText with CALCRECT
|
|
// doesn't seem to work -jyg-
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
int GetNameExtent(HDC hDC, LPTSTR szName)
|
|
{
|
|
TCHAR szBuffer[128];
|
|
LPTSTR pstr;
|
|
SIZE Size;
|
|
|
|
pstr = szBuffer;
|
|
|
|
#ifdef JAPAN // Apr-23-1993 MSKK [ShigeO]
|
|
do
|
|
{
|
|
if (*szName == TEXT('(') && *(szName+1) == TEXT('&') && *(szName+3) == TEXT(')'))
|
|
szName += 4;
|
|
}
|
|
#else
|
|
do
|
|
{
|
|
if (*szName == TEXT('&'))
|
|
szName++;
|
|
}
|
|
#endif
|
|
while (*pstr++ = *szName++);
|
|
|
|
GetTextExtentPoint(hDC, szBuffer, lstrlen(szBuffer), &Size);
|
|
|
|
return (int)Size.cx;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// -added- 4/16/91 If someone has an applet without an accelerator we
|
|
// add it to the first character -jyg-
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
void ScanName(LPTSTR szName)
|
|
{
|
|
TCHAR szBuffer[128];
|
|
LPTSTR pstr1 = szName, pstr2 = szBuffer;
|
|
|
|
while (*pstr1)
|
|
{
|
|
if (*pstr1 == TEXT('&'))
|
|
{
|
|
if (*(pstr1+1) != TEXT('&'))
|
|
return;
|
|
if (*(pstr1+1) == TEXT('&'))
|
|
pstr1++;
|
|
}
|
|
pstr1++;
|
|
}
|
|
|
|
// No ampersand! Tack one on the first char
|
|
|
|
szBuffer[0] = TEXT('&');
|
|
szBuffer[1] = TEXT('\0');
|
|
lstrcat(szBuffer, szName);
|
|
lstrcpy(szName, szBuffer);
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Init the icon handle, and ptrs to the strings.
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
void SetAppItems(PCPLMODULE pCPlMod, LPCPLINFO pCPlInfo, LPNEWCPLINFO pNewCPlInfo, PCPLAPPLET pCPlApp, int iIndex)
|
|
{
|
|
HANDLE hLib;
|
|
HMENU hMenu;
|
|
HDC hDC;
|
|
HFONT hFont;
|
|
int numChars;
|
|
TCHAR szBuffer[128];
|
|
TCHAR szTemp[10];
|
|
LPNTCPL lpNtCPl;
|
|
|
|
hLib = pCPlMod->hLibrary;
|
|
|
|
// now the help, info and icon stuff
|
|
|
|
if (pNewCPlInfo)
|
|
{
|
|
pCPlApp->lData = pNewCPlInfo->lData;
|
|
pCPlApp->dwContext = pNewCPlInfo->dwHelpContext;
|
|
if (pNewCPlInfo->szHelpFile)
|
|
{
|
|
pCPlApp->pszHelpFile = (LPTSTR) LocalAlloc (LPTR, ByteCountOf(lstrlen(pNewCPlInfo->szHelpFile)+1));
|
|
if (pCPlApp->pszHelpFile)
|
|
lstrcpy(pCPlApp->pszHelpFile, pNewCPlInfo->szHelpFile);
|
|
}
|
|
pCPlApp->hIcon = pNewCPlInfo->hIcon;
|
|
lstrcpy(szBuffer, pNewCPlInfo->szInfo);
|
|
numChars = lstrlen(szBuffer);
|
|
}
|
|
else
|
|
{
|
|
pCPlApp->lData = pCPlInfo->lData;
|
|
pCPlApp->dwContext = 0L;
|
|
pCPlApp->pszHelpFile = NULL;
|
|
|
|
/* get the text for the applet's description */
|
|
pCPlApp->hIcon = LoadIcon (hLib, (LPTSTR) MAKEINTRESOURCE(pCPlInfo->idIcon));
|
|
numChars = LoadString (hLib, pCPlInfo->idInfo, szBuffer, CharSizeOf(szBuffer));
|
|
}
|
|
|
|
if (pCPlApp->pszInfo = (LPTSTR) LocalAlloc (LPTR, ByteCountOf(numChars+1)))
|
|
lstrcpy(pCPlApp->pszInfo, szBuffer);
|
|
|
|
/* get the text for the applet's name */
|
|
if (pNewCPlInfo)
|
|
{
|
|
lstrcpy(szBuffer, pNewCPlInfo->szName);
|
|
numChars = lstrlen(szBuffer);
|
|
}
|
|
else
|
|
{
|
|
numChars = LoadString (hLib, pCPlInfo->idName, szBuffer, CharSizeOf(szBuffer) - 5);
|
|
}
|
|
|
|
if (pCPlApp->pszName = (LPTSTR) LocalAlloc (LPTR, ByteCountOf(numChars+1)))
|
|
{
|
|
LPTSTR pIn, pOut;
|
|
|
|
// Save the complete name of the applet which has the '&' char
|
|
if (pCPlApp->pszFullName = (LPTSTR) LocalAlloc (LPTR, ByteCountOf(numChars+1)))
|
|
lstrcpy (pCPlApp->pszFullName, szBuffer);
|
|
|
|
|
|
pIn = szBuffer;
|
|
pOut = pCPlApp->pszName;
|
|
#ifdef JAPAN /* V-KeijiY July.7.1992 */
|
|
do
|
|
{
|
|
if (*pIn == TEXT('(') && *(pIn+1) == TEXT('&') && *(pIn+3) == TEXT(')'))
|
|
pIn += 3;
|
|
else if (*pIn != TEXT('&'))
|
|
*pOut++ = *pIn;
|
|
} while (*pIn++) ;
|
|
#else
|
|
do
|
|
{
|
|
if (*pIn != TEXT('&'))
|
|
*pOut++ = *pIn;
|
|
} while (*pIn++) ;
|
|
#endif
|
|
|
|
// check to see if this is in the [don't load] section
|
|
// after stripping & junk
|
|
|
|
GetPrivateProfileString(TEXT("don't load"), pCPlApp->pszName, szNULL,
|
|
szTemp, 10, aszControlIniPath);
|
|
if (szTemp[0]) // yep, don't put this in the list
|
|
return;
|
|
}
|
|
|
|
if (!hCPlWnd) // don't add the LB stuff if we aren't showing
|
|
return; // the main window
|
|
|
|
hDC = GetDC (NULL);
|
|
hFont = SelectObject (hDC, hCPlFont);
|
|
|
|
// longest name determines column width
|
|
|
|
/* Get the name length without '&' and adjust the longest name
|
|
Also add & before the first char if there is none -jyg- */
|
|
|
|
if ( (pCPlApp->iNameW = GetNameExtent(hDC, szBuffer)) > iWidth)
|
|
iWidth = pCPlApp->iNameW ;
|
|
|
|
SelectObject (hDC, hFont);
|
|
ReleaseDC (NULL, hDC);
|
|
|
|
ScanName (szBuffer); // adds '&'
|
|
|
|
/* append a new menu item to the Settings pop-up menu */
|
|
lstrcat (szBuffer, szDots);
|
|
hMenu = GetMenu (hCPlWnd);
|
|
hMenu = GetSubMenu (hMenu, 0);
|
|
InsertMenu (hMenu, numApps,
|
|
(!numApps || (numApps & 0x0F)) ?
|
|
MF_STRING|MF_BYPOSITION : MF_STRING|MF_MENUBARBREAK|MF_BYPOSITION,
|
|
numApps+MENU_SETTINGS, szBuffer);
|
|
|
|
numApps++;
|
|
|
|
lpNtCPl = (LPNTCPL) LocalAlloc (LPTR, sizeof(NTCPL));
|
|
|
|
if (lpNtCPl)
|
|
{
|
|
lpNtCPl->pCPlMod = pCPlMod;
|
|
lpNtCPl->iApplet = iIndex;
|
|
SendMessage( hCPlLB, LB_ADDSTRING, 0, (LONG)lpNtCPl);
|
|
}
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Send the inquire messages for each app and initialize necessary
|
|
// structs for them.
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
void AskApps(PCPLMODULE pCPlMod, int numApplets)
|
|
{
|
|
CPLINFO CPlInfo;
|
|
NEWCPLINFO NewCPlInfo;
|
|
PCPLAPPLET pCPlApp;
|
|
int i;
|
|
|
|
for (i = 0, pCPlApp = pCPlMod->pCPlApps; i < numApplets; i++, pCPlApp++)
|
|
{
|
|
// try the new method first
|
|
|
|
NewCPlInfo.dwSize = 0L;
|
|
NewCPlInfo.dwFlags = 0L;
|
|
(*pCPlMod->lpfnCPlApplet)(hCPlWnd,
|
|
CPL_NEWINQUIRE,
|
|
(LONG)i,
|
|
(LONG)&NewCPlInfo);
|
|
|
|
if (NewCPlInfo.dwSize == sizeof(NEWCPLINFOA))
|
|
{
|
|
ConvertCplInfoA (&NewCPlInfo);
|
|
SetAppItems(pCPlMod, NULL, &NewCPlInfo, pCPlApp, i);
|
|
}
|
|
else if (NewCPlInfo.dwSize == sizeof(NEWCPLINFO))
|
|
{
|
|
SetAppItems(pCPlMod, NULL, &NewCPlInfo, pCPlApp, i);
|
|
}
|
|
else
|
|
{
|
|
// old method
|
|
(*pCPlMod->lpfnCPlApplet)(hCPlWnd, CPL_INQUIRE, (LONG)i,
|
|
(LONG)&CPlInfo);
|
|
SetAppItems(pCPlMod, &CPlInfo, NULL, pCPlApp, i);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Returns TRUE if lpszStr and lpszName match
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL NameCmp (LPTSTR lpszStr, LPTSTR lpszName)
|
|
{
|
|
while (*lpszStr)
|
|
{
|
|
if (*lpszName == TEXT('&'))
|
|
lpszName++;
|
|
|
|
// Compare the uppercase of the first character
|
|
|
|
else if ((TCHAR)(ULONG)CharUpper((LPTSTR)(ULONG)(TUCHAR)*(lpszStr++)) !=
|
|
(TCHAR)(ULONG)CharUpper((LPTSTR)(ULONG)(TUCHAR)*(lpszName++)))
|
|
return FALSE;
|
|
}
|
|
return !*lpszName;
|
|
}
|
|
|
|
int FindApplet (LPTSTR szName)
|
|
{
|
|
PCPLMODULE pCPlMod;
|
|
PCPLAPPLET pCPlApp;
|
|
int iApplet;
|
|
int i, n;
|
|
LONG lData;
|
|
|
|
n = (int)SendMessage (hCPlLB, LB_GETCOUNT, 0, 0L);
|
|
|
|
for (i = 0; i < n; i++)
|
|
{
|
|
lData = SendMessage (hCPlLB, LB_GETITEMDATA, i, 0L );
|
|
|
|
iApplet = ((LPNTCPL)lData)->iApplet;
|
|
pCPlMod = ((LPNTCPL)lData)->pCPlMod;
|
|
pCPlApp = pCPlMod ? &pCPlMod->pCPlApps[iApplet] : NULL;
|
|
|
|
if (pCPlApp && NameCmp (szName, pCPlApp->pszName))
|
|
return i;
|
|
}
|
|
|
|
return(-1);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Return whether we already loaded this module.
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL AlreadyLoaded(HANDLE hLib, int numMods)
|
|
{
|
|
int i;
|
|
|
|
for (i = numMods - 1; i >= 0; i--)
|
|
{
|
|
if (CPlMods[i]->hLibrary == hLib)
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Pass initial messages to the indicated module, and fill in the
|
|
// passed CPLMODULE structure.
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL AskModule (LPTSTR name, int numMods)
|
|
{
|
|
TCHAR szLoadingName[64+ CharSizeOf(szLoading)];
|
|
int numApplets;
|
|
BOOL bLoaded=FALSE;
|
|
PCPLMODULE pCPlMod;
|
|
UINT uError;
|
|
|
|
|
|
if (pCPlMod = (PCPLMODULE)LocalAlloc (LPTR, sizeof(CPLMODULE)))
|
|
{
|
|
// Module pathname
|
|
lstrcpy(pCPlMod->szPathname, name);
|
|
|
|
if (hCPlWnd)
|
|
{
|
|
/* update feedback on what applet library is currently loading */
|
|
#ifdef JAPAN /* V-KeijiY July.6.7 */
|
|
lstrcpy(szLoadingName, name);
|
|
lstrcat(szLoadingName, szLoading);
|
|
#else
|
|
lstrcpy(szLoadingName, szLoading);
|
|
lstrcat(szLoadingName, name);
|
|
#endif
|
|
|
|
SetWindowText(hCPlTB, szLoadingName);
|
|
UpdateWindow(hCPlWnd);
|
|
}
|
|
|
|
uError = SetErrorMode (SEM_FAILCRITICALERRORS);
|
|
|
|
if (pCPlMod->hLibrary = LoadLibrary(name))
|
|
{
|
|
if (!AlreadyLoaded(pCPlMod->hLibrary, numMods) &&
|
|
(pCPlMod->lpfnCPlApplet =
|
|
(APPLET_PROC)GetProcAddress(pCPlMod->hLibrary, szCPlApplet))
|
|
&& (*pCPlMod->lpfnCPlApplet)(hCPlWnd, CPL_INIT, 0L, 0L))
|
|
{
|
|
|
|
numApplets = (int)(*pCPlMod->lpfnCPlApplet)(hCPlWnd,
|
|
CPL_GETCOUNT, 0L, 0L);
|
|
pCPlMod->numApplets = numApplets;
|
|
|
|
if (pCPlMod->pCPlApps = (PCPLAPPLET)LocalAlloc(LPTR,
|
|
numApplets*sizeof(CPLAPPLET)))
|
|
{
|
|
AskApps (pCPlMod, numApplets);
|
|
pCPlMod->bLoaded = bLoaded = TRUE;
|
|
CPlMods[numMods] = pCPlMod;
|
|
}
|
|
}
|
|
else
|
|
FreeLibrary(pCPlMod->hLibrary);
|
|
}
|
|
else
|
|
FreeLibrary(pCPlMod->hLibrary);
|
|
|
|
SetErrorMode (uError);
|
|
}
|
|
|
|
if (!bLoaded && pCPlMod)
|
|
LocalFree((HANDLE)pCPlMod);
|
|
|
|
return bLoaded;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Copy pstrFile on top of the filename already attached pstrPath.
|
|
// Length is the length of pstrPath.
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CatPath(LPTSTR pstrPath, LPTSTR pstrFile, int length )
|
|
{
|
|
LPTSTR pstr;
|
|
|
|
for ( pstr = pstrPath + length;
|
|
( pstr >= pstrPath ) && ( *pstr != cBackslash );
|
|
pstr = CharPrev(pstrPath,pstr)
|
|
)
|
|
;
|
|
lstrcpy( pstr+1, pstrFile );
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Return the WIN3 SYSTEM dir concatenated with the past filename.
|
|
// Assumes array size of pstrPath = 256, and pstrFile begins with a '\'.
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
void MakeSysPath (LPTSTR pstrPath, LPTSTR pstrFile)
|
|
{
|
|
int length;
|
|
LPTSTR pstr;
|
|
|
|
length = GetSystemDirectory (pstrPath, 256);
|
|
|
|
pstr = CharPrev (pstrPath, pstrPath+length);
|
|
|
|
if (*pstr != cBackslash )
|
|
*(++pstr) = cBackslash; // add the trailing '\'
|
|
|
|
lstrcpy (pstr+1, pstrFile);
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Return the WIN3 dir concatenated with the past filename.
|
|
// Assumes array size of pstrPath = 256, and pstrFile begins with a '\'.
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
void MakeWinPath (LPTSTR pstrPath, LPTSTR pstrFile)
|
|
{
|
|
int length;
|
|
LPTSTR pstr;
|
|
|
|
length = GetWindowsDirectory (pstrPath, 256);
|
|
|
|
pstr = CharPrev (pstrPath, pstrPath+length);
|
|
|
|
if (*pstr != cBackslash )
|
|
*(++pstr) = cBackslash; // add the trailing '\'
|
|
|
|
lstrcpy (pstr+1, pstrFile);
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Get the keynames under [MMCPL] in CONTROL.INI and cycle
|
|
// through all such keys to load their applets into our
|
|
// list box. Also allocate the array of CPLMODULE structs.
|
|
// Returns early if can't load old WIN3 applets.
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
int LoadApplets()
|
|
{
|
|
LPTSTR pstr;
|
|
int i, numMods = 0;
|
|
TCHAR szKeys[256]; // array size = 256 is assumed by GetSysDir()!
|
|
TCHAR szName[64];
|
|
HANDLE fFile;
|
|
BOOL b;
|
|
WIN32_FIND_DATA FindFileData;
|
|
|
|
// Load in MAIN.CPL first, then search for and load any other applets
|
|
|
|
/* try the SYSTEM directory */
|
|
MakeSysPath (szKeys, szMAINCPL);
|
|
|
|
if (AskModule (szKeys, numMods))
|
|
numMods += 1;
|
|
|
|
// load the modules specified in CONTROL.INI under [MMCPL]
|
|
|
|
GetPrivateProfileString(szMMCPL, NULL, szNULL, szKeys, CharSizeOf(szKeys), aszControlIniPath);
|
|
|
|
for (pstr = szKeys; *pstr && (numMods < CPL_MAXMODS-1); pstr += lstrlen(pstr) + 1)
|
|
{
|
|
GetPrivateProfileString(szMMCPL, pstr, szNULL, szName, 64, aszControlIniPath);
|
|
|
|
if (_tcsicmp(pstr, szNumApps) && _tcsicmp(pstr, szMaxWidth) &&
|
|
!((*(pstr+1) == (TCHAR) 0) && // skip over Wnd size keynames
|
|
((*pstr == *szWndDim[0]) || (*pstr == *szWndDim[1]) ||
|
|
(*pstr == *szWndDim[2]) || (*pstr == *szWndDim[3])) ) &&
|
|
AskModule(szName, numMods))
|
|
numMods += 1; // Increment module count if it loaded ok
|
|
}
|
|
|
|
// load applets from the system directory
|
|
|
|
MakeSysPath (szKeys, szCPL);
|
|
|
|
if ((fFile = FindFirstFile(szKeys, &FindFileData)) != INVALID_HANDLE_VALUE)
|
|
{
|
|
b = TRUE;
|
|
|
|
while (b && (numMods < CPL_MAXMODS - 1))
|
|
{
|
|
// Since we force load of MAIN.CPL first, skip over this
|
|
// file when we find it during the FINDFILE operation.
|
|
|
|
if (_tcsicmp (FindFileData.cFileName, szMAINCPL))
|
|
{
|
|
CatPath(szKeys, FindFileData.cFileName, lstrlen(szKeys));
|
|
if (AskModule(szKeys, numMods))
|
|
numMods++;
|
|
}
|
|
b = FindNextFile(fFile, &FindFileData);
|
|
}
|
|
FindClose(fFile);
|
|
}
|
|
|
|
CPlMods[numMods] = NULL; // NULL terminate CPlMods[]
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
// Now free all .CPL modules until the User asks for one
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
for (i = 0; i < numMods; )
|
|
FreeCachedModule (CPlMods[i++]);
|
|
|
|
return numMods;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Allocate memory and load the strings that CPL must always have around
|
|
// Assumes hCPlInst has already been set.
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL GetCPLStrings(void)
|
|
{
|
|
WORD wString;
|
|
TCHAR szTemp[256];
|
|
|
|
for (wString = 0; wString < NUMCPLSTRINGS; ++wString)
|
|
{
|
|
/* No need to deallocate memory, since it all goes away if we
|
|
* return FALSE
|
|
*/
|
|
if (!LoadString (hCPlInst, INITS+wString, szTemp, CharSizeOf(szTemp)))
|
|
return (FALSE);
|
|
if (!(pStrings[wString] = (LPTSTR) LocalAlloc (LPTR, ByteCountOf(lstrlen(szTemp)+1))))
|
|
return (FALSE);
|
|
lstrcpy (pStrings[wString], szTemp);
|
|
}
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Creates the owner-draw list box for displaying the applet icons,
|
|
// the static text control for the description, and calls routines
|
|
// to load and intialize the applets.
|
|
// Returns successful if was at least able to initialize the old
|
|
// WIN3 applets.
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL CreatChildWindows(void)
|
|
{
|
|
TEXTMETRIC tm;
|
|
HDC hDC;
|
|
HFONT hFont;
|
|
LOGFONT lf; // logical font for Applet name area
|
|
|
|
/* init the height of the Info text box */
|
|
|
|
// this font and brush init stuff should go somewhere else!
|
|
|
|
SystemParametersInfo (SPI_GETICONTITLELOGFONT, sizeof(lf), (PVOID)&lf, FALSE);
|
|
hCPlFont = CreateFontIndirect (&lf);
|
|
|
|
hDC = GetDC (NULL);
|
|
if (hCPlFont)
|
|
hFont = SelectObject (hDC, hCPlFont);
|
|
else
|
|
hFont = NULL;
|
|
GetTextMetrics (hDC, &tm);
|
|
|
|
if (hFont)
|
|
SelectObject(hDC, hFont);
|
|
ReleaseDC (NULL, hDC);
|
|
ifontH = tm.tmHeight + tm.tmExternalLeading;
|
|
|
|
/* create a brush to be used for painting the list box background */
|
|
hCPlBk = CreateSolidBrush (GetSysColor (COLOR_APPWORKSPACE));
|
|
|
|
/* create the list box to hold all the applets */
|
|
hCPlLB = CreateWindow (szlistbox, NULL,
|
|
WS_CHILD | WS_VISIBLE | WS_BORDER |
|
|
WS_HSCROLL| LBS_NOTIFY | LBS_MULTICOLUMN |
|
|
LBS_NOINTEGRALHEIGHT | LBS_OWNERDRAWFIXED |
|
|
LBS_WANTKEYBOARDINPUT,
|
|
0, 0, 0, 0, hCPlWnd, (HMENU)ID_LISTBOX, hCPlInst, NULL);
|
|
|
|
if (!hCPlLB)
|
|
return FALSE;
|
|
|
|
/* create the box at bottom where applet description text is displayed */
|
|
hCPlTB = CreateWindow (szText, szLoading,
|
|
WS_BORDER | SS_LEFT | WS_CHILD | WS_VISIBLE,
|
|
0, 0, 0, 0, hCPlWnd, (HMENU)ID_INFOBOX, hCPlInst, NULL);
|
|
|
|
if (!hCPlTB)
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
int LoadAndSizeApplets(void)
|
|
{
|
|
int iLoaded;
|
|
int nNumApps;
|
|
HCURSOR hCur;
|
|
int iOldWidth;
|
|
TCHAR szTemp[20];
|
|
BOOL bCache = FALSE;
|
|
|
|
hCur = SetCursor(LoadCursor(NULL, IDC_WAIT));
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
// Old way
|
|
// SendMessage (hCPlLB, WM_SETREDRAW, FALSE, 0L);
|
|
///////////////////////////////////////////////////////////////////////
|
|
|
|
// Preliminary setup of values to allow drawing of ICONs in LB
|
|
// as .CPL modules are read in by Control
|
|
//
|
|
// Get stored "MaxWidth" value as a good starting point
|
|
//
|
|
// NOTE: Assumes that iWidth (global) is initialized to a reasonable value
|
|
|
|
iOldWidth = GetPrivateProfileInt(szMMCPL, szMaxWidth, iWidth, aszControlIniPath);
|
|
SendMessage (hCPlLB, LB_SETCOLUMNWIDTH, iOldWidth, 0L);
|
|
|
|
iIconX = (iOldWidth - iIconW) / 2;
|
|
iIconY = ifontH >> 1;
|
|
|
|
InvalidateRect (hCPlTB, NULL, TRUE);
|
|
UpdateWindow (hCPlTB);
|
|
|
|
// Init iWidth global to 1, in order to force a recalc of text widths
|
|
iWidth = 1;
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// Check cache first
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
if (CheckCache())
|
|
{
|
|
bCache = TRUE;
|
|
SendMessage (hCPlLB, WM_SETREDRAW, FALSE, 0L);
|
|
|
|
if (!LoadFromCache())
|
|
{
|
|
FullLoad:
|
|
bCache = FALSE;
|
|
SendMessage (hCPlLB, WM_SETREDRAW, TRUE, 0L);
|
|
|
|
// LoadApplets will recalculate "iWidth" value
|
|
|
|
if ((iLoaded = LoadApplets()) != 0)
|
|
{
|
|
CheckColumnWidth:
|
|
if (iOldWidth != iWidth)
|
|
{
|
|
// Save new MaxWidth value
|
|
wsprintf (szTemp, TEXT("%d"), iWidth);
|
|
WritePrivateProfileString(szMMCPL, szMaxWidth, szTemp, aszControlIniPath);
|
|
|
|
/* Set the column width from the widest name */
|
|
SendMessage (hCPlLB, LB_SETCOLUMNWIDTH, iWidth, 0L);
|
|
iIconX = (iWidth - iIconW) / 2;
|
|
iIconY = ifontH >> 1;
|
|
}
|
|
|
|
// SendMessage (hCPlLB, LB_SETCURSEL, 0, 0L);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
iLoaded = 1;
|
|
|
|
goto CheckColumnWidth;
|
|
}
|
|
}
|
|
else
|
|
goto FullLoad;
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// Check and Update cache as necessary
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
if (!IsCacheValid())
|
|
UpdateCache();
|
|
|
|
|
|
if (!IsIconic(hCPlWnd) &&
|
|
(nNumApps = (int)SendMessage(hCPlLB, LB_GETCOUNT, 0, 0L)) !=
|
|
(int)GetPrivateProfileInt(szMMCPL, szNumApps, 0, aszControlIniPath))
|
|
{
|
|
int nx, nWidth, nMaxWidth;
|
|
int ny, nHeight, nMaxHeight;
|
|
RECT rLB, rCPl;
|
|
|
|
wsprintf(szTemp, TEXT("%d"), nNumApps);
|
|
WritePrivateProfileString(szMMCPL, szNumApps, szTemp,aszControlIniPath);
|
|
|
|
GetClientRect(hCPlLB, &rLB);
|
|
nx = rLB.right / iWidth;
|
|
if (!nx)
|
|
nx = 1;
|
|
ny = (nNumApps + nx - 1) / nx;
|
|
nHeight = ny * ItemHeight;
|
|
if (nHeight > rLB.bottom)
|
|
{
|
|
/* At this point, there must be a scroll bar, which makes
|
|
* out calculation of nWidth 16 pixels too large, but I like
|
|
* the effect, so I'm going to leave it.
|
|
*/
|
|
GetWindowRect(hCPlWnd, &rCPl);
|
|
nWidth = 7 * iWidth;
|
|
nWidth += rCPl.right-rCPl.left - rLB.right;
|
|
|
|
nMaxWidth = GetSystemMetrics( SM_CXSCREEN );
|
|
if (nWidth > nMaxWidth)
|
|
nWidth = nMaxWidth;
|
|
if (rCPl.left > nMaxWidth-nWidth)
|
|
rCPl.left = nMaxWidth-nWidth;
|
|
|
|
nx = nWidth / iWidth;
|
|
if (!nx)
|
|
nx = 1;
|
|
ny = (nNumApps + nx - 1) / nx;
|
|
nHeight = ny * ItemHeight;
|
|
nHeight += rCPl.bottom-rCPl.top - rLB.bottom;
|
|
|
|
nMaxHeight = GetSystemMetrics( SM_CYSCREEN );
|
|
if (nHeight > nMaxHeight)
|
|
nHeight = nMaxHeight;
|
|
if (rCPl.top > nMaxHeight-nHeight)
|
|
rCPl.top = nMaxHeight-nHeight;
|
|
|
|
SetWindowPos(hCPlWnd, NULL, rCPl.left, rCPl.top,
|
|
nWidth, nHeight, SWP_NOZORDER);
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
// Old way
|
|
// SendMessage (hCPlLB, WM_SETREDRAW, TRUE, 0L);
|
|
// InvalidateRect (hCPlWnd, NULL, TRUE);
|
|
///////////////////////////////////////////////////////////////////////
|
|
|
|
// New way - don't force a redraw unless necessary
|
|
|
|
if (iOldWidth != iWidth)
|
|
InvalidateRect (hCPlWnd, NULL, TRUE);
|
|
else
|
|
// Force a re-size of ListBox to draw scroll bars, as necessary
|
|
SendMessage (hCPlLB, WM_SIZE, 0, 0L);
|
|
|
|
if (bCache)
|
|
{
|
|
DWORD tid;
|
|
HANDLE hThread;
|
|
|
|
SendMessage (hCPlLB, LB_SETCURSEL, 0, 0L);
|
|
SendMessage (hCPlLB, WM_SETREDRAW, TRUE, 0L);
|
|
InvalidateRect (hCPlWnd, NULL, TRUE);
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// If we loaded from the Cache perform an Absolute Validation of
|
|
// of applets in the background, just to be sure something did not
|
|
// enable or change one of the applets that we could not detect
|
|
// during CheckCache().
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
UpdateWindow (hCPlWnd);
|
|
|
|
bValidationDone = FALSE;
|
|
hThread = CreateThread (NULL, 0,
|
|
(LPTHREAD_START_ROUTINE) AbsoluteValidation,
|
|
NULL, 0, &tid);
|
|
|
|
CloseHandle(hThread);
|
|
}
|
|
else
|
|
{
|
|
// Force selection of first Lbox item (also force redraw of Info text)
|
|
|
|
SendMessage (hCPlLB, LB_SETCURSEL, 1, 0L);
|
|
SendMessage (hCPlLB, LB_SETCURSEL, 0, 0L);
|
|
}
|
|
|
|
SetCursor (hCur);
|
|
return (iLoaded);
|
|
}
|
|
|
|
|
|
PCPLAPPLET SendAppMsg (int Msg, DWORD i)
|
|
{
|
|
HCURSOR hCur;
|
|
PCPLMODULE pCPlMod;
|
|
PCPLAPPLET pCPlApp;
|
|
int iApplet;
|
|
LONG lData;
|
|
TCHAR szMutex[MAX_PATH];
|
|
HANDLE hmutex = NULL;
|
|
|
|
//
|
|
// Check and set global flag indicating an applet is active
|
|
//
|
|
|
|
if (bAppletActive)
|
|
return NULL;
|
|
|
|
bAppletActive = TRUE;
|
|
|
|
lData = SendMessage (hCPlLB, LB_GETITEMDATA, i, 0L);
|
|
|
|
if (Msg == CPL_DBLCLK)
|
|
hCur = SetCursor (LoadCursor(NULL, IDC_WAIT));
|
|
|
|
iApplet = ((LPNTCPL)lData)->iApplet;
|
|
pCPlMod = ((LPNTCPL)lData)->pCPlMod;
|
|
pCPlApp = pCPlMod ? pCPlMod->pCPlApps + iApplet : NULL;
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Before loading this module, make sure that it is not in use
|
|
// by the AbsoluteValidation thread.
|
|
//
|
|
///////////////////////////////////////////////////////////////////////
|
|
|
|
if (!bValidationDone)
|
|
{
|
|
CreateMutexNameFromPath (pCPlMod->szPathname, szMutex);
|
|
hmutex = CreateMutex (NULL, FALSE, szMutex);
|
|
|
|
WaitForSingleObject (hmutex, INFINITE);
|
|
}
|
|
|
|
if ((Msg == CPL_DBLCLK) && (pCPlMod->bLoaded == FALSE))
|
|
{
|
|
// Load module into memory and init it
|
|
pCPlMod->bLoaded = LoadCachedModule (pCPlMod);
|
|
}
|
|
|
|
if (pCPlMod->bLoaded && pCPlApp)
|
|
{
|
|
// ASSERT(*pCPlMod->lpfnCPlApplet != NULL);
|
|
|
|
(*pCPlMod->lpfnCPlApplet)(hCPlWnd, Msg, (LONG)iApplet, pCPlApp->lData);
|
|
|
|
if (Msg == CPL_DBLCLK)
|
|
{
|
|
(*pCPlMod->lpfnCPlApplet)(hCPlWnd, CPL_STOP, (LONG)iApplet, pCPlApp->lData);
|
|
FreeCachedModule (pCPlMod);
|
|
SetCursor (hCur);
|
|
}
|
|
}
|
|
|
|
if (hmutex)
|
|
{
|
|
ReleaseMutex (hmutex);
|
|
CloseHandle (hmutex);
|
|
}
|
|
|
|
bAppletActive = FALSE;
|
|
|
|
//
|
|
// Check global flag to see if we should rebuild applet cache
|
|
//
|
|
|
|
if (bRebuildCache)
|
|
{
|
|
//
|
|
// IMPORTANT NOTE: Global BOOL value must be set FALSE here
|
|
// before calling RebuildCache() in order to avoid infinite
|
|
// recursion, and eventual stack overflow. SendAppMsg() is
|
|
// called many times during the cache rebuild processing.
|
|
//
|
|
// ALSO: RebuildCache() must be called only after the
|
|
// bAppletActive flag is reset, or else the cache
|
|
// will never get rebuilt.
|
|
//
|
|
|
|
bRebuildCache = FALSE;
|
|
RebuildCache ();
|
|
}
|
|
|
|
return (pCPlApp);
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Run the applet specified on the Command line
|
|
//
|
|
// NOTE: This routine must ReleaseMutex on the hmutex if it is going to
|
|
// run a control panel applet. It must NOT release the mutex if
|
|
// it is going to return FALSE.
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL RunCommandLineApplet (int argc, LPTSTR szCplFile, LPTSTR szApplet, HANDLE hmutex)
|
|
{
|
|
HCURSOR hCur;
|
|
PCPLMODULE pCPlMod;
|
|
PCPLAPPLET pCPlApp;
|
|
int iApplet;
|
|
|
|
hCur = SetCursor (LoadCursor (NULL, IDC_WAIT));
|
|
|
|
// See if this is a valid Control Panel applet file
|
|
|
|
if (!AskModule (szCplFile, 0))
|
|
{
|
|
SetCursor (hCur);
|
|
return (FALSE);
|
|
}
|
|
|
|
pCPlMod = CPlMods[0];
|
|
|
|
// Now search for the specified applet within the CPL file
|
|
|
|
if (argc > 2)
|
|
{
|
|
for (iApplet = 0; iApplet < pCPlMod->numApplets; iApplet++)
|
|
{
|
|
if (NameCmp (szApplet, pCPlMod->pCPlApps[iApplet].pszName))
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
iApplet = 0;
|
|
|
|
// If search not successful, launch the first applet in CPL file
|
|
|
|
if (iApplet >= pCPlMod->numApplets)
|
|
iApplet = 0;
|
|
|
|
pCPlApp = &pCPlMod->pCPlApps[iApplet];
|
|
|
|
// If this is a valid applet, release Initialization mutex to let
|
|
// other instances of the Control Panel run
|
|
|
|
if (hmutex)
|
|
{
|
|
ReleaseMutex (hmutex);
|
|
CloseHandle (hmutex);
|
|
}
|
|
|
|
// If running under "Setup" let applet know first
|
|
if (pCPlApp && bSetup)
|
|
(*pCPlMod->lpfnCPlApplet)(NULL, CPL_SETUP, 0L, 0L);
|
|
|
|
if (pCPlApp)
|
|
(*pCPlMod->lpfnCPlApplet)(NULL, CPL_DBLCLK, (LONG)iApplet, pCPlApp->lData);
|
|
|
|
FreeLibrary (pCPlMod->hLibrary);
|
|
|
|
SetCursor (hCur);
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Set the initial position and size of CPl app window, and return
|
|
// whether the window was last closed as an icon.
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
void GetCPlWndPos(int *CPlWndPos)
|
|
{
|
|
int i, iX, iY;
|
|
|
|
// Set the default position and size.
|
|
|
|
// Default sizing is normally determined by the profile. In the
|
|
// event that the profile is munged, we default to fixed sizes.
|
|
// We should add a resize message based on the icon metrics to be
|
|
// nice to developers adding their own CPL applets. -jyg 4/9/91
|
|
|
|
// System position defaults (better than fixed).
|
|
|
|
CPlWndPos[0] = CW_USEDEFAULT;
|
|
CPlWndPos[1] = 0;
|
|
|
|
// Fixed size defaults. The spec says we should be big enough to hold
|
|
// all of our own (1.0) icons.
|
|
|
|
CPlWndPos[2] = CPL_DEFDIMW;
|
|
CPlWndPos[3] = CPL_DEFDIMH;
|
|
|
|
// Let CONTROL.INI defines initial position and size
|
|
for (i = 0; i < 4; i++ )
|
|
CPlWndPos[i] = GetPrivateProfileInt (szMMCPL,
|
|
szWndDim[i], CPlWndPos[i], aszControlIniPath);
|
|
|
|
// Make sure CONTROL.INI position didn't put us off the screen
|
|
|
|
iX = GetSystemMetrics (SM_CXSCREEN);
|
|
iY = GetSystemMetrics (SM_CYSCREEN);
|
|
|
|
// Remember, CW_USEDEFAULT is ((int)0x8000) (-1)
|
|
|
|
// Also make sure at least 25% of Window is visible to User
|
|
|
|
if (CPlWndPos[0] != CW_USEDEFAULT)
|
|
while (CPlWndPos[0] >= (iX - CPlWndPos[2] / 4))
|
|
CPlWndPos[0] /= 2 ;
|
|
|
|
while (CPlWndPos[1] >= (iY - CPlWndPos[3] / 4))
|
|
CPlWndPos[1] /= 2 ;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Record the last valid size of the CPL window.
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
void SetCPlWndPos (HWND hWnd)
|
|
{
|
|
int i, dim[4];
|
|
TCHAR szBuffer[64];
|
|
WINDOWPLACEMENT wp;
|
|
|
|
wp.length = sizeof(wp);
|
|
GetWindowPlacement (hWnd, &wp);
|
|
|
|
dim[0] = wp.rcNormalPosition.left;
|
|
dim[1] = wp.rcNormalPosition.top;
|
|
dim[2] = wp.rcNormalPosition.right - wp.rcNormalPosition.left;
|
|
dim[3] = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;
|
|
|
|
for (i = 0; i < 4; i++ )
|
|
{
|
|
wsprintf (szBuffer, TEXT("%d"), dim[i]);
|
|
WritePrivateProfileString (szMMCPL,
|
|
szWndDim[i], szBuffer, aszControlIniPath);
|
|
}
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// For each CPLMODULE free its CPLAPPLET array, and library handle
|
|
// and then free the CPLMODULE array.
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
void FreeApps(void)
|
|
{
|
|
PCPLMODULE pCPlMod;
|
|
int i;
|
|
|
|
// free lib handles and CPLMod array here
|
|
for (i = 0; pCPlMod = CPlMods[i]; i++)
|
|
{
|
|
LocalFree ((HANDLE)pCPlMod->pCPlApps);
|
|
|
|
// no longer in need of your services
|
|
if (*pCPlMod->lpfnCPlApplet)
|
|
(*pCPlMod->lpfnCPlApplet) (hCPlWnd, CPL_EXIT, 0L, 0L);
|
|
|
|
if (pCPlMod->hLibrary)
|
|
FreeLibrary (pCPlMod->hLibrary);
|
|
LocalFree ((HANDLE)pCPlMod);
|
|
CPlMods[i] = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Taken from old WIN3 UTILTEXT.C.
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
void ErrMemDlg (HWND hParent)
|
|
{
|
|
MessageBox (hParent, szErrMem, szAppName, MB_OK | MB_ICONEXCLAMATION | MB_SYSTEMMODAL);
|
|
}
|
|
|
|
|
|
int ChngSelApp (int iApplet,TCHAR chKey)
|
|
{
|
|
LPTSTR pstr;
|
|
LONG lItemData;
|
|
PCPLAPPLET pCPlApp;
|
|
int i;
|
|
//
|
|
// change selected applet to the next one whose accelerator
|
|
// matches the character just pressed
|
|
//
|
|
//
|
|
|
|
for (i = iApplet + 1; i != iApplet; i++)
|
|
{
|
|
if (i == (int) numApps)
|
|
//
|
|
// wrap search around to the first applet
|
|
//
|
|
if (iApplet)
|
|
i = 0;
|
|
else
|
|
//
|
|
// started with the first applet so outta here
|
|
//
|
|
return -1;
|
|
|
|
//
|
|
// compare the accelerator letter of this applet with user key
|
|
//
|
|
if ((lItemData = SendMessage (hCPlLB, LB_GETITEMDATA, i, 0L)) != LB_ERR)
|
|
{
|
|
int iTmpApplet;
|
|
|
|
iTmpApplet = ((LPNTCPL)lItemData)->iApplet;
|
|
pCPlApp = (((LPNTCPL)lItemData)->pCPlMod)->pCPlApps + iTmpApplet;
|
|
|
|
pstr = pCPlApp->pszName;
|
|
|
|
// look for accelerator part "&x"
|
|
|
|
while ((*pstr) != TEXT('\0'))
|
|
{
|
|
if (*pstr == TEXT('&'))
|
|
{
|
|
if (*(pstr+1) == TEXT('&'))
|
|
pstr++;
|
|
else if ((TCHAR)(ULONG)CharUpper((LPTSTR)(ULONG)(TUCHAR)*(pstr + 1)) == chKey)
|
|
//
|
|
// found a match, make it the current selection
|
|
//
|
|
return( i );
|
|
}
|
|
pstr++;
|
|
}
|
|
|
|
// now look for first char only
|
|
|
|
if ((TCHAR)(ULONG)CharUpper((LPTSTR)(ULONG)(TUCHAR)*(pCPlApp->pszName)) == chKey)
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// GetAppletName
|
|
//
|
|
// Read the name of the applet from another processes memory space and
|
|
// copy it to local buffer. If the calling app is ANSI, assume that
|
|
// we are reading an ANSI string and vice versa for UNICODE.
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL GetAppletName (DWORD dwProcessId, LPTSTR lpszAppletName, DWORD dwAddress, BOOL fUnicode)
|
|
{
|
|
HANDLE hProcess;
|
|
DWORD dwBytesRead;
|
|
TCHAR szReadBufferW[2];
|
|
CHAR szReadBufferA[2];
|
|
CHAR szNameBufferA[MAX_APPLET_NAME_LEN];
|
|
int i;
|
|
|
|
|
|
hProcess = OpenProcess (PROCESS_VM_READ, FALSE, dwProcessId);
|
|
|
|
// Read other processes memory 1 or 2 bytes at a time until we reach
|
|
// end of their string or our data buffer (assumed to be 32 bytes)
|
|
|
|
if (hProcess)
|
|
{
|
|
i = 0;
|
|
|
|
if (fUnicode)
|
|
{
|
|
while (ReadProcessMemory (hProcess,
|
|
(LPVOID) dwAddress,
|
|
szReadBufferW,
|
|
2,
|
|
&dwBytesRead))
|
|
{
|
|
if ((dwBytesRead == 2) && (i < MAX_APPLET_NAME_LEN-1))
|
|
{
|
|
lpszAppletName[i++] = szReadBufferW[0];
|
|
|
|
// Reached end of string?
|
|
if (szReadBufferW[0] == TEXT('\0'))
|
|
break;
|
|
else
|
|
dwAddress += 2;
|
|
}
|
|
else
|
|
{
|
|
// Error - Terminate string
|
|
lpszAppletName[i] = TEXT('\0');
|
|
#if DBG
|
|
GetLastError();
|
|
#endif // DBG
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while (ReadProcessMemory (hProcess,
|
|
(LPVOID) dwAddress,
|
|
szReadBufferA,
|
|
1,
|
|
&dwBytesRead))
|
|
{
|
|
if ((dwBytesRead == 1) && (i < MAX_APPLET_NAME_LEN-1))
|
|
{
|
|
szNameBufferA[i++] = szReadBufferA[0];
|
|
|
|
// Reached end of string?
|
|
if (szReadBufferA[0] == '\0')
|
|
{
|
|
ConvertAppletName:
|
|
// Convert to Unicode
|
|
MultiByteToWideChar (CP_ACP,
|
|
MB_PRECOMPOSED,
|
|
szNameBufferA,
|
|
-1,
|
|
lpszAppletName,
|
|
MAX_APPLET_NAME_LEN);
|
|
break;
|
|
}
|
|
else
|
|
dwAddress++;
|
|
}
|
|
else
|
|
{
|
|
// Error - Terminate string, then goto common exit
|
|
// for Unicode conversion
|
|
szNameBufferA[i] = '\0';
|
|
#if DBG
|
|
GetLastError();
|
|
#endif // DBG
|
|
goto ConvertAppletName;
|
|
// break;
|
|
}
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
#if DBG
|
|
GetLastError ();
|
|
#endif // DBG
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
LRESULT APIENTRY CPlWndProc (HWND hWnd, UINT Message, WPARAM wParam, LONG lParam)
|
|
{
|
|
LPDRAWITEMSTRUCT drwI;
|
|
LPMEASUREITEMSTRUCT measI;
|
|
PCPLAPPLET pCPlApp;
|
|
PCPLMODULE pCPlMod;
|
|
HBRUSH hBrush;
|
|
HFONT hFont;
|
|
RECT Rect;
|
|
LONG rgbBG, rgbFG;
|
|
BOOL bLaunchedOK;
|
|
UINT Msg;
|
|
int i, dx, dy, iBkMode;
|
|
DWORD dwContext;
|
|
LPTSTR pHelpFile;
|
|
TCHAR szTitle[40];
|
|
DWORD dwProcessId;
|
|
TCHAR szAppletName[MAX_APPLET_NAME_LEN];
|
|
BOOL fUnicode;
|
|
|
|
switch (Message)
|
|
{
|
|
|
|
// NOTE: for WM_CPL_LAUNCH and WM_CPL_LAUNCHEX messages
|
|
//
|
|
// For Windows NT, we may have to try to read the name of the
|
|
// applet from another processes memory space. The NT way is
|
|
// to open that process and do a ReadMemory from its data space.
|
|
|
|
case WM_CPL_LAUNCH:
|
|
// wParam is HWND of calling app
|
|
// lParam points to name of applet to run.
|
|
// when done call back the caller, unless it was us
|
|
|
|
bLaunchedOK = FALSE;
|
|
|
|
if (lParam)
|
|
{
|
|
if (wParam && ((HWND) wParam != hWnd))
|
|
{
|
|
GetWindowThreadProcessId ((HWND) wParam, &dwProcessId);
|
|
|
|
fUnicode = IsWindowUnicode ((HWND) wParam);
|
|
|
|
if (GetAppletName (dwProcessId, szAppletName, lParam, fUnicode))
|
|
{
|
|
i = FindApplet(szAppletName);
|
|
}
|
|
else
|
|
{
|
|
#if DBG
|
|
GetLastError ();
|
|
#endif // DBG
|
|
return (LRESULT)bLaunchedOK;
|
|
}
|
|
}
|
|
else
|
|
i = FindApplet ((LPTSTR) lParam);
|
|
|
|
if (i >= 0)
|
|
{
|
|
if (wParam)
|
|
EnableWindow ((HWND)wParam, FALSE);
|
|
|
|
SendMessage(hWnd, LB_SETCURSEL, i, 0L);
|
|
bLaunchedOK = SendAppMsg(CPL_DBLCLK, i) != NULL;
|
|
|
|
if (wParam)
|
|
EnableWindow((HWND)wParam, TRUE);
|
|
}
|
|
}
|
|
|
|
// if called from another app let 'em know it completed
|
|
|
|
if (wParam)
|
|
{
|
|
PostMessage((HWND)wParam, WM_CPL_LAUNCHED, bLaunchedOK, 0L);
|
|
|
|
// if the message is from us then it must have been from
|
|
// the command line, so exit
|
|
|
|
if (((HWND)wParam == hWnd) && bLaunchedOK)
|
|
PostMessage(hWnd, WM_CLOSE, 0, 0L);
|
|
}
|
|
return (LRESULT)bLaunchedOK;
|
|
|
|
|
|
case WM_CPL_LAUNCHEX:
|
|
// wParam is PROCESS ID of calling app
|
|
// lParam points to name of applet to run.
|
|
|
|
bLaunchedOK = FALSE;
|
|
|
|
if (lParam)
|
|
{
|
|
// For Windows NT, we may have to try to read the name of the
|
|
// applet from another processes memory space. The NT way is
|
|
// to open that process and do a ReadMemory from its data space.
|
|
|
|
// IMPORTANT NOTE: Since this is currently a private message, we
|
|
// can always assume that it is called from another copy of the
|
|
// Control Panel and that wParam is the ProcessId. No calls are
|
|
// made back to calling app. Also, we can assume that the calling
|
|
// Control Panel app is UNICODE.
|
|
|
|
if (wParam)
|
|
{
|
|
i = 0;
|
|
|
|
// Assume Unicode name string at *lParam
|
|
if (GetAppletName (wParam, szAppletName, lParam, TRUE))
|
|
{
|
|
i = FindApplet(szAppletName);
|
|
}
|
|
else
|
|
{
|
|
#if DBG
|
|
GetLastError ();
|
|
#endif // DBG
|
|
return (LRESULT)bLaunchedOK;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Called locally, assume lParam ptr is in our address space
|
|
|
|
i = FindApplet((LPTSTR)lParam);
|
|
}
|
|
|
|
if (i >= 0)
|
|
{
|
|
SendMessage(hWnd, LB_SETCURSEL, i, 0L);
|
|
bLaunchedOK = SendAppMsg(CPL_DBLCLK, i) != NULL;
|
|
}
|
|
}
|
|
return (LRESULT)bLaunchedOK;
|
|
|
|
|
|
case WM_COMMAND:
|
|
if (lParam && (LOWORD(wParam) == ID_LISTBOX))
|
|
{
|
|
// it's a list box message
|
|
//
|
|
// Use local signed variable to get around USER bug
|
|
// where they return a negative number for LB_GETCURSEL
|
|
// if someone is holding down Left Mouse Button, moves
|
|
// mouse outside left-side of window and then releases
|
|
// mouse buttom. (See WM_DRAWITEM, below)
|
|
//
|
|
|
|
if (HIWORD(wParam) == LBN_DBLCLK)
|
|
Msg = CPL_DBLCLK;
|
|
else if (HIWORD(wParam) == LBN_SELCHANGE)
|
|
Msg = CPL_SELECT;
|
|
else
|
|
break;
|
|
|
|
i = SendMessage(hCPlLB, LB_GETCURSEL, 0, 0L);
|
|
|
|
if (i >= 0)
|
|
SendAppMsg(Msg, i);
|
|
}
|
|
else if ((LOWORD(wParam) >= MENU_SETTINGS) &&
|
|
(LOWORD(wParam) < (WORD) (MENU_SETTINGS + (DWORD)numApps)))
|
|
{
|
|
/* it's a menu selection to launch an applet */
|
|
i = wParam - MENU_SETTINGS;
|
|
SendMessage(hCPlLB, LB_SETCURSEL, i, 0L);
|
|
SendAppMsg(CPL_DBLCLK, i);
|
|
return (LRESULT) TRUE;
|
|
}
|
|
else
|
|
{
|
|
/* it's some other menu message */
|
|
|
|
switch (LOWORD(wParam))
|
|
{
|
|
/* help stuff taken from old WIN3 source: CONTROL.C */
|
|
case MENU_CACHE:
|
|
RebuildCache ();
|
|
break;
|
|
|
|
case MENU_EXIT:
|
|
PostMessage (hWnd, WM_CLOSE, 0, 0L);
|
|
break;
|
|
|
|
case MENU_F1:
|
|
i = (int) SendMessage (hCPlLB, LB_GETCURSEL, 0, 0L);
|
|
if (i >= 0)
|
|
{
|
|
pCPlApp = SendAppMsg(CPL_SELECT, i);
|
|
dwContext = pCPlApp->dwContext;
|
|
pHelpFile = pCPlApp->pszHelpFile;
|
|
CPHelp(hWnd, pHelpFile, dwContext);
|
|
}
|
|
break;
|
|
|
|
case MENU_USEHELP:
|
|
if (!WinHelp(hWnd, NULL, HELP_HELPONHELP, 0L))
|
|
ErrMemDlg(hWnd);
|
|
break;
|
|
|
|
case MENU_INDHELP:
|
|
if (!WinHelp(hWnd, szHelpFile, HELP_INDEX, 0L))
|
|
ErrMemDlg(hWnd);
|
|
break;
|
|
|
|
case MENU_SCHHELP:
|
|
if (!WinHelp(hWnd, szHelpFile, HELP_PARTIALKEY, (DWORD)(LPSTR)""))
|
|
ErrMemDlg(hWnd);
|
|
break;
|
|
|
|
case MENU_ABOUT:
|
|
{
|
|
HANDLE hLib;
|
|
SHELL_ABOUT pfn;
|
|
|
|
LoadString(hCPlInst, CONTROLABOUT, szTitle, CharSizeOf(szTitle));
|
|
|
|
hLib = LoadLibrary (TEXT("Shell32.dll"));
|
|
|
|
pfn = (SHELL_ABOUT) GetProcAddress (hLib, "ShellAboutW");
|
|
|
|
if (pfn)
|
|
(*pfn) (hWnd, szTitle, NULL, LoadIcon (hCPlInst,
|
|
(LPTSTR) MAKEINTRESOURCE(MMCPL)));
|
|
FreeLibrary (hLib);
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WM_CHARTOITEM:
|
|
//
|
|
// Select the applet according to the accelerator -jyg-
|
|
//
|
|
return(LRESULT)(ChngSelApp((int)HIWORD(wParam),
|
|
(TCHAR)(ULONG)CharUpper((LPTSTR)(ULONG)(TUCHAR)LOWORD(wParam))));
|
|
|
|
case WM_CREATE:
|
|
hCPlWnd = hWnd;
|
|
if (!CreatChildWindows ())
|
|
return (LRESULT) -1L;
|
|
break;
|
|
|
|
case WM_DELETEITEM:
|
|
// free memory for applets
|
|
if (pCPlApp = SendAppMsg (CPL_STOP,
|
|
((LPDELETEITEMSTRUCT)lParam)->itemID))
|
|
{
|
|
LocalFree ((HANDLE)pCPlApp->pszFullName);
|
|
LocalFree ((HANDLE)pCPlApp->pszName);
|
|
LocalFree ((HANDLE)pCPlApp->pszInfo);
|
|
if (pCPlApp->pszHelpFile)
|
|
LocalFree ((HANDLE)pCPlApp->pszHelpFile);
|
|
}
|
|
|
|
if (--numApps == 0)
|
|
FreeApps();
|
|
break;
|
|
|
|
case WM_CLOSE:
|
|
case WM_ENDSESSION:
|
|
SetCPlWndPos (hWnd);
|
|
break;
|
|
|
|
case WM_DESTROY:
|
|
if (hCPlFont)
|
|
DeleteObject (hCPlFont);
|
|
if (hCPlBk)
|
|
DeleteObject (hCPlBk);
|
|
|
|
WinHelp (hWnd, szHelpFile, HELP_QUIT, 0L);
|
|
PostQuitMessage (0);
|
|
break;
|
|
|
|
case WM_CTLCOLORLISTBOX:
|
|
rgbBG = GetSysColor (COLOR_APPWORKSPACE);
|
|
if ((WORD)GetRValue(rgbBG) + (WORD)GetGValue (rgbBG)
|
|
+ (WORD)GetBValue(rgbBG) > 383)
|
|
rgbFG = RGB(0,0,0);
|
|
else
|
|
rgbFG = RGB(255,255,255);
|
|
|
|
SetBkColor ((HDC)wParam, rgbBG);
|
|
SetTextColor ((HDC)wParam, rgbFG);
|
|
return ((LRESULT)hCPlBk);
|
|
break;
|
|
|
|
case WM_DRAWITEM:
|
|
drwI = (LPDRAWITEMSTRUCT)lParam;
|
|
|
|
//
|
|
// Make sure itemData field is not equal to -1 or 0, this
|
|
// is to work around USER problem where they will send us
|
|
// an DRAWITEMSTRUCT.itemID value of -1, -2, etc. if someone
|
|
// is holding the LEFTMOUSEBUTTON down, moves outside of the
|
|
// window and then releases the button. (See WM_COMMAND, above)
|
|
//
|
|
// The itemData value is a pointer to our local NTCPL data struct
|
|
//
|
|
|
|
if (((int)drwI->itemID >= 0) &&
|
|
(pCPlMod = ((LPNTCPL)drwI->itemData)->pCPlMod) &&
|
|
(pCPlApp = pCPlMod->pCPlApps + ((LPNTCPL)drwI->itemData)->iApplet))
|
|
{
|
|
// Draw the icon if we must
|
|
if (drwI->itemAction & ODA_DRAWENTIRE)
|
|
DrawIcon (drwI->hDC, (drwI->rcItem).left + iIconX,
|
|
(drwI->rcItem).top + iIconY, pCPlApp->hIcon);
|
|
|
|
// Draw the Name text under the icon
|
|
// dx = (iWidth - pCPlApp->iNameW) / 2;
|
|
|
|
if (drwI->itemState & ODS_SELECTED)
|
|
{
|
|
rgbBG = SetBkColor (drwI->hDC, GetSysColor(COLOR_ACTIVECAPTION));
|
|
rgbFG = SetTextColor (drwI->hDC, GetSysColor(COLOR_CAPTIONTEXT));
|
|
|
|
}
|
|
else // erase underneath text with background color
|
|
{
|
|
CopyRect (&Rect, &drwI->rcItem);
|
|
Rect.top += iIconH + iIconY;
|
|
hBrush = SelectObject (drwI->hDC, hCPlBk);
|
|
FillRect (drwI->hDC, &Rect, hCPlBk);
|
|
SelectObject (drwI->hDC, hBrush);
|
|
iBkMode = SetBkMode (drwI->hDC, TRANSPARENT);
|
|
}
|
|
|
|
hFont = SelectObject (drwI->hDC, hCPlFont);
|
|
CopyRect (&Rect, &drwI->rcItem);
|
|
Rect.top += iIconH + iIconY;
|
|
DrawText (drwI->hDC,pCPlApp->pszName,-1, (LPRECT)&Rect, DT_CENTER|DT_NOCLIP|DT_SINGLELINE);
|
|
SelectObject (drwI->hDC, hFont);
|
|
|
|
// Draw the Info text at the bottom of the window
|
|
if (drwI->itemState & ODS_SELECTED)
|
|
{
|
|
SetBkColor (drwI->hDC, rgbBG);
|
|
SetTextColor (drwI->hDC, rgbFG);
|
|
SetWindowText (hCPlTB,pCPlApp->pszInfo); //!!!
|
|
}
|
|
else
|
|
SetBkMode(drwI->hDC, iBkMode);
|
|
}
|
|
|
|
return((LRESULT)TRUE);
|
|
|
|
case WM_MEASUREITEM:
|
|
measI = ((LPMEASUREITEMSTRUCT)lParam);
|
|
measI->itemHeight = ItemHeight;
|
|
return((LRESULT)TRUE);
|
|
|
|
case WM_MOVE:
|
|
if (!IsIconic(hWnd) && !IsZoomed(hWnd))
|
|
GetWindowRect (hWnd, &CPlRect);
|
|
break;
|
|
|
|
case WM_SETFOCUS:
|
|
SetFocus (hCPlLB);
|
|
break;
|
|
|
|
case WM_SIZE:
|
|
if (wParam != SIZEICONIC)
|
|
{
|
|
int cx, cy;
|
|
cx = GetSystemMetrics (SM_CXBORDER);
|
|
cy = GetSystemMetrics (SM_CYBORDER);
|
|
|
|
dx = LOWORD(lParam);
|
|
dy = HIWORD(lParam);
|
|
|
|
// This code assumes that the text window has a border
|
|
// and the listbox window does too, and offsets the two
|
|
// windows accordingly. The listbox is given a border,
|
|
// then inflated so that the border is hidden by the
|
|
// surrounding window parts; this avoids the common but
|
|
// ugly double-border-around-scrollbar syndrome.
|
|
//
|
|
// edh, 24-Sep-91.
|
|
|
|
GetClientRect (hCPlTB, &Rect);
|
|
MoveWindow (hCPlLB, -cx, -cy, dx+cx+cx, dy-Rect.bottom+cy, TRUE);
|
|
MoveWindow (hCPlTB, -cx, dy-Rect.bottom-cy, dx+cx+cx, Rect.bottom+cy+cy, TRUE);
|
|
|
|
if (wParam != SIZEFULLSCREEN)
|
|
GetWindowRect (hWnd, &CPlRect);
|
|
}
|
|
break;
|
|
|
|
|
|
case WM_SYSCOLORCHANGE:
|
|
if (hCPlBk)
|
|
DeleteObject (hCPlBk);
|
|
|
|
hCPlBk = CreateSolidBrush (GetSysColor (COLOR_APPWORKSPACE));
|
|
break;
|
|
|
|
case WM_MENUSELECT:
|
|
if (lParam)
|
|
{
|
|
// Save the menu the user selected
|
|
wMenuID = LOWORD(wParam);
|
|
dwMenuBits = (DWORD) HIWORD(wParam);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
if (Message == wHelpMessage)
|
|
{
|
|
if (hSetup)
|
|
{
|
|
// trap F1 for setup and pass it on
|
|
|
|
PostMessage(hSetup, WM_COMMAND, 3, 0L);
|
|
|
|
}
|
|
else if (wParam == MSGF_MENU)
|
|
{
|
|
// Get outta menu mode if help for a menu item
|
|
|
|
if (wMenuID && HIWORD(dwMenuBits))
|
|
{
|
|
WORD m = wMenuID; // save
|
|
DWORD mb = dwMenuBits;
|
|
|
|
SendMessage(hWnd, WM_CANCELMODE, 0, 0L);
|
|
|
|
wMenuID = m; // restore
|
|
dwMenuBits = mb;
|
|
}
|
|
|
|
if (!(LOWORD(dwMenuBits) & MF_POPUP))
|
|
{
|
|
dwContext = 0L;
|
|
pHelpFile = NULL;
|
|
|
|
if (LOWORD(dwMenuBits) & MF_SYSMENU)
|
|
{
|
|
dwContext = IDH_SYSMENU;
|
|
}
|
|
else if ((wMenuID >= MENU_SETTINGS) &&
|
|
(wMenuID < (WORD)(MENU_SETTINGS + numApps)))
|
|
{
|
|
pCPlApp = SendAppMsg(CPL_SELECT, wMenuID - MENU_SETTINGS);
|
|
dwContext = pCPlApp->dwContext;
|
|
pHelpFile = pCPlApp->pszHelpFile;
|
|
}
|
|
else
|
|
{
|
|
dwContext = wMenuID + IDH_HELPFIRST;
|
|
}
|
|
|
|
CPHelp(hWnd, pHelpFile, dwContext);
|
|
}
|
|
|
|
}
|
|
else if (wParam == MSGF_DIALOGBOX)
|
|
{
|
|
// let dialog box deal with it
|
|
|
|
PostMessage(GetRealParent((HWND)lParam), wHelpMessage, 0, 0L);
|
|
}
|
|
}
|
|
else
|
|
return (LRESULT)DefWindowProc(hWnd, Message, wParam, lParam);
|
|
break;
|
|
}
|
|
|
|
return DefWindowProc(hWnd, Message, wParam, lParam);
|
|
}
|
|
|
|
LRESULT APIENTRY fnText (HWND hwnd, UINT message, WPARAM wParam, LONG lParam)
|
|
{
|
|
static HFONT hStatFont = NULL;
|
|
|
|
TCHAR ach[128];
|
|
|
|
switch (message)
|
|
{
|
|
case WM_CREATE:
|
|
{
|
|
HDC hDC;
|
|
HFONT hOldFont;
|
|
TEXTMETRIC tm;
|
|
|
|
hDC = GetDC (hwnd);
|
|
|
|
#ifdef JAPAN /* V-KeijiY July.16.1992 */
|
|
hStatFont = CreateFont(-10 * GetDeviceCaps(hDC, LOGPIXELSY) / 72,
|
|
0, 0, 0, 400, 0, 0, 0, ( ANSI_CHARSET | SHIFTJIS_CHARSET ),
|
|
OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
|
|
VARIABLE_PITCH | FF_SWISS, TEXT("System"));
|
|
#else
|
|
#ifdef UNICODE
|
|
/* initialize the Unicode font */
|
|
hStatFont = CreateFont (-10 * GetDeviceCaps(hDC, LOGPIXELSY) / 72,
|
|
0, 0, 0, 400, 0, 0, 0, ANSI_CHARSET,
|
|
OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
|
|
DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS,
|
|
TEXT("MS Shell Dlg"));
|
|
#else
|
|
hStatFont = CreateFont (-10 * GetDeviceCaps(hDC, LOGPIXELSY) / 72,
|
|
0, 0, 0, 400, 0, 0, 0, ANSI_CHARSET,
|
|
OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
|
|
DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS,
|
|
TEXT("MS Shell Dlg"));
|
|
#endif
|
|
#endif
|
|
|
|
if (hStatFont)
|
|
hOldFont = SelectObject (hDC, hStatFont);
|
|
else
|
|
hOldFont = NULL;
|
|
|
|
GetTextMetrics (hDC, &tm);
|
|
SetWindowPos (hwnd, NULL, 0, 0, 0, tm.tmHeight+6*GetSystemMetrics(SM_CYBORDER)+2,
|
|
SWP_NOZORDER | SWP_NOMOVE);
|
|
|
|
SelectObject (hDC, hOldFont);
|
|
|
|
ReleaseDC (hwnd, hDC);
|
|
break;
|
|
}
|
|
|
|
case WM_DESTROY:
|
|
if (hStatFont)
|
|
DeleteObject (hStatFont);
|
|
break;
|
|
|
|
case WM_SETTEXT:
|
|
/* Do nothing if the text does not change
|
|
* This prevents flicker on PAINT messages
|
|
*/
|
|
GetWindowText (hwnd, ach, CharSizeOf(ach));
|
|
if (lstrcmp (ach, (LPTSTR)lParam))
|
|
{
|
|
DefWindowProc (hwnd, message, wParam, lParam);
|
|
InvalidateRect (hwnd,NULL,FALSE);
|
|
UpdateWindow (hwnd);
|
|
}
|
|
return 0L;
|
|
|
|
case WM_PAINT:
|
|
{
|
|
PAINTSTRUCT ps;
|
|
RECT rc;
|
|
int len, nxBorder, nyBorder;
|
|
HFONT hOldFont = NULL;
|
|
HBRUSH hBrush, hOldBrush;
|
|
int nOldMode;
|
|
DWORD dwOldColor;
|
|
|
|
BeginPaint (hwnd, &ps);
|
|
|
|
GetClientRect (hwnd,&rc);
|
|
|
|
nxBorder = GetSystemMetrics (SM_CXBORDER);
|
|
rc.left += 9 * nxBorder;
|
|
rc.right -= 9 * nxBorder;
|
|
|
|
nyBorder = GetSystemMetrics (SM_CYBORDER);
|
|
rc.top += 3 * nyBorder;
|
|
rc.bottom -= 3 * nyBorder;
|
|
|
|
if (hBrush=CreateSolidBrush (GetSysColor (COLOR_BTNHIGHLIGHT)))
|
|
{
|
|
if (hOldBrush = SelectObject (ps.hdc, hBrush))
|
|
{
|
|
PatBlt (ps.hdc, rc.left-nxBorder, rc.bottom,
|
|
rc.right-rc.left+2*nxBorder, nyBorder, PATCOPY);
|
|
PatBlt (ps.hdc, rc.right, rc.top-nyBorder,
|
|
nxBorder, rc.bottom-rc.top+2*nxBorder, PATCOPY);
|
|
SelectObject (ps.hdc, hOldBrush);
|
|
}
|
|
DeleteObject (hBrush);
|
|
}
|
|
|
|
if (hBrush = CreateSolidBrush (GetSysColor(COLOR_BTNSHADOW)))
|
|
{
|
|
if (hOldBrush = SelectObject (ps.hdc, hBrush))
|
|
{
|
|
PatBlt (ps.hdc, rc.left-nxBorder, rc.top-nyBorder,
|
|
rc.right-rc.left+nxBorder, nyBorder, PATCOPY);
|
|
PatBlt (ps.hdc, rc.left-nxBorder, rc.top-nyBorder,
|
|
nxBorder, rc.bottom-rc.top+nxBorder, PATCOPY);
|
|
SelectObject (ps.hdc, hOldBrush);
|
|
}
|
|
DeleteObject (hBrush);
|
|
}
|
|
|
|
if (hBrush = CreateSolidBrush (GetSysColor(COLOR_BTNFACE)))
|
|
{
|
|
if (hOldBrush = SelectObject (ps.hdc, hBrush))
|
|
{
|
|
PatBlt (ps.hdc, rc.left, rc.top, rc.right-rc.left,
|
|
rc.bottom-rc.top, PATCOPY);
|
|
SelectObject (ps.hdc, hOldBrush);
|
|
}
|
|
DeleteObject (hBrush);
|
|
}
|
|
|
|
len = GetWindowText (hwnd, ach, CharSizeOf(ach));
|
|
nOldMode = SetBkMode (ps.hdc, TRANSPARENT);
|
|
dwOldColor = SetTextColor (ps.hdc, GetSysColor(COLOR_BTNTEXT));
|
|
|
|
if (hStatFont)
|
|
hOldFont = SelectObject (ps.hdc, hStatFont);
|
|
ExtTextOut (ps.hdc, rc.left + 2 * nxBorder, rc.top, ETO_CLIPPED,
|
|
&rc, ach, len, NULL);
|
|
if (hOldFont)
|
|
SelectObject (ps.hdc, hOldFont);
|
|
|
|
SetBkMode (ps.hdc, nOldMode);
|
|
SetTextColor (ps.hdc, dwOldColor);
|
|
|
|
EndPaint (hwnd, &ps);
|
|
return 0L;
|
|
}
|
|
}
|
|
return DefWindowProc(hwnd, message, wParam, lParam);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CONTROL.EXE [FILE.CPL] [Applet Name] [Setup]
|
|
// CONTROL.EXE /Cache
|
|
//
|
|
// if given a CPL file to load and an optional applet name we
|
|
// will load that CPL (and not create the main window) and then
|
|
// run the appropriate applet. If the applet name is not found
|
|
// (or not specified) we will run the first applet in the .CPL file
|
|
//
|
|
// =============== May 4, 1993 [stevecat] ===============================
|
|
//
|
|
// A third possible argument was added to satisfy a last minute NT
|
|
// Setup request. If "Setup" is exactly the third argument after
|
|
// Control.exe on command line, I will send a new message "CPL_SETUP"
|
|
// to the applet before trying to run the command line applet. In
|
|
// this case, the second argument must be present, and must be a
|
|
// valid applet name within the CPL file.
|
|
//
|
|
// ======================================================================
|
|
//
|
|
// if no parameters are given we will run normally, if another instance
|
|
// is already running (minimized or not) we will bring it to the
|
|
// foreground. If given an applet name we will activate that applet
|
|
//
|
|
// =============== Sept. 21, 1993 [stevecat] ============================
|
|
//
|
|
// Added new command line arg to force Control Panel module cache to
|
|
// be rebuilt.
|
|
//
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
_CRTAPI1 main (int argc, char *argv[])
|
|
{
|
|
WNDCLASS rClass;
|
|
MSG rMsg;
|
|
int CPlWndPos[4];
|
|
HACCEL hAccel;
|
|
HANDLE hPrevInstance;
|
|
HANDLE hmutexInit = NULL;
|
|
int len;
|
|
TCHAR **v_U;
|
|
STARTUPINFO si;
|
|
|
|
extern lbInit(HANDLE);
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
// Immediately create a simple blocking MUTEX to stop multiple
|
|
// instances of the Control Panel being launched before we can
|
|
// detect it thru the USER FindWindow api.
|
|
///////////////////////////////////////////////////////////////////////
|
|
|
|
hmutexInit = CreateMutex (NULL, FALSE, TEXT("Control Panel Init"));
|
|
|
|
WaitForSingleObject (hmutexInit, INFINITE);
|
|
|
|
//
|
|
// Get User's logon name to use in MUTEX object naming for uniqueness
|
|
//
|
|
|
|
len = MAX_PATH;
|
|
|
|
GetUserName (szUsername, &len);
|
|
|
|
//
|
|
// Get UNICODE command line arguments
|
|
//
|
|
|
|
v_U = CommandLineToArgvW (GetCommandLine(), &argc);
|
|
|
|
hCPlInst = GetModuleHandle (NULL);
|
|
|
|
if (!GetCPLStrings())
|
|
return(FALSE);
|
|
|
|
MakeWinPath(aszControlIniPath, aszControlIni);
|
|
|
|
// Load in all key accelerators and setup help messages and
|
|
// hooks so they are available for when only one applet is
|
|
// running alone.
|
|
|
|
hAccel = LoadAccelerators (hCPlInst, (LPTSTR) MAKEINTRESOURCE(MMCPL));
|
|
wHelpMessage = RegisterWindowMessage (TEXT("ShellHelp"));
|
|
hhkMsgFilter = SetWindowsHook (WH_MSGFILTER, (HOOKPROC) MessageFilter);
|
|
|
|
//
|
|
// Check Command line for Cache argument
|
|
//
|
|
|
|
if ((argc > 1) && (!lstrcmpi (TEXT("/Cache"), v_U[1])))
|
|
{
|
|
// Force Cache to be rebuilt
|
|
ClearCacheValid();
|
|
|
|
// No other arguments accepted with this one
|
|
argc = 1;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// This is a sneaky thing I am doing. If someone calls the control
|
|
// panel with any argument, I check the cache and build it if needed.
|
|
// This will get invoked during setup (timezone) and will create the
|
|
// cached entries at setup time. Then, the first time the User starts
|
|
// the Control Panel after they logon, it will just seem very fast.
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
if (argc > 1)
|
|
{
|
|
BuildCache ();
|
|
|
|
if (!lstrcmpi (TEXT("/Build"), v_U[1]))
|
|
return (TRUE);
|
|
}
|
|
|
|
// Check Command line for Setup argument
|
|
if ((argc >= 4) && (!lstrcmpi (TEXT("Setup"), v_U[3])))
|
|
bSetup = TRUE;
|
|
|
|
//
|
|
// RunCommandLineApplet releases MUTEX as necessary
|
|
//
|
|
|
|
if (argc > 1 && RunCommandLineApplet (argc, v_U[1], v_U[2], hmutexInit))
|
|
return (TRUE);
|
|
|
|
hPrevInstance = FindWindow (szClass, szAppName);
|
|
|
|
if (hPrevInstance != NULL)
|
|
{
|
|
// Free MUTEX here since we will exit along this code path
|
|
if (hmutexInit)
|
|
{
|
|
ReleaseMutex (hmutexInit);
|
|
CloseHandle (hmutexInit);
|
|
}
|
|
|
|
// If there was a previous instance of the Control Panel active,
|
|
// just bring it to the foreground (or its' last active child window).
|
|
|
|
if (IsIconic(hPrevInstance))
|
|
{
|
|
ShowWindow (hPrevInstance, SW_RESTORE);
|
|
SetForegroundWindow (hPrevInstance);
|
|
}
|
|
else
|
|
SetForegroundWindow (GetLastActivePopup(hPrevInstance));
|
|
|
|
if (IsWindowEnabled (hPrevInstance))
|
|
{
|
|
if (argc > 1)
|
|
SendMessage (hPrevInstance, WM_CPL_LAUNCHEX,
|
|
GetCurrentProcessId(), (LONG) v_U[1]);
|
|
return (TRUE);
|
|
}
|
|
|
|
return (FALSE);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// We haven't registered our window classes yet
|
|
//
|
|
|
|
// DoRegisterStuff:
|
|
|
|
lbInit(hCPlInst);
|
|
|
|
// TODO: [stevecat] TEST for FULL DRAG redraw - remove Class styles
|
|
rClass.style = 0; // CS_HREDRAW | CS_VREDRAW;
|
|
rClass.lpfnWndProc = CPlWndProc;
|
|
rClass.cbClsExtra = 0;
|
|
rClass.cbWndExtra = 0;
|
|
rClass.hInstance = hCPlInst;
|
|
rClass.hIcon = LoadIcon (hCPlInst, (LPTSTR) MAKEINTRESOURCE(MMCPL));
|
|
rClass.hCursor = LoadCursor (NULL, IDC_ARROW);
|
|
rClass.hbrBackground = (HBRUSH)(COLOR_APPWORKSPACE+1);
|
|
rClass.lpszMenuName = (LPTSTR) MAKEINTRESOURCE(MMCPL);
|
|
rClass.lpszClassName = szClass;
|
|
|
|
if (!RegisterClass(&rClass))
|
|
return (FALSE);
|
|
|
|
rClass.style = CS_HREDRAW | CS_VREDRAW;
|
|
rClass.lpfnWndProc = fnText;
|
|
rClass.cbClsExtra = 0;
|
|
rClass.cbWndExtra = 0;
|
|
rClass.hInstance = hCPlInst;
|
|
rClass.hIcon = NULL;
|
|
rClass.hCursor = LoadCursor (NULL, IDC_ARROW);
|
|
rClass.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
|
|
rClass.lpszMenuName = NULL;
|
|
rClass.lpszClassName = szText;
|
|
|
|
if (!RegisterClass (&rClass))
|
|
return FALSE;
|
|
|
|
GetCPlWndPos (CPlWndPos);
|
|
|
|
//
|
|
// How big are icons around here anyway?
|
|
//
|
|
|
|
iIconH = GetSystemMetrics(SM_CYICON);
|
|
iIconW = GetSystemMetrics(SM_CXICON);
|
|
|
|
hCPlWnd = CreateWindow(szClass, szAppName,
|
|
WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX, // Style bits
|
|
CPlWndPos[0], // x, y position
|
|
CPlWndPos[1],
|
|
CPlWndPos[2], // width/height
|
|
CPlWndPos[3],
|
|
(HWND)NULL, // no parent window
|
|
(HMENU)NULL, // use class menu
|
|
(HANDLE)NULL, // window instance
|
|
(LPVOID)NULL // no params to pass on
|
|
);
|
|
|
|
if (!hCPlWnd)
|
|
return FALSE;
|
|
|
|
//
|
|
// ShowWindow based on flags, with default however.
|
|
//
|
|
|
|
GetStartupInfo (&si);
|
|
|
|
len = (si.dwFlags & STARTF_USESHOWWINDOW) ? (int) si.wShowWindow :
|
|
SW_SHOWNORMAL;
|
|
|
|
ShowWindow (hCPlWnd, len);
|
|
|
|
|
|
if (!LoadAndSizeApplets ())
|
|
{
|
|
MessageBox( hCPlWnd, szErrNoApps, szAppName,
|
|
MB_OK | MB_ICONEXCLAMATION | MB_SYSTEMMODAL );
|
|
rMsg.wParam = 1;
|
|
goto DeregisterPen;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now we can free MUTEX and let other instances run since
|
|
// we have created our main window
|
|
//
|
|
|
|
if (hmutexInit)
|
|
{
|
|
ReleaseMutex (hmutexInit);
|
|
CloseHandle (hmutexInit);
|
|
}
|
|
|
|
//
|
|
// bring up any app on the cmd line
|
|
//
|
|
|
|
if (argc > 1)
|
|
PostMessage(hCPlWnd, WM_CPL_LAUNCH, (DWORD)hCPlWnd, (LONG) v_U[1]);
|
|
|
|
while (GetMessage(&rMsg, NULL, 0, 0))
|
|
{
|
|
if (!TranslateAccelerator(hCPlWnd, hAccel, &rMsg))
|
|
{
|
|
TranslateMessage(&rMsg);
|
|
DispatchMessage(&rMsg);
|
|
}
|
|
}
|
|
|
|
DeregisterPen:
|
|
|
|
return rMsg.wParam;
|
|
}
|
|
|
|
void CPHelp(HWND hwnd, LPTSTR pszHelpFile, DWORD dwContext)
|
|
{
|
|
WORD w;
|
|
|
|
// no help file? use our default help file
|
|
if (!pszHelpFile)
|
|
pszHelpFile = szHelpFile;
|
|
|
|
if (dwContext == 0L)
|
|
w = HELP_INDEX;
|
|
else
|
|
w = HELP_CONTEXT;
|
|
|
|
WinHelp(hwnd, pszHelpFile, w, dwContext);
|
|
}
|
|
|
|
|
|
HWND GetRealParent(HWND hwnd)
|
|
{
|
|
//
|
|
// Run up the parent chain until you find a hwnd
|
|
// that doesn't have WS_CHILD set
|
|
//
|
|
|
|
while (GetWindowLong(hwnd, GWL_STYLE) & WS_CHILD)
|
|
hwnd = (HWND) GetWindowLong (hwnd, GWL_HWNDPARENT);
|
|
|
|
return hwnd;
|
|
}
|
|
|
|
|
|
int APIENTRY MessageFilter(int nCode, DWORD wParam, LPMSG lpMsg)
|
|
{
|
|
if (nCode < 0)
|
|
goto DefHook;
|
|
|
|
if (nCode == MSGF_MENU)
|
|
{
|
|
if (lpMsg->message == WM_KEYDOWN && lpMsg->wParam == VK_F1)
|
|
{
|
|
// Window of menu we want help for is in loword of lParam.
|
|
|
|
// We don't want to process messages for somebody else's menu
|
|
if (IsWindowEnabled(hCPlWnd))
|
|
{
|
|
PostMessage(hCPlWnd, wHelpMessage, MSGF_MENU, (LONG)lpMsg->hwnd);
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
else if (nCode == MSGF_DIALOGBOX)
|
|
{
|
|
if (lpMsg->message == WM_KEYDOWN && lpMsg->wParam == VK_F1)
|
|
{
|
|
// Dialog box we want help for is in loword of lParam
|
|
|
|
// PostMessage (GetParent(lpMsg->hwnd), wHelpMessage, MSGF_DIALOGBOX, (LPARAM)lpMsg->hwnd);
|
|
PostMessage (hCPlWnd, wHelpMessage, MSGF_DIALOGBOX, (LPARAM)lpMsg->hwnd);
|
|
|
|
return 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
DefHook:
|
|
return((int)DefHookProc(nCode, wParam, (LONG)lpMsg, &hhkMsgFilter));
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|