//--------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1992 - 1996 // // File: cprinter.cxx // // Contents: // // History: 9-26-96 yihsins Created. // //---------------------------------------------------------------------------- #include "ldap.hxx" #pragma hdrstop HRESULT ChangeSeparator( LPWSTR pszDN ); HRESULT GetAuthIdentityForCaller( CCredentials& Credentials, IADs * pIADs, OUT SEC_WINNT_AUTH_IDENTITY *pAuthI, BOOL fEnforceMutualAuth ); BOOL ImpersonateWrapper( CCredentials& Credentials, IADs * pIADs, HANDLE* phUserToken ) { HRESULT hr = S_OK; SEC_WINNT_AUTH_IDENTITY AuthI; BOOL fImpersonating = FALSE; AuthI.User = NULL; AuthI.Domain = NULL; AuthI.Password = NULL; hr = GetAuthIdentityForCaller( Credentials, pIADs, &AuthI, FALSE ); BAIL_ON_FAILURE(hr); if (LogonUser( AuthI.User, AuthI.Domain, AuthI.Password, LOGON32_LOGON_NEW_CREDENTIALS, LOGON32_PROVIDER_WINNT50, phUserToken ) ) { // // Call succeeded so we should use this context. // if (ImpersonateLoggedOnUser(*phUserToken)) { fImpersonating = TRUE; } } error: if (AuthI.User) { FreeADsStr(AuthI.User); } if (AuthI.Domain) { FreeADsStr(AuthI.Domain); } if (AuthI.Password) { SecureZeroMemory(AuthI.Password, AuthI.PasswordLength*sizeof(WCHAR)); FreeADsStr(AuthI.Password); } return fImpersonating; } struct _propmap { LPTSTR pszADsProp; LPTSTR pszLDAPProp; } aPrintPropMapping[] = { { TEXT("Description"), TEXT("description") }, { TEXT("PrintDevices"), TEXT("PortName") }, { TEXT("Location"), TEXT("location") }, { TEXT("HostComputer"), TEXT("serverName") }, { TEXT("Model"), TEXT("DriverName") }, { TEXT("StartTime"), TEXT("PrintStartTime") }, { TEXT("UntilTime"), TEXT("PrintEndTime") }, { TEXT("Priority"), TEXT("Priority") }, { TEXT("BannerPage"), TEXT("PrintSeparatorfile") } // { TEXT("NetAddresses"), TEXT("PrintNetworkAddress") }, }; #define UNCNAME TEXT("uNCName") // // Class CLDAPPrintQueue // // IADsExtension::PrivateGetIDsOfNames()/Invoke(), Operate() not included DEFINE_IADsExtension_Implementation(CLDAPPrintQueue) DEFINE_IPrivateDispatch_Implementation(CLDAPPrintQueue) DEFINE_DELEGATING_IDispatch_Implementation(CLDAPPrintQueue) DEFINE_CONTAINED_IADs_Implementation(CLDAPPrintQueue) DEFINE_CONTAINED_IADsPutGet_Implementation(CLDAPPrintQueue,aPrintPropMapping) CLDAPPrintQueue::CLDAPPrintQueue(): _pUnkOuter(NULL), _pADs(NULL), _fDispInitialized(FALSE), _pDispMgr(NULL) { ENLIST_TRACKING(CLDAPPrintQueue); } CLDAPPrintQueue::~CLDAPPrintQueue() { delete _pDispMgr; } HRESULT CLDAPPrintQueue:: CreatePrintQueue( IUnknown *pUnkOuter, REFIID riid, LPVOID * ppvObj ) { CLDAPPrintQueue FAR * pPrintQueue = NULL; IADs FAR * pADs = NULL; CAggregateeDispMgr FAR * pDispMgr = NULL; HRESULT hr = S_OK; // // our extension object only works in a provider (aggregator) environment // environment // ASSERT(pUnkOuter); ASSERT(ppvObj); ASSERT(IsEqualIID(riid, IID_IUnknown)); pPrintQueue = new CLDAPPrintQueue(); if (pPrintQueue == NULL) { hr = E_OUTOFMEMORY; BAIL_ON_FAILURE(hr); } // // Reference Count = 1 from object tracker // // // CAggregateeDispMgr to handle // IADsExtension::PrivateGetIDsOfNames()/PrivatInovke() // pDispMgr = new CAggregateeDispMgr; if (pDispMgr == NULL) { hr = E_OUTOFMEMORY; BAIL_ON_FAILURE(hr); } pPrintQueue->_pDispMgr = pDispMgr; hr = pDispMgr->LoadTypeInfoEntry( LIBID_ADs, IID_IADsPrintQueue, (IADsPrintQueue *)pPrintQueue, DISPID_REGULAR ); BAIL_ON_FAILURE(hr); hr = pDispMgr->LoadTypeInfoEntry( LIBID_ADs, IID_IADsPrintQueueOperations, (IADsPrintQueueOperations *)pPrintQueue, DISPID_REGULAR ); BAIL_ON_FAILURE(hr); // // Store the pointer to the pUnkOuter object to delegate all IUnknown // calls to the aggregator AND DO NOT add ref this pointer // pPrintQueue->_pUnkOuter = pUnkOuter; // // Ccache pADs Pointer to delegate all IDispatch calls to // the aggregator. But release immediately to avoid the aggregatee // having a reference count on the aggregator -> cycle ref counting // hr = pUnkOuter->QueryInterface( IID_IADs, (void **)&pADs ); // // Our spec stated extesnion writers can expect the aggregator in our // provider ot support IDispatch. If not, major bug. // ASSERT(SUCCEEDED(hr)); pADs->Release(); // see doc above pUnkOuter->QI pPrintQueue->_pADs = pADs; // // pass non-delegating IUnknown back to the aggregator // *ppvObj = (INonDelegatingUnknown FAR *) pPrintQueue; RRETURN(hr); error: if (pPrintQueue) delete pPrintQueue; *ppvObj = NULL; RRETURN(hr); } /* IUnknown methods */ STDMETHODIMP CLDAPPrintQueue::QueryInterface( REFIID iid, LPVOID FAR* ppv ) { HRESULT hr = S_OK; hr = _pUnkOuter->QueryInterface(iid,ppv); RRETURN(hr); } STDMETHODIMP CLDAPPrintQueue::NonDelegatingQueryInterface( REFIID iid, LPVOID FAR* ppv ) { ASSERT(ppv); if (IsEqualIID(iid, IID_IADsPrintQueue)) { *ppv = (IADsPrintQueue FAR *) this; } else if (IsEqualIID(iid, IID_IADsPrintQueueOperations)) { *ppv = (IADsPrintQueueOperations FAR *) this; } else if (IsEqualIID(iid, IID_IADsExtension)) { *ppv = (IADsExtension FAR *) this; } else if (IsEqualIID(iid, IID_IUnknown)) { // // probably not needed since our 3rd party extension does not stand // alone and provider does not ask for this, but to be safe // *ppv = (INonDelegatingUnknown FAR *) this; } else { *ppv = NULL; return E_NOINTERFACE; } // // Delegating AddRef to aggregator for IADsExtesnion and. // AddRef on itself for IPrivateUnknown. (both tested.) // ((IUnknown *) (*ppv)) -> AddRef(); return S_OK; } /* IADs methods */ /* IADsPrintQueue methods */ STDMETHODIMP CLDAPPrintQueue::get_PrinterPath(THIS_ BSTR FAR* retval) { GET_PROPERTY_BSTR((IADsPrintQueue *)this, uNCName ); } STDMETHODIMP CLDAPPrintQueue::put_PrinterPath(THIS_ BSTR bstruNCName) { PUT_PROPERTY_BSTR((IADsPrintQueue*)this, uNCName); } STDMETHODIMP CLDAPPrintQueue::get_Model(THIS_ BSTR FAR* retval) { GET_PROPERTY_BSTR((IADsPrintQueue *)this, Model); } STDMETHODIMP CLDAPPrintQueue::put_Model(THIS_ BSTR bstrModel) { PUT_PROPERTY_BSTR((IADsPrintQueue *)this, Model); } STDMETHODIMP CLDAPPrintQueue::get_Datatype(THIS_ BSTR *retval) { RRETURN(E_ADS_PROPERTY_NOT_SUPPORTED); } STDMETHODIMP CLDAPPrintQueue::put_Datatype(THIS_ BSTR bstrDatatype) { RRETURN(E_ADS_PROPERTY_NOT_SUPPORTED); } STDMETHODIMP CLDAPPrintQueue::get_PrintProcessor(THIS_ BSTR FAR* retval) { RRETURN(E_ADS_PROPERTY_NOT_SUPPORTED); } STDMETHODIMP CLDAPPrintQueue::put_PrintProcessor(THIS_ BSTR bstrPrintProcessor) { RRETURN(E_ADS_PROPERTY_NOT_SUPPORTED); } STDMETHODIMP CLDAPPrintQueue::get_Description(THIS_ BSTR FAR* retval) { GET_PROPERTY_BSTR((IADsPrintQueue *)this, Description); } STDMETHODIMP CLDAPPrintQueue::put_Description(THIS_ BSTR bstrDescription) { PUT_PROPERTY_BSTR((IADsPrintQueue *)this, Description); } STDMETHODIMP CLDAPPrintQueue::get_Location(THIS_ BSTR FAR* retval) { GET_PROPERTY_BSTR((IADsPrintQueue *)this, Location); } STDMETHODIMP CLDAPPrintQueue::put_Location(THIS_ BSTR bstrLocation) { PUT_PROPERTY_BSTR((IADsPrintQueue *)this, Location); } STDMETHODIMP CLDAPPrintQueue::get_StartTime(THIS_ DATE FAR* retval) { GET_PROPERTY_LONGDATE((IADsPrintQueue *)this, StartTime); } STDMETHODIMP CLDAPPrintQueue::put_StartTime(THIS_ DATE daStartTime) { PUT_PROPERTY_LONGDATE((IADsPrintQueue *)this, StartTime); } STDMETHODIMP CLDAPPrintQueue::get_UntilTime(THIS_ DATE FAR* retval) { GET_PROPERTY_LONGDATE((IADsPrintQueue *)this, UntilTime); } STDMETHODIMP CLDAPPrintQueue::put_UntilTime(THIS_ DATE daUntilTime) { PUT_PROPERTY_LONGDATE((IADsPrintQueue *)this, UntilTime); } STDMETHODIMP CLDAPPrintQueue::get_DefaultJobPriority(THIS_ LONG FAR* retval) { RRETURN(E_ADS_PROPERTY_NOT_SUPPORTED); } STDMETHODIMP CLDAPPrintQueue::put_DefaultJobPriority(THIS_ LONG lDefaultJobPriority) { RRETURN(E_ADS_PROPERTY_NOT_SUPPORTED); } STDMETHODIMP CLDAPPrintQueue::get_Priority(THIS_ LONG FAR* retval) { GET_PROPERTY_LONG((IADsPrintQueue *)this, Priority); } STDMETHODIMP CLDAPPrintQueue::put_Priority(THIS_ LONG lPriority) { PUT_PROPERTY_LONG((IADsPrintQueue *)this, Priority); } STDMETHODIMP CLDAPPrintQueue::get_BannerPage(THIS_ BSTR FAR* retval) { GET_PROPERTY_BSTR((IADsPrintQueue *)this, BannerPage); } STDMETHODIMP CLDAPPrintQueue::put_BannerPage(THIS_ BSTR bstrBannerPage) { PUT_PROPERTY_BSTR((IADsPrintQueue *)this, BannerPage); } STDMETHODIMP CLDAPPrintQueue::get_PrintDevices(THIS_ VARIANT FAR* retval) { GET_PROPERTY_BSTRARRAY((IADsPrintQueue *)this,PrintDevices); } STDMETHODIMP CLDAPPrintQueue::put_PrintDevices(THIS_ VARIANT vPrintDevices) { PUT_PROPERTY_BSTRARRAY((IADsPrintQueue *)this,PrintDevices); } STDMETHODIMP CLDAPPrintQueue::get_NetAddresses(THIS_ VARIANT FAR* retval) { RRETURN(E_ADS_PROPERTY_NOT_SUPPORTED); } STDMETHODIMP CLDAPPrintQueue::put_NetAddresses(THIS_ VARIANT vNetAddresses) { RRETURN(E_ADS_PROPERTY_NOT_SUPPORTED); } /* IADsPrintQueueOperations methods */ STDMETHODIMP CLDAPPrintQueue::get_Status(THIS_ long FAR* retval) { BOOL fSuccess = FALSE; HRESULT hr = S_OK; DWORD dwStatus = 0; HANDLE hPrinter = NULL; BSTR bstrPath = NULL ; LPPRINTER_INFO_2 lpPrinterInfo2 = NULL; LPPRINTER_INFO_2 lpTempInfo = NULL; DWORD dwBufferSize = 1024, dwNeeded ; PRINTER_DEFAULTS PrinterDefaults = {0, 0, PRINTER_ACCESS_USE | READ_CONTROL}; // // get the 'Path' property // hr = get_BSTR_Property(this->_pADs, UNCNAME, &bstrPath) ; BAIL_IF_ERROR(hr); // // Do a GetPrinter call to bstrPath // fSuccess = OpenPrinter((LPTSTR)bstrPath, &hPrinter, &PrinterDefaults ); if (!fSuccess) { dwStatus = GetLastError(); if (dwStatus == ERROR_ACCESS_DENIED) { PrinterDefaults.DesiredAccess = PRINTER_ACCESS_USE ; fSuccess = OpenPrinter((LPTSTR)bstrPath, &hPrinter, &PrinterDefaults ); } } if (!fSuccess) { hr = HRESULT_FROM_WIN32(GetLastError()); BAIL_IF_ERROR(hr); } if (!(lpPrinterInfo2 = (LPPRINTER_INFO_2) AllocADsMem(dwBufferSize))) { hr = HRESULT_FROM_WIN32(GetLastError()) ; BAIL_IF_ERROR(hr); } fSuccess = GetPrinter(hPrinter, 2, (LPBYTE) lpPrinterInfo2, dwBufferSize, &dwNeeded); if (!fSuccess) { dwStatus = GetLastError() ; if (dwStatus == ERROR_INSUFFICIENT_BUFFER) { lpTempInfo = (LPPRINTER_INFO_2) ReallocADsMem( lpPrinterInfo2, dwBufferSize, dwNeeded) ; if (!lpTempInfo) { hr = HRESULT_FROM_WIN32(GetLastError()) ; BAIL_IF_ERROR(hr); } lpPrinterInfo2 = lpTempInfo; dwBufferSize = dwNeeded ; fSuccess = GetPrinter(hPrinter, 2, (LPBYTE) lpPrinterInfo2, dwBufferSize, &dwNeeded); } } if (!fSuccess) { hr = HRESULT_FROM_WIN32(GetLastError()); BAIL_IF_ERROR(hr); } *retval = lpPrinterInfo2->Status; cleanup: if (lpPrinterInfo2) { FreeADsMem((LPBYTE)lpPrinterInfo2); } if (hPrinter) { (void) ClosePrinter(hPrinter); } if(bstrPath) { SysFreeString(bstrPath); bstrPath = NULL; } RRETURN(hr); } STDMETHODIMP CLDAPPrintQueue::PrintJobs( THIS_ IADsCollection * FAR* ppCollection ) { // // The job collection object is created and it is passed the printer // name. It uses this to create a printer object // HRESULT hr = S_OK; BSTR bstrPath = NULL; WCHAR *pszADsPath = NULL; IADsPrintQueueOperations *pPrintQueueOps = NULL; LPWSTR pszUserName = NULL; LPWSTR pszPassword = NULL; DWORD dwAuthFlags = 0; hr = _Credentials.GetUserName(&pszUserName); BAIL_IF_ERROR(hr); hr = _Credentials.GetPassword(&pszPassword); BAIL_IF_ERROR(hr); dwAuthFlags = _Credentials.GetAuthFlags(); hr = get_BSTR_Property(_pADs, UNCNAME, &bstrPath) ; BAIL_IF_ERROR(hr); // // UNCName has '\' as separators. Convert them to '/'s. // hr = ChangeSeparator(bstrPath); BAIL_IF_ERROR(hr); pszADsPath = (LPWSTR) AllocADsMem( ( wcslen(TEXT("WinNT://")) + wcslen( bstrPath + 2) + 1 ) * sizeof(WCHAR)); if ( pszADsPath == NULL ) { hr = E_OUTOFMEMORY; BAIL_IF_ERROR(hr); } wcscpy(pszADsPath, L"WinNT://"); wcscat(pszADsPath, bstrPath+2); hr = ADsOpenObject( pszADsPath, pszUserName, pszPassword, dwAuthFlags, IID_IADsPrintQueueOperations, (void **)&pPrintQueueOps ); BAIL_IF_ERROR(hr); hr = pPrintQueueOps->PrintJobs(ppCollection); cleanup: if (pPrintQueueOps){ pPrintQueueOps->Release(); } if (bstrPath){ ADsFreeString(bstrPath); } if (pszADsPath){ FreeADsMem(pszADsPath); } if (pszPassword) { SecureZeroMemory(pszPassword, wcslen(pszPassword)*sizeof(WCHAR)); FreeADsStr(pszPassword); pszPassword = NULL; } if (pszUserName) { FreeADsStr(pszUserName); pszUserName = NULL; } RRETURN(hr); } //+------------------------------------------------------------------------ // // Function: CLDAPPrintQueue::Pause // // Synopsis: Binds to real printer as specified in _bstrPrinterName // and attempts to pause the real printer. // // Arguments: none // // Returns: HRESULT. // // Modifies: nothing // // History: 11-07-95 RamV Created // Appropriated from Old NetOle Code. // //--------------------------------------------------------------------------- STDMETHODIMP CLDAPPrintQueue::Pause(THIS) { HRESULT hr = S_OK; BOOL fStatus = FALSE; BSTR bstrPath = NULL ; HANDLE hPrinter = NULL; PRINTER_DEFAULTS PrinterDefaults = {0, 0, PRINTER_ACCESS_ADMINISTER}; BOOL fImpersonate = FALSE; HANDLE hUserToken = INVALID_HANDLE_VALUE; // // get the 'Path' property // hr = get_BSTR_Property(this->_pADs, UNCNAME, &bstrPath) ; BAIL_ON_FAILURE(hr); if(!_Credentials.IsNullCredentials()) { fImpersonate = ImpersonateWrapper(_Credentials, _pADs, &hUserToken); } // we don't want to break the existing app, so even the impersonation call fails, we will still try to call OpenPrinter // instead of bailing out // // use Win32 to open the printer // fStatus = OpenPrinter( (LPTSTR)bstrPath, &hPrinter, &PrinterDefaults); if (!fStatus) { hr = HRESULT_FROM_WIN32(GetLastError()); goto error; } fStatus = SetPrinter(hPrinter, 0, NULL, PRINTER_CONTROL_PAUSE); if (!fStatus) { hr = HRESULT_FROM_WIN32(GetLastError()); } error: if(fImpersonate) { RevertToSelf(); } if (hUserToken != INVALID_HANDLE_VALUE ) { CloseHandle(hUserToken); hUserToken = NULL; } if(hPrinter) { (void) ClosePrinter(hPrinter); } if (bstrPath) { ADsFreeString(bstrPath); } RRETURN(hr); } //+--------------------------------------------------------------------------- // // Function: CLDAPPrintQueue::Resume // // Synopsis: Binds to real printer as specified in _bstrPrinterName and // attempts to resume the real printer. // // Arguments: void // // Returns: HRESULT. // // Modifies: // // History: 11-07-95 RamV Created // Appropriated from old NetOle Project //---------------------------------------------------------------------------- STDMETHODIMP CLDAPPrintQueue::Resume(THIS) { HRESULT hr = S_OK; BOOL fStatus = FALSE; BSTR bstrPath = NULL ; HANDLE hPrinter = NULL; PRINTER_DEFAULTS PrinterDefaults = {0, 0, PRINTER_ACCESS_ADMINISTER}; BOOL fImpersonate = FALSE; HANDLE hUserToken = INVALID_HANDLE_VALUE; // // get the 'Path' property // hr = get_BSTR_Property(this->_pADs, UNCNAME, &bstrPath) ; BAIL_ON_FAILURE(hr); if(!_Credentials.IsNullCredentials()) { fImpersonate = ImpersonateWrapper(_Credentials, _pADs, &hUserToken); } // we don't want to break the existing app, so even the impersonation call fails, we will still try to call OpenPrinter // instead of bailing out // // use Win32 to open the printer // fStatus = OpenPrinter( (LPTSTR)bstrPath, &hPrinter, &PrinterDefaults); if (!fStatus) { hr = HRESULT_FROM_WIN32(GetLastError()); goto error; } fStatus = SetPrinter(hPrinter, 0, NULL, PRINTER_CONTROL_RESUME); if (!fStatus) { hr = HRESULT_FROM_WIN32(GetLastError()); } error: if(fImpersonate) { RevertToSelf(); } if (hUserToken != INVALID_HANDLE_VALUE ) { CloseHandle(hUserToken); hUserToken = NULL; } if(hPrinter) { (void) ClosePrinter(hPrinter); } if (bstrPath) { ADsFreeString(bstrPath); } RRETURN(hr); } //+--------------------------------------------------------------------------- // // Function: CLDAPPrintQueue::Purge // // Synopsis: Binds to real printer as specified in _PrinterName and attempts // to purge the real printer. // // Arguments: void // // Returns: HRESULT. // // Modifies: // // History: 11-07-95 RamV Created // Appropriated from old NetOle Code //---------------------------------------------------------------------------- STDMETHODIMP CLDAPPrintQueue::Purge(THIS) { HRESULT hr = S_OK; BOOL fStatus = FALSE; BSTR bstrPath = NULL ; HANDLE hPrinter = NULL; PRINTER_DEFAULTS PrinterDefaults = {0, 0, PRINTER_ACCESS_ADMINISTER}; BOOL fImpersonate = FALSE; HANDLE hUserToken = INVALID_HANDLE_VALUE; // // get the 'Path' property // hr = get_BSTR_Property(this->_pADs, UNCNAME, &bstrPath) ; BAIL_ON_FAILURE(hr); if(!_Credentials.IsNullCredentials()) { fImpersonate = ImpersonateWrapper(_Credentials, _pADs, &hUserToken); } // we don't want to break the existing app, so even the impersonation call fails, we will still try to call OpenPrinter // instead of bailing out // // use Win32 to open the printer // fStatus = OpenPrinter( (LPTSTR)bstrPath, &hPrinter, &PrinterDefaults); if (!fStatus) { hr = HRESULT_FROM_WIN32(GetLastError()); goto error; } fStatus = SetPrinter(hPrinter, 0, NULL, PRINTER_CONTROL_PURGE); if (!fStatus) { hr = HRESULT_FROM_WIN32(GetLastError()); } error: if(fImpersonate) { RevertToSelf(); } if (hUserToken != INVALID_HANDLE_VALUE ) { CloseHandle(hUserToken); hUserToken = NULL; } if(hPrinter) { (void) ClosePrinter(hPrinter); } if (bstrPath) { ADsFreeString(bstrPath); } RRETURN(hr); } STDMETHODIMP CLDAPPrintQueue::ADSIInitializeDispatchManager( long dwExtensionId ) { HRESULT hr = S_OK; if (_fDispInitialized) { RRETURN(E_FAIL); } hr = _pDispMgr->InitializeDispMgr(dwExtensionId); if (SUCCEEDED(hr)) { _fDispInitialized = TRUE; } RRETURN(hr); } STDMETHODIMP CLDAPPrintQueue::ADSIInitializeObject( THIS_ BSTR lpszUserName, BSTR lpszPassword, long lnReserved ) { CCredentials NewCredentials(lpszUserName, lpszPassword, lnReserved); _Credentials = NewCredentials; RRETURN(S_OK); } STDMETHODIMP CLDAPPrintQueue::ADSIReleaseObject() { delete this; RRETURN(S_OK); } // // IADsExtension::Operate() // STDMETHODIMP CLDAPPrintQueue::Operate( THIS_ DWORD dwCode, VARIANT varData1, VARIANT varData2, VARIANT varData3 ) { HRESULT hr = S_OK; switch (dwCode) { case ADS_EXT_INITCREDENTIALS: hr = InitCredentials( &varData1, &varData2, &varData3 ); break; default: hr = E_FAIL; break; } RRETURN(hr); } HRESULT CLDAPPrintQueue::InitCredentials( VARIANT * pvarUserName, VARIANT * pvarPassword, VARIANT * pvarFlags ) { BSTR bstrUser = NULL; BSTR bstrPwd = NULL; DWORD dwFlags = 0; ASSERT(V_VT(pvarUserName) == VT_BSTR); ASSERT(V_VT(pvarPassword) == VT_BSTR); ASSERT(V_VT(pvarFlags) == VT_I4); bstrUser = V_BSTR(pvarUserName); bstrPwd = V_BSTR(pvarPassword); dwFlags = V_I4(pvarFlags); CCredentials NewCredentials(bstrUser, bstrPwd, dwFlags); _Credentials = NewCredentials; RRETURN(S_OK); }