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.
1129 lines
31 KiB
1129 lines
31 KiB
/****************************** Module Header ******************************\
|
|
* Module Name: clinit.c
|
|
*
|
|
* Copyright (C) 1985-1995, 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 <winss.h>
|
|
|
|
/*
|
|
* Global variables local to this module (startup).
|
|
*/
|
|
BOOL gfFirstThread = TRUE;
|
|
PDESKTOPINFO pdiLocal = NULL;
|
|
BOOL gbIhaveBeenInited = FALSE;
|
|
|
|
|
|
/*
|
|
* External declared routines needed for startup.
|
|
*/
|
|
typedef DWORD (*PFNWAITFORINPUTIDLE)(HANDLE hProcess, DWORD dwMilliseconds);
|
|
VOID RegisterWaitForInputIdle(PFNWAITFORINPUTIDLE);
|
|
BOOL GdiProcessSetup();
|
|
BOOL GdiDllInitialize(IN PVOID hmod, IN DWORD Reason, IN PCONTEXT pctx OPTIONAL);
|
|
#ifdef FE_IME
|
|
VOID ImmRegisterClient(PSHAREDINFO psiClient);
|
|
#endif
|
|
|
|
NTSTATUS CallSoundDriver(
|
|
IN BOOL fInit,
|
|
IN LPWSTR lpszName OPTIONAL,
|
|
IN DWORD idSnd,
|
|
IN DWORD dwFlags,
|
|
IN PBOOL pbResult);
|
|
|
|
/***************************************************************************\
|
|
* UserClientDllInitialize
|
|
*
|
|
* 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.
|
|
\***************************************************************************/
|
|
|
|
extern CONST PCSR_CALLBACK_ROUTINE apfnDispatch[];
|
|
extern CONST ULONG ulMaxApiIndex;
|
|
|
|
BOOL UserClientDllInitialize(
|
|
IN PVOID hmod,
|
|
IN DWORD Reason,
|
|
IN PCONTEXT pctx OPTIONAL)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
|
|
DBG_UNREFERENCED_PARAMETER(pctx);
|
|
|
|
if (Reason == DLL_PROCESS_ATTACH) {
|
|
|
|
NTSTATUS status;
|
|
USERCONNECT userconnect;
|
|
ULONG ulConnect = sizeof(USERCONNECT);
|
|
PCSR_FAST_ANSI_OEM_TABLES Tables;
|
|
|
|
#if 0
|
|
/*
|
|
* Leave thread callouts enabled for gdi
|
|
*/
|
|
DisableThreadLibraryCalls(hmod);
|
|
#endif
|
|
|
|
if (gbIhaveBeenInited) {
|
|
return TRUE;
|
|
}
|
|
|
|
gbIhaveBeenInited = TRUE;
|
|
|
|
RtlInitializeCriticalSection(&gcsClipboard);
|
|
RtlInitializeCriticalSection(&gcsLookaside);
|
|
RtlInitializeCriticalSection(&gcsHdc);
|
|
InitDDECrit;
|
|
|
|
userconnect.ulVersion = USERCURRENTVERSION;
|
|
|
|
status = CsrClientConnectToServer(WINSS_OBJECT_DIRECTORY_NAME,
|
|
USERSRV_SERVERDLL_INDEX,
|
|
NULL,
|
|
&userconnect,
|
|
&ulConnect,
|
|
(PBOOLEAN)&gfServerProcess);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
RIPMSG1(RIP_WARNING,
|
|
"USER32: couldn't connect to server, status=%#lx",
|
|
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) {
|
|
gSharedInfo = userconnect.siClient;
|
|
gpsi = gSharedInfo.psi;
|
|
#ifdef FE_IME
|
|
ImmRegisterClient(&userconnect.siClient);
|
|
#endif
|
|
}
|
|
|
|
rescalls.pfnFindResourceExA = (PFNFINDA)FindResourceExA;
|
|
rescalls.pfnFindResourceExW = (PFNFINDW)FindResourceExW;
|
|
rescalls.pfnLoadResource = (PFNLOAD)LoadResource;
|
|
rescalls.pfnSizeofResource = (PFNSIZEOF)SizeofResource;
|
|
|
|
Tables = NtCurrentPeb()->ReadOnlyStaticServerData[CSRSRV_SERVERDLL_INDEX];
|
|
|
|
gpOemToAnsi = Tables->OemToAnsiTable;
|
|
gpAnsiToOem = Tables->AnsiToOemTable;
|
|
|
|
/*
|
|
* 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;
|
|
|
|
InitClassOffsets();
|
|
|
|
} else if (Reason == DLL_PROCESS_DETACH) {
|
|
|
|
BOOL bResult;
|
|
|
|
CallSoundDriver(FALSE, L"Close", 0,
|
|
SND_ALIAS | SND_ASYNC | SND_NODEFAULT, &bResult);
|
|
}
|
|
|
|
bRet = GdiDllInitialize(hmod, Reason, pctx);
|
|
|
|
return(bRet);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* LoadCursorsAndIcons
|
|
*
|
|
* 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.
|
|
\***************************************************************************/
|
|
|
|
VOID LoadCursorsAndIcons(VOID)
|
|
{
|
|
int i;
|
|
HANDLE h;
|
|
|
|
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)(OIC_FIRST_DEFAULT + i),
|
|
RT_ICON,
|
|
SYSMET(CXSMICON),
|
|
SYSMET(CYSMICON),
|
|
LR_GLOBAL);
|
|
UserAssert(h);
|
|
}
|
|
h = LoadIcoCur(NULL,
|
|
(LPCWSTR)(OIC_FIRST_DEFAULT + i),
|
|
RT_ICON,
|
|
0,
|
|
0,
|
|
LR_SHARED | LR_GLOBAL);
|
|
UserAssert(h);
|
|
}
|
|
|
|
for (i = 0; i < COCR_CONFIGURABLE; i++) {
|
|
h = LoadIcoCur(NULL,
|
|
(LPCWSTR)(OCR_FIRST_DEFAULT + i),
|
|
RT_CURSOR,
|
|
0,
|
|
0,
|
|
LR_SHARED | LR_GLOBAL);
|
|
UserAssert(h);
|
|
}
|
|
|
|
/*
|
|
* Now go to the kernel and fixup the IDs from DEFAULT values
|
|
* to standard values.
|
|
*/
|
|
NtUserCallNoParam(SFI__LOADCURSORSANDICONS);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* RW_RegisterControls
|
|
*
|
|
* Register the control classes. This function must be called for each
|
|
* client process.
|
|
*
|
|
* History:
|
|
* ??-??-?? DarrinM Ported.
|
|
* ??-??-?? MikeKe Moved here from server.
|
|
\***************************************************************************/
|
|
|
|
/*
|
|
* NOTE -- the class names must stay in the RegisterClass exactly as they are, since
|
|
* MS-TEST assumes these names exist as strings.
|
|
*/
|
|
|
|
VOID RW_RegisterControls(VOID)
|
|
{
|
|
int i;
|
|
WNDCLASSEX wndcls;
|
|
|
|
static CONST struct {
|
|
UINT style;
|
|
WNDPROC lpfnWndProcW;
|
|
PROC lpfnWorker;
|
|
int cbWndExtra;
|
|
LPCTSTR lpszCursor;
|
|
HBRUSH hbrBackground;
|
|
LPCTSTR lpszClassName;
|
|
WORD fnid;
|
|
} rc[] = {
|
|
|
|
{CS_GLOBALCLASS | CS_PARENTDC | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW,
|
|
ButtonWndProcW,
|
|
ButtonWndProcWorker,
|
|
sizeof(BUTNWND) - sizeof(WND),
|
|
IDC_ARROW,
|
|
NULL,
|
|
L"Button",
|
|
FNID_BUTTON
|
|
},
|
|
|
|
{CS_GLOBALCLASS | CS_DBLCLKS | CS_PARENTDC | CS_VREDRAW | CS_HREDRAW,
|
|
ComboBoxWndProcW,
|
|
ComboBoxWndProcWorker,
|
|
sizeof(COMBOWND) - sizeof(WND),
|
|
IDC_ARROW,
|
|
NULL,
|
|
L"ComboBox",
|
|
FNID_COMBOBOX
|
|
},
|
|
|
|
{CS_GLOBALCLASS | CS_DBLCLKS | CS_SAVEBITS,
|
|
ComboListBoxWndProcW,
|
|
ListBoxWndProcWorker,
|
|
sizeof(LBWND) - sizeof(WND),
|
|
IDC_ARROW,
|
|
NULL,
|
|
L"ComboLBox",
|
|
FNID_COMBOLISTBOX
|
|
},
|
|
|
|
{CS_GLOBALCLASS | CS_DBLCLKS | CS_SAVEBITS,
|
|
DefDlgProcW,
|
|
DefDlgProcWorker,
|
|
DLGWINDOWEXTRA,
|
|
IDC_ARROW,
|
|
NULL,
|
|
DIALOGCLASS,
|
|
FNID_DIALOG
|
|
},
|
|
|
|
{CS_GLOBALCLASS | CS_PARENTDC | CS_DBLCLKS,
|
|
EditWndProcW,
|
|
EditWndProcWorker,
|
|
CBEDITEXTRA,
|
|
IDC_IBEAM,
|
|
NULL,
|
|
L"Edit",
|
|
FNID_EDIT
|
|
},
|
|
|
|
{CS_GLOBALCLASS | CS_PARENTDC | CS_DBLCLKS,
|
|
ListBoxWndProcW,
|
|
ListBoxWndProcWorker,
|
|
sizeof(LBWND) - sizeof(WND),
|
|
IDC_ARROW,
|
|
NULL,
|
|
L"ListBox",
|
|
FNID_LISTBOX
|
|
},
|
|
|
|
{CS_GLOBALCLASS,
|
|
MDIClientWndProcW,
|
|
MDIClientWndProcWorker,
|
|
sizeof(MDIWND) - sizeof(WND),
|
|
IDC_ARROW,
|
|
(HBRUSH)(COLOR_APPWORKSPACE + 1),
|
|
L"MDIClient",
|
|
FNID_MDICLIENT
|
|
},
|
|
|
|
#ifdef FE_IME
|
|
{CS_GLOBALCLASS,
|
|
ImeWndProcW,
|
|
ImeWndProcWorker,
|
|
sizeof(IMEWND) - sizeof(WND),
|
|
IDC_ARROW,
|
|
NULL,
|
|
L"IME",
|
|
FNID_IME
|
|
},
|
|
|
|
#endif
|
|
{CS_GLOBALCLASS | CS_PARENTDC | CS_DBLCLKS,
|
|
StaticWndProcW,
|
|
StaticWndProcWorker,
|
|
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;
|
|
|
|
RegisterClassExWOWW(
|
|
&wndcls,
|
|
NULL,
|
|
rc[i].lpfnWorker,
|
|
rc[i].fnid);
|
|
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* RW_RegisterDDEMLMother
|
|
*
|
|
* Register the DDEML client instance mother window - the holder of all
|
|
* DDEML client and server windows.
|
|
*
|
|
* History:
|
|
* 01-Dec-1991 Sanfords Created.
|
|
\***************************************************************************/
|
|
|
|
VOID RW_RegisterDDEMLMother(VOID)
|
|
{
|
|
WNDCLASSEX wndcls;
|
|
|
|
#ifdef LATER
|
|
/*
|
|
* RegisterClass() will not register it again if it is already created
|
|
* so no need to check first.
|
|
*/
|
|
|
|
/*
|
|
* If the class has been registered, don't do it again
|
|
*/
|
|
if (GetClassInfo(hmodUser, szDDEMLMOTHERCLASS, &wndcls))
|
|
return;
|
|
#endif
|
|
|
|
RtlZeroMemory(&wndcls, sizeof(wndcls));
|
|
wndcls.cbSize = sizeof(wndcls);
|
|
wndcls.lpfnWndProc = DDEMLMotherWndProc;
|
|
wndcls.cbWndExtra = sizeof(PCL_INSTANCE_INFO);
|
|
wndcls.hInstance = hmodUser;
|
|
wndcls.lpszClassName = L"DDEMLMom";
|
|
RegisterClassExWOWW(&wndcls, NULL, NULL, FNID_DDE_BIT);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* RW_RegisterDDEMLClient
|
|
*
|
|
* History:
|
|
* 01-Dec-1991 Sanfords Created.
|
|
\***************************************************************************/
|
|
|
|
VOID RW_RegisterDDEMLClient(VOID)
|
|
{
|
|
WNDCLASSEXA wndclsa;
|
|
WNDCLASSEXW wndclsw;
|
|
|
|
#ifdef LATER
|
|
/*
|
|
* RegisterClass() will not register it again if it is already created
|
|
* so no need to check first.
|
|
*/
|
|
|
|
/*
|
|
* If the class has been registered, don't do it again
|
|
*/
|
|
if (!GetClassInfoA(hmodUser, szDDEMLCLIENTCLASSA, &wndclsa))
|
|
return;
|
|
#endif
|
|
|
|
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";
|
|
wndclsa.hIconSm = NULL;
|
|
|
|
RegisterClassExWOWA(&wndclsa, NULL, NULL, FNID_DDE_BIT);
|
|
|
|
RtlZeroMemory(&wndclsw, sizeof(wndclsw));
|
|
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";
|
|
wndclsw.hIconSm = NULL;
|
|
|
|
RegisterClassExWOWW(&wndclsw, NULL, NULL, FNID_DDE_BIT);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* RW_RegisterDDEMLServer
|
|
*
|
|
* History:
|
|
* 01-Dec-1991 Sanfords Created.
|
|
\***************************************************************************/
|
|
|
|
VOID RW_RegisterDDEMLServer(VOID)
|
|
{
|
|
WNDCLASSEXA wndclsa;
|
|
WNDCLASSEXW wndclsw;
|
|
|
|
#ifdef LATER
|
|
/*
|
|
* RegisterClass() will not register it again if it is already created
|
|
* so no need to check first.
|
|
*/
|
|
|
|
/*
|
|
* If the class has been registered, don't do it again
|
|
*/
|
|
if (!GetClassInfoA(hmodUser, szDDEMLSERVERCLASSA, &wndclsa))
|
|
return;
|
|
#endif
|
|
|
|
RtlZeroMemory(&wndclsa, sizeof(wndclsa));
|
|
wndclsa.cbSize = sizeof(wndclsa);
|
|
wndclsa.lpfnWndProc = DDEMLServerWndProc;
|
|
wndclsa.cbWndExtra = sizeof(PSVR_CONV_INFO); // GWL_PSI
|
|
wndclsa.hInstance = hmodUser;
|
|
wndclsa.lpszClassName = "DDEMLAnsiServer";
|
|
|
|
RegisterClassExWOWA(&wndclsa, NULL, NULL, FNID_DDE_BIT);
|
|
|
|
RtlZeroMemory(&wndclsw, sizeof(wndclsw));
|
|
wndclsw.cbSize = sizeof(wndclsw);
|
|
wndclsw.lpfnWndProc = DDEMLServerWndProc;
|
|
wndclsw.cbWndExtra = sizeof(PSVR_CONV_INFO); // GWL_PSI
|
|
wndclsw.hInstance = hmodUser;
|
|
wndclsw.lpszClassName = L"DDEMLUnicodeServer";
|
|
|
|
RegisterClassExWOWW(&wndclsw, NULL, NULL, FNID_DDE_BIT);
|
|
|
|
}
|
|
|
|
#ifdef SUPPORT_LPK
|
|
/***************************************************************************\
|
|
* LoadMultiLng - Load Multilingual DLLs
|
|
*
|
|
*
|
|
*
|
|
\***************************************************************************/
|
|
|
|
|
|
/*
|
|
* load up the charset lpk's if they exist. Also perform any final fixups on
|
|
* the lcid array.
|
|
*
|
|
* HKEY_CURRENT_USER\REGSTR_PATH_LPK\00B1=lpkheb.dll
|
|
* HKEY_CURRENT_USER\REGSTR_PATH_LPK\0000=lpkansi.dll
|
|
*
|
|
* etc
|
|
*/
|
|
|
|
WCHAR static wszKeyBase[] = L"\\HKEY_CURRENT_USER\\REGSTR_PATH_LPK";
|
|
PCHARSETBLOCK gpCharset = NULL;
|
|
UINT gnCharset = 0;
|
|
|
|
typedef struct {
|
|
UINT id;
|
|
HDC hdc;
|
|
} LPKUSERINIT;
|
|
|
|
typedef VOID (FAR PASCAL *LPFNLPKENABLE)(LPKUSERINIT FAR *);
|
|
|
|
/*
|
|
* STUB: GDI needs to havve this function
|
|
*/
|
|
BOOL AddLpkToGDI(
|
|
HANDLE hlibLPK,
|
|
UINT iCharset)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
VOID LoadMultiLng(VOID)
|
|
{
|
|
HKEY hk;
|
|
DWORD dwIdx;
|
|
WCHAR wszValue[9];
|
|
WCHAR wszData[MAX_PATH];
|
|
DWORD cch;
|
|
DWORD cb2;
|
|
DWORD dwType = REG_SZ;
|
|
LPKUSERINIT LpkUserInit;
|
|
LPFNLPKENABLE lpfnLpkEnable;
|
|
|
|
/*
|
|
* there is a max of 32 charset blocks in the system
|
|
*/
|
|
gpCharset = (PCHARSETBLOCK)LocalAlloc(LMEM_ZEROINIT | LMEM_FIXED,
|
|
(32 * sizeof(CHARSETBLOCK)));
|
|
if (gpCharset == NULL)
|
|
return ;
|
|
|
|
LpkUserInit.id = 2; // user calling.
|
|
LpkUserInit.hdc = hdcBits2; // lpk needs to know this for initialising
|
|
// stuff like cxSysFont etc.
|
|
|
|
if (RegOpenKey( HKEY_CURRENT_USER, wszKeyBase, &hk) == ERROR_SUCCESS) {
|
|
goto finished_with_registry;
|
|
}
|
|
|
|
dwIdx = 0;
|
|
while (TRUE) {
|
|
|
|
cch = sizeof(wszValue) / sizeof(WCHAR);
|
|
cb2 = sizeof(wszData);
|
|
|
|
if (RegEnumValueW(hk,
|
|
dwIdx++,
|
|
wszValue,
|
|
&cch,
|
|
NULL,
|
|
&dwType,
|
|
(LPBYTE)wszData,
|
|
&cb2) != ERROR_SUCCESS) {
|
|
break;
|
|
}
|
|
|
|
if (!wcslen(wszData)) {
|
|
|
|
/*
|
|
* enum'ed default string - has NULL data
|
|
*/
|
|
continue;
|
|
|
|
}
|
|
|
|
gpCharset[gnCharset].iCharset = (UINT)wcstol(wszValue, NULL, 10) & 0xff;
|
|
gpCharset[gnCharset].hlibLPK = LoadLibrary(wszData);
|
|
|
|
if (gpCharset[gnCharset].hlibLPK == NULL) {
|
|
|
|
/*
|
|
* darn! It didn't load.
|
|
*/
|
|
continue;
|
|
}
|
|
|
|
if (!AddLpkToGDI(gpCharset[gnCharset].hlibLPK,
|
|
gpCharset[gnCharset].iCharset)) {
|
|
/*
|
|
* darn! GDI refused it.
|
|
*/
|
|
FreeLibrary(gpCharset[gnCharset].hlibLPK);
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* add in the stuff USER needs. If any of these fail, continue to the
|
|
* next one. The LPK will still be active in GDI. This is the correct
|
|
* thing as it is enough for certain languages.
|
|
*/
|
|
if (!(gpCharset[gnCharset].lpfnTabTextCall = (TABTEXTCALLBACK)
|
|
GetProcAddress(gpCharset[gnCharset].hlibLPK,
|
|
(LPCSTR)MAKELONG(21,0))))
|
|
continue;
|
|
|
|
if (!(gpCharset[gnCharset].lpfnDrawTextCall =
|
|
GetProcAddress(gpCharset[gnCharset].hlibLPK,
|
|
(LPCSTR)MAKELONG(22,0))))
|
|
continue;
|
|
|
|
if (!(gpCharset[gnCharset].lpfnPSMTextOutCall = (LPFNTEXTOUT)
|
|
GetProcAddress(gpCharset[gnCharset].hlibLPK,
|
|
(LPCSTR)MAKELONG(23,0))))
|
|
continue;
|
|
|
|
/*
|
|
* get editclass helper. If this fails, let the lpk stay active as it
|
|
* can still be used for document editing in 4.0 apps.
|
|
*/
|
|
gpCharset[gnCharset].lpfnEditCall = (EDITCHARSETPROC)
|
|
GetProcAddress(gpCharset[gnCharset].hlibLPK,
|
|
(LPCSTR)MAKELONG(20,0));
|
|
|
|
/*
|
|
* enable from user. no need to check if the entry point exists,
|
|
* it doesn't, GDI wouldn't allow it.
|
|
*/
|
|
lpfnLpkEnable = (LPFNLPKENABLE)
|
|
GetProcAddress(gpCharset[gnCharset].hlibLPK,
|
|
(LPCSTR)MAKELONG(2,0));
|
|
|
|
(*lpfnLpkEnable)(&LpkUserInit);
|
|
|
|
#if defined(WINDOWS_ME)
|
|
/*
|
|
* kill_font_association helper
|
|
*/
|
|
#if defined(hebrew)
|
|
if (gpCharset[gnCharset].iCharset == HEBREW_CHARSET)
|
|
#endif
|
|
|
|
#if defined(arabic)
|
|
if (gpCharset[gnCharset].iCharset == ARABIC_CHARSET)
|
|
#endif
|
|
lpfnMonoFont =(LPFNPUREFONT)GetProcAddress(
|
|
gpCharset[gnCharset].hlibLPK,
|
|
(LPCSTR)MAKELONG(102,0));
|
|
#endif
|
|
gnCharset++;
|
|
}
|
|
RegCloseKey(hk);
|
|
|
|
finished_with_registry:
|
|
|
|
if (gnCharset) {
|
|
gpCharset = (PCHARSETBLOCK)LocalReAlloc((HANDLE)gpCharset,
|
|
sizeof(CHARSETBLOCK) * gnCharset,
|
|
LMEM_MOVEABLE );
|
|
} else {
|
|
LocalFree((HANDLE)gpCharset);
|
|
gpCharset = NULL;
|
|
}
|
|
}
|
|
#endif // SUPPORT_LPK
|
|
|
|
|
|
/***************************************************************************\
|
|
* LoadAppDlls()
|
|
*
|
|
* History:
|
|
*
|
|
* 10-Apr-1992 sanfords Birthed.
|
|
\***************************************************************************/
|
|
VOID LoadAppDlls(VOID)
|
|
{
|
|
LPTSTR psz;
|
|
extern BOOL gfLogonProcess;
|
|
|
|
if ((gSharedInfo.pszDllList == NULL) || gfLogonProcess) {
|
|
|
|
/*
|
|
* 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;
|
|
}
|
|
|
|
/*
|
|
* Load any modules referenced by the [appinit_dlls] section of
|
|
* win.ini.
|
|
*/
|
|
psz = gSharedInfo.pszDllList;
|
|
while (*psz != TEXT('\0')) {
|
|
LoadLibrary(psz);
|
|
while (*psz != TEXT('\0')) {
|
|
psz++;
|
|
}
|
|
psz++;
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* ClientThreadSetup
|
|
*
|
|
*
|
|
*
|
|
\***************************************************************************/
|
|
|
|
BOOL ClientThreadSetup(VOID)
|
|
{
|
|
PPFNCLIENT ppfnClientA;
|
|
PPFNCLIENT ppfnClientW;
|
|
PFNCLIENT pfnClientA;
|
|
PFNCLIENT pfnClientW;
|
|
PCLIENTINFO pci;
|
|
HDC hdc;
|
|
BOOL fFirstThread = InterlockedExchange(&gfFirstThread, FALSE);
|
|
|
|
#ifdef TRACE_THREAD_INIT
|
|
KdPrint(("USER32: ClientThreadSetup (pteb: 0x%lx)\n", NtCurrentTeb()));
|
|
#endif
|
|
|
|
/*
|
|
* setup GDI before continuing
|
|
*/
|
|
if (fFirstThread) {
|
|
GdiProcessSetup();
|
|
}
|
|
|
|
/*
|
|
* 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).
|
|
*/
|
|
ppfnClientA = NULL;
|
|
ppfnClientW = NULL;
|
|
|
|
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;
|
|
Status = NtUserProcessConnect(NtCurrentProcess(),
|
|
&userconnect,
|
|
sizeof(USERCONNECT));
|
|
if (!NT_SUCCESS(Status))
|
|
return FALSE;
|
|
|
|
gSharedInfo = userconnect.siClient;
|
|
gpsi = gSharedInfo.psi;
|
|
UserAssert(gpsi);
|
|
|
|
#ifdef DEBUG
|
|
RtlZeroMemory(&pfnClientA, sizeof(pfnClientA));
|
|
RtlZeroMemory(&pfnClientW, sizeof(pfnClientW));
|
|
#endif
|
|
|
|
ppfnClientA = &pfnClientA;
|
|
pfnClientA.pfnScrollBarWndProc = (PROC)ScrollBarWndProcA;
|
|
pfnClientA.pfnTitleWndProc = (PROC)TitleWndProcA;
|
|
pfnClientA.pfnMenuWndProc = (PROC)MenuWndProcA;
|
|
pfnClientA.pfnDesktopWndProc = (PROC)DesktopWndProcA;
|
|
pfnClientA.pfnDefWindowProc = (PROC)DefWindowProcA;
|
|
pfnClientA.pfnHkINLPCWPSTRUCT = (PROC)fnHkINLPCWPSTRUCTA;
|
|
pfnClientA.pfnHkINLPCWPRETSTRUCT = (PROC)fnHkINLPCWPRETSTRUCTA;
|
|
pfnClientA.pfnButtonWndProc = (PROC)ButtonWndProcA;
|
|
pfnClientA.pfnComboBoxWndProc = (PROC)ComboBoxWndProcA;
|
|
pfnClientA.pfnComboListBoxProc = (PROC)ComboListBoxWndProcA;
|
|
pfnClientA.pfnDialogWndProc = (PROC)DefDlgProcA;
|
|
pfnClientA.pfnEditWndProc = (PROC)EditWndProcA;
|
|
pfnClientA.pfnListBoxWndProc = (PROC)ListBoxWndProcA;
|
|
pfnClientA.pfnMB_DlgProc = (PROC)MB_DlgProcA;
|
|
pfnClientA.pfnMDIActivateDlgProc = (PROC)MDIActivateDlgProcA;
|
|
pfnClientA.pfnMDIClientWndProc = (PROC)MDIClientWndProcA;
|
|
pfnClientA.pfnStaticWndProc = (PROC)StaticWndProcA;
|
|
pfnClientA.pfnDispatchHook = (PROC)DispatchHookA;
|
|
pfnClientA.pfnDispatchMessage = (PROC)DispatchClientMessage;
|
|
#ifdef FE_IME
|
|
pfnClientA.pfnImeWndProc = (PROC)ImeWndProcA;
|
|
#endif
|
|
|
|
ppfnClientW = &pfnClientW;
|
|
pfnClientW.pfnScrollBarWndProc = (PROC)ScrollBarWndProcW;
|
|
pfnClientW.pfnTitleWndProc = (PROC)TitleWndProcW;
|
|
pfnClientW.pfnMenuWndProc = (PROC)MenuWndProcW;
|
|
pfnClientW.pfnDesktopWndProc = (PROC)DesktopWndProcW;
|
|
pfnClientW.pfnDefWindowProc = (PROC)DefWindowProcW;
|
|
pfnClientW.pfnHkINLPCWPSTRUCT = (PROC)fnHkINLPCWPSTRUCTA;
|
|
pfnClientW.pfnHkINLPCWPRETSTRUCT = (PROC)fnHkINLPCWPRETSTRUCTA;
|
|
pfnClientW.pfnButtonWndProc = (PROC)ButtonWndProcW;
|
|
pfnClientW.pfnComboBoxWndProc = (PROC)ComboBoxWndProcW;
|
|
pfnClientW.pfnComboListBoxProc = (PROC)ComboListBoxWndProcW;
|
|
pfnClientW.pfnDialogWndProc = (PROC)DefDlgProcW;
|
|
pfnClientW.pfnEditWndProc = (PROC)EditWndProcW;
|
|
pfnClientW.pfnListBoxWndProc = (PROC)ListBoxWndProcW;
|
|
pfnClientW.pfnMB_DlgProc = (PROC)MB_DlgProcW;
|
|
pfnClientW.pfnMDIActivateDlgProc = (PROC)MDIActivateDlgProcW;
|
|
pfnClientW.pfnMDIClientWndProc = (PROC)MDIClientWndProcW;
|
|
pfnClientW.pfnStaticWndProc = (PROC)StaticWndProcW;
|
|
pfnClientW.pfnDispatchHook = (PROC)DispatchHookW;
|
|
pfnClientW.pfnDispatchMessage = (PROC)DispatchClientMessage;
|
|
#ifdef FE_IME
|
|
pfnClientW.pfnImeWndProc = (PROC)ImeWndProcW;
|
|
#endif
|
|
|
|
|
|
#ifdef DEBUG
|
|
{
|
|
PDWORD pdw;
|
|
|
|
/*
|
|
* Make sure that everyone got initialized
|
|
*/
|
|
for (pdw = (PDWORD)&pfnClientA;
|
|
(DWORD)pdw<(DWORD)(&pfnClientA) + sizeof(pfnClientA);
|
|
pdw++) {
|
|
UserAssert(*pdw);
|
|
}
|
|
|
|
for (pdw = (PDWORD)&pfnClientW;
|
|
(DWORD)pdw<(DWORD)(&pfnClientW) + sizeof(pfnClientW);
|
|
pdw++) {
|
|
UserAssert(*pdw);
|
|
}
|
|
}
|
|
#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 (!NT_SUCCESS(NtUserInitializeClientPfnArrays(ppfnClientA, ppfnClientW, hmodUser))) {
|
|
|
|
RIPERR0(ERROR_OUTOFMEMORY,
|
|
RIP_WARNING,
|
|
"NtUserInitializeClientPfnArrays failed");
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
#ifdef FE_SB // ClientThreadSetup()
|
|
/*
|
|
* Clear Dbcs messaging buffer.
|
|
*/
|
|
RtlZeroMemory(&(pci->msgDbcsCB),sizeof(pci->msgDbcsCB));
|
|
RtlZeroMemory(&(pci->achDbcsCF),sizeof(pci->achDbcsCF));
|
|
#endif // FE_SB
|
|
|
|
/*
|
|
* 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) {
|
|
|
|
if ((ghdcBits2 = CreateCompatibleDC(NULL)) == NULL) {
|
|
RIPERR0(ERROR_OUTOFMEMORY, RIP_WARNING, "ghdcBits2 creation failed");
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* Get things we need from Gdi.
|
|
*/
|
|
NtUserInitBrushes(ahbrSystem, &ghbrGray);
|
|
if (ghbrWhite == NULL)
|
|
ghbrWhite = GetStockObject(WHITE_BRUSH);
|
|
|
|
if (ghbrBlack == NULL)
|
|
ghbrBlack = GetStockObject(BLACK_BRUSH);
|
|
|
|
if (hdc = CreateIC(L"DISPLAY", NULL, NULL, NULL)) {
|
|
SetRect(&rcScreen,
|
|
0,
|
|
0,
|
|
GetDeviceCaps(hdc, DESKTOPHORZRES),
|
|
GetDeviceCaps(hdc, DESKTOPVERTRES));
|
|
DeleteDC(hdc);
|
|
}
|
|
|
|
InitClientDrawing();
|
|
|
|
gfSystemInitialized = (BOOL)NtUserGetThreadDesktop(GetCurrentThreadId(),
|
|
NULL);
|
|
|
|
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) {
|
|
LoadCursorsAndIcons();
|
|
}
|
|
|
|
atomBwlProp = AddAtomW(WINDOWLIST_PROP_NAME);
|
|
atomMsgBoxCallback = AddAtomW(MSGBOX_CALLBACK);
|
|
|
|
/*
|
|
* Load some strings.
|
|
*/
|
|
LoadStringW(hmodUser, STR_ERROR, szERROR, 10);
|
|
|
|
/*
|
|
* Register the control classes.
|
|
*/
|
|
RW_RegisterControls();
|
|
RW_RegisterDDEMLMother();
|
|
RW_RegisterDDEMLClient();
|
|
RW_RegisterDDEMLServer();
|
|
|
|
#ifdef SUPPORT_LPK
|
|
LoadMultiLng();
|
|
#endif // SUPPORT_LPK
|
|
|
|
LoadAppDlls();
|
|
|
|
|
|
} else if (gfServerProcess) {
|
|
GetClientInfo()->pDeskInfo = pdiLocal;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* Dispatch routines.
|
|
*
|
|
*
|
|
\***************************************************************************/
|
|
|
|
HLOCAL WINAPI DispatchLocalAlloc(
|
|
UINT uFlags,
|
|
UINT uBytes,
|
|
HANDLE hInstance)
|
|
{
|
|
return LocalAlloc(uFlags, uBytes);
|
|
}
|
|
|
|
HLOCAL WINAPI DispatchLocalReAlloc(
|
|
HLOCAL hMem,
|
|
UINT uBytes,
|
|
UINT uFlags,
|
|
HANDLE hInstance,
|
|
PVOID* ppv)
|
|
{
|
|
UNREFERENCED_PARAMETER(ppv);
|
|
|
|
return LocalReAlloc(hMem, uBytes, uFlags);
|
|
}
|
|
|
|
LPVOID WINAPI DispatchLocalLock(
|
|
HLOCAL hMem,
|
|
HANDLE hInstance)
|
|
{
|
|
return LocalLock(hMem);
|
|
}
|
|
|
|
BOOL WINAPI DispatchLocalUnlock(
|
|
HLOCAL hMem,
|
|
HANDLE hInstance)
|
|
{
|
|
return LocalUnlock(hMem);
|
|
}
|
|
|
|
UINT WINAPI DispatchLocalSize(
|
|
HLOCAL hMem,
|
|
HANDLE hInstance)
|
|
{
|
|
return LocalSize(hMem);
|
|
}
|
|
|
|
HLOCAL WINAPI DispatchLocalFree(
|
|
HLOCAL hMem,
|
|
HANDLE hInstance)
|
|
{
|
|
return LocalFree(hMem);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* Allocation routines for RTL functions.
|
|
*
|
|
*
|
|
\***************************************************************************/
|
|
|
|
PVOID UserRtlAllocMem(
|
|
ULONG uBytes)
|
|
{
|
|
return UserLocalAlloc(HEAP_ZERO_MEMORY, uBytes);
|
|
}
|
|
|
|
VOID UserRtlFreeMem(
|
|
PVOID pMem)
|
|
{
|
|
UserLocalFree(pMem);
|
|
}
|
|
|
|
VOID UserRtlRaiseStatus(
|
|
NTSTATUS Status)
|
|
{
|
|
RtlRaiseStatus(Status);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* InitClientDrawing
|
|
*
|
|
* History:
|
|
* 20-Aug-1992 mikeke Created
|
|
\***************************************************************************/
|
|
|
|
VOID InitClientDrawing(VOID)
|
|
{
|
|
static CONST WORD patGray[8] = {0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa};
|
|
|
|
|
|
HBITMAP hbmGray = CreateBitmap(8, 8, 1, 1, (LPBYTE)patGray);;
|
|
|
|
/*
|
|
* Create the global-objects for client drawing.
|
|
*/
|
|
ghbrWindowText = CreateSolidBrush(GetSysColor(COLOR_WINDOWTEXT));
|
|
ghFontSys = GetStockObject(SYSTEM_FONT);
|
|
ghdcGray = CreateCompatibleDC(NULL);
|
|
|
|
/*
|
|
* Setup the gray surface.
|
|
*/
|
|
SelectObject(ghdcGray, hbmGray);
|
|
SelectObject(ghdcGray, ghFontSys);
|
|
SelectObject(ghdcGray, ghbrGray);
|
|
|
|
/*
|
|
* Setup the gray attributes.
|
|
*/
|
|
SetBkMode(ghdcGray, OPAQUE);
|
|
SetTextColor(ghdcGray, 0x00000000L);
|
|
SetBkColor(ghdcGray, 0x00FFFFFFL);
|
|
|
|
gcxGray = 8;
|
|
gcyGray = 8;
|
|
|
|
#ifdef DEBUG
|
|
if (ghdcGray == NULL) {
|
|
RIPERR0(ERROR_OUTOFMEMORY, RIP_WARNING, "Init Client Drawing failed");
|
|
}
|
|
|
|
if ((hbmGray == NULL) || (ghbrWindowText == NULL) || (ghFontSys == NULL)) {
|
|
RIPMSG0(RIP_WARNING, "InitClientDrawing: Unsuccessful initialization");
|
|
}
|
|
#endif
|
|
}
|