553 lines
14 KiB
C++
553 lines
14 KiB
C++
|
#include "mslocusr.h"
|
||
|
#include "msluglob.h"
|
||
|
#include <buffer.h>
|
||
|
#include <regentry.h>
|
||
|
#include "profiles.h"
|
||
|
|
||
|
extern "C" {
|
||
|
#include "netmpr.h"
|
||
|
};
|
||
|
|
||
|
#include <ole2.h>
|
||
|
|
||
|
CLUUser::CLUUser(CLUDatabase *pDB)
|
||
|
: m_cRef(1),
|
||
|
m_hkeyDB(NULL),
|
||
|
m_hkeyUser(NULL),
|
||
|
m_fUserExists(FALSE),
|
||
|
m_fAppearsSupervisor(FALSE),
|
||
|
m_fLoadedProfile(FALSE),
|
||
|
m_nlsUsername(),
|
||
|
m_nlsDir(MAX_PATH),
|
||
|
m_nlsPassword(),
|
||
|
m_fAuthenticated(FALSE),
|
||
|
m_pDB(pDB)
|
||
|
{
|
||
|
/* We have a reference to the database so we can get back to its idea
|
||
|
* of the current user. We handle circular refcount problems specifically
|
||
|
* in CLUDatabase::Release; the database only has one reference to an
|
||
|
* IUser, so if his refcount gets down to 1, he releases his cached
|
||
|
* current-user object.
|
||
|
*/
|
||
|
m_pDB->AddRef();
|
||
|
|
||
|
RefThisDLL(TRUE);
|
||
|
}
|
||
|
|
||
|
|
||
|
CLUUser::~CLUUser(void)
|
||
|
{
|
||
|
if (m_hkeyDB != NULL)
|
||
|
RegCloseKey(m_hkeyDB);
|
||
|
|
||
|
if (m_hkeyUser != NULL)
|
||
|
RegCloseKey(m_hkeyUser);
|
||
|
|
||
|
if (m_pDB != NULL)
|
||
|
m_pDB->Release();
|
||
|
|
||
|
RefThisDLL(FALSE);
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT CLUUser::Init(LPCSTR pszUsername)
|
||
|
{
|
||
|
m_nlsUsername = pszUsername;
|
||
|
|
||
|
UINT err = m_nlsUsername.QueryError();
|
||
|
if (err != ERROR_SUCCESS)
|
||
|
return HRESULT_FROM_WIN32(err);
|
||
|
|
||
|
err = m_nlsDir.QueryError();
|
||
|
if (err != ERROR_SUCCESS)
|
||
|
return HRESULT_FROM_WIN32(err);
|
||
|
|
||
|
err = (UINT)RegOpenKey(HKEY_LOCAL_MACHINE, ::szProfileList, &m_hkeyDB);
|
||
|
if (err != ERROR_SUCCESS)
|
||
|
return HRESULT_FROM_WIN32(err);
|
||
|
|
||
|
if (!::strcmpf(pszUsername, ::szDefaultUserName)) {
|
||
|
m_fUserExists = TRUE;
|
||
|
m_fAppearsSupervisor = FALSE;
|
||
|
}
|
||
|
else {
|
||
|
err = (UINT)RegOpenKey(m_hkeyDB, pszUsername, &m_hkeyUser);
|
||
|
if (err != ERROR_SUCCESS) {
|
||
|
m_hkeyUser = NULL;
|
||
|
m_fUserExists = FALSE;
|
||
|
}
|
||
|
else {
|
||
|
DWORD cb = sizeof(m_fAppearsSupervisor);
|
||
|
if (RegQueryValueEx(m_hkeyUser, ::szSupervisor, NULL, NULL,
|
||
|
(LPBYTE)&m_fAppearsSupervisor, &cb) != ERROR_SUCCESS) {
|
||
|
m_fAppearsSupervisor = FALSE;
|
||
|
}
|
||
|
DWORD cbDir = m_nlsDir.QueryAllocSize();
|
||
|
LPBYTE pbDir = (LPBYTE)m_nlsDir.Party();
|
||
|
err = RegQueryValueEx(m_hkeyUser, ::szProfileImagePath, NULL, NULL,
|
||
|
pbDir, &cbDir);
|
||
|
if (err != ERROR_SUCCESS)
|
||
|
*pbDir = '\0';
|
||
|
m_nlsDir.DonePartying();
|
||
|
m_fUserExists = (err == ERROR_SUCCESS);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return NOERROR;
|
||
|
}
|
||
|
|
||
|
|
||
|
STDMETHODIMP CLUUser::QueryInterface(REFIID riid, LPVOID * ppvObj)
|
||
|
{
|
||
|
if (!IsEqualIID(riid, IID_IUnknown) &&
|
||
|
!IsEqualIID(riid, IID_IUser)) {
|
||
|
*ppvObj = NULL;
|
||
|
return ResultFromScode(E_NOINTERFACE);
|
||
|
}
|
||
|
|
||
|
*ppvObj = this;
|
||
|
AddRef();
|
||
|
return NOERROR;
|
||
|
}
|
||
|
|
||
|
|
||
|
STDMETHODIMP_(ULONG) CLUUser::AddRef(void)
|
||
|
{
|
||
|
return ++m_cRef;
|
||
|
}
|
||
|
|
||
|
|
||
|
STDMETHODIMP_(ULONG) CLUUser::Release(void)
|
||
|
{
|
||
|
ULONG cRef;
|
||
|
|
||
|
cRef = --m_cRef;
|
||
|
|
||
|
if (0L == m_cRef) {
|
||
|
delete this;
|
||
|
}
|
||
|
|
||
|
return cRef;
|
||
|
}
|
||
|
|
||
|
|
||
|
STDMETHODIMP CLUUser::GetName(LPSTR pbBuffer, LPDWORD pcbBuffer)
|
||
|
{
|
||
|
if (m_nlsUsername.QueryError())
|
||
|
return ResultFromScode(E_OUTOFMEMORY);
|
||
|
|
||
|
UINT err = NPSCopyNLS(&m_nlsUsername, pbBuffer, pcbBuffer);
|
||
|
|
||
|
return HRESULT_FROM_WIN32(err);
|
||
|
}
|
||
|
|
||
|
|
||
|
STDMETHODIMP CLUUser::GetProfileDirectory(LPSTR pbBuffer, LPDWORD pcbBuffer)
|
||
|
{
|
||
|
if (m_nlsDir.QueryError())
|
||
|
return ResultFromScode(E_OUTOFMEMORY);
|
||
|
|
||
|
if (!m_nlsDir.strlen())
|
||
|
return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
|
||
|
|
||
|
UINT err = NPSCopyNLS(&m_nlsDir, pbBuffer, pcbBuffer);
|
||
|
|
||
|
return HRESULT_FROM_WIN32(err);
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL CLUUser::IsSystemCurrentUser(void)
|
||
|
{
|
||
|
NLS_STR nlsSystemUsername(MAX_PATH);
|
||
|
if (nlsSystemUsername.QueryError() == ERROR_SUCCESS) {
|
||
|
if (SUCCEEDED(GetSystemCurrentUser(&nlsSystemUsername)) &&
|
||
|
!m_nlsUsername.stricmp(nlsSystemUsername)) {
|
||
|
return TRUE;
|
||
|
}
|
||
|
}
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT CLUUser::GetSupervisorPassword(BUFFER *pbufOut)
|
||
|
{
|
||
|
LPSTR pBuffer = (LPSTR)pbufOut->QueryPtr();
|
||
|
|
||
|
if (!m_fAuthenticated) {
|
||
|
if (IsSystemCurrentUser()) {
|
||
|
WORD cbBuffer = (WORD)pbufOut->QuerySize();
|
||
|
APIERR err = WNetGetCachedPassword((LPSTR)::szSupervisorPWLKey,
|
||
|
(WORD)::strlenf(::szSupervisorPWLKey),
|
||
|
pBuffer,
|
||
|
&cbBuffer,
|
||
|
PCE_MISC);
|
||
|
if (err == ERROR_SUCCESS)
|
||
|
return S_OK;
|
||
|
if (err == WN_CANCEL)
|
||
|
return S_FALSE;
|
||
|
return HRESULT_FROM_WIN32(err);
|
||
|
}
|
||
|
return HRESULT_FROM_WIN32(ERROR_NOT_AUTHENTICATED);
|
||
|
}
|
||
|
|
||
|
HPWL hPWL;
|
||
|
HRESULT hres = GetPasswordCache(m_nlsPassword.QueryPch(), &hPWL);
|
||
|
if (FAILED(hres))
|
||
|
return hres;
|
||
|
|
||
|
APIERR err = FindCacheResource(hPWL, ::szSupervisorPWLKey,
|
||
|
(WORD)::strlenf(::szSupervisorPWLKey),
|
||
|
pBuffer,
|
||
|
(WORD)pbufOut->QuerySize(),
|
||
|
PCE_MISC);
|
||
|
::ClosePasswordCache(hPWL, TRUE);
|
||
|
|
||
|
if (err == IERR_CacheEntryNotFound)
|
||
|
return S_FALSE;
|
||
|
else if (err != NOERROR)
|
||
|
return HRESULT_FROM_WIN32(err);
|
||
|
|
||
|
CACHE_ENTRY_INFO *pcei = (CACHE_ENTRY_INFO *)pBuffer;
|
||
|
::memmovef(pBuffer, pBuffer + pcei->dchPassword, pcei->cbPassword);
|
||
|
|
||
|
return NOERROR;
|
||
|
}
|
||
|
|
||
|
|
||
|
STDMETHODIMP CLUUser::IsSupervisor(void)
|
||
|
{
|
||
|
/* If the supervisor password is blank, then everybody's a supervisor */
|
||
|
if (::VerifySupervisorPassword(::szNULL) == S_OK)
|
||
|
return S_OK;
|
||
|
|
||
|
/* If temporary supervisor privilege has been granted to this user object,
|
||
|
* honor it.
|
||
|
*/
|
||
|
|
||
|
if (m_fTempSupervisor)
|
||
|
return S_OK;
|
||
|
|
||
|
BUFFER bufPCE(MAX_ENTRY_SIZE+2);
|
||
|
if (bufPCE.QueryPtr() == NULL)
|
||
|
return E_OUTOFMEMORY;
|
||
|
|
||
|
HRESULT hres = GetSupervisorPassword(&bufPCE);
|
||
|
|
||
|
if (hres != S_OK)
|
||
|
return hres;
|
||
|
|
||
|
return ::VerifySupervisorPassword((LPCSTR)bufPCE.QueryPtr());
|
||
|
}
|
||
|
|
||
|
|
||
|
APIERR MakeSupervisor(HPWL hPWL, LPCSTR pszSupervisorPassword)
|
||
|
{
|
||
|
#ifdef MSLOCUSR_USE_SUPERVISOR_PASSWORD
|
||
|
return ::AddCacheResource(hPWL,
|
||
|
::szSupervisorPWLKey,
|
||
|
::strlenf(::szSupervisorPWLKey),
|
||
|
pszSupervisorPassword,
|
||
|
::strlenf(pszSupervisorPassword)+1,
|
||
|
PCE_MISC, 0);
|
||
|
#else
|
||
|
return ERROR_SUCCESS;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
|
||
|
STDMETHODIMP CLUUser::SetSupervisorPrivilege(BOOL fMakeSupervisor, LPCSTR pszSupervisorPassword)
|
||
|
{
|
||
|
if (m_pDB == NULL)
|
||
|
return E_UNEXPECTED;
|
||
|
|
||
|
#ifndef MSLOCUSR_USE_SUPERVISOR_PASSWORD
|
||
|
|
||
|
/* Don't write stuff to the user's password cache if we're not doing any
|
||
|
* supervisor password stuff.
|
||
|
*/
|
||
|
m_fAppearsSupervisor = fMakeSupervisor;
|
||
|
return S_OK;
|
||
|
|
||
|
#else
|
||
|
|
||
|
BUFFER bufPCE(MAX_ENTRY_SIZE+2);
|
||
|
if (bufPCE.QueryPtr() == NULL)
|
||
|
return E_OUTOFMEMORY;
|
||
|
|
||
|
HRESULT hres = S_OK;
|
||
|
|
||
|
/* If supervisor password is provided by the caller, use that, otherwise
|
||
|
* inspect the current user's password cache.
|
||
|
*/
|
||
|
if (pszSupervisorPassword == NULL) {
|
||
|
IUser *pCurrentUser;
|
||
|
if (FAILED(m_pDB->GetCurrentUser(&pCurrentUser)))
|
||
|
return E_ACCESSDENIED;
|
||
|
|
||
|
hres = ((CLUUser *)pCurrentUser)->GetSupervisorPassword(&bufPCE);
|
||
|
pCurrentUser->Release();
|
||
|
pszSupervisorPassword = (LPCSTR)bufPCE.QueryPtr();
|
||
|
}
|
||
|
|
||
|
if (SUCCEEDED(hres)) {
|
||
|
hres = ::VerifySupervisorPassword(pszSupervisorPassword);
|
||
|
if (hres == S_OK) { /* not SUCCEEDED because S_FALSE means wrong PW */
|
||
|
HPWL hpwlThisUser;
|
||
|
hres = GetPasswordCache(m_nlsPassword.QueryPch(), &hpwlThisUser);
|
||
|
if (SUCCEEDED(hres)) {
|
||
|
APIERR err;
|
||
|
if (fMakeSupervisor)
|
||
|
{
|
||
|
err = ::MakeSupervisor(hpwlThisUser, pszSupervisorPassword);
|
||
|
}
|
||
|
else {
|
||
|
err = ::DeleteCacheResource(hpwlThisUser,
|
||
|
::szSupervisorPWLKey,
|
||
|
::strlenf(::szSupervisorPWLKey),
|
||
|
PCE_MISC);
|
||
|
}
|
||
|
::ClosePasswordCache(hpwlThisUser, TRUE);
|
||
|
|
||
|
hres = HRESULT_FROM_WIN32(err);
|
||
|
}
|
||
|
else if (!m_fAuthenticated && IsSystemCurrentUser()) {
|
||
|
APIERR err;
|
||
|
if (fMakeSupervisor) {
|
||
|
err = ::WNetCachePassword(
|
||
|
(LPSTR)::szSupervisorPWLKey,
|
||
|
::strlenf(::szSupervisorPWLKey),
|
||
|
(LPSTR)pszSupervisorPassword,
|
||
|
::strlenf(pszSupervisorPassword)+1,
|
||
|
PCE_MISC, 0);
|
||
|
}
|
||
|
else {
|
||
|
err = ::WNetRemoveCachedPassword(
|
||
|
(LPSTR)::szSupervisorPWLKey,
|
||
|
::strlenf(::szSupervisorPWLKey),
|
||
|
PCE_MISC);
|
||
|
}
|
||
|
hres = HRESULT_FROM_WIN32(err);
|
||
|
}
|
||
|
|
||
|
if (SUCCEEDED(hres)) {
|
||
|
m_fAppearsSupervisor = fMakeSupervisor;
|
||
|
if (m_hkeyUser != NULL)
|
||
|
RegSetValueEx(m_hkeyUser, ::szSupervisor, NULL,
|
||
|
REG_DWORD, (LPBYTE)&m_fAppearsSupervisor,
|
||
|
sizeof(m_fAppearsSupervisor));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return hres;
|
||
|
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
|
||
|
STDMETHODIMP CLUUser::MakeTempSupervisor(BOOL fMakeSupervisor, LPCSTR pszSupervisorPassword)
|
||
|
{
|
||
|
if (!fMakeSupervisor)
|
||
|
m_fTempSupervisor = FALSE;
|
||
|
else {
|
||
|
HRESULT hres = ::VerifySupervisorPassword(pszSupervisorPassword);
|
||
|
if (hres == S_FALSE)
|
||
|
hres = E_ACCESSDENIED;
|
||
|
if (FAILED(hres))
|
||
|
return hres;
|
||
|
|
||
|
m_fTempSupervisor = TRUE;
|
||
|
}
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
STDMETHODIMP CLUUser::AppearsSupervisor(void)
|
||
|
{
|
||
|
if (m_fTempSupervisor)
|
||
|
return S_OK;
|
||
|
|
||
|
return m_fAppearsSupervisor ? S_OK : S_FALSE;
|
||
|
}
|
||
|
|
||
|
|
||
|
STDMETHODIMP CLUUser::Authenticate(LPCSTR pszPassword)
|
||
|
{
|
||
|
HPWL hPWL = NULL;
|
||
|
|
||
|
HRESULT hres = GetPasswordCache(pszPassword, &hPWL);
|
||
|
if (FAILED(hres))
|
||
|
return hres;
|
||
|
|
||
|
::ClosePasswordCache(hPWL, TRUE);
|
||
|
|
||
|
return NOERROR;
|
||
|
}
|
||
|
|
||
|
|
||
|
STDMETHODIMP CLUUser::ChangePassword(LPCSTR pszOldPassword, LPCSTR pszNewPassword)
|
||
|
{
|
||
|
// if current user is supervisor, allow null pszOldPassword
|
||
|
|
||
|
NLS_STR nlsNewPassword(pszNewPassword);
|
||
|
if (nlsNewPassword.QueryError())
|
||
|
return HRESULT_FROM_WIN32(nlsNewPassword.QueryError());
|
||
|
nlsNewPassword.strupr();
|
||
|
nlsNewPassword.ToOEM();
|
||
|
|
||
|
HPWL hPWL;
|
||
|
|
||
|
HRESULT hres = GetPasswordCache(pszOldPassword, &hPWL);
|
||
|
|
||
|
if (FAILED(hres))
|
||
|
return hres;
|
||
|
|
||
|
hres = HRESULT_FROM_WIN32(::SetCachePassword(hPWL, nlsNewPassword.QueryPch()));
|
||
|
|
||
|
if (SUCCEEDED(hres)) {
|
||
|
m_nlsPassword = pszNewPassword; /* FEATURE - obfuscate me */
|
||
|
m_fAuthenticated = TRUE;
|
||
|
}
|
||
|
|
||
|
::ClosePasswordCache(hPWL, TRUE);
|
||
|
|
||
|
return hres;
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT GetUserPasswordCache(LPCSTR pszUsername, LPCSTR pszPassword, LPHANDLE phOut, BOOL fCreate)
|
||
|
{
|
||
|
NLS_STR nlsUsername(pszUsername);
|
||
|
if (nlsUsername.QueryError())
|
||
|
return HRESULT_FROM_WIN32(nlsUsername.QueryError());
|
||
|
|
||
|
nlsUsername.strupr();
|
||
|
nlsUsername.ToOEM();
|
||
|
|
||
|
NLS_STR nlsPassword(pszPassword);
|
||
|
if (nlsPassword.QueryError())
|
||
|
return HRESULT_FROM_WIN32(nlsPassword.QueryError());
|
||
|
|
||
|
nlsPassword.ToOEM();
|
||
|
|
||
|
*phOut = NULL;
|
||
|
UINT err = ::OpenPasswordCache(phOut, nlsUsername.QueryPch(), nlsPassword.QueryPch(), TRUE);
|
||
|
|
||
|
if (fCreate &&
|
||
|
(err == IERR_UsernameNotFound || err == ERROR_FILE_NOT_FOUND ||
|
||
|
err == ERROR_PATH_NOT_FOUND)) {
|
||
|
err = ::CreatePasswordCache(phOut, nlsUsername.QueryPch(), nlsPassword.QueryPch());
|
||
|
}
|
||
|
|
||
|
if (err == IERR_IncorrectUsername) {
|
||
|
nlsPassword.ToAnsi(); /* must convert to OEM to uppercase properly */
|
||
|
nlsPassword.strupr();
|
||
|
nlsPassword.ToOEM();
|
||
|
err = ::OpenPasswordCache(phOut, nlsUsername.QueryPch(), nlsPassword.QueryPch(), TRUE);
|
||
|
}
|
||
|
|
||
|
if (err)
|
||
|
return HRESULT_FROM_WIN32(err);
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
STDMETHODIMP CLUUser::GetPasswordCache(LPCSTR pszPassword, LPHANDLE phOut)
|
||
|
{
|
||
|
HRESULT hres = ::GetUserPasswordCache(m_nlsUsername.QueryPch(), pszPassword,
|
||
|
phOut, TRUE);
|
||
|
if (FAILED(hres))
|
||
|
return hres;
|
||
|
|
||
|
m_nlsPassword = pszPassword; /* FEATURE - obfuscate me */
|
||
|
m_fAuthenticated = TRUE;
|
||
|
|
||
|
return NOERROR;
|
||
|
}
|
||
|
|
||
|
|
||
|
STDMETHODIMP CLUUser::LoadProfile(HKEY *phkeyUser)
|
||
|
{
|
||
|
if (IsSystemCurrentUser() ||
|
||
|
!::strcmpf(m_nlsUsername.QueryPch(), ::szDefaultUserName)) {
|
||
|
/* If he's the current or default user, his profile should be loaded
|
||
|
* under HKEY_USERS. If it is, we can return that key. Otherwise,
|
||
|
* we'll need to load it.
|
||
|
*/
|
||
|
if (RegOpenKeyEx(HKEY_USERS, m_nlsUsername.QueryPch(), 0,
|
||
|
KEY_READ | KEY_WRITE, phkeyUser) == ERROR_SUCCESS) {
|
||
|
m_fLoadedProfile = FALSE;
|
||
|
return S_OK;
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
if (IsCurrentUserSupervisor(m_pDB) != S_OK)
|
||
|
return E_ACCESSDENIED;
|
||
|
}
|
||
|
|
||
|
RegEntry reRoot(::szProfileList, HKEY_LOCAL_MACHINE);
|
||
|
if (reRoot.GetError() != ERROR_SUCCESS)
|
||
|
return HRESULT_FROM_WIN32(reRoot.GetError());
|
||
|
|
||
|
reRoot.MoveToSubKey(m_nlsUsername.QueryPch());
|
||
|
if (reRoot.GetError() != ERROR_SUCCESS)
|
||
|
return HRESULT_FROM_WIN32(reRoot.GetError());
|
||
|
|
||
|
NLS_STR nlsProfilePath(MAX_PATH);
|
||
|
if (nlsProfilePath.QueryError() != ERROR_SUCCESS)
|
||
|
return E_OUTOFMEMORY;
|
||
|
|
||
|
reRoot.GetValue(::szProfileImagePath, &nlsProfilePath);
|
||
|
if (reRoot.GetError() != ERROR_SUCCESS)
|
||
|
return HRESULT_FROM_WIN32(reRoot.GetError());
|
||
|
|
||
|
AddBackslash(nlsProfilePath);
|
||
|
nlsProfilePath.strcat(::szStdNormalProfile);
|
||
|
if (!FileExists(nlsProfilePath.QueryPch()))
|
||
|
return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
|
||
|
|
||
|
LONG err = ::MyRegLoadKey(HKEY_USERS, m_nlsUsername.QueryPch(), nlsProfilePath.QueryPch());
|
||
|
if (err == ERROR_SUCCESS) {
|
||
|
HKEY hkeyNewProfile;
|
||
|
err = ::RegOpenKey(HKEY_USERS, m_nlsUsername.QueryPch(), phkeyUser);
|
||
|
if (err != ERROR_SUCCESS) {
|
||
|
::RegUnLoadKey(HKEY_USERS, m_nlsUsername.QueryPch());
|
||
|
}
|
||
|
else {
|
||
|
m_fLoadedProfile = TRUE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return HRESULT_FROM_WIN32(err);
|
||
|
}
|
||
|
|
||
|
|
||
|
STDMETHODIMP CLUUser::UnloadProfile(HKEY hkeyUser)
|
||
|
{
|
||
|
RegFlushKey(hkeyUser);
|
||
|
RegCloseKey(hkeyUser);
|
||
|
|
||
|
if (m_fLoadedProfile) {
|
||
|
RegUnLoadKey(HKEY_USERS, m_nlsUsername.QueryPch());
|
||
|
m_fLoadedProfile = FALSE;
|
||
|
}
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
STDMETHODIMP CLUUser::GetComponentSettings(REFCLSID clsidComponent,
|
||
|
LPCSTR pszName, IUnknown **ppOut,
|
||
|
DWORD fdwAccess)
|
||
|
{
|
||
|
return ResultFromScode(E_NOTIMPL);
|
||
|
}
|
||
|
|
||
|
|
||
|
STDMETHODIMP CLUUser::EnumerateComponentSettings(IEnumUnknown **ppOut,
|
||
|
DWORD fdwAccess)
|
||
|
{
|
||
|
return ResultFromScode(E_NOTIMPL);
|
||
|
}
|