720 lines
17 KiB
C++
720 lines
17 KiB
C++
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
|
|
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
|
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
|
|
// PARTICULAR PURPOSE.
|
|
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved
|
|
|
|
#include "resource.h"
|
|
#include "shvui.h"
|
|
#include "shvuicf.h"
|
|
#include "regutil.h"
|
|
#include "registration.h"
|
|
#include "DebugHelper.h"
|
|
#include <new>
|
|
|
|
SHVUIClassFactoryData* g_FactoryData = NULL;
|
|
HWND g_hwndDlg = NULL;
|
|
HINSTANCE g_hInst = NULL;
|
|
|
|
// Cache of the remote configuration
|
|
// or non-default configuration (multi-config)
|
|
BYTE g_config = 0;
|
|
|
|
//
|
|
// g_fConfigModeMemoryOnly indicates that we are on remote configuration
|
|
// or non-default configuration (multi-config).
|
|
// Hence instead of reading the configuration settings from registry, read it
|
|
// from g_config, which a cache of the configuration result,
|
|
// and the caller (NPS UI) will call SetConfig() or SetConfigToID() to persist it.
|
|
|
|
// When configuring the remote machine, the local machine invokes
|
|
// INapComponentConfig::GetConfig() on remote machine to obtain the remote
|
|
// machine configuration, it then invokes local UI by calling
|
|
// INapComponentConfig2::InvokeUIFromConfigBlob() and obtains user's changes.
|
|
// It sets the new changes to remote machine using
|
|
// INapComponentConfig::SetConfig().
|
|
//
|
|
|
|
// When configuring a multi-config configuration, the scenario is similar to
|
|
// the remote configuration. The only difference is that
|
|
// INapComponentConfig::GetConfig() is replaced with INapComponentConfig3::GetConfigFromID()
|
|
// and INapComponentConfig::SetConfig() is replaced with INapComponentConfig3::SetConfigToID()
|
|
//
|
|
BOOL g_fConfigModeMemoryOnly = FALSE;
|
|
|
|
INT_PTR CALLBACK DlgProcSHVPage(
|
|
_In_ HWND hDlg,
|
|
_In_ UINT message,
|
|
_In_ WPARAM wParam,
|
|
_In_ LPARAM //lParam
|
|
);
|
|
|
|
// Caller needs to pass in an array of size of MAX_PATH or bigger to use this function safely.
|
|
void AssembleRegkeyPathWithID (_Out_writes_(MAX_PATH) LPTSTR pConfigRegkeyWithID, _In_ UINT32 uConfigID);
|
|
|
|
int __stdcall wWinMain(
|
|
_In_ HINSTANCE hInst,
|
|
_In_opt_ HINSTANCE, // unreferenced hPrevInstance
|
|
_In_ LPWSTR lpCmdLine,
|
|
_In_ int // unreferenced nShowCmd
|
|
)
|
|
{
|
|
BOOL bExit = FALSE ;
|
|
BOOL bShow = TRUE;
|
|
BOOL bCOMInvoked = FALSE;
|
|
|
|
int retCode = 0;
|
|
BOOL bIsCOMInitialized = FALSE;
|
|
|
|
WCHAR command[64];
|
|
int nMax = sizeof(command)/sizeof(WCHAR);
|
|
|
|
g_hInst = hInst;
|
|
|
|
g_FactoryData = new (std::nothrow) SHVUIClassFactoryData();
|
|
if (g_FactoryData == NULL)
|
|
{
|
|
DebugPrintfW(L" --- wWinMain - Out of memory");
|
|
return FALSE;
|
|
}
|
|
|
|
HRESULT hr = CoInitialize(NULL) ;
|
|
if (FAILED(hr))
|
|
{
|
|
DebugPrintfW(L" --- wWinMain - CoInitialize failed %#x!",hr);
|
|
goto Cleanup;
|
|
}
|
|
|
|
bIsCOMInitialized = TRUE;
|
|
|
|
// Check for "/RegServer" or "/UnRegServer" parameters.
|
|
LPWSTR tokens = L"-/" ;
|
|
LPWSTR nextToken = NULL;
|
|
LPWSTR oneToken = wcstok_s(lpCmdLine, tokens, &nextToken) ;
|
|
while (oneToken != NULL)
|
|
{
|
|
int ret = LoadString(hInst, IDS_CMD_EMBEDDED, command, nMax);
|
|
if (ret == 0)
|
|
{
|
|
DebugPrintfW(L" --- wWinMain - LoadString failed for IDS_CMD_EMBEDDED");
|
|
goto Cleanup;
|
|
}
|
|
if (_wcsicmp(oneToken, command) == 0)
|
|
{
|
|
//Called through INapComponentConfig. Hide until InvokeUI() call.
|
|
bCOMInvoked = TRUE;
|
|
bShow = FALSE;
|
|
}
|
|
ret = LoadString(hInst, IDS_CMD_UNREGSERVER, command, nMax);
|
|
if (ret == 0)
|
|
{
|
|
DebugPrintfW(L" --- wWinMain - LoadString failed for IDS_CMD_UNREGSERVER");
|
|
goto Cleanup;
|
|
}
|
|
if (_wcsicmp(oneToken, command) == 0)
|
|
{
|
|
ShvUIUnRegisterServer();
|
|
// We are done, so exit.
|
|
bExit = TRUE ;
|
|
}
|
|
ret = LoadString(hInst, IDS_CMD_REGSERVER, command, nMax);
|
|
if (ret == 0)
|
|
{
|
|
DebugPrintfW(L" --- wWinMain - LoadString failed for IDS_CMD_REGSERVER");
|
|
goto Cleanup;
|
|
}
|
|
if (_wcsicmp(oneToken, command) == 0)
|
|
{
|
|
ShvUIRegisterServer();
|
|
// We are done, so exit.
|
|
bExit = TRUE ;
|
|
}
|
|
|
|
oneToken = wcstok_s(NULL, tokens, &nextToken) ;
|
|
}
|
|
|
|
if (bExit)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
ShvUIClassFactory::StartFactory();
|
|
|
|
if (bCOMInvoked)
|
|
{
|
|
//Process / dispatch messages, allow system to wait for COM method calls
|
|
BOOL bContinue = TRUE;
|
|
DWORD result;
|
|
while (bContinue)
|
|
{
|
|
result = MsgWaitForMultipleObjects (
|
|
1,
|
|
NULL,
|
|
FALSE,
|
|
INFINITE,
|
|
QS_ALLINPUT);
|
|
switch (result)
|
|
{
|
|
case WAIT_OBJECT_0 + 1:
|
|
MSG msg;
|
|
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
|
|
{
|
|
if (msg.message == WM_QUIT)
|
|
{
|
|
bContinue = FALSE;
|
|
PostQuitMessage(0);
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
ShvUIClassFactory::StopFactory();
|
|
|
|
if (bIsCOMInitialized)
|
|
{
|
|
CoUninitialize();
|
|
}
|
|
|
|
if (g_FactoryData != NULL)
|
|
{
|
|
delete (g_FactoryData);
|
|
g_FactoryData = NULL;
|
|
}
|
|
|
|
return retCode ;
|
|
}
|
|
|
|
ShvUI::ShvUI() :
|
|
m_cRef(0)
|
|
{
|
|
InterlockedIncrement(&g_cObjRefCount);
|
|
}
|
|
|
|
ShvUI::~ShvUI()
|
|
{
|
|
InterlockedDecrement(&g_cObjRefCount);
|
|
|
|
//The only supported interface is destroyed...quit the application
|
|
PostQuitMessage(0);
|
|
}
|
|
|
|
|
|
//Implementation of IUnknown
|
|
|
|
STDMETHODIMP ShvUI::QueryInterface(
|
|
__RPC__in const IID& iid,
|
|
__RPC__out void** ppv)
|
|
{
|
|
if (iid == IID_IUnknown)
|
|
{
|
|
*ppv = static_cast<INapComponentConfig*>(this);
|
|
}
|
|
else if (iid == IID_INapComponentConfig)
|
|
{
|
|
*ppv = static_cast<INapComponentConfig*>(this);
|
|
}
|
|
else if (iid == IID_INapComponentConfig2)
|
|
{
|
|
*ppv = static_cast<INapComponentConfig2*>(this);
|
|
}
|
|
else if (iid == IID_INapComponentConfig3)
|
|
{
|
|
*ppv = static_cast<INapComponentConfig3*>(this);
|
|
}
|
|
else
|
|
{
|
|
*ppv = NULL;
|
|
return E_NOINTERFACE;
|
|
}
|
|
reinterpret_cast<IUnknown*>(*ppv)->AddRef();
|
|
return S_OK;
|
|
}
|
|
|
|
ULONG ShvUI::AddRef()
|
|
{
|
|
return InterlockedIncrement(&m_cRef);
|
|
}
|
|
|
|
ULONG ShvUI::Release()
|
|
{
|
|
ULONG cRef = InterlockedDecrement(&m_cRef);
|
|
if (cRef == 0)
|
|
{
|
|
delete this;
|
|
}
|
|
return cRef;
|
|
}
|
|
|
|
|
|
// Implementation of INapComponentConfig
|
|
|
|
STDMETHODIMP ShvUI::IsUISupported(
|
|
__RPC__out BOOL *isSupported)
|
|
{
|
|
*isSupported = TRUE;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP ShvUI::InvokeUI(
|
|
__RPC__in_opt HWND hwndParent)
|
|
{
|
|
DialogBox(
|
|
g_hInst,
|
|
MAKEINTRESOURCE(IDD_SDKSHV_DIALOG),
|
|
hwndParent,
|
|
DlgProcSHVPage
|
|
);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
STDMETHODIMP ShvUI::GetConfig(
|
|
_Out_ UINT16 *bCount,
|
|
_Outptr_result_buffer_all_(*bCount) BYTE** data)
|
|
{
|
|
if (!bCount || !data)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
DWORD result = ERROR_SUCCESS;
|
|
HRESULT hr = S_OK;
|
|
PVOID isChecked = NULL;
|
|
|
|
result = ShvuiGetRegistryValue(
|
|
HKEY_LOCAL_MACHINE,
|
|
SDKSHV_DEFAULT_CONFIG_KEY,
|
|
SHV_CONFIG_BLOB,
|
|
REG_DWORD,
|
|
&isChecked);
|
|
|
|
*bCount = static_cast<UINT16>(sizeof(BYTE));
|
|
*data = static_cast<BYTE*>(CoTaskMemAlloc(sizeof(BYTE)));
|
|
if (*data)
|
|
{
|
|
if (result == ERROR_SUCCESS)
|
|
{
|
|
**data = reinterpret_cast<BYTE>(isChecked);
|
|
}
|
|
else
|
|
{
|
|
**data = TRUE;
|
|
hr = __HRESULT_FROM_WIN32(result);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
STDMETHODIMP ShvUI::SetConfig(
|
|
UINT16 bCount,
|
|
__RPC__in_ecount_full(bCount) BYTE* data)
|
|
{
|
|
if (!data)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (bCount == sizeof(g_config))
|
|
{
|
|
// set the cached data
|
|
g_config = *data;
|
|
|
|
// commit the cache
|
|
BOOL isChecked = g_config;
|
|
ShvuiSetRegistryValue(
|
|
SDKSHV_DEFAULT_CONFIG_KEY,
|
|
NULL,
|
|
SHV_CONFIG_BLOB,
|
|
REG_DWORD,
|
|
reinterpret_cast<PBYTE>(&isChecked),
|
|
sizeof(DWORD));
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
// Implementation of INapComponentConfig2
|
|
|
|
STDMETHODIMP ShvUI::IsRemoteConfigSupported(
|
|
__RPC__out BOOL* isSupported,
|
|
__RPC__out UINT8* remoteConfigType)
|
|
{
|
|
*isSupported = TRUE;
|
|
*remoteConfigType = remoteConfigTypeConfigBlob;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP ShvUI::InvokeUIForMachine(
|
|
__RPC__in_opt HWND hwndParent,
|
|
__RPC__in_opt CountedString* machineName)
|
|
{
|
|
WCHAR command[64];
|
|
int nMax = sizeof(command)/sizeof(WCHAR);
|
|
LoadString(GetModuleHandle(NULL), IDS_INVOKE_UI, command, nMax);
|
|
|
|
MessageBox(hwndParent, command, machineName->string, MB_OK);
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP ShvUI::InvokeUIFromConfigBlob(
|
|
__RPC__in_opt HWND hwndParent,
|
|
__RPC__in UINT16 inbCount,
|
|
__RPC__in_ecount_full(inbCount) BYTE *inData,
|
|
__RPC__out UINT16 *outbCount,
|
|
_Outptr_result_buffer_all_(*outbCount) BYTE **outdata,
|
|
__RPC__out BOOL *fConfigChanged)
|
|
{
|
|
HRESULT result = S_OK;
|
|
|
|
if (inbCount != sizeof(g_config))
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
g_fConfigModeMemoryOnly = TRUE;
|
|
|
|
//
|
|
// We should have set it TRUE only if the results before and after are different.
|
|
// To keep it simple, set it TRUE without comparison.
|
|
//
|
|
*fConfigChanged = TRUE;
|
|
|
|
g_config = *inData;
|
|
|
|
DialogBox(
|
|
g_hInst,
|
|
MAKEINTRESOURCE(IDD_SDKSHV_DIALOG),
|
|
hwndParent,
|
|
DlgProcSHVPage
|
|
);
|
|
|
|
*outbCount = static_cast<UINT16>(inbCount);
|
|
*outdata = static_cast<BYTE*>(CoTaskMemAlloc(inbCount));
|
|
if (*outdata)
|
|
{
|
|
**outdata = g_config;
|
|
}
|
|
else
|
|
{
|
|
result = E_OUTOFMEMORY;
|
|
}
|
|
|
|
g_fConfigModeMemoryOnly = FALSE;
|
|
return result;
|
|
}
|
|
|
|
|
|
// Implementation of INapComponentConfig3
|
|
|
|
STDMETHODIMP ShvUI::NewConfig(
|
|
UINT32 configID)
|
|
{
|
|
// We have no action to take, and we act when SetConfigToID is called.
|
|
UNREFERENCED_PARAMETER(configID);
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP ShvUI::DeleteConfig(
|
|
UINT32 configID)
|
|
{
|
|
WCHAR configRegkeyWithID[MAX_PATH] = {0};
|
|
AssembleRegkeyPathWithID(configRegkeyWithID, configID);
|
|
ShvuiDeleteRegistryKey(configRegkeyWithID);
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP ShvUI::DeleteAllConfig()
|
|
{
|
|
ShvuiDeleteRegistryKey(SDKSHV_MULTI_CONFIG_KEY);
|
|
return S_OK;
|
|
}
|
|
|
|
_Success_(return == 0)
|
|
STDMETHODIMP ShvUI::GetConfigFromID(
|
|
_In_ UINT32 configID,
|
|
_Out_ UINT16 *bCount,
|
|
_Outptr_result_buffer_all_(*bCount) BYTE **outdata)
|
|
{
|
|
if (!bCount || !outdata)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (configID == DEFAULT_CONFIGURATION_ID)
|
|
{
|
|
return GetConfig(bCount, outdata);
|
|
}
|
|
|
|
HRESULT result = ERROR_SUCCESS;
|
|
PVOID isChecked = NULL;
|
|
|
|
WCHAR configRegkeyWithID[MAX_PATH] = {0};
|
|
AssembleRegkeyPathWithID(configRegkeyWithID, configID);
|
|
|
|
result = ShvuiGetRegistryValue(
|
|
HKEY_LOCAL_MACHINE,
|
|
configRegkeyWithID,
|
|
SHV_CONFIG_BLOB,
|
|
REG_DWORD,
|
|
&isChecked);
|
|
|
|
*bCount = static_cast<UINT16>(sizeof(BYTE));
|
|
*outdata = static_cast<BYTE*>(CoTaskMemAlloc(sizeof(BYTE)));
|
|
if (*outdata)
|
|
{
|
|
**outdata = (result == ERROR_SUCCESS) ? reinterpret_cast<BYTE>(isChecked) : TRUE;
|
|
}
|
|
else
|
|
{
|
|
result = E_OUTOFMEMORY;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
STDMETHODIMP ShvUI::SetConfigToID(
|
|
UINT32 configID,
|
|
UINT16 bCount,
|
|
__RPC__in_ecount_full(bCount) BYTE *outdata)
|
|
{
|
|
if (!outdata)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (configID == DEFAULT_CONFIGURATION_ID)
|
|
{
|
|
return SetConfig(bCount, outdata);
|
|
}
|
|
|
|
WCHAR configRegkeyWithID[MAX_PATH] = {0};
|
|
AssembleRegkeyPathWithID(configRegkeyWithID, configID);
|
|
|
|
if (bCount == sizeof(g_config))
|
|
{
|
|
// commit the cache
|
|
BOOL isChecked = *outdata;
|
|
ShvuiSetRegistryValue(
|
|
configRegkeyWithID,
|
|
NULL,
|
|
SHV_CONFIG_BLOB,
|
|
REG_DWORD,
|
|
reinterpret_cast<PBYTE>(&isChecked),
|
|
sizeof(DWORD)
|
|
);
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
DWORD ProcessCheckBox(
|
|
_In_ HWND hDlg,
|
|
_In_ int nIDCheckBox)
|
|
{
|
|
DWORD result = ERROR_SUCCESS;
|
|
BOOL isChecked = FALSE;
|
|
|
|
isChecked = (BST_CHECKED == (BOOL)IsDlgButtonChecked(
|
|
hDlg,
|
|
nIDCheckBox
|
|
));
|
|
if(nIDCheckBox == IDC_SDKSHV_FW)
|
|
{
|
|
if (!g_fConfigModeMemoryOnly)
|
|
{
|
|
result = ShvuiSetRegistryValue(
|
|
SDKSHV_DEFAULT_CONFIG_KEY,
|
|
NULL,
|
|
SHV_CONFIG_BLOB,
|
|
REG_DWORD,
|
|
reinterpret_cast<PBYTE>(&isChecked),
|
|
sizeof(DWORD));
|
|
}
|
|
else
|
|
{
|
|
if(isChecked)
|
|
{
|
|
g_config = 1;
|
|
}
|
|
else
|
|
{
|
|
g_config = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
DWORD OnClickOk(
|
|
_In_ HWND hDlg)
|
|
{
|
|
DWORD result = ERROR_SUCCESS;
|
|
|
|
result = ProcessCheckBox(
|
|
hDlg,
|
|
IDC_SDKSHV_FW);
|
|
DEBUGLOGRESULT(L"OnClickOk-ProcessCheckBox(IDC_SHV_FW)", result);
|
|
|
|
cleanup:
|
|
return result;
|
|
}
|
|
|
|
|
|
DWORD InitializeCheckBoxes(
|
|
_In_ HWND hDlg)
|
|
{
|
|
DWORD result = ERROR_SUCCESS;
|
|
DWORD isChecked = FALSE;
|
|
DWORD data = 0;
|
|
|
|
if (g_fConfigModeMemoryOnly)
|
|
{
|
|
isChecked = g_config;
|
|
}
|
|
else
|
|
{
|
|
result = ShvuiGetRegistryValue(
|
|
HKEY_LOCAL_MACHINE,
|
|
SDKSHV_DEFAULT_CONFIG_KEY,
|
|
SHV_CONFIG_BLOB,
|
|
REG_DWORD,
|
|
(PVOID *)(&data));
|
|
if (result == ERROR_SUCCESS)
|
|
{
|
|
isChecked = data;
|
|
}
|
|
}
|
|
|
|
// if the value is not present, we will try and set a default value of 1
|
|
if (result == ERROR_FILE_NOT_FOUND)
|
|
{
|
|
isChecked = TRUE;
|
|
result = ShvuiSetRegistryValue(
|
|
SDKSHV_DEFAULT_CONFIG_KEY,
|
|
NULL,
|
|
SHV_CONFIG_BLOB,
|
|
REG_DWORD,
|
|
reinterpret_cast<PBYTE>(&isChecked),
|
|
sizeof(DWORD));
|
|
|
|
DEBUGLOGRESULT(L"SetCheckBox-ShvuiSetRegistryValue", result);
|
|
}
|
|
else
|
|
{
|
|
DEBUGLOGRESULT(L"SetCheckBox-ShvuiGetRegistryValue", result);
|
|
}
|
|
|
|
if (isChecked == TRUE)
|
|
{
|
|
if (FALSE == CheckDlgButton(
|
|
hDlg,
|
|
IDC_SDKSHV_FW,
|
|
(isChecked == TRUE) ? BST_CHECKED : BST_UNCHECKED
|
|
))
|
|
{
|
|
result = GetLastError();
|
|
}
|
|
}
|
|
|
|
cleanup:
|
|
return result;
|
|
|
|
}
|
|
|
|
static BOOLEAN bInitializing = TRUE;
|
|
|
|
// DlgProc
|
|
INT_PTR CALLBACK DlgProcSHVPage(
|
|
_In_ HWND hDlg,
|
|
_In_ UINT message,
|
|
_In_ WPARAM wParam,
|
|
_In_ LPARAM /*lParam*/)
|
|
{
|
|
DWORD result = ERROR_SUCCESS;
|
|
|
|
//
|
|
// process message
|
|
//
|
|
switch(message)
|
|
{
|
|
|
|
case WM_INITDIALOG:
|
|
// read from the registries and set the check boxes as appropriate
|
|
InitializeCheckBoxes(hDlg);
|
|
bInitializing = FALSE;
|
|
return TRUE;
|
|
|
|
case WM_COMMAND:
|
|
|
|
switch(LOWORD(wParam))
|
|
{
|
|
//Ignore check-boxes, these are processed in OnClickOK()
|
|
case IDC_SDKSHV_FW:
|
|
return TRUE;
|
|
|
|
case IDOK:
|
|
result = OnClickOk(hDlg);
|
|
if(ERROR_SUCCESS == result)
|
|
{
|
|
EndDialog(hDlg, 0);
|
|
}
|
|
return TRUE;
|
|
|
|
case IDAPPLY:
|
|
OnClickOk(hDlg);
|
|
break;
|
|
|
|
case IDCANCEL:
|
|
// end the dialog
|
|
EndDialog(hDlg, 0);
|
|
return TRUE;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case WM_QUIT:
|
|
// end the dialog
|
|
EndDialog(hDlg, 0);
|
|
return TRUE;
|
|
default:
|
|
break;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
void AssembleRegkeyPathWithID (
|
|
_Out_writes_(MAX_PATH) LPTSTR pconfigRegkeyWithID,
|
|
_In_ UINT32 uConfigID)
|
|
{
|
|
ZeroMemory(pconfigRegkeyWithID, MAX_PATH);
|
|
WCHAR configID[20] = {0};
|
|
StringCchPrintf (configID, 20, L"\\%u", uConfigID);
|
|
StringCchCopy (pconfigRegkeyWithID, MAX_PATH, SDKSHV_MULTI_CONFIG_KEY);
|
|
StringCchCat (pconfigRegkeyWithID, MAX_PATH, configID);
|
|
}
|
|
|