// PortForwardEngine.cpp: implementation of the CPortForwardEngine class. // ////////////////////////////////////////////////////////////////////// #include "PortForwardEngine.hpp" #include #include // forward declaration of global function which is included at the end of this file void SetThreadName( DWORD dwThreadID, LPCSTR szThreadName); #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[]=__FILE__; #define new DEBUG_NEW #endif ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// CPortForwardEngine::CPortForwardEngine() { InitializeMembersToNull(); ::InitializeCriticalSection( &m_cs ); } CPortForwardEngine::~CPortForwardEngine() { StopListeningForUpnpChanges( ); ::DeleteCriticalSection( &m_cs ); } void CPortForwardEngine::InitializeMembersToNull() { m_piNAT = NULL; m_piEventManager = NULL; m_piExternalIPAddressCallback= NULL; m_piNumberOfEntriesCallback = NULL; m_pChangeCallbackFunctions = NULL; m_pPortMappingThread = NULL; m_pDeviceInfoThread = NULL; m_pAddMappingThread = NULL; m_pEditMappingThread = NULL; m_pDeleteMappingThread = NULL; m_hWndForPortMappingThread = NULL; m_hWndForDeviceInfoThread = NULL; m_hWndForAddMappingThread = NULL; m_hWndForEditMappingThread = NULL; m_hWndForDeleteMappingThread = NULL; m_bListeningForUpnpChanges = FALSE; } void CPortForwardEngine::DeinitializeCom() { if ( m_piExternalIPAddressCallback != NULL ) { m_piExternalIPAddressCallback->Release(); m_piExternalIPAddressCallback = NULL; } if ( m_piNumberOfEntriesCallback != NULL ) { m_piNumberOfEntriesCallback->Release(); m_piNumberOfEntriesCallback = NULL; } if ( m_piEventManager != NULL ) { m_piEventManager->Release(); m_piEventManager = NULL; } if ( m_piNAT != NULL ) { m_piNAT->Release(); m_piNAT = NULL; } CoUninitialize(); // balancing call for CoInitialize } HRESULT CPortForwardEngine::ListenForUpnpChanges(CPortForwardChangeCallbacks *pCallbacks /* =NULL */ ) { // check if we are already listening if ( m_bListeningForUpnpChanges == TRUE ) return E_FAIL; m_bListeningForUpnpChanges = TRUE; if ( pCallbacks==NULL ) { SetChangeEventCallbackPointer( new CPortForwardChangeCallbacks ); } else { SetChangeEventCallbackPointer( pCallbacks ); } // initialize COM for this thread HRESULT result = CoInitialize(NULL); // STA model if ( !SUCCEEDED(result) ) { return E_FAIL; } // create COM instance of IUPnPNAT result = CoCreateInstance(__uuidof(UPnPNAT), NULL, CLSCTX_ALL, __uuidof(IUPnPNAT), (void **)&m_piNAT); if ( !SUCCEEDED(result) || ( m_piNAT==NULL ) ) { CoUninitialize(); return E_FAIL; } // Get the INATEventManager interface result = m_piNAT->get_NATEventManager(&m_piEventManager); if ( !SUCCEEDED(result) || (m_piEventManager==NULL ) ) { m_piNAT->Release(); m_piNAT = NULL; CoUninitialize(); return E_FAIL; } result = m_piEventManager->put_ExternalIPAddressCallback( m_piExternalIPAddressCallback = new IDerivedNATExternalIPAddressCallback( m_pChangeCallbackFunctions ) ); if ( !SUCCEEDED(result) ) { m_piEventManager->Release(); m_piEventManager = NULL; m_piNAT->Release(); m_piNAT = NULL; CoUninitialize(); return E_FAIL; } result = m_piEventManager->put_NumberOfEntriesCallback( m_piNumberOfEntriesCallback = new IDerivedNATNumberOfEntriesCallback( m_pChangeCallbackFunctions ) ); if ( !SUCCEEDED(result) ) { m_piEventManager->Release(); m_piEventManager = NULL; m_piNAT->Release(); m_piNAT = NULL; CoUninitialize(); return E_FAIL; } return S_OK; } HRESULT CPortForwardEngine::StopListeningForUpnpChanges( ) { // Stops listenting for UPnP change events on the router and deletes any // CPortForwardChangeCallbacks-derived objects that are currently being held // check if we are already listening if ( m_bListeningForUpnpChanges == FALSE ) return E_FAIL; m_bListeningForUpnpChanges = FALSE; DeinitializeCom( ); if ( m_pChangeCallbackFunctions != NULL ) { delete m_pChangeCallbackFunctions; m_pChangeCallbackFunctions = NULL; } return S_OK; } HRESULT CPortForwardEngine::SetChangeEventCallbackPointer(CPortForwardChangeCallbacks *pCallbacks) { ASSERT( pCallbacks!=NULL ); if ( m_pChangeCallbackFunctions != NULL ) { delete m_pChangeCallbackFunctions; m_pChangeCallbackFunctions = NULL; } m_pChangeCallbackFunctions = pCallbacks; return S_OK; } BOOL CPortForwardEngine::IsAnyThreadRunning() const { BOOL bRet = FALSE; bRet |= ( m_pPortMappingThread != NULL ); bRet |= ( m_pDeviceInfoThread != NULL ); bRet |= ( m_pAddMappingThread != NULL ); bRet |= ( m_pEditMappingThread != NULL ); bRet |= ( m_pDeleteMappingThread != NULL ); return bRet; } std::vector CPortForwardEngine::GetPortMappingVector() const { // returns a copy of the current mappings (note: thread-awareness is needed) // cast away const-ness of the critical section (since this is a const function) CPortForwardEngine* pThis = const_cast< CPortForwardEngine* >( this ); ::EnterCriticalSection( &(pThis->m_cs) ); std::vector retVector; retVector = m_MappingContainer; ::LeaveCriticalSection( &(pThis->m_cs) ); return retVector; } CPortForwardEngine::DeviceInformationContainer CPortForwardEngine::GetDeviceInformationContainer() const { // returns a copy of the current device information (note: thread-awareness is needed) // cast away const-ness of the critical section (since this is a const function) CPortForwardEngine* pThis = const_cast< CPortForwardEngine* >( this ); ::EnterCriticalSection( &(pThis->m_cs) ); CPortForwardEngine::DeviceInformationContainer retDeviceInfo; retDeviceInfo = m_DeviceInfo; ::LeaveCriticalSection( &(pThis->m_cs) ); return retDeviceInfo; } /////////////////////////////////////////////// // // Get Mappings and Device Information Using Threads // /////////////////////////////////////////////// // // These comments explain the how to receive notifications from the threads that the // Port Forward Engine creates when running COM requests for device information or for // retreival/change of port mappings. // // There are five functions that create threads, and each function takes a HWND as a // parameter. During execution of the thread, each thread will post messages to this HWND, // so as to notify the HWMND of the thread's progress through the needed COM tasks. The // message is always the same: a UINT named UWM_PORT_FORWARD_ENGINE_THREAD_NOTIFICATION. // Encodings of the WPARAM and LPARAM within the message will enable the HWND to determine // what's going on inside the thread. The five functions are: // // GetMappingsUsingThread() // EditMappingUsingThread() // AddMappingUsingThread() // DeleteMappingUsingThread() // GetDeviceInformationUsingThread() // // The comments below explain each how to modify your class to receive the thread's notification // message, and also explain how to decode the WPARAM and LPARAM values // define the value of the registered Window message. An arbitrary GUID is included, to ensure uniqueness extern const UINT UWM_PORT_FORWARD_ENGINE_THREAD_NOTIFICATION = ::RegisterWindowMessage( _T("UWM_PORT_FORWARD_ENGINE_THREAD_NOTIFICATION-{7C29C80A_5712_40e8_A124_A82E4B2795A7}") ); /////////////////////////////////////////////// // // GetMappingsUsingThread() // ////////////////////////////////////////////// // // The GetMappingsUsingThread() function populates a std::vector of PortMappingContainer's with port // mappings that it finds using a thread. The reason for the thread is that COM retrieval of // port mappings is SLOW. Moreover, retrieval of the port mappings also BLOCKS the message // pump, so without a separate thread the application would otherwise appear to be hung/frozen. // // The thread frees the user interface of your program to do other things. The price you // pay for this convenience is that communication to your application is via Windows messages, // which the thread sends to your application and which your application must interpret and process. // // To use this function, your program must be able to receive (and process) // a registered window message posted from the thread when the thread is finished. // Thus, you must pass in a HWND of one of your windows that will receive the message. Typically, // you would choose your CMainFrame window (use the ::AfxGetMainWnd() function). However, you might // choose a different window, such as your CView-derived window for SDI applications // // The window that you choose must be able to process the message, which is a UINT named // UWM_PORT_FORWARD_ENGINE_THREAD_NOTIFICATION. For an MFC application, here are the changes // you must make to your CWnd class: // // 1. Declare a handler in your .h file using the following signature, in which // the name "OnMappingThreadNotificationMeesage" is arbitrary (ie, you can use // any name that you want, but you must be consistent): // // afx_msg LRESULT OnMappingThreadNotificationMeesage(WPARAM wParam, LPARAM lParam); // // 2. In your *.cpp file include the following "extern" statement somewhere at the beginning of // the file. This statement tells the linker that the value of the message is defined elsewhere: // // extern const UINT UWM_PORT_FORWARD_ENGINE_THREAD_NOTIFICATION; // defined in PortForwadEngine.cpp // // 3. In your .cpp implementation file, add an entry to the message map as follows: // // ON_REGISTERED_MESSAGE( UWM_PORT_FORWARD_ENGINE_THREAD_NOTIFICATION, OnMappingThreadNotificationMeesage ) // // 4. Again in your .cpp file, write the body of the OnMappingThreadNotificationMeesage() function. // Typically, you would check the WPARAM parameter to determine the nature of the notification. // WPARAM == CPortForwardEngine::EnumPortRetrieveInterval is sent at intervals, where // LPARAM goes from 0 to 10. You can use this to update a progress control (if you want) // WPARAM == CPortForwardEngine::EnumPortRetrieveDone is sent when the thread is done, where // LPARAM signifies if the thread was or was not successful (S_OK or E_FAIL). Call the // GetPortMappingVector() function to get a copy of the current contents of // std::vector< CPortForwardEngine::PortMappingContainer > m_MappingContainer BOOL CPortForwardEngine::GetMappingsUsingThread( HWND hWnd ) { // returns TRUE if thread was started successfully if ( (m_pPortMappingThread!=NULL) || (hWnd==NULL) || (!IsWindow(hWnd)) ) return FALSE; m_hWndForPortMappingThread = hWnd; if ( m_pPortMappingThread != NULL ) { return TRUE; } else { return FALSE; } } /////////////////////////////////////////////// // // EditMappingUsingThread() // ////////////////////////////////////////////// // // The thread created by the EditMappingUsingThread() function uses the same architecture for // message notification as above (ie, it posts a UWM_PORT_FORWARD_ENGINE_THREAD_NOTIFICATION // message), but with the following WPARAM and LPARAM encodings: // WPARAM == CPortForwardEngine::EnumEditMappingInterval at intervals, where // LPARAM varies from 0 to 10. You can use this to update of a progress control (if you want). // WPARAM == CPortForwardEngine::EnumEditMappingDone when the thread is finished, where // LPARAM signifies if the thread was or was not successful (S_OK or E_FAIL). BOOL CPortForwardEngine::EditMappingUsingThread( CPortForwardEngine::PortMappingContainer& oldMapping, CPortForwardEngine::PortMappingContainer& newMapping, HWND hWnd ) { // returns TRUE if thread was started successfully if ( (m_pEditMappingThread!=NULL) || (hWnd==NULL) || (!IsWindow(hWnd)) ) return FALSE; m_scratchpadOldMapping = oldMapping; m_scratchpadNewMapping = newMapping; m_hWndForEditMappingThread = hWnd; if ( m_pEditMappingThread != NULL ) { return TRUE; } else { return FALSE; } } /////////////////////////////////////////////// // // AddMappingUsingThread() // ////////////////////////////////////////////// // // The thread created by the AddMappingUsingThread() function uses the same architecture for // message notification as above (ie, it posts a UWM_PORT_FORWARD_ENGINE_THREAD_NOTIFICATION // message), but with the following WPARAM and LPARAM encodings: // WPARAM == CPortForwardEngine::EnumAddMappingInterval at intervals, where // LPARAM varies from 0 to 10. You can use this to update of a progress control (if you want). // WPARAM == CPortForwardEngine::EnumAddMappingDone when the thread is finished, where // LPARAM signifies if the thread was or was not successful (S_OK or E_FAIL). BOOL CPortForwardEngine::AddMappingUsingThread( CPortForwardEngine::PortMappingContainer& newMapping, HWND hWnd ) { // returns TRUE if thread was started successfully m_scratchpadAddedMapping = newMapping; m_hWndForAddMappingThread = hWnd; ThreadToAddMapping(0); return 0; } /////////////////////////////////////////////// // // DeleteMappingUsingThread() // ////////////////////////////////////////////// // // The thread created by the DeleteMappingUsingThread() function uses the same architecture for // message notification as above (ie, it posts a UWM_PORT_FORWARD_ENGINE_THREAD_NOTIFICATION // message), but with the following WPARAM and LPARAM encodings: // WPARAM == CPortForwardEngine::EnumDeleteMappingInterval at intervals, where // LPARAM varies from 0 to 10. You can use this to update of a progress control (if you want). // WPARAM == CPortForwardEngine::EnumDeleteMappingDone when the thread is finished, where // LPARAM signifies if the thread was or was not successful (S_OK or E_FAIL). BOOL CPortForwardEngine::DeleteMappingUsingThread( CPortForwardEngine::PortMappingContainer& oldMapping, HWND hWnd ) { // returns TRUE if thread was started successfully if ( (m_pDeleteMappingThread!=NULL) || (hWnd==NULL) || (!IsWindow(hWnd)) ) return FALSE; m_scratchpadDeletedMapping = oldMapping; m_hWndForDeleteMappingThread = hWnd; if ( m_pDeleteMappingThread != NULL ) { return TRUE; } else { return FALSE; } } /////////////////////////////////////////////// // // GetDeviceInformationUsingThread() // ////////////////////////////////////////////// // // The thread created by the GetDeviceInformationUsingThread() function uses the same architecture for // message notification as above (ie, it posts a UWM_PORT_FORWARD_ENGINE_THREAD_NOTIFICATION // message), but with the following WPARAM and LPARAM encodings: // WPARAM == CPortForwardEngine::EnumDeviceInfoInterval at intervals, where // LPARAM varies from 0 to 10. You can use this to update of a progress control (if you want). // WPARAM == CPortForwardEngine::EnumDeviceInfoDone when thread is finished, where // LPARAM signifies if the thread was or was not successful (S_OK or E_FAIL). Call the // GetDeviceInformationContainer() function to retrieve a copy of the current contents of // CPortForwardEngine::DeviceInformationContainer m_DeviceInfo BOOL CPortForwardEngine::GetDeviceInformationUsingThread( HWND hWnd ) { // returns TRUE if thread was started successfully if ( (m_pDeviceInfoThread!=NULL) || (hWnd==NULL) || (!IsWindow(hWnd)) ) return FALSE; m_hWndForDeviceInfoThread = hWnd; if ( m_pDeviceInfoThread != NULL ) { return TRUE; } else { return FALSE; } } /* static */ UINT CPortForwardEngine::ThreadForPortRetrieval(LPVOID pVoid) { ::SetThreadName( -1, "PortRtrv" ); // helps in debugging to see a thread's name BOOL bContinue = TRUE; CPortForwardEngine* pThis = (CPortForwardEngine*)pVoid; // local copies of shared variables HWND hWndForPosting = pThis->m_hWndForPortMappingThread; WPARAM wp = EnumPortRetrieveInterval; LPARAM lp = 0; ::PostMessage( hWndForPosting, UWM_PORT_FORWARD_ENGINE_THREAD_NOTIFICATION, wp, lp ); // initialize COM if ( !SUCCEEDED( CoInitialize(NULL) ) ) // STA model bContinue = FALSE; // create COM instance of IUPnPNAT IUPnPNAT* piNAT = NULL; IStaticPortMappingCollection* piPortMappingCollection = NULL; if ( !bContinue || !SUCCEEDED( CoCreateInstance(__uuidof(UPnPNAT), NULL, CLSCTX_ALL, __uuidof(IUPnPNAT), (void **)&piNAT) ) || ( piNAT==NULL ) ) bContinue = FALSE; // Get the collection of forwarded ports if ( !bContinue || !SUCCEEDED( piNAT->get_StaticPortMappingCollection(&piPortMappingCollection) ) || (piPortMappingCollection==NULL ) ) bContinue = FALSE; lp = 1; ::PostMessage( hWndForPosting, UWM_PORT_FORWARD_ENGINE_THREAD_NOTIFICATION, wp, lp ); // Get mapping enumerator and reset the list of mappings IUnknown* piUnk = NULL; IEnumVARIANT* piEnumerator = NULL; if ( !bContinue || !SUCCEEDED( piPortMappingCollection->get__NewEnum( &piUnk ) ) || piUnk==NULL ) bContinue = FALSE; if ( !bContinue || !SUCCEEDED( piUnk->QueryInterface(IID_IEnumVARIANT, (void **)&piEnumerator) ) || piEnumerator==NULL ) bContinue = FALSE; if ( !bContinue || !SUCCEEDED( piEnumerator->Reset() ) ) bContinue = FALSE; lp = 2; ::PostMessage( hWndForPosting, UWM_PORT_FORWARD_ENGINE_THREAD_NOTIFICATION, wp, lp ); // get count of static mappings long cMappings = 0; if ( !bContinue || !SUCCEEDED( piPortMappingCollection->get_Count( &cMappings ) ) ) bContinue = FALSE; if ( cMappings <= 0 ) cMappings = 4; // arbitrary non-zero value, so we can divide by non-zero value lp = 3; ::PostMessage( hWndForPosting, UWM_PORT_FORWARD_ENGINE_THREAD_NOTIFICATION, wp, lp ); HRESULT result = S_OK; int iii = 0; // clear all current mappings (note: thread-awareness is needed) ::EnterCriticalSection( &(pThis->m_cs) ); pThis->m_MappingContainer.clear(); ::LeaveCriticalSection( &(pThis->m_cs) ); CPortForwardEngine::PortMappingContainer mapCont; while ( bContinue && SUCCEEDED(result) ) { result = pThis->GetNextMapping( piEnumerator, mapCont ); if ( SUCCEEDED(result) ) { ::EnterCriticalSection( &(pThis->m_cs) ); pThis->m_MappingContainer.push_back( mapCont ); ::LeaveCriticalSection( &(pThis->m_cs) ); } lp = 3 + (10-3)*(++iii)/cMappings; ::PostMessage( hWndForPosting, UWM_PORT_FORWARD_ENGINE_THREAD_NOTIFICATION, wp, lp ); } // release COM objects and de-initialize COM if ( piEnumerator != NULL ) { piEnumerator->Release(); piEnumerator = NULL; } if ( piPortMappingCollection != NULL ) { piPortMappingCollection->Release(); piPortMappingCollection = NULL; } if ( piNAT != NULL ) { piNAT->Release(); piNAT = NULL; } CoUninitialize(); // post completion message lp = (bContinue ? S_OK : E_FAIL); wp = EnumPortRetrieveDone; ::PostMessage( hWndForPosting, UWM_PORT_FORWARD_ENGINE_THREAD_NOTIFICATION, wp, lp ); pThis->m_pPortMappingThread = NULL; pThis->m_hWndForPortMappingThread = NULL; return 0; } /* static */ UINT CPortForwardEngine::ThreadForDeviceInformationRetrieval( LPVOID pVoid ) { ::SetThreadName( -1, "DevInfo" ); // helps in debugging to see a thread's name BOOL bContinue = TRUE; CPortForwardEngine* pThis = (CPortForwardEngine*)pVoid; // local copies of shared variables HWND hWndForPosting = pThis->m_hWndForDeviceInfoThread; WPARAM wp = EnumDeviceInfoInterval; LPARAM lp = 0; ::PostMessage( hWndForPosting, UWM_PORT_FORWARD_ENGINE_THREAD_NOTIFICATION, wp, lp ); // initialize COM if ( !SUCCEEDED( CoInitialize(NULL) ) ) // STA model bContinue = FALSE; // create COM instance of IUPnPDeviceFinder IUPnPDeviceFinder* piDeviceFinder = NULL; if ( !bContinue || !SUCCEEDED( CoCreateInstance( CLSID_UPnPDeviceFinder, NULL, CLSCTX_ALL, IID_IUPnPDeviceFinder, (void**) &piDeviceFinder ) ) || ( piDeviceFinder==NULL ) ) bContinue = FALSE; lp = 1; ::PostMessage( hWndForPosting, UWM_PORT_FORWARD_ENGINE_THREAD_NOTIFICATION, wp, lp ); // get devices of the desired type, using the PnP schema // < deviceType>urn:schemas-upnp-org:device:InternetGatewayDevice:1< /deviceType> BSTR bStrDev = SysAllocString( L"urn:schemas-upnp-org:device:InternetGatewayDevice:1" ); IUPnPDevices* piFoundDevices = NULL; if ( !bContinue || !SUCCEEDED( piDeviceFinder->FindByType( bStrDev, 0, &piFoundDevices ) ) || ( piFoundDevices==NULL ) ) bContinue = FALSE; SysFreeString( bStrDev ); lp = 5; ::PostMessage( hWndForPosting, UWM_PORT_FORWARD_ENGINE_THREAD_NOTIFICATION, wp, lp ); // now traverse the collection of piFoundDevices // the following code is based on code provided by MSDN in its Control Point API: // "Device Collections Returned By Synchronous Searches" at // http://msdn.microsoft.com/library/en-us/upnp/upnp/device_collections_returned_by_synchronous_searches.asp HRESULT result = S_OK; IUnknown * pUnk = NULL; DeviceInformationContainer deviceInfo; if ( bContinue && SUCCEEDED( piFoundDevices->get__NewEnum(&pUnk) ) && ( pUnk!=NULL ) ) { IEnumVARIANT * pEnumVar = NULL; result = pUnk->QueryInterface(IID_IEnumVARIANT, (void **) &pEnumVar); if (SUCCEEDED(result)) { VARIANT varCurDevice; VariantInit(&varCurDevice); pEnumVar->Reset(); // Loop through each device in the collection while (S_OK == pEnumVar->Next(1, &varCurDevice, NULL)) { IUPnPDevice * pDevice = NULL; IDispatch * pdispDevice = V_DISPATCH(&varCurDevice); if (SUCCEEDED(pdispDevice->QueryInterface(IID_IUPnPDevice, (void **) &pDevice))) { // finally, post interval notification message and get all the needed information lp = 6; ::PostMessage( hWndForPosting, UWM_PORT_FORWARD_ENGINE_THREAD_NOTIFICATION, wp, lp ); result = pThis->PopulateDeviceInfoContainer( pDevice, deviceInfo, hWndForPosting ); } VariantClear(&varCurDevice); } pEnumVar->Release(); } pUnk->Release(); } // release COM objects and de-initialize COM if ( piDeviceFinder!=NULL ) { piDeviceFinder->Release(); piDeviceFinder = NULL; } CoUninitialize(); bContinue = SUCCEEDED( result ); // store local devInfo into class member pThis->m_DeviceInfo = deviceInfo; // post completion message lp = (bContinue ? S_OK : E_FAIL); wp = EnumDeviceInfoDone; ::PostMessage( hWndForPosting, UWM_PORT_FORWARD_ENGINE_THREAD_NOTIFICATION, wp, lp ); pThis->m_pDeviceInfoThread = NULL; pThis->m_hWndForDeviceInfoThread = NULL; return 0; } /* static */ UINT CPortForwardEngine::ThreadToEditMapping( LPVOID pVoid ) { ::SetThreadName( -1, "EditMap" ); // helps in debugging to see a thread's name BOOL bContinue = TRUE; CPortForwardEngine* pThis = (CPortForwardEngine*)pVoid; // local copies of shared variables PortMappingContainer oldMapping = pThis->m_scratchpadOldMapping; PortMappingContainer newMapping = pThis->m_scratchpadNewMapping; HWND hWndForPosting = pThis->m_hWndForEditMappingThread; WPARAM wp = EnumEditMappingInterval; LPARAM lp = 0; ::PostMessage( hWndForPosting, UWM_PORT_FORWARD_ENGINE_THREAD_NOTIFICATION, wp, lp ); // initialize COM if ( !SUCCEEDED( CoInitialize(NULL) ) ) // STA model bContinue = FALSE; // create COM instance of IUPnPNAT IUPnPNAT* piNAT = NULL; IStaticPortMappingCollection* piPortMappingCollection = NULL; if ( !bContinue || !SUCCEEDED( CoCreateInstance(__uuidof(UPnPNAT), NULL, CLSCTX_ALL, __uuidof(IUPnPNAT), (void **)&piNAT) ) || ( piNAT==NULL ) ) bContinue = FALSE; // Get the collection of forwarded ports if ( !bContinue || !SUCCEEDED( piNAT->get_StaticPortMappingCollection(&piPortMappingCollection) ) || (piPortMappingCollection==NULL ) ) bContinue = FALSE; lp = 1; ::PostMessage( hWndForPosting, UWM_PORT_FORWARD_ENGINE_THREAD_NOTIFICATION, wp, lp ); // get the target old mapping from the collection of mappings IStaticPortMapping* piStaticPortMapping = NULL; USES_CONVERSION; // for conversion from CString's if ( !bContinue || !SUCCEEDED( piPortMappingCollection->get_Item( _ttol(oldMapping.ExternalPort), T2OLE( oldMapping.Protocol.GetBuffer(0) ), &piStaticPortMapping ) ) || (piStaticPortMapping==NULL) ) bContinue = FALSE; oldMapping.Protocol.ReleaseBuffer(); lp = 2; ::PostMessage( hWndForPosting, UWM_PORT_FORWARD_ENGINE_THREAD_NOTIFICATION, wp, lp ); // update the mapping if ( bContinue ) { HRESULT hr = S_OK; VARIANT_BOOL vb = ( ( newMapping.Enabled == _T("Yes") ) ? VARIANT_TRUE : VARIANT_FALSE ); hr |= piStaticPortMapping->Enable( vb ); lp = 4; ::PostMessage( hWndForPosting, UWM_PORT_FORWARD_ENGINE_THREAD_NOTIFICATION, wp, lp ); hr |= piStaticPortMapping->EditDescription( T2OLE( newMapping.Description.GetBuffer(0) ) ); newMapping.Description.ReleaseBuffer(); lp = 6; ::PostMessage( hWndForPosting, UWM_PORT_FORWARD_ENGINE_THREAD_NOTIFICATION, wp, lp ); hr |= piStaticPortMapping->EditInternalPort( _ttol(newMapping.InternalPort) ); lp = 8; ::PostMessage( hWndForPosting, UWM_PORT_FORWARD_ENGINE_THREAD_NOTIFICATION, wp, lp ); hr |= piStaticPortMapping->EditInternalClient( T2OLE( newMapping.InternalClient.GetBuffer(0) ) ); newMapping.InternalClient.ReleaseBuffer(); lp = 10; ::PostMessage( hWndForPosting, UWM_PORT_FORWARD_ENGINE_THREAD_NOTIFICATION, wp, lp ); if ( !SUCCEEDED(hr) ) bContinue = FALSE; } // clean up and de-initialize COM if ( piStaticPortMapping != NULL ) { piStaticPortMapping->Release(); piStaticPortMapping = NULL; } if ( piPortMappingCollection != NULL ) { piPortMappingCollection->Release(); piPortMappingCollection = NULL; } if ( piNAT != NULL ) { piNAT->Release(); piNAT = NULL; } CoUninitialize(); // post completion message lp = (bContinue ? S_OK : E_FAIL); wp = EnumEditMappingDone; ::PostMessage( hWndForPosting, UWM_PORT_FORWARD_ENGINE_THREAD_NOTIFICATION, wp, lp ); pThis->m_pEditMappingThread = NULL; pThis->m_hWndForEditMappingThread = NULL; return 0; } /* static */ UINT CPortForwardEngine::ThreadToDeleteMapping( LPVOID pVoid ) { ::SetThreadName( -1, "DelMap" ); // helps in debugging to see a thread's name BOOL bContinue = TRUE; CPortForwardEngine* pThis = (CPortForwardEngine*)pVoid; // local copies of shared variables PortMappingContainer oldMapping = pThis->m_scratchpadDeletedMapping; HWND hWndForPosting = pThis->m_hWndForDeleteMappingThread; WPARAM wp = EnumDeleteMappingInterval; LPARAM lp = 0; ::PostMessage( hWndForPosting, UWM_PORT_FORWARD_ENGINE_THREAD_NOTIFICATION, wp, lp ); // initialize COM if ( !SUCCEEDED( CoInitialize(NULL) ) ) // STA model bContinue = FALSE; // create COM instance of IUPnPNAT IUPnPNAT* piNAT = NULL; IStaticPortMappingCollection* piPortMappingCollection = NULL; if ( !bContinue || !SUCCEEDED( CoCreateInstance(__uuidof(UPnPNAT), NULL, CLSCTX_ALL, __uuidof(IUPnPNAT), (void **)&piNAT) ) || ( piNAT==NULL ) ) bContinue = FALSE; // Get the collection of forwarded ports if ( !bContinue || !SUCCEEDED( piNAT->get_StaticPortMappingCollection(&piPortMappingCollection) ) || (piPortMappingCollection==NULL ) ) bContinue = FALSE; lp = 1; ::PostMessage( hWndForPosting, UWM_PORT_FORWARD_ENGINE_THREAD_NOTIFICATION, wp, lp ); // get the target old mapping from the collection of mappings USES_CONVERSION; // for conversion from CString's if ( !bContinue || !SUCCEEDED( piPortMappingCollection->Remove( _ttol(oldMapping.ExternalPort), T2OLE( oldMapping.Protocol.GetBuffer(0) ) ) ) ) bContinue = FALSE; oldMapping.Protocol.ReleaseBuffer(); lp = 2; ::PostMessage( hWndForPosting, UWM_PORT_FORWARD_ENGINE_THREAD_NOTIFICATION, wp, lp ); // clean up and de-initialize COM if ( piPortMappingCollection != NULL ) { piPortMappingCollection->Release(); piPortMappingCollection = NULL; } if ( piNAT != NULL ) { piNAT->Release(); piNAT = NULL; } CoUninitialize(); // post completion message lp = (bContinue ? S_OK : E_FAIL); wp = EnumDeleteMappingDone; ::PostMessage( hWndForPosting, UWM_PORT_FORWARD_ENGINE_THREAD_NOTIFICATION, wp, lp ); pThis->m_pDeleteMappingThread = NULL; pThis->m_hWndForDeleteMappingThread = NULL; return 0; } /* static */ UINT CPortForwardEngine::ThreadToAddMapping( LPVOID pVoid ) { CPortForwardEngine* pThis = (CPortForwardEngine*)pVoid; PortMappingContainer& newMapping = pThis->m_scratchpadAddedMapping; BOOL bContinue = TRUE; WPARAM wp = EnumAddMappingInterval; LPARAM lp = 0; // initialize COM if ( !SUCCEEDED( CoInitialize(NULL) ) ) // STA model bContinue = FALSE; // create COM instance of IUPnPNAT IUPnPNAT* piNAT = NULL; IStaticPortMappingCollection* piPortMappingCollection = NULL; if ( !bContinue || !SUCCEEDED( CoCreateInstance(__uuidof(UPnPNAT), NULL, CLSCTX_ALL, __uuidof(IUPnPNAT), (void **)&piNAT) ) || ( piNAT==NULL ) ) bContinue = FALSE; // Get the collection of forwarded ports if ( !bContinue || !SUCCEEDED( piNAT->get_StaticPortMappingCollection(&piPortMappingCollection) ) || (piPortMappingCollection==NULL ) ) bContinue = FALSE; lp = 1; ::PostMessage( pThis->m_hWndForAddMappingThread, UWM_PORT_FORWARD_ENGINE_THREAD_NOTIFICATION, wp, lp ); // add the new mapping IStaticPortMapping* piStaticPortMapping = NULL; USES_CONVERSION; // for conversion from CString's VARIANT_BOOL vb = ( ( newMapping.Enabled == _T("Yes") ) ? VARIANT_TRUE : VARIANT_FALSE ); if ( !bContinue || !SUCCEEDED( piPortMappingCollection->Add( _ttol(newMapping.ExternalPort), T2OLE( newMapping.Protocol.GetBuffer(0) ), _ttol(newMapping.InternalPort), T2OLE( newMapping.InternalClient.GetBuffer(0) ), vb, T2OLE( newMapping.Description.GetBuffer(0) ), &piStaticPortMapping ) ) || (piStaticPortMapping==NULL) ) bContinue = FALSE; newMapping.Protocol.ReleaseBuffer(); newMapping.InternalClient.ReleaseBuffer(); newMapping.Description.ReleaseBuffer(); lp = 2; ::PostMessage( pThis->m_hWndForAddMappingThread, UWM_PORT_FORWARD_ENGINE_THREAD_NOTIFICATION, wp, lp ); // clean up and de-initialize COM if ( piStaticPortMapping != NULL ) { piStaticPortMapping->Release(); piStaticPortMapping = NULL; } if ( piPortMappingCollection != NULL ) { piPortMappingCollection->Release(); piPortMappingCollection = NULL; } if ( piNAT != NULL ) { piNAT->Release(); piNAT = NULL; } CoUninitialize(); // post completion message lp = (bContinue ? S_OK : E_FAIL); wp = EnumAddMappingDone; pThis->m_pAddMappingThread = NULL; pThis->m_hWndForAddMappingThread = NULL; return 0; } HRESULT CPortForwardEngine::GetNextMapping( IEnumVARIANT* piEnumerator, CPortForwardEngine::PortMappingContainer& mappingContainer ) { // uses the enumerator to get the next mapping and fill in a mapping container structure USES_CONVERSION; // for conversion to CString's if ( piEnumerator == NULL ) { return E_FAIL; } VARIANT varCurMapping; VariantInit(&varCurMapping); HRESULT result = piEnumerator->Next( 1, &varCurMapping, NULL); if( !SUCCEEDED(result) ) { return E_FAIL; } if ( varCurMapping.vt == VT_EMPTY ) { return E_FAIL; } IStaticPortMapping* piMapping = NULL; IDispatch* piDispMap = V_DISPATCH(&varCurMapping); result = piDispMap->QueryInterface(IID_IStaticPortMapping, (void **)&piMapping); if( !SUCCEEDED(result) ) { return E_FAIL; } // get external address BSTR bStr = NULL; result = piMapping->get_ExternalIPAddress( &bStr ); if( !SUCCEEDED(result) ) { piMapping->Release(); piMapping = NULL; return E_FAIL; } if( bStr != NULL ) mappingContainer.ExternalIPAddress = CString( OLE2T( bStr ) ); SysFreeString(bStr); bStr = NULL; // get external port long lValue = 0; result = piMapping->get_ExternalPort( &lValue ); if( !SUCCEEDED(result) ) { piMapping->Release(); piMapping = NULL; return E_FAIL; } mappingContainer.ExternalPort.Format( _T("%d"), lValue ); // get internal port result = piMapping->get_InternalPort( &lValue ); if( !SUCCEEDED(result) ) { piMapping->Release(); piMapping = NULL; return E_FAIL; } mappingContainer.InternalPort.Format( _T("%d"), lValue ); // get protocol result = piMapping->get_Protocol( &bStr ); if( !SUCCEEDED(result) ) { piMapping->Release(); piMapping = NULL; return E_FAIL; } if( bStr != NULL ) mappingContainer.Protocol = CString( OLE2T( bStr ) ); SysFreeString(bStr); bStr = NULL; // get internal client result = piMapping->get_InternalClient( &bStr ); if( !SUCCEEDED(result) ) { piMapping->Release(); piMapping = NULL; return E_FAIL; } if( bStr != NULL ) mappingContainer.InternalClient = CString( OLE2T( bStr ) ); SysFreeString(bStr); bStr = NULL; // determine whether it's enabled VARIANT_BOOL bValue = VARIANT_FALSE; result = piMapping->get_Enabled( &bValue ); if( !SUCCEEDED(result) ) { piMapping->Release(); piMapping = NULL; return E_FAIL; } mappingContainer.Enabled = CString( ( (bValue==VARIANT_FALSE) ? _T("No") : _T("Yes") ) ); // get description result = piMapping->get_Description( &bStr ); if( !SUCCEEDED(result) ) { piMapping->Release(); piMapping = NULL; return E_FAIL; } if( bStr != NULL ) mappingContainer.Description = CString( OLE2T( bStr ) ); SysFreeString(bStr); bStr = NULL; // clean up piMapping->Release(); piMapping = NULL; VariantClear( &varCurMapping ); return S_OK; } HRESULT CPortForwardEngine::PopulateDeviceInfoContainer( IUPnPDevice* piDevice, CPortForwardEngine::DeviceInformationContainer& deviceInfo, HWND hWnd /* =NULL */ ) { USES_CONVERSION; // for conversion to CString's HRESULT result=S_OK, hrReturn=S_OK; long lValue = 0; BSTR bStr = NULL; VARIANT_BOOL bValue = VARIANT_FALSE; IUPnPDevices* piDevices = NULL; // parameters for interval notification messages double lp = 6.0; double addend = (10.0 - 6.0)/19.0; // there are 19 parameters that are retrieved, and we already sent 6 notifications WPARAM wp = EnumDeviceInfoInterval; // get children: We will NOT enumerate through the devices that might be returned. // Rather, we will only indicate how many there are. So, in this sense, the information // is somewhat redundant to HasChildren result = piDevice->get_Children( &piDevices ); hrReturn |= result; if ( SUCCEEDED(result) && (piDevices!=NULL) ) { piDevices->get_Count( &lValue ); if ( lValue==0 ) { deviceInfo.Children.Format( _T("No: Zero children") ); } else if ( lValue==1 ) { deviceInfo.Children.Format( _T("Yes: One child") ); } else if ( lValue>=1 ) { deviceInfo.Children.Format( _T("Yes: %d children"), lValue ); } piDevices->Release(); piDevices = NULL; lValue = 0; } if ( hWnd!=NULL ) ::PostMessage( hWnd, UWM_PORT_FORWARD_ENGINE_THREAD_NOTIFICATION, wp, (LPARAM)(lp += addend) ); // Get Description result = piDevice->get_Description( &bStr ); hrReturn |= result; if ( SUCCEEDED(result) ) { deviceInfo.Description = CString( OLE2T( bStr ) ); SysFreeString(bStr); bStr = NULL; } if ( hWnd!=NULL ) ::PostMessage( hWnd, UWM_PORT_FORWARD_ENGINE_THREAD_NOTIFICATION, wp, (LPARAM)(lp += addend) ); // Get FriendlyName result = piDevice->get_FriendlyName( &bStr ); hrReturn |= result; if ( SUCCEEDED(result) ) { deviceInfo.FriendlyName = CString( OLE2T( bStr ) ); SysFreeString(bStr); bStr = NULL; } if ( hWnd!=NULL ) ::PostMessage( hWnd, UWM_PORT_FORWARD_ENGINE_THREAD_NOTIFICATION, wp, (LPARAM)(lp += addend) ); // Get HasChildren result = piDevice->get_HasChildren( &bValue ); hrReturn |= result; if ( SUCCEEDED(result) ) { deviceInfo.HasChildren = CString( ( (bValue==VARIANT_FALSE) ? _T("No") : _T("Yes") ) ); bValue = VARIANT_FALSE; } if ( hWnd!=NULL ) ::PostMessage( hWnd, UWM_PORT_FORWARD_ENGINE_THREAD_NOTIFICATION, wp, (LPARAM)(lp += addend) ); // Get IconURL BSTR bStrMime = SysAllocString(L"image/gif"); result = piDevice->IconURL( bStrMime, 32, 32, 8, &bStr ); hrReturn |= result; if ( SUCCEEDED(result) ) { deviceInfo.IconURL = CString( OLE2T( bStr ) ); SysFreeString(bStr); bStr = NULL; } SysFreeString(bStrMime); bStrMime = NULL; if ( hWnd!=NULL ) ::PostMessage( hWnd, UWM_PORT_FORWARD_ENGINE_THREAD_NOTIFICATION, wp, (LPARAM)(lp += addend) ); // Get IsRootDevice result = piDevice->get_IsRootDevice( &bValue ); hrReturn |= result; if ( SUCCEEDED(result) ) { deviceInfo.IsRootDevice = CString( ( (bValue==VARIANT_FALSE) ? _T("No") : _T("Yes") ) ); bValue = VARIANT_FALSE; } if ( hWnd!=NULL ) ::PostMessage( hWnd, UWM_PORT_FORWARD_ENGINE_THREAD_NOTIFICATION, wp, (LPARAM)(lp += addend) ); // Get ManufacturerName result = piDevice->get_ManufacturerName( &bStr ); hrReturn |= result; if ( SUCCEEDED(result) ) { deviceInfo.ManufacturerName = CString( OLE2T( bStr ) ); SysFreeString(bStr); bStr = NULL; } if ( hWnd!=NULL ) ::PostMessage( hWnd, UWM_PORT_FORWARD_ENGINE_THREAD_NOTIFICATION, wp, (LPARAM)(lp += addend) ); // Get ManufacturerURL result = piDevice->get_ManufacturerURL( &bStr ); hrReturn |= result; if ( SUCCEEDED(result) ) { deviceInfo.ManufacturerURL = CString( OLE2T( bStr ) ); SysFreeString(bStr); bStr = NULL; } if ( hWnd!=NULL ) ::PostMessage( hWnd, UWM_PORT_FORWARD_ENGINE_THREAD_NOTIFICATION, wp, (LPARAM)(lp += addend) ); // Get ModelName result = piDevice->get_ModelName( &bStr ); hrReturn |= result; if ( SUCCEEDED(result) ) { deviceInfo.ModelName = CString( OLE2T( bStr ) ); SysFreeString(bStr); bStr = NULL; } if ( hWnd!=NULL ) ::PostMessage( hWnd, UWM_PORT_FORWARD_ENGINE_THREAD_NOTIFICATION, wp, (LPARAM)(lp += addend) ); // Get ModelNumber result = piDevice->get_ModelNumber( &bStr ); hrReturn |= result; if ( SUCCEEDED(result) ) { deviceInfo.ModelNumber = CString( OLE2T( bStr ) ); SysFreeString(bStr); bStr = NULL; } if ( hWnd!=NULL ) ::PostMessage( hWnd, UWM_PORT_FORWARD_ENGINE_THREAD_NOTIFICATION, wp, (LPARAM)(lp += addend) ); // Get ModelURL result = piDevice->get_ModelURL( &bStr ); hrReturn |= result; if ( SUCCEEDED(result) ) { deviceInfo.ModelURL = CString( OLE2T( bStr ) ); SysFreeString(bStr); bStr = NULL; } if ( hWnd!=NULL ) ::PostMessage( hWnd, UWM_PORT_FORWARD_ENGINE_THREAD_NOTIFICATION, wp, (LPARAM)(lp += addend) ); // Get ParentDevice. Actually, we will only get the FriendlyName of the parent device, // if there is one IUPnPDevice* piDev = NULL; result = piDevice->get_ParentDevice( &piDev ); hrReturn |= result; if ( SUCCEEDED(result) ) { if ( piDev==NULL ) { deviceInfo.ParentDevice.Format( _T("This is a topmost device") ); } else { if ( SUCCEEDED( piDev->get_FriendlyName( &bStr ) ) ) { deviceInfo.ParentDevice = CString( OLE2T( bStr ) ); SysFreeString(bStr); bStr = NULL; } piDev->Release(); piDev = NULL; } } if ( hWnd!=NULL ) ::PostMessage( hWnd, UWM_PORT_FORWARD_ENGINE_THREAD_NOTIFICATION, wp, (LPARAM)(lp += addend) ); // Get PresentationURL result = piDevice->get_PresentationURL( &bStr ); hrReturn |= result; if ( SUCCEEDED(result) ) { deviceInfo.PresentationURL = CString( OLE2T( bStr ) ); SysFreeString(bStr); bStr = NULL; } if ( hWnd!=NULL ) ::PostMessage( hWnd, UWM_PORT_FORWARD_ENGINE_THREAD_NOTIFICATION, wp, (LPARAM)(lp += addend) ); // Get RootDevice. Actually, we will only get the FriendlyName of the root device, // if there is one result = piDevice->get_RootDevice( &piDev ); hrReturn |= result; if ( SUCCEEDED(result) ) { if ( piDev==NULL ) { deviceInfo.RootDevice.Format( _T("This is a topmost device") ); } else { if ( SUCCEEDED( piDev->get_FriendlyName( &bStr ) ) ) { deviceInfo.RootDevice = CString( OLE2T( bStr ) ); SysFreeString(bStr); bStr = NULL; } piDev->Release(); piDev = NULL; } } if ( hWnd!=NULL ) ::PostMessage( hWnd, UWM_PORT_FORWARD_ENGINE_THREAD_NOTIFICATION, wp, (LPARAM)(lp += addend) ); // Get SerialNumber result = piDevice->get_SerialNumber( &bStr ); hrReturn |= result; if ( SUCCEEDED(result) ) { deviceInfo.SerialNumber = CString( OLE2T( bStr ) ); SysFreeString(bStr); bStr = NULL; } if ( hWnd!=NULL ) ::PostMessage( hWnd, UWM_PORT_FORWARD_ENGINE_THREAD_NOTIFICATION, wp, (LPARAM)(lp += addend) ); // Get Services. Actually, we will NOT enumerate through all the services that are contained // in the IUPnPServices collection. Rather, we will only get a count of services IUPnPServices* piServices = NULL; result = piDevice->get_Services( &piServices ); hrReturn |= result; if ( SUCCEEDED(result) && (piServices!=NULL) ) { piServices->get_Count( &lValue ); if ( lValue==0 ) { deviceInfo.Services.Format( _T("No: Zero services") ); } else if ( lValue==1 ) { deviceInfo.Services.Format( _T("Yes: One service") ); } else if ( lValue>=1 ) { deviceInfo.Services.Format( _T("Yes: %d services"), lValue ); } piServices->Release(); piServices = NULL; lValue = 0; } if ( hWnd!=NULL ) ::PostMessage( hWnd, UWM_PORT_FORWARD_ENGINE_THREAD_NOTIFICATION, wp, (LPARAM)(lp += addend) ); // Get Type result = piDevice->get_Type( &bStr ); hrReturn |= result; if ( SUCCEEDED(result) ) { deviceInfo.Type = CString( OLE2T( bStr ) ); SysFreeString(bStr); bStr = NULL; } if ( hWnd!=NULL ) ::PostMessage( hWnd, UWM_PORT_FORWARD_ENGINE_THREAD_NOTIFICATION, wp, (LPARAM)(lp += addend) ); // Get UniqueDeviceName result = piDevice->get_UniqueDeviceName( &bStr ); hrReturn |= result; if ( SUCCEEDED(result) ) { deviceInfo.UniqueDeviceName = CString( OLE2T( bStr ) ); SysFreeString(bStr); bStr = NULL; } if ( hWnd!=NULL ) ::PostMessage( hWnd, UWM_PORT_FORWARD_ENGINE_THREAD_NOTIFICATION, wp, (LPARAM)(lp += addend) ); // Get UPC result = piDevice->get_UPC( &bStr ); hrReturn |= result; if ( SUCCEEDED(result) ) { deviceInfo.UPC = CString( OLE2T( bStr ) ); SysFreeString(bStr); bStr = NULL; } if ( hWnd!=NULL ) ::PostMessage( hWnd, UWM_PORT_FORWARD_ENGINE_THREAD_NOTIFICATION, wp, (LPARAM)(lp += addend) ); return hrReturn; } ////////////////////////////////////////////////////////////////////////////////////////////// // // implementation of Port Forward Change Event Callbacks // ///////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// CPortForwardChangeCallbacks::CPortForwardChangeCallbacks() { } CPortForwardChangeCallbacks::~CPortForwardChangeCallbacks() { } /* virtual */ HRESULT /* STDMETHODCALLTYPE */ CPortForwardChangeCallbacks::OnNewNumberOfEntries( long lNewNumberOfEntries ) { CString tempStr; tempStr.Format( _T("UPnP has detected a change in the number of port mappings for your router \n") _T("New number of mappings = %d \n") _T("It is recommended to update your list of mappings"), lNewNumberOfEntries ); ::MessageBox( NULL, tempStr, _T("Change Detected in Number of Port Mappings"), MB_OK | MB_ICONEXCLAMATION ); return S_OK; } /* virtual */ HRESULT /* STDMETHODCALLTYPE */ CPortForwardChangeCallbacks::OnNewExternalIPAddress( BSTR bstrNewExternalIPAddress ) { USES_CONVERSION; // for ole to tstr conversion CString tempStr; tempStr.Format( _T("UPnP has detected a change in your external IP address \n") _T("New IP address = %s \n") _T("It is recommended to update your list of mappings"), OLE2T( bstrNewExternalIPAddress ) ); ::MessageBox( NULL, tempStr, _T("Change Detected in External IP Address"), MB_OK | MB_ICONEXCLAMATION ); return S_OK; } HRESULT STDMETHODCALLTYPE CPortForwardEngine:: IDerivedNATNumberOfEntriesCallback::QueryInterface(REFIID iid, void ** ppvObject) { HRESULT hr = S_OK; *ppvObject = NULL; if ( iid == IID_IUnknown || iid == IID_INATNumberOfEntriesCallback ) { *ppvObject = this; AddRef(); hr = NOERROR; } else { hr = E_NOINTERFACE; } return hr; } HRESULT STDMETHODCALLTYPE CPortForwardEngine:: IDerivedNATExternalIPAddressCallback::QueryInterface(REFIID iid, void ** ppvObject) { HRESULT hr = S_OK; *ppvObject = NULL; if ( iid == IID_IUnknown || iid == IID_INATExternalIPAddressCallback ) { *ppvObject = this; AddRef(); hr = NOERROR; } else { hr = E_NOINTERFACE; } return hr; } ///////////////////////////////////////////////////////////////////// // // SetThreadName -- a function to set the current thread's 8-character name // so as to be able to distinguish between the threads during debug operations // // Usage: SetThreadName (-1, "MainThread"); // Must be called from the thread you're trying to name // For example SetThreadName(-1, "1st Thread"); // Will truncate name to 8 characters // // code obtained from // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vsdebug/html/vxtsksettingthreadname.asp // void SetThreadName( DWORD dwThreadID, LPCSTR szThreadName) { struct THREADNAME_INFO { DWORD dwType; // must be 0x1000 LPCSTR szName; // pointer to name (in user addr space) DWORD dwThreadID; // thread ID (-1=caller thread) DWORD dwFlags; // reserved for future use, must be zero } ; THREADNAME_INFO info; info.dwType = 0x1000; info.szName = szThreadName; info.dwThreadID = dwThreadID; info.dwFlags = 0; __try { RaiseException( 0x406D1388, 0, sizeof(info)/sizeof(DWORD), (DWORD*)&info ); } __except(EXCEPTION_CONTINUE_EXECUTION) { } }