mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1898 lines
61 KiB
1898 lines
61 KiB
//+---------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
// Copyright (C) Microsoft Corporation, 1997-2001.
|
|
//
|
|
// File: C O N M A N . C P P
|
|
//
|
|
// Contents: Connection manager.
|
|
//
|
|
// Notes:
|
|
//
|
|
// Author: shaunco 21 Sep 1997
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
#include "pch.h"
|
|
#pragma hdrstop
|
|
#include <dbt.h>
|
|
#include <ndisguid.h>
|
|
#include "conman.h"
|
|
#include "dialup.h"
|
|
#include "enum.h"
|
|
#include "enumw.h"
|
|
#include "ncnetcon.h"
|
|
#include "ncreg.h"
|
|
#include "nminit.h"
|
|
#if DBG
|
|
#include "ncras.h"
|
|
#endif // DBG
|
|
#include <wmium.h>
|
|
#include "cmutil.h"
|
|
#include <shlwapi.h>
|
|
#include <shfolder.h>
|
|
#include "cobase.h"
|
|
#define SECURITY_WIN32
|
|
#include <security.h>
|
|
#include <wzcsvc.h>
|
|
|
|
bool operator < (const GUID& rguid1, const GUID& rguid2)
|
|
{
|
|
return memcmp(&rguid1, &rguid2, sizeof(GUID)) < 0;
|
|
}
|
|
|
|
static const WCHAR c_szRegKeyClassManagers [] = L"System\\CurrentControlSet\\Control\\Network\\Connections";
|
|
static const WCHAR c_szRegValClassManagers [] = L"ClassManagers";
|
|
|
|
volatile CConnectionManager* CConnectionManager::g_pConMan = NULL;
|
|
volatile BOOL CConnectionManager::g_fInUse = FALSE;
|
|
|
|
bool operator == (const NETCON_PROPERTIES& rProps1, const NETCON_PROPERTIES& rProps2)
|
|
{
|
|
return (IsEqualGUID(rProps1.clsidThisObject, rProps2.clsidThisObject) &&
|
|
IsEqualGUID(rProps1.clsidUiObject, rProps2.clsidUiObject) &&
|
|
(rProps1.dwCharacter == rProps2.dwCharacter) &&
|
|
IsEqualGUID(rProps1.guidId, rProps2.guidId) &&
|
|
(rProps1.MediaType == rProps2.MediaType) &&
|
|
(rProps1.pszwDeviceName == rProps2.pszwDeviceName) &&
|
|
(rProps1.pszwName == rProps2.pszwName) &&
|
|
(rProps1.Status == rProps2.Status));
|
|
}
|
|
|
|
const DWORD MAX_DISABLE_EVENT_TIMEOUT = 0xFFFF;
|
|
|
|
//static
|
|
BOOL
|
|
CConnectionManager::FHasActiveConnectionPoints ()
|
|
{
|
|
BOOL fRet = FALSE;
|
|
|
|
// Note our intent to use g_pConMan. We may find out that it is not
|
|
// available for use, but setting g_fInUse to TRUE prevents FinalRelease
|
|
// from allowing the object to be destroyed while we are using it.
|
|
//
|
|
g_fInUse = TRUE;
|
|
|
|
// Save g_pConMan into a local variable since we have to test and use
|
|
// it atomically. If we tested g_pConMan directly and then used it
|
|
// directly, it may have been set to NULL by FinalRelease in between
|
|
// our test and use. (Uh, which would be bad.)
|
|
//
|
|
// The const_cast is because g_pConMan is declared volatile.
|
|
//
|
|
CConnectionManager* pConMan = const_cast<CConnectionManager*>(g_pConMan);
|
|
if (pConMan)
|
|
{
|
|
pConMan->Lock();
|
|
|
|
IUnknown** ppUnk;
|
|
for (ppUnk = pConMan->m_vec.begin();
|
|
ppUnk < pConMan->m_vec.end();
|
|
ppUnk++)
|
|
{
|
|
if (ppUnk && *ppUnk)
|
|
{
|
|
fRet = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
pConMan->Unlock();
|
|
}
|
|
|
|
// Now that we are finished using the object, indicate so. FinalRelease
|
|
// may be waiting for this condition in which case the object will soon
|
|
// be destroyed.
|
|
//
|
|
g_fInUse = FALSE;
|
|
|
|
return fRet;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CConnectionManager::FinalRelease
|
|
//
|
|
// Purpose: COM destructor
|
|
//
|
|
// Arguments:
|
|
// (none)
|
|
//
|
|
// Returns: nothing
|
|
//
|
|
// Author: shaunco 21 Sep 1997
|
|
//
|
|
// Notes:
|
|
//
|
|
VOID
|
|
CConnectionManager::FinalRelease ()
|
|
{
|
|
TraceFileFunc(ttidConman);
|
|
|
|
if (m_hRegClassManagerKey)
|
|
{
|
|
RegCloseKey(m_hRegClassManagerKey);
|
|
m_hRegClassManagerKey = NULL;
|
|
}
|
|
|
|
NTSTATUS Status = RtlDeregisterWait(m_hRegNotifyWait);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TraceError("Could not deregister Registry Change Notification", HrFromLastWin32Error());
|
|
}
|
|
m_hRegNotifyWait = NULL;
|
|
|
|
if (m_hRegNotify)
|
|
{
|
|
CloseHandle(m_hRegNotify);
|
|
m_hRegNotify = NULL;
|
|
}
|
|
|
|
// Unregister for PnP device events if we successfully registered for
|
|
// them.
|
|
//
|
|
if (m_hDevNotify)
|
|
{
|
|
TraceTag (ttidConman, "Calling UnregisterDeviceNotification...");
|
|
|
|
if (!UnregisterDeviceNotification (m_hDevNotify))
|
|
{
|
|
TraceHr (ttidError, FAL, HrFromLastWin32Error(), FALSE,
|
|
"UnregisterDeviceNotification");
|
|
}
|
|
}
|
|
|
|
(VOID) HrEnsureRegisteredOrDeregisteredWithWmi (FALSE);
|
|
|
|
// Revoke the global connection manager pointer so that subsequent calls
|
|
// to NotifyClientsOfEvent on other threads will do nothing.
|
|
//
|
|
g_pConMan = NULL;
|
|
|
|
// Wait for g_fInUse to become FALSE. NotifyClientsOfEvent will set
|
|
// this to TRUE while it is using us.
|
|
// Keep track of the number of times we sleep and trace it for
|
|
// informational purposes. If we see that we are waiting quite a few
|
|
// number of times, increase the wait period.
|
|
//
|
|
#ifdef ENABLETRACE
|
|
if (g_fInUse)
|
|
{
|
|
TraceTag (ttidConman, "CConnectionManager::FinalRelease is waiting "
|
|
"for NotifyClientsOfEvent to finish...");
|
|
}
|
|
#endif
|
|
|
|
ULONG cSleeps = 0;
|
|
const DWORD nMilliseconds = 0;
|
|
while (g_fInUse)
|
|
{
|
|
cSleeps++;
|
|
Sleep (nMilliseconds);
|
|
}
|
|
|
|
#ifdef ENABLETRACE
|
|
if (cSleeps)
|
|
{
|
|
TraceTag (ttidConman, "CConnectionManager::FinalRelease slept %d "
|
|
"times. (%d ms each time.)",
|
|
cSleeps, nMilliseconds);
|
|
}
|
|
#endif
|
|
|
|
// Release our class managers.
|
|
//
|
|
for (CLASSMANAGERMAP::iterator iter = m_mapClassManagers.begin(); iter != m_mapClassManagers.end(); iter++)
|
|
{
|
|
ReleaseObj (iter->second);
|
|
}
|
|
|
|
TraceTag (ttidConman, "Connection manager being destroyed");
|
|
}
|
|
|
|
inline
|
|
LPVOID OffsetToPointer(LPVOID pStart, DWORD dwNumBytes)
|
|
{
|
|
DWORD_PTR dwPtr;
|
|
|
|
dwPtr = reinterpret_cast<DWORD_PTR>(pStart);
|
|
|
|
dwPtr += dwNumBytes;
|
|
|
|
return reinterpret_cast<LPVOID>(dwPtr);
|
|
}
|
|
|
|
//
|
|
// Must match type of NOTIFICATIONCALLBACK
|
|
//
|
|
VOID
|
|
WINAPI
|
|
WmiEventCallback (
|
|
PWNODE_HEADER Wnode,
|
|
UINT_PTR NotificationContext)
|
|
{
|
|
TraceTag (ttidConman,
|
|
"WmiEventCallback called...");
|
|
|
|
TraceTag(ttidEvents, "Flags: %d", Wnode->Flags);
|
|
|
|
if (WNODE_FLAG_SINGLE_INSTANCE == (WNODE_FLAG_SINGLE_INSTANCE & Wnode->Flags))
|
|
{
|
|
PWNODE_SINGLE_INSTANCE pInstance = reinterpret_cast<PWNODE_SINGLE_INSTANCE>(Wnode);
|
|
LPCWSTR lpszDevice = NULL;
|
|
LPWSTR lpszGuid;
|
|
GUID guidAdapter;
|
|
|
|
lpszDevice = reinterpret_cast<LPCWSTR>(OffsetToPointer(pInstance, pInstance->DataBlockOffset));
|
|
|
|
lpszGuid = wcsrchr(lpszDevice, L'{');
|
|
|
|
TraceTag(ttidEvents, "Adapter Guid From NDIS for Media Status Change Event: %S", lpszGuid);
|
|
if (SUCCEEDED(CLSIDFromString(lpszGuid, &guidAdapter)))
|
|
{
|
|
CONMAN_EVENT* pEvent;
|
|
|
|
pEvent = new CONMAN_EVENT;
|
|
|
|
if(pEvent)
|
|
{
|
|
pEvent->ConnectionManager = CONMAN_LAN;
|
|
pEvent->guidId = guidAdapter;
|
|
pEvent->Type = CONNECTION_STATUS_CHANGE;
|
|
|
|
if (IsEqualGUID(Wnode->Guid, GUID_NDIS_STATUS_MEDIA_CONNECT))
|
|
{
|
|
pEvent->Status = NCS_CONNECTED;
|
|
}
|
|
else if (IsEqualGUID(Wnode->Guid, GUID_NDIS_STATUS_MEDIA_DISCONNECT))
|
|
{
|
|
pEvent->Status = NCS_MEDIA_DISCONNECTED;
|
|
}
|
|
else
|
|
{
|
|
AssertSz(FALSE, "We never registered for this event ... WMI may be having internal issues.");
|
|
MemFree(pEvent);
|
|
return;
|
|
}
|
|
if (!QueueUserWorkItemInThread(LanEventWorkItem, reinterpret_cast<PVOID>(pEvent), EVENTMGR_CONMAN))
|
|
{
|
|
FreeConmanEvent(pEvent);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LanEventNotify (REFRESH_ALL, NULL, NULL, NULL);
|
|
}
|
|
}
|
|
|
|
HRESULT
|
|
CConnectionManager::HrEnsureRegisteredOrDeregisteredWithWmi (
|
|
BOOL fRegister)
|
|
{
|
|
// Already registered or deregistered?
|
|
//
|
|
if (!!m_fRegisteredWithWmi == !!fRegister)
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
m_fRegisteredWithWmi = !!fRegister;
|
|
|
|
HRESULT hr = S_OK;
|
|
DWORD dwErr;
|
|
INT i;
|
|
const GUID* apguid [] =
|
|
{
|
|
&GUID_NDIS_STATUS_MEDIA_CONNECT,
|
|
&GUID_NDIS_STATUS_MEDIA_DISCONNECT,
|
|
};
|
|
|
|
TraceTag (ttidConman,
|
|
"Calling WmiNotificationRegistration to %s for NDIS media events...",
|
|
(fRegister) ? "register" : "unregister");
|
|
|
|
for (i = 0; i < celems(apguid); i++)
|
|
{
|
|
dwErr = WmiNotificationRegistration (
|
|
const_cast<GUID*>(apguid[i]),
|
|
!!fRegister, // !! for BOOL to BOOLEAN
|
|
WmiEventCallback,
|
|
0,
|
|
NOTIFICATION_CALLBACK_DIRECT);
|
|
|
|
hr = HRESULT_FROM_WIN32 (dwErr);
|
|
TraceHr (ttidError, FAL, hr, FALSE, "WmiNotificationRegistration");
|
|
}
|
|
|
|
TraceHr (ttidError, FAL, hr, FALSE, "HrEnsureRegisteredOrDeregisteredWithWmi");
|
|
return hr;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CConnectionManager::NotifyClientsOfEvent
|
|
//
|
|
// Purpose: Notify our connection points that this object has changed
|
|
// state in some way and that a re-enumeration is needed.
|
|
//
|
|
// Arguments:
|
|
// (none)
|
|
//
|
|
// Returns: nothing
|
|
//
|
|
// Author: shaunco 20 Mar 1998
|
|
//
|
|
// Notes: This is a static function. No this pointer is passed.
|
|
//
|
|
// static
|
|
VOID CConnectionManager::NotifyClientsOfEvent (
|
|
CONMAN_EVENT* pEvent)
|
|
{
|
|
HRESULT hr;
|
|
// Let's be sure we only do work if the service state is still running.
|
|
// If we have a stop pending for example, we don't need to do anything.
|
|
//
|
|
if (SERVICE_RUNNING != _Module.DwServiceStatus ())
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Note our intent to use g_pConMan. We may find out that it is not
|
|
// available for use, but setting g_fInUse to TRUE prevents FinalRelease
|
|
// from allowing the object to be destroyed while we are using it.
|
|
//
|
|
g_fInUse = TRUE;
|
|
|
|
// Save g_pConMan into a local variable since we have to test and use
|
|
// it atomically. If we tested g_pConMan directly and then used it
|
|
// directly, it may have been set to NULL by FinalRelease in between
|
|
// our test and use. (Uh, which would be bad.)
|
|
//
|
|
// The const_cast is because g_pConMan is declared volatile.
|
|
//
|
|
CConnectionManager* pConMan = const_cast<CConnectionManager*>(g_pConMan);
|
|
if (pConMan)
|
|
{
|
|
ULONG cpUnk;
|
|
IUnknown** apUnk;
|
|
|
|
hr = HrCopyIUnknownArrayWhileLocked (
|
|
pConMan,
|
|
&pConMan->m_vec,
|
|
&cpUnk,
|
|
&apUnk);
|
|
|
|
if (SUCCEEDED(hr) && cpUnk && apUnk)
|
|
{
|
|
#ifdef DBG
|
|
CHAR szClientList[MAX_PATH];
|
|
ZeroMemory(szClientList, MAX_PATH);
|
|
LPSTR pszClientList = szClientList;
|
|
|
|
ITERUSERNOTIFYMAP iter;
|
|
for (iter = pConMan->m_mapNotify.begin(); iter != pConMan->m_mapNotify.end(); iter++)
|
|
{
|
|
pszClientList += sprintf(pszClientList, "%d ", iter->second->dwCookie);
|
|
if (pszClientList > (szClientList + MAX_PATH-50) )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (iter != pConMan->m_mapNotify.end())
|
|
{
|
|
pszClientList += sprintf(pszClientList, "(more)");
|
|
}
|
|
|
|
TraceTag (ttidConman,
|
|
"NotifyClientsOfEvent: Notifying %d clients. Cookies: %s)",
|
|
cpUnk, szClientList);
|
|
#endif
|
|
for (ULONG i = 0; i < cpUnk; i++)
|
|
{
|
|
INetConnectionNotifySink* pSink = NULL;
|
|
BOOL fFireEventOnSink = FALSE;
|
|
|
|
hr = apUnk[i]->QueryInterface(IID_INetConnectionNotifySink, reinterpret_cast<LPVOID*>(&pSink));
|
|
|
|
ReleaseObj(apUnk[i]);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = CoSetProxyBlanket (
|
|
pSink,
|
|
RPC_C_AUTHN_WINNT, // use NT default security
|
|
RPC_C_AUTHZ_NONE, // use NT default authentication
|
|
NULL, // must be null if default
|
|
RPC_C_AUTHN_LEVEL_CALL, // call
|
|
RPC_C_IMP_LEVEL_IDENTIFY,
|
|
NULL, // use process token
|
|
EOAC_DEFAULT);
|
|
|
|
switch (pEvent->Type)
|
|
{
|
|
case CONNECTION_ADDED:
|
|
|
|
Assert (pEvent);
|
|
Assert (pEvent->pPropsEx);
|
|
|
|
TraceTag(ttidEvents, "Characteristics: %s", DbgNccf(pEvent->pPropsEx->dwCharacter));
|
|
|
|
if (!(NCCF_ALL_USERS == (pEvent->pPropsEx->dwCharacter & NCCF_ALL_USERS)))
|
|
{
|
|
const WCHAR* pchw = reinterpret_cast<const WCHAR*>(pEvent->pPropsEx->bstrPersistData);
|
|
const WCHAR* pchwMax;
|
|
PCWSTR pszwPhonebook;
|
|
WCHAR LeadWord = PersistDataLead;
|
|
WCHAR TrailWord = PersistDataTrail;
|
|
IUnknown* pUnkSink = NULL;
|
|
|
|
hr = pSink->QueryInterface(IID_IUnknown, reinterpret_cast<LPVOID*>(&pUnkSink));
|
|
AssertSz(SUCCEEDED(hr), "Please explain how this happened...");
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// The last valid pointer for the embedded strings.
|
|
//
|
|
pchwMax = reinterpret_cast<const WCHAR*>(pEvent->pbPersistData + pEvent->cbPersistData
|
|
- (sizeof (GUID) +
|
|
sizeof (BOOL) +
|
|
sizeof (TrailWord)));
|
|
|
|
if (pchw && (LeadWord == *pchw))
|
|
{
|
|
TraceTag(ttidEvents, "Found Correct Lead Character.");
|
|
// Skip past our lead byte.
|
|
//
|
|
pchw++;
|
|
|
|
// Get PhoneBook path. Search for the terminating null and make sure
|
|
// we find it before the end of the buffer. Using lstrlen to skip
|
|
// the string can result in an an AV in the event the string is
|
|
// not actually null-terminated.
|
|
//
|
|
for (pszwPhonebook = pchw; *pchw != L'\0' ; pchw++)
|
|
{
|
|
if (pchw >= pchwMax)
|
|
{
|
|
pszwPhonebook = NULL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
TraceTag(ttidEvents, "Found Valid Phonebook: %S", (pszwPhonebook) ? L"TRUE" : L"FALSE");
|
|
|
|
if (pszwPhonebook)
|
|
{
|
|
ITERUSERNOTIFYMAP iter = pConMan->m_mapNotify.find(pUnkSink);
|
|
if (iter != pConMan->m_mapNotify.end())
|
|
{
|
|
tstring& strUserDataPath = iter->second->szUserProfilesPath;
|
|
TraceTag(ttidEvents, "Comparing stored Path: %S to Phonebook Path: %S",
|
|
strUserDataPath.c_str(), pszwPhonebook);
|
|
if (_wcsnicmp(pszwPhonebook, strUserDataPath.c_str(), strUserDataPath.length()) == 0)
|
|
{
|
|
fFireEventOnSink = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TraceTag(ttidError, "Could not find Path for NotifySink: 0x%08x", pUnkSink);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Some other devices do not use this Format, but need to be sent events.
|
|
fFireEventOnSink = TRUE;
|
|
}
|
|
|
|
ReleaseObj(pUnkSink);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TraceTag(ttidEvents, "All User Connection");
|
|
fFireEventOnSink = TRUE;
|
|
}
|
|
|
|
if (fFireEventOnSink)
|
|
{
|
|
TraceTag (ttidEvents,
|
|
"Notifying ConnectionAdded... (pSink=0x%p)",
|
|
pSink);
|
|
|
|
hr = pSink->ConnectionAdded (
|
|
pEvent->pPropsEx);
|
|
}
|
|
|
|
break;
|
|
|
|
case CONNECTION_BANDWIDTH_CHANGE:
|
|
TraceTag (ttidEvents,
|
|
"Notifying ConnectionBandWidthChange... (pSink=0x%p)",
|
|
pSink);
|
|
|
|
hr = pSink->ConnectionBandWidthChange (&pEvent->guidId);
|
|
break;
|
|
|
|
case CONNECTION_DELETED:
|
|
TraceTag (ttidEvents,
|
|
"Notifying ConnectionDeleted... (pSink=0x%p)",
|
|
pSink);
|
|
|
|
hr = pSink->ConnectionDeleted (&pEvent->guidId);
|
|
break;
|
|
|
|
case CONNECTION_MODIFIED:
|
|
Assert (pEvent->pPropsEx);
|
|
|
|
TraceTag (ttidEvents,
|
|
"Notifying ConnectionModified... (pSink=0x%p)",
|
|
pSink);
|
|
|
|
hr = pSink->ConnectionModified (pEvent->pPropsEx);
|
|
|
|
break;
|
|
|
|
case CONNECTION_RENAMED:
|
|
TraceTag (ttidEvents,
|
|
"Notifying ConnectionRenamed... (pSink=0x%p)",
|
|
pSink);
|
|
|
|
hr = pSink->ConnectionRenamed (&pEvent->guidId,
|
|
pEvent->szNewName);
|
|
break;
|
|
|
|
case CONNECTION_STATUS_CHANGE:
|
|
TraceTag (ttidEvents,
|
|
"Notifying ConnectionStatusChange... (pSink=0x%p)",
|
|
pSink);
|
|
|
|
TraceTag(ttidEvents, "Status changed to: %s", DbgNcs(pEvent->Status));
|
|
|
|
hr = pSink->ConnectionStatusChange (&pEvent->guidId,
|
|
pEvent->Status);
|
|
break;
|
|
|
|
case REFRESH_ALL:
|
|
TraceTag (ttidEvents,
|
|
"Notifying RefreshAll... (pSink=0x%p)",
|
|
pSink);
|
|
|
|
hr = pSink->RefreshAll ();
|
|
break;
|
|
|
|
case CONNECTION_ADDRESS_CHANGE:
|
|
TraceTag (ttidEvents,
|
|
"Notifying ConnectionAddressChange... (pSink=0x%p)",
|
|
pSink);
|
|
|
|
hr = pSink->ConnectionAddressChange(&pEvent->guidId);
|
|
break;
|
|
|
|
case CONNECTION_BALLOON_POPUP:
|
|
TraceTag (ttidEvents,
|
|
"Notifying ConnectionStatusChange... (pSink=0x%p)",
|
|
pSink);
|
|
|
|
hr = pSink->ShowBalloon(&pEvent->guidId, pEvent->szCookie, pEvent->szBalloonText);
|
|
break;
|
|
|
|
case DISABLE_EVENTS:
|
|
TraceTag (ttidEvents,
|
|
"Notifying DisableEvents... (pSink=0x%p)",
|
|
pSink);
|
|
|
|
hr = pSink->DisableEvents(pEvent->fDisable, pEvent->ulDisableTimeout);
|
|
break;
|
|
|
|
default:
|
|
TraceTag(ttidEvents, "Event Type Passed: %d", pEvent->Type);
|
|
AssertSz (FALSE, "Invalid Type specified in pEvent");
|
|
break;
|
|
}
|
|
TraceErrorOptional("pSink call failed: ", hr, (S_FALSE == hr) );
|
|
|
|
if ( (HRESULT_FROM_WIN32(RPC_S_SERVER_UNAVAILABLE) == hr) ||
|
|
(HRESULT_FROM_WIN32(RPC_S_CALL_FAILED_DNE) == hr) ||
|
|
(RPC_E_SERVER_DIED == hr) ||
|
|
(RPC_E_DISCONNECTED == hr) ||
|
|
(HRESULT_FROM_WIN32(RPC_S_CALL_FAILED) == hr) )
|
|
{
|
|
IUnknown* pUnkSink = NULL;
|
|
HRESULT hrT = pSink->QueryInterface(IID_IUnknown, reinterpret_cast<LPVOID*>(&pUnkSink));
|
|
if (SUCCEEDED(hrT))
|
|
{
|
|
ITERUSERNOTIFYMAP iter = pConMan->m_mapNotify.find(pUnkSink);
|
|
if (iter != pConMan->m_mapNotify.end())
|
|
{
|
|
TraceTag(ttidError, "Dead client detected. Removing notify advise for: %S", iter->second->szUserName.c_str());
|
|
|
|
hrT = pConMan->Unadvise(iter->second->dwCookie);
|
|
}
|
|
ReleaseObj(pUnkSink);
|
|
}
|
|
TraceHr (ttidError, FAL, hrT, S_FALSE == hrT, "Error removing notify advise.");
|
|
}
|
|
|
|
ReleaseObj(pSink);
|
|
}
|
|
}
|
|
MemFree (apUnk);
|
|
}
|
|
}
|
|
|
|
|
|
// Now that we are finished using the object, indicate so. FinalRelease
|
|
// may be waiting for this condition in which case the object will soon
|
|
// be destroyed.
|
|
//
|
|
|
|
g_fInUse = FALSE;
|
|
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CConnectionManager::HrEnsureClassManagersLoaded
|
|
//
|
|
// Purpose: Loads the class managers if they have not been loaded yet.
|
|
//
|
|
// Arguments:
|
|
// (none)
|
|
//
|
|
// Returns: S_OK or an error code.
|
|
//
|
|
// Author: shaunco 10 Dec 1997
|
|
//
|
|
// Notes:
|
|
//
|
|
HRESULT
|
|
CConnectionManager::HrEnsureClassManagersLoaded ()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
// Need to protect m_mapClassManagers for the case that two clients
|
|
// get our object simultaneously and call a method which invokes
|
|
// this method. Need to transition m_mapClassManagers from being
|
|
// empty to being full in one atomic operation.
|
|
//
|
|
CExceptionSafeComObjectLock EsLock (this);
|
|
|
|
// If our vector of class managers is emtpy, try to load them.
|
|
// This will certainly be the case if we haven't tried to load them yet.
|
|
// If will also be the case when no class managers are registered.
|
|
// This isn't likely because we register them in hivesys.inf, but it
|
|
// shouldn't hurt to keep trying if they're really are none registered.
|
|
//
|
|
if (m_mapClassManagers.empty())
|
|
{
|
|
TraceTag (ttidConman, "Loading class managers...");
|
|
|
|
// Load the registered class managers.
|
|
//
|
|
|
|
// Open the registry key where the class managers are registered.
|
|
//
|
|
HKEY hkey;
|
|
hr = HrRegOpenKeyEx (HKEY_LOCAL_MACHINE,
|
|
c_szRegKeyClassManagers, KEY_READ, &hkey);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Read the multi-sz of class manager CLSIDs.
|
|
//
|
|
PWSTR pmsz;
|
|
hr = HrRegQueryMultiSzWithAlloc (hkey, c_szRegValClassManagers,
|
|
&pmsz);
|
|
if (S_OK == hr)
|
|
{
|
|
(VOID) HrNmWaitForClassObjectsToBeRegistered ();
|
|
|
|
// For each CLSID, create the object and request its
|
|
// INetConnectionManager interface.
|
|
//
|
|
for (PCWSTR pszClsid = pmsz;
|
|
*pszClsid;
|
|
pszClsid += wcslen (pszClsid) + 1)
|
|
{
|
|
// Convert the string to a CLSID. If it fails, skip it.
|
|
//
|
|
CLSID clsid;
|
|
if (FAILED(CLSIDFromString ((LPOLESTR)pszClsid, &clsid)))
|
|
{
|
|
TraceTag (ttidConman, "Skipping bogus CLSID (%S)",
|
|
pszClsid);
|
|
continue;
|
|
}
|
|
|
|
// Create the class manager and add it to our list.
|
|
//
|
|
INetConnectionManager* pConMan;
|
|
|
|
hr = CoCreateInstance (
|
|
clsid, NULL,
|
|
CLSCTX_ALL | CLSCTX_NO_CODE_DOWNLOAD,
|
|
IID_INetConnectionManager,
|
|
reinterpret_cast<VOID**>(&pConMan));
|
|
|
|
TraceHr (ttidError, FAL, hr, FALSE,
|
|
"CConnectionManager::HrEnsureClassManagersLoaded: "
|
|
"CoCreateInstance failed for class manager %S.",
|
|
pszClsid);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
TraceTag (ttidConman, "Loaded class manager %S",
|
|
pszClsid);
|
|
|
|
Assert (pConMan);
|
|
|
|
if (m_mapClassManagers.find(clsid) != m_mapClassManagers.end())
|
|
{
|
|
AssertSz(FALSE, "Attempting to insert the same class manager twice!");
|
|
}
|
|
else
|
|
{
|
|
m_mapClassManagers[clsid] = pConMan;
|
|
}
|
|
}
|
|
|
|
/*
|
|
// If CoCreateInstance starts failing again on retail builds, this can
|
|
// be helpful.
|
|
else
|
|
{
|
|
CHAR psznBuf [512];
|
|
wsprintfA (psznBuf, "NETCFG: CoCreateInstance failed "
|
|
"(0x%08x) on class manager %i.\n",
|
|
hr, m_mapClassManagers.size ());
|
|
OutputDebugStringA (psznBuf);
|
|
}
|
|
*/
|
|
}
|
|
|
|
MemFree (pmsz);
|
|
}
|
|
|
|
RegCloseKey (hkey);
|
|
}
|
|
|
|
TraceTag (ttidConman, "Loaded %i class managers",
|
|
m_mapClassManagers.size ());
|
|
}
|
|
|
|
TraceErrorOptional ("CConnectionManager::HrEnsureClassManagersLoaded", hr, (S_FALSE == hr));
|
|
return hr;
|
|
}
|
|
|
|
VOID NTAPI CConnectionManager::RegChangeNotifyHandler(IN LPVOID pContext, IN BOOLEAN fTimerFired)
|
|
{
|
|
TraceTag(ttidConman, "CConnectionManager::RegChangeNotifyHandler (%d)", fTimerFired);
|
|
|
|
CConnectionManager *pThis = reinterpret_cast<CConnectionManager *>(pContext);
|
|
CExceptionSafeComObjectLock EsLock (pThis);
|
|
|
|
list<GUID> lstRegisteredGuids;
|
|
|
|
HKEY hkey;
|
|
HRESULT hr = HrRegOpenKeyEx (HKEY_LOCAL_MACHINE, c_szRegKeyClassManagers, KEY_READ, &hkey);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Read the multi-sz of class manager CLSIDs.
|
|
//
|
|
PWSTR pmsz;
|
|
hr = HrRegQueryMultiSzWithAlloc (hkey, c_szRegValClassManagers,
|
|
&pmsz);
|
|
if (S_OK == hr)
|
|
{
|
|
for (PCWSTR pszClsid = pmsz;
|
|
*pszClsid;
|
|
pszClsid += wcslen (pszClsid) + 1)
|
|
{
|
|
CLSID clsid;
|
|
if (FAILED(CLSIDFromString ((LPOLESTR)pszClsid, &clsid)))
|
|
{
|
|
TraceTag (ttidConman, "Skipping bogus CLSID (%S)", pszClsid);
|
|
continue;
|
|
}
|
|
lstRegisteredGuids.push_back(clsid);
|
|
}
|
|
}
|
|
RegCloseKey(hkey);
|
|
|
|
|
|
BOOL bFound;
|
|
do
|
|
{
|
|
bFound = FALSE;
|
|
|
|
CLASSMANAGERMAP::iterator iterClassMgr;
|
|
|
|
for (iterClassMgr = pThis->m_mapClassManagers.begin(); iterClassMgr != pThis->m_mapClassManagers.end(); iterClassMgr++)
|
|
{
|
|
if (find(lstRegisteredGuids.begin(), lstRegisteredGuids.end(), iterClassMgr->first) == lstRegisteredGuids.end())
|
|
{
|
|
// Class manager key has been removed
|
|
TraceTag(ttidConman, "Removing class manager");
|
|
bFound = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (bFound)
|
|
{
|
|
ULONG uRefCount = iterClassMgr->second->Release();
|
|
TraceTag(ttidConman, "Releasing class manager - Refcount = %d", uRefCount);
|
|
pThis->m_mapClassManagers.erase(iterClassMgr);
|
|
}
|
|
} while (bFound);
|
|
|
|
for (list<GUID>::iterator iter = lstRegisteredGuids.begin(); iter != lstRegisteredGuids.end(); iter++)
|
|
{
|
|
if (pThis->m_mapClassManagers.find(*iter) == pThis->m_mapClassManagers.end())
|
|
{
|
|
// Class manager key has been added
|
|
TraceTag(ttidConman, "Adding class manager");
|
|
|
|
INetConnectionManager* pConMan;
|
|
hr = CoCreateInstance (
|
|
*iter,
|
|
NULL,
|
|
CLSCTX_ALL | CLSCTX_NO_CODE_DOWNLOAD,
|
|
IID_INetConnectionManager,
|
|
reinterpret_cast<VOID**>(&pConMan));
|
|
|
|
TraceHr (ttidError, FAL, hr, FALSE,
|
|
"CConnectionManager::RegChangeNotifyHandler: CoCreateInstance failed for class manager.");
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
TraceTag (ttidConman, "Loaded class manager");
|
|
Assert (pConMan);
|
|
|
|
if (pThis->m_mapClassManagers.find(*iter) != pThis->m_mapClassManagers.end())
|
|
{
|
|
AssertSz(FALSE, "Attempting to insert the same class manager twice!");
|
|
}
|
|
else
|
|
{
|
|
pThis->m_mapClassManagers[*iter] = pConMan;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TraceError("Could not open registry key", HrFromLastWin32Error());
|
|
}
|
|
|
|
TraceError("RegChangeNotifyHandler", hr);
|
|
|
|
LanEventNotify (REFRESH_ALL, NULL, NULL, NULL);
|
|
|
|
RegNotifyChangeKeyValue(pThis->m_hRegClassManagerKey, FALSE, REG_NOTIFY_CHANGE_LAST_SET, pThis->m_hRegNotify, TRUE);
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
// INetConnectionManager
|
|
//
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CConnectionManager::EnumConnections
|
|
//
|
|
// Purpose: Return an INetConnection enumerator.
|
|
//
|
|
// Arguments:
|
|
// Flags [in]
|
|
// ppEnum [out] The enumerator.
|
|
//
|
|
// Returns: S_OK or an error code.
|
|
//
|
|
// Author: shaunco 21 Sep 1997
|
|
//
|
|
// Notes:
|
|
//
|
|
STDMETHODIMP
|
|
CConnectionManager::EnumConnections (
|
|
NETCONMGR_ENUM_FLAGS Flags,
|
|
IEnumNetConnection** ppEnum)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
{
|
|
CExceptionSafeComObjectLock EsLock (this);
|
|
|
|
Assert(FImplies(m_hRegNotify, m_hRegClassManagerKey));
|
|
if (!m_hRegNotify)
|
|
{
|
|
m_hRegNotify = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
if (m_hRegNotify)
|
|
{
|
|
NTSTATUS Status = RtlRegisterWait(&m_hRegNotifyWait, m_hRegNotify, &CConnectionManager::RegChangeNotifyHandler, this, INFINITE, WT_EXECUTEDEFAULT);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
hr = HRESULT_FROM_NT(Status);
|
|
}
|
|
else
|
|
{
|
|
hr = HrRegOpenKeyEx (HKEY_LOCAL_MACHINE, c_szRegKeyClassManagers, KEY_READ, &m_hRegClassManagerKey);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = RegNotifyChangeKeyValue(m_hRegClassManagerKey, FALSE, REG_NOTIFY_CHANGE_LAST_SET, m_hRegNotify, TRUE);
|
|
if (FAILED(hr))
|
|
{
|
|
RegCloseKey(m_hRegClassManagerKey);
|
|
m_hRegClassManagerKey = NULL;
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
Status = RtlDeregisterWait(m_hRegNotifyWait);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
hr = HRESULT_FROM_NT(Status);
|
|
}
|
|
m_hRegNotifyWait = NULL;
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
CloseHandle(m_hRegNotify);
|
|
m_hRegNotify = NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = HrFromLastWin32Error();
|
|
}
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
|
|
// Create and return the enumerator.
|
|
//
|
|
hr = HrEnsureClassManagersLoaded ();
|
|
}
|
|
}
|
|
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
hr = CConnectionManagerEnumConnection::CreateInstance (
|
|
Flags,
|
|
m_mapClassManagers,
|
|
IID_IEnumNetConnection,
|
|
reinterpret_cast<VOID**>(ppEnum));
|
|
}
|
|
TraceErrorOptional ("CConnectionManager::EnumConnections", hr, (S_FALSE == hr));
|
|
return hr;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
// INetConnectionRefresh
|
|
//
|
|
STDMETHODIMP
|
|
CConnectionManager::RefreshAll()
|
|
{
|
|
LanEventNotify (REFRESH_ALL, NULL, NULL, NULL);
|
|
return S_OK;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: CConnectionManager::ConnectionAdded
|
|
//
|
|
// Purpose: Notifies event sinks that a new connection has been added.
|
|
//
|
|
// Arguments:
|
|
// pConnection [in] INetConnection* for new connection.
|
|
//
|
|
// Returns: Standard HRESULT
|
|
//
|
|
// Author: deonb 22 Mar 2001
|
|
//
|
|
// Notes:
|
|
//
|
|
STDMETHODIMP
|
|
CConnectionManager::ConnectionAdded(
|
|
IN INetConnection* pConnection)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
CONMAN_EVENT* pEvent = new CONMAN_EVENT;
|
|
|
|
if (pEvent)
|
|
{
|
|
ZeroMemory(pEvent, sizeof(CONMAN_EVENT));
|
|
pEvent->Type = CONNECTION_ADDED;
|
|
|
|
hr = HrGetPropertiesExFromINetConnection(pConnection, &pEvent->pPropsEx);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (QueueUserWorkItemInThread(ConmanEventWorkItem, pEvent, EVENTMGR_CONMAN))
|
|
{
|
|
return hr;
|
|
}
|
|
hr = E_FAIL;
|
|
}
|
|
FreeConmanEvent(pEvent);
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: CConnectionManager::ConnectionDeleted
|
|
//
|
|
// Purpose: Sends an event to notify of a connection being deleted.
|
|
//
|
|
// Arguments:
|
|
// pguidId [in] GUID of the connectoid
|
|
//
|
|
// Returns: Standard HRESULT
|
|
//
|
|
// Author: ckotze 18 Apr 2001
|
|
//
|
|
// Notes:
|
|
//
|
|
STDMETHODIMP
|
|
CConnectionManager::ConnectionDeleted(
|
|
IN const GUID* pguidId)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
CONMAN_EVENT* pEvent = new CONMAN_EVENT;
|
|
|
|
if (pEvent)
|
|
{
|
|
ZeroMemory(pEvent, sizeof(CONMAN_EVENT));
|
|
pEvent->Type = CONNECTION_DELETED;
|
|
pEvent->guidId = *pguidId;
|
|
|
|
if (!QueueUserWorkItemInThread(ConmanEventWorkItem, reinterpret_cast<PVOID>(pEvent), EVENTMGR_CONMAN))
|
|
{
|
|
hr = E_FAIL;
|
|
FreeConmanEvent(pEvent);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: CConnectionManager::ConnectionRenamed
|
|
//
|
|
// Purpose: Notifies the event sinks of a connection being renamed.
|
|
//
|
|
// Arguments:
|
|
// pConnection [in] INetConnection* for new connection.
|
|
//
|
|
// Returns: Standard HRESULT
|
|
//
|
|
// Author: ckotze 19 Apr 2001
|
|
//
|
|
// Notes:
|
|
//
|
|
STDMETHODIMP
|
|
CConnectionManager::ConnectionRenamed(
|
|
IN INetConnection* pConnection)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
CONMAN_EVENT* pEvent = new CONMAN_EVENT;
|
|
|
|
if (pEvent)
|
|
{
|
|
ZeroMemory(pEvent, sizeof(CONMAN_EVENT));
|
|
pEvent->Type = CONNECTION_RENAMED;
|
|
|
|
hr = HrGetPropertiesExFromINetConnection(pConnection, &pEvent->pPropsEx);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
lstrcpynW (pEvent->szNewName, pEvent->pPropsEx->bstrName, celems(pEvent->szNewName) );
|
|
pEvent->guidId = pEvent->pPropsEx->guidId;
|
|
|
|
if (QueueUserWorkItemInThread(ConmanEventWorkItem, pEvent, EVENTMGR_CONMAN))
|
|
{
|
|
return hr;
|
|
}
|
|
hr = E_FAIL;
|
|
}
|
|
FreeConmanEvent(pEvent);
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: CConnectionManager::ConnectionModified
|
|
//
|
|
// Purpose: Sends an event to notify clients that the connection has been
|
|
// Modified.
|
|
// Arguments:
|
|
// pConnection [in] INetConnection* for new connection.
|
|
//
|
|
// Returns: Standard HRESULT
|
|
//
|
|
// Author: ckotze 22 Mar 2001
|
|
//
|
|
// Notes:
|
|
//
|
|
STDMETHODIMP
|
|
CConnectionManager::ConnectionModified(INetConnection* pConnection)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
CONMAN_EVENT* pEvent = new CONMAN_EVENT;
|
|
|
|
if (pEvent)
|
|
{
|
|
ZeroMemory(pEvent, sizeof(CONMAN_EVENT));
|
|
pEvent->Type = CONNECTION_MODIFIED;
|
|
|
|
hr = HrGetPropertiesExFromINetConnection(pConnection, &pEvent->pPropsEx);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (QueueUserWorkItemInThread(ConmanEventWorkItem, pEvent, EVENTMGR_CONMAN))
|
|
{
|
|
return hr;
|
|
}
|
|
hr = E_FAIL;
|
|
}
|
|
FreeConmanEvent(pEvent);
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: CConnectionManager::ConnectionStatusChanged
|
|
//
|
|
// Purpose: Sends the ShowBalloon event to each applicable netshell
|
|
//
|
|
// Arguments:
|
|
// pguidId [in] GUID of the connectoid
|
|
// ncs [in] New status of the connectoid
|
|
//
|
|
// Returns: Standard HRESULT
|
|
//
|
|
// Author: deonb 22 Mar 2001
|
|
//
|
|
// Notes:
|
|
//
|
|
STDMETHODIMP
|
|
CConnectionManager::ConnectionStatusChanged(
|
|
IN const GUID* pguidId,
|
|
IN const NETCON_STATUS ncs)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
CONMAN_EVENT* pEvent = new CONMAN_EVENT;
|
|
|
|
if (pEvent)
|
|
{
|
|
ZeroMemory(pEvent, sizeof(CONMAN_EVENT));
|
|
pEvent->Type = CONNECTION_STATUS_CHANGE;
|
|
pEvent->guidId = *pguidId;
|
|
pEvent->Status = ncs;
|
|
|
|
if (!QueueUserWorkItemInThread(ConmanEventWorkItem, reinterpret_cast<PVOID>(pEvent), EVENTMGR_CONMAN))
|
|
{
|
|
FreeConmanEvent(pEvent);
|
|
hr = E_FAIL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: CConnectionManager::ShowBalloon
|
|
//
|
|
// Purpose: Sends the ShowBalloon event to each applicable netshell
|
|
//
|
|
// Arguments:
|
|
// pguidId [in] GUID of the connectoid
|
|
// szCookie [in] A specified cookie that will end up at netshell
|
|
// szBalloonText [in] The balloon text
|
|
//
|
|
// Returns: Standard HRESULT
|
|
//
|
|
// Author: deonb 22 Mar 2001
|
|
//
|
|
// Notes:
|
|
//
|
|
STDMETHODIMP
|
|
CConnectionManager::ShowBalloon(
|
|
IN const GUID *pguidId,
|
|
IN const BSTR szCookie,
|
|
IN const BSTR szBalloonText)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
CONMAN_EVENT* pEvent = new CONMAN_EVENT;
|
|
|
|
if (pEvent)
|
|
{
|
|
ZeroMemory(pEvent, sizeof(CONMAN_EVENT));
|
|
pEvent->Type = CONNECTION_BALLOON_POPUP;
|
|
pEvent->guidId = *pguidId;
|
|
pEvent->szCookie = SysAllocStringByteLen(reinterpret_cast<LPCSTR>(szCookie), SysStringByteLen(szCookie));
|
|
pEvent->szBalloonText = SysAllocString(szBalloonText);
|
|
|
|
if (!QueueUserWorkItemInThread(ConmanEventWorkItem, reinterpret_cast<PVOID>(pEvent), EVENTMGR_CONMAN))
|
|
{
|
|
hr = E_FAIL;
|
|
FreeConmanEvent(pEvent);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: CConnectionManager::DisableEvents
|
|
//
|
|
// Purpose: Disable netshell processing of events for x milliseconds
|
|
//
|
|
// Arguments:
|
|
// fDisable [in] TRUE to disable EVENT processing, FALSE to renable
|
|
// ulDisableTimeout [in] Number of milliseconds to disable event processing for
|
|
//
|
|
// Returns: Standard HRESULT
|
|
//
|
|
// Author: deonb 10 April 2001
|
|
//
|
|
// Notes:
|
|
//
|
|
STDMETHODIMP CConnectionManager::DisableEvents(IN const BOOL fDisable, IN const ULONG ulDisableTimeout)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (ulDisableTimeout > MAX_DISABLE_EVENT_TIMEOUT)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
CONMAN_EVENT* pEvent = new CONMAN_EVENT;
|
|
|
|
if (pEvent)
|
|
{
|
|
ZeroMemory(pEvent, sizeof(CONMAN_EVENT));
|
|
pEvent->Type = DISABLE_EVENTS;
|
|
pEvent->fDisable = fDisable;
|
|
// We set the high bit to let netshell know that this is coming from our private interface.
|
|
pEvent->ulDisableTimeout = 0x80000000 | ulDisableTimeout;
|
|
|
|
if (!QueueUserWorkItemInThread(ConmanEventWorkItem, reinterpret_cast<PVOID>(pEvent), EVENTMGR_CONMAN))
|
|
{
|
|
hr = E_FAIL;
|
|
FreeConmanEvent(pEvent);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: CConnectionManager::RefreshConnections
|
|
//
|
|
// Purpose: Refreshes the connections in the connections folder
|
|
//
|
|
// Arguments:
|
|
// None.
|
|
//
|
|
//
|
|
// Returns: Standard HRESULT
|
|
//
|
|
// Author: ckotze 19 April 2001
|
|
//
|
|
// Notes: This is a public interface that we are providing in order to
|
|
// allow other components/companies to have some control over
|
|
// the Connections Folder.
|
|
STDMETHODIMP CConnectionManager::RefreshConnections()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
CONMAN_EVENT* pEvent = new CONMAN_EVENT;
|
|
|
|
if (pEvent)
|
|
{
|
|
ZeroMemory(pEvent, sizeof(CONMAN_EVENT));
|
|
pEvent->Type = REFRESH_ALL;
|
|
|
|
if (!QueueUserWorkItemInThread(ConmanEventWorkItem, reinterpret_cast<PVOID>(pEvent), EVENTMGR_CONMAN))
|
|
{
|
|
hr = E_FAIL;
|
|
FreeConmanEvent(pEvent);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: CConnectionManager::Enable
|
|
//
|
|
// Purpose: Enables events to be fired once more in the connections folder
|
|
//
|
|
// Arguments:
|
|
// None.
|
|
//
|
|
// Returns: Standard HRESULT
|
|
//
|
|
// Author: ckotze 19 April 2001
|
|
//
|
|
// Notes: This is a public interface that we are providing in order to
|
|
// allow other components/companies to have some control over
|
|
// the Connections Folder.
|
|
// Since this is a public interface, we are only allowing the
|
|
// INVALID_ADDRESS notification to be disabled
|
|
// (this will take place in netshell.dll).
|
|
//
|
|
STDMETHODIMP CConnectionManager::Enable()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
CONMAN_EVENT* pEvent = new CONMAN_EVENT;
|
|
|
|
if (pEvent)
|
|
{
|
|
ZeroMemory(pEvent, sizeof(CONMAN_EVENT));
|
|
pEvent->Type = DISABLE_EVENTS;
|
|
pEvent->fDisable = FALSE;
|
|
|
|
if (!QueueUserWorkItemInThread(ConmanEventWorkItem, reinterpret_cast<PVOID>(pEvent), EVENTMGR_CONMAN))
|
|
{
|
|
hr = E_FAIL;
|
|
FreeConmanEvent(pEvent);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: CConnectionManager::Disable
|
|
//
|
|
// Purpose: Disable netshell processing of events for x milliseconds
|
|
//
|
|
// Arguments:
|
|
// ulDisableTimeout [in] Number of milliseconds to disable event
|
|
// processing for. Max is 60000 (1 minute)
|
|
//
|
|
// Returns: Standard HRESULT
|
|
//
|
|
// Author: ckotze 19 April 2001
|
|
//
|
|
// Notes: This is a public interface that we are providing in order to
|
|
// allow other components/companies to have some control over
|
|
// the Connections Folder.
|
|
// Since this is a public interface, we are only allowing the
|
|
// INVALID_ADDRESS notification to be disabled
|
|
// (this will take place in netshell.dll).
|
|
//
|
|
STDMETHODIMP CConnectionManager::Disable(IN const ULONG ulDisableTimeout)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (ulDisableTimeout > MAX_DISABLE_EVENT_TIMEOUT)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
CONMAN_EVENT* pEvent = new CONMAN_EVENT;
|
|
|
|
if (pEvent)
|
|
{
|
|
ZeroMemory(pEvent, sizeof(CONMAN_EVENT));
|
|
pEvent->Type = DISABLE_EVENTS;
|
|
pEvent->fDisable = TRUE;
|
|
pEvent->ulDisableTimeout = ulDisableTimeout;
|
|
|
|
if (!QueueUserWorkItemInThread(ConmanEventWorkItem, reinterpret_cast<PVOID>(pEvent), EVENTMGR_CONMAN))
|
|
{
|
|
hr = E_FAIL;
|
|
FreeConmanEvent(pEvent);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
// INetConnectionCMUtil
|
|
//
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: MapCMHiddenConnectionToOwner
|
|
//
|
|
// Purpose: Maps a child connection to its parent connection.
|
|
//
|
|
// Connection Manager has two stages: Dialup and VPN.
|
|
// For the Dialup it creates a hidden connectoid that the
|
|
// folder (netshell) does not see. However netman caches
|
|
// the name, guid and status of this connectedoid. Both
|
|
// the parent and child connectoid have the same name. When
|
|
// the status of the hidden connectiod is updated the folder
|
|
// recives the guid of the hidden connectoid and maps the
|
|
// connectiod to it parent (Connection Manager) by searching
|
|
// netmans cache for the name of the hidden connectoid. Then it
|
|
// searches the connections in the folder for that name and thus
|
|
// gets the guid of the parent connectoid.
|
|
//
|
|
// When the folder gets a notify message from netman for the hidden
|
|
// connection it uses this function to find the parent and update the
|
|
// parent's status. The hidden connection is not displayed.
|
|
//
|
|
// Arguments:
|
|
// guidHidden [in] GUID of the hidden connectiod
|
|
// pguidOwner [out] GUID of the parent connectiod
|
|
//
|
|
// Returns: S_OK -- Found hidden connection
|
|
// else not found
|
|
//
|
|
// Author: omiller 1 Jun 2000
|
|
//
|
|
// Notes:
|
|
//
|
|
STDMETHODIMP
|
|
CConnectionManager::MapCMHiddenConnectionToOwner (
|
|
/*[in]*/ REFGUID guidHidden,
|
|
/*[out]*/ GUID * pguidOwner)
|
|
{
|
|
|
|
HRESULT hr = S_OK;
|
|
bool bFound = false;
|
|
|
|
hr = HrEnsureClassManagersLoaded ();
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Impersonate the user so that we see all of the connections displayed in the folder
|
|
//
|
|
hr = CoImpersonateClient();
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
|
|
// Find the hidden connection with this guid
|
|
//
|
|
CMEntry cm;
|
|
|
|
hr = CCMUtil::Instance().HrGetEntry(guidHidden, cm);
|
|
// Did we find the hidden connection?
|
|
if( S_OK == hr )
|
|
{
|
|
// Enumerate through all of the connections and find connection with the same name as the
|
|
// hidden connection
|
|
//
|
|
IEnumNetConnection * pEnum = NULL;
|
|
|
|
hr = CWanConnectionManagerEnumConnection::CreateInstance(NCME_DEFAULT, IID_IEnumNetConnection, reinterpret_cast<LPVOID *>(&pEnum));
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
INetConnection * pNetCon;
|
|
NETCON_PROPERTIES * pProps;
|
|
while(!bFound)
|
|
{
|
|
// Get the nect connection
|
|
//
|
|
hr = pEnum->Next(1, &pNetCon, NULL);
|
|
if(S_OK != hr)
|
|
{
|
|
// Ooops encountered some error, stop searching
|
|
//
|
|
break;
|
|
}
|
|
|
|
// Get the properties of the connection
|
|
//
|
|
hr = pNetCon->GetProperties(&pProps);
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
if(lstrcmp(cm.m_szEntryName, pProps->pszwName) == 0)
|
|
{
|
|
// Found the connection that has the same name a the hidden connection
|
|
// Stop searching!!
|
|
*pguidOwner = pProps->guidId;
|
|
bFound = true;
|
|
}
|
|
FreeNetconProperties(pProps);
|
|
}
|
|
ReleaseObj(pNetCon);
|
|
}
|
|
ReleaseObj(pEnum);
|
|
}
|
|
}
|
|
// Stop Impersonating the user
|
|
//
|
|
CoRevertToSelf();
|
|
}
|
|
}
|
|
|
|
return bFound ? S_OK : S_FALSE;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
// IConnectionPoint overrides netman!CConnectionManager__Advise
|
|
//
|
|
STDMETHODIMP
|
|
CConnectionManager::Advise (
|
|
IUnknown* pUnkSink,
|
|
DWORD* pdwCookie)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
TraceFileFunc(ttidConman);
|
|
|
|
Assert(!FBadInPtr(pUnkSink));
|
|
|
|
// Set our cloaked identity to be used when we call back on this
|
|
// advise interface. It's important to do this for security. Since
|
|
// we run as LocalSystem, if we were to call back without identify
|
|
// impersonation only, the client could impersonate us and get a free
|
|
// LocalSystem context which could be used for malicious purposes.
|
|
//
|
|
//
|
|
CoSetProxyBlanket (
|
|
pUnkSink,
|
|
RPC_C_AUTHN_WINNT, // use NT default security
|
|
RPC_C_AUTHZ_NONE, // use NT default authentication
|
|
NULL, // must be null if default
|
|
RPC_C_AUTHN_LEVEL_CALL, // call
|
|
RPC_C_IMP_LEVEL_IDENTIFY,
|
|
NULL, // use process token
|
|
EOAC_DEFAULT);
|
|
TraceHr(ttidError, FAL, hr, FALSE, "CoSetProxyBlanket");
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
// Initialize our event handler if it has not already been done.
|
|
hr = HrEnsureEventHandlerInitialized();
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// We still work for other events if this fails.
|
|
hr = HrEnsureRegisteredWithNla();
|
|
TraceErrorOptional("Could not register with Nla", hr, (S_FALSE == hr));
|
|
|
|
// Call the underlying ATL implementation of Advise.
|
|
//
|
|
hr = IConnectionPointImpl<
|
|
CConnectionManager,
|
|
&IID_INetConnectionNotifySink>::Advise(pUnkSink, pdwCookie);
|
|
|
|
TraceTag (ttidConman,
|
|
"CConnectionManager::Advise called... pUnkSink=0x%p, cookie=%d",
|
|
pUnkSink,
|
|
*pdwCookie);
|
|
|
|
TraceHr (ttidError, FAL, hr, FALSE, "IConnectionPointImpl::Advise");
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
WCHAR szUserAppData[MAX_PATH];
|
|
HRESULT hrT = S_OK;
|
|
HRESULT hrImpersonate = S_OK;
|
|
|
|
if (SUCCEEDED(hrT))
|
|
{
|
|
// We ignore this HRESULT because we can be called inproc and that would definitely fail.
|
|
// Instead we just make sure that it succeeded when befor call CoRevertToSelf. This is
|
|
// fine because we'll still get a valid User App data path, it will just be something like
|
|
// LocalService or LocalSystem and we can still determine which sinks to send events to.
|
|
|
|
BOOLEAN fNotifyWZC = FALSE;
|
|
WCHAR szUserName[MAX_PATH];
|
|
|
|
CNotifySourceInfo* pNotifySourceInfo = new CNotifySourceInfo();
|
|
if (!pNotifySourceInfo)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
else
|
|
{
|
|
pNotifySourceInfo->dwCookie = *pdwCookie;
|
|
|
|
hrImpersonate = CoImpersonateClient();
|
|
if (SUCCEEDED(hrImpersonate) || (RPC_E_CALL_COMPLETE == hrImpersonate))
|
|
{
|
|
hrT = SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, 0, szUserAppData);
|
|
if (SUCCEEDED(hrT))
|
|
{
|
|
pNotifySourceInfo->szUserProfilesPath = szUserAppData;
|
|
TraceTag(ttidEvents, "Adding IUnknown for Sink to Map: 0x%08x. Path: %S", reinterpret_cast<DWORD_PTR>(pUnkSink), szUserAppData);
|
|
TraceTag(ttidEvents, "Number of Items in Map: %d", m_mapNotify.size());
|
|
}
|
|
else
|
|
{
|
|
TraceError("Unable to get Folder Path", hrT);
|
|
}
|
|
|
|
ZeroMemory(szUserName, celems(szUserName));
|
|
|
|
ULONG nSize = celems(szUserName);
|
|
if (GetUserNameEx(NameSamCompatible, szUserName, &nSize) && *szUserName)
|
|
{
|
|
pNotifySourceInfo->szUserName = szUserName;
|
|
fNotifyWZC = TRUE;
|
|
}
|
|
else
|
|
{
|
|
pNotifySourceInfo->szUserName = L"System";
|
|
TraceError("Unable to get the user name", HrFromLastWin32Error());
|
|
}
|
|
}
|
|
Lock();
|
|
m_mapNotify[pUnkSink] = pNotifySourceInfo;
|
|
Unlock();
|
|
|
|
#ifdef DBG
|
|
LPWSTR* ppszAdviseUsers;
|
|
DWORD dwCount;
|
|
HRESULT hrT = GetClientAdvises(&ppszAdviseUsers, &dwCount);
|
|
if (SUCCEEDED(hrT))
|
|
{
|
|
Assert(dwCount);
|
|
|
|
TraceTag(ttidConman, "Advise client list after ::Advise:");
|
|
for (DWORD x = 0; x < dwCount; x++)
|
|
{
|
|
LPWSTR szUserName = ppszAdviseUsers[x];
|
|
TraceTag(ttidConman, "%x: %S", x, szUserName);
|
|
}
|
|
|
|
CoTaskMemFree(ppszAdviseUsers);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if (SUCCEEDED(hrImpersonate))
|
|
{
|
|
CoRevertToSelf();
|
|
}
|
|
|
|
if (fNotifyWZC)
|
|
{
|
|
WZCTrayIconReady(szUserName);
|
|
}
|
|
}
|
|
|
|
if (!m_lRegisteredOneTime)
|
|
{
|
|
// Do an explicit interlocked exchange to only let one thread
|
|
// through to do the registration.
|
|
//
|
|
if (0 == InterlockedExchange (&m_lRegisteredOneTime, 1))
|
|
{
|
|
// Register for device notifications. Specifically, we're
|
|
// interested in network adapters coming and going. If this
|
|
// fails, we proceed anyway.
|
|
//
|
|
TraceTag (ttidConman, "Calling RegisterDeviceNotification...");
|
|
|
|
DEV_BROADCAST_DEVICEINTERFACE PnpFilter;
|
|
ZeroMemory (&PnpFilter, sizeof(PnpFilter));
|
|
PnpFilter.dbcc_size = sizeof(PnpFilter);
|
|
PnpFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
|
|
PnpFilter.dbcc_classguid = GUID_NDIS_LAN_CLASS;
|
|
|
|
m_hDevNotify = RegisterDeviceNotification (
|
|
(HANDLE)_Module.m_hStatus,
|
|
&PnpFilter,
|
|
DEVICE_NOTIFY_SERVICE_HANDLE);
|
|
if (!m_hDevNotify)
|
|
{
|
|
TraceHr (ttidError, FAL, HrFromLastWin32Error(), FALSE,
|
|
"RegisterDeviceNotification");
|
|
}
|
|
|
|
(VOID) HrEnsureRegisteredOrDeregisteredWithWmi (TRUE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
TraceErrorOptional ("CConnectionManager::Advise", hr, (S_FALSE == hr));
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CConnectionManager::Unadvise(DWORD dwCookie)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
TraceFileFunc(ttidConman);
|
|
|
|
hr = IConnectionPointImpl<CConnectionManager, &IID_INetConnectionNotifySink>::Unadvise(dwCookie);
|
|
|
|
Lock();
|
|
BOOL fFound = FALSE;
|
|
for (ITERUSERNOTIFYMAP iter = m_mapNotify.begin(); iter != m_mapNotify.end(); iter++)
|
|
{
|
|
if (iter->second->dwCookie == dwCookie)
|
|
{
|
|
fFound = TRUE;
|
|
delete iter->second;
|
|
m_mapNotify.erase(iter);
|
|
break;
|
|
}
|
|
}
|
|
Unlock();
|
|
|
|
if (!fFound)
|
|
{
|
|
TraceTag(ttidError, "Unadvise cannot find advise for cookie 0x%08x in notify map", dwCookie);
|
|
}
|
|
|
|
#ifdef DBG
|
|
LPWSTR* ppszAdviseUsers;
|
|
DWORD dwCount;
|
|
HRESULT hrT = GetClientAdvises(&ppszAdviseUsers, &dwCount);
|
|
if (SUCCEEDED(hrT))
|
|
{
|
|
if (!dwCount)
|
|
{
|
|
TraceTag(ttidConman, "Unadvise removed the last advise client");
|
|
}
|
|
else
|
|
{
|
|
TraceTag(ttidConman, "Advise client list after ::Unadvise:");
|
|
}
|
|
|
|
for (DWORD x = 0; x < dwCount; x++)
|
|
{
|
|
LPWSTR szUserName = ppszAdviseUsers[x];
|
|
TraceTag(ttidConman, "%x: %S", x, szUserName);
|
|
}
|
|
|
|
CoTaskMemFree(ppszAdviseUsers);
|
|
}
|
|
#endif
|
|
return hr;
|
|
}
|
|
|
|
#if DBG
|
|
|
|
//+---------------------------------------------------------------------------
|
|
// INetConnectionManagerDebug
|
|
//
|
|
|
|
DWORD
|
|
WINAPI
|
|
ConmanNotifyTest (
|
|
PVOID pvContext
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
|
|
RASENUMENTRYDETAILS* pDetails;
|
|
DWORD cDetails;
|
|
hr = HrRasEnumAllEntriesWithDetails (NULL,
|
|
&pDetails, &cDetails);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
RASEVENT Event;
|
|
|
|
for (DWORD i = 0; i < cDetails; i++)
|
|
{
|
|
Event.Type = ENTRY_ADDED;
|
|
Event.Details = pDetails[i];
|
|
RasEventNotify (&Event);
|
|
|
|
Event.Type = ENTRY_MODIFIED;
|
|
RasEventNotify (&Event);
|
|
|
|
Event.Type = ENTRY_CONNECTED;
|
|
Event.guidId = pDetails[i].guidId;
|
|
RasEventNotify (&Event);
|
|
|
|
Event.Type = ENTRY_DISCONNECTED;
|
|
Event.guidId = pDetails[i].guidId;
|
|
RasEventNotify (&Event);
|
|
|
|
Event.Type = ENTRY_RENAMED;
|
|
lstrcpyW (Event.pszwNewName, L"foobar");
|
|
RasEventNotify (&Event);
|
|
|
|
Event.Type = ENTRY_DELETED;
|
|
Event.guidId = pDetails[i].guidId;
|
|
RasEventNotify (&Event);
|
|
}
|
|
|
|
MemFree (pDetails);
|
|
}
|
|
|
|
LanEventNotify (REFRESH_ALL, NULL, NULL, NULL);
|
|
|
|
TraceErrorOptional ("ConmanNotifyTest", hr, (S_FALSE == hr));
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CConnectionManager::NotifyTestStart ()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (!QueueUserWorkItem (ConmanNotifyTest, NULL, WT_EXECUTEDEFAULT))
|
|
{
|
|
hr = HrFromLastWin32Error ();
|
|
}
|
|
|
|
TraceErrorOptional ("CConnectionManager::NotifyTestStart", hr, (S_FALSE == hr));
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CConnectionManager::NotifyTestStop ()
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
#endif // DBG
|