windows-nt/Source/XPSP1/NT/shell/shell32/cplobj.c
2020-09-26 16:20:57 +08:00

1169 lines
34 KiB
C

#include "shellprv.h"
#pragma hdrstop
#include "control.h"
#include "uemapp.h"
#include <limits.h>
#define TF_CPL TF_CUSTOM2
typedef struct
{
ATOM aCPL; // CPL name atom (so we can match requests)
ATOM aApplet; // applet name atom (so we can match requests, may be zero)
HWND hwndStub; // window for this dude (so we can switch to it)
UINT flags; // see PCPLIF_ flags below
} CPLAPPLETID;
//
// PCPLIF_DEFAULT_APPLET
// There are two ways of getting the default applet, asking for it my name
// and passing an empty applet name. This flag should be set regardless,
// so that the code which switches to an already-active applet can always
// find a previous instance if it exists.
//
#define PCPLIF_DEFAULT_APPLET (0x1)
typedef struct
{
int icon;
TCHAR cpl[ CCHPATHMAX ];
TCHAR applet[ MAX_CCH_CPLNAME ];
TCHAR *params;
} CPLEXECINFO;
ATOM aCPLName = (ATOM)0;
ATOM aCPLFlags = (ATOM)0;
void CPL_ParseCommandLine (CPLEXECINFO *info, LPTSTR pszCmdLine, BOOL extract_icon);
BOOL CPL_LoadAndFindApplet (LPCPLMODULE *pcplm, HICON *phIcon, UINT *puControl, CPLEXECINFO *info);
BOOL CPL_FindCPLInfo(LPTSTR pszCmdLine, HICON *phIcon, UINT *ppapl, LPTSTR *pparm)
{
LPCPLMODULE pmod;
CPLEXECINFO info;
CPL_ParseCommandLine(&info, pszCmdLine, TRUE);
if (CPL_LoadAndFindApplet(&pmod, phIcon, ppapl, &info))
{
*pparm = info.params;
CPL_FreeCPLModule(pmod);
return TRUE;
}
*pparm = NULL;
return FALSE;
}
typedef struct _fcc {
LPTSTR lpszClassStub;
CPLAPPLETID *target;
HWND hwndMatch;
} FCC, *LPFCC;
BOOL _FindCPLCallback(HWND hwnd, LPARAM lParam)
{
LPFCC lpfcc = (LPFCC)lParam;
TCHAR szClass[32];
GetClassName(hwnd, szClass, ARRAYSIZE(szClass));
if (lstrcmp(szClass, lpfcc->lpszClassStub) == 0) // Must be same class...
{
// Found a stub window
if (lpfcc->target->aCPL != 0)
{
HANDLE hHandle;
ATOM aCPL;
hHandle = GetProp(hwnd, (LPCTSTR)(DWORD_PTR)aCPLName);
ASSERT((DWORD_PTR) hHandle < USHRT_MAX);
aCPL = (ATOM)(DWORD_PTR) hHandle;
if (aCPL != 0 && aCPL == lpfcc->target->aCPL)
{
ATOM aApplet;
hHandle = GetProp(hwnd, (LPCTSTR)(DWORD_PTR)aCPL);
aApplet = (ATOM)(DWORD_PTR) hHandle;
ASSERT((DWORD_PTR) hHandle < USHRT_MAX);
// users may request any applet by name
if (aApplet != 0 && aApplet == lpfcc->target->aApplet)
{
lpfcc->hwndMatch = hwnd;
return FALSE;
}
//
// Users may request the default w/o specifying a name
//
if (lpfcc->target->flags & PCPLIF_DEFAULT_APPLET)
{
UINT flags = HandleToUlong(GetProp(hwnd, MAKEINTATOM(aCPLFlags)));
if (flags & PCPLIF_DEFAULT_APPLET)
{
lpfcc->hwndMatch = hwnd;
return FALSE;
}
}
}
}
}
return TRUE;
}
HWND FindCPL(HWND hwndStub, CPLAPPLETID *target)
{
FCC fcc;
TCHAR szClassStub[32];
if (aCPLName == (ATOM)0)
{
aCPLName = GlobalAddAtom(TEXT("CPLName"));
aCPLFlags = GlobalAddAtom(TEXT("CPLFlags"));
if (aCPLName == (ATOM)0 || aCPLFlags == (ATOM)0)
return NULL; // This should never happen... didn't find hwnd
}
szClassStub[0] = '\0'; // a NULL hwnd has no class
if (hwndStub)
{
GetClassName(hwndStub, szClassStub, ARRAYSIZE(szClassStub));
}
fcc.lpszClassStub = szClassStub;
fcc.target = target;
fcc.hwndMatch = (HWND)0;
EnumWindows(_FindCPLCallback, (LPARAM)&fcc);
return fcc.hwndMatch;
}
//----------------------------------------------------------------------------
// parsing helper for comma lists
//
TCHAR *CPL_ParseToSeparator(TCHAR *dst, TCHAR *psrc, size_t dstmax, BOOL spacedelimits)
{
if (psrc)
{
TCHAR source[CCHPATHMAX], *src;
TCHAR *delimiter, *closingquote = NULL;
lstrcpyn(source, psrc, (int)((dstmax < ARRAYSIZE(source)) ? dstmax : ARRAYSIZE(source)));
src = source;
//
// eat whitespace
//
while(*src == TEXT(' '))
src++;
delimiter = src;
//
// ignore stuff inside quoted strings
//
if (*src == TEXT('"'))
{
//
// start after first quote, advance src past quote
//
closingquote = ++src;
while(*closingquote && *closingquote != TEXT('"'))
closingquote++;
//
// see if loop above ended on a quote
//
if (*closingquote)
{
//
// temporary NULL termination
//
*closingquote = 0;
//
// start looking for delimiter again after quotes
//
delimiter = closingquote + 1;
}
else
closingquote = NULL;
}
if (spacedelimits)
{
delimiter += StrCSpn(delimiter, TEXT(", "));
if (!*delimiter)
delimiter = NULL;
}
else
delimiter = StrChr(delimiter, TEXT(','));
//
// temporary NULL termination
//
if (delimiter)
*delimiter = 0;
if (dst)
{
lstrcpyn(dst, src, (int)dstmax);
dst[ dstmax - 1 ] = 0;
}
//
// put back stuff we terminated above
//
if (delimiter)
*delimiter = TEXT(',');
if (closingquote)
*closingquote = TEXT('"');
//
// return start of next string
//
psrc = (delimiter ? (psrc + ((delimiter + 1) - source)) : NULL);
}
else if (dst)
{
*dst = 0;
}
//
// new source location
//
return psrc;
}
// parse the Control_RunDLL command line
// format: "CPL name, applet name, extra params"
// format: "CPL name, icon index, applet name, extra params"
//
// NOTE: [stevecat] 3/10/95
//
// The 'extra params' do not have to be delimited by a ","
// in NT for the case "CPL name applet name extra params"
//
// A workaround for applet names that include a space
// in their name would be to enclose that value in
// double quotes (see the CPL_ParseToSeparator routine.)
//
void CPL_ParseCommandLine(CPLEXECINFO *info, LPTSTR pszCmdLine, BOOL extract_icon)
{
//
// parse out the CPL name, spaces are valid separators
//
pszCmdLine = CPL_ParseToSeparator(info->cpl, pszCmdLine, CCHPATHMAX, TRUE);
if (extract_icon)
{
TCHAR icon[ 8 ];
//
// parse out the icon id/index, spaces are not valid separators
//
pszCmdLine = CPL_ParseToSeparator(icon, pszCmdLine, ARRAYSIZE(icon), FALSE);
info->icon = StrToInt(icon);
}
else
info->icon = 0;
//
// parse out the applet name, spaces are not valid separators
//
info->params = CPL_ParseToSeparator(info->applet, pszCmdLine,
MAX_CCH_CPLNAME, FALSE);
CPL_StripAmpersand(info->applet);
}
BOOL CPL_LoadAndFindApplet(LPCPLMODULE *ppcplm, HICON *phIcon, UINT *puControl, CPLEXECINFO *info)
{
TCHAR szControl[MAX_CCH_CPLNAME];
LPCPLMODULE pcplm;
LPCPLITEM pcpli;
int nControl = 0; // fall thru to default
int NumControls;
ENTERCRITICAL;
pcplm = CPL_LoadCPLModule(info->cpl);
if (!pcplm || !pcplm->hacpli)
{
DebugMsg(DM_ERROR, TEXT("Control_RunDLL: ") TEXT("CPL_LoadCPLModule failed \"%s\""), info->cpl);
LEAVECRITICAL;
goto Error0;
}
//
// Look for the specified applet
// no applet specified selects applet 0
//
if (*info->applet)
{
NumControls = DSA_GetItemCount(pcplm->hacpli);
if (info->applet[0] == TEXT('@'))
{
nControl = StrToLong(info->applet+1);
if (nControl >= 0 && nControl < NumControls)
{
goto GotControl;
}
}
//
// Check for the "Setup" argument and send the special CPL_SETUP
// message to the applet to tell it we are running under Setup.
//
if (!lstrcmpi (TEXT("Setup"), info->params))
CPL_CallEntry(pcplm, NULL, CPL_SETUP, 0L, 0L);
for (nControl=0; nControl < NumControls; nControl++)
{
pcpli = DSA_GetItemPtr(pcplm->hacpli, nControl);
lstrcpyn(szControl, pcpli->pszName, ARRAYSIZE(szControl));
CPL_StripAmpersand(szControl);
// if there is only one control, then use it. This solves
// some compat issues with CP names changing.
if (lstrcmpi(info->applet, szControl) == 0 || 1 == NumControls)
break;
}
//
// If we get to the end of the list, bail out
//
// LEGACY WARNING: It might be necessary to handle some old applet names in a special
// way. This would be bad because the names are localized. We would need to somehow
// call into the CPL's to ask them if they support the given name. Only the CPL
// itself would know the correct legacy name mapping. Adding a new CPL message might
// cause as many legacy CPL problems as it solves so we would need to do something tricky
// like adding an exported function. We could then GetProcAddress on this exported function.
// If the export exists, we would pass it the legacy name and it would return a number.
//
// Example: "control mmsys.cpl,Sounds" must work even though mmsys.cpl no longer contains
// an applet called "Sounds". "control mmsys.cpl,Multimedia" must work even though
// mmsys.cpl no longer contains an applet called "Multimedia". You can't simply
// rename the applet becuase these two CPL's were both merged into one CPL. Renaming
// could never solve more than half the problem.
if (nControl >= NumControls)
{
DebugMsg(DM_ERROR, TEXT("Control_RunDLL: ") TEXT("Cannot find specified applet"));
LEAVECRITICAL;
goto Error1;
}
}
GotControl:
if (phIcon != NULL)
{
pcpli = DSA_GetItemPtr(pcplm->hacpli, nControl);
*phIcon = CopyIcon(pcpli->hIcon);
}
LEAVECRITICAL;
//
// yes, we really do want to pass negative indices through...
//
*puControl = (UINT)nControl;
*ppcplm = pcplm;
return TRUE;
Error1:
CPL_FreeCPLModule(pcplm);
Error0:
return FALSE;
}
BOOL CPL_Identify(CPLAPPLETID *identity, CPLEXECINFO *info, HWND stub)
{
identity->aApplet = (ATOM)0;
identity->hwndStub = stub;
identity->flags = 0;
if ((identity->aCPL = GlobalAddAtom(info->cpl)) == (ATOM)0)
return FALSE;
if (*info->applet)
{
if ((identity->aApplet = GlobalAddAtom(info->applet)) == (ATOM)0)
return FALSE;
}
else
{
//
// no applet name means use the default
//
identity->flags = PCPLIF_DEFAULT_APPLET;
}
return TRUE;
}
void CPL_UnIdentify(CPLAPPLETID *identity)
{
if (identity->aCPL)
{
GlobalDeleteAtom(identity->aCPL);
identity->aCPL = (ATOM)0;
}
if (identity->aApplet)
{
GlobalDeleteAtom(identity->aApplet);
identity->aApplet = (ATOM)0;
}
identity->hwndStub = NULL;
identity->flags = 0;
}
// It's time for Legacy Mode!!! In NT5 we removed a bunch of CPL files
// from the product. These files are used by name by many programs. As a result,
// the old names need to keep working even though the files no longer exist.
// We handle this by checking if the file exists. If it does not exist, we run
// the cpl name through a mapping table and then try again. The mapping table can
// potentially change the CPL name, the applet number, and the params.
typedef struct
{
LPTSTR oldInfo_cpl;
LPTSTR oldInfo_applet;
LPTSTR oldInfo_params;
LPTSTR newInfo_cpl;
LPTSTR newInfo_applet;
LPTSTR newInfo_params;
} RUNDLLCPLMAPPING;
// For the oldInfo member, a NULL means to match any value from the pinfo structure.
// If the oldInfo structure mathces the pinfo structure then it will be updated using
// the data from newInfo structure. For the newInfo member, a NULL means to leave the
// corresponding pinfo member unchanged.
const RUNDLLCPLMAPPING g_rgRunDllCPLMapping[] =
{
{ TEXT("MODEM.CPL"), NULL, NULL, TEXT("TELEPHON.CPL"), TEXT("@0"), TEXT("1") },
{ TEXT("UPS.CPL"), NULL, NULL, TEXT("POWERCFG.CPL"), NULL, NULL }
};
BOOL CPL_CheckLegacyMappings(CPLEXECINFO * pinfo)
{
LPTSTR p;
int i;
TraceMsg(TF_CPL, "Attmepting Legacy CPL conversion on %s", pinfo->cpl);
// we want only the filename, strip off any path information
p = PathFindFileName(pinfo->cpl);
StrCpyN(pinfo->cpl, p, CCHPATHMAX);
for (i = 0; i < ARRAYSIZE(g_rgRunDllCPLMapping); i++)
{
if (0 == StrCmpI(pinfo->cpl, g_rgRunDllCPLMapping[i].oldInfo_cpl))
{
if (!g_rgRunDllCPLMapping[i].oldInfo_applet ||
0 == StrCmpI(pinfo->applet, g_rgRunDllCPLMapping[i].oldInfo_applet))
{
if (!g_rgRunDllCPLMapping[i].oldInfo_params ||
(pinfo->params &&
0 == StrCmpI(pinfo->params, g_rgRunDllCPLMapping[i].oldInfo_params)
)
)
{
if (pinfo->params)
{
TraceMsg(TF_CPL, "%s,%s,%s matches item %d", pinfo->cpl, pinfo->applet, pinfo->params, i);
}
else
{
TraceMsg(TF_CPL, "%s,%s matches item %d", pinfo->cpl, pinfo->applet, i);
}
// The current entry matches the request. Map to the new info and then
// ensure the new CPL exists.
StrCpyN(pinfo->cpl, g_rgRunDllCPLMapping[i].newInfo_cpl, CCHPATHMAX);
if (g_rgRunDllCPLMapping[i].newInfo_applet)
{
StrCpyN(pinfo->applet, g_rgRunDllCPLMapping[i].newInfo_applet, MAX_CCH_CPLNAME);
}
if (g_rgRunDllCPLMapping[i].newInfo_params)
{
// the params pointer is normally a pointer into the remaining chunk of a string
// buffer. As such, we don't need to delete the memory it points to. Also, this
// argument is read only so it should be safe for us to point it at our constant
// data.
pinfo->params = g_rgRunDllCPLMapping[i].newInfo_params;
}
if (pinfo->params)
{
TraceMsg(TF_CPL, "CPL mapped to %s,%s,%s", pinfo->cpl, pinfo->applet, pinfo->params);
}
else
{
TraceMsg(TF_CPL, "CPL mapped to %s,%s", pinfo->cpl, pinfo->applet);
}
return PathFindOnPath(pinfo->cpl, NULL);
}
}
}
}
return FALSE;
}
// Goes through all of the work of identifying and starting a control
// applet. Accepts a flag specifying whether or not to load a new DLL if it
// is not already present. This code will ALLWAYS switch to an existing
// instance of the applet if bFindExisting is specified.
//
// WARNING: this function butchers the command line you pass in!
BOOL CPL_RunMeBaby(HWND hwndStub, HINSTANCE hAppInstance, LPTSTR pszCmdLine, int nCmdShow, BOOL bAllowLoad, BOOL bFindExisting)
{
int nApplet;
LPCPLMODULE pcplm;
LPCPLITEM pcpli;
CPLEXECINFO info;
CPLAPPLETID identity;
TCHAR szApplet[ MAX_CCH_CPLNAME ];
BOOL bResult = FALSE;
HWND hwndOtherStub;
HRESULT hrInit;
if (SHRestricted(REST_NOCONTROLPANEL))
{
ShellMessageBox(HINST_THISDLL, hwndStub, MAKEINTRESOURCE(IDS_RESTRICTIONS),
MAKEINTRESOURCE(IDS_RESTRICTIONSTITLE), MB_OK|MB_ICONSTOP);
return FALSE;
}
hrInit = SHCoInitialize();
//
// parse the command line we got
//
CPL_ParseCommandLine(&info, pszCmdLine, FALSE);
//
// no applet to run means open the controls folder
//
if (!*info.cpl)
{
InvokeFolderPidl(MAKEINTIDLIST(CSIDL_CONTROLS), nCmdShow);
bResult = TRUE;
goto Error0;
}
// expand CPL name to a full path if it isn't already
if (PathIsFileSpec(info.cpl))
{
if (!PathFindOnPath(info.cpl, NULL))
{
if (!CPL_CheckLegacyMappings(&info))
goto Error0;
}
}
else if (!PathFileExists(info.cpl))
{
if (!CPL_CheckLegacyMappings(&info))
goto Error0;
}
if (!CPL_Identify(&identity, &info, hwndStub))
goto Error0;
//
// If we have already loaded this CPL, then jump to the existing window
//
hwndOtherStub = FindCPL(hwndStub, &identity);
//
// If we found a window and the caller says its ok to find an existing
// window then set the focus to it
//
if (bFindExisting && hwndOtherStub)
{
//
// try to find a CPL window on top of it
//
HWND hwndTarget = GetLastActivePopup(hwndOtherStub);
if (hwndTarget && IsWindow(hwndTarget))
{
DebugMsg(DM_WARNING, TEXT("Control_RunDLL: ") TEXT("Switching to already loaded CPL applet"));
SetForegroundWindow(hwndTarget);
bResult = TRUE;
goto Error1;
}
//
// couldn't find it, must be exiting or some sort of error...
// so ignore it.
//
DebugMsg(DM_WARNING, TEXT("Control_RunDLL: ") TEXT("Bogus CPL identity in array; purging after (presumed) RunDLL crash"));
}
//
// stop here if we're not allowed to load the cpl
//
if (!bAllowLoad)
goto Error1;
//
// i guess we didn't stop up there
//
if (!CPL_LoadAndFindApplet(&pcplm, NULL, &nApplet, &info))
goto Error1;
//
// get the name that the applet thinks it should have
//
pcpli = DSA_GetItemPtr(pcplm->hacpli, nApplet);
lstrcpyn(szApplet, pcpli->pszName, ARRAYSIZE(szApplet));
CPL_StripAmpersand(szApplet);
// handle "default applet" cases before running anything
if (identity.aApplet)
{
// we were started with an explicitly named applet
if (!nApplet)
{
// we were started with the name of the default applet
identity.flags |= PCPLIF_DEFAULT_APPLET;
}
}
else
{
// we were started without a name, assume the default applet
identity.flags |= PCPLIF_DEFAULT_APPLET;
// get the applet's name (now that we've loaded it's CPL)
if ((identity.aApplet = GlobalAddAtom(szApplet)) == (ATOM)0)
{
// bail 'cause we could nuke a CPL if we don't have this
goto Error2;
}
}
// mark the window so we'll be able to verify that it's really ours
if (aCPLName == (ATOM)0)
{
aCPLName = GlobalAddAtom(TEXT("CPLName"));
aCPLFlags = GlobalAddAtom(TEXT("CPLFlags"));
if (aCPLName == (ATOM)0 || aCPLFlags == (ATOM)0)
goto Error2; // This should never happen... blow off applet
}
if (!SetProp(hwndStub, // Mark its name
MAKEINTATOM(aCPLName), (HANDLE)(DWORD_PTR)identity.aCPL))
{
goto Error2;
}
if (!SetProp(hwndStub, // Mark its applet
MAKEINTATOM(identity.aCPL), (HANDLE)(DWORD_PTR)identity.aApplet))
{
goto Error2;
}
if (identity.flags)
{
if (aCPLFlags == (ATOM)0)
aCPLFlags = GlobalAddAtom(TEXT("CPLFlags"));
// Mark its flags
SetProp(hwndStub, MAKEINTATOM(aCPLFlags), (HANDLE)UIntToPtr(identity.flags));
}
//
// Send the stub window a message so it will have the correct title and
// icon in the alt-tab window, etc...
//
if (hwndStub) {
DWORD dwPID;
SendMessage(hwndStub, STUBM_SETICONTITLE, (WPARAM)pcpli->hIcon, (LPARAM)szApplet);
GetWindowThreadProcessId(hwndStub, &dwPID);
if (dwPID == GetCurrentProcessId()) {
RUNDLL_NOTIFY sNotify;
sNotify.hIcon = pcpli->hIcon;
sNotify.lpszTitle = szApplet;
// HACK: It will look like the stub window is sending itself
// a WM_NOTIFY message. Oh well.
//
SendNotify(hwndStub, hwndStub, RDN_TASKINFO, (NMHDR FAR*)&sNotify);
}
}
if (info.params)
{
DebugMsg(DM_TRACE, TEXT("Control_RunDLL: ") TEXT("Sending CPL_STARTWPARAMS to applet with: %s"), info.params);
bResult = BOOLFROMPTR(CPL_CallEntry(pcplm, hwndStub, CPL_STARTWPARMS, (LONG)nApplet, (LPARAM)info.params));
}
// Check whether we need to run as a different windows version
{
PPEB Peb = NtCurrentPeb();
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pcplm->minst.hinst;
PIMAGE_NT_HEADERS pHeader = (PIMAGE_NT_HEADERS)((DWORD_PTR)pcplm->minst.hinst + pDosHeader->e_lfanew);
if (pHeader->FileHeader.SizeOfOptionalHeader != 0 &&
pHeader->OptionalHeader.Win32VersionValue != 0)
{
//
// Stolen from ntos\mm\procsup.c
//
Peb->OSMajorVersion = pHeader->OptionalHeader.Win32VersionValue & 0xFF;
Peb->OSMinorVersion = (pHeader->OptionalHeader.Win32VersionValue >> 8) & 0xFF;
Peb->OSBuildNumber = (USHORT) ((pHeader->OptionalHeader.Win32VersionValue >> 16) & 0x3FFF);
Peb->OSPlatformId = (pHeader->OptionalHeader.Win32VersionValue >> 30) ^ 0x2;
}
}
#ifdef UNICODE
//
// If the cpl didn't respond to CPL_STARTWPARMSW (unicode version),
// maybe it is an ANSI only CPL
//
if (info.params && (!bResult))
{
int cchParams = WideCharToMultiByte(CP_ACP, 0, info.params, -1, NULL, 0, NULL, NULL);
LPSTR lpstrParams = LocalAlloc(LMEM_FIXED, sizeof(char) * cchParams);
if (lpstrParams != NULL)
{
WideCharToMultiByte(CP_ACP, 0, info.params, -1, lpstrParams, cchParams, NULL, NULL);
DebugMsg(DM_TRACE, TEXT("Control_RunDLL: ") TEXT("Sending CPL_STARTWPARAMSA to applet with: %hs"), lpstrParams);
bResult = BOOLFROMPTR(CPL_CallEntry(pcplm, hwndStub, CPL_STARTWPARMSA, (LONG)nApplet, (LPARAM)lpstrParams));
LocalFree(lpstrParams);
}
}
#endif
if (!bResult)
{
DebugMsg(DM_TRACE, TEXT("Control_RunDLL: ") TEXT("Sending CPL_DBLCLK to applet"));
CPL_CallEntry(pcplm, hwndStub, CPL_DBLCLK, (LONG)nApplet, pcpli->lData);
// some 3x applets return the wrong value so we can't fail here
bResult = TRUE;
}
bResult = TRUE; // make it!
RemoveProp(hwndStub, (LPCTSTR)(UINT_PTR)identity.aCPL);
Error2:
CPL_FreeCPLModule(pcplm);
Error1:
CPL_UnIdentify(&identity);
Error0:
SHCoUninitialize(hrInit);
return bResult;
}
//
// Check the following reg location and see if this CPL is registered to run in proc:
// HKLM\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\ControlPanel\\InProcCPLs
//
STDAPI_(BOOL) CPL_IsInProc(LPCTSTR pszCmdLine)
{
BOOL bInProcCPL = FALSE;
TCHAR szTempCmdLine[2 * MAX_PATH];
CPLEXECINFO info = {0};
LPTSTR pszCPLFile = NULL;
ASSERT(pszCmdLine);
// Make a copy of the command line
lstrcpyn(szTempCmdLine, pszCmdLine, ARRAYSIZE(szTempCmdLine));
// Parse the command line using standard parsing function
CPL_ParseCommandLine(&info, szTempCmdLine, FALSE);
// Find the file name of this cpl
pszCPLFile = PathFindFileName(info.cpl);
if (pszCPLFile)
{
// Open the reg key
HKEY hkeyInProcCPL = NULL;
if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE,
TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\ControlPanel\\InProcCPLs"), 0, KEY_READ, &hkeyInProcCPL))
{
// Look up in the registry for this cpl name
LONG cbData;
if (ERROR_SUCCESS == SHQueryValueEx(hkeyInProcCPL, pszCPLFile, NULL, NULL, NULL, &cbData))
bInProcCPL = TRUE;
RegCloseKey(hkeyInProcCPL);
}
}
return bInProcCPL;
}
BOOL UsePCHealthFaultUploading(LPCTSTR pszCmdLine)
{
// Do we want exceptions to go unhandled so PCHealth will upload the faults?
BOOL fUsePCHealth = FALSE; // By default no, because
LPCTSTR pszFilename = PathFindFileName(pszCmdLine);
if (pszFilename)
{
DWORD dwType;
DWORD dwFlags;
DWORD cbSize = sizeof(dwFlags);
DWORD dwError = SHGetValue(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\ControlPanel\\Flags"),
pszFilename, &dwType, (void *)&dwFlags, &cbSize);
if ((ERROR_SUCCESS == dwError) && (REG_DWORD == dwType))
{
// The 0x00000001 bit will indicate if they want to not have exceptions caught for them.
if (0x00000001 & dwFlags)
{
fUsePCHealth = TRUE;
}
}
}
return fUsePCHealth;
}
//
// Starts a remote control applet on a new RunDLL process
// Or on another thread InProcess
//
STDAPI_(BOOL) CPL_RunRemote(LPCTSTR pszCmdLine, HWND hwnd, BOOL fRunAsNewUser)
{
BOOL bRet = FALSE;
TCHAR szRunParams[2 * MAX_PATH];
BOOL fUsePCHealth = UsePCHealthFaultUploading(pszCmdLine);
if (fRunAsNewUser)
{
wnsprintf(szRunParams, ARRAYSIZE(szRunParams), TEXT("%sshell32.dll,Control_RunDLLAsUser %s"), (fUsePCHealth ? TEXT("/d ") : TEXT("")), pszCmdLine);
}
else
{
wnsprintf(szRunParams, ARRAYSIZE(szRunParams), TEXT("%sshell32.dll,Control_RunDLL %s"), (fUsePCHealth ? TEXT("/d ") : TEXT("")), pszCmdLine);
}
if (!fRunAsNewUser && CPL_IsInProc(pszCmdLine))
{
// lanuch this cpl in process from another thread
bRet = SHRunDLLThread(hwnd, szRunParams, SW_SHOWNORMAL);
}
else
{
// lanuch this cpl on another thread
bRet = SHRunDLLProcess(hwnd, szRunParams, SW_SHOWNORMAL, IDS_CONTROLPANEL, fRunAsNewUser);
}
return bRet;
}
//
// Attempts to open the specified control applet.
// Tries to switch to an existing instance before starting a new one, unless the user
// specifies the fRunAsNewUser in which case we always launch a new process.
//
STDAPI_(BOOL) SHRunControlPanelEx(LPCTSTR pszOrigCmdLine, HWND hwnd, BOOL fRunAsNewUser)
{
BOOL bRes = FALSE;
LPTSTR pszCmdLine = NULL;
// check to see if the caller passed a resource id instead of a string
if (!IS_INTRESOURCE(pszOrigCmdLine))
{
pszCmdLine = StrDup(pszOrigCmdLine);
}
else
{
TCHAR szCmdLine[MAX_PATH];
if (LoadString(HINST_THISDLL, PtrToUlong((void *)pszOrigCmdLine), szCmdLine, ARRAYSIZE(szCmdLine)))
pszCmdLine = StrDup(szCmdLine);
}
//
// CPL_RunMeBaby whacks on the command line while parsing...use a dup
//
if (pszCmdLine)
{
if (!fRunAsNewUser)
{
// if fRunAsNewUser is NOT specified, then try to switch to an active CPL
// which matches our pszCmdLine
bRes = CPL_RunMeBaby(NULL, NULL, pszCmdLine, SW_SHOWNORMAL, FALSE, TRUE);
}
if (!bRes)
{
// launch a new cpl in a separate process
bRes = CPL_RunRemote(pszCmdLine, hwnd, fRunAsNewUser);
}
LocalFree(pszCmdLine);
}
if (bRes && UEMIsLoaded() && !IS_INTRESOURCE(pszOrigCmdLine))
{
UEMFireEvent(&UEMIID_SHELL, UEME_RUNCPL, UEMF_XEVENT, -1, (LPARAM)pszOrigCmdLine);
}
return bRes;
}
// This function is a TCHAR export from shell32 (header defn is in shsemip.h)
//
// UNDOCUMENTED: You may pass a shell32 resource ID in place of a pszCmdLine
//
STDAPI_(BOOL) SHRunControlPanel(LPCTSTR pszOrigCmdLine, HWND hwnd)
{
return SHRunControlPanelEx(pszOrigCmdLine, hwnd, FALSE);
}
//
// Attempts to open the specified control applet.
// This function is intended to be called by RunDLL for isolating applets.
// Tries to switch to an existing instance before starting a new one.
//
// The command lines for Control_RunDLL are as follows:
//
// 1) rundll32 shell32.dll,Control_RunDLL fred.cpl,@n,arguments
//
// This launches the (n+1)th applet in fred.cpl.
//
// If "@n" is not supplied, the default is @0.
//
// 2) rundll32 shell32.dll,Control_RunDLL fred.cpl,Ba&rney,arguments
//
// This launches the applet in fred.cpl named "Barney". Ampersands are
// stripped from the name.
//
// 3) rundll32 shell32.dll,Control_RunDLL fred.cpl,Setup
//
// This loads fred.cpl and sends it a CPL_SETUP message.
//
// In cases (1) and (2), the "arguments" are passed to the applet via
// the CPL_STARTWPARAMS (start with parameters) message. It is the
// applet's job to parse the arguments and do something interesting.
//
// It is traditional for the command line of a cpl to be the index of
// the page that should initially be shown to the user, but that's just
// tradition.
//
STDAPI_(void) Control_RunDLL(HWND hwndStub, HINSTANCE hAppInstance, LPSTR pszCmdLine, int nCmdShow)
{
TCHAR szCmdLine[MAX_PATH * 2];
SHAnsiToTChar(pszCmdLine, szCmdLine, ARRAYSIZE(szCmdLine));
CPL_RunMeBaby(hwndStub, hAppInstance, szCmdLine, nCmdShow, TRUE, TRUE);
}
STDAPI_(void) Control_RunDLLW(HWND hwndStub, HINSTANCE hAppInstance, LPWSTR lpwszCmdLine, int nCmdShow)
{
TCHAR szCmdLine[MAX_PATH * 2];
SHUnicodeToTChar(lpwszCmdLine, szCmdLine, ARRAYSIZE(szCmdLine));
CPL_RunMeBaby(hwndStub, hAppInstance, szCmdLine, nCmdShow, TRUE, TRUE);
}
// This is the entry that gets called when we run a cpl as a new user.
//
STDAPI_(void) Control_RunDLLAsUserW(HWND hwndStub, HINSTANCE hAppInstance, LPWSTR lpwszCmdLine, int nCmdShow)
{
CPL_RunMeBaby(hwndStub, hAppInstance, lpwszCmdLine, nCmdShow, TRUE, FALSE);
}
// data passed around dialog and worker thread for Control_FillCache_RunDLL
typedef struct
{
IShellFolder * psfControl;
IEnumIDList * penumControl;
HWND dialog;
} FillCacheData;
//
// important work of Control_FillCache_RunDLL
// jogs the control panel enumerator so it will fill the presentation cache
// also forces the applet icons to be extracted into the shell icon cache
//
DWORD CALLBACK Control_FillCacheThreadProc(void *pv)
{
FillCacheData *data = (FillCacheData *)pv;
LPITEMIDLIST pidlApplet;
ULONG dummy;
while(data->penumControl->lpVtbl->Next(data->penumControl, 1, &pidlApplet, &dummy) == NOERROR)
{
SHMapPIDLToSystemImageListIndex(data->psfControl, pidlApplet, NULL);
ILFree(pidlApplet);
}
if (data->dialog)
EndDialog(data->dialog, 0);
return 0;
}
//
// dlgproc for Control_FillCache_RunDLL UI
// just something to keep the user entertained while we load a billion DLLs
//
BOOL_PTR CALLBACK _Control_FillCacheDlg(HWND dialog, UINT message, WPARAM wparam, LPARAM lparam)
{
switch(message)
{
case WM_INITDIALOG:
{
DWORD dummy;
HANDLE thread;
((FillCacheData *)lparam)->dialog = dialog;
thread = CreateThread(NULL, 0, Control_FillCacheThreadProc, (void*)lparam, 0, &dummy);
if (thread)
CloseHandle(thread);
else
EndDialog(dialog, -1);
}
break;
case WM_COMMAND:
break;
default:
return FALSE;
}
return TRUE;
}
//
// enumerates control applets in a manner that fills the presentation cache
// this is so the first time a user opens the control panel it comes up fast
// intended to be called at final setup on first boot
//
// FUNCTION WORKS FOR BOTH ANSI/UNICODE, it never uses pszCmdLine
//
STDAPI_(void) Control_FillCache_RunDLL(HWND hwndStub, HINSTANCE hAppInstance, LPSTR pszCmdLine, int nCmdShow)
{
IShellFolder *psfDesktop;
HKEY hk;
// nuke the old data so that any bogus cached info from a beta goes away
if (RegOpenKey(HKEY_LOCAL_MACHINE, REGSTR_PATH_CONTROLSFOLDER, &hk) == ERROR_SUCCESS)
{
RegDeleteValue(hk, TEXT("Presentation Cache"));
RegCloseKey(hk);
}
SHGetDesktopFolder(&psfDesktop);
Shell_GetImageLists(NULL, NULL); // make sure icon cache is around
if (psfDesktop)
{
LPITEMIDLIST pidlControl = SHCloneSpecialIDList(hwndStub, CSIDL_CONTROLS, FALSE);
if (pidlControl)
{
FillCacheData data = {0};
if (SUCCEEDED(psfDesktop->lpVtbl->BindToObject(psfDesktop,
pidlControl, NULL, &IID_IShellFolder, &data.psfControl)))
{
if (S_OK == data.psfControl->lpVtbl->EnumObjects(
data.psfControl, NULL, SHCONTF_NONFOLDERS, &data.penumControl))
{
if (nCmdShow == SW_HIDE || DialogBoxParam(HINST_THISDLL,
MAKEINTRESOURCE(DLG_CPL_FILLCACHE), hwndStub,
_Control_FillCacheDlg, (LPARAM)&data) == -1)
{
Control_FillCacheThreadProc(&data);
}
data.penumControl->lpVtbl->Release(data.penumControl);
}
data.psfControl->lpVtbl->Release(data.psfControl);
}
ILFree(pidlControl);
}
}
}
STDAPI_(void) Control_FillCache_RunDLLW(HWND hwndStub, HINSTANCE hAppInstance, LPWSTR lpwszCmdLine, int nCmdShow)
{
Control_FillCache_RunDLL(hwndStub,hAppInstance,NULL,nCmdShow);
}