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.
2045 lines
53 KiB
2045 lines
53 KiB
/*++
|
|
|
|
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
|
|
|