//+--------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1997. // // File: D I A L U P . C P P // // Contents: Implements the dial up connection object. // // Notes: // // Author: shaunco 23 Sep 1997 // //---------------------------------------------------------------------------- #include "pch.h" #pragma hdrstop #include "dialup.h" #include "nccom.h" #include "ncnetcon.h" #include "ncras.h" #include "ncreg.h" #include "userenv.h" #include "cmutil.h" #include "cobase.h" //+--------------------------------------------------------------------------- // // Member: CDialupConnection::CreateInstanceUninitialized // // Purpose: Create an uninitialized instance of // CComObject and return an interface // pointer as well as a pointer to the CDialupConnection. // // Arguments: // riid [in] IID of desired interface. // ppv [out] Returned interface pointer. // ppObj [out] Returned object pointer. // // Returns: S_OK or E_OUTOFMEMORY. // // Author: shaunco 20 Apr 1998 // // Notes: // //static HRESULT CDialupConnection::CreateInstanceUninitialized ( IN REFIID riid, OUT VOID** ppv, OUT CDialupConnection** ppObj) { Assert (ppObj); Assert (ppv); *ppv = NULL; *ppObj = NULL; HRESULT hr = E_OUTOFMEMORY; CDialupConnection* pObj; pObj = new CComObject ; if (pObj) { // Do the standard CComCreator::CreateInstance stuff. // pObj->SetVoid (NULL); pObj->InternalFinalConstructAddRef (); hr = pObj->FinalConstruct (); pObj->InternalFinalConstructRelease (); if (SUCCEEDED(hr)) { INetConnection* pCon = static_cast(pObj); hr = pCon->QueryInterface (riid, ppv); if (SUCCEEDED(hr)) { *ppObj = pObj; } } if (FAILED(hr)) { delete pObj; } } TraceError ("CDialupConnection::CreateInstanceFromDetails", hr); return hr; } //+--------------------------------------------------------------------------- // // Member: CDialupConnection::CreateInstanceFromDetails // // Purpose: Create an initialized instance of // CComObject given RASENUMENTRYDETAILS and // return an interface pointer on that object. // // Arguments: // pszwPbkFile [in] Path to the phonebook file. // pszwEntryName [in] Name of the entry in the phonebook. // fForAllUsers [in] TRUE if this entry is for all users. // riid [in] IID of desired interface. // ppv [out] Returned interface pointer. // // Returns: S_OK or E_OUTOFMEMORY. // // Author: shaunco 23 Sep 1997 // // Notes: // // static HRESULT CDialupConnection::CreateInstanceFromDetails ( const RASENUMENTRYDETAILS* pEntryDetails, REFIID riid, VOID** ppv) { Assert(pEntryDetails); Assert(pEntryDetails->szPhonebookPath); CDialupConnection* pObj; HRESULT hr = CreateInstanceUninitialized (riid, ppv, &pObj); if (SUCCEEDED(hr)) { pObj->SetPbkFile (pEntryDetails->szPhonebookPath); pObj->CacheProperties (pEntryDetails); // We are now a full-fledged object. // pObj->m_fInitialized = TRUE; } TraceError ("CDialupConnection::CreateInstanceFromDetails", hr); return hr; } //+--------------------------------------------------------------------------- // // Member: CDialupConnection::CreateInstanceFromPbkFileAndEntryName // // Purpose: Create an initialized instance of // CComObject given only a phonebook path, // entry name, and weather it is for all users or not and // return an interface pointer on that object. // // Arguments: // pszPbkFile [in] Phonebook path. // pszEntryName [in] Entry name. // riid [in] IID of desired interface. // ppv [out] Returned interface pointer. // // Returns: S_OK or E_OUTOFMEMORY. // // Author: shaunco 20 Apr 1998 // // Notes: This is called from Duplicate. // //static HRESULT CDialupConnection::CreateInstanceFromPbkFileAndEntryName ( IN PCWSTR pszPbkFile, IN PCWSTR pszEntryName, IN REFIID riid, OUT VOID** ppv) { TraceTag (ttidWanCon, "CDialupConnection::CreateInstanceFromPbkFileAndEntryName called"); CDialupConnection* pObj; HRESULT hr = CreateInstanceUninitialized (riid, ppv, &pObj); if (SUCCEEDED(hr)) { pObj->SetPbkFile (pszPbkFile); pObj->SetEntryName (pszEntryName); pObj->m_guidId = GUID_NULL; // We are now a full-fledged object. // pObj->m_fInitialized = TRUE; } TraceError ("CDialupConnection::CreateInstanceFromPbkFileAndEntryName", hr); return hr; } //+--------------------------------------------------------------------------- // INetConnection // STDMETHODIMP CDialupConnection::GetUiObjectClassId ( OUT CLSID* pclsid) { HRESULT hr = S_OK; // Validate parameters. // if (!pclsid) { hr = E_POINTER; } else if (!m_fInitialized) { hr = E_UNEXPECTED; } else { static const CLSID CLSID_DialupConnectionUi = {0x7007ACC1,0x3202,0x11D1,{0xAA,0xD2,0x00,0x80,0x5F,0xC1,0x27,0x0E}}; static const CLSID CLSID_DirectConnectionUi = {0x7007ACC2,0x3202,0x11D1,{0xAA,0xD2,0x00,0x80,0x5F,0xC1,0x27,0x0E}}; static const CLSID CLSID_VpnConnectionUi = {0x7007ACC6,0x3202,0x11D1,{0xAA,0xD2,0x00,0x80,0x5F,0xC1,0x27,0x0E}}; static const CLSID CLSID_PPPoEUi = {0x7007ACD4,0x3202,0x11D1,{0xAA,0xD2,0x00,0x80,0x5F,0xC1,0x27,0x0E}}; hr = HrEnsureEntryPropertiesCached (); if (SUCCEEDED(hr)) { switch (MediaType ()) { case NCM_DIRECT: *pclsid = CLSID_DirectConnectionUi; break; case NCM_ISDN: case NCM_PHONE: *pclsid = CLSID_DialupConnectionUi; break; case NCM_TUNNEL: *pclsid = CLSID_VpnConnectionUi; break; case NCM_PPPOE: *pclsid = CLSID_PPPoEUi; break; default: *pclsid = CLSID_DialupConnectionUi; TraceTag (ttidWanCon, "GetUiObjectClassId: Unknown media type " "(%d) treating as CLSID_DialupConnectionUi", MediaType()); } } } TraceError ("CDialupConnection::GetUiObjectClassId", hr); return hr; } STDMETHODIMP CDialupConnection::Connect () { return E_NOTIMPL; } STDMETHODIMP CDialupConnection::Disconnect () { HRESULT hr; if (!m_fInitialized) { hr = E_UNEXPECTED; } else { HRASCONN hRasConn; hr = HrFindRasConn (&hRasConn, NULL); if (S_OK == hr) { // Because RasHangup could call RasCustomHangup // we need to impersonate the client to allow the correct // per-user information to be used. // // Impersonate the client. // HRESULT hrT = CoImpersonateClient (); TraceErrorOptional ("CDialupConnection::Disconnect -- CoImpersonateClient", hrT, RPC_E_CALL_COMPLETE == hrT); // We need to continue if we're called in-proc (ie. if RPC_E_CALL_COMPLETE is returned). if (SUCCEEDED(hrT) || (RPC_E_CALL_COMPLETE == hrT)) { hr = HrRasHangupUntilDisconnected (hRasConn); } if (SUCCEEDED(hrT)) { hrT = CoRevertToSelf (); TraceError ("CDialupConnection::Disconnect -- CoRevertToSelf", hrT); } } else if (S_FALSE == hr) { hr = S_OK; } } TraceError ("CDialupConnection::Disconnect", hr); return hr; } STDMETHODIMP CDialupConnection::Delete () { HRESULT hr = E_UNEXPECTED; if (!m_fInitialized) { hr = E_UNEXPECTED; } else { hr = HrEnsureEntryPropertiesCached (); if (SUCCEEDED(hr)) { // If we allow removal (a decision which is based on the // whether the user is an admin, whether the connection is // 'for all users', and the connection's current state) then // proceed. If FAllowRemoval fails, it's output parameter // is the "reason" in the form of an HRESULT. We can use // this as our return value in that case. // HRESULT hrReason; if (FAllowRemoval (&hrReason)) { // If we're active in any way, we can't be removed. // NETCON_STATUS status; hr = HrGetStatus(&status); if (SUCCEEDED(hr) && ((NCS_CONNECTING != status) && (NCS_CONNECTED != status) && (NCS_DISCONNECTING != status))) { // We do an impersonation here in case the connection has a RAS custom delete notification // setup (CM connections do, for instance). This allows the RasCustomDeleteEntryNotify // function to interact with the system as the user. // HRESULT hrT = CoImpersonateClient (); TraceError ("HrRemoveCmProfile -- CoImpersonateClient", hrT); // We need to continue if we're called in-proc (ie. if RPC_E_CALL_COMPLETE is returned). if (SUCCEEDED(hrT) || (RPC_E_CALL_COMPLETE == hrT)) { // Delete the RAS entry, note that for branded connections, RAS // will call RasCustomDeleteEntryNotify after deletion. // DWORD dwErr = RasDeleteEntry (PszwPbkFile (), PszwEntryName ()); hr = HRESULT_FROM_WIN32 (dwErr); TraceError ("RasDeleteEntry", hr); } // Revert to ourselves // if (SUCCEEDED(hrT)) { CoRevertToSelf (); } } else { // Don't allow deletion unless disconnected // TraceTag (ttidWanCon, "Disallowing delete while in connected or" "partially connected state"); hr = E_UNEXPECTED; } } else { hr = hrReason; } } } TraceError ("CDialupConnection::Delete", hr); return hr; } STDMETHODIMP CDialupConnection::Duplicate ( IN PCWSTR pszDuplicateName, OUT INetConnection** ppCon) { HRESULT hr = S_OK; // Validate parameters. // if (!pszDuplicateName || !ppCon) { hr = E_POINTER; } else if (!m_fInitialized) { hr = E_UNEXPECTED; } else { *ppCon = NULL; hr = HrEnsureEntryPropertiesCached (); if (SUCCEEDED(hr)) { // Make sure the name is valid in the same phone book. // DWORD dwErr = RasValidateEntryName ( PszwPbkFile (), pszDuplicateName); hr = HRESULT_FROM_WIN32 (dwErr); TraceError ("RasValidateEntryName", hr); if (SUCCEEDED(hr)) { dwErr = DwCloneEntry ( PszwPbkFile(), PszwEntryName(), pszDuplicateName); hr = HRESULT_FROM_WIN32 (dwErr); TraceError ("DwCloneEntry", hr); if (SUCCEEDED(hr)) { hr = CreateInstanceFromPbkFileAndEntryName ( PszwPbkFile (), pszDuplicateName, IID_INetConnection, (VOID**)ppCon); if (SUCCEEDED(hr)) { hr = HrEnsureHNetPropertiesCached(); } if (SUCCEEDED(hr)) { if (m_HNetProperties.fFirewalled || m_HNetProperties.fIcsPublic) // lazy eval the hnetcfg stuff { IHNetCfgMgr* pHomenetConfigManager; hr = HrGetHNetCfgMgr(&pHomenetConfigManager); if(SUCCEEDED(hr)) { IHNetConnection* pNewHomenetConnection; hr = pHomenetConfigManager->GetIHNetConnectionForINetConnection(*ppCon, &pNewHomenetConnection); if(SUCCEEDED(hr)) { IHNetConnection* pHomenetConnection; hr = HrGetIHNetConnection(&pHomenetConnection); if(SUCCEEDED(hr)) { // copy port bindings // REVIEW if somethings fails here to we need to nuke the new connection? IEnumHNetPortMappingBindings* pEnumPortMappingBindings; hr = pNewHomenetConnection->EnumPortMappings(FALSE, &pEnumPortMappingBindings); if(SUCCEEDED(hr)) { ULONG ulFetched; IHNetPortMappingBinding* pNewPortMappingBinding; while(S_OK == pEnumPortMappingBindings->Next(1, &pNewPortMappingBinding, &ulFetched)) { Assert(1 == ulFetched); IHNetPortMappingProtocol* pPortMappingProtocol; hr = pNewPortMappingBinding->GetProtocol(&pPortMappingProtocol); if(SUCCEEDED(hr)) { // find the original binding by using the protocol field IHNetPortMappingBinding* pPortMappingBinding; hr = pHomenetConnection->GetBindingForPortMappingProtocol(pPortMappingProtocol, &pPortMappingBinding); if(SUCCEEDED(hr)) { BOOLEAN bEnabled; hr = pPortMappingBinding->GetEnabled(&bEnabled); if(SUCCEEDED(hr)) { if(TRUE == bEnabled) { hr = pNewPortMappingBinding->SetEnabled(bEnabled); } } // always set the computer address if(SUCCEEDED(hr)) { ULONG ulAddress; hr = pPortMappingBinding->GetTargetComputerAddress(&ulAddress); if(SUCCEEDED(hr)) { if(0 != ulAddress) { hr = pNewPortMappingBinding->SetTargetComputerAddress(ulAddress); } } } // only set the computer name if it is used if(SUCCEEDED(hr)) { BOOLEAN bUseName; hr = pPortMappingBinding->GetCurrentMethod(&bUseName); if(SUCCEEDED(hr) && TRUE == bUseName) { OLECHAR* pszTargetComputerName; hr = pPortMappingBinding->GetTargetComputerName(&pszTargetComputerName); if(SUCCEEDED(hr)) { if(L'\0' != *pszTargetComputerName) { hr = pNewPortMappingBinding->SetTargetComputerName(pszTargetComputerName); } CoTaskMemFree(pszTargetComputerName); } } } ReleaseObj(pPortMappingBinding); } ReleaseObj(pPortMappingProtocol); } ReleaseObj(pNewPortMappingBinding); } ReleaseObj(pEnumPortMappingBindings); } if(m_HNetProperties.fFirewalled) // copy firewall yes/no and ICMP settings { IHNetFirewalledConnection* pFirewalledConnection; hr = pNewHomenetConnection->Firewall(&pFirewalledConnection); if(SUCCEEDED(hr)) { HNET_FW_ICMP_SETTINGS* pICMPSettings; hr = pHomenetConnection->GetIcmpSettings(&pICMPSettings); if(SUCCEEDED(hr)) { hr = pNewHomenetConnection->SetIcmpSettings(pICMPSettings); CoTaskMemFree(pICMPSettings); } ReleaseObj(pFirewalledConnection); } } ReleaseObj(pHomenetConnection); } ReleaseObj(pNewHomenetConnection); } ReleaseObj(pHomenetConfigManager); } } } } } } } TraceError ("CDialupConnection::Duplicate", hr); return hr; } STDMETHODIMP CDialupConnection::GetProperties ( OUT NETCON_PROPERTIES** ppProps) { HRESULT hr = S_OK; HRESULT hrHiddenCM = S_OK; CMEntry cm; // Validate parameters. // if (!ppProps) { hr = E_POINTER; } else if (!m_fInitialized) { hr = E_UNEXPECTED; } else { // Initialize the output parameter. // *ppProps = NULL; hr = HrEnsureEntryPropertiesCached (); if (SUCCEEDED(hr)) { NETCON_PROPERTIES* pProps; hr = HrCoTaskMemAlloc (sizeof (NETCON_PROPERTIES), reinterpret_cast(&pProps)); if (SUCCEEDED(hr)) { HRESULT hrT; ZeroMemory (pProps, sizeof (NETCON_PROPERTIES)); // guidId // pProps->guidId = GuidId (); // pszwName // hrT = HrCoTaskMemAllocAndDupSz ( PszwEntryName(), &pProps->pszwName, NETCON_MAX_NAME_LEN); if (FAILED(hrT)) { hr = hrT; } hrT = HrCoTaskMemAllocAndDupSz ( PszwDeviceName(), &pProps->pszwDeviceName, NETCON_MAX_NAME_LEN); if (FAILED(hrT)) { hr = hrT; } // Status // hrT = HrGetStatus (&pProps->Status); if (FAILED(hrT)) { hr = hrT; } // Verify that the status return is accurate. HrGetStatus returns NCS_DISCONNECTED // if the connectoid is NCS_CONNECTING, which is wrong!!!!. // if( pProps->Status == NCS_DISCONNECTED ) { // CMUtil remebers the Hidden connection (Connection Manager) and the status of // any ras events (i.e. Connecting, Disconnecting etc). The Data is filled in // in function RasEventNotify. // hrHiddenCM = CCMUtil::Instance().HrGetEntry(pProps->guidId,cm); if ( S_OK == hrHiddenCM ) { // Use CCMUtil's status, its more accurate. // pProps->Status = cm.m_ncs; } } // Check if this connection has a child connection // hrHiddenCM = CCMUtil::Instance().HrGetEntry(PszwEntryName(),cm); if( hrHiddenCM == S_OK ) { // It has a child connectoid // Now we have to determine which one describes the overall status of the connection // if( cm.m_ncs == NCS_CONNECTING || cm.m_ncs == NCS_DISCONNECTING || cm.m_ncs == NCS_CONNECTED) { if( pProps->Status == NCS_DISCONNECTING ) { // This case happens if the parent is disconnecting // The parent is disconnecting, so the child will be disconnecting. // Change the status of the child to disconnecting so that we do not // get confused later on when the child is connected at the parent is // disconnected. i.e. are we overall connecting or disconnecting!!!! // CCMUtil::Instance().SetEntry(GuidId (), PszwEntryName(),pProps->Status); } else if( cm.m_ncs == NCS_CONNECTED && pProps->Status == NCS_DISCONNECTED ) { // This case will only happen if the child is connected and the parent is still // disconnected. // pProps->Status = NCS_CONNECTING; } else if (!IsEqualGUID(pProps->guidId, cm.m_guid)) { TraceTag(ttidWanCon, "Overwriting parent connection status: %s with child status: %s", DbgNcs(pProps->Status), DbgNcs(cm.m_ncs)); // When in doubt and the GUID's are different (ie. not a BAP/Multilink connection) take the childs status =) // pProps->Status = cm.m_ncs; } } } // Type // pProps->MediaType = MediaType (); // dwCharacter // hrT = HrGetCharacteristics (&pProps->dwCharacter); if (FAILED(hrT)) { hr = hrT; } // clsidThisObject // pProps->clsidThisObject = CLSID_DialupConnection; // clsidUiObject // hrT = GetUiObjectClassId (&pProps->clsidUiObject); if (FAILED(hrT)) { hr = hrT; } // Assign the output parameter or cleanup if we had any failures. // if (SUCCEEDED(hr)) { *ppProps = pProps; } else { Assert (NULL == *ppProps); FreeNetconProperties (pProps); } } } } TraceError ("CDialupConnection::GetProperties", hr); return hr; } STDMETHODIMP CDialupConnection::Rename ( IN PCWSTR pszNewName) { HRESULT hr; // Validate parameters. // if (!pszNewName) { hr = E_POINTER; } else if (!m_fInitialized) { hr = E_UNEXPECTED; } else { hr = HrLockAndRenameEntry (pszNewName, this); } TraceError ("CDialupConnection::Rename", hr); return hr; } //+--------------------------------------------------------------------------- // INetRasConnection // STDMETHODIMP CDialupConnection::GetRasConnectionInfo ( OUT RASCON_INFO* pRasConInfo) { HRESULT hr = HrGetRasConnectionInfo (pRasConInfo); TraceError ("CDialupConnection::GetRasConnectionInfo", hr); return hr; } STDMETHODIMP CDialupConnection::SetRasConnectionInfo ( IN const RASCON_INFO* pRasConInfo) { HRESULT hr = HrSetRasConnectionInfo (pRasConInfo); TraceError ("CDialupConnection::SetRasConnectionInfo", hr); return hr; } STDMETHODIMP CDialupConnection::GetRasConnectionHandle ( OUT ULONG_PTR* phRasConn) { HRESULT hr = HrGetRasConnectionHandle ( reinterpret_cast(phRasConn)); TraceError ("CDialupConnection::GetRasConnectionHandle", (S_FALSE == hr) ? S_OK : hr); return hr; } //+--------------------------------------------------------------------------- // IPersistNetConnection // STDMETHODIMP CDialupConnection::GetClassID ( OUT CLSID* pclsid) { HRESULT hr = S_OK; // Validate parameters. // if (!pclsid) { hr = E_POINTER; } else { *pclsid = CLSID_DialupConnection; } TraceError ("CDialupConnection::GetClassID", hr); return hr; } STDMETHODIMP CDialupConnection::GetSizeMax ( OUT ULONG* pcbSize) { HRESULT hr = HrPersistGetSizeMax (pcbSize); TraceError ("CDialupConnection::GetSizeMax", hr); return hr; } STDMETHODIMP CDialupConnection::Load ( IN const BYTE* pbBuf, IN ULONG cbSize) { HRESULT hr = HrPersistLoad (pbBuf, cbSize); TraceError ("CDialupConnection::Load", hr); return hr; } STDMETHODIMP CDialupConnection::Save ( OUT BYTE* pbBuf, OUT ULONG cbSize) { HRESULT hr = HrPersistSave (pbBuf, cbSize); TraceError ("CDialupConnection::Save", hr); return hr; } //+--------------------------------------------------------------------------- // INetConnectionBrandingInfo // //+--------------------------------------------------------------------------- // // Member: CDialupConnection::GetBrandingIconPaths // // Purpose: Returns the full paths to three icons. // // Arguments: pConBrandInfo - pointer to an Icon branding structure // // Returns: S_OK or an error code // STDMETHODIMP CDialupConnection::GetBrandingIconPaths( OUT CON_BRANDING_INFO ** ppConBrandInfo) { HRESULT hr = HrEnsureEntryPropertiesCached (); CON_BRANDING_INFO * pConBrandInfo = NULL; BOOL fRunOldVersion = TRUE; typedef DWORD (WINAPI* pfnGetCustomPropertiesFunc)(LPCWSTR, LPCWSTR, LPWSTR, PBYTE*, DWORD*); Assert(ppConBrandInfo); if (SUCCEEDED(hr)) { if (!FIsBranded ()) { hr = E_NOTIMPL; } else { WCHAR szTemp[MAX_PATH+1]; WCHAR szIconName[MAX_PATH+1]; const WCHAR* const CMSECTION = L"Connection Manager"; HICON hIcon; hr = HrCoTaskMemAlloc(sizeof(CON_BRANDING_INFO), (LPVOID*)&pConBrandInfo); if (SUCCEEDED(hr)) { ZeroMemory(pConBrandInfo, sizeof(CON_BRANDING_INFO)); WCHAR szExpandedCustomDLL[MAX_PATH+1] = {0}; HMODULE hCustomDll = NULL; DWORD dwRetCode = ERROR_SUCCESS; RASENTRY re = {0}; DWORD dwNeededSize = sizeof(re); re.dwSize = dwNeededSize; DWORD dwTODOREMOVE = sizeof(re.szCustomDialDll)/sizeof(WCHAR); DWORD dwNum = GetPrivateProfileStringW(PszwEntryName (), L"CustomRasDialDll", L"", &re.szCustomDialDll[0], (sizeof(re.szCustomDialDll)/sizeof(WCHAR))-1, PszwPbkFile ()); if (0 == dwNum) { dwRetCode = RasGetEntryProperties(PszwPbkFile (), PszwEntryName (), &re, &dwNeededSize, NULL, NULL); } if ((ERROR_SUCCESS == dwRetCode) && (lstrlenW(re.szCustomDialDll))) { BOOL fRetCode = ExpandEnvironmentStringsForUserW(NULL, re.szCustomDialDll, szExpandedCustomDLL, MAX_PATH); if (fRetCode) { hCustomDll = LoadLibrary(szExpandedCustomDLL); if (hCustomDll) { WCHAR szIconPath[(2*MAX_PATH) + 1] = {0}; WCHAR szHideTrayIcon[(2*MAX_PATH) + 1] = {0}; WCHAR szTrayIcon[(2*MAX_PATH) + 1] = {0}; pfnGetCustomPropertiesFunc pfnGetCustomProperties = NULL; pfnGetCustomProperties = (pfnGetCustomPropertiesFunc)GetProcAddress(hCustomDll, "GetCustomProperty"); if (pfnGetCustomProperties) { DWORD dwSize = 2*MAX_PATH; BOOL fRet = pfnGetCustomProperties(PszwPbkFile (), PszwEntryName (), L"Icon", (PBYTE*)&szIconPath, &dwSize); if (fRet) { if (NULL != (hIcon = (HICON)LoadImage(NULL, szIconPath, IMAGE_ICON, 32, 32, LR_LOADFROMFILE))) { DestroyIcon(hIcon); hr = HrCoTaskMemAllocAndDupSz (szIconPath, &(pConBrandInfo->szwLargeIconPath), MAX_PATH); } } dwSize = 2*MAX_PATH; fRet = pfnGetCustomProperties(PszwPbkFile (), PszwEntryName (), L"HideTrayIcon", (PBYTE*)&szHideTrayIcon, &dwSize); // We don't care if we the call fails just need to check the length below if (SUCCEEDED(hr)) { DWORD dwNewHideTrayIcon = 1; // default is to hide the icon if (szHideTrayIcon) { if (lstrlenW(szHideTrayIcon)) { dwNewHideTrayIcon = _ttoi(szHideTrayIcon); } } if (dwNewHideTrayIcon) { dwSize = 2*MAX_PATH; fRet = pfnGetCustomProperties(PszwPbkFile (), PszwEntryName (), L"TrayIcon", (PBYTE*)&szTrayIcon, &dwSize); if (fRet) { if (NULL != (hIcon = (HICON)LoadImage(NULL, szTrayIcon, IMAGE_ICON, 16, 16, LR_LOADFROMFILE))) { DestroyIcon(hIcon); hr = HrCoTaskMemAllocAndDupSz (szTrayIcon, &(pConBrandInfo->szwTrayIconPath), MAX_PATH); if (SUCCEEDED(hr)) { // // We got all the info for the icons // fRunOldVersion = FALSE; } } } } else { // // We got this far (meaning the call for getting the Icon succeeded), // thus no need to run the older code which is left for compatibility reasons. // fRunOldVersion = FALSE; } } } FreeLibrary(hCustomDll); hCustomDll = NULL; } } } // // For compatibility reasons we leave this code. If the CustomDialDll doesn't have the appropriate // entry point, then we'll just use this version of the code. // if (fRunOldVersion) { // Get the path to the cms file to get the Icon entries from. // hr = HrEnsureCmStringsLoaded(); if (SUCCEEDED(hr)) { // Get the Large Icon path // if (0 != GetPrivateProfileStringW(CMSECTION, L"Icon", L"", szIconName, celems(szIconName), PszwCmsFile ())) { lstrcpynW(szTemp, PszwCmDir (), celems(szTemp)); lstrcatW(szTemp, szIconName); if (NULL != (hIcon = (HICON)LoadImage(NULL, szTemp, IMAGE_ICON, 32, 32, LR_LOADFROMFILE))) { DestroyIcon(hIcon); hr = HrCoTaskMemAllocAndDupSz (szTemp, &(pConBrandInfo->szwLargeIconPath), MAX_PATH); } } // See if the CM icon is hidden WCHAR szHideTrayIcon[MAX_PATH+1]; DWORD dwHideTrayIcon = 1; // default is to hide the CM icon if (SUCCEEDED(hr) && (0 != GetPrivateProfileStringW(CMSECTION, L"HideTrayIcon", L"1", szHideTrayIcon, celems(szHideTrayIcon), PszwCmsFile ()))) { dwHideTrayIcon = _ttoi(szHideTrayIcon); } if (dwHideTrayIcon) // If the CM icon is not hidden, we don't want another branded icon. We'll use blinky lights instead { // Get the Tray Icon path // if (SUCCEEDED(hr) && (0 != GetPrivateProfileStringW(CMSECTION, L"TrayIcon", L"", szIconName, celems(szIconName), PszwCmsFile ()))) { lstrcpynW(szTemp, PszwCmDir (), celems(szTemp)); lstrcatW(szTemp, szIconName); if (NULL != (hIcon = (HICON)LoadImage(NULL, szTemp, IMAGE_ICON, 16, 16, LR_LOADFROMFILE))) { DestroyIcon(hIcon); hr = HrCoTaskMemAllocAndDupSz (szTemp, &(pConBrandInfo->szwTrayIconPath), MAX_PATH); } } } } } } } } // Fill in the out param struct if we succeeded, otherwise leave it alone so it will still // marshall. // if (SUCCEEDED(hr)) { *ppConBrandInfo = pConBrandInfo; } TraceError ("CDialupConnection::GetBrandingIconPaths", hr); return hr; } //+--------------------------------------------------------------------------- // // Member: CDialupConnection::GetTrayMenuEntries // // Purpose: Returns any branded menu items to be added to the tray menu. // // Arguments: pMenuData -- Pointer to a Tray Menu Data struct // // Returns: S_OK or an error code // STDMETHODIMP CDialupConnection::GetTrayMenuEntries( OUT CON_TRAY_MENU_DATA** ppMenuData) { // initialize output Assert(ppMenuData); *ppMenuData = NULL; CON_TRAY_MENU_DATA * pMenuData = NULL; HRESULT hr = HrEnsureEntryPropertiesCached (); BOOL fRunOldVersion = TRUE; typedef DWORD (WINAPI* pfnGetCustomPropertiesFunc)(LPCWSTR, LPCWSTR, LPWSTR, PBYTE*, DWORD*); if (SUCCEEDED(hr)) { if (!FIsBranded ()) { hr = E_NOTIMPL; } else { WCHAR szExpandedCustomDLL[MAX_PATH+1] = {0}; HMODULE hCustomDll = NULL; DWORD dwRetCode = ERROR_SUCCESS; RASENTRY re = {0}; DWORD dwNeededSize = sizeof(re); re.dwSize = dwNeededSize; DWORD dwNum = GetPrivateProfileStringW(PszwEntryName (), L"CustomRasDialDll", L"", &re.szCustomDialDll[0], (sizeof(re.szCustomDialDll)/sizeof(WCHAR))-1, PszwPbkFile ()); if (0 == dwNum) { dwRetCode = RasGetEntryProperties(PszwPbkFile (), PszwEntryName (), &re, &dwNeededSize, NULL, NULL); } if ((ERROR_SUCCESS == dwRetCode) && (lstrlenW(re.szCustomDialDll))) { BOOL fRetCode = ExpandEnvironmentStringsForUserW(NULL, re.szCustomDialDll, szExpandedCustomDLL, MAX_PATH); if (fRetCode) { hCustomDll = LoadLibrary(szExpandedCustomDLL); if (hCustomDll) { pfnGetCustomPropertiesFunc pfnGetCustomProperties = NULL; pfnGetCustomProperties = (pfnGetCustomPropertiesFunc)GetProcAddress(hCustomDll, "GetCustomProperty"); if (pfnGetCustomProperties) { // // This call actually allocates memory and fills in the whole structure inside // the GetCustomProperty call // DWORD dwSize = 0; BOOL fRet = pfnGetCustomProperties(PszwPbkFile (), PszwEntryName (), L"Menu Items", (PBYTE*)&pMenuData, &dwSize); if (fRet) { hr = S_OK; // needed at the end of the function fRunOldVersion = FALSE; } } FreeLibrary(hCustomDll); hCustomDll = NULL; } } } // // For compatibility reasons we leave this code. If the CustomDialDll doesn't have the appropriate // entry point, then we'll just use this version of the code. // if (fRunOldVersion) { hr = HrEnsureCmStringsLoaded(); if (SUCCEEDED(hr)) { // // Get the menu item section // WCHAR* pszMenuItemsSection = NULL; int nSize; hr = HrGetPrivateProfileSectionWithAlloc(&pszMenuItemsSection, &nSize); // Process the menu items // if (SUCCEEDED(hr) && (nSize>0)) { // We have menu items to process. First make a copy of the data // and figure out a line count. // hr = HrCoTaskMemAlloc(sizeof(CON_TRAY_MENU_DATA), (LPVOID*)&pMenuData); if (SUCCEEDED(hr)) { DWORD dwCount = 0; WCHAR*pszLine = NULL; WCHAR szName[MAX_PATH+1]; WCHAR szCmdLine[MAX_PATH+1]; WCHAR szParams[MAX_PATH+1]; pszLine = pszMenuItemsSection; while ((NULL != pszLine) && (0 != *pszLine)) { if (SUCCEEDED(HrGetMenuNameAndCmdLine(pszLine, szName, szCmdLine, szParams))) { dwCount++; } pszLine = pszLine + lstrlenW(pszLine) + 1; } ASSERT(0 != dwCount); // Now that we have an accurate count, lets // allocate the memory for the marshalling and // reparse the items. // hr = HrCoTaskMemAlloc(dwCount*sizeof(CON_TRAY_MENU_ENTRY), (LPVOID*)&pMenuData->pctme); if (SUCCEEDED(hr)) { pMenuData->dwCount = dwCount; DWORD dwNumAdded = 0; pszLine = pszMenuItemsSection; while ((NULL != pszLine) && (0 != *pszLine) && SUCCEEDED(hr)) { if (SUCCEEDED(HrGetMenuNameAndCmdLine(pszLine, szName, szCmdLine, szParams)) && (dwNumAdded <= dwCount)) { hr = HrFillInConTrayMenuEntry(szName, szCmdLine, szParams, &(pMenuData->pctme[dwNumAdded])); if (FAILED(hr)) { CoTaskMemFree(&pMenuData->pctme); } dwNumAdded++; } pszLine = pszLine + lstrlenW(pszLine) + 1; } } else { delete pMenuData; } } delete (pszMenuItemsSection); } } } } } // Fill in the out param struct if we succeeded, otherwise leave it alone so it will still // marshall. // if (SUCCEEDED(hr)) { *ppMenuData = pMenuData; } TraceError ("CDialupConnection::GetTrayMenuEntries", hr); return hr; } //+--------------------------------------------------------------------------- // // Member: CDialupConnection::HrGetPrivateProfileSectionWithAlloc // // Purpose: This function ensures that the CM specific member vars for dialup // are loaded and usable by CM specific functions. // // Arguments: none // // Returns: S_OK or an error code // HRESULT CDialupConnection::HrGetPrivateProfileSectionWithAlloc ( OUT WCHAR** pszSection, OUT int* pnSize) { Assert(pszSection); Assert(pnSize); HRESULT hr = HrEnsureCmStringsLoaded(); if (!pszSection) { return E_POINTER; } if (!pnSize) { return E_POINTER; } if (SUCCEEDED(hr)) { const int c_64K= 64*1024; int nAllocated = 1024; *pnSize = nAllocated - 2; while ((nAllocated <= c_64K) && ((*pnSize) == (nAllocated - 2))) { // Should never need more than the 4-5 lines we already allocated // but someone might want lots of menu options. // if (NULL != *pszSection) { delete (*pszSection); } *pszSection = new WCHAR[nAllocated]; if (*pszSection) { *pnSize = GetPrivateProfileSectionW(L"Menu Options", *pszSection, nAllocated, PszwCmsFile ()); } else { hr = E_OUTOFMEMORY; break; } nAllocated = 2*nAllocated; } if (nAllocated > c_64K) { hr = E_UNEXPECTED; } if (nAllocated > c_64K || 0 == *pnSize) { // We need to free this in both cases, because if the size is 0, then the callers don't free this. delete *pszSection; } } TraceError ("CDialupConnection::HrGetPrivateProfileSectionWithAlloc", hr); return hr; } //+--------------------------------------------------------------------------- // // Member: CDialupConnection::HrGetMenuNameAndCmdLine // // Purpose: Given a menu item line from a CMS file parses out the Menu item name, // Menu executable, and Menu item parameters. // // Arguments: pMenuData -- Pointer to a Tray Menu Data struct // // Returns: S_OK or an error code // HRESULT CDialupConnection::HrGetMenuNameAndCmdLine( IN PCWSTR pszString, OUT PWSTR szName, OUT PWSTR szProgram, OUT PWSTR szParams) { WCHAR* pszPtr1; WCHAR* pszPtr2; WCHAR szLine[MAX_PATH+1]; BOOL fLong = FALSE; HRESULT hr; Assert(NULL != pszString); Assert(NULL != szName); Assert(NULL != szProgram); Assert(NULL != szParams); ZeroMemory(szName, celems(szName)); ZeroMemory(szProgram, celems(szProgram)); ZeroMemory(szParams, celems(szParams)); lstrcpynW(szLine, pszString, celems(szLine)); // Process the first portion, the "Name=" part // pszPtr1 = wcsstr(szLine, L"="); if (pszPtr1) { *pszPtr1 = 0; lstrcpynW(szName, szLine, MAX_PATH); // Process next portion, the program name // pszPtr1++; if (pszPtr1) { // Look for "+" or " " marking end of program portion // if (*pszPtr1 == L'+') { pszPtr1++; pszPtr2 = wcsstr(pszPtr1, L"+"); fLong = TRUE; } else { // If not a long filename then we have two choices, // either a short program name and params or just a // short program name. // pszPtr2 = wcsstr(pszPtr1, L" "); fLong = FALSE; } // Terminate program name and copy // if (pszPtr2) { if (*pszPtr2 != 0) { *pszPtr2 = 0; pszPtr2++; } lstrcpynW(szProgram, pszPtr1, MAX_PATH); // Process final portion, the params // if (fLong) { pszPtr2++; // skip blank } // Now we are have the param string // if (pszPtr2) { lstrcpynW(szParams, pszPtr2, MAX_PATH); } } else { // Just a program with no params and no space seperator // (this happens on memphis) // lstrcpynW(szProgram, pszPtr1, MAX_PATH); } } hr = S_OK; } else { // No entries // hr = E_UNEXPECTED; } TraceError ("CDialupConnection::HrGetMenuNameAndCmdLine", hr); return hr; } //+--------------------------------------------------------------------------- // // Member: CDialupConnection::HrFillInConTrayMenuEntry // // Purpose: Given the elements of a ConTrayMenuEntry struct, the function // allocs the memory necessary and copies of the given elements. // // Arguments: szwName - Display name of the command to show in the tray context menu // szwCmdLine - actual command to run for this menu entry // szwParams - command params for this command // pMenuEntry - pointer to the struct to fill in and execute // // Returns: S_OK or an error code // HRESULT CDialupConnection::HrFillInConTrayMenuEntry ( IN PCWSTR szName, IN PCWSTR szCmdLine, IN PCWSTR szParams, OUT CON_TRAY_MENU_ENTRY* pMenuEntry) { HRESULT hr; ZeroMemory(pMenuEntry, sizeof(CON_TRAY_MENU_ENTRY)); hr = HrCoTaskMemAlloc ((lstrlenW(szName)+1)*sizeof(WCHAR), (LPVOID*)&(pMenuEntry->szwMenuText)); if (SUCCEEDED(hr)) { lstrcpyW(pMenuEntry->szwMenuText, szName); hr = HrCoTaskMemAlloc ((lstrlenW(szParams)+1)*sizeof(WCHAR), (LPVOID*)&(pMenuEntry->szwMenuParams)); if (S_OK == hr) { lstrcpyW(pMenuEntry->szwMenuParams, szParams); if (0 == wcsncmp(PszwShortServiceName (), szCmdLine, lstrlenW(PszwShortServiceName ()))) { // // Then we have an included file. Add the profile dir path // // Take out the "short service name" because it's already included in the path PCWSTR pszFileName = szCmdLine + lstrlenW(PszwShortServiceName()) + 1; hr = HrCoTaskMemAlloc ((lstrlenW(pszFileName)+lstrlenW(PszwProfileDir())+1)*sizeof(WCHAR), (LPVOID*)&(pMenuEntry->szwMenuCmdLine)); if (S_OK == hr) { lstrcpyW(pMenuEntry->szwMenuCmdLine, PszwProfileDir ()); lstrcatW(pMenuEntry->szwMenuCmdLine, pszFileName); } } else { hr = HrCoTaskMemAlloc ((lstrlenW(szCmdLine)+1)*sizeof(WCHAR), (LPVOID*)&(pMenuEntry->szwMenuCmdLine)); if (S_OK == hr) { lstrcpyW(pMenuEntry->szwMenuCmdLine, szCmdLine); } } } } if (FAILED(hr)) { // // We Failed so free the memory // CoTaskMemFree(pMenuEntry->szwMenuText); CoTaskMemFree(pMenuEntry->szwMenuCmdLine); CoTaskMemFree(pMenuEntry->szwMenuParams); } return hr; } //+--------------------------------------------------------------------------- // // Member: CDialupConnection::HrGetCmpFileLocation // // Purpose: Compares the phonebook file path to path of the current user's // application data dir. If the initial paths are the same we have // a private profile. Please NOTE that calling this function // requires the calling client to properly setup CoSetProxyBlanket for // a private user profile (matches the call to CoImpersonateClient) // // Arguments: szwPhonebook -- path to the phonebook the CM connectoid lives in // // Returns: S_OK or an error code // HRESULT CDialupConnection::HrGetCmpFileLocation( IN PCWSTR szPhonebook, IN PCWSTR szEntryName, OUT PWSTR szCmpFilePath) { DWORD dwSize = MAX_PATH; HKEY hKey; HANDLE hBaseKey = NULL; HANDLE hFile; HRESULT hr; HRESULT hrImpersonate = E_FAIL; static const WCHAR c_mappingsRegKey[] = L"Software\\Microsoft\\Connection Manager\\Mappings"; HANDLE hImpersonationToken = NULL; // The token of the thread HANDLE hPrimaryToken = NULL; // The primary token for the new process if ((NULL == szCmpFilePath) || (NULL == szPhonebook)) { hr = E_POINTER; } else { hr = HrEnsureEntryPropertiesCached (); if (SUCCEEDED(hr)) { if (m_fForAllUsers) { // We have an all users key so get the information from HKLM // hBaseKey = HKEY_LOCAL_MACHINE; } else { // Then we have a private profile. Since netman runs as a system account, // we must impersonate the client and then make an RTL call to get // the current users HKCU hive before querying the registry for the // cmp path. We also need to get the user token so that we can expand the // cmp string in the single user case. // hrImpersonate = CoImpersonateClient (); TraceError ("HrGetCmpFileLocation -- CoImpersonateClient", hr); if (SUCCEEDED(hrImpersonate)) { NTSTATUS ntstat = RtlOpenCurrentUser(KEY_READ | KEY_WRITE, &hBaseKey); hr = HRESULT_FROM_NT(ntstat); TraceError ("RtlOpenCurrentUser", hr); if (SUCCEEDED(hr)) { // Create a primary token // if (!OpenThreadToken( GetCurrentThread(), TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY, TRUE, &hImpersonationToken)) { hr = HrFromLastWin32Error(); TraceError ("HrGetCmpFileLocation -- OpenThreadToken", hr); } else { if(!DuplicateTokenEx(hImpersonationToken, TOKEN_IMPERSONATE | TOKEN_READ | TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE, NULL, SecurityImpersonation, TokenPrimary, &hPrimaryToken )) { hr = HrFromLastWin32Error(); TraceError ("HrGetCmpFileLocation -- DuplicateTokenEx", hr); } } } } else { hr = hrImpersonate; } } // Now Open the mappings key and get the cmp file path // if (SUCCEEDED(hr) && hBaseKey) { hr = HrRegOpenKeyEx((HKEY)hBaseKey, c_mappingsRegKey, KEY_READ, &hKey); if (SUCCEEDED(hr)) { dwSize = MAX_PATH; WCHAR szTemp[MAX_PATH+1]; hr = HrRegQuerySzBuffer(hKey, szEntryName, szTemp, &dwSize); if (SUCCEEDED (hr)) { // Check to see if the file exists // if (!m_fForAllUsers) { ExpandEnvironmentStringsForUserW(hPrimaryToken, szTemp, szCmpFilePath, MAX_PATH); } else { lstrcpyW(szCmpFilePath, szTemp); } hFile = CreateFile(szCmpFilePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (INVALID_HANDLE_VALUE == hFile) { hr = HRESULT_FROM_WIN32(GetLastError()); } else { CloseHandle(hFile); hr = S_OK; } } RegCloseKey(hKey); } } } if (!m_fForAllUsers) { if (hImpersonationToken) { CloseHandle(hImpersonationToken); } if (hPrimaryToken) { CloseHandle(hPrimaryToken); } // If it's a valid key that is not NULL then we can close it because we opened it. if (hBaseKey) { // Close the handle opened by RtlOpenCurrentUser // NtClose(hBaseKey); } } if (SUCCEEDED(hrImpersonate)) { hr = CoRevertToSelf (); TraceError ("HrGetCmpFileLocation -- CoRevertToSelf", hr); } } TraceError ("CDialupConnection::HrGetCmpFileLocation", hr); return hr; } //+--------------------------------------------------------------------------- // // Member: CDialupConnection::HrEnsureCmStringsLoaded // // Purpose: This function ensures that the CM specific member vars for dialup // are loaded and usable by CM specific functions. Please NOTE that // calling EnsureCmStringsAreLoaded requires the calling client to // properly setup CoSetProxyBlanket for a private user profile. // // Arguments: none // // Returns: S_OK or an error code // HRESULT CDialupConnection::HrEnsureCmStringsLoaded() { HRESULT hr = S_OK; if (!m_fCmPathsLoaded) { WCHAR szwCmpFile[MAX_PATH]; WCHAR szwCmsFile[MAX_PATH]; WCHAR szwDrive[MAX_PATH]; WCHAR szwDir[MAX_PATH]; WCHAR szwFileName[MAX_PATH]; WCHAR szwExtension[MAX_PATH]; WCHAR szwProfileDir[MAX_PATH]; WCHAR szwCmDir[MAX_PATH]; int nNumChars; hr = HrGetCmpFileLocation(PszwPbkFile (), PszwEntryName (), szwCmpFile); if (SUCCEEDED(hr)) { // Now split the path // _wsplitpath(szwCmpFile, szwDrive, szwDir, szwFileName, szwExtension); // Now construct the path to the cms file // nNumChars = wsprintfW(szwCmsFile, L"%s%s%s\\%s%s", szwDrive, szwDir, szwFileName, szwFileName, L".cms"); ASSERT(nNumChars < celems(szwCmsFile)); // Now construct the profile dir path // nNumChars = wsprintfW(szwProfileDir, L"%s%s%s\\", szwDrive, szwDir, szwFileName); ASSERT(nNumChars < celems(szwProfileDir)); // Now construct the CM dir path // nNumChars = wsprintfW(szwCmDir, L"%s%s", szwDrive, szwDir); ASSERT(nNumChars < celems(szwCmDir)); // Now transfer to the member variables // m_strCmsFile = szwCmsFile; m_strProfileDir = szwProfileDir; // remember this already has the trailing slash m_strCmDir = szwCmDir; // remember this already has the trailing slash m_strShortServiceName = szwFileName; m_fCmPathsLoaded = TRUE; } } TraceError ("CDialupConnection::HrEnsureCmStringsLoaded", hr); return hr; } // INetDefaultConnection //+--------------------------------------------------------------------------- // // Member: CDialupConnection::SetDefault // // Purpose: Set the default RAS connection // // Arguments: TRUE to set as default connection. FALSE to unset it // // Returns: S_OK or an error code // HRESULT CDialupConnection::SetDefault(IN BOOL bDefault) { HRESULT hr = S_OK; HRESULT hrT = S_OK; if (!m_fInitialized) { hr = E_UNEXPECTED; } else { hr = HrEnsureEntryPropertiesCached (); if (SUCCEEDED(hr)) { RASAUTODIALENTRY adEntry; ZeroMemory(&adEntry, sizeof(adEntry)); adEntry.dwSize = sizeof(adEntry); if (bDefault) { wcsncpy(adEntry.szEntry, PszwEntryName(), sizeof(adEntry.szEntry) / sizeof(TCHAR)); } hrT = CoImpersonateClient(); if (SUCCEEDED(hrT)) { DWORD dwErr = RasSetAutodialAddress( NULL, NULL, &adEntry, sizeof(adEntry), 1); if (dwErr != NO_ERROR) { hr = HRESULT_FROM_WIN32(dwErr); } hrT = CoRevertToSelf(); } } } TraceError ("CDialupConnection::SetDefault", hr); return hr; } //+--------------------------------------------------------------------------- // // Member: CDialupConnection::GetDefault // // Purpose: Get the default RAS connection // // Arguments: pbDefault - Is this the default connection // // Returns: S_OK or an error code // HRESULT CDialupConnection::GetDefault (OUT BOOL* pbDefault) { HRESULT hr = S_OK; if (!m_fInitialized) { hr = E_UNEXPECTED; } else { hr = HrEnsureEntryPropertiesCached (); if (SUCCEEDED(hr)) { if (m_dwFlagsPriv & REED_F_Default) { *pbDefault = TRUE; } else { *pbDefault = FALSE; } } } TraceError ("CDialupConnection::GetDefault", hr); return hr; } //+--------------------------------------------------------------------------- // // Member: CDialupConnection::GetPropertiesEx // // Purpose: Get the GetPropertiesEx for this connection // // Arguments: ppConnectionPropertiesEx [out] // // Returns: S_OK or an error code // HRESULT CDialupConnection::GetPropertiesEx(OUT NETCON_PROPERTIES_EX** ppConnectionPropertiesEx) { HRESULT hr = S_OK; *ppConnectionPropertiesEx = NULL; if (!m_fInitialized) { hr = E_UNEXPECTED; } else { NETCON_PROPERTIES* pProps; NETCON_PROPERTIES_EX* pPropsEx = reinterpret_cast(CoTaskMemAlloc(sizeof(NETCON_PROPERTIES_EX))); if (pPropsEx) { ZeroMemory(pPropsEx, sizeof(NETCON_PROPERTIES_EX)); hr = GetProperties(&pProps); if (SUCCEEDED(hr)) { hr = HrBuildPropertiesExFromProperties(pProps, pPropsEx, dynamic_cast(this)); if (SUCCEEDED(hr)) { pPropsEx->bstrPhoneOrHostAddress = SysAllocString(m_strPhoneNumber.c_str()); *ppConnectionPropertiesEx = pPropsEx; } FreeNetconProperties(pProps); } if (FAILED(hr)) { CoTaskMemFree(pPropsEx); } } else { hr = E_OUTOFMEMORY; } } TraceError ("CDialupConnection::GetPropertiesEx", hr); return hr; }