|
|
/*++
Copyright (c) 1999 Microsoft Corporation
Module Name:
ExtensionData.cpp
Abstract:
This file provides implementation of the named extension data functions (get / set / notify)
Author:
Eran Yariv (EranY) Nov, 1999
Revision History:
--*/
#include "faxsvc.h"
#pragma hdrstop
static DWORD FAXGetExtensionData ( IN DWORD dwOrigDeviceId, IN FAX_ENUM_DEVICE_ID_SOURCE DevIdSrc, IN LPCWSTR lpctstrNameGUID, IN OUT LPBYTE *ppData, IN OUT LPDWORD lpdwDataSize );
static DWORD FAXSetExtensionData ( IN HINSTANCE hInst, IN LPCWSTR lpcwstrComputerName, IN DWORD dwOrigDeviceId, IN FAX_ENUM_DEVICE_ID_SOURCE DevIdSrc, IN LPCWSTR lpctstrNameGUID, IN LPBYTE pData, IN DWORD dwDataSize );
static BOOL FindTAPIPermanentLineIdFromFaxDeviceId ( IN DWORD dwFaxDeviceId, OUT LPDWORD lpdwTapiLineId );
BOOL CExtNotifyCallbackPacket::Init( PFAX_EXT_CONFIG_CHANGE pCallback, DWORD dwDeviceId, LPCWSTR lpcwstrDataGuid, LPBYTE lpbData, DWORD dwDataSize)
{ DEBUG_FUNCTION_NAME(TEXT("CExtNotifyCallbackPacket::Init")); DWORD ec = ERROR_SUCCESS;
Assert(pCallback); Assert(lpcwstrDataGuid); Assert(lpbData); Assert(dwDataSize);
Assert(m_lpbData == NULL);
m_pCallback = pCallback; m_dwDeviceId = dwDeviceId;
m_lpwstrGUID = StringDup (lpcwstrDataGuid); if (!m_lpwstrGUID) { ec = GetLastError(); DebugPrintEx( DEBUG_ERR, TEXT("Cannot allocate memory to copy string %s"), lpcwstrDataGuid); goto Error; }
m_dwDataSize = dwDataSize;
m_lpbData = (LPBYTE)MemAlloc(m_dwDataSize); if (!m_lpbData) { ec = GetLastError(); DebugPrintEx( DEBUG_ERR, TEXT("Failed to allocate data for callback packet. Size (%ld)"), m_dwDataSize); goto Error;
}
memcpy(m_lpbData, lpbData, m_dwDataSize); goto Exit;
Error: MemFree(m_lpwstrGUID); MemFree(m_lpbData); m_lpwstrGUID = NULL; m_lpbData = NULL; Exit: return (ERROR_SUCCESS == ec); };
CExtNotifyCallbackPacket::CExtNotifyCallbackPacket() { m_lpwstrGUID = NULL; m_lpbData = NULL; }
CExtNotifyCallbackPacket::~CExtNotifyCallbackPacket() { MemFree(m_lpwstrGUID); MemFree(m_lpbData); }
/************************************
* * * CDeviceAndGUID * * * ************************************/
bool CDeviceAndGUID::operator < ( const CDeviceAndGUID &other ) const /*++
Routine name : operator <
Class: CDeviceAndGUID
Routine description:
Compares myself with another Device and GUID key
Author:
Eran Yariv (EranY), Nov, 1999
Arguments:
other [in] - Other key
Return Value:
true only is i'm less than the other key
--*/ { if (m_dwDeviceId < other.m_dwDeviceId) { return true; } if (m_dwDeviceId > other.m_dwDeviceId) { return false; } //
// Equal device id, comapre GUIDs
//
return (m_strGUID.compare (other.m_strGUID) < 0); } // CDeviceAndGUID::operator <
/************************************
* * * CLocalNotificationSink * * * ************************************/
CLocalNotificationSink::CLocalNotificationSink ( PFAX_EXT_CONFIG_CHANGE lpConfigChangeCallback, DWORD dwNotifyDeviceId, HINSTANCE hInst) : CNotificationSink (), m_lpConfigChangeCallback (lpConfigChangeCallback), m_dwNotifyDeviceId (dwNotifyDeviceId), m_hInst (hInst) /*++
Routine name : CLocalNotificationSink::CLocalNotificationSink
Class: CLocalNotificationSink
Routine description:
CEtensionNotificationSink constractor
Author:
Eran Yariv (EranY), Nov, 1999
Arguments:
lpConfigChangeCallback [in] - Pointer to notification callback dwNotifyDeviceId [in] - Device id to notify with
Return Value:
None.
--*/ { m_type = SINK_TYPE_LOCAL; } // CLocalNotificationSink::CLocalNotificationSink
bool CLocalNotificationSink::operator == ( const CNotificationSink &rhs ) const { Assert (SINK_TYPE_UNKNOWN != rhs.Type()); //
// Comapre types and then downcast to CLocalNotificationSink and compare pointers
//
return ((SINK_TYPE_LOCAL == rhs.Type()) && (m_lpConfigChangeCallback == (static_cast<const CLocalNotificationSink&>(rhs)).m_lpConfigChangeCallback ) ); } // CLocalNotificationSink::operator ==
HRESULT CLocalNotificationSink::Notify ( DWORD dwDeviceId, LPCWSTR lpcwstrNameGUID, LPCWSTR lpcwstrComputerName, HANDLE hModule, LPBYTE lpData, DWORD dwDataSize, LPBOOL lpbRemove ) /*++
Routine name : CLocalNotificationSink::Notify
Class: CLocalNotificationSink
Routine description:
Notify the sink
Author:
Eran Yariv (EranY), Nov, 1999
Arguments:
dwDeviceId [in ] - Device id lpcwstrNameGUID [in ] - Data name lpData [in ] - Pointer to data dwDataSize [in ] - Data size lpbRemove [out] - Set to TRUE if this sink cannot be used and must be removed.
Return Value:
Standard HRESULT.
--*/ { HRESULT hr = NOERROR;
CExtNotifyCallbackPacket * pCallbackPacket = NULL; DEBUG_FUNCTION_NAME(TEXT("CLocalNotificationSink::Notify"));
Assert (m_lpConfigChangeCallback); // Should have caught it in FaxExtRegisterForExtensionEvents
*lpbRemove = FALSE; if (!lstrcmp (TEXT(""), lpcwstrComputerName)) { //
// The source of the data change was local (extension)
//
if (hModule == m_hInst) { //
// The source of the data change is the same module this sink notifies to.
// Don't notify and return success.
//
DebugPrintEx( DEBUG_MSG, TEXT("Local extension (hInst = %ld) set data and the notification for it was blocked"), m_hInst); return hr; } }
pCallbackPacket = new (std::nothrow) CExtNotifyCallbackPacket(); if (!pCallbackPacket) { DebugPrintEx( DEBUG_ERR, TEXT("Failed to allocate callback packet"));
hr = HRESULT_FROM_WIN32(GetLastError()); goto Error; }
if (!pCallbackPacket->Init( m_lpConfigChangeCallback, m_dwNotifyDeviceId, lpcwstrNameGUID, lpData, dwDataSize)) { DWORD ec; ec = GetLastError(); DebugPrintEx( DEBUG_ERR, TEXT("Failed to initialize callback packet (ec: %ld)"), ec); hr = HRESULT_FROM_WIN32(ec); goto Error; }
if (!PostQueuedCompletionStatus ( g_pNotificationMap->m_hCompletionPort, 0, 0, (LPOVERLAPPED)pCallbackPacket )) { DWORD dwRes; dwRes = GetLastError (); DebugPrintEx( DEBUG_ERR, TEXT("PostQueuedCompletionStatus failed. (ec: %ld)"), dwRes); hr = HRESULT_FROM_WIN32(dwRes); goto Error; }
goto Exit; Error: if (pCallbackPacket) { delete pCallbackPacket; }
Exit: return hr; } // CLocalNotificationSink::Notify
/************************************
* * * CSinksList * * * ************************************/
CSinksList::~CSinksList () { DEBUG_FUNCTION_NAME(TEXT("CSinksList::~CSinksList")); try { for (SINKS_LIST::iterator it = m_List.begin(); it != m_List.end(); ++it) { CNotificationSink *pSink = *it; delete pSink; } } catch (exception &ex) { DebugPrintEx( DEBUG_ERR, TEXT("Got an STL exception while clearing a sinks list (%S)"), ex.what()); } } // CSinksList::~CSinksList ()
/************************************
* * * CNotificationMap * * * ************************************/
CNotificationMap::~CNotificationMap () { DEBUG_FUNCTION_NAME(TEXT("CNotificationMap::~CNotificationMap")); try { for (NOTIFY_MAP::iterator it = m_Map.begin(); it != m_Map.end(); ++it) { CSinksList *pSinksList = (*it).second; delete pSinksList; } } catch (exception &ex) { DebugPrintEx( DEBUG_ERR, TEXT("Got an STL exception while clearing the notifications map (%S)"), ex.what()); } //
// Handle our completion port threads now
//
if (m_hCompletionPort) { CloseHandle (m_hCompletionPort); }
//
// Close our critical section
//
m_CsExtensionData.SafeDelete();
} // CNotificationMap::~CNotificationMap
void CNotificationMap::Notify ( DWORD dwDeviceId, LPCWSTR lpcwstrNameGUID, LPCWSTR lpcwstrComputerName, HANDLE hModule, LPBYTE lpData, DWORD dwDataSize) /*++
Routine name : CNotificationMap::Notify
Class: CNotificationMap
Routine description:
Notify the all the sinks (in a list) for a map lookup value. Each sink that returns a failure code (FALSE) is deleted and removed from the list.
After the list is traversed, if it becomes empty, it is deleted and removed from the map.
Author:
Eran Yariv (EranY), Nov, 1999
Arguments:
dwDeviceId [in] - Device id lpcwstrNameGUID [in] - Data name lpcwstrComputerName [in] - Computer where data changing module runs hModule [in] - Handle of the module that changed the data lpData [in] - Pointer to new data dwDataSize [in] - New data size
Return Value:
None.
--*/ { SINKS_LIST::iterator ListIter; CSinksList *pList; DEBUG_FUNCTION_NAME(TEXT("CNotificationMap::Notify"));
//
// We're notifying now - block calls to Add*Sink and Remove*Sink
//
if (g_bServiceIsDown) { //
// We don't supply extension data services when the service is going down
//
DebugPrintEx( DEBUG_ERR, TEXT("Called while service is shutting - operation canceled")); return; } Assert (!m_bNotifying); m_bNotifying = TRUE; CDeviceAndGUID key (dwDeviceId, lpcwstrNameGUID); NOTIFY_MAP::iterator it;
if((it = m_Map.find(key)) == m_Map.end()) { //
// Key not found in map - no one to notify
//
DebugPrintEx( DEBUG_MSG, TEXT("No one to notify")); goto exit; } //
// Retrieve list
//
pList = (*it).second; //
// If the list is already being notified, we're in a loop here - quit now
//
if (pList->m_bNotifying) { //
// OK, here's what happened.
// We were walking the list and notifying each sink. One sink, while processing
// it's notification, called FaxExtSetData on the same GUID + device ID.
// This resulted in a 2nd notification attempt that we're now catching.
// The second notification will not be sent !!!
//
DebugPrintEx( DEBUG_MSG, TEXT("Notification loop caught on device ID = %ld, GUID = %s. 2nd notification cancelled"), dwDeviceId, lpcwstrNameGUID); goto exit; } //
// Mark map value as busy notifying
//
pList->m_bNotifying = TRUE; //
// Walk the list and notify each element
//
for (ListIter = pList->m_List.begin(); ListIter != pList->m_List.end(); ++ListIter) { CNotificationSink *pSink = (*ListIter); BOOL bRemove;
pSink->Notify ( dwDeviceId, lpcwstrNameGUID, lpcwstrComputerName, hModule, lpData, dwDataSize, &bRemove ); if (bRemove) { //
// The notification indicates that the sink became invalid.
// This is a good time to remove it from the list.
//
//
// Tell the sink to gracefully disconnect
//
HRESULT hr = pSink->Disconnect (); delete pSink; //
// Remove item from list, advancing the iterator to next item (or end)
//
ListIter = pList->m_List.erase (ListIter); } } //
// Mark map value as not busy notifying
//
pList->m_bNotifying = FALSE; //
// We might get an empty list here at the end
//
if (pList->m_List.empty()) { //
// Remove empty list from map
//
delete pList; m_Map.erase (key); } exit: //
// We're not notifying any more - allow calls to Add*Sink and Remove*Sink
//
m_bNotifying = FALSE; } // CNotificationMap::Notify
CNotificationSink * CNotificationMap::AddLocalSink ( HINSTANCE hInst, DWORD dwDeviceId, DWORD dwNotifyDeviceId, LPCWSTR lpcwstrNameGUID, PFAX_EXT_CONFIG_CHANGE lpConfigChangeCallback ) /*++
Routine name : CNotificationMap::AddLocalSink
Class: CNotificationMap
Routine description:
Adds a local sink to the list of sinks for a given device id + GUID
Author:
Eran Yariv (EranY), Nov, 1999
Arguments:
hInst [in] - Instance of extension dwDeviceId [in] - Device id to listen to dwNotifyDeviceId [in] - Device id to report in callback lpcwstrNameGUID [in] - Data name lpConfigChangeCallback [in] - Pointer to notification callback
Return Value:
Pointer to newly created sink. If NULL, sets the last error.
--*/ { DWORD dwRes = ERROR_SUCCESS; SINKS_LIST::iterator ListIter; NOTIFY_MAP::iterator it; CSinksList *pList; CNotificationSink *pSink = NULL;
DEBUG_FUNCTION_NAME(TEXT("CNotificationMap::AddLocalSink"));
Assert (lpConfigChangeCallback); // Should have caught it in FaxExtRegisterForExtensionEvents
if (m_bNotifying) { //
// We're notifying now - can't change the list.
//
DebugPrintEx( DEBUG_MSG, TEXT("Caller tried to to add a local sink to a notification list while notifying")); SetLastError (ERROR_BUSY); // The requested resource is in use.
return NULL; } //
// See if entry exists in map
//
CDeviceAndGUID key (dwDeviceId, lpcwstrNameGUID); if((it = m_Map.find(key)) == m_Map.end()) { //
// Key not found in map - add it with a new list
//
pList = new (std::nothrow) CSinksList; if (!pList) { DebugPrintEx( DEBUG_ERR, TEXT("Cannot allocate a new sinks list")); SetLastError (ERROR_NOT_ENOUGH_MEMORY); return NULL; } m_Map[key] = pList; } else { //
// Get the existing list
//
pList = (*it).second; } //
// Create new sink
//
pSink = new (std::nothrow) CLocalNotificationSink (lpConfigChangeCallback, dwNotifyDeviceId, hInst); if (!pSink) { //
// Can't crate sink
//
DebugPrintEx( DEBUG_ERR, TEXT("Cannot allocate a notification sink")); SetLastError (ERROR_NOT_ENOUGH_MEMORY); goto exit; } //
// Scan the list to see if an identical sink already exists.
//
for (ListIter = pList->m_List.begin(); ListIter != pList->m_List.end(); ++ListIter) { CNotificationSink *pCurSink = (*ListIter); if (*pSink == *pCurSink) { //
// Ooops, same sink already exists
//
DebugPrintEx( DEBUG_MSG, TEXT("Caller tried to to add an indetical local sink to a notification list")); SetLastError (ERROR_ALREADY_ASSIGNED); //
// Tell the sink to gracefully disconnect
//
HRESULT hr = pSink->Disconnect (); delete pSink; pSink = NULL; goto exit; } } //
// Add the new sink
//
pList->m_List.insert (pList->m_List.end(), pSink);
exit: if (pList->m_List.empty()) { //
// Remove empty list from map
//
delete pList; m_Map.erase (key); } return pSink; } // CNotificationMap::AddLocalSink
DWORD CNotificationMap::RemoveSink ( CNotificationSink *pSinkToRemove ) /*++
Routine name : CNotificationMap::RemoveSink
Class: CNotificationMap
Routine description:
Removes a sink from the list of sinks for a given sink pointer. If the list is empty, it is deleted and removed from the map.
Author:
Eran Yariv (EranY), Nov, 1999
Arguments:
Return Value:
Standard Win32 error code.
--*/ { DEBUG_FUNCTION_NAME(TEXT("CNotificationMap::RemoveSink"));
if (m_bNotifying) { //
// We're notifying now - can't change the list.
//
DebugPrintEx( DEBUG_MSG, TEXT("Caller tried to to add a local sink to a notification list while notifying")); return ERROR_BUSY; // The requested resource is in use.
} //
// Lookup the sink
//
NOTIFY_MAP::iterator it; BOOL bFound = FALSE; for (it = m_Map.begin(); it != m_Map.end (); ++it) { //
// Get map value (list of sinks)
//
CSinksList *pList = (*it).second; //
// Lookup sink in list
//
SINKS_LIST::iterator ListIter; CNotificationSink *pSink = NULL; for (ListIter = pList->m_List.begin(); ListIter != pList->m_List.end(); ++ListIter) { pSink = (*ListIter); if (pSinkToRemove == pSink) // Pointer comparison !!!!
{ //
// Found the sink - remove it
//
pList->m_List.erase (ListIter); HRESULT hr = pSinkToRemove->Disconnect (); delete pSinkToRemove; bFound = TRUE; break; } } if (bFound) { //
// Since we removed a sink from the list, the list may become empty now
//
if (pList->m_List.empty()) { //
// Remove empty list
//
m_Map.erase (it); delete pList; } //
// Break the map search
//
break; } } if (!bFound) { //
// Reached the end of the map but the requested sink could not be found
//
DebugPrintEx( DEBUG_MSG, TEXT("Caller tried to to remove a non-existent sink")); return ERROR_NOT_FOUND; // Element not found.
} return ERROR_SUCCESS; } // CNotificationMap::RemoveSink
DWORD CNotificationMap::ExtNotificationThread( LPVOID UnUsed ) /*++
Routine name : CNotificationMap::ExtNotificationThread
Routine description:
This is the main thread function of the thread(s) that dequeue the notification completion port.
This is a static class function !!!!
Pointers to instances of ExtNotificationDataPacket are dequeued by this function and the map notificiation function is called on them.
Author:
Eran Yariv (EranY), Dec, 1999
Arguments:
UnUsed [in] - Unused
Return Value:
Standard Win32 Error code
--*/ { DWORD dwRes; DEBUG_FUNCTION_NAME(TEXT("CNotificationMap::ExtNotificationThread"));
for (;;) { DWORD dwNumBytes; ULONG_PTR CompletionKey; CExtNotifyCallbackPacket *pPacket;
if (!GetQueuedCompletionStatus ( g_pNotificationMap->m_hCompletionPort, &dwNumBytes, &CompletionKey, (LPOVERLAPPED*) &pPacket, INFINITE )) { dwRes = GetLastError (); DebugPrintEx( DEBUG_ERR, TEXT("GetQueuedCompletionStatus failed with error = %ld. Aborting thread"), dwRes); return dwRes; } if (SERVICE_SHUT_DOWN_KEY == CompletionKey) { //
// This is a special signal from the service that all thread should die now. Tell all other notification threads to die
//
if (!PostQueuedCompletionStatus( g_pNotificationMap->m_hCompletionPort, 0, SERVICE_SHUT_DOWN_KEY, (LPOVERLAPPED) NULL)) { dwRes = GetLastError(); DebugPrintEx( DEBUG_ERR, TEXT("PostQueuedCompletionStatus failed (SERVICE_SHUT_DOWN_KEY). (ec: %ld)"), dwRes); }
if (!DecreaseServiceThreadsCount()) { DebugPrintEx( DEBUG_ERR, TEXT("DecreaseServiceThreadsCount() failed (ec: %ld)"), GetLastError()); } return ERROR_SUCCESS; } Assert (pPacket && pPacket->m_lpbData && pPacket->m_dwDataSize && pPacket->m_lpwstrGUID );
//
// Do the notification
//
DebugPrintEx( DEBUG_MSG, TEXT("Calling notification callback %p. DeviceId: %ld GUID: %s Data: %p DataSize: %ld"), pPacket->m_pCallback, pPacket->m_dwDeviceId, pPacket->m_lpwstrGUID, pPacket->m_lpbData, pPacket->m_dwDataSize);
pPacket->m_pCallback(pPacket->m_dwDeviceId, // Notify with internal device id.
pPacket->m_lpwstrGUID, pPacket->m_lpbData, pPacket->m_dwDataSize);
//
// Kill notification object
//
delete pPacket; } // Dequeue loop
UNREFERENCED_PARAMETER (UnUsed); } // CNotificationMap::ExtNotificationThread
DWORD CNotificationMap::Init () /*++
Routine name : CNotificationMap::Init
Routine description:
Initialize the notification map
Author:
Eran Yariv (EranY), Dec, 1999
Arguments:
Return Value:
Standard Win32 error code
--*/ { DWORD dwRes; DWORD dwNumThreads = 0; DEBUG_FUNCTION_NAME(TEXT("CNotificationMap::Init"));
//
// Try to init our critical section
//
if (!m_CsExtensionData.Initialize()) { dwRes = GetLastError(); DebugPrintEx( DEBUG_ERR, TEXT("CFaxCriticalSection::Initialize(&m_CsExtensionData) failed: err = %d"), dwRes); return dwRes; } //
// Create the completion port
//
m_hCompletionPort = CreateIoCompletionPort ( INVALID_HANDLE_VALUE, // No device
NULL, // New one
0, // Key
MAX_CONCURRENT_EXT_DATA_SET_THREADS); if (NULL == m_hCompletionPort) { dwRes = GetLastError (); DebugPrintEx( DEBUG_ERR, TEXT("CreateIoCompletionPort failed with %ld"), dwRes); return dwRes; } //
// Create completion port dequeueing thread(s)
//
for (DWORD dw = 0; dw < NUM_EXT_DATA_SET_THREADS; dw++) { HANDLE hThread = CreateThreadAndRefCount ( NULL, // Security
0, // Stack size
g_pNotificationMap->ExtNotificationThread, // Start routine
0, // Parameter
0, // Creation flag(s)
NULL); // Don't want thread id
if (NULL == hThread) { dwRes = GetLastError (); DebugPrintEx( DEBUG_ERR, TEXT("CreateThreadAndRefCount failed with %ld"), dwRes); } else { dwNumThreads++; CloseHandle(hThread); } } if (!dwNumThreads) { //
// Not even a single thread was created
//
CloseHandle (m_hCompletionPort); m_hCompletionPort = NULL; return dwRes; } return ERROR_SUCCESS; } // CNotificationMap::Init
/************************************
* * * CMapDeviceId * * * ************************************/
DWORD CMapDeviceId::AddDevice ( DWORD dwDeviceId, DWORD dwFaxId ) /*++
Routine name : CMapDeviceId::AddDevice
Routine description:
Adds a new device to the devices map
Author:
Eran Yariv (EranY), Dec, 1999
Arguments:
dwDeviceId [in] - The source id of the device dwFaxId [in] - The unique fax device id (destination id)
Return Value:
Standard Win32 error code
--*/ { DEVICE_IDS_MAP::iterator it; DWORD dwRes = ERROR_SUCCESS; DEBUG_FUNCTION_NAME(TEXT("CMapDeviceId::AddDevice"));
EnterCriticalSection (&g_pNotificationMap->m_CsExtensionData); try { //
// See if entry exists in map
//
if((it = m_Map.find(dwDeviceId)) != m_Map.end()) { dwRes = ERROR_ALREADY_ASSIGNED; goto exit; } //
// Add new map entry
//
m_Map[dwDeviceId] = dwFaxId; } catch (exception &ex) { DebugPrintEx( DEBUG_ERR, TEXT("map caused exception (%S)"), ex.what()); dwRes = ERROR_GEN_FAILURE; }
exit: LeaveCriticalSection (&g_pNotificationMap->m_CsExtensionData); return dwRes; } // CMapDeviceId::AddDevice
DWORD CMapDeviceId::RemoveDevice ( DWORD dwDeviceId ) /*++
Routine name : CMapDeviceId::RemoveDevice
Routine description:
Removes an existing device from the devices map
Author:
Eran Yariv (EranY), Dec, 1999
Arguments:
dwDeviceId [in] - The source id of the device
Return Value:
Standard Win32 error code
--*/ { DEVICE_IDS_MAP::iterator it; DWORD dwRes = ERROR_SUCCESS; DEBUG_FUNCTION_NAME(TEXT("CMapDeviceId::RemoveDevice"));
EnterCriticalSection (&g_pNotificationMap->m_CsExtensionData); try { //
// See if entry exists in map
//
if((it = m_Map.find(dwDeviceId)) == m_Map.end()) { dwRes = ERROR_NOT_FOUND; goto exit; } //
// Remove map entry
//
m_Map.erase (it); } catch (exception &ex) { DebugPrintEx( DEBUG_ERR, TEXT("map caused exception (%S)"), ex.what()); dwRes = ERROR_GEN_FAILURE; }
exit: LeaveCriticalSection (&g_pNotificationMap->m_CsExtensionData); return dwRes; } // CMapDeviceId::RemoveDevice
DWORD CMapDeviceId::LookupUniqueId ( DWORD dwOtherId, LPDWORD lpdwFaxId ) const /*++
Routine name : CMapDeviceId::LookupUniqueId
Routine description:
Looks up a unique fax device id from a given device id
Author:
Eran Yariv (EranY), Dec, 1999
Arguments:
dwOtherId [in ] - Given device it (lookup source) lpdwFaxId [out] - Fax unique device id
Return Value:
Standard Win32 error code
--*/ { DEVICE_IDS_MAP::iterator it; DWORD dwRes = ERROR_SUCCESS; DEBUG_FUNCTION_NAME(TEXT("CMapDeviceId::LookupUniqueId"));
if (!dwOtherId) { //
// Special device id == 0 case
//
*lpdwFaxId = 0; return dwRes; } EnterCriticalSection (&g_pNotificationMap->m_CsExtensionData); try { //
// See if entry exists in map
//
if((it = m_Map.find(dwOtherId)) == m_Map.end()) { dwRes = ERROR_NOT_FOUND; goto exit; } *lpdwFaxId = (*it).second; } catch (exception &ex) { DebugPrintEx( DEBUG_ERR, TEXT("map caused exception (%S)"), ex.what()); dwRes = ERROR_GEN_FAILURE; }
exit: LeaveCriticalSection (&g_pNotificationMap->m_CsExtensionData); return dwRes; } // CMapDeviceId::LookupUniqueId
/************************************
* * * Globals * * * ************************************/
CNotificationMap* g_pNotificationMap; // Map of DeviceId+GUID to list of notification sinks
/*
The map maps between TAPI permanent line ids to fax unique ids. TAPI-based FSPs / EFPSs talk to us using TAPI-permamnet line ids and have no clue of the fax unique device ids. This map is here for quick lookup for TAPI-based FSPs. */
CMapDeviceId* g_pTAPIDevicesIdsMap; // Map between TAPI permanent line id and fax unique device id.
DWORD LookupUniqueFaxDeviceId ( DWORD dwDeviceId, LPDWORD lpdwResult, FAX_ENUM_DEVICE_ID_SOURCE DevIdSrc) /*++
Routine name : LookupUniqueFaxDeviceId
Routine description:
Looks up a fax unique device id from a general device id.
Author:
Eran Yariv (EranY), Dec, 1999
Arguments:
dwDeviceId [in ] - Original device id lpdwResult [out] - Looked up device id DevIdSrc [in ] - Source of device id
Return Value:
Standard Win32 error code.
--*/ { DEBUG_FUNCTION_NAME(TEXT("LookupUniqueFaxDeviceId"));
switch (DevIdSrc) { case DEV_ID_SRC_FAX: // No maping required
*lpdwResult = dwDeviceId; return ERROR_SUCCESS;
case DEV_ID_SRC_TAPI: return g_pTAPIDevicesIdsMap->LookupUniqueId (dwDeviceId, lpdwResult);
default: DebugPrintEx( DEBUG_ERR, TEXT("Invalid device id source (%ld)"), DevIdSrc); ASSERT_FALSE; return ERROR_INVALID_PARAMETER; } } // LookupUniqueFaxDeviceId
/************************************
* * * Get/Set Data * * * ************************************/
static BOOL FindTAPIPermanentLineIdFromFaxDeviceId ( IN DWORD dwFaxDeviceId, OUT LPDWORD lpdwTapiLineId ) /*++
Routine name : FindTAPIPermanentLineIdFromFaxDeviceId
Routine description:
Given a fax device id, returns the TAPI permanent line id associated with this fax device. If the fax device is not found or is a virtual fax (no TAPI association), the search fails.
Author:
Eran Yariv (EranY), Feb, 2000
Arguments:
dwFaxDeviceId [in] - Fax device id lpdwTapiLineId [out] - TAPI permanent line id
Return Value:
TRUE if the search succeeed. FALSE otherwise.
--*/ { BOOL bRes = FALSE;
DEBUG_FUNCTION_NAME(TEXT("FindTAPIPermanentLineIdFromFaxDeviceId"));
EnterCriticalSection(&g_CsLine); PLINE_INFO pLine = GetTapiLineFromDeviceId (dwFaxDeviceId, FALSE); if (!pLine) { goto exit; } if (pLine->Flags & FPF_VIRTUAL) { //
// This fax device is virtual. It does not have a corresponding TAPI line.
//
goto exit; } *lpdwTapiLineId = pLine->TapiPermanentLineId; bRes = TRUE; exit: LeaveCriticalSection(&g_CsLine); return bRes; } // FindTAPIPermanentLineIdFromFaxDeviceId
DWORD FAXGetExtensionData ( IN DWORD dwOrigDeviceId, IN FAX_ENUM_DEVICE_ID_SOURCE DevIdSrc, IN LPCWSTR lpctstrNameGUID, IN OUT LPBYTE *ppData, IN OUT LPDWORD lpdwDataSize ) /*++
Routine name : FAXGetExtensionData
Routine description:
Gets the extension data for a device (internal)
Author:
Eran Yariv (EranY), Feb, 2000
Arguments:
dwOrigDeviceId [in] - Original device id (as arrived from extension) DevIdSrc [in] - Device id source (Fax / TAPI) lpctstrNameGUID [in] - Data GUID ppData [out] - Pointer to data buffer lpdwDataSize [out] - Pointer to retrieved data size
Return Value:
Standard Win32 error code
--*/ { DWORD dwRes; DEBUG_FUNCTION_NAME(TEXT("FAXGetExtensionData"));
if (!lpctstrNameGUID || !ppData || !lpdwDataSize) { return ERROR_INVALID_PARAMETER; }
if ((DevIdSrc != DEV_ID_SRC_FAX) && (DevIdSrc != DEV_ID_SRC_TAPI)) { //
// Invalid device id class
//
return ERROR_INVALID_PARAMETER; }
dwRes = IsValidGUID (lpctstrNameGUID); if (ERROR_SUCCESS != dwRes) { if (ERROR_WMI_GUID_NOT_FOUND == dwRes) { //
// Return a more conservative error code
//
dwRes = ERROR_INVALID_PARAMETER; } return dwRes; } if (DEV_ID_SRC_FAX == DevIdSrc) { //
// Try to see if this fax device has a matching tapi line id.
//
DWORD dwTapiLineId; if (FindTAPIPermanentLineIdFromFaxDeviceId (dwOrigDeviceId, &dwTapiLineId)) { //
// Matching tapi line id found. Use it to read the data.
//
DevIdSrc = DEV_ID_SRC_TAPI; dwOrigDeviceId = dwTapiLineId; } } EnterCriticalSection (&g_pNotificationMap->m_CsExtensionData); dwRes = ReadExtensionData ( dwOrigDeviceId, DevIdSrc, lpctstrNameGUID, ppData, lpdwDataSize ); if (ERROR_SUCCESS != dwRes) { DebugPrintEx( DEBUG_ERR, TEXT("Reading extension data for device id %ld, GUID %s failed with %ld"), dwOrigDeviceId, lpctstrNameGUID, dwRes); } LeaveCriticalSection (&g_pNotificationMap->m_CsExtensionData); return dwRes; } // FAXGetExtensionData
DWORD FAXSetExtensionData ( IN HINSTANCE hInst, IN LPCWSTR lpcwstrComputerName, IN DWORD dwOrigDeviceId, IN FAX_ENUM_DEVICE_ID_SOURCE DevIdSrc, IN LPCWSTR lpctstrNameGUID, IN LPBYTE pData, IN DWORD dwDataSize ) /*++
Routine name : FAXSetExtensionData
Routine description:
Writes the extension data for a device (internal)
Author:
Eran Yariv (EranY), Feb, 2000
Arguments:
hInst [in] - Caller's instance lpcwstrComputerName [in] - Calling module computer name dwOrigDeviceId [in] - Original device id (as arrived from extension) DevIdSrc [in] - Device id source (Fax / TAPI) lpctstrNameGUID [in] - Data GUID pData [in] - Pointer to data buffer dwDataSize [in] - Data size
Return Value:
Standard Win32 error code
--*/ { DWORD dwRes; DEBUG_FUNCTION_NAME(TEXT("FAXSetExtensionData"));
if (!lpctstrNameGUID || !pData || !dwDataSize || !lpcwstrComputerName) { return ERROR_INVALID_PARAMETER; }
if ((DevIdSrc != DEV_ID_SRC_FAX) && (DevIdSrc != DEV_ID_SRC_TAPI)) { //
// Invalid device id class
//
return ERROR_INVALID_PARAMETER; }
dwRes = IsValidGUID (lpctstrNameGUID); if (ERROR_SUCCESS != dwRes) { if (ERROR_WMI_GUID_NOT_FOUND == dwRes) { //
// Return a more conservative error code
//
dwRes = ERROR_INVALID_PARAMETER; } return dwRes; } FAX_ENUM_DEVICE_ID_SOURCE RegistryDeviceIdSource = DevIdSrc; DWORD dwRegistryDeviceId = dwOrigDeviceId; if (DEV_ID_SRC_FAX == DevIdSrc) { //
// Try to see if this fax device has a matching tapi line id.
//
DWORD dwTapiLineId; if (FindTAPIPermanentLineIdFromFaxDeviceId (dwOrigDeviceId, &dwTapiLineId)) { //
// Matching tapi line id found. Use it to read the data.
//
RegistryDeviceIdSource = DEV_ID_SRC_TAPI; dwRegistryDeviceId = dwTapiLineId; } } EnterCriticalSection (&g_pNotificationMap->m_CsExtensionData); dwRes = WriteExtensionData (dwRegistryDeviceId, RegistryDeviceIdSource, lpctstrNameGUID, pData, dwDataSize ); if (ERROR_SUCCESS != dwRes) { DebugPrintEx( DEBUG_ERR, TEXT("Writing extension data for device id %ld (registry device id = %ld), GUID %s failed with %ld"), dwOrigDeviceId, dwRegistryDeviceId, lpctstrNameGUID, dwRes); goto exit; } //
// Notification is always done using the fax id (not the TAPI device id).
// We must lookup the fax id from the TAPI id before we attempt notification registration.
//
DWORD dwFaxUniqueID; dwRes = LookupUniqueFaxDeviceId (dwOrigDeviceId, &dwFaxUniqueID, DevIdSrc); if (ERROR_SUCCESS != dwRes) { DebugPrintEx( DEBUG_ERR, TEXT("LookupUniqueFaxDeviceId failed for device id %ld (ec: %ld). No write notification will be performed."), dwOrigDeviceId, dwRes); //
// We support writing to non-exiting devices configuration data
//
}
if (ERROR_SUCCESS == dwRes) {
try { g_pNotificationMap->Notify ( dwFaxUniqueID, // Device for which data has changed
lpctstrNameGUID, // Name of data
lpcwstrComputerName,// Computer name from which data has chnaged
hInst, // Module handle from which data has changed
pData, // Pointer to new data
dwDataSize); // Size of new data
} catch (exception &ex) { DebugPrintEx( DEBUG_ERR, TEXT("Notify() caused exception (%S)"), ex.what()); } }
exit: LeaveCriticalSection (&g_pNotificationMap->m_CsExtensionData); return dwRes; } // FAXSetExtensionData
/************************************
* * * RPC handlers * * * ************************************/ extern "C" error_status_t FAX_GetExtensionData ( IN handle_t hFaxHandle, IN DWORD dwDeviceId, IN LPCWSTR lpctstrNameGUID, IN OUT LPBYTE *ppData, IN OUT LPDWORD lpdwDataSize ) /*++
Routine name : FAX_GetExtensionData
Routine description:
Read the extension's private data - Implements FaxGetExtensionData
Author:
Eran Yariv (EranY), Nov, 1999
Arguments:
hFaxHandle [in ] - Unused dwDeviceId [in ] - Device identifier. 0 = Unassociated data lpctstrNameGUID [in ] - GUID of named data ppData [out] - Pointer to data buffer lpdwDataSize [out] - Returned size of data
Return Value:
Standard RPC error codes
--*/ { DWORD dwRes; BOOL fAccess; DEBUG_FUNCTION_NAME(TEXT("FAX_GetExtensionData"));
//
// Access check
//
dwRes = FaxSvcAccessCheck (FAX_ACCESS_QUERY_CONFIG, &fAccess, NULL); if (ERROR_SUCCESS != dwRes) { DebugPrintEx(DEBUG_ERR, TEXT("FaxSvcAccessCheck Failed, Error : %ld"), dwRes); return dwRes; }
if (FALSE == fAccess) { DebugPrintEx(DEBUG_ERR, TEXT("The user does not have the FAX_ACCESS_QUERY_CONFIG right")); return ERROR_ACCESS_DENIED; }
return FAXGetExtensionData (dwDeviceId, DEV_ID_SRC_FAX, // RPC clients do not know the TAPI line ids
lpctstrNameGUID, ppData, lpdwDataSize); } // FAX_GetExtensionData
extern "C" error_status_t FAX_SetExtensionData ( IN handle_t hFaxHandle, IN LPCWSTR lpcwstrComputerName, IN DWORD dwDeviceId, IN LPCWSTR lpctstrNameGUID, IN LPBYTE pData, IN DWORD dwDataSize ) /*++
Routine name : FAX_SetExtensionData
Routine description:
Write the extension's private data - Implements FaxSetExtensionData
Author:
Eran Yariv (EranY), Nov, 1999
Arguments:
hFaxHandle [in] - The handle of the module that sets the data lpcwstrComputerName [in] - The computer name of the module that sets the data dwDeviceId [in] - Device identifier. 0 = Unassociated data lpctstrNameGUID [in] - GUID of named data pData [in] - Pointer to data dwDataSize [in] - Size of data
Return Value:
Standard RPC error codes
--*/ { DWORD dwRes; BOOL fAccess; DEBUG_FUNCTION_NAME(TEXT("FAX_SetExtensionData"));
//
// Access check
//
dwRes = FaxSvcAccessCheck (FAX_ACCESS_MANAGE_CONFIG, &fAccess, NULL); if (ERROR_SUCCESS != dwRes) { DebugPrintEx(DEBUG_ERR, TEXT("FaxSvcAccessCheck Failed, Error : %ld"), dwRes); return dwRes; }
if (FALSE == fAccess) { DebugPrintEx(DEBUG_ERR, TEXT("The user does not have the FAX_ACCESS_MANAGE_CONFIG right")); return ERROR_ACCESS_DENIED; }
return FAXSetExtensionData ((HINSTANCE)hFaxHandle, lpcwstrComputerName, dwDeviceId, DEV_ID_SRC_FAX, // RPC clients do not know the TAPI line ids
lpctstrNameGUID, pData, dwDataSize); } // FAX_SetExtensionData
/************************************
* * * Callback functions (fxsext.h) * * * ************************************/
DWORD FaxExtGetData ( DWORD dwDeviceId, // Device id (0 = No device)
FAX_ENUM_DEVICE_ID_SOURCE DevIdSrc, // The source of the device id
LPCWSTR lpcwstrNameGUID,// GUID of data
LPBYTE *ppData, // (Out) Pointer to allocated data
LPDWORD lpdwDataSize // (Out) Pointer to data size
) /*++
Routine name : FaxExtGetData
Routine description:
Callback function (called from the fax extension). Gets data for a device + GUID
Author:
Eran Yariv (EranY), Dec, 1999
Arguments:
dwDeviceId [in] - Device id (0 = No device) DevIdSrc [in] - The source of the device id lpcwstrNameGUID [in] - GUID of data ppData [in] - Pointer to data buffer lpdwDataSize [in] - Data size retrieved
Return Value:
Standard Win32 error code
--*/ { DEBUG_FUNCTION_NAME(TEXT("FaxExtGetData")); if (g_bServiceIsDown) { //
// We don't supply extension data services when the service is going down
//
DebugPrintEx( DEBUG_ERR, TEXT("Called while service is shutting - operation canceled")); return ERROR_SHUTDOWN_IN_PROGRESS; } return FAXGetExtensionData ( dwDeviceId, DevIdSrc, lpcwstrNameGUID, ppData, lpdwDataSize ); } // FaxExtGetData
DWORD FaxExtSetData ( HINSTANCE hInst, DWORD dwDeviceId, FAX_ENUM_DEVICE_ID_SOURCE DevIdSrc, LPCWSTR lpcwstrNameGUID, LPBYTE pData, DWORD dwDataSize ) /*++
Routine name : FaxExtSetData
Routine description:
Callback function (called from the fax extension). Sets data for a device + GUID
Author:
Eran Yariv (EranY), Dec, 1999
Arguments:
hInst [in] - Extension DLL instance dwDeviceId [in] - Device id (0 = No device) DevIdSrc [in] - The source of the device id lpcwstrNameGUID [in] - GUID of data pData [in] - Pointer to data size [in] - Data size
Return Value:
Standard Win32 error code
--*/ { DEBUG_FUNCTION_NAME(TEXT("FaxExtSetData")); if (g_bServiceIsDown) { //
// We don't supply extension data services when the service is going down
//
DebugPrintEx( DEBUG_ERR, TEXT("Called while service is shutting - operation canceled")); return ERROR_SHUTDOWN_IN_PROGRESS; } return FAXSetExtensionData ( hInst, TEXT (""), // No computer name - a local extension sets the data
dwDeviceId, DevIdSrc, lpcwstrNameGUID, pData, dwDataSize ); } // FaxExtSetData
HANDLE FaxExtRegisterForEvents ( HINSTANCE hInst, DWORD dwDeviceId, // Device id (0 = No device)
FAX_ENUM_DEVICE_ID_SOURCE DevIdSrc, // The source of the device id
LPCWSTR lpcwstrNameGUID, // GUID of data
PFAX_EXT_CONFIG_CHANGE lpConfigChangeCallback // Notification callback function
) /*++
Routine name : FaxExtRegisterForEvents
Routine description:
Register a local callback for notifications on a data change for a device and GUID
Author:
Eran Yariv (EranY), Nov, 1999
Arguments:
hInst [in] - Instance of calling extension dwDeviceId [in] - Device id bTapiDevice [in] - If TRUE, the function attempts to convert to a Fax unique device id. The callback will receive the device id specified in dwDeviceId regardless of the lookup. lpcwstrNameGUID [in] - Data name lpConfigChangeCallback [in] - Pointer to notification callback function
Return Value:
Notification HANDLE. If NULL, call GetLastError () to retrieve error code.
--*/ { HANDLE h = NULL; DEBUG_FUNCTION_NAME(TEXT("FaxExtRegisterForEvents"));
if (g_bServiceIsDown) { //
// We don't supply extension data services when the service is going down
//
DebugPrintEx( DEBUG_ERR, TEXT("Called while service is shutting - operation canceled")); SetLastError (ERROR_SHUTDOWN_IN_PROGRESS); return NULL; }
if (!lpConfigChangeCallback) { SetLastError (ERROR_INVALID_PARAMETER); return NULL; } DWORD dwRes = IsValidGUID (lpcwstrNameGUID); if (ERROR_SUCCESS != dwRes) { if (ERROR_WMI_GUID_NOT_FOUND == dwRes) { //
// Return a more conservative error code
//
dwRes = ERROR_INVALID_PARAMETER; } SetLastError (dwRes); return NULL; } //
// Notification is always done using the fax id (not the TAPI device id).
// We must lookup the fax id from the TAPI id before we attempt notification registration.
//
DWORD dwFaxUniqueID; dwRes = LookupUniqueFaxDeviceId (dwDeviceId, &dwFaxUniqueID, DevIdSrc); if (ERROR_SUCCESS != dwRes) { DebugPrintEx( DEBUG_ERR, TEXT("LookupUniqueFaxDeviceId failed for device id %ld (ec: %ld)"), dwDeviceId, dwRes); SetLastError (dwRes); return NULL; } EnterCriticalSection (&g_pNotificationMap->m_CsExtensionData); try { //
// STL throws exceptions
//
h = (HANDLE) g_pNotificationMap->AddLocalSink ( hInst, // Instance of extension
dwFaxUniqueID, // Listen to fax device unique id
dwDeviceId, // Report the id specified by the caller.
lpcwstrNameGUID, lpConfigChangeCallback); } catch (exception &ex) { DebugPrintEx( DEBUG_ERR, TEXT("AddLocalSink() caused exception (%S)"), ex.what()); SetLastError (ERROR_GEN_FAILURE); } LeaveCriticalSection (&g_pNotificationMap->m_CsExtensionData); return h; } // FaxExtRegisterForEvents
DWORD FaxExtUnregisterForEvents ( HANDLE hNotification ) /*++
Routine name : FaxExtUnregisterForEvents
Routine description:
Unregsiters a local callback for notifications on a data change for a device and GUID.
The functions succeeds only if the same callback function was previously registered (by calling FaxExtRegisterForEvents) to the same device id and GUID.
Author:
Eran Yariv (EranY), Nov, 1999
Arguments:
hNotification [in] - Notification handle returned by FaxExtRegisterForExtensionEvents
Return Value:
Standard Win32 error code.
--*/ { DWORD dwRes; DEBUG_FUNCTION_NAME(TEXT("FaxExtUnregisterForEvents"));
if (g_bServiceIsDown) { //
// We don't supply extension data services when the service is going down
//
DebugPrintEx( DEBUG_ERR, TEXT("Called while service is shutting - operation canceled")); return ERROR_SHUTDOWN_IN_PROGRESS; } EnterCriticalSection (&g_pNotificationMap->m_CsExtensionData); try { //
// STL throws exceptions
//
dwRes = g_pNotificationMap->RemoveSink ( (CNotificationSink *)(hNotification) ); } catch (exception &ex) { DebugPrintEx( DEBUG_ERR, TEXT("RemoveLocalSink() caused exception (%S)"), ex.what()); dwRes = ERROR_GEN_FAILURE; } LeaveCriticalSection (&g_pNotificationMap->m_CsExtensionData); return dwRes; } // FaxExtUnregisterForEvents
|