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.
1943 lines
52 KiB
1943 lines
52 KiB
/*++
|
|
|
|
Copyright (c) 1990 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
misc.c
|
|
|
|
Abstract:
|
|
|
|
This file implements the NT console server font routines.
|
|
|
|
Author:
|
|
|
|
Therese Stowell (thereses) 22-Jan-1991
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "shellprv.h"
|
|
#pragma hdrstop
|
|
|
|
#include "lnkcon.h"
|
|
|
|
#define CONSOLE_REGISTRY_STRING (TEXT("Console"))
|
|
#define CONSOLE_REGISTRY_FONTSIZE (TEXT("FontSize"))
|
|
#define CONSOLE_REGISTRY_FONTFAMILY (TEXT("FontFamily"))
|
|
#define CONSOLE_REGISTRY_BUFFERSIZE (TEXT("ScreenBufferSize"))
|
|
#define CONSOLE_REGISTRY_CURSORSIZE (TEXT("CursorSize"))
|
|
#define CONSOLE_REGISTRY_WINDOWSIZE (TEXT("WindowSize"))
|
|
#define CONSOLE_REGISTRY_WINDOWPOS (TEXT("WindowPosition"))
|
|
#define CONSOLE_REGISTRY_FILLATTR (TEXT("ScreenColors"))
|
|
#define CONSOLE_REGISTRY_POPUPATTR (TEXT("PopupColors"))
|
|
#define CONSOLE_REGISTRY_FULLSCR (TEXT("FullScreen"))
|
|
#define CONSOLE_REGISTRY_QUICKEDIT (TEXT("QuickEdit"))
|
|
#define CONSOLE_REGISTRY_FACENAME (TEXT("FaceName"))
|
|
#define CONSOLE_REGISTRY_FONTWEIGHT (TEXT("FontWeight"))
|
|
#define CONSOLE_REGISTRY_INSERTMODE (TEXT("InsertMode"))
|
|
#define CONSOLE_REGISTRY_HISTORYSIZE (TEXT("HistoryBufferSize"))
|
|
#define CONSOLE_REGISTRY_HISTORYBUFS (TEXT("NumberOfHistoryBuffers"))
|
|
#define CONSOLE_REGISTRY_HISTORYNODUP (TEXT("HistoryNoDup"))
|
|
#define CONSOLE_REGISTRY_COLORTABLE (TEXT("ColorTable%02u"))
|
|
#define CONSOLE_REGISTRY_CODEPAGE (TEXT("CodePage"))
|
|
|
|
|
|
/*
|
|
* Initial default fonts and face names
|
|
*/
|
|
|
|
/*
|
|
* TTPoints -- Initial font pixel heights for TT fonts
|
|
*/
|
|
SHORT TTPoints[] = {
|
|
5, 6, 7, 8, 10, 12, 14, 16, 18, 20, 24, 28, 36, 72
|
|
};
|
|
/*
|
|
* TTPointsDbcs -- Initial font pixel heights for TT fonts of DBCS.
|
|
*/
|
|
SHORT TTPointsDbcs[] = {
|
|
6, 8, 10, 12, 14, 16, 18, 20, 24, 28, 36, 72
|
|
};
|
|
|
|
|
|
typedef struct _FONTENUMDATA {
|
|
CONSOLEPROP_DATA *pcpd;
|
|
HDC hDC;
|
|
BOOL bFindFaces;
|
|
ULONG ulFE;
|
|
PSHORT pTTPoints;
|
|
UINT nTTPoints;
|
|
UINT uDefCP;
|
|
} FONTENUMDATA, *PFONTENUMDATA;
|
|
|
|
|
|
FACENODE *
|
|
AddFaceNode(FACENODE * *ppStart, LPTSTR ptsz) {
|
|
FACENODE * pNew;
|
|
FACENODE * *ppTmp;
|
|
UINT cch;
|
|
int cb;
|
|
HRESULT hr;
|
|
|
|
/*
|
|
* Is it already here?
|
|
*/
|
|
for (ppTmp = ppStart; *ppTmp; ppTmp = &((*ppTmp)->pNext)) {
|
|
if (lstrcmp(((*ppTmp)->atch), ptsz) == 0) {
|
|
// already there !
|
|
return *ppTmp;
|
|
}
|
|
}
|
|
|
|
cch = lstrlen(ptsz) + 1;
|
|
cb = cch * sizeof(TCHAR);
|
|
pNew = (FACENODE *)LocalAlloc(LPTR ,sizeof(FACENODE) + cb);
|
|
if (pNew == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
pNew->pNext = NULL;
|
|
pNew->dwFlag = 0;
|
|
|
|
hr = StringCchCopy(pNew->atch, cch, ptsz);
|
|
if (FAILED(hr))
|
|
{
|
|
LocalFree(pNew); // return as failure to allocate
|
|
pNew = NULL;
|
|
}
|
|
|
|
*ppTmp = pNew;
|
|
return pNew;
|
|
}
|
|
|
|
|
|
VOID
|
|
DestroyFaceNodes( CONSOLEPROP_DATA *pcpd ) {
|
|
FACENODE * pNext;
|
|
FACENODE * pTmp;
|
|
|
|
pTmp = pcpd->gpFaceNames;
|
|
while (pTmp != NULL) {
|
|
pNext = pTmp->pNext;
|
|
LocalFree(pTmp);
|
|
pTmp = pNext;
|
|
}
|
|
pcpd->gpFaceNames = NULL;
|
|
}
|
|
|
|
|
|
int
|
|
AddFont(
|
|
CONSOLEPROP_DATA *pcpd,
|
|
ENUMLOGFONT *pelf,
|
|
NEWTEXTMETRIC *pntm,
|
|
int nFontType,
|
|
HDC hDC,
|
|
FACENODE * pFN
|
|
)
|
|
|
|
/*++
|
|
|
|
Add the font desribed by the LOGFONT structure to the font table if
|
|
it's not already there.
|
|
|
|
--*/
|
|
|
|
{
|
|
HFONT hFont;
|
|
TEXTMETRIC tm;
|
|
LONG nFont;
|
|
COORD SizeToShow;
|
|
COORD SizeActual;
|
|
COORD SizeWant;
|
|
BYTE tmFamily;
|
|
SIZE Size;
|
|
LPTSTR ptszFace = pelf->elfLogFont.lfFaceName;
|
|
|
|
/* get font info */
|
|
SizeWant.Y = (SHORT)pelf->elfLogFont.lfHeight;
|
|
SizeWant.X = (SHORT)pelf->elfLogFont.lfWidth;
|
|
CreateBoldFont:
|
|
hFont = CreateFontIndirect(&pelf->elfLogFont);
|
|
ASSERT(hFont);
|
|
if (!hFont) {
|
|
return FE_SKIPFONT; // same font in other sizes may still be suitable
|
|
}
|
|
|
|
//
|
|
// for reasons unbeknownst to me, removing this code causes GDI
|
|
// to yack, claiming that the font is owned by another process.
|
|
//
|
|
|
|
SelectObject(hDC, hFont);
|
|
GetTextMetrics(hDC, &tm);
|
|
|
|
GetTextExtentPoint32(hDC, TEXT("0"), 1, &Size);
|
|
SizeActual.X = (SHORT)Size.cx;
|
|
SizeActual.Y = (SHORT)(tm.tmHeight + tm.tmExternalLeading);
|
|
tmFamily = tm.tmPitchAndFamily;
|
|
if (TM_IS_TT_FONT(tmFamily) && (SizeWant.Y >= 0)) {
|
|
SizeToShow = SizeWant;
|
|
if (SizeWant.X == 0) {
|
|
// Asking for zero width height gets a default aspect-ratio width
|
|
// It's better to show that width rather than 0.
|
|
SizeToShow.X = SizeActual.X;
|
|
}
|
|
} else {
|
|
SizeToShow = SizeActual;
|
|
}
|
|
|
|
// there's a GDI bug - this assert fails occasionally
|
|
//ASSERT (tm.tmMaxCharWidth == pntm->tmMaxCharWidth);
|
|
|
|
/*
|
|
* NOW, determine whether this font entry has already been cached
|
|
* LATER : it may be possible to do this before creating the font, if
|
|
* we can trust the dimensions & other info from pntm.
|
|
* Sort by size:
|
|
* 1) By pixelheight (negative Y values)
|
|
* 2) By height (as shown)
|
|
* 3) By width (as shown)
|
|
*/
|
|
for (nFont = 0; nFont < (LONG)pcpd->NumberOfFonts; ++nFont) {
|
|
COORD SizeShown;
|
|
|
|
if (pcpd->FontInfo[nFont].hFont == NULL) {
|
|
continue;
|
|
}
|
|
|
|
if (pcpd->FontInfo[nFont].SizeWant.X > 0) {
|
|
SizeShown.X = pcpd->FontInfo[nFont].SizeWant.X;
|
|
} else {
|
|
SizeShown.X = pcpd->FontInfo[nFont].Size.X;
|
|
}
|
|
|
|
if (pcpd->FontInfo[nFont].SizeWant.Y > 0) {
|
|
// This is a font specified by cell height.
|
|
SizeShown.Y = pcpd->FontInfo[nFont].SizeWant.Y;
|
|
} else {
|
|
SizeShown.Y = pcpd->FontInfo[nFont].Size.Y;
|
|
if (pcpd->FontInfo[nFont].SizeWant.Y < 0) {
|
|
// This is a TT font specified by character height.
|
|
if (SizeWant.Y < 0 && SizeWant.Y > pcpd->FontInfo[nFont].SizeWant.Y) {
|
|
// Requested pixelheight is smaller than this one.
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if (SIZE_EQUAL(SizeShown, SizeToShow) &&
|
|
pcpd->FontInfo[nFont].Family == tmFamily &&
|
|
pcpd->FontInfo[nFont].Weight == tm.tmWeight &&
|
|
lstrcmp(pcpd->FontInfo[nFont].FaceName, ptszFace) == 0) {
|
|
/*
|
|
* Already have this font
|
|
*/
|
|
DeleteObject(hFont);
|
|
return FE_FONTOK;
|
|
}
|
|
|
|
|
|
if ((SizeToShow.Y < SizeShown.Y) ||
|
|
(SizeToShow.Y == SizeShown.Y && SizeToShow.X < SizeShown.X)) {
|
|
/*
|
|
* This new font is smaller than nFont
|
|
*/
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If we have to grow our font table, do it
|
|
*/
|
|
if (pcpd->NumberOfFonts == pcpd->FontInfoLength) {
|
|
FONT_INFO *Temp;
|
|
|
|
pcpd->FontInfoLength += FONT_INCREMENT;
|
|
Temp = (FONT_INFO *)LocalReAlloc(pcpd->FontInfo,
|
|
sizeof(FONT_INFO) * pcpd->FontInfoLength, LMEM_MOVEABLE|LMEM_ZEROINIT);
|
|
ASSERT(Temp);
|
|
if (Temp == NULL) {
|
|
pcpd->FontInfoLength -= FONT_INCREMENT;
|
|
return FE_ABANDONFONT; // no point enumerating more - no memory!
|
|
}
|
|
pcpd->FontInfo = Temp;
|
|
}
|
|
|
|
/*
|
|
* The font we are adding should be inserted into the list,
|
|
* if it is smaller than the last one.
|
|
*/
|
|
if (nFont < (LONG)pcpd->NumberOfFonts) {
|
|
MoveMemory( &pcpd->FontInfo[nFont+1],
|
|
&pcpd->FontInfo[nFont],
|
|
sizeof(FONT_INFO) * (pcpd->NumberOfFonts - nFont)
|
|
);
|
|
}
|
|
|
|
/*
|
|
* Store the font info
|
|
*/
|
|
pcpd->FontInfo[nFont].hFont = hFont;
|
|
pcpd->FontInfo[nFont].Family = tmFamily;
|
|
pcpd->FontInfo[nFont].Size = SizeActual;
|
|
if (TM_IS_TT_FONT(tmFamily)) {
|
|
pcpd->FontInfo[nFont].SizeWant = SizeWant;
|
|
} else {
|
|
pcpd->FontInfo[nFont].SizeWant.X = 0;
|
|
pcpd->FontInfo[nFont].SizeWant.Y = 0;
|
|
}
|
|
pcpd->FontInfo[nFont].Weight = tm.tmWeight;
|
|
pcpd->FontInfo[nFont].FaceName = pFN->atch;
|
|
pcpd->FontInfo[nFont].tmCharSet = tm.tmCharSet;
|
|
|
|
++pcpd->NumberOfFonts;
|
|
|
|
/*
|
|
* If this is a true type font, create a bold version too.
|
|
*/
|
|
if (nFontType == TRUETYPE_FONTTYPE && !IS_BOLD(pcpd->FontInfo[nFont].Weight)) {
|
|
pelf->elfLogFont.lfWeight = FW_BOLD;
|
|
goto CreateBoldFont;
|
|
}
|
|
|
|
return FE_FONTOK; // and continue enumeration
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
InitializeFonts( CONSOLEPROP_DATA *pcpd )
|
|
{
|
|
return EnumerateFonts( pcpd, EF_DEFFACE); // Just the Default font
|
|
}
|
|
|
|
STDAPI_(void) DestroyFonts( CONSOLEPROP_DATA *pcpd )
|
|
{
|
|
ULONG FontIndex;
|
|
|
|
if (pcpd->FontInfo != NULL) {
|
|
for (FontIndex = 0; FontIndex < pcpd->NumberOfFonts; FontIndex++) {
|
|
DeleteObject(pcpd->FontInfo[FontIndex].hFont);
|
|
}
|
|
LocalFree(pcpd->FontInfo);
|
|
pcpd->FontInfo = NULL;
|
|
pcpd->NumberOfFonts = 0;
|
|
}
|
|
|
|
DestroyFaceNodes( pcpd );
|
|
}
|
|
|
|
|
|
/*
|
|
* Returns bit combination
|
|
* FE_ABANDONFONT - do not continue enumerating this font
|
|
* FE_SKIPFONT - skip this font but keep enumerating
|
|
* FE_FONTOK - font was created and added to cache or already there
|
|
*/
|
|
int
|
|
FontEnum(
|
|
ENUMLOGFONT *pelf,
|
|
NEWTEXTMETRIC *pntm,
|
|
int nFontType,
|
|
PFONTENUMDATA pfed
|
|
)
|
|
|
|
/*++
|
|
|
|
Is called exactly once by GDI for each font in the system. This
|
|
routine is used to store the FONT_INFO structure.
|
|
|
|
--*/
|
|
|
|
{
|
|
UINT i;
|
|
LPTSTR ptszFace = pelf->elfLogFont.lfFaceName;
|
|
FACENODE * pFN;
|
|
|
|
BOOL bNegAC;
|
|
|
|
#ifdef DEBUG
|
|
OSVERSIONINFO osvi;
|
|
osvi.dwOSVersionInfoSize = sizeof(osvi);
|
|
GetVersionEx(&osvi);
|
|
|
|
// NTMW_STRUCTURE is different on 5.0+ platforms and the flag for 5.0+
|
|
// platforms now lives in NEWTEXTMETRIC structure.
|
|
AssertMsg(osvi.dwMajorVersion > 4, TEXT("We now only support running on Win2k or Millennium and later so we should never hit this."));
|
|
#endif
|
|
|
|
bNegAC = !(pntm->ntmFlags & NTM_NONNEGATIVE_AC);
|
|
|
|
//
|
|
// reject variable width and italic fonts, also tt fonts with neg ac
|
|
//
|
|
|
|
if
|
|
(
|
|
!(pelf->elfLogFont.lfPitchAndFamily & FIXED_PITCH) ||
|
|
(pelf->elfLogFont.lfItalic) ||
|
|
bNegAC
|
|
)
|
|
{
|
|
if (!IsAvailableTTFont(pfed->pcpd,ptszFace))
|
|
return pfed->bFindFaces ? FE_SKIPFONT : FE_ABANDONFONT;
|
|
}
|
|
|
|
/*
|
|
* reject TT fonts for whoom family is not modern, that is do not use
|
|
* FF_DONTCARE // may be surprised unpleasantly
|
|
* FF_DECORATIVE // likely to be symbol fonts
|
|
* FF_SCRIPT // cursive, inappropriate for console
|
|
* FF_SWISS OR FF_ROMAN // variable pitch
|
|
*/
|
|
|
|
if ((nFontType == TRUETYPE_FONTTYPE) &&
|
|
((pelf->elfLogFont.lfPitchAndFamily & 0xf0) != FF_MODERN)) {
|
|
return pfed->bFindFaces ? FE_SKIPFONT : FE_ABANDONFONT;
|
|
}
|
|
|
|
/*
|
|
* reject non-TT fonts that aren't OEM
|
|
*/
|
|
if ((nFontType != TRUETYPE_FONTTYPE) &&
|
|
(!IsFarEastCP(pfed->uDefCP) || !IS_ANY_DBCS_CHARSET(pelf->elfLogFont.lfCharSet)) &&
|
|
(pelf->elfLogFont.lfCharSet != OEM_CHARSET)) {
|
|
return pfed->bFindFaces ? FE_SKIPFONT : FE_ABANDONFONT;
|
|
}
|
|
|
|
/*
|
|
* reject non-TT vertical/non-Terminal Font for FE
|
|
*/
|
|
if (IsFarEastCP(pfed->uDefCP))
|
|
{
|
|
if ((nFontType != TRUETYPE_FONTTYPE) &&
|
|
((ptszFace[0] == TEXT('@')) ||
|
|
(lstrcmp(ptszFace, TEXT("Terminal")) != 0)))
|
|
{
|
|
return pfed->bFindFaces ? FE_SKIPFONT : FE_ABANDONFONT;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* reject Far East TT fonts that aren't Far East charset.
|
|
*/
|
|
if (IsAvailableTTFont(pfed->pcpd, ptszFace) &&
|
|
!IS_ANY_DBCS_CHARSET(pelf->elfLogFont.lfCharSet) &&
|
|
!IsAvailableTTFontCP(pfed->pcpd, ptszFace,0)
|
|
) {
|
|
return FE_SKIPFONT; // should be enumerate next charset.
|
|
}
|
|
|
|
/*
|
|
* Add or find the facename
|
|
*/
|
|
pFN = AddFaceNode(&pfed->pcpd->gpFaceNames, ptszFace);
|
|
if (pFN == NULL) {
|
|
return FE_ABANDONFONT;
|
|
}
|
|
|
|
if (pfed->bFindFaces) {
|
|
DWORD dwFontType = 0;
|
|
if (nFontType == TRUETYPE_FONTTYPE) {
|
|
dwFontType = EF_TTFONT;
|
|
} else if (nFontType == RASTER_FONTTYPE) {
|
|
dwFontType = EF_OEMFONT;
|
|
}
|
|
pFN->dwFlag |= dwFontType | EF_NEW;
|
|
|
|
if (IS_ANY_DBCS_CHARSET(pelf->elfLogFont.lfCharSet))
|
|
pFN->dwFlag |= EF_DBCSFONT;
|
|
|
|
return FE_SKIPFONT;
|
|
}
|
|
|
|
|
|
if (IS_BOLD(pelf->elfLogFont.lfWeight)) {
|
|
// return FE_SKIPFONT;
|
|
}
|
|
|
|
/*
|
|
* Add the font to the table. If this is a true type font, add the
|
|
* sizes from the array. Otherwise, just add the size we got.
|
|
*/
|
|
if (nFontType & TRUETYPE_FONTTYPE) {
|
|
for (i = 0; i < pfed->nTTPoints; i++) {
|
|
pelf->elfLogFont.lfHeight = pfed->pTTPoints[i];
|
|
pelf->elfLogFont.lfWidth = 0;
|
|
pelf->elfLogFont.lfWeight = 400;
|
|
pfed->ulFE |= AddFont(pfed->pcpd, pelf, pntm, nFontType, pfed->hDC, pFN);
|
|
if (pfed->ulFE & FE_ABANDONFONT) {
|
|
return FE_ABANDONFONT;
|
|
}
|
|
}
|
|
} else {
|
|
pfed->ulFE |= AddFont(pfed->pcpd, pelf, pntm, nFontType, pfed->hDC, pFN);
|
|
if (pfed->ulFE & FE_ABANDONFONT) {
|
|
return FE_ABANDONFONT;
|
|
}
|
|
}
|
|
|
|
return FE_FONTOK; // and continue enumeration
|
|
}
|
|
|
|
BOOL
|
|
DoFontEnum(
|
|
CONSOLEPROP_DATA *pcpd,
|
|
HDC hDC,
|
|
LPTSTR ptszFace,
|
|
PSHORT pTTPoints,
|
|
UINT nTTPoints)
|
|
{
|
|
BOOL bDeleteDC = FALSE;
|
|
FONTENUMDATA fed;
|
|
LOGFONT LogFont;
|
|
HRESULT hr = S_OK;
|
|
|
|
if (hDC == NULL) {
|
|
hDC = CreateDC(TEXT("DISPLAY"), NULL, NULL, NULL);
|
|
bDeleteDC = TRUE;
|
|
}
|
|
|
|
fed.pcpd = pcpd;
|
|
fed.hDC = hDC;
|
|
fed.bFindFaces = (ptszFace == NULL);
|
|
fed.ulFE = 0;
|
|
fed.pTTPoints = pTTPoints;
|
|
fed.nTTPoints = nTTPoints;
|
|
fed.uDefCP = pcpd->uOEMCP;
|
|
RtlZeroMemory(&LogFont, sizeof(LOGFONT));
|
|
LogFont.lfCharSet = DEFAULT_CHARSET;
|
|
if (ptszFace)
|
|
{
|
|
hr = StringCchCopy(LogFont.lfFaceName, ARRAYSIZE(LogFont.lfFaceName), ptszFace);
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
/*
|
|
* EnumFontFamiliesEx function enumerates one font in every face in every character set.
|
|
*/
|
|
EnumFontFamiliesEx(hDC, &LogFont, (FONTENUMPROC)FontEnum, (LPARAM)&fed, 0);
|
|
}
|
|
|
|
if (bDeleteDC) {
|
|
DeleteDC(hDC);
|
|
}
|
|
return (fed.ulFE & FE_FONTOK) != 0;
|
|
}
|
|
|
|
|
|
VOID
|
|
RemoveFace(CONSOLEPROP_DATA *pcpd, LPTSTR ptszFace)
|
|
{
|
|
DWORD i;
|
|
int nToRemove = 0;
|
|
|
|
//
|
|
// Delete & Remove fonts with Face Name == ptszFace
|
|
//
|
|
for (i = 0; i < pcpd->NumberOfFonts; i++) {
|
|
if (lstrcmp(pcpd->FontInfo[i].FaceName, ptszFace) == 0) {
|
|
BOOL bDeleted = DeleteObject(pcpd->FontInfo[i].hFont);
|
|
pcpd->FontInfo[i].hFont = NULL;
|
|
nToRemove++;
|
|
} else if (nToRemove > 0) {
|
|
/*
|
|
* Shuffle from FontInfo[i] down nToRemove slots.
|
|
*/
|
|
MoveMemory( &pcpd->FontInfo[i - nToRemove],
|
|
&pcpd->FontInfo[i],
|
|
sizeof(FONT_INFO)*(pcpd->NumberOfFonts - i)
|
|
);
|
|
pcpd->NumberOfFonts -= nToRemove;
|
|
i -= nToRemove;
|
|
nToRemove = 0;
|
|
}
|
|
}
|
|
pcpd->NumberOfFonts -= nToRemove;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
EnumerateFonts(
|
|
CONSOLEPROP_DATA *pcpd,
|
|
DWORD Flags)
|
|
{
|
|
TEXTMETRIC tm;
|
|
HDC hDC;
|
|
FACENODE * pFN;
|
|
ULONG ulOldEnumFilter;
|
|
BOOL bEnumOEMFace = TRUE;
|
|
DWORD FontIndex;
|
|
DWORD dwFontType = 0;
|
|
|
|
|
|
dwFontType = (EF_TTFONT|EF_OEMFONT|EF_DEFFACE) & Flags;
|
|
|
|
if (pcpd->FontInfo == NULL) {
|
|
//
|
|
// allocate memory for the font array
|
|
//
|
|
pcpd->NumberOfFonts = 0;
|
|
|
|
pcpd->FontInfo = (FONT_INFO *)LocalAlloc(LPTR, sizeof(FONT_INFO) * INITIAL_FONTS);
|
|
if (pcpd->FontInfo == NULL)
|
|
return STATUS_NO_MEMORY;
|
|
pcpd->FontInfoLength = INITIAL_FONTS;
|
|
}
|
|
|
|
hDC = CreateDC(TEXT("DISPLAY"), NULL, NULL, NULL);
|
|
|
|
// Before enumeration, turn off font enumeration filters.
|
|
ulOldEnumFilter = SetFontEnumeration(FE_FILTER_NONE);
|
|
|
|
if (Flags & EF_DEFFACE) {
|
|
SelectObject(hDC, GetStockObject(OEM_FIXED_FONT));
|
|
GetTextFace(hDC, LF_FACESIZE, pcpd->DefaultFaceName);
|
|
|
|
// Make sure we are going to enumerate the OEM face.
|
|
pFN = AddFaceNode(&pcpd->gpFaceNames, pcpd->DefaultFaceName);
|
|
if (NULL == pFN)
|
|
{
|
|
LocalFree(pcpd->FontInfo);
|
|
pcpd->FontInfo = NULL;
|
|
pcpd->FontInfoLength = 0;
|
|
pcpd->NumberOfFonts = 0;
|
|
SetFontEnumeration(ulOldEnumFilter);
|
|
DeleteDC(hDC);
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
pFN->dwFlag |= EF_DEFFACE | EF_OEMFONT;
|
|
GetTextMetrics(hDC, &tm);
|
|
pcpd->DefaultFontSize.X = (SHORT)(tm.tmMaxCharWidth);
|
|
pcpd->DefaultFontSize.Y = (SHORT)(tm.tmHeight+tm.tmExternalLeading);
|
|
pcpd->DefaultFontFamily = tm.tmPitchAndFamily;
|
|
|
|
if (IS_ANY_DBCS_CHARSET(tm.tmCharSet))
|
|
pcpd->DefaultFontSize.X /= 2;
|
|
}
|
|
|
|
if (pcpd->gbEnumerateFaces) {
|
|
/*
|
|
* Set the EF_OLD bit and clear the EF_NEW bit
|
|
* for all previously available faces
|
|
*/
|
|
for (pFN = pcpd->gpFaceNames; pFN; pFN = pFN->pNext) {
|
|
pFN->dwFlag |= EF_OLD;
|
|
pFN->dwFlag &= ~EF_NEW;
|
|
}
|
|
|
|
//
|
|
// Use DoFontEnum to get the names of all the suitable Faces
|
|
// All facenames found will be put in gpFaceNames with
|
|
// the EF_NEW bit set.
|
|
//
|
|
DoFontEnum(pcpd, hDC, NULL, TTPoints, 1);
|
|
pcpd->gbEnumerateFaces = FALSE;
|
|
}
|
|
|
|
// Use DoFontEnum to get all fonts from the system. Our FontEnum
|
|
// proc puts just the ones we want into an array
|
|
//
|
|
for (pFN = pcpd->gpFaceNames; pFN; pFN = pFN->pNext) {
|
|
|
|
if ((pFN->dwFlag & (EF_OLD|EF_NEW)) == EF_OLD) {
|
|
// The face is no longer available
|
|
RemoveFace(pcpd, pFN->atch);
|
|
pFN->dwFlag &= ~EF_ENUMERATED;
|
|
continue;
|
|
}
|
|
if ((pFN->dwFlag & dwFontType) == 0) {
|
|
// not the kind of face we want
|
|
continue;
|
|
}
|
|
if (pFN->dwFlag & EF_ENUMERATED) {
|
|
// we already enumerated this face
|
|
continue;
|
|
}
|
|
|
|
if (pFN->dwFlag & EF_TTFONT) {
|
|
if (IsFarEastCP(pcpd->uOEMCP) && !IsAvailableTTFontCP(pcpd, pFN->atch, 0))
|
|
DoFontEnum(pcpd, hDC, pFN->atch, TTPointsDbcs, NELEM(TTPointsDbcs));
|
|
else
|
|
DoFontEnum(pcpd, hDC, pFN->atch, TTPoints, NELEM(TTPoints));
|
|
} else {
|
|
DoFontEnum(pcpd, hDC, pFN->atch, NULL, 0);
|
|
|
|
// If we find that the face just enumerated is the same as OEM,
|
|
// reset flag so we don't try to enumerate it again.
|
|
|
|
if (lstrcmpi(pFN->atch, pcpd->DefaultFaceName) == 0)
|
|
{
|
|
bEnumOEMFace = FALSE;
|
|
}
|
|
}
|
|
pFN->dwFlag |= EF_ENUMERATED;
|
|
}
|
|
|
|
|
|
// After enumerating fonts, restore the font enumeration filter.
|
|
SetFontEnumeration(ulOldEnumFilter);
|
|
|
|
DeleteDC(hDC);
|
|
|
|
for (FontIndex = 0; FontIndex < pcpd->NumberOfFonts; FontIndex++) {
|
|
if (pcpd->FontInfo[FontIndex].Size.X == pcpd->DefaultFontSize.X &&
|
|
pcpd->FontInfo[FontIndex].Size.Y == pcpd->DefaultFontSize.Y &&
|
|
pcpd->FontInfo[FontIndex].Family == pcpd->DefaultFontFamily) {
|
|
break;
|
|
}
|
|
}
|
|
ASSERT(FontIndex < pcpd->NumberOfFonts);
|
|
if (FontIndex < pcpd->NumberOfFonts) {
|
|
pcpd->DefaultFontIndex = FontIndex;
|
|
} else {
|
|
pcpd->DefaultFontIndex = 0;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* Get the font index for a new font
|
|
* If necessary, attempt to create the font.
|
|
* Always return a valid FontIndex (even if not correct)
|
|
* Family: Find/Create a font with of this Family
|
|
* 0 - don't care
|
|
* ptszFace: Find/Create a font with this face name.
|
|
* NULL or TEXT("") - use DefaultFaceName
|
|
* Size: Must match SizeWant or actual Size.
|
|
*/
|
|
int
|
|
FindCreateFont(
|
|
CONSOLEPROP_DATA *pcpd,
|
|
DWORD Family,
|
|
LPTSTR ptszFace,
|
|
COORD Size,
|
|
LONG Weight)
|
|
{
|
|
#define NOT_CREATED_NOR_FOUND -1
|
|
#define CREATED_BUT_NOT_FOUND -2
|
|
|
|
int i;
|
|
int FontIndex = NOT_CREATED_NOR_FOUND;
|
|
BOOL bFontOK;
|
|
TCHAR AltFaceName[LF_FACESIZE];
|
|
COORD AltFontSize;
|
|
BYTE AltFontFamily;
|
|
ULONG AltFontIndex = 0;
|
|
LPTSTR ptszAltFace = NULL;
|
|
UINT uCurrentCP = pcpd->lpFEConsole->uCodePage;
|
|
UINT uDefaultCP = pcpd->uOEMCP;
|
|
|
|
BYTE CharSet = CodePageToCharSet(uCurrentCP);
|
|
|
|
if (!IsFarEastCP(uDefaultCP) || IS_ANY_DBCS_CHARSET(CharSet))
|
|
{
|
|
if (ptszFace == NULL || *ptszFace == TEXT('\0')) {
|
|
ptszFace = pcpd->DefaultFaceName;
|
|
}
|
|
if (Size.Y == 0) {
|
|
Size = pcpd->DefaultFontSize;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
MakeAltRasterFont(pcpd, uCurrentCP, &AltFontSize, &AltFontFamily, &AltFontIndex, AltFaceName, ARRAYSIZE(AltFaceName));
|
|
|
|
if (ptszFace == NULL || *ptszFace == L'\0') {
|
|
ptszFace = AltFaceName;
|
|
}
|
|
if (Size.Y == 0) {
|
|
Size.X = AltFontSize.X;
|
|
Size.Y = AltFontSize.Y;
|
|
}
|
|
}
|
|
|
|
if (IsAvailableTTFont(pcpd, ptszFace)) {
|
|
ptszAltFace = GetAltFaceName(pcpd, ptszFace);
|
|
}
|
|
else {
|
|
ptszAltFace = ptszFace;
|
|
}
|
|
|
|
/*
|
|
* Try to find the exact font
|
|
*/
|
|
TryFindExactFont:
|
|
for (i=0; i < (int)pcpd->NumberOfFonts; i++) {
|
|
/*
|
|
* If looking for a particular Family, skip non-matches
|
|
*/
|
|
if ((Family != 0) &&
|
|
((BYTE)Family != pcpd->FontInfo[i].Family)) {
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Skip non-matching sizes
|
|
*/
|
|
if ((!SIZE_EQUAL(pcpd->FontInfo[i].SizeWant, Size) &&
|
|
!SIZE_EQUAL(pcpd->FontInfo[i].Size, Size))) {
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Skip non-matching weights
|
|
*/
|
|
if ((Weight != 0) && (Weight != pcpd->FontInfo[i].Weight)) {
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Skip fonts that have unmatched charset
|
|
*/
|
|
if (!TM_IS_TT_FONT(pcpd->FontInfo[i].Family) &&
|
|
pcpd->FontInfo[i].tmCharSet != CharSet) {
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Size (and maybe Family) match.
|
|
* If we don't care about the name, or if it matches, use this font.
|
|
* Else if name doesn't match and it is a raster font, consider it.
|
|
*/
|
|
if ((ptszFace == NULL) || (ptszFace[0] == TEXT('\0')) ||
|
|
(lstrcmp(pcpd->FontInfo[i].FaceName, ptszFace) == 0) ||
|
|
(lstrcmp(pcpd->FontInfo[i].FaceName, ptszAltFace) == 0) ) {
|
|
FontIndex = i;
|
|
goto FoundFont;
|
|
} else if (!TM_IS_TT_FONT(pcpd->FontInfo[i].Family)) {
|
|
FontIndex = i;
|
|
}
|
|
}
|
|
|
|
if (FontIndex == NOT_CREATED_NOR_FOUND) {
|
|
/*
|
|
* Didn't find the exact font, so try to create it
|
|
*/
|
|
ULONG ulOldEnumFilter;
|
|
ulOldEnumFilter = SetFontEnumeration(FE_FILTER_NONE);
|
|
if (Size.Y < 0) {
|
|
Size.Y = -Size.Y;
|
|
}
|
|
bFontOK = DoFontEnum(pcpd, NULL, ptszFace, &Size.Y, 1);
|
|
SetFontEnumeration(ulOldEnumFilter);
|
|
if (bFontOK) {
|
|
FontIndex = CREATED_BUT_NOT_FOUND;
|
|
goto TryFindExactFont;
|
|
} else {
|
|
}
|
|
} else if (FontIndex >= 0) {
|
|
// a close Raster Font fit - only the name doesn't match.
|
|
goto FoundFont;
|
|
}
|
|
|
|
/*
|
|
* Failed to find exact match, even after enumeration, so now try
|
|
* to find a font of same family and same size or bigger
|
|
*/
|
|
for (i=0; i < (int)pcpd->NumberOfFonts; i++) {
|
|
|
|
if ((Family != 0) &&
|
|
((BYTE)Family != pcpd->FontInfo[i].Family)) {
|
|
continue;
|
|
}
|
|
|
|
if (!TM_IS_TT_FONT(pcpd->FontInfo[i].Family) &&
|
|
pcpd->FontInfo[i].tmCharSet != CharSet) {
|
|
continue;
|
|
}
|
|
|
|
if (pcpd->FontInfo[i].Size.Y >= Size.Y &&
|
|
pcpd->FontInfo[i].Size.X >= Size.X) {
|
|
// Same family, size >= desired.
|
|
FontIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (FontIndex < 0) {
|
|
if (uCurrentCP == uDefaultCP)
|
|
{
|
|
FontIndex = pcpd->DefaultFontIndex;
|
|
}
|
|
else
|
|
{
|
|
FontIndex = AltFontIndex;
|
|
}
|
|
}
|
|
|
|
FoundFont:
|
|
return FontIndex;
|
|
|
|
#undef NOT_CREATED_NOR_FOUND
|
|
#undef CREATED_BUT_NOT_FOUND
|
|
}
|
|
|
|
|
|
LPTSTR
|
|
TranslateConsoleTitle(
|
|
LPTSTR ConsoleTitle
|
|
)
|
|
/*++
|
|
|
|
this routine translates path characters into '_' characters because
|
|
the NT registry apis do not allow the creation of keys with
|
|
names that contain path characters. it allocates a buffer that
|
|
must be freed.
|
|
|
|
--*/
|
|
{
|
|
int ConsoleTitleLength, i;
|
|
LPTSTR TranslatedTitle;
|
|
|
|
ConsoleTitleLength = lstrlen(ConsoleTitle) + 1;
|
|
TranslatedTitle = LocalAlloc(LPTR,
|
|
ConsoleTitleLength * sizeof(TCHAR));
|
|
if (TranslatedTitle == NULL) {
|
|
return NULL;
|
|
}
|
|
for (i = 0; i < ConsoleTitleLength; i++) {
|
|
if (ConsoleTitle[i] == TEXT('\\')) {
|
|
TranslatedTitle[i] = TEXT('_');
|
|
} else {
|
|
TranslatedTitle[i] = ConsoleTitle[i];
|
|
}
|
|
}
|
|
return TranslatedTitle;
|
|
}
|
|
|
|
|
|
|
|
void
|
|
InitRegistryValues( CONSOLEPROP_DATA *pcpd )
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine allocates a state info structure and fill it in with
|
|
default values. It then tries to load the default settings for
|
|
console from the registry.
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return Value:
|
|
|
|
pStateInfo - pointer to structure to receive information
|
|
|
|
--*/
|
|
|
|
{
|
|
TCHAR chSave;
|
|
|
|
|
|
pcpd->lpConsole->wFillAttribute = 0x07; // white on black
|
|
pcpd->lpConsole->wPopupFillAttribute = 0xf5; // purple on white
|
|
pcpd->lpConsole->bInsertMode = FALSE;
|
|
pcpd->lpConsole->bQuickEdit = FALSE;
|
|
pcpd->lpConsole->bFullScreen = FALSE;
|
|
pcpd->lpConsole->dwScreenBufferSize.X = 80;
|
|
pcpd->lpConsole->dwScreenBufferSize.Y = 25;
|
|
pcpd->lpConsole->dwWindowSize.X = 80;
|
|
pcpd->lpConsole->dwWindowSize.Y = 25;
|
|
pcpd->lpConsole->dwWindowOrigin.X = 0;
|
|
pcpd->lpConsole->dwWindowOrigin.Y = 0;
|
|
pcpd->lpConsole->bAutoPosition = TRUE;
|
|
pcpd->lpConsole->dwFontSize.X = 0;
|
|
pcpd->lpConsole->dwFontSize.Y = 0;
|
|
pcpd->lpConsole->uFontFamily = 0;
|
|
pcpd->lpConsole->uFontWeight = 0;
|
|
#ifdef UNICODE
|
|
FillMemory( pcpd->lpConsole->FaceName, sizeof(pcpd->lpConsole->FaceName), 0 );
|
|
pcpd->lpFaceName = (LPTSTR)pcpd->lpConsole->FaceName;
|
|
#else
|
|
FillMemory( pcpd->szFaceName, sizeof(pcpd->szFaceName), 0 );
|
|
pcpd->lpFaceName = pcpd->szFaceName;
|
|
#endif
|
|
pcpd->lpConsole->uCursorSize = 25;
|
|
pcpd->lpConsole->uHistoryBufferSize = 25;
|
|
pcpd->lpConsole->uNumberOfHistoryBuffers = 4;
|
|
pcpd->lpConsole->bHistoryNoDup = 0;
|
|
pcpd->lpConsole->ColorTable[ 0] = RGB(0, 0, 0 );
|
|
pcpd->lpConsole->ColorTable[ 1] = RGB(0, 0, 0x80);
|
|
pcpd->lpConsole->ColorTable[ 2] = RGB(0, 0x80,0 );
|
|
pcpd->lpConsole->ColorTable[ 3] = RGB(0, 0x80,0x80);
|
|
pcpd->lpConsole->ColorTable[ 4] = RGB(0x80,0, 0 );
|
|
pcpd->lpConsole->ColorTable[ 5] = RGB(0x80,0, 0x80);
|
|
pcpd->lpConsole->ColorTable[ 6] = RGB(0x80,0x80,0 );
|
|
pcpd->lpConsole->ColorTable[ 7] = RGB(0xC0,0xC0,0xC0);
|
|
pcpd->lpConsole->ColorTable[ 8] = RGB(0x80,0x80,0x80);
|
|
pcpd->lpConsole->ColorTable[ 9] = RGB(0, 0, 0xFF);
|
|
pcpd->lpConsole->ColorTable[10] = RGB(0, 0xFF,0 );
|
|
pcpd->lpConsole->ColorTable[11] = RGB(0, 0xFF,0xFF);
|
|
pcpd->lpConsole->ColorTable[12] = RGB(0xFF,0, 0 );
|
|
pcpd->lpConsole->ColorTable[13] = RGB(0xFF,0, 0xFF);
|
|
pcpd->lpConsole->ColorTable[14] = RGB(0xFF,0xFF,0 );
|
|
pcpd->lpConsole->ColorTable[15] = RGB(0xFF,0xFF,0xFF);
|
|
pcpd->lpFEConsole->uCodePage = pcpd->uOEMCP;
|
|
|
|
// make console title NULL so we load the default settings for the console
|
|
chSave = pcpd->ConsoleTitle[0];
|
|
pcpd->ConsoleTitle[0] = TEXT('\0');
|
|
GetRegistryValues( pcpd );
|
|
|
|
// restore the console title
|
|
pcpd->ConsoleTitle[0] = chSave;
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
GetTitleFromLinkName(
|
|
LPTSTR szLinkName,
|
|
LPTSTR szTitle,
|
|
UINT cchTitle
|
|
)
|
|
{
|
|
LPTSTR pLnk, pDot;
|
|
LPTSTR pPath = szLinkName;
|
|
HRESULT hr;
|
|
|
|
// Error checking
|
|
if (!szTitle)
|
|
return;
|
|
|
|
if (!szLinkName)
|
|
{
|
|
szTitle[0] = TEXT('\0');
|
|
return;
|
|
}
|
|
|
|
|
|
// find filename at end of fully qualified link name and point pLnk to it
|
|
for (pLnk = pPath; *pPath; pPath++)
|
|
{
|
|
if ( (pPath[0] == TEXT('\\') || pPath[0] == TEXT(':')) &&
|
|
pPath[1] &&
|
|
(pPath[1] != TEXT('\\'))
|
|
)
|
|
pLnk = pPath + 1;
|
|
}
|
|
|
|
// find extension (.lnk)
|
|
pPath = pLnk;
|
|
for (pDot = NULL; *pPath; pPath++)
|
|
{
|
|
switch (*pPath) {
|
|
case TEXT('.'):
|
|
pDot = pPath; // remember the last dot
|
|
break;
|
|
case TEXT('\\'):
|
|
case TEXT(' '): // extensions can't have spaces
|
|
pDot = NULL; // forget last dot, it was in a directory
|
|
break;
|
|
}
|
|
}
|
|
|
|
// if we found the extension, pDot points to it, if not, pDot
|
|
// is NULL.
|
|
|
|
if (pDot)
|
|
{
|
|
hr = StringCchCopyN(szTitle, cchTitle, pLnk, pDot - pLnk);
|
|
}
|
|
else
|
|
{
|
|
hr = StringCchCopy(szTitle, cchTitle, pLnk);
|
|
}
|
|
if (FAILED(hr))
|
|
{
|
|
szTitle[0] = TEXT('\0');
|
|
}
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
GetRegistryValues(
|
|
CONSOLEPROP_DATA *pcpd
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine reads in values from the registry and places them
|
|
in the supplied structure.
|
|
|
|
Arguments:
|
|
|
|
pStateInfo - optional pointer to structure to receive information
|
|
|
|
Return Value:
|
|
|
|
current page number
|
|
|
|
--*/
|
|
|
|
{
|
|
HKEY hConsoleKey;
|
|
HKEY hTitleKey;
|
|
LPTSTR TranslatedTitle;
|
|
DWORD dwValue, dwSize;
|
|
DWORD dwRet = 0;
|
|
DWORD i;
|
|
WCHAR awchBuffer[LF_FACESIZE];
|
|
|
|
//
|
|
// Open the console registry key
|
|
//
|
|
if (ERROR_SUCCESS != RegOpenKeyEx(HKEY_CURRENT_USER, CONSOLE_REGISTRY_STRING,
|
|
0, KEY_QUERY_VALUE, &hConsoleKey))
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// If there is no structure to fill out, just bail out
|
|
//
|
|
|
|
if ((!pcpd) || (!pcpd->lpConsole))
|
|
goto CloseKey;
|
|
|
|
//
|
|
// Open the console title subkey, if there is one
|
|
//
|
|
|
|
if (pcpd->ConsoleTitle[0] != TEXT('\0'))
|
|
{
|
|
TranslatedTitle = TranslateConsoleTitle(pcpd->ConsoleTitle);
|
|
if (TranslatedTitle == NULL)
|
|
goto GetDefaultConsole;
|
|
dwValue = RegOpenKeyEx( hConsoleKey, TranslatedTitle,
|
|
0, KEY_QUERY_VALUE,
|
|
&hTitleKey);
|
|
LocalFree(TranslatedTitle);
|
|
if (dwValue!=ERROR_SUCCESS)
|
|
goto GetDefaultConsole;
|
|
} else {
|
|
|
|
GetDefaultConsole:
|
|
hTitleKey = hConsoleKey;
|
|
}
|
|
|
|
//
|
|
// Initial screen fill
|
|
//
|
|
|
|
dwSize = sizeof(dwValue);
|
|
if (SHQueryValueEx( hTitleKey,
|
|
CONSOLE_REGISTRY_FILLATTR,
|
|
NULL,
|
|
NULL,
|
|
(LPBYTE)&dwValue,
|
|
&dwSize
|
|
) == ERROR_SUCCESS)
|
|
{
|
|
pcpd->lpConsole->wFillAttribute = (WORD)dwValue;
|
|
}
|
|
|
|
//
|
|
// Initial popup fill
|
|
//
|
|
|
|
dwSize = sizeof(dwValue);
|
|
if (SHQueryValueEx( hTitleKey,
|
|
CONSOLE_REGISTRY_POPUPATTR,
|
|
NULL,
|
|
NULL,
|
|
(LPBYTE)&dwValue,
|
|
&dwSize
|
|
) == ERROR_SUCCESS)
|
|
{
|
|
pcpd->lpConsole->wPopupFillAttribute = (WORD)dwValue;
|
|
}
|
|
|
|
//
|
|
// Initial color table
|
|
//
|
|
|
|
for (i = 0; i < 16; i++)
|
|
{
|
|
StringCchPrintf(awchBuffer, ARRAYSIZE(awchBuffer), CONSOLE_REGISTRY_COLORTABLE, i); // ok to truncate - read will just fail
|
|
dwSize = sizeof(dwValue);
|
|
if (SHQueryValueEx( hTitleKey,
|
|
(LPTSTR)awchBuffer,
|
|
NULL,
|
|
NULL,
|
|
(LPBYTE)&dwValue,
|
|
&dwSize
|
|
) == ERROR_SUCCESS)
|
|
{
|
|
pcpd->lpConsole->ColorTable[i] = dwValue;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Initial insert mode
|
|
//
|
|
|
|
dwSize = sizeof(dwValue);
|
|
if (SHQueryValueEx( hTitleKey,
|
|
CONSOLE_REGISTRY_INSERTMODE,
|
|
NULL,
|
|
NULL,
|
|
(LPBYTE)&dwValue,
|
|
&dwSize
|
|
) == ERROR_SUCCESS)
|
|
{
|
|
pcpd->lpConsole->bInsertMode = !!dwValue;
|
|
}
|
|
|
|
//
|
|
// Initial quick edit mode
|
|
//
|
|
|
|
dwSize = sizeof(dwValue);
|
|
if (SHQueryValueEx( hTitleKey,
|
|
CONSOLE_REGISTRY_QUICKEDIT,
|
|
NULL,
|
|
NULL,
|
|
(LPBYTE)&dwValue,
|
|
&dwSize
|
|
) == ERROR_SUCCESS)
|
|
{
|
|
pcpd->lpConsole->bQuickEdit = !!dwValue;
|
|
}
|
|
|
|
#ifdef i386
|
|
//
|
|
// Initial full screen mode
|
|
//
|
|
|
|
dwSize = sizeof(dwValue);
|
|
if (SHQueryValueEx( hTitleKey,
|
|
CONSOLE_REGISTRY_FULLSCR,
|
|
NULL,
|
|
NULL,
|
|
(LPBYTE)&dwValue,
|
|
&dwSize
|
|
) == ERROR_SUCCESS)
|
|
{
|
|
pcpd->lpConsole->bFullScreen = !!dwValue;
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Initial code page
|
|
//
|
|
|
|
dwSize = sizeof(dwValue);
|
|
if (SHQueryValueEx( hTitleKey,
|
|
CONSOLE_REGISTRY_CODEPAGE,
|
|
NULL,
|
|
NULL,
|
|
(LPBYTE)&dwValue,
|
|
&dwSize
|
|
) == ERROR_SUCCESS)
|
|
{
|
|
pcpd->lpFEConsole->uCodePage = (UINT)dwValue;
|
|
}
|
|
|
|
//
|
|
// Initial screen buffer size
|
|
//
|
|
|
|
dwSize = sizeof(dwValue);
|
|
if (SHQueryValueEx( hTitleKey,
|
|
CONSOLE_REGISTRY_BUFFERSIZE,
|
|
NULL,
|
|
NULL,
|
|
(LPBYTE)&dwValue,
|
|
&dwSize
|
|
) == ERROR_SUCCESS)
|
|
{
|
|
pcpd->lpConsole->dwScreenBufferSize.X = LOWORD(dwValue);
|
|
pcpd->lpConsole->dwScreenBufferSize.Y = HIWORD(dwValue);
|
|
}
|
|
|
|
//
|
|
// Initial window size
|
|
//
|
|
|
|
dwSize = sizeof(dwValue);
|
|
if (SHQueryValueEx( hTitleKey,
|
|
CONSOLE_REGISTRY_WINDOWSIZE,
|
|
NULL,
|
|
NULL,
|
|
(LPBYTE)&dwValue,
|
|
&dwSize
|
|
) == ERROR_SUCCESS)
|
|
{
|
|
pcpd->lpConsole->dwWindowSize.X = LOWORD(dwValue);
|
|
pcpd->lpConsole->dwWindowSize.Y = HIWORD(dwValue);
|
|
}
|
|
|
|
//
|
|
// Initial window position
|
|
//
|
|
|
|
dwSize = sizeof(dwValue);
|
|
if (SHQueryValueEx( hTitleKey,
|
|
CONSOLE_REGISTRY_WINDOWPOS,
|
|
NULL,
|
|
NULL,
|
|
(LPBYTE)&dwValue,
|
|
&dwSize
|
|
) == ERROR_SUCCESS)
|
|
{
|
|
pcpd->lpConsole->dwWindowOrigin.X = (SHORT)LOWORD(dwValue);
|
|
pcpd->lpConsole->dwWindowOrigin.Y = (SHORT)HIWORD(dwValue);
|
|
pcpd->lpConsole->bAutoPosition = FALSE;
|
|
}
|
|
|
|
//
|
|
// Initial font size
|
|
//
|
|
|
|
dwSize = sizeof(dwValue);
|
|
if (SHQueryValueEx( hTitleKey,
|
|
CONSOLE_REGISTRY_FONTSIZE,
|
|
NULL,
|
|
NULL,
|
|
(LPBYTE)&dwValue,
|
|
&dwSize
|
|
) == ERROR_SUCCESS)
|
|
{
|
|
pcpd->lpConsole->dwFontSize.X = LOWORD(dwValue);
|
|
pcpd->lpConsole->dwFontSize.Y = HIWORD(dwValue);
|
|
}
|
|
|
|
//
|
|
// Initial font family
|
|
//
|
|
|
|
dwSize = sizeof(dwValue);
|
|
if (SHQueryValueEx( hTitleKey,
|
|
CONSOLE_REGISTRY_FONTFAMILY,
|
|
NULL,
|
|
NULL,
|
|
(LPBYTE)&dwValue,
|
|
&dwSize
|
|
) == ERROR_SUCCESS)
|
|
{
|
|
pcpd->lpConsole->uFontFamily = dwValue;
|
|
}
|
|
|
|
//
|
|
// Initial font weight
|
|
//
|
|
|
|
dwSize = sizeof(dwValue);
|
|
if (SHQueryValueEx( hTitleKey,
|
|
CONSOLE_REGISTRY_FONTWEIGHT,
|
|
NULL,
|
|
NULL,
|
|
(LPBYTE)&dwValue,
|
|
&dwSize
|
|
) == ERROR_SUCCESS)
|
|
{
|
|
pcpd->lpConsole->uFontWeight = dwValue;
|
|
}
|
|
|
|
//
|
|
// Initial font face name
|
|
//
|
|
|
|
dwSize = sizeof(awchBuffer);
|
|
if (SHQueryValueEx( hTitleKey,
|
|
CONSOLE_REGISTRY_FACENAME,
|
|
NULL,
|
|
NULL,
|
|
(LPBYTE)awchBuffer,
|
|
&dwSize
|
|
) == ERROR_SUCCESS)
|
|
{
|
|
CopyMemory((LPBYTE)pcpd->lpFaceName, (LPBYTE)awchBuffer, LF_FACESIZE*sizeof(TCHAR));
|
|
pcpd->lpFaceName[ARRAYSIZE(pcpd->lpFaceName)-1] = TEXT('\0');
|
|
}
|
|
|
|
//
|
|
// Initial cursor size
|
|
//
|
|
|
|
dwSize = sizeof(dwValue);
|
|
if (SHQueryValueEx( hTitleKey,
|
|
CONSOLE_REGISTRY_CURSORSIZE,
|
|
NULL,
|
|
NULL,
|
|
(LPBYTE)&dwValue,
|
|
&dwSize
|
|
) == ERROR_SUCCESS)
|
|
{
|
|
pcpd->lpConsole->uCursorSize = dwValue;
|
|
}
|
|
|
|
//
|
|
// Initial history buffer size
|
|
//
|
|
|
|
dwSize = sizeof(dwValue);
|
|
if (SHQueryValueEx( hTitleKey,
|
|
CONSOLE_REGISTRY_HISTORYSIZE,
|
|
NULL,
|
|
NULL,
|
|
(LPBYTE)&dwValue,
|
|
&dwSize
|
|
) == ERROR_SUCCESS)
|
|
{
|
|
pcpd->lpConsole->uHistoryBufferSize = dwValue;
|
|
}
|
|
|
|
//
|
|
// Initial number of history buffers
|
|
//
|
|
|
|
dwSize = sizeof(dwValue);
|
|
if (SHQueryValueEx( hTitleKey,
|
|
CONSOLE_REGISTRY_HISTORYBUFS,
|
|
NULL,
|
|
NULL,
|
|
(LPBYTE)&dwValue,
|
|
&dwSize
|
|
) == ERROR_SUCCESS)
|
|
{
|
|
pcpd->lpConsole->uNumberOfHistoryBuffers = dwValue;
|
|
}
|
|
|
|
//
|
|
// Initial history duplication mode
|
|
//
|
|
|
|
dwSize = sizeof(dwValue);
|
|
if (SHQueryValueEx( hTitleKey,
|
|
CONSOLE_REGISTRY_HISTORYNODUP,
|
|
NULL,
|
|
NULL,
|
|
(LPBYTE)&dwValue,
|
|
&dwSize
|
|
) == ERROR_SUCCESS)
|
|
{
|
|
pcpd->lpConsole->bHistoryNoDup = dwValue;
|
|
}
|
|
|
|
//
|
|
// Close the registry keys
|
|
//
|
|
|
|
if (hTitleKey != hConsoleKey) {
|
|
RegCloseKey(hTitleKey);
|
|
}
|
|
|
|
CloseKey:
|
|
RegCloseKey(hConsoleKey);
|
|
}
|
|
|
|
|
|
VOID
|
|
SetRegistryValues(
|
|
CONSOLEPROP_DATA *pcpd
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine writes values to the registry from the supplied
|
|
structure.
|
|
|
|
Arguments:
|
|
|
|
pStateInfo - optional pointer to structure containing information
|
|
dwPage - current page number
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
|
|
{
|
|
HKEY hConsoleKey;
|
|
HKEY hTitleKey;
|
|
LPTSTR TranslatedTitle;
|
|
DWORD dwValue;
|
|
DWORD i;
|
|
WCHAR awchBuffer[LF_FACESIZE];
|
|
|
|
//
|
|
// Open the console registry key
|
|
//
|
|
if (ERROR_SUCCESS != RegCreateKeyEx(HKEY_CURRENT_USER, CONSOLE_REGISTRY_STRING,
|
|
0, NULL, 0, KEY_SET_VALUE, NULL,
|
|
&hConsoleKey, NULL))
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// If we only want to save the current page, bail out
|
|
//
|
|
|
|
if (pcpd == NULL)
|
|
{
|
|
goto CloseKey;
|
|
}
|
|
|
|
//
|
|
// Open the console title subkey, if there is one
|
|
//
|
|
|
|
if (pcpd->ConsoleTitle[0] != TEXT('\0'))
|
|
{
|
|
TranslatedTitle = TranslateConsoleTitle(pcpd->ConsoleTitle);
|
|
if (TranslatedTitle == NULL)
|
|
{
|
|
RegCloseKey(hConsoleKey);
|
|
return;
|
|
}
|
|
dwValue = RegCreateKeyEx( hConsoleKey, TranslatedTitle,
|
|
0, NULL, 0, KEY_SET_VALUE, NULL,
|
|
&hTitleKey, NULL);
|
|
LocalFree(TranslatedTitle);
|
|
if (dwValue!=ERROR_SUCCESS)
|
|
{
|
|
RegCloseKey(hConsoleKey);
|
|
return;
|
|
}
|
|
} else {
|
|
hTitleKey = hConsoleKey;
|
|
}
|
|
|
|
//
|
|
// Save screen and popup colors and color table
|
|
//
|
|
|
|
dwValue = pcpd->lpConsole->wFillAttribute;
|
|
RegSetValueEx( hTitleKey,
|
|
CONSOLE_REGISTRY_FILLATTR,
|
|
0,
|
|
REG_DWORD,
|
|
(LPBYTE)&dwValue,
|
|
sizeof(dwValue)
|
|
);
|
|
dwValue = pcpd->lpConsole->wPopupFillAttribute;
|
|
RegSetValueEx( hTitleKey,
|
|
CONSOLE_REGISTRY_POPUPATTR,
|
|
0,
|
|
REG_DWORD,
|
|
(LPBYTE)&dwValue,
|
|
sizeof(dwValue)
|
|
);
|
|
for (i = 0; i < 16; i++)
|
|
{
|
|
dwValue = pcpd->lpConsole->ColorTable[i];
|
|
StringCchPrintf(awchBuffer, ARRAYSIZE(awchBuffer), CONSOLE_REGISTRY_COLORTABLE, i); // truncation will never happen
|
|
RegSetValueEx( hTitleKey,
|
|
(LPTSTR)awchBuffer,
|
|
0,
|
|
REG_DWORD,
|
|
(LPBYTE)&dwValue,
|
|
sizeof(dwValue)
|
|
);
|
|
}
|
|
|
|
//
|
|
// Save insert, quickedit, and fullscreen mode settings
|
|
//
|
|
|
|
dwValue = pcpd->lpConsole->bInsertMode;
|
|
RegSetValueEx( hTitleKey,
|
|
CONSOLE_REGISTRY_INSERTMODE,
|
|
0,
|
|
REG_DWORD,
|
|
(LPBYTE)&dwValue,
|
|
sizeof(dwValue)
|
|
);
|
|
dwValue = pcpd->lpConsole->bQuickEdit;
|
|
RegSetValueEx( hTitleKey,
|
|
CONSOLE_REGISTRY_QUICKEDIT,
|
|
0,
|
|
REG_DWORD,
|
|
(LPBYTE)&dwValue,
|
|
sizeof(dwValue)
|
|
);
|
|
#ifdef i386
|
|
dwValue = pcpd->lpConsole->bFullScreen;
|
|
RegSetValueEx( hTitleKey,
|
|
CONSOLE_REGISTRY_FULLSCR,
|
|
0,
|
|
REG_DWORD,
|
|
(LPBYTE)&dwValue,
|
|
sizeof(dwValue)
|
|
);
|
|
#endif
|
|
|
|
//
|
|
// Save screen buffer size
|
|
//
|
|
|
|
dwValue = MAKELONG(pcpd->lpConsole->dwScreenBufferSize.X,
|
|
pcpd->lpConsole->dwScreenBufferSize.Y);
|
|
RegSetValueEx( hTitleKey,
|
|
CONSOLE_REGISTRY_BUFFERSIZE,
|
|
0,
|
|
REG_DWORD,
|
|
(LPBYTE)&dwValue,
|
|
sizeof(dwValue)
|
|
);
|
|
|
|
//
|
|
// Save window size
|
|
//
|
|
|
|
dwValue = MAKELONG(pcpd->lpConsole->dwWindowSize.X,
|
|
pcpd->lpConsole->dwWindowSize.Y);
|
|
RegSetValueEx( hTitleKey,
|
|
CONSOLE_REGISTRY_WINDOWSIZE,
|
|
0,
|
|
REG_DWORD,
|
|
(LPBYTE)&dwValue,
|
|
sizeof(dwValue)
|
|
);
|
|
|
|
//
|
|
// Save window position
|
|
//
|
|
|
|
if (pcpd->lpConsole->bAutoPosition) {
|
|
RegDeleteKey(hTitleKey, CONSOLE_REGISTRY_WINDOWPOS);
|
|
} else {
|
|
dwValue = MAKELONG(pcpd->lpConsole->dwWindowOrigin.X,
|
|
pcpd->lpConsole->dwWindowOrigin.Y);
|
|
RegSetValueEx( hTitleKey,
|
|
CONSOLE_REGISTRY_WINDOWPOS,
|
|
0,
|
|
REG_DWORD,
|
|
(LPBYTE)&dwValue,
|
|
sizeof(dwValue)
|
|
);
|
|
}
|
|
|
|
//
|
|
// Save font size, family, weight, and face name
|
|
//
|
|
|
|
dwValue = MAKELONG(pcpd->lpConsole->dwFontSize.X,
|
|
pcpd->lpConsole->dwFontSize.Y);
|
|
RegSetValueEx( hTitleKey,
|
|
CONSOLE_REGISTRY_FONTSIZE,
|
|
0,
|
|
REG_DWORD,
|
|
(LPBYTE)&dwValue,
|
|
sizeof(dwValue)
|
|
);
|
|
dwValue = pcpd->lpConsole->uFontFamily;
|
|
RegSetValueEx( hTitleKey,
|
|
CONSOLE_REGISTRY_FONTFAMILY,
|
|
0,
|
|
REG_DWORD,
|
|
(LPBYTE)&dwValue,
|
|
sizeof(dwValue)
|
|
);
|
|
dwValue = pcpd->lpConsole->uFontWeight;
|
|
RegSetValueEx( hTitleKey,
|
|
CONSOLE_REGISTRY_FONTWEIGHT,
|
|
0,
|
|
REG_DWORD,
|
|
(LPBYTE)&dwValue,
|
|
sizeof(dwValue)
|
|
);
|
|
RegSetValueEx( hTitleKey,
|
|
CONSOLE_REGISTRY_FACENAME,
|
|
0,
|
|
REG_SZ,
|
|
(LPBYTE)pcpd->lpFaceName,
|
|
(lstrlen(pcpd->lpFaceName) + 1) * sizeof(TCHAR)
|
|
);
|
|
|
|
//
|
|
// Save cursor size
|
|
//
|
|
|
|
dwValue = pcpd->lpConsole->uCursorSize;
|
|
RegSetValueEx( hTitleKey,
|
|
CONSOLE_REGISTRY_CURSORSIZE,
|
|
0,
|
|
REG_DWORD,
|
|
(LPBYTE)&dwValue,
|
|
sizeof(dwValue)
|
|
);
|
|
|
|
//
|
|
// Save history buffer size and number
|
|
//
|
|
|
|
dwValue = pcpd->lpConsole->uHistoryBufferSize;
|
|
RegSetValueEx( hTitleKey,
|
|
CONSOLE_REGISTRY_HISTORYSIZE,
|
|
0,
|
|
REG_DWORD,
|
|
(LPBYTE)&dwValue,
|
|
sizeof(dwValue)
|
|
);
|
|
dwValue = pcpd->lpConsole->uNumberOfHistoryBuffers;
|
|
RegSetValueEx( hTitleKey,
|
|
CONSOLE_REGISTRY_HISTORYBUFS,
|
|
0,
|
|
REG_DWORD,
|
|
(LPBYTE)&dwValue,
|
|
sizeof(dwValue)
|
|
);
|
|
dwValue = pcpd->lpConsole->bHistoryNoDup;
|
|
RegSetValueEx( hTitleKey,
|
|
CONSOLE_REGISTRY_HISTORYNODUP,
|
|
0,
|
|
REG_DWORD,
|
|
(LPBYTE)&dwValue,
|
|
sizeof(dwValue)
|
|
);
|
|
|
|
//
|
|
// Close the registry keys
|
|
//
|
|
|
|
if (hTitleKey != hConsoleKey) {
|
|
RegCloseKey(hTitleKey);
|
|
}
|
|
|
|
CloseKey:
|
|
RegCloseKey(hConsoleKey);
|
|
}
|
|
|
|
void
|
|
InitFERegistryValues( CONSOLEPROP_DATA *pcpd )
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine allocates a state info structure and fill it in with
|
|
default values. It then tries to load the default settings for
|
|
console from the registry.
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return Value:
|
|
|
|
pStateInfo - pointer to structure to receive information
|
|
|
|
--*/
|
|
|
|
{
|
|
/*
|
|
* In this case: console reads a property of US version.
|
|
* It doesn't have code page information.
|
|
* Console should sets some code page as default.
|
|
* However, I don't know right value. 437 is temporary value.
|
|
*/
|
|
pcpd->lpFEConsole->uCodePage = 437;
|
|
|
|
GetFERegistryValues( pcpd );
|
|
}
|
|
|
|
|
|
VOID
|
|
GetFERegistryValues(
|
|
CONSOLEPROP_DATA *pcpd
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine reads in values from the registry and places them
|
|
in the supplied structure.
|
|
|
|
Arguments:
|
|
|
|
pStateInfo - optional pointer to structure to receive information
|
|
|
|
Return Value:
|
|
|
|
current page number
|
|
|
|
--*/
|
|
|
|
{
|
|
HKEY hConsoleKey;
|
|
HKEY hTitleKey;
|
|
LPTSTR TranslatedTitle;
|
|
DWORD dwValue, dwSize;
|
|
DWORD dwRet = 0;
|
|
|
|
//
|
|
// Open the console registry key
|
|
//
|
|
|
|
if (ERROR_SUCCESS != RegOpenKeyEx(HKEY_CURRENT_USER, CONSOLE_REGISTRY_STRING,
|
|
0, KEY_QUERY_VALUE, &hConsoleKey))
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// If there is no structure to fill out, just bail out
|
|
//
|
|
|
|
if ((!pcpd) || (!pcpd->lpFEConsole))
|
|
goto CloseKey;
|
|
|
|
//
|
|
// Open the console title subkey, if there is one
|
|
//
|
|
|
|
if (pcpd->ConsoleTitle[0] != TEXT('\0'))
|
|
{
|
|
TranslatedTitle = TranslateConsoleTitle(pcpd->ConsoleTitle);
|
|
if (TranslatedTitle == NULL)
|
|
goto CloseKey;
|
|
dwValue = RegOpenKeyEx( hConsoleKey, TranslatedTitle,
|
|
0, KEY_QUERY_VALUE,
|
|
&hTitleKey);
|
|
LocalFree(TranslatedTitle);
|
|
if (dwValue!=ERROR_SUCCESS)
|
|
goto CloseKey;
|
|
} else {
|
|
goto CloseKey;
|
|
}
|
|
|
|
//
|
|
// Initial code page
|
|
//
|
|
|
|
dwSize = sizeof(dwValue);
|
|
if (SHQueryValueEx( hTitleKey,
|
|
CONSOLE_REGISTRY_CODEPAGE,
|
|
NULL,
|
|
NULL,
|
|
(LPBYTE)&dwValue,
|
|
&dwSize
|
|
) == ERROR_SUCCESS)
|
|
{
|
|
pcpd->lpFEConsole->uCodePage = (UINT)dwValue;
|
|
}
|
|
|
|
//
|
|
// Close the registry keys
|
|
//
|
|
|
|
if (hTitleKey != hConsoleKey) {
|
|
RegCloseKey(hTitleKey);
|
|
}
|
|
|
|
CloseKey:
|
|
RegCloseKey(hConsoleKey);
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
SetFERegistryValues(
|
|
CONSOLEPROP_DATA *pcpd
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine writes values to the registry from the supplied
|
|
structure.
|
|
|
|
Arguments:
|
|
|
|
pStateInfo - optional pointer to structure containing information
|
|
dwPage - current page number
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
|
|
{
|
|
HKEY hConsoleKey;
|
|
HKEY hTitleKey;
|
|
LPTSTR TranslatedTitle;
|
|
DWORD dwValue;
|
|
|
|
//
|
|
// Open the console registry key
|
|
//
|
|
|
|
if (ERROR_SUCCESS != RegCreateKeyEx(HKEY_CURRENT_USER, CONSOLE_REGISTRY_STRING,
|
|
0, NULL, 0, KEY_SET_VALUE, NULL,
|
|
&hConsoleKey, NULL))
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// If we only want to save the current page, bail out
|
|
//
|
|
|
|
if (pcpd == NULL)
|
|
{
|
|
goto CloseKey;
|
|
}
|
|
|
|
//
|
|
// Open the console title subkey, if there is one
|
|
//
|
|
|
|
if (pcpd->ConsoleTitle[0] != TEXT('\0'))
|
|
{
|
|
TranslatedTitle = TranslateConsoleTitle(pcpd->ConsoleTitle);
|
|
if (TranslatedTitle == NULL)
|
|
{
|
|
RegCloseKey(hConsoleKey);
|
|
return;
|
|
}
|
|
dwValue = RegCreateKeyEx( hConsoleKey, TranslatedTitle,
|
|
0, NULL, 0, KEY_SET_VALUE, NULL,
|
|
&hTitleKey, NULL);
|
|
LocalFree(TranslatedTitle);
|
|
if (dwValue!=ERROR_SUCCESS)
|
|
{
|
|
goto CloseKey;
|
|
}
|
|
} else {
|
|
hTitleKey = hConsoleKey;
|
|
}
|
|
|
|
// scotthsu
|
|
dwValue = pcpd->lpFEConsole->uCodePage;
|
|
RegSetValueEx( hTitleKey,
|
|
CONSOLE_REGISTRY_CODEPAGE,
|
|
0,
|
|
REG_DWORD,
|
|
(LPBYTE)&dwValue,
|
|
sizeof(dwValue)
|
|
);
|
|
|
|
//
|
|
// Close the registry keys
|
|
//
|
|
|
|
if (hTitleKey != hConsoleKey) {
|
|
RegCloseKey(hTitleKey);
|
|
}
|
|
|
|
CloseKey:
|
|
RegCloseKey(hConsoleKey);
|
|
}
|