windows-nt/Source/XPSP1/NT/windows/winstate/cobra/modules/sysmod/access.c

900 lines
23 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1999 Microsoft Corporation
Module Name:
access.c
Abstract:
Implements Win9x accessiblity conversion by hooking the physical registry type
and emulating the NT registry format.
Author:
Jim Schmidt (jimschm) 29-Aug-2000
Revision History:
<alias> <date> <comments>
--*/
//
// Includes
//
#include "pch.h"
#include "logmsg.h"
#define DBG_ACCESS "Accessibility"
//
// Strings
//
#define S_ACCESSIBILITY_ROOT TEXT("HKCU\\Control Panel\\Accessibility")
//
// Constants
//
#define SPECIAL_INVERT_OPTION 0x80000000
//
// Macros
//
// none
//
// Types
//
typedef struct {
PCTSTR ValueName;
DWORD FlagVal;
} ACCESS_OPTION, *PACCESS_OPTION;
typedef struct {
PACCESS_OPTION AccessibilityMap;
PCTSTR Win9xSubKey;
PCTSTR NtSubKey;
} ACCESSIBILITY_MAPPINGS, *PACCESSIBILITY_MAPPINGS;
typedef struct {
MEMDB_ENUM EnumStruct;
DWORD RegType;
} ACCESSIBILITY_ENUM_STATE, *PACCESSIBILITY_ENUM_STATE;
//
// Globals
//
MIG_OBJECTTYPEID g_RegistryTypeId;
HASHTABLE g_ProhibitTable;
ACCESS_OPTION g_FilterKeys[] = {
TEXT("On"), FKF_FILTERKEYSON,
TEXT("Available"), FKF_AVAILABLE,
TEXT("HotKeyActive"), FKF_HOTKEYACTIVE,
TEXT("ConfirmHotKey"), FKF_CONFIRMHOTKEY,
TEXT("HotKeySound"), FKF_HOTKEYSOUND,
TEXT("ShowStatusIndicator"), FKF_INDICATOR,
TEXT("ClickOn"), FKF_CLICKON,
TEXT("OnOffFeedback"), 0,
NULL
};
ACCESS_OPTION g_MouseKeys[] = {
TEXT("On"), MKF_MOUSEKEYSON,
TEXT("Available"), MKF_AVAILABLE,
TEXT("HotKeyActive"), MKF_HOTKEYACTIVE,
TEXT("ConfirmHotKey"), MKF_CONFIRMHOTKEY,
TEXT("HotKeySound"), MKF_HOTKEYSOUND,
TEXT("ShowStatusIndicator"), MKF_INDICATOR,
TEXT("Modifiers"), MKF_MODIFIERS|SPECIAL_INVERT_OPTION,
TEXT("ReplaceNumbers"), MKF_REPLACENUMBERS,
TEXT("OnOffFeedback"), 0,
NULL
};
ACCESS_OPTION g_StickyKeys[] = {
TEXT("On"), SKF_STICKYKEYSON,
TEXT("Available"), SKF_AVAILABLE,
TEXT("HotKeyActive"), SKF_HOTKEYACTIVE,
TEXT("ConfirmHotKey"), SKF_CONFIRMHOTKEY,
TEXT("HotKeySound"), SKF_HOTKEYSOUND,
TEXT("ShowStatusIndicator"), SKF_INDICATOR,
TEXT("AudibleFeedback"), SKF_AUDIBLEFEEDBACK,
TEXT("TriState"), SKF_TRISTATE,
TEXT("TwoKeysOff"), SKF_TWOKEYSOFF,
TEXT("OnOffFeedback"), 0,
NULL
};
ACCESS_OPTION g_SoundSentry[] = {
TEXT("On"), SSF_SOUNDSENTRYON,
TEXT("Available"), SSF_AVAILABLE,
TEXT("ShowStatusIndicator"), SSF_INDICATOR,
NULL
};
ACCESS_OPTION g_TimeOut[] = {
TEXT("On"), ATF_TIMEOUTON,
TEXT("OnOffFeedback"), ATF_ONOFFFEEDBACK,
NULL
};
ACCESS_OPTION g_ToggleKeys[] = {
TEXT("On"), TKF_TOGGLEKEYSON,
TEXT("Available"), TKF_AVAILABLE,
TEXT("HotKeyActive"), TKF_HOTKEYACTIVE,
TEXT("ConfirmHotKey"), TKF_CONFIRMHOTKEY,
TEXT("HotKeySound"), TKF_HOTKEYSOUND,
TEXT("ShowStatusIndicator"), TKF_INDICATOR,
TEXT("OnOffFeedback"), 0,
NULL
};
ACCESS_OPTION g_HighContrast[] = {
TEXT("On"), HCF_HIGHCONTRASTON,
TEXT("Available"), HCF_AVAILABLE,
TEXT("HotKeyActive"), HCF_HOTKEYACTIVE,
TEXT("ConfirmHotKey"), HCF_CONFIRMHOTKEY,
TEXT("HotKeySound"), HCF_HOTKEYSOUND,
TEXT("ShowStatusIndicator"), HCF_INDICATOR,
TEXT("HotKeyAvailable"), HCF_HOTKEYAVAILABLE,
TEXT("OnOffFeedback"), 0,
NULL
};
ACCESSIBILITY_MAPPINGS g_AccessibilityMappings[] = {
{g_FilterKeys, TEXT("KeyboardResponse"), TEXT("Keyboard Response")},
{g_MouseKeys, TEXT("MouseKeys")},
{g_StickyKeys, TEXT("StickyKeys")},
{g_SoundSentry, TEXT("SoundSentry")},
{g_TimeOut, TEXT("TimeOut")},
{g_ToggleKeys, TEXT("ToggleKeys")},
{g_HighContrast, TEXT("HighContrast")},
{NULL}
};
//
// Macro expansion list
//
// none
//
// Private function prototypes
//
// none
//
// Macro expansion definition
//
// none
//
// Private prototypes
//
ETMINITIALIZE AccessibilityEtmInitialize;
MIG_PHYSICALENUMADD EmulatedEnumCallback;
MIG_PHYSICALACQUIREHOOK AcquireAccessibilityFlags;
MIG_PHYSICALACQUIREFREE ReleaseAccessibilityFlags;
//
// Code
//
VOID
pProhibit9xSetting (
IN PCTSTR Key,
IN PCTSTR ValueName OPTIONAL
)
{
MIG_OBJECTSTRINGHANDLE handle;
handle = IsmCreateObjectHandle (Key, ValueName);
MYASSERT (handle);
IsmProhibitPhysicalEnum (g_RegistryTypeId, handle, NULL, 0, NULL);
HtAddString (g_ProhibitTable, handle);
IsmDestroyObjectHandle (handle);
}
BOOL
pStoreEmulatedSetting (
IN PCTSTR Key,
IN PCTSTR ValueName, OPTIONAL
IN DWORD Type,
IN PBYTE ValueData,
IN UINT ValueDataSize
)
{
MIG_OBJECTSTRINGHANDLE handle;
PCTSTR memdbNode;
BOOL stored = FALSE;
handle = IsmCreateObjectHandle (Key, ValueName);
memdbNode = JoinPaths (TEXT("~Accessibility"), handle);
IsmDestroyObjectHandle (handle);
if (MemDbAddKey (memdbNode)) {
if (ValueData) {
stored = (MemDbSetValue (memdbNode, Type) != 0);
stored &= (MemDbSetUnorderedBlob (memdbNode, 0, ValueData, ValueDataSize) != 0);
} else {
stored = TRUE;
}
}
FreePathString (memdbNode);
return stored;
}
VOID
pMoveAccessibilityValue (
IN PCTSTR Win9xKey,
IN PCTSTR Win9xValue,
IN PCTSTR NtKey,
IN PCTSTR NtValue,
IN BOOL ForceDword
)
{
HKEY key;
PBYTE data = NULL;
PBYTE storeData;
DWORD conversionDword;
DWORD valueType;
DWORD valueSize;
MIG_OBJECTSTRINGHANDLE handle;
BOOL prohibited;
handle = IsmCreateObjectHandle (Win9xKey, Win9xValue);
prohibited = (HtFindString (g_ProhibitTable, handle) != NULL);
IsmDestroyObjectHandle (handle);
if (prohibited) {
return;
}
key = OpenRegKeyStr (Win9xKey);
if (!key) {
return;
}
__try {
if (!GetRegValueTypeAndSize (key, Win9xValue, &valueType, &valueSize)) {
__leave;
}
if (valueType != REG_SZ && valueType != REG_DWORD) {
__leave;
}
data = GetRegValueData (key, Win9xValue);
if (!data) {
__leave;
}
if (ForceDword && valueType == REG_SZ) {
storeData = (PBYTE) &conversionDword;
conversionDword = _ttoi ((PCTSTR) data);
valueType = REG_DWORD;
valueSize = sizeof (DWORD);
} else {
storeData = data;
}
if (pStoreEmulatedSetting (NtKey, NtValue, valueType, storeData, valueSize)) {
pProhibit9xSetting (Win9xKey, Win9xValue);
}
}
__finally {
CloseRegKey (key);
if (data) {
FreeAlloc (data);
}
}
}
VOID
pMoveAccessibilityKey (
IN PCTSTR Win9xKey,
IN PCTSTR NtKey
)
{
HKEY key;
PBYTE data = NULL;
DWORD valueType;
DWORD valueSize;
LONG rc;
DWORD index = 0;
TCHAR valueName[MAX_REGISTRY_KEY];
DWORD valueNameSize;
GROWBUFFER value = INIT_GROWBUFFER;
MIG_OBJECTSTRINGHANDLE handle;
BOOL prohibited;
key = OpenRegKeyStr (Win9xKey);
if (!key) {
return;
}
__try {
for (;;) {
valueNameSize = ARRAYSIZE(valueName);
valueSize = 0;
rc = RegEnumValue (key, index, valueName, &valueNameSize, NULL, &valueType, NULL, &valueSize);
if (rc != ERROR_SUCCESS) {
break;
}
handle = IsmCreateObjectHandle (Win9xKey, valueName);
prohibited = (HtFindString (g_ProhibitTable, handle) != NULL);
IsmDestroyObjectHandle (handle);
if (!prohibited) {
value.End = 0;
data = GbGrow (&value, valueSize);
valueNameSize = ARRAYSIZE(valueName);
rc = RegEnumValue (key, index, valueName, &valueNameSize, NULL, &valueType, value.Buf, &valueSize);
if (rc != ERROR_SUCCESS) {
break;
}
if (pStoreEmulatedSetting (NtKey, valueName, valueType, data, valueSize)) {
pProhibit9xSetting (Win9xKey, valueName);
}
}
index++;
}
if (pStoreEmulatedSetting (NtKey, NULL, 0, NULL, 0)) {
pProhibit9xSetting (Win9xKey, NULL);
}
}
__finally {
CloseRegKey (key);
GbFree (&value);
}
}
VOID
pTranslateAccessibilityKey (
IN PCTSTR Win9xSubKey,
IN PCTSTR NtSubKey,
IN PACCESS_OPTION AccessibilityMap
)
{
TCHAR full9xKey[MAX_REGISTRY_KEY];
TCHAR fullNtKey[MAX_REGISTRY_KEY];
MIG_OBJECTSTRINGHANDLE handle = NULL;
HKEY key = NULL;
PCTSTR data;
DWORD flags = 0;
DWORD thisFlag;
BOOL enabled;
TCHAR buffer[32];
__try {
StringCopy (full9xKey, S_ACCESSIBILITY_ROOT TEXT("\\"));
StringCopy (fullNtKey, full9xKey);
StringCat (full9xKey, Win9xSubKey);
StringCat (fullNtKey, NtSubKey);
key = OpenRegKeyStr (full9xKey);
if (!key) {
__leave;
}
while (AccessibilityMap->ValueName) {
//
// Prohibit enum of this value
//
handle = IsmCreateObjectHandle (full9xKey, AccessibilityMap->ValueName);
MYASSERT (handle);
IsmProhibitPhysicalEnum (g_RegistryTypeId, handle, NULL, 0, NULL);
HtAddString (g_ProhibitTable, handle);
IsmDestroyObjectHandle (handle);
handle = NULL;
//
// Update the emulated flags
//
data = GetRegValueString (key, AccessibilityMap->ValueName);
if (data) {
enabled = (_ttoi (data) != 0);
thisFlag = (AccessibilityMap->FlagVal & (~SPECIAL_INVERT_OPTION));
if (AccessibilityMap->FlagVal & SPECIAL_INVERT_OPTION) {
enabled = !enabled;
}
if (enabled) {
flags |= thisFlag;
}
FreeAlloc (data);
}
AccessibilityMap++;
}
//
// Put the emulated value in the hash table
//
wsprintf (buffer, TEXT("%u"), flags);
pStoreEmulatedSetting (fullNtKey, TEXT("Flags"), REG_SZ, (PBYTE) buffer, SizeOfString (buffer));
}
__finally {
if (key) {
CloseRegKey (key);
}
}
}
VOID
pFillTranslationTable (
VOID
)
{
PACCESSIBILITY_MAPPINGS mappings;
//
// Loop through all flags that need translation. Disable enumeration of
// the Win9x physical values and enable enumeration of the translated values
// via population of the hash table.
//
mappings = g_AccessibilityMappings;
while (mappings->AccessibilityMap) {
pTranslateAccessibilityKey (
mappings->Win9xSubKey,
mappings->NtSubKey ? mappings->NtSubKey : mappings->Win9xSubKey,
mappings->AccessibilityMap
);
mappings++;
}
//
// Add all keys that have moved, ordered from most specific to least specific
//
// AutoRepeat values are transposed
pMoveAccessibilityValue (
S_ACCESSIBILITY_ROOT TEXT("\\KeyboardResponse"), TEXT("AutoRepeatDelay"),
S_ACCESSIBILITY_ROOT TEXT("\\Keyboard Response"), TEXT("AutoRepeatRate"),
FALSE
);
pMoveAccessibilityValue (
S_ACCESSIBILITY_ROOT TEXT("\\KeyboardResponse"), TEXT("AutoRepeatRate"),
S_ACCESSIBILITY_ROOT TEXT("\\Keyboard Response"), TEXT("AutoRepeatDelay"),
FALSE
);
// double c in DelayBeforeAcceptance value name
pMoveAccessibilityValue (
S_ACCESSIBILITY_ROOT TEXT("\\KeyboardResponse"), TEXT("DelayBeforeAcceptancce"),
S_ACCESSIBILITY_ROOT TEXT("\\Keyboard Response"), TEXT("DelayBeforeAcceptance"),
FALSE
);
// add a space to the key name for the rest of the values
pMoveAccessibilityKey (
S_ACCESSIBILITY_ROOT TEXT("\\KeyboardResponse"),
S_ACCESSIBILITY_ROOT TEXT("\\Keyboard Response")
);
// change BaudRate to Baud & convert to DWORD
pMoveAccessibilityValue (
S_ACCESSIBILITY_ROOT TEXT("\\SerialKeys"), TEXT("BaudRate"),
S_ACCESSIBILITY_ROOT TEXT("\\SerialKeys"), TEXT("Baud"),
TRUE
);
// convert Flags to DWORD
pMoveAccessibilityValue (
S_ACCESSIBILITY_ROOT TEXT("\\SerialKeys"), TEXT("Flags"),
S_ACCESSIBILITY_ROOT TEXT("\\SerialKeys"), TEXT("Flags"),
TRUE
);
// add space between high and contrast
pMoveAccessibilityValue (
S_ACCESSIBILITY_ROOT TEXT("\\HighContrast"), TEXT("Pre-HighContrast Scheme"),
S_ACCESSIBILITY_ROOT TEXT("\\HighContrast"), TEXT("Pre-High Contrast Scheme"),
FALSE
);
// move two values from the root into their own subkeys
pMoveAccessibilityValue (
S_ACCESSIBILITY_ROOT, TEXT("Blind Access"),
S_ACCESSIBILITY_ROOT TEXT("\\Blind Access"), TEXT("On"),
FALSE
);
pStoreEmulatedSetting (S_ACCESSIBILITY_ROOT TEXT("\\Blind Access"), NULL, 0, NULL, 0);
pMoveAccessibilityValue (
S_ACCESSIBILITY_ROOT, TEXT("Keyboard Preference"),
S_ACCESSIBILITY_ROOT TEXT("\\Keyboard Preference"), TEXT("On"),
FALSE
);
pStoreEmulatedSetting (S_ACCESSIBILITY_ROOT TEXT("\\Keyboard Preference"), NULL, 0, NULL, 0);
}
BOOL
WINAPI
AccessibilityEtmInitialize (
IN MIG_PLATFORMTYPEID Platform,
IN PMIG_LOGCALLBACK LogCallback,
IN PVOID Reserved
)
{
MIG_OBJECTSTRINGHANDLE objectName;
BOOL b = TRUE;
LogReInit (NULL, NULL, NULL, (PLOGCALLBACK) LogCallback);
if (ISWIN9X()) {
g_RegistryTypeId = IsmGetObjectTypeId (S_REGISTRYTYPE);
MYASSERT (g_RegistryTypeId);
g_ProhibitTable = HtAlloc();
MYASSERT (g_ProhibitTable);
if (g_RegistryTypeId) {
//
// Add a callback for additional enumeration. If we are unable to do so, then
// someone else is already doing something different for this key.
//
objectName = IsmCreateObjectHandle (S_ACCESSIBILITY_ROOT, NULL);
b = IsmAddToPhysicalEnum (g_RegistryTypeId, objectName, EmulatedEnumCallback, 0);
IsmDestroyObjectHandle (objectName);
if (b) {
//
// Add a callback to acquire the data of the new physical objects
//
objectName = IsmCreateSimpleObjectPattern (
S_ACCESSIBILITY_ROOT,
TRUE,
NULL,
TRUE
);
b = IsmRegisterPhysicalAcquireHook (
g_RegistryTypeId,
objectName,
AcquireAccessibilityFlags,
ReleaseAccessibilityFlags,
0,
NULL
);
IsmDestroyObjectHandle (objectName);
}
if (b) {
//
// Now load memdb with the current registry values and
// prohibit the enumeration of Win9x values.
//
pFillTranslationTable ();
}
ELSE_DEBUGMSG ((DBG_WARNING, "Not allowed to translate accessibility key"));
}
HtFree (g_ProhibitTable);
g_ProhibitTable = NULL;
}
return b;
}
BOOL
WINAPI
EmulatedEnumCallback (
IN OUT PMIG_TYPEOBJECTENUM ObjectEnum,
IN MIG_OBJECTSTRINGHANDLE Pattern,
IN MIG_PARSEDPATTERN ParsedPattern,
IN ULONG_PTR Arg,
IN BOOL Abort
)
{
PACCESSIBILITY_ENUM_STATE state = (PACCESSIBILITY_ENUM_STATE) ObjectEnum->EtmHandle;
BOOL result = FALSE;
BOOL cleanUpMemdb = TRUE;
PCTSTR p;
for (;;) {
if (!Abort) {
//
// Begin or continue? If the EtmHandle is NULL, begin. Otherwise, continue.
//
if (!state) {
state = (PACCESSIBILITY_ENUM_STATE) MemAllocUninit (sizeof (ACCESSIBILITY_ENUM_STATE));
if (!state) {
MYASSERT (FALSE);
return FALSE;
}
ObjectEnum->EtmHandle = (LONG_PTR) state;
result = MemDbEnumFirst (
&state->EnumStruct,
TEXT("~Accessibility\\*"),
ENUMFLAG_NORMAL,
1,
MEMDB_LAST_LEVEL
);
} else {
result = MemDbEnumNext (&state->EnumStruct);
}
//
// If an item was found, populate the enum struct. Otherwise, set
// Abort to TRUE to clean up.
//
if (result) {
//
// Test against pattern
//
if (!IsmParsedPatternMatch (ParsedPattern, 0, state->EnumStruct.KeyName)) {
continue;
}
MYASSERT ((ObjectEnum->ObjectTypeId & (~PLATFORM_MASK)) == g_RegistryTypeId);
ObjectEnum->ObjectName = state->EnumStruct.KeyName;
state->RegType = state->EnumStruct.Value;
//
// Fill in node, leaf and details
//
IsmDestroyObjectString (ObjectEnum->ObjectNode);
IsmDestroyObjectString (ObjectEnum->ObjectLeaf);
IsmReleaseMemory (ObjectEnum->NativeObjectName);
IsmCreateObjectStringsFromHandle (
ObjectEnum->ObjectName,
&ObjectEnum->ObjectNode,
&ObjectEnum->ObjectLeaf
);
MYASSERT (ObjectEnum->ObjectNode);
ObjectEnum->Level = 0;
p = _tcschr (ObjectEnum->ObjectNode, TEXT('\\'));
while (p) {
ObjectEnum->Level++;
p = _tcschr (p + 1, TEXT('\\'));
}
ObjectEnum->SubLevel = 0;
if (ObjectEnum->ObjectLeaf) {
ObjectEnum->IsNode = FALSE;
ObjectEnum->IsLeaf = TRUE;
} else {
ObjectEnum->IsNode = TRUE;
ObjectEnum->IsLeaf = FALSE;
}
if (state->RegType) {
ObjectEnum->Details.DetailsSize = sizeof (state->RegType);
ObjectEnum->Details.DetailsData = &state->RegType;
} else {
ObjectEnum->Details.DetailsSize = 0;
ObjectEnum->Details.DetailsData = NULL;
}
//
// Rely on base type to get the native object name
//
ObjectEnum->NativeObjectName = IsmGetNativeObjectName (
ObjectEnum->ObjectTypeId,
ObjectEnum->ObjectName
);
} else {
Abort = TRUE;
cleanUpMemdb = FALSE;
}
}
if (Abort) {
//
// Clean up our enum struct
//
if (state) {
if (cleanUpMemdb) {
MemDbAbortEnum (&state->EnumStruct);
}
IsmDestroyObjectString (ObjectEnum->ObjectNode);
ObjectEnum->ObjectNode = NULL;
IsmDestroyObjectString (ObjectEnum->ObjectLeaf);
ObjectEnum->ObjectLeaf = NULL;
IsmReleaseMemory (ObjectEnum->NativeObjectName);
ObjectEnum->NativeObjectName = NULL;
FreeAlloc (state);
}
// return value ignored in Abort case, and ObjectEnum is zeroed by the ISM
}
break;
}
return result;
}
BOOL
WINAPI
AcquireAccessibilityFlags(
IN MIG_OBJECTSTRINGHANDLE ObjectName,
IN PMIG_CONTENT ObjectContent,
IN MIG_CONTENTTYPE ContentType,
IN UINT MemoryContentLimit,
OUT PMIG_CONTENT *NewObjectContent, CALLER_INITIALIZED OPTIONAL
IN BOOL ReleaseContent,
IN ULONG_PTR Arg
)
{
BOOL result = TRUE;
PDWORD details;
PMIG_CONTENT ourContent;
PCTSTR memdbNode;
//
// Is this object in our hash table?
//
if (ContentType == CONTENTTYPE_FILE) {
DEBUGMSG ((DBG_ERROR, "Accessibility content cannot be saved to a file"));
result = FALSE;
} else {
memdbNode = JoinPaths (TEXT("~Accessibility"), ObjectName);
if (MemDbTestKey (memdbNode)) {
//
// Alloc updated content struct
//
ourContent = MemAllocZeroed (sizeof (MIG_CONTENT) + sizeof (DWORD));
ourContent->EtmHandle = ourContent;
details = (PDWORD) (ourContent + 1);
//
// Get the content from memdb
//
ourContent->MemoryContent.ContentBytes = MemDbGetUnorderedBlob (
memdbNode,
0,
&ourContent->MemoryContent.ContentSize
);
if (ourContent->MemoryContent.ContentBytes) {
MemDbGetValue (memdbNode, details);
ourContent->Details.DetailsSize = sizeof (DWORD);
ourContent->Details.DetailsData = details;
} else {
ourContent->MemoryContent.ContentSize = 0;
ourContent->Details.DetailsSize = 0;
ourContent->Details.DetailsData = NULL;
}
ourContent->ContentInFile = FALSE;
//
// Pass it to ISM
//
*NewObjectContent = ourContent;
}
FreePathString (memdbNode);
}
return result; // always TRUE unless an error occurred
}
VOID
WINAPI
ReleaseAccessibilityFlags(
IN PMIG_CONTENT ObjectContent
)
{
//
// This callback is called to free the content we allocated above.
//
if (ObjectContent->MemoryContent.ContentBytes) {
MemDbReleaseMemory (ObjectContent->MemoryContent.ContentBytes);
}
FreeAlloc ((PMIG_CONTENT) ObjectContent->EtmHandle);
}
BOOL
WINAPI
AccessibilitySourceInitialize (
IN PMIG_LOGCALLBACK LogCallback,
IN PVOID Reserved
)
{
LogReInit (NULL, NULL, NULL, (PLOGCALLBACK) LogCallback);
if (!g_RegistryTypeId) {
g_RegistryTypeId = IsmGetObjectTypeId (S_REGISTRYTYPE);
}
return TRUE;
}