2025-04-27 07:49:33 -04:00

1061 lines
24 KiB
C

/*++
Copyright (c) 1996 Microsoft Corporation
Module Name:
ifsrdr.c
Abstract:
This module implements a minimal app to load and unload,
ifs monolithic minirdr. Also explicit start/stop control is
provided
This module also populates the registry entries for the
driver, and the network provider.
--*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <ifsmrx.h>
//
// This struct is used as a temporary in-memory representation of a registry entry.
//
// N.B. The pvValue entry serves 'double duty'. For integral datatypes (REG_DWORD) it
// holds the value of the DWORD, not its' address. Make sure you don't dereference
// pvValue if the type is REG_DWORD. In the remaining cases, it contains a pointer
// to the first byte of storage containing the value.
//
typedef struct {
PCHAR pszKey;
DWORD dwType;
DWORD dwLength;
PVOID pvValue;
} REGENTRY, *PREGENTRY;
void
ReadRegistryKeyValues(
HKEY hKey,
DWORD Count,
PREGENTRY pValues);
void
WriteRegistryKeyValues(
HKEY hKey,
DWORD Count,
PREGENTRY pValues);
void IfsMrxStart(void);
void IfsMrxStop(void);
void IfsMrxLoad(void);
void IfsMrxUnload(void);
void IfsMrxUsage(void);
void SetupIfsMrxRegistryEntries(void);
//
// routines for manipulating registry key values
//
BOOL GetRegsz (HKEY hKey, PCHAR pszKey, PVOID * ppvValue, DWORD *pdwLength);
BOOL GetRegesz(HKEY hKey, PCHAR pszKey, PVOID * ppvValue, DWORD *pdwLength);
BOOL GetRegmsz(HKEY hKey, PCHAR pszKey, PVOID * ppvValue, DWORD *pdwLength);
BOOL GetRegdw (HKEY hKey, PCHAR pszKey, PVOID * ppvValue, DWORD *pdwLength);
//
// routines for manipulating registry keys
//
BOOL OpenKey(PCHAR pszKey, PHKEY phKey);
BOOL CreateKey(PCHAR pszKey, PHKEY phKey);
BOOL AddValue(HKEY hKey, PCHAR pszKey, DWORD dwType, DWORD dwLength, PVOID pvValue);
char* IfsMrxDriverName = "IfsMrx";
VOID
__cdecl
main(
int argc,
char *argv[]
)
{
char command[16];
BOOL IfsMrxStarted = FALSE;
BOOL IfsMrxLoaded = FALSE;
IfsMrxUsage();
for (;;)
{
printf("\nCommand:");
scanf("%s",command);
if (command[0] == 'Q' || command[0] == 'q') { break; }
if (!strncmp(command,"?",1)) { IfsMrxUsage(); }
if (command[0] == 'R' || command[0] == 'r') {
SetupIfsMrxRegistryEntries();
exit(0);
}
if (command[0] == 'L' || command[0] == 'l') {
if (!IfsMrxLoaded) {
IfsMrxLoad();
} else {
printf("IFS Mini redirector already loaded\n");
}
}
if (command[0] == 'U' || command[0] == 'l') {
if (IfsMrxLoaded) {
IfsMrxUnload();
} else {
printf("IFS Mini redirector not loaded\n");
}
}
if (command[0] == 'S' || command[0] == 's') {
if (!IfsMrxStarted) {
IfsMrxStart();
IfsMrxStarted = TRUE;
} else {
printf("IFS mini redirector already started\n");
}
}
if (command[0] == 'T' || command[0] == 't') {
if (IfsMrxStarted) {
IfsMrxStop();
} else {
printf("IFS Mini redirector not started\n");
}
}
}
}
// These handles are retained
HANDLE hSharedMemory;
HANDLE hMutex;
VOID IfsMrxStart()
/*++
Routine Description:
This routine starts the IFS sample mini redirector.
Notes:
The start is distinguished from Load. During this phase the appropriate FSCTL
is issued and the shared memory/mutex data structures required for the Network
provider DLL are initialized.
--*/
{
NTSTATUS ntstatus;
UNICODE_STRING DeviceName;
IO_STATUS_BLOCK IoStatusBlock;
OBJECT_ATTRIBUTES ObjectAttributes;
HANDLE IfsMrxHandle;
//
// Open the Ifs Mrx device.
//
RtlInitUnicodeString(&DeviceName,DD_IFSMRX_FS_DEVICE_NAME_U);
InitializeObjectAttributes(
&ObjectAttributes,
&DeviceName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL
);
ntstatus = NtOpenFile(
&IfsMrxHandle,
SYNCHRONIZE,
&ObjectAttributes,
&IoStatusBlock,
FILE_SHARE_VALID_FLAGS,
FILE_SYNCHRONOUS_IO_NONALERT
);
if (ntstatus == STATUS_SUCCESS) {
ntstatus = NtFsControlFile(
IfsMrxHandle,
0,
NULL,
NULL,
&IoStatusBlock,
FSCTL_IFSMRX_START,
NULL,
0,
NULL,
0
);
NtClose(IfsMrxHandle);
}
if (ntstatus == STATUS_SUCCESS) {
DWORD Status;
HANDLE hMutex;
hSharedMemory = CreateFileMappingW(
INVALID_HANDLE_VALUE,
NULL,
PAGE_READWRITE,
0,
sizeof(IFSMRXNP_SHARED_MEMORY),
IFSMRXNP_SHARED_MEMORY_NAME);
if (hSharedMemory == NULL) {
Status = GetLastError();
if (Status == ERROR_ALREADY_EXISTS) {
Status = STATUS_SUCCESS;
}
printf("IFS MRx Net Provider Mutex Creation status %lx\n",Status);
} else {
PIFSMRXNP_SHARED_MEMORY pSharedMemory;
pSharedMemory = MapViewOfFile(hSharedMemory, FILE_MAP_WRITE, 0, 0, 0);
if (pSharedMemory != NULL) {
pSharedMemory->HighestIndexInUse = -1;
pSharedMemory->NumberOfResourcesInUse = 0;
}
UnmapViewOfFile(pSharedMemory);
}
hMutex = CreateMutexW(
NULL,
FALSE,
IFSMRXNP_MUTEX_NAME);
if (hSharedMemory == NULL) {
Status = GetLastError();
if (Status == ERROR_ALREADY_EXISTS) {
Status = STATUS_SUCCESS;
}
printf("IFS MRx Net Provider Mutex Creation status %lx\n",Status);
}
}
printf("IFS MRx sample mini redirector start status %lx\n",ntstatus);
}
VOID IfsMrxStop()
/*++
Routine Description:
This routine stops the IFS sample mini redirector.
Notes:
The stop is distinguished from unload. During this phase the appropriate FSCTL
is issued and the shared memory/mutex data structures required for the Network
provider DLL are torn down.
--*/
{
NTSTATUS ntstatus;
UNICODE_STRING DeviceName;
IO_STATUS_BLOCK IoStatusBlock;
OBJECT_ATTRIBUTES ObjectAttributes;
HANDLE IfsMrxHandle;
//
// Open the Ifs Mrx device.
//
RtlInitUnicodeString(&DeviceName,DD_IFSMRX_FS_DEVICE_NAME_U);
InitializeObjectAttributes(
&ObjectAttributes,
&DeviceName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL
);
ntstatus = NtOpenFile(
&IfsMrxHandle,
SYNCHRONIZE,
&ObjectAttributes,
&IoStatusBlock,
FILE_SHARE_VALID_FLAGS,
FILE_SYNCHRONOUS_IO_NONALERT
);
if (ntstatus == STATUS_SUCCESS) {
ntstatus = NtFsControlFile(
IfsMrxHandle,
0,
NULL,
NULL,
&IoStatusBlock,
FSCTL_IFSMRX_STOP,
NULL,
0,
NULL,
0
);
NtClose(IfsMrxHandle);
}
CloseHandle(hSharedMemory);
CloseHandle(hMutex);
printf("IFS MRx sample mini redirector start status %lx\n",ntstatus);
}
VOID IfsMrxLoad()
{
printf("Loading Ifs example minirdr.......\n");
system("net start ifsmrx");
}
VOID IfsMrxUnload(void)
{
printf("Unloading Ifs example minirdr\n");
system("net stop ifsmrx");
}
VOID IfsMrxUsage(void){
printf("\n");
printf(" Ifs Example Mini-rdr Utility");
printf(" The following commands are valid \n");
printf(" R -> load the registry entries for ifs minirdr\n");
printf(" L -> load the ifs minirdr driver\n");
printf(" U -> unload the ifs minirdr driver\n");
printf(" S -> start the ifs minirdr driver\n");
printf(" T -> stop the ifs minirdr driver\n");
}
REGENTRY LinkageKeyValues[] =
{
{ "Bind", REG_MULTI_SZ, 0, 0 },
{ "Export", REG_MULTI_SZ, 0, 0 },
{ "Route", REG_MULTI_SZ, 0, 0 }
};
REGENTRY LinkageDisabledKeyValues[] =
{
{ "Bind", REG_MULTI_SZ, 0, 0 },
{ "Export", REG_MULTI_SZ, 0, 0 },
{ "Route", REG_MULTI_SZ, 0, 0 }
};
REGENTRY NetworkProviderKeyValues[] =
{
{
"Devicename",
REG_SZ,
IFSMRX_DEVICE_NAME_A_LENGTH,
IFSMRX_DEVICE_NAME_A
},
{
"ProviderPath",
REG_EXPAND_SZ,
35,
"%SystemRoot%\\System32\\IfsMrxNp.dll"
},
{
"Name",
REG_SZ,
IFSMRX_PROVIDER_NAME_A_LENGTH,
IFSMRX_PROVIDER_NAME_A
}
};
REGENTRY IfsMrxKeyValues[] =
{
{ "Type", REG_DWORD, 4, (PVOID) 2 },
{ "Start", REG_DWORD, 4, (PVOID) 3 },
{ "ErrorControl", REG_DWORD, 4, (PVOID) 1 },
{ "ImagePath", REG_EXPAND_SZ, 40,
"\\SystemRoot\\System32\\drivers\\ifsmrx.sys" },
{ "DisplayName", REG_SZ, 7, "IfsMrx" },
{ "Group", REG_SZ, 8, "Network" }
};
REGENTRY ProviderOrderKeyValues[] =
{
{ "ProviderOrder", REG_SZ, 0, 0 }
};
void SetupIfsMrxRegistryEntries(void)
/*++
Routine Description:
This routine initializes the registry entries for the ifsmrx
minirdr. This only needs to be done once.
Arguments:
None
Return Value:
None
--*/
{
HKEY hCurrentKey;
printf("Setting up IfsMrx registry Entries\n");
// Open the ifs mrx key and write out the values ...
if (CreateKey("System\\CurrentControlSet\\Services\\ifsmrx",&hCurrentKey)) {
WriteRegistryKeyValues(
hCurrentKey,
sizeof(IfsMrxKeyValues)/sizeof(REGENTRY),
IfsMrxKeyValues);
RegCloseKey(hCurrentKey);
} else {
printf("Error Creating Key %s Status %d\n","ifsmrx",GetLastError());
return;
}
// Read the linkage values associated with the Lanman workstation service.
// This contains all the transports and the order in which they need to be used
if (OpenKey("System\\CurrentControlSet\\Services\\LanmanWorkstation\\Linkage",&hCurrentKey)) {
ReadRegistryKeyValues(
hCurrentKey,
sizeof(LinkageKeyValues)/sizeof(REGENTRY),
LinkageKeyValues);
RegCloseKey(hCurrentKey);
} else {
printf("Error Opening Key %s Status %d\n","LanmanWorkstation\\Linkage",GetLastError());
return;
}
// Update the Ifs MRx linkage values
if (CreateKey("System\\CurrentControlSet\\Services\\ifsmrx\\Linkage",&hCurrentKey)) {
WriteRegistryKeyValues(
hCurrentKey,
sizeof(LinkageKeyValues)/sizeof(REGENTRY),
LinkageKeyValues);
RegCloseKey(hCurrentKey);
} else {
printf("Error Creating Key %s Status %d\n","ifsmrx\\linkage",GetLastError());
return;
}
if (OpenKey("System\\CurrentControlSet\\Services\\LanmanWorkstation\\Linkage\\Disabled",&hCurrentKey)) {
ReadRegistryKeyValues(
hCurrentKey,
sizeof(LinkageDisabledKeyValues)/sizeof(REGENTRY),
LinkageDisabledKeyValues);
RegCloseKey(hCurrentKey);
} else {
printf("Error Opening Key %s Status %d\n","LanmanWorkstation\\Linkage\\Disabled",GetLastError());
return;
}
// Update the Ifs MRx linkage disabled values
if (CreateKey("System\\CurrentControlSet\\Services\\ifsmrx\\Linkage\\Disabled",&hCurrentKey)) {
WriteRegistryKeyValues(
hCurrentKey,
sizeof(LinkageDisabledKeyValues)/sizeof(REGENTRY),
LinkageDisabledKeyValues);
RegCloseKey(hCurrentKey);
} else {
printf("Error Creating Key %s Status %d\n","ifsmrx\\linkage\\disabled",GetLastError());
return;
}
// Update the ifsmrx network provider section
if (CreateKey("System\\CurrentControlSet\\Services\\ifsmrx\\networkprovider",&hCurrentKey)) {
WriteRegistryKeyValues(
hCurrentKey,
sizeof(NetworkProviderKeyValues)/sizeof(REGENTRY),
NetworkProviderKeyValues);
RegCloseKey(hCurrentKey);
} else {
printf("Error Creating Key %s Status %d\n","ifsmrx\\linkage\\disabled",GetLastError());
return;
}
if (CreateKey("System\\CurrentControlSet\\Services\\ifsmrx\\parameters",&hCurrentKey)) {
RegCloseKey(hCurrentKey);
} else {
printf("Error Creating Key %s Status %d\n","ifsmrx\\parameters",GetLastError());
return;
}
// Update the provider order to include the IFS sample mini redirector
// as well
printf("Updating the provider order\n");
if (OpenKey("System\\CurrentControlSet\\control\\networkprovider\\order",&hCurrentKey)) {
char *pNewValue;
DWORD ProviderNameLength = strlen(",ifsmrx");
DWORD NewValueSize = 0;
ReadRegistryKeyValues(
hCurrentKey,
sizeof(ProviderOrderKeyValues)/sizeof(REGENTRY),
ProviderOrderKeyValues);
RegCloseKey(hCurrentKey);
ProviderNameLength = strlen(IfsMrxDriverName);
if (ProviderOrderKeyValues[0].dwLength != 0) {
// There are more than one providers. Include space for
// a delimiter
ProviderNameLength += sizeof(char);
}
// Include the Ifs sample mini redirector in the list of providers.
NewValueSize = ProviderOrderKeyValues[0].dwLength +
ProviderNameLength;
pNewValue = malloc(NewValueSize);
if (pNewValue != NULL) {
if (ProviderOrderKeyValues[0].dwLength != 0) {
strcpy(
pNewValue,
ProviderOrderKeyValues[0].pvValue);
strcat(pNewValue,",");
} else {
*pNewValue = '\0';
}
strcat(
pNewValue,
IfsMrxDriverName);
// free the previously allocated string
free( ProviderOrderKeyValues[0].pvValue );
ProviderOrderKeyValues[0].pvValue = pNewValue;
ProviderOrderKeyValues[0].dwLength = NewValueSize;
if (CreateKey("System\\CurrentControlSet\\control\\networkprovider\\order",&hCurrentKey)) {
WriteRegistryKeyValues(
hCurrentKey,
sizeof(ProviderOrderKeyValues)/sizeof(REGENTRY),
ProviderOrderKeyValues);
RegCloseKey(hCurrentKey);
}
free(pNewValue);
} else {
printf("error updating order -- out of memory\n");
}
} else {
printf("error opening control\\networkprovider\\order key\n");
}
}
void
ReadRegistryKeyValues(
HKEY hCurrentKey,
DWORD NumberOfValues,
PREGENTRY pValues)
/*++
Routine Description:
This routine reads a bunch of values associated with a given key.
Arguments:
hCurrentKey - the key
NumberOfValues - the number of values
pValues - the array of values
Return Value:
None
--*/
{
//
// Iterate throught table reading the values along the way
//
DWORD i;
for (i = 0; i < NumberOfValues; i++)
{
DWORD dwType;
PCHAR pszKey;
dwType = pValues[i].dwType;
pszKey = pValues[i].pszKey;
switch (dwType)
{
case REG_SZ:
(void) GetRegsz(hCurrentKey,
pszKey,
&pValues[i].pvValue,
&pValues[i].dwLength);
break;
case REG_DWORD:
(void) GetRegdw(hCurrentKey,
pszKey,
&pValues[i].pvValue,
&pValues[i].dwLength);
break;
case REG_EXPAND_SZ:
(void) GetRegesz(hCurrentKey,
pszKey,
&pValues[i].pvValue,
&pValues[i].dwLength);
break;
case REG_MULTI_SZ:
(void) GetRegmsz(hCurrentKey,
pszKey,
&pValues[i].pvValue,
&pValues[i].dwLength);
break;
case REG_BINARY:
printf("%s is a REG_BINARY and won't be duplicated\n", pszKey);
break;
default:
printf("%s is an unknown type; %d (decimal)\n", pszKey, dwType);
break;
}
}
}
//
// Get a REG_SZ value and stick it in the table entry, along with the
// length
//
BOOL GetRegsz(HKEY hKey, PCHAR pszKey, PVOID * ppvValue, DWORD *pdwLength)
{
char achValue[1024];
DWORD dwLength;
LONG Status;
DWORD dwType = REG_SZ;
PCHAR pszValue = NULL;
if ((NULL == pszKey) || (NULL == ppvValue) || (NULL == hKey) || (NULL == pdwLength)) {
return FALSE;
}
#ifdef _DEBUG
FillMemory(achValue, sizeof(achValue), 0xcd);
#endif
dwLength = sizeof(achValue);
Status = RegQueryValueEx(
hKey,
pszKey,
NULL,
&dwType,
(PUCHAR) &achValue[0],
&dwLength);
if ((ERROR_SUCCESS != Status) || (REG_SZ != dwType)) {
return FALSE;
}
pszValue = malloc(dwLength);
if (NULL == pszValue) {
return FALSE;
}
CopyMemory(pszValue, achValue, dwLength);
*ppvValue = pszValue;
*pdwLength = dwLength;
return TRUE;
}
//
// Get the value of a REG_EXPAND_SZ and its length
//
BOOL GetRegesz(HKEY hKey, PCHAR pszKey, PVOID * ppvValue, DWORD * pdwLength)
{
char achValue[1024];
DWORD dwLength;
LONG Status;
DWORD dwType = REG_EXPAND_SZ;
PCHAR pszValue = NULL;
if ((NULL == pszKey) || (NULL == ppvValue) || (NULL == hKey) || (NULL == pdwLength)) {
return FALSE;
}
#ifdef _DEBUG
FillMemory(achValue, sizeof(achValue), 0xcd);
#endif
dwLength = sizeof(achValue);
Status = RegQueryValueEx(
hKey,
pszKey,
NULL,
&dwType,
(PUCHAR) &achValue[0],
&dwLength);
if ((ERROR_SUCCESS != Status) || (REG_EXPAND_SZ != dwType)) {
return FALSE;
}
pszValue = malloc(dwLength);
if (NULL == pszValue) {
return FALSE;
}
CopyMemory(pszValue, achValue, dwLength);
*ppvValue = pszValue;
*pdwLength = dwLength;
return TRUE;
}
//
// Get value and length of REG_MULTI_SZ
//
BOOL GetRegmsz(HKEY hKey, PCHAR pszKey, PVOID * ppvValue, DWORD * pdwLength)
{
char achValue[1024];
DWORD dwLength;
LONG Status;
DWORD dwType = REG_MULTI_SZ;
PCHAR pszValue = NULL;
if ((NULL == pszKey) || (NULL == ppvValue) || (NULL == hKey) || (NULL == pdwLength)) {
return FALSE;
}
#ifdef _DEBUG
FillMemory(achValue, sizeof(achValue), 0xcd);
#endif
dwLength = sizeof(achValue);
Status = RegQueryValueEx(
hKey,
pszKey,
NULL,
&dwType,
(PUCHAR) &achValue[0],
&dwLength);
if ((ERROR_SUCCESS != Status) || (REG_MULTI_SZ != dwType)) {
return FALSE;
}
pszValue = malloc(dwLength);
if (NULL == pszValue) {
return FALSE;
}
CopyMemory(pszValue, achValue, dwLength);
*ppvValue = pszValue;
*pdwLength = dwLength;
return TRUE;
}
//
// Get value and length of REG_DWORD
//
BOOL GetRegdw(HKEY hKey, PCHAR pszKey, PVOID * ppvValue, DWORD * pdwLength)
{
DWORD dwValue = 0;
DWORD dwLength;
LONG Status;
DWORD dwType = REG_DWORD;
if ((NULL == pszKey) || (NULL == ppvValue) || (NULL == hKey) || (NULL == pdwLength)) {
return FALSE;
}
dwLength = sizeof(dwValue);
Status = RegQueryValueEx(
hKey,
pszKey,
NULL,
&dwType,
(PUCHAR) &dwValue,
&dwLength);
if ((ERROR_SUCCESS != Status) || (REG_DWORD != dwType)) {
return FALSE;
}
*ppvValue = (PVOID) dwValue;
*pdwLength = dwLength;
return TRUE;
}
void
WriteRegistryKeyValues(
HKEY hCurrentKey,
DWORD NumberOfValues,
PREGENTRY pValues)
/*++
Routine Description:
This routine reads a bunch of values associated with a given key.
Arguments:
hCurrentKey - the key
NumberOfValues - the number of values
pValues - the array of values
Return Value:
None
--*/
{
DWORD i;
for (i = 0; i < NumberOfValues; i++)
{
DWORD dwType;
PVOID pvValue;
DWORD dwLength;
PCHAR pszKey;
pszKey = pValues[i].pszKey;
dwType = pValues[i].dwType;
dwLength = pValues[i].dwLength;
pvValue = pValues[i].pvValue;
switch (dwType)
{
case REG_SZ:
(void) AddValue(hCurrentKey, pszKey, dwType, dwLength, pvValue);
break;
case REG_DWORD:
(void) AddValue(hCurrentKey, pszKey, dwType, dwLength, &pvValue);
break;
case REG_EXPAND_SZ:
(void) AddValue(hCurrentKey, pszKey, dwType, dwLength, pvValue);
break;
case REG_MULTI_SZ:
(void) AddValue(hCurrentKey, pszKey, dwType, dwLength, pvValue);
break;
case REG_BINARY:
//
// There are no binary values we need to copy. If we did, we'd
// put something here
//
break;
default:
printf("%s is an unknown type; %d (decimal)\n", pszKey, dwType);
break;
}
}
}
//
// Open a key so we can read the values
//
BOOL OpenKey(
PCHAR pszKey,
PHKEY phKey)
/*++
Routine Description:
This routine opens a registry key.
Arguments:
pszKey - the name of the key relative to HKEY_LOCAL_MACHINE
phKey - the key handlle
Return Value:
TRUE if successful, otherwise FALSE
--*/
{
HKEY hNewKey = 0;
DWORD Status;
Status = RegOpenKeyEx(
HKEY_LOCAL_MACHINE,
pszKey,
0,
KEY_QUERY_VALUE,
&hNewKey);
if (ERROR_SUCCESS != Status)
{
*phKey = NULL;
return FALSE;
}
else
{
*phKey = hNewKey;
return TRUE;
}
}
BOOL CreateKey(PCHAR pszKey, PHKEY phKey)
/*++
Routine Description:
This routine creates a registry key.
Arguments:
pszKey - the name of the key relative to HKEY_LOCAL_MACHINE
phKey - the key handlle
Return Value:
TRUE if successful, otherwise FALSE
--*/
{
LONG Status;
DWORD Disposition;
Status = RegCreateKeyEx(
HKEY_LOCAL_MACHINE, // hkey
pszKey, // subkey
0, // reserved
REG_NONE, // class
REG_OPTION_NON_VOLATILE, // options
KEY_ALL_ACCESS, // sam desired
NULL, // security structure
phKey, // address of new key buf
&Disposition); // status
if ( ERROR_SUCCESS == Status)
{
return TRUE;
} else {
printf("error creating key %s Status %d\n",pszKey,Status);
return FALSE;
}
}
//
// Add a value to the registry
//
BOOL AddValue(HKEY hKey, PCHAR pszKey, DWORD dwType, DWORD dwLength, PVOID pvValue)
{
BOOL fSuccess = TRUE;
LONG Status = ERROR_SUCCESS;
Status = RegSetValueEx(
hKey,
pszKey,
0,
dwType,
pvValue,
dwLength);
if (Status != ERROR_SUCCESS)
{
fSuccess = FALSE;
RegCloseKey(hKey);
}
return fSuccess;
}