1537 lines
47 KiB
C
1537 lines
47 KiB
C
/****************************** Module Header ******************************\
|
|
* Module Name: clinit.c
|
|
|
|
* Copyright (c) 1985 - 1999, Microsoft Corporation
|
|
|
|
* This module contains all the init code for the USER.DLL.
|
|
* When the DLL is dynlinked its initialization procedure (UserDllInitialize) is called by the loader.
|
|
|
|
* History:
|
|
* 18-Sep-1990 DarrinM Created.
|
|
*/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
#include <conroute.h>
|
|
#include "csrhlpr.h"
|
|
|
|
#include "badapps.h"
|
|
|
|
/*
|
|
* Global variables local to this module (startup).
|
|
*/
|
|
BOOL gfFirstThread = TRUE;
|
|
PDESKTOPINFO pdiLocal;
|
|
BOOL gbIhaveBeenInited;
|
|
static DWORD gdwLpkEntryPoints;
|
|
|
|
CONST WCHAR pwszWindowsKey[] = L"\\Registry\\Machine\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Windows";
|
|
CONST WCHAR szAppInit[] = L"AppInit_DLLs";
|
|
|
|
CONST WCHAR gwszShimDll[] = L"shim.dll";
|
|
CONST CHAR gszLoadPathDll[] = "LoadPatchDll";
|
|
|
|
/*
|
|
* External declared routines needed for startup.
|
|
*/
|
|
BOOL GdiProcessSetup();
|
|
BOOL GdiDllInitialize(IN PVOID hmod, IN DWORD Reason, IN PCONTEXT pctx OPTIONAL);
|
|
|
|
WCHAR szWindowStationDirectory[MAX_SESSION_PATH];
|
|
#ifdef _JANUS_
|
|
BOOL InitInstrument(LPDWORD lpEMIControl);
|
|
#endif
|
|
|
|
extern CONST PCSR_CALLBACK_ROUTINE apfnDispatch[];
|
|
extern CONST ULONG ulMaxApiIndex;
|
|
|
|
|
|
BOOL UserClientDllInitialize(IN PVOID hmod, IN DWORD Reason, IN PCONTEXT pctx OPTIONAL)
|
|
/*
|
|
* When USER.DLL is loaded by an EXE (either at EXE load or at LoadModule time) this routine is called by the loader.
|
|
* Its purpose is to initialize everything that will be needed for future User API calls by the app.
|
|
|
|
* History:
|
|
* 19-Sep-1990 DarrinM Created.
|
|
*/
|
|
{
|
|
SYSTEM_BASIC_INFORMATION SystemInformation;
|
|
BOOL bRet = FALSE;
|
|
|
|
DBG_UNREFERENCED_PARAMETER(pctx);
|
|
|
|
#if DBG
|
|
if (RtlGetNtGlobalFlags() & FLG_SHOW_LDR_SNAPS) {
|
|
RIPMSG1(RIP_WARNING, "UserClientDllInitialize: entered for reason %x", Reason);
|
|
}
|
|
#endif
|
|
|
|
if (Reason == DLL_PROCESS_ATTACH) {
|
|
NTSTATUS status;
|
|
USERCONNECT userconnect;
|
|
ULONG ulConnect = sizeof(USERCONNECT);
|
|
ULONG SessionId = NtCurrentPeb()->SessionId;
|
|
WCHAR szSessionDir[MAX_SESSION_PATH];
|
|
|
|
DisableThreadLibraryCalls(hmod);
|
|
|
|
if (gbIhaveBeenInited) {
|
|
return TRUE;
|
|
}
|
|
|
|
gbIhaveBeenInited = TRUE;
|
|
|
|
status = RtlInitializeCriticalSection(&gcsClipboard);
|
|
status |= RtlInitializeCriticalSection(&gcsLookaside);
|
|
status |= RtlInitializeCriticalSection(&gcsHdc);
|
|
status |= RtlInitializeCriticalSection(&gcsAccelCache);
|
|
status |= RtlInitializeCriticalSection(&gcsDDEML);
|
|
#ifdef WX86
|
|
status |= RtlInitializeCriticalSection(&gcsWx86Load);
|
|
#endif
|
|
if (!NT_SUCCESS(status)) {
|
|
RIPMSG1(RIP_WARNING, "UserClientDllInitialize: Failed to create critical sections. Status %x", status);
|
|
return FALSE;
|
|
}
|
|
#if DBG
|
|
gpDDEMLHeap = RtlCreateHeap(HEAP_GROWABLE | HEAP_CLASS_1 | HEAP_TAIL_CHECKING_ENABLED | HEAP_FREE_CHECKING_ENABLED,
|
|
NULL,
|
|
8 * 1024, 2 * 1024,
|
|
NULL,
|
|
NULL);
|
|
if (gpDDEMLHeap == NULL) {
|
|
gpDDEMLHeap = RtlProcessHeap();
|
|
}
|
|
#endif
|
|
|
|
status = NtQuerySystemInformation(SystemBasicInformation, &SystemInformation, sizeof(SystemInformation), NULL);
|
|
if (!NT_SUCCESS(status)) {
|
|
RIPMSG1(RIP_WARNING, "UserClientDllInitialize: NtQuerySystemInformation failed with Status %x", status);
|
|
return FALSE;
|
|
}
|
|
gHighestUserAddress = SystemInformation.MaximumUserModeAddress;
|
|
|
|
userconnect.ulVersion = USERCURRENTVERSION;
|
|
|
|
if (ISTS()) {
|
|
if (SessionId == 0) {
|
|
wcscpy(szSessionDir, WINSS_OBJECT_DIRECTORY_NAME);
|
|
} else {
|
|
swprintf(szSessionDir, L"%ws\\%ld%ws", SESSION_ROOT, SessionId, WINSS_OBJECT_DIRECTORY_NAME);
|
|
}
|
|
|
|
status = UserConnectToServer(szSessionDir, &userconnect, &ulConnect, (PBOOLEAN)&gfServerProcess);
|
|
} else {
|
|
status = UserConnectToServer(WINSS_OBJECT_DIRECTORY_NAME, &userconnect, &ulConnect, (PBOOLEAN)&gfServerProcess);
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
RIPMSG1(RIP_WARNING, "UserClientDllInitialize: UserConnectToServer failed with status %x", status);
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* If this is the server process, the shared info is not yet valid, so don't copy out the returned info.
|
|
*/
|
|
if (!gfServerProcess) {
|
|
HINSTANCE hImm32 = NULL;
|
|
|
|
gSharedInfo = userconnect.siClient;
|
|
gpsi = gSharedInfo.psi;
|
|
|
|
// make sure the stub (dummy) routine works
|
|
UserAssert(sizeof(LRESULT) == sizeof(ULONG_PTR));
|
|
UserAssert(sizeof(PVOID) == sizeof(ULONG_PTR));
|
|
if (IS_IME_ENABLED()) {
|
|
WCHAR wszImmFile[MAX_PATH];
|
|
|
|
InitializeImmEntryTable();
|
|
GetImmFileName(wszImmFile);
|
|
hImm32 = GetModuleHandleW(wszImmFile);
|
|
}
|
|
if (!fpImmRegisterClient(&userconnect.siClient, hImm32)) {
|
|
RIPMSG0(RIP_WARNING, "UserClientDllInitialize: ImmRegisterClient failed");
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
pfnFindResourceExA = (PFNFINDA)FindResourceExA;
|
|
pfnFindResourceExW = (PFNFINDW)FindResourceExW;
|
|
pfnLoadResource = (PFNLOAD)LoadResource;
|
|
pfnSizeofResource = (PFNSIZEOF)SizeofResource;
|
|
|
|
/*
|
|
* Register with the base the USER hook it should call when it does a WinExec() (this is soft-linked because some people still use charmode nt!
|
|
*/
|
|
RegisterWaitForInputIdle(WaitForInputIdle);
|
|
|
|
/*
|
|
* Remember USER.DLL's hmodule so we can grab resources from it later.
|
|
*/
|
|
hmodUser = hmod;
|
|
|
|
pUserHeap = RtlProcessHeap();
|
|
|
|
/*
|
|
* Initialize callback table
|
|
*/
|
|
NtCurrentPeb()->KernelCallbackTable = apfnDispatch;
|
|
NtCurrentPeb()->PostProcessInitRoutine = NULL;
|
|
|
|
if (SessionId != 0) {
|
|
swprintf(szWindowStationDirectory, L"%ws\\%ld%ws", SESSION_ROOT, SessionId, WINSTA_DIR);
|
|
RtlInitUnicodeString(&strRootDirectory, szWindowStationDirectory);
|
|
} else {
|
|
RtlInitUnicodeString(&strRootDirectory, WINSTA_DIR);
|
|
}
|
|
|
|
#ifdef _JANUS_
|
|
gfEMIEnable = InitInstrument(&gdwEMIControl);
|
|
if (gfServerProcess) {
|
|
gfEMIEnable = FALSE;
|
|
}
|
|
#endif
|
|
} else if (Reason == DLL_PROCESS_DETACH) {
|
|
if (ghImm32 != NULL) {
|
|
FreeLibrary(ghImm32);// IMM32.DLL is loaded by USER32, so free it.
|
|
}
|
|
|
|
/*
|
|
* If we loaded OLE, tell it we're done.
|
|
*/
|
|
if (ghinstOLE != NULL) {
|
|
/*
|
|
* Later5.0 GerardoB. This causes check OLE32.DLL to fault
|
|
* because they get their DLL_PROCESS_DETACH first
|
|
* (*(OLEUNINITIALIZEPROC)gpfnOLEOleUninitialize)();
|
|
*/
|
|
RIPMSG0(RIP_WARNING, "OLE would fault if I call OleUninitialize now");
|
|
FreeLibrary(ghinstOLE);
|
|
}
|
|
|
|
/*
|
|
* If we loaded WinSta.Dll for Terminal Services, then unload it now.
|
|
*/
|
|
if (ghinstWinStaDll != NULL) {
|
|
FreeLibrary(ghinstWinStaDll);
|
|
}
|
|
|
|
#ifdef _JANUS_
|
|
if (gfEMIEnable && (ghAdvApi != NULL)) {
|
|
FreeLibrary(ghAdvApi);
|
|
}
|
|
#endif
|
|
|
|
RtlDeleteCriticalSection(&gcsClipboard);
|
|
RtlDeleteCriticalSection(&gcsLookaside);
|
|
RtlDeleteCriticalSection(&gcsHdc);
|
|
RtlDeleteCriticalSection(&gcsAccelCache);
|
|
RtlDeleteCriticalSection(&gcsDDEML);
|
|
#if DBG
|
|
if (gpDDEMLHeap != RtlProcessHeap()) {
|
|
RtlDestroyHeap(gpDDEMLHeap);
|
|
}
|
|
#endif
|
|
|
|
#ifdef WX86
|
|
RtlDeleteCriticalSection(&gcsWx86Load);
|
|
#endif
|
|
}
|
|
|
|
bRet = GdiDllInitialize(hmod, Reason, pctx);
|
|
if (!bRet) {
|
|
RIPMSG0(RIP_WARNING, "UserClientDllInitialize: GdiDllInitialize failed");
|
|
}
|
|
|
|
return(bRet);
|
|
}
|
|
|
|
|
|
BOOL LoadCursorsAndIcons(VOID)
|
|
/*
|
|
* This gets called from our initialization call from csr so they're around when window classes get registered.
|
|
* Window classes get registered right after the initial csr initialization call.
|
|
|
|
* Later on these default images will get overwritten by custom registry entries.
|
|
* See UpdateCursors/IconsFromRegistry().
|
|
|
|
* 27-Sep-1992 ScottLu Created.
|
|
* 14-Oct-1995 SanfordS Rewrote.
|
|
*/
|
|
{
|
|
int i;
|
|
HANDLE h;
|
|
BOOL fSuccess = TRUE;
|
|
|
|
for (i = 0; i < COIC_CONFIGURABLE; i++) {
|
|
/*
|
|
* load the small version of WINLOGO which will be set into
|
|
* gpsi->hIconSmWindows on the kernel side.
|
|
*/
|
|
if (i == OIC_WINLOGO_DEFAULT - OIC_FIRST_DEFAULT) {
|
|
h = LoadIcoCur(NULL, (LPCWSTR)UIntToPtr((OIC_FIRST_DEFAULT + i)), RT_ICON, SYSMET(CXSMICON), SYSMET(CYSMICON), LR_GLOBAL);
|
|
fSuccess &= !!h;
|
|
}
|
|
h = LoadIcoCur(NULL, (LPCWSTR)UIntToPtr((OIC_FIRST_DEFAULT + i)), RT_ICON, 0, 0, LR_SHARED | LR_GLOBAL);
|
|
fSuccess &= !!h;
|
|
}
|
|
|
|
for (i = 0; i < COCR_CONFIGURABLE; i++) {
|
|
h = LoadIcoCur(NULL, (LPCWSTR)UIntToPtr((OCR_FIRST_DEFAULT + i)), RT_CURSOR, 0, 0, LR_SHARED | LR_GLOBAL);
|
|
fSuccess &= !!h;
|
|
}
|
|
|
|
if (!fSuccess) {
|
|
RIPMSG0(RIP_WARNING, "LoadCursorsAndIcons failed to load cursors");
|
|
}
|
|
|
|
/*
|
|
* Now go to the kernel and fixup the IDs from DEFAULT values
|
|
* to standard values.
|
|
*/
|
|
NtUserCallNoParam(SFI__LOADCURSORSANDICONS);
|
|
|
|
return fSuccess;
|
|
}
|
|
|
|
|
|
/*
|
|
* NOTE -- the class names must stay in the RegisterClass exactly as they are, since
|
|
* MS-TEST assumes these names exist as strings.
|
|
*/
|
|
|
|
BOOL RW_RegisterControls(VOID)
|
|
/*
|
|
* Register the control classes. This function must be called for each client process.
|
|
|
|
* History:
|
|
* ??-??-?? DarrinM Ported.
|
|
* ??-??-?? MikeKe Moved here from server.
|
|
*/
|
|
{
|
|
int i;
|
|
WNDCLASSEX wndcls;
|
|
BOOL fSuccess = TRUE;
|
|
|
|
static CONST struct {
|
|
UINT style;
|
|
WNDPROC lpfnWndProcW;
|
|
int cbWndExtra;
|
|
LPCTSTR lpszCursor;
|
|
HBRUSH hbrBackground;
|
|
LPCTSTR lpszClassName;
|
|
WORD fnid;
|
|
} rc[] = {
|
|
{CS_GLOBALCLASS | CS_PARENTDC | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW,
|
|
ButtonWndProcW,
|
|
sizeof(BUTNWND) - sizeof(WND),
|
|
IDC_ARROW,
|
|
NULL,
|
|
L"Button",
|
|
FNID_BUTTON
|
|
},
|
|
|
|
{CS_GLOBALCLASS | CS_DBLCLKS | CS_PARENTDC | CS_VREDRAW | CS_HREDRAW,
|
|
ComboBoxWndProcW,
|
|
sizeof(COMBOWND) - sizeof(WND),
|
|
IDC_ARROW,
|
|
NULL,
|
|
L"ComboBox",
|
|
FNID_COMBOBOX
|
|
},
|
|
|
|
{CS_GLOBALCLASS | CS_DBLCLKS | CS_SAVEBITS,
|
|
ComboListBoxWndProcW,
|
|
sizeof(LBWND) - sizeof(WND),
|
|
IDC_ARROW,
|
|
NULL,
|
|
L"ComboLBox",
|
|
FNID_COMBOLISTBOX
|
|
},
|
|
|
|
{CS_GLOBALCLASS | CS_DBLCLKS | CS_SAVEBITS,
|
|
DefDlgProcW,
|
|
DLGWINDOWEXTRA,
|
|
IDC_ARROW,
|
|
NULL,
|
|
DIALOGCLASS,
|
|
FNID_DIALOG
|
|
},
|
|
|
|
{CS_GLOBALCLASS | CS_PARENTDC | CS_DBLCLKS,
|
|
EditWndProcW,
|
|
max((sizeof(EDITWND) - sizeof(WND)), CBEDITEXTRA),
|
|
IDC_IBEAM,
|
|
NULL,
|
|
L"Edit",
|
|
FNID_EDIT
|
|
},
|
|
|
|
{CS_GLOBALCLASS | CS_PARENTDC | CS_DBLCLKS,
|
|
ListBoxWndProcW,
|
|
sizeof(LBWND) - sizeof(WND),
|
|
IDC_ARROW,
|
|
NULL,
|
|
L"ListBox",
|
|
FNID_LISTBOX
|
|
},
|
|
|
|
{CS_GLOBALCLASS,
|
|
MDIClientWndProcW,
|
|
sizeof(MDIWND) - sizeof(WND),
|
|
IDC_ARROW,
|
|
(HBRUSH)(COLOR_APPWORKSPACE + 1),
|
|
L"MDIClient",
|
|
FNID_MDICLIENT
|
|
},
|
|
|
|
{CS_GLOBALCLASS,
|
|
ImeWndProcW,
|
|
sizeof(IMEWND) - sizeof(WND),
|
|
IDC_ARROW,
|
|
NULL,
|
|
L"IME",
|
|
FNID_IME
|
|
},
|
|
|
|
{CS_GLOBALCLASS | CS_PARENTDC | CS_DBLCLKS,
|
|
StaticWndProcW,
|
|
sizeof(STATWND) - sizeof(WND),
|
|
IDC_ARROW,
|
|
NULL,
|
|
L"Static",
|
|
FNID_STATIC
|
|
}
|
|
};
|
|
|
|
/*
|
|
* Classes are registered via the table.
|
|
*/
|
|
RtlZeroMemory(&wndcls, sizeof(wndcls));
|
|
wndcls.cbSize = sizeof(wndcls);
|
|
wndcls.hInstance = hmodUser;
|
|
|
|
for (i = 0; i < (sizeof(rc) / sizeof(rc[0])); i++) {
|
|
wndcls.style = rc[i].style;
|
|
wndcls.lpfnWndProc = rc[i].lpfnWndProcW;
|
|
wndcls.cbWndExtra = rc[i].cbWndExtra;
|
|
wndcls.hCursor = LoadCursor(NULL, rc[i].lpszCursor);
|
|
wndcls.hbrBackground = rc[i].hbrBackground;
|
|
wndcls.lpszClassName = rc[i].lpszClassName;
|
|
|
|
fSuccess &= !!RegisterClassExWOWW(&wndcls, NULL, rc[i].fnid);
|
|
}
|
|
|
|
if (!fSuccess) {
|
|
RIPMSG0(RIP_WARNING, "RW_RegisterControls failed to register classes");
|
|
}
|
|
|
|
return fSuccess;
|
|
}
|
|
|
|
|
|
BOOL RW_RegisterDDEML(VOID)
|
|
/*
|
|
* Register all the DDEML classes
|
|
|
|
* History:
|
|
* 01-Dec-1991 Sanfords Created.
|
|
*/
|
|
{
|
|
WNDCLASSEXA wndclsa;
|
|
WNDCLASSEXW wndclsw;
|
|
BOOL fSuccess = TRUE;
|
|
|
|
RtlZeroMemory(&wndclsw, sizeof(wndclsw));
|
|
wndclsw.cbSize = sizeof(wndclsw);
|
|
wndclsw.lpfnWndProc = DDEMLMotherWndProc;
|
|
wndclsw.cbWndExtra = sizeof(PCL_INSTANCE_INFO);
|
|
wndclsw.hInstance = hmodUser;
|
|
wndclsw.lpszClassName = L"DDEMLMom";
|
|
|
|
fSuccess &= !!RegisterClassExWOWW(&wndclsw, NULL, FNID_DDE_BIT);
|
|
|
|
RtlZeroMemory(&wndclsa, sizeof(wndclsa));
|
|
wndclsa.cbSize = sizeof(wndclsa);
|
|
wndclsa.lpfnWndProc = DDEMLClientWndProc;
|
|
wndclsa.cbWndExtra =
|
|
sizeof(PCL_CONV_INFO) + // GWL_PCI
|
|
sizeof(CONVCONTEXT) + // GWL_CONVCONTEXT
|
|
sizeof(LONG) + // GWL_CONVSTATE
|
|
sizeof(HANDLE) + // GWL_CHINST
|
|
sizeof(HANDLE); // GWL_SHINST
|
|
wndclsa.hInstance = hmodUser;
|
|
wndclsa.lpszClassName = "DDEMLAnsiClient";
|
|
|
|
fSuccess &= !!RegisterClassExWOWA(&wndclsa, NULL, FNID_DDE_BIT);
|
|
|
|
wndclsw.cbSize = sizeof(wndclsw);
|
|
wndclsw.lpfnWndProc = DDEMLClientWndProc;
|
|
wndclsw.cbWndExtra =
|
|
sizeof(PCL_CONV_INFO) + // GWL_PCI
|
|
sizeof(CONVCONTEXT) + // GWL_CONVCONTEXT
|
|
sizeof(LONG) + // GWL_CONVSTATE
|
|
sizeof(HANDLE) + // GWL_CHINST
|
|
sizeof(HANDLE); // GWL_SHINST
|
|
wndclsw.hInstance = hmodUser;
|
|
wndclsw.lpszClassName = L"DDEMLUnicodeClient";
|
|
|
|
fSuccess &= !!RegisterClassExWOWW(&wndclsw, NULL, FNID_DDE_BIT);
|
|
|
|
wndclsa.cbSize = sizeof(wndclsa);
|
|
wndclsa.lpfnWndProc = DDEMLServerWndProc;
|
|
wndclsa.cbWndExtra = sizeof(PSVR_CONV_INFO);// GWL_PSI
|
|
wndclsa.hInstance = hmodUser;
|
|
wndclsa.lpszClassName = "DDEMLAnsiServer";
|
|
|
|
fSuccess &= !!RegisterClassExWOWA(&wndclsa, NULL, FNID_DDE_BIT);
|
|
|
|
wndclsw.cbSize = sizeof(wndclsw);
|
|
wndclsw.lpfnWndProc = DDEMLServerWndProc;
|
|
wndclsw.cbWndExtra = sizeof(PSVR_CONV_INFO);// GWL_PSI
|
|
wndclsw.hInstance = hmodUser;
|
|
wndclsw.lpszClassName = L"DDEMLUnicodeServer";
|
|
|
|
fSuccess &= !!RegisterClassExWOWW(&wndclsw, NULL, FNID_DDE_BIT);
|
|
if (!fSuccess) {
|
|
RIPMSG0(RIP_WARNING, "RW_RegisterDDEML failed to register classes");
|
|
}
|
|
|
|
return fSuccess;
|
|
}
|
|
|
|
|
|
LPSTR GetBadAppCmdLine(IN LPCWSTR lpApplicationName)
|
|
/*
|
|
* This routine checks if the app specified by 'lpApplicationName' needs patching.
|
|
|
|
* Arguments:
|
|
* lpApplicationName - contains the full path and application name for the binary that needs to be identified for patching
|
|
|
|
* Return Value:
|
|
* Returns a pointer to the command line for the patch DLL.
|
|
* NOTE: The caller needs to free the pointer returned by this function
|
|
|
|
* History:
|
|
* 27-Jul-1998 clupu Created.
|
|
*/
|
|
{
|
|
#define DLLPATCH_NAME L"DllPatch"
|
|
UNICODE_STRING strKeyPath;
|
|
UNICODE_STRING strDllPatchName;
|
|
OBJECT_ATTRIBUTES objA;
|
|
WCHAR strBuffer[256];
|
|
WCHAR strDllPatch[64];
|
|
WCHAR strName[32];
|
|
HANDLE hkey;
|
|
NTSTATUS Status;
|
|
WCHAR* lpShortName;
|
|
BADAPP_DATA badAppData;
|
|
BADAPP_PROP badAppProp;
|
|
ULONG iValue, cbSize, cbCrtSize;
|
|
LPSTR pszCmdLine = NULL;
|
|
BOOL bRet;
|
|
|
|
PKEY_VALUE_FULL_INFORMATION pKeyValueInformation;
|
|
|
|
#if DBG
|
|
/*
|
|
* Assert that there is at least one path separator
|
|
*/
|
|
|
|
lpShortName = (LPWSTR)lpApplicationName;
|
|
|
|
while (*lpShortName != 0) {
|
|
if (*lpShortName == OBJ_NAME_PATH_SEPARATOR) {
|
|
break;
|
|
}
|
|
lpShortName++;
|
|
}
|
|
|
|
UserAssert(*lpShortName == OBJ_NAME_PATH_SEPARATOR);
|
|
#endif // DBG
|
|
|
|
/*
|
|
* Get the short name of the file
|
|
*/
|
|
lpShortName = (WCHAR*)lpApplicationName + wcslen(lpApplicationName) - 1;
|
|
|
|
while (*lpShortName != OBJ_NAME_PATH_SEPARATOR) {
|
|
lpShortName--;
|
|
}
|
|
|
|
/*
|
|
* Prepare the registry key
|
|
*/
|
|
wcscpy(strBuffer, L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Session Manager\\AppCompatibility");
|
|
wcscat(strBuffer, lpShortName);
|
|
RtlInitUnicodeString(&strKeyPath, strBuffer);
|
|
InitializeObjectAttributes(&objA, &strKeyPath, OBJ_CASE_INSENSITIVE, NULL, NULL);
|
|
Status = NtOpenKey(&hkey, GENERIC_READ, &objA);
|
|
if (!NT_SUCCESS(Status)) {
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Now verify that this is the actual version of the app
|
|
*/
|
|
pKeyValueInformation = NULL;
|
|
cbCrtSize = 0;
|
|
|
|
badAppProp.Size = sizeof(BADAPP_PROP);
|
|
badAppData.Size = sizeof(BADAPP_DATA);
|
|
badAppData.FilePath = (LPCWSTR)lpApplicationName;
|
|
|
|
/*
|
|
* Presume innocent
|
|
*/
|
|
bRet = FALSE;
|
|
|
|
for (iValue = 0; ; iValue++) {
|
|
Status = NtEnumerateValueKey(hkey, iValue, KeyValueFullInformation, pKeyValueInformation, cbCrtSize, &cbSize);
|
|
if (Status == STATUS_NO_MORE_ENTRIES) {
|
|
break;
|
|
}
|
|
|
|
if (Status == STATUS_BUFFER_TOO_SMALL || Status == STATUS_BUFFER_OVERFLOW) {
|
|
if (pKeyValueInformation != NULL) {
|
|
RtlFreeHeap(RtlProcessHeap(), 0, pKeyValueInformation);
|
|
}
|
|
|
|
pKeyValueInformation = RtlAllocateHeap(RtlProcessHeap(), 0, cbSize);
|
|
if (pKeyValueInformation == NULL) {
|
|
/*
|
|
* Print some memory allocation error message
|
|
*/
|
|
TAGMSG0(DBGTAG_Shim, "Memory allocation error");
|
|
break;
|
|
}
|
|
|
|
cbCrtSize = cbSize;
|
|
|
|
/*
|
|
* Enumerate the key again. This time it should have a big enough buffer.
|
|
*/
|
|
Status = NtEnumerateValueKey(hkey, iValue, KeyValueFullInformation, pKeyValueInformation, cbCrtSize, &cbSize);
|
|
UserAssert(!NT_SUCCESS(Status) || (cbCrtSize == cbSize));
|
|
}
|
|
|
|
if (NT_SUCCESS(Status) && pKeyValueInformation->Type == REG_BINARY) {
|
|
RtlCopyMemory(strName, pKeyValueInformation->Name, pKeyValueInformation->NameLength);
|
|
strName[pKeyValueInformation->NameLength / sizeof(WCHAR)] = 0;
|
|
badAppData.Blob = (PBYTE)pKeyValueInformation + pKeyValueInformation->DataOffset;
|
|
badAppData.BlobSize = pKeyValueInformation->DataLength;
|
|
if (SHIsBadApp(&badAppData, &badAppProp)) {
|
|
TAGMSG0(DBGTAG_Shim, "SHIsBadApp returned TRUE");
|
|
bRet = TRUE;
|
|
break;
|
|
} else {
|
|
TAGMSG0(DBGTAG_Shim, "SHIsBadApp returned FALSE");
|
|
}
|
|
} else {
|
|
if (!NT_SUCCESS(Status)) {
|
|
TAGMSG1(DBGTAG_Shim, "failed reading key. Status 0x%x", Status);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pKeyValueInformation != NULL) {
|
|
RtlFreeHeap(RtlProcessHeap(), 0, pKeyValueInformation);
|
|
pKeyValueInformation = NULL;
|
|
}
|
|
|
|
if (bRet) {
|
|
/*
|
|
* Read the REG_SZ for 'DllPatch-name'
|
|
*/
|
|
wsprintf(strDllPatch, L"%ws-%ws", DLLPATCH_NAME, strName);
|
|
RtlInitUnicodeString(&strDllPatchName, strDllPatch);
|
|
Status = NtQueryValueKey(hkey, &strDllPatchName, KeyValueFullInformation, NULL, 0, &cbSize);
|
|
if (Status == STATUS_BUFFER_TOO_SMALL || Status == STATUS_BUFFER_OVERFLOW) {
|
|
LPWSTR pwszCmdLine;
|
|
|
|
pKeyValueInformation = RtlAllocateHeap(RtlProcessHeap(), 0, cbSize);
|
|
if (pKeyValueInformation == NULL) {
|
|
|
|
/*
|
|
* Print some memory allocation error message
|
|
*/
|
|
TAGMSG0(DBGTAG_Shim, "Memory allocation error");
|
|
goto Exit;
|
|
}
|
|
|
|
Status = NtQueryValueKey(hkey, &strDllPatchName, KeyValueFullInformation, pKeyValueInformation, cbSize, &cbCrtSize);
|
|
|
|
/*
|
|
* Bail out if this is not of the type we expect
|
|
*/
|
|
if (!NT_SUCCESS(Status) || pKeyValueInformation->Type != REG_SZ) {
|
|
TAGMSG1(DBGTAG_Shim, "The key '%s' is not of REG_SZ type", strDllPatch);
|
|
goto Exit;
|
|
}
|
|
|
|
pwszCmdLine = (PWCHAR)((PBYTE)pKeyValueInformation + pKeyValueInformation->DataOffset + pKeyValueInformation->DataLength);
|
|
|
|
/*
|
|
* NULL terminate the string
|
|
*/
|
|
*(pwszCmdLine - 1) = 0;
|
|
|
|
pwszCmdLine = (LPWSTR)((PBYTE)pKeyValueInformation + pKeyValueInformation->DataOffset);
|
|
|
|
TAGMSG2(DBGTAG_Shim, "The app '%ws' has a patch DLL '%ws'", lpApplicationName, pwszCmdLine);
|
|
cbSize = wcslen(pwszCmdLine);
|
|
pszCmdLine = RtlAllocateHeap(RtlProcessHeap(), 0, cbSize + 1);
|
|
if (pszCmdLine == NULL) {
|
|
TAGMSG0(DBGTAG_Shim, "Memory allocation error");
|
|
goto Exit;
|
|
}
|
|
|
|
WideCharToMultiByte(CP_OEMCP, 0, pwszCmdLine, cbSize + 1, pszCmdLine, cbSize + 1, (LPSTR)NULL, (LPBOOL)NULL);
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
NtClose(hkey);
|
|
return pszCmdLine;
|
|
}
|
|
|
|
|
|
typedef BOOL(*PFNLOADPATCHDLL)(LPSTR pwszPatchDll);
|
|
|
|
|
|
BOOL CheckBadApp(VOID)
|
|
/*
|
|
* History:
|
|
* 27-Jul-1998 clupu Created.
|
|
*/
|
|
{
|
|
WCHAR wszAppName[MAX_PATH];
|
|
PFNLOADPATCHDLL pfnLoadPatchDll;
|
|
LPSTR pszCmdLine;
|
|
BOOL bRet;
|
|
DWORD dwSize;
|
|
HMODULE hMod;
|
|
|
|
dwSize = GetModuleFileNameW(NULL, wszAppName, MAX_PATH);
|
|
UserAssert(dwSize > 0);
|
|
|
|
pszCmdLine = GetBadAppCmdLine(wszAppName);
|
|
if (pszCmdLine == NULL) {
|
|
return TRUE;
|
|
}
|
|
if (_stricmp(pszCmdLine, "disable") == 0) {
|
|
return FALSE;
|
|
}
|
|
|
|
hMod = LoadLibraryW(gwszShimDll);
|
|
if (hMod == NULL) {
|
|
RIPMSG0(RIP_WARNING, "Couldn't load SHIM.DLL");
|
|
goto Exit;
|
|
}
|
|
|
|
pfnLoadPatchDll = (PFNLOADPATCHDLL)GetProcAddress(hMod, gszLoadPathDll);
|
|
if (pfnLoadPatchDll == NULL) {
|
|
RIPMSG1(RIP_WARNING, "Couldn't find '%s' in SHIM.DLL", gszLoadPathDll);
|
|
FreeLibrary(hMod);
|
|
goto Exit;
|
|
}
|
|
|
|
bRet = (*pfnLoadPatchDll)(pszCmdLine);
|
|
|
|
UserAssert(bRet);
|
|
|
|
Exit:
|
|
RtlFreeHeap(RtlProcessHeap(), 0, pszCmdLine);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
VOID LoadAppDlls(VOID)
|
|
/*
|
|
* History:
|
|
* 10-Apr-1992 sanfords Birthed.
|
|
*/
|
|
{
|
|
extern BOOL gfLogonProcess;
|
|
UNICODE_STRING UnicodeString;
|
|
OBJECT_ATTRIBUTES ObjA;
|
|
HKEY hKeyWindows;
|
|
NTSTATUS Status;
|
|
DWORD cbSize;
|
|
struct {
|
|
KEY_VALUE_PARTIAL_INFORMATION KeyInfo;
|
|
WCHAR awch[24];
|
|
} KeyFile;
|
|
PKEY_VALUE_PARTIAL_INFORMATION lpKeyFile = (PKEY_VALUE_PARTIAL_INFORMATION)&KeyFile;
|
|
DWORD cbSizeCurrent = sizeof(KeyFile);
|
|
BOOL bAlloc = FALSE;
|
|
|
|
if (gfLogonProcess || gfServerProcess) {
|
|
/*
|
|
* Don't let the logon process load appdlls because if the dll sets any hooks or creates any windows, the logon process
|
|
* will fail SetThreadDesktop(). This hack fixes that. (SAS)
|
|
*/
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* If the image is an NT Native image, we are running in the context of the server.
|
|
*/
|
|
if (RtlImageNtHeader(NtCurrentPeb()->ImageBaseAddress)->
|
|
OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_NATIVE) {
|
|
return;
|
|
}
|
|
|
|
RtlInitUnicodeString(&UnicodeString, pwszWindowsKey);
|
|
InitializeObjectAttributes(&ObjA, &UnicodeString, OBJ_CASE_INSENSITIVE, NULL, NULL);
|
|
Status = NtOpenKey(&hKeyWindows, KEY_READ, &ObjA);
|
|
if (!NT_SUCCESS(Status))
|
|
return;
|
|
|
|
/*
|
|
* Read the "AppInit_Dlls" value.
|
|
*/
|
|
RtlInitUnicodeString(&UnicodeString, szAppInit);
|
|
while (TRUE) {
|
|
Status = NtQueryValueKey(hKeyWindows, &UnicodeString, KeyValuePartialInformation, lpKeyFile, cbSizeCurrent, &cbSize);
|
|
if (Status == STATUS_BUFFER_OVERFLOW) {
|
|
if (bAlloc) {
|
|
UserGlobalFree(lpKeyFile);
|
|
}
|
|
lpKeyFile = GlobalAlloc(LPTR, cbSize);
|
|
if (!lpKeyFile) {
|
|
RIPERR0(ERROR_OUTOFMEMORY, RIP_WARNING, "LoadAppDlls failed");
|
|
NtClose(hKeyWindows);
|
|
return;
|
|
}
|
|
bAlloc = TRUE;
|
|
cbSizeCurrent = cbSize;
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
if (NT_SUCCESS(Status)) {
|
|
LPWSTR pszSrc;
|
|
LPWSTR pszDst;
|
|
LPWSTR pszBase;
|
|
WCHAR ch;
|
|
|
|
/*
|
|
* Process the DLL data.
|
|
*/
|
|
pszBase = pszDst = pszSrc = (LPWSTR)lpKeyFile->Data;
|
|
while (*pszSrc != TEXT('\0')) {
|
|
while (*pszSrc == TEXT(' ') || *pszSrc == TEXT(','))
|
|
pszSrc++;
|
|
|
|
if (*pszSrc == TEXT('\0'))
|
|
break;
|
|
|
|
while (*pszSrc != TEXT(',') && *pszSrc != TEXT('\0') && *pszSrc != TEXT(' ')) {
|
|
*pszDst++ = *pszSrc++;
|
|
}
|
|
|
|
ch = *pszSrc; // get it here cuz its being done in-place.
|
|
*pszDst++ = TEXT('\0'); // '\0' is dll name delimiter
|
|
|
|
LoadLibrary(pszBase);
|
|
pszBase = pszDst;
|
|
|
|
pszSrc++;
|
|
|
|
if (ch == TEXT('\0'))
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (bAlloc) {
|
|
UserGlobalFree(lpKeyFile);
|
|
}
|
|
NtClose(hKeyWindows);
|
|
}
|
|
|
|
|
|
VOID InitOemXlateTables()
|
|
{
|
|
char ach[NCHARS];
|
|
WCHAR awch[NCHARS];
|
|
WCHAR awchCtrl[NCTRLS];
|
|
INT i;
|
|
INT cch;
|
|
char OemToAnsi[NCHARS];
|
|
char AnsiToOem[NCHARS];
|
|
|
|
for (i = 0; i < NCHARS; i++) {
|
|
ach[i] = (char)i;
|
|
}
|
|
|
|
/*
|
|
* First generate pAnsiToOem table.
|
|
*/
|
|
if (GetOEMCP() == GetACP()) {
|
|
/*
|
|
* For far east code pages using MultiByteToWideChar below
|
|
* won't work. Conveniently for these code pages the OEM
|
|
* CP equals the ANSI codepage making it trivial to compute
|
|
* pOemToAnsi and pAnsiToOem arrays
|
|
*/
|
|
RtlCopyMemory(OemToAnsi, ach, NCHARS);
|
|
RtlCopyMemory(AnsiToOem, ach, NCHARS);
|
|
} else {
|
|
cch = MultiByteToWideChar(CP_ACP, // ANSI -> Unicode
|
|
MB_PRECOMPOSED,// map to precomposed
|
|
ach, NCHARS, // source & length
|
|
awch, NCHARS); // destination & length
|
|
|
|
UserAssert(cch == NCHARS);
|
|
|
|
WideCharToMultiByte(CP_OEMCP, // Unicode -> OEM
|
|
0, // gives best visual match
|
|
awch, NCHARS, // source & length
|
|
AnsiToOem, NCHARS,// dest & max poss. length
|
|
"_", // default char
|
|
NULL); // (don't care whether defaulted)
|
|
/*
|
|
* Now generate pOemToAnsi table.
|
|
*/
|
|
cch = MultiByteToWideChar(CP_OEMCP, // OEM -> Unicode
|
|
MB_PRECOMPOSED | MB_USEGLYPHCHARS,// visual map to precomposed
|
|
ach, NCHARS, // source & length
|
|
awch, NCHARS); // destination
|
|
|
|
UserAssert(cch == NCHARS);
|
|
|
|
/*
|
|
* Now patch special cases for Win3.1 compatibility
|
|
|
|
* 0x07 BULLET (glyph 0x2022) must become 0x0007 BELL
|
|
* 0x0F WHITE STAR WITH SUN (glyph 0x263C) must become 0x00A4 CURRENCY SIGN
|
|
* 0x7F HOUSE (glyph 0x2302) must become 0x007f DELETE
|
|
*/
|
|
awch[0x07] = 0x0007;
|
|
awch[0x0F] = 0x00a4;
|
|
awch[0x7f] = 0x007f;
|
|
|
|
WideCharToMultiByte(CP_ACP, // Unicode -> ANSI
|
|
0, // gives best visual match
|
|
awch, NCHARS, // source & length
|
|
OemToAnsi, NCHARS,// dest & max poss. length
|
|
"_", // default char
|
|
NULL); // (don't care whether defaulted)
|
|
|
|
/*
|
|
* Now for all OEM chars < 0x20 (control chars), test whether the glyph
|
|
* we have is really in CP_ACP or not. If not, then restore the
|
|
* original control character. Note: 0x00 remains 0x00.
|
|
*/
|
|
MultiByteToWideChar(CP_ACP, 0, OemToAnsi, NCTRLS, awchCtrl, NCTRLS);
|
|
|
|
for (i = 1; i < NCTRLS; i++) {
|
|
if (awchCtrl[i] != awch[i]) {
|
|
OemToAnsi[i] = (char)i;
|
|
}
|
|
}
|
|
}
|
|
|
|
NtUserCallTwoParam((ULONG_PTR)OemToAnsi, (ULONG_PTR)AnsiToOem, SFI_INITANSIOEM);
|
|
}
|
|
|
|
const PFNCLIENT pfnClientA = {
|
|
(KPROC)ScrollBarWndProcA,
|
|
(KPROC)DefWindowProcA,
|
|
(KPROC)MenuWndProcA,
|
|
(KPROC)DesktopWndProcA,
|
|
(KPROC)DefWindowProcA,
|
|
(KPROC)ButtonWndProcA,
|
|
(KPROC)ComboBoxWndProcA,
|
|
(KPROC)ComboListBoxWndProcA,
|
|
(KPROC)DefDlgProcA,
|
|
(KPROC)EditWndProcA,
|
|
(KPROC)ListBoxWndProcA,
|
|
(KPROC)MDIClientWndProcA,
|
|
(KPROC)StaticWndProcA,
|
|
(KPROC)ImeWndProcA,
|
|
(KPROC)fnHkINLPCWPSTRUCTA,
|
|
(KPROC)fnHkINLPCWPRETSTRUCTA,
|
|
(KPROC)DispatchHookA,
|
|
(KPROC)DispatchClientMessage,
|
|
(KPROC)MB_DlgProcA,
|
|
(KPROC)MDIActivateDlgProcA};
|
|
|
|
const PFNCLIENT pfnClientW = {
|
|
(KPROC)ScrollBarWndProcW,
|
|
(KPROC)DefWindowProcW,
|
|
(KPROC)MenuWndProcW,
|
|
(KPROC)DesktopWndProcW,
|
|
(KPROC)DefWindowProcW,
|
|
(KPROC)ButtonWndProcW,
|
|
(KPROC)ComboBoxWndProcW,
|
|
(KPROC)ComboListBoxWndProcW,
|
|
(KPROC)DefDlgProcW,
|
|
(KPROC)EditWndProcW,
|
|
(KPROC)ListBoxWndProcW,
|
|
(KPROC)MDIClientWndProcW,
|
|
(KPROC)StaticWndProcW,
|
|
(KPROC)ImeWndProcW,
|
|
(KPROC)fnHkINLPCWPSTRUCTW,
|
|
(KPROC)fnHkINLPCWPRETSTRUCTW,
|
|
(KPROC)DispatchHookW,
|
|
(KPROC)DispatchClientMessage,
|
|
(KPROC)MB_DlgProcW,
|
|
(KPROC)MDIActivateDlgProcW};
|
|
|
|
const PFNCLIENTWORKER pfnClientWorker = {
|
|
(KPROC)ButtonWndProcWorker,
|
|
(KPROC)ComboBoxWndProcWorker,
|
|
(KPROC)ListBoxWndProcWorker,
|
|
(KPROC)DefDlgProcWorker,
|
|
(KPROC)EditWndProcWorker,
|
|
(KPROC)ListBoxWndProcWorker,
|
|
(KPROC)MDIClientWndProcWorker,
|
|
(KPROC)StaticWndProcWorker,
|
|
(KPROC)ImeWndProcWorker};
|
|
|
|
|
|
BOOL ClientThreadSetup(VOID)
|
|
{
|
|
PCLIENTINFO pci;
|
|
BOOL fFirstThread;
|
|
DWORD ConnectState;
|
|
|
|
#ifdef TRACE_THREAD_INIT
|
|
KdPrint(("USER32: ClientThreadSetup (pteb: %#p)\n", NtCurrentTeb()));
|
|
#endif
|
|
|
|
/*
|
|
* BUG 268642: Only the first thread calls GdiProcessSetup but all the other
|
|
* threads must wait until the setup for GDI is finished.
|
|
|
|
* We can safely use gcsAccelCache critical section to protect this (even though the name is not intuitive at all)
|
|
*/
|
|
|
|
RtlEnterCriticalSection(&gcsAccelCache);
|
|
|
|
fFirstThread = gfFirstThread;
|
|
|
|
/*
|
|
* setup GDI before continuing
|
|
*/
|
|
if (fFirstThread) {
|
|
gfFirstThread = FALSE;
|
|
GdiProcessSetup();
|
|
}
|
|
|
|
RtlLeaveCriticalSection(&gcsAccelCache);
|
|
|
|
/*
|
|
* We've already checked to see if we need to connect
|
|
* (i.e. NtCurrentTeb()->Win32ThreadInfo == NULL) This routine
|
|
* just does the connecting. If we've already been through here
|
|
* once, don't do it again.
|
|
*/
|
|
pci = GetClientInfo();
|
|
if (pci->CI_flags & CI_INITIALIZED) {
|
|
RIPMSG0(RIP_ERROR, "Already initialized!");
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* Create the queue info and thread info. Only once for this process do
|
|
* we pass client side addresses to the server (for server callbacks).
|
|
*/
|
|
if (gfServerProcess && fFirstThread) {
|
|
USERCONNECT userconnect;
|
|
NTSTATUS Status;
|
|
|
|
/*
|
|
* We know that the shared info is now available in the kernel.
|
|
* Map it into the server process.
|
|
*/
|
|
userconnect.ulVersion = USERCURRENTVERSION;
|
|
userconnect.dwDispatchCount = gDispatchTableValues;
|
|
Status = NtUserProcessConnect(NtCurrentProcess(), &userconnect, sizeof(USERCONNECT));
|
|
if (!NT_SUCCESS(Status))
|
|
return FALSE;
|
|
|
|
gSharedInfo = userconnect.siClient;
|
|
gpsi = gSharedInfo.psi;
|
|
UserAssert(gpsi);
|
|
|
|
UserAssert(pfnClientA.pfnScrollBarWndProc == (KPROC)ScrollBarWndProcA);
|
|
UserAssert(pfnClientA.pfnTitleWndProc == (KPROC)DefWindowProcA);
|
|
UserAssert(pfnClientA.pfnMenuWndProc == (KPROC)MenuWndProcA);
|
|
UserAssert(pfnClientA.pfnDesktopWndProc == (KPROC)DesktopWndProcA);
|
|
UserAssert(pfnClientA.pfnDefWindowProc == (KPROC)DefWindowProcA);
|
|
UserAssert(pfnClientA.pfnHkINLPCWPSTRUCT == (KPROC)fnHkINLPCWPSTRUCTA);
|
|
UserAssert(pfnClientA.pfnHkINLPCWPRETSTRUCT == (KPROC)fnHkINLPCWPRETSTRUCTA);
|
|
UserAssert(pfnClientA.pfnButtonWndProc == (KPROC)ButtonWndProcA);
|
|
UserAssert(pfnClientA.pfnComboBoxWndProc == (KPROC)ComboBoxWndProcA);
|
|
UserAssert(pfnClientA.pfnComboListBoxProc == (KPROC)ComboListBoxWndProcA);
|
|
UserAssert(pfnClientA.pfnDialogWndProc == (KPROC)DefDlgProcA);
|
|
UserAssert(pfnClientA.pfnEditWndProc == (KPROC)EditWndProcA);
|
|
UserAssert(pfnClientA.pfnListBoxWndProc == (KPROC)ListBoxWndProcA);
|
|
UserAssert(pfnClientA.pfnMB_DlgProc == (KPROC)MB_DlgProcA);
|
|
UserAssert(pfnClientA.pfnMDIActivateDlgProc == (KPROC)MDIActivateDlgProcA);
|
|
UserAssert(pfnClientA.pfnMDIClientWndProc == (KPROC)MDIClientWndProcA);
|
|
UserAssert(pfnClientA.pfnStaticWndProc == (KPROC)StaticWndProcA);
|
|
UserAssert(pfnClientA.pfnDispatchHook == (KPROC)DispatchHookA);
|
|
UserAssert(pfnClientA.pfnDispatchMessage == (KPROC)DispatchClientMessage);
|
|
UserAssert(pfnClientA.pfnImeWndProc == (KPROC)ImeWndProcA);
|
|
|
|
UserAssert(pfnClientW.pfnScrollBarWndProc == (KPROC)ScrollBarWndProcW);
|
|
UserAssert(pfnClientW.pfnTitleWndProc == (KPROC)DefWindowProcW);
|
|
UserAssert(pfnClientW.pfnMenuWndProc == (KPROC)MenuWndProcW);
|
|
UserAssert(pfnClientW.pfnDesktopWndProc == (KPROC)DesktopWndProcW);
|
|
UserAssert(pfnClientW.pfnDefWindowProc == (KPROC)DefWindowProcW);
|
|
UserAssert(pfnClientW.pfnHkINLPCWPSTRUCT == (KPROC)fnHkINLPCWPSTRUCTW);
|
|
UserAssert(pfnClientW.pfnHkINLPCWPRETSTRUCT == (KPROC)fnHkINLPCWPRETSTRUCTW);
|
|
UserAssert(pfnClientW.pfnButtonWndProc == (KPROC)ButtonWndProcW);
|
|
UserAssert(pfnClientW.pfnComboBoxWndProc == (KPROC)ComboBoxWndProcW);
|
|
UserAssert(pfnClientW.pfnComboListBoxProc == (KPROC)ComboListBoxWndProcW);
|
|
UserAssert(pfnClientW.pfnDialogWndProc == (KPROC)DefDlgProcW);
|
|
UserAssert(pfnClientW.pfnEditWndProc == (KPROC)EditWndProcW);
|
|
UserAssert(pfnClientW.pfnListBoxWndProc == (KPROC)ListBoxWndProcW);
|
|
UserAssert(pfnClientW.pfnMB_DlgProc == (KPROC)MB_DlgProcW);
|
|
UserAssert(pfnClientW.pfnMDIActivateDlgProc == (KPROC)MDIActivateDlgProcW);
|
|
UserAssert(pfnClientW.pfnMDIClientWndProc == (KPROC)MDIClientWndProcW);
|
|
UserAssert(pfnClientW.pfnStaticWndProc == (KPROC)StaticWndProcW);
|
|
UserAssert(pfnClientW.pfnDispatchHook == (KPROC)DispatchHookW);
|
|
UserAssert(pfnClientW.pfnDispatchMessage == (KPROC)DispatchClientMessage);
|
|
UserAssert(pfnClientW.pfnImeWndProc == (KPROC)ImeWndProcW);
|
|
|
|
UserAssert(pfnClientWorker.pfnButtonWndProc == (KPROC)ButtonWndProcWorker);
|
|
UserAssert(pfnClientWorker.pfnComboBoxWndProc == (KPROC)ComboBoxWndProcWorker);
|
|
UserAssert(pfnClientWorker.pfnComboListBoxProc == (KPROC)ListBoxWndProcWorker);
|
|
UserAssert(pfnClientWorker.pfnDialogWndProc == (KPROC)DefDlgProcWorker);
|
|
UserAssert(pfnClientWorker.pfnEditWndProc == (KPROC)EditWndProcWorker);
|
|
UserAssert(pfnClientWorker.pfnListBoxWndProc == (KPROC)ListBoxWndProcWorker);
|
|
UserAssert(pfnClientWorker.pfnMDIClientWndProc == (KPROC)MDIClientWndProcWorker);
|
|
UserAssert(pfnClientWorker.pfnStaticWndProc == (KPROC)StaticWndProcWorker);
|
|
UserAssert(pfnClientWorker.pfnImeWndProc == (KPROC)ImeWndProcWorker);
|
|
|
|
#if DBG
|
|
{
|
|
PULONG_PTR pdw;
|
|
|
|
/*
|
|
* Make sure that everyone got initialized
|
|
*/
|
|
for (pdw = (PULONG_PTR)&pfnClientA; (ULONG_PTR)pdw < (ULONG_PTR)(&pfnClientA) + sizeof(pfnClientA); pdw++) {
|
|
UserAssert(*pdw);
|
|
}
|
|
|
|
for (pdw = (PULONG_PTR)&pfnClientW; (ULONG_PTR)pdw < (ULONG_PTR)(&pfnClientW) + sizeof(pfnClientW); pdw++) {
|
|
UserAssert(*pdw);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if DBG
|
|
{
|
|
extern CONST INT gcapfnScSendMessage;
|
|
BOOLEAN apfnCheckMessage[64];
|
|
int i;
|
|
|
|
/*
|
|
* Do some verification of the message table. Since we only have
|
|
* 6 bits to store the function index, the function table can have at most 64 entries.
|
|
* Also verify that none of the indexes point past the end of the table and that all the function entries are used.
|
|
*/
|
|
UserAssert(gcapfnScSendMessage <= 64);
|
|
RtlZeroMemory(apfnCheckMessage, sizeof(apfnCheckMessage));
|
|
for (i = 0; i < WM_USER; i++) {
|
|
UserAssert(MessageTable[i].iFunction < gcapfnScSendMessage);
|
|
apfnCheckMessage[MessageTable[i].iFunction] = TRUE;
|
|
}
|
|
|
|
for (i = 0; i < gcapfnScSendMessage; i++) {
|
|
UserAssert(apfnCheckMessage[i]);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Pass the function pointer arrays to the kernel.
|
|
* This also establishes the kernel state for the thread.
|
|
* If ClientThreadSetup is called from CsrConnectToUser this call will raise an exception if the thread cannot be converted to a gui thread.
|
|
* The exception is handled in CsrConnectToUser.
|
|
*/
|
|
#if DBG && !defined(BUILD_WOW6432)
|
|
/*
|
|
* On debug systems, go to the kernel for all processes to verify we're
|
|
* loading user32.dll at the right address.
|
|
*/
|
|
if (fFirstThread) {
|
|
#elif defined(BUILD_WOW6432)
|
|
/*
|
|
* On WOW64 allways register the client fns
|
|
*/
|
|
{
|
|
#else
|
|
if (gfServerProcess && fFirstThread) {
|
|
#endif
|
|
if (!NT_SUCCESS(NtUserInitializeClientPfnArrays(&pfnClientA, &pfnClientW, &pfnClientWorker, hmodUser))) {
|
|
RIPERR0(ERROR_OUTOFMEMORY, RIP_WARNING, "NtUserInitializeClientPfnArrays failed");
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Mark this thread as being initialized. If the connection to
|
|
* the server fails, NtCurrentTeb()->Win32ThreadInfo will remain NULL.
|
|
*/
|
|
pci->CI_flags |= CI_INITIALIZED;
|
|
|
|
/*
|
|
* Some initialization only has to occur once per process
|
|
*/
|
|
if (fFirstThread) {
|
|
ConnectState = (DWORD)NtUserCallNoParam(SFI_REMOTECONNECTSTATE);
|
|
|
|
/*
|
|
* Winstation Winlogon and CSR must do graphics initialization after the connect.
|
|
*/
|
|
if (ConnectState != CTX_W32_CONNECT_STATE_IDLE) {
|
|
if ((ghdcBits2 = CreateCompatibleDC(NULL)) == NULL) {
|
|
RIPERR0(ERROR_OUTOFMEMORY, RIP_WARNING, "ghdcBits2 creation failed");
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* Get things we need from Gdi.
|
|
*/
|
|
if (ghbrWhite == NULL)
|
|
ghbrWhite = GetStockObject(WHITE_BRUSH);
|
|
|
|
if (ghbrBlack == NULL)
|
|
ghbrBlack = GetStockObject(BLACK_BRUSH);
|
|
|
|
InitClientDrawing();
|
|
}
|
|
|
|
gfSystemInitialized = NtUserGetThreadDesktop(GetCurrentThreadId(), NULL) != NULL;
|
|
|
|
/*
|
|
* If an lpk is loaded for this process notify the kernel
|
|
*/
|
|
if (gdwLpkEntryPoints) {
|
|
NtUserCallOneParam(gdwLpkEntryPoints, SFI_REGISTERLPK);
|
|
}
|
|
|
|
if (gfServerProcess || (GetClientInfo()->pDeskInfo == NULL)) {
|
|
/*
|
|
* Perform any server initialization.
|
|
*/
|
|
UserAssert(gpsi);
|
|
|
|
if (pdiLocal = LocalAlloc(LPTR, sizeof(DESKTOPINFO))) {
|
|
GetClientInfo()->pDeskInfo = pdiLocal;
|
|
} else {
|
|
RIPERR0(ERROR_OUTOFMEMORY, RIP_WARNING, "pdiLocal creation failed");
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (gfServerProcess) {
|
|
/*
|
|
* Winstation Winlogon and CSR must do graphics initialization after the connect.
|
|
*/
|
|
if (ConnectState != CTX_W32_CONNECT_STATE_IDLE) {
|
|
if (!LoadCursorsAndIcons()) {
|
|
RIPERR0(ERROR_OUTOFMEMORY, RIP_WARNING, "LoadCursorsAndIcons failed");
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
InitOemXlateTables();
|
|
}
|
|
|
|
if (!CheckBadApp()) {
|
|
RIPERR0(ERROR_OUTOFMEMORY, RIP_WARNING, "Don't start app for server appliance");
|
|
return FALSE;
|
|
}
|
|
|
|
LoadAppDlls();//加载那个注册表配置下的所有的DLL。
|
|
} else if (gfServerProcess) {
|
|
GetClientInfo()->pDeskInfo = pdiLocal;
|
|
}
|
|
|
|
/*
|
|
* Kernel sets CI_REGISTERCLASSES when appropriate (i.e. always
|
|
* for the first thread and for other threads if the last GUI thread for a process has exited) except for the CSR proces.
|
|
* For the CSR process, you must register the classes on the first thread anyways.
|
|
*/
|
|
if (fFirstThread || (pci->CI_flags & CI_REGISTERCLASSES)) {
|
|
|
|
/*
|
|
* If it's the first thread we already made it to the kernel
|
|
* to get the ConnectState...
|
|
*/
|
|
if (!fFirstThread) {
|
|
ConnectState = (DWORD)NtUserCallNoParam(SFI_REMOTECONNECTSTATE);
|
|
}
|
|
|
|
if (ConnectState != CTX_W32_CONNECT_STATE_IDLE) {
|
|
/*
|
|
* Register the control classes.
|
|
*/
|
|
RW_RegisterControls();
|
|
RW_RegisterDDEML();
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
HLOCAL WINAPI DispatchLocalAlloc(UINT uFlags, UINT uBytes, HANDLE hInstance)
|
|
/*
|
|
* Dispatch routines.
|
|
*/
|
|
{
|
|
UNREFERENCED_PARAMETER(hInstance);
|
|
|
|
return LocalAlloc(uFlags, uBytes);
|
|
}
|
|
|
|
|
|
HLOCAL WINAPI DispatchLocalReAlloc(HLOCAL hMem, UINT uBytes, UINT uFlags, HANDLE hInstance, PVOID * ppv)
|
|
{
|
|
UNREFERENCED_PARAMETER(hInstance);
|
|
UNREFERENCED_PARAMETER(ppv);
|
|
|
|
return LocalReAlloc(hMem, uBytes, uFlags);
|
|
}
|
|
|
|
|
|
LPVOID WINAPI DispatchLocalLock(HLOCAL hMem, HANDLE hInstance)
|
|
{
|
|
UNREFERENCED_PARAMETER(hInstance);
|
|
return LocalLock(hMem);
|
|
}
|
|
|
|
|
|
BOOL WINAPI DispatchLocalUnlock(HLOCAL hMem, HANDLE hInstance)
|
|
{
|
|
UNREFERENCED_PARAMETER(hInstance);
|
|
return LocalUnlock(hMem);
|
|
}
|
|
|
|
|
|
UINT WINAPI DispatchLocalSize(HLOCAL hMem, HANDLE hInstance)
|
|
{
|
|
UNREFERENCED_PARAMETER(hInstance);
|
|
|
|
return (UINT)LocalSize(hMem);
|
|
}
|
|
|
|
|
|
HLOCAL WINAPI DispatchLocalFree(HLOCAL hMem, HANDLE hInstance)
|
|
{
|
|
UNREFERENCED_PARAMETER(hInstance);
|
|
|
|
return LocalFree(hMem);
|
|
}
|
|
|
|
|
|
PVOID UserRtlAllocMem(ULONG uBytes)
|
|
/*
|
|
* Allocation routines for RTL functions.
|
|
*/
|
|
{
|
|
return UserLocalAlloc(HEAP_ZERO_MEMORY, uBytes);
|
|
}
|
|
|
|
|
|
VOID UserRtlFreeMem(PVOID pMem)
|
|
{
|
|
UserLocalFree(pMem);
|
|
}
|
|
|
|
|
|
VOID UserRtlRaiseStatus(NTSTATUS Status)
|
|
{
|
|
RtlRaiseStatus(Status);
|
|
}
|
|
|
|
|
|
BOOL InitClientDrawing(VOID)
|
|
/*
|
|
* History:
|
|
* 20-Aug-1992 mikeke Created
|
|
*/
|
|
{
|
|
static CONST WORD patGray[8] = {0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa};
|
|
BOOL fSuccess = TRUE;
|
|
|
|
HBITMAP hbmGray = CreateBitmap(8, 8, 1, 1, (LPBYTE)patGray);;
|
|
|
|
fSuccess &= !!hbmGray;
|
|
|
|
/*
|
|
* Create the global-objects for client drawing.
|
|
*/
|
|
ghbrWindowText = CreateSolidBrush(GetSysColor(COLOR_WINDOWTEXT));
|
|
fSuccess &= !!ghbrWindowText;
|
|
|
|
ghFontSys = GetStockObject(SYSTEM_FONT);
|
|
fSuccess &= !!ghFontSys;
|
|
|
|
ghdcGray = CreateCompatibleDC(NULL);
|
|
fSuccess &= !!ghdcGray;
|
|
|
|
if (!fSuccess) {
|
|
RIPMSG0(RIP_WARNING, "InitClientDrawing failed to allocate resources");
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* Setup the gray surface.
|
|
*/
|
|
SelectObject(ghdcGray, hbmGray);
|
|
SelectObject(ghdcGray, ghFontSys);
|
|
SelectObject(ghdcGray, gpsi->hbrGray);
|
|
|
|
/*
|
|
* Setup the gray attributes.
|
|
*/
|
|
SetBkMode(ghdcGray, OPAQUE);
|
|
SetTextColor(ghdcGray, 0x00000000L);
|
|
SetBkColor(ghdcGray, 0x00FFFFFFL);
|
|
|
|
gcxGray = 8;
|
|
gcyGray = 8;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
VOID InitializeLpkHooks(CONST FARPROC * lpfpLpkHooks)
|
|
{
|
|
/*
|
|
* Called from GdiInitializeLanguagePack().
|
|
* Remember what entry points are supported.
|
|
* Pass the information to the kernel the first time this process connects in ClientThreadSetup().
|
|
*/
|
|
if (lpfpLpkHooks[LPK_TABBED_TEXT_OUT]) {
|
|
fpLpkTabbedTextOut = (FPLPKTABBEDTEXTOUT)lpfpLpkHooks[LPK_TABBED_TEXT_OUT];
|
|
gdwLpkEntryPoints |= (1 << LPK_TABBED_TEXT_OUT);
|
|
}
|
|
if (lpfpLpkHooks[LPK_PSM_TEXT_OUT]) {
|
|
fpLpkPSMTextOut = (FPLPKPSMTEXTOUT)lpfpLpkHooks[LPK_PSM_TEXT_OUT];
|
|
gdwLpkEntryPoints |= (1 << LPK_PSM_TEXT_OUT);
|
|
}
|
|
if (lpfpLpkHooks[LPK_DRAW_TEXT_EX]) {
|
|
fpLpkDrawTextEx = (FPLPKDRAWTEXTEX)lpfpLpkHooks[LPK_DRAW_TEXT_EX];
|
|
gdwLpkEntryPoints |= (1 << LPK_DRAW_TEXT_EX);
|
|
}
|
|
if (lpfpLpkHooks[LPK_EDIT_CONTROL]) {
|
|
fpLpkEditControl = (PLPKEDITCALLOUT)lpfpLpkHooks[LPK_EDIT_CONTROL];
|
|
gdwLpkEntryPoints |= (1 << LPK_EDIT_CONTROL);
|
|
}
|
|
}
|
|
|
|
|
|
BOOL CtxInitUser32(VOID)
|
|
/*
|
|
* Called by CreateWindowStation() and winsrv.dll DoConnect routine.
|
|
|
|
* Winstation Winlogon and CSR must do graphics initialization
|
|
* after the connect. This is because no video driver is loaded until then.
|
|
|
|
* This routine must contain everything that was skipped before.
|
|
|
|
* History:
|
|
* Dec-11-1997 clupu Ported from Citrix
|
|
*/
|
|
{
|
|
/*
|
|
* Only do once.
|
|
*/
|
|
if (ghdcBits2 != NULL || !ISREMOTESESSION())
|
|
return TRUE;
|
|
|
|
ghdcBits2 = CreateCompatibleDC(NULL);
|
|
if (ghdcBits2 == NULL) {
|
|
RIPMSG0(RIP_WARNING, "Could not allocate ghdcBits2");
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* Get things we need from Gdi.
|
|
*/
|
|
if (ghbrWhite == NULL)
|
|
ghbrWhite = GetStockObject(WHITE_BRUSH);
|
|
|
|
if (ghbrBlack == NULL)
|
|
ghbrBlack = GetStockObject(BLACK_BRUSH);
|
|
|
|
UserAssert(ghbrWhite != NULL && ghbrBlack != NULL);
|
|
|
|
if (!InitClientDrawing()) {
|
|
RIPMSG0(RIP_WARNING, "InitClientDrawing failed");
|
|
return FALSE;
|
|
}
|
|
|
|
if (gfServerProcess) {
|
|
if (!LoadCursorsAndIcons()) {
|
|
RIPMSG0(RIP_WARNING, "LoadCursorsAndIcons failed");
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Register the control classes.
|
|
*/
|
|
if (!RW_RegisterControls())
|
|
return FALSE;
|
|
|
|
if (!RW_RegisterDDEML())
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
DWORD GetRipComponent(VOID)
|
|
{
|
|
return RIP_USER;
|
|
}
|
|
|
|
|
|
DWORD GetDbgTagFlags(int tag)
|
|
{
|
|
#if DEBUGTAGS
|
|
return (gpsi != NULL ? gpsi->adwDBGTAGFlags[tag] : 0);
|
|
#else
|
|
return 0;
|
|
UNREFERENCED_PARAMETER(tag);
|
|
#endif // DEBUGTAGS
|
|
}
|
|
|
|
|
|
DWORD GetRipPID(VOID)
|
|
{
|
|
return (gpsi != NULL ? gpsi->wRIPPID : 0);
|
|
}
|
|
|
|
|
|
DWORD GetRipFlags(VOID)
|
|
{
|
|
return (gpsi != NULL ? gpsi->wRIPFlags : RIPF_DEFAULT);
|
|
}
|
|
|
|
|
|
VOID SetRipFlags(DWORD dwRipFlags, DWORD dwRipPID)
|
|
{
|
|
NtUserSetRipFlags(dwRipFlags, dwRipPID);
|
|
}
|
|
|
|
|
|
VOID SetDbgTag(int tag, DWORD dwBitFlags)
|
|
{
|
|
NtUserSetDbgTag(tag, dwBitFlags);
|
|
}
|