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.
521 lines
20 KiB
521 lines
20 KiB
/*****************************************************************************
|
|
* (C) COPYRIGHT MICROSOFT CORPORATION, 2002
|
|
*
|
|
* AUTHOR: ByronC
|
|
*
|
|
* DATE: 3/22/2002
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @module AsyncRPCEventTransport.cpp - Implementation for the client-side transport mechanism to receive events |
|
|
*
|
|
* This file contains the implementation for the <c AsyncRPCEventTransport>
|
|
* class. It is used to shield the higher-level run-time event notification
|
|
* classes from the particulars of using AsyncRPC as a transport mechanism
|
|
* for event notifications.
|
|
*
|
|
*****************************************************************************/
|
|
#include "cplusinc.h"
|
|
#include "coredbg.h"
|
|
|
|
/*****************************************************************************
|
|
* @doc INTERNAL
|
|
*
|
|
* @mfunc | AsyncRPCEventTransport | AsyncRPCEventTransport |
|
|
*
|
|
* We initialize all member variables. In general, this sets the values to 0,
|
|
* except:
|
|
* <nl><md AsyncRPCEventTransport::m_ulSig> is set to be AsyncRPCEventTransport_UNINIT_SIG.
|
|
*
|
|
*****************************************************************************/
|
|
AsyncRPCEventTransport::AsyncRPCEventTransport() :
|
|
ClientEventTransport(),
|
|
m_RpcBindingHandle(NULL),
|
|
m_AsyncClientContext(NULL),
|
|
m_SyncClientContext(NULL)
|
|
{
|
|
DBG_FN(AsyncRPCEventTransport);
|
|
|
|
memset(&m_AsyncState, 0, sizeof(m_AsyncState));
|
|
memset(&m_AsyncEventNotifyData, 0, sizeof(m_AsyncEventNotifyData));
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* @doc INTERNAL
|
|
*
|
|
* @mfunc | AsyncRPCEventTransport | ~AsyncRPCEventTransport |
|
|
*
|
|
* Do any cleanup that is not already done. Specifically, we:
|
|
* <nl> - Call <mf AsyncRPCEventTransport::FreeAsyncEventNotifyData>.
|
|
* <nl> - Call <mf AsyncRPCEventTransport::CloseNotificationChannel>.
|
|
* <nl> - Call <mf AsyncRPCEventTransport::CloseConnectionToServer>.
|
|
*
|
|
* Also:
|
|
* <nl><md AsyncRPCEventTransport::m_ulSig> is set to be AsyncRPCEventTransport_DEL_SIG.
|
|
*
|
|
*****************************************************************************/
|
|
AsyncRPCEventTransport::~AsyncRPCEventTransport()
|
|
{
|
|
DBG_FN(~AsyncRPCEventTransport);
|
|
|
|
FreeAsyncEventNotifyData();
|
|
|
|
// Close notification channel and connection to server. In both cases
|
|
// we're not interested in the return values.
|
|
HRESULT hr = S_OK;
|
|
hr = CloseNotificationChannel();
|
|
hr = CloseConnectionToServer();
|
|
|
|
m_AsyncClientContext = NULL;
|
|
m_SyncClientContext = NULL;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* @doc INTERNAL
|
|
*
|
|
* @mfunc HRESULT | AsyncRPCEventTransport | OpenConnectionToServer |
|
|
*
|
|
* This function does the necessary setup work needed to make our RPC calls.
|
|
* Essentially, it simply binds to the RPC Server over LRPC. The RPC binding
|
|
* handle is stored in the member variable
|
|
* <mf AsyncRPCEventTransport::m_RpcBindingHandle>.
|
|
*
|
|
* If <mf AsyncRPCEventTransport::m_RpcBindingHandle> is not NULL, this method
|
|
* will call <mf AsyncRPCEventTransport::CloseConnectionToServer> to free it,
|
|
* and then attempt to establish a new connection.
|
|
*
|
|
* If successful, callers should clean-up by calling
|
|
* <mf AsyncRPCEventTransport::CloseConnectionToServer>, although the destructor
|
|
* will do it if the caller does not.
|
|
*
|
|
* @rvalue RPC_S_OK |
|
|
* The function succeeded.
|
|
* @rvalue RPC_XXXXXXX |
|
|
* RPC Error code.
|
|
*****************************************************************************/
|
|
HRESULT AsyncRPCEventTransport::OpenConnectionToServer()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
DBG_TRC(("Opened connection to server"));
|
|
|
|
RpcTryExcept
|
|
{
|
|
//
|
|
// Check whether we have an existing connection. If we do, then close it.
|
|
//
|
|
if (m_RpcBindingHandle)
|
|
{
|
|
CloseConnectionToServer();
|
|
m_RpcBindingHandle = NULL;
|
|
}
|
|
|
|
LPTSTR pszBinding = NULL;
|
|
DWORD dwError = RPC_S_OK;
|
|
|
|
//
|
|
// Compose the binding string for local binding
|
|
//
|
|
dwError = RpcStringBindingCompose(NULL, // ObjUuid
|
|
STI_LRPC_SEQ, // transport seq
|
|
NULL, // NetworkAddr
|
|
STI_LRPC_ENDPOINT, // Endpoint
|
|
NULL, // Options
|
|
&pszBinding); // StringBinding
|
|
if ( dwError == RPC_S_OK )
|
|
{
|
|
|
|
//
|
|
// establish the binding handle using string binding.
|
|
//
|
|
dwError = RpcBindingFromStringBinding(pszBinding,&m_RpcBindingHandle);
|
|
if (dwError == RPC_S_OK)
|
|
{
|
|
//
|
|
// Check that the server we're connecting to has the appropriate credentials.
|
|
// In our case, we don't know exactly which principal name the WIA Service
|
|
// is running under, so we need to look it up.
|
|
// Note that we assume the Services section of the registry is secure, and
|
|
// only Admins can change it.
|
|
// Also note that we default to "NT Authority\LocalService" if we cannot read the key.
|
|
//
|
|
CSimpleReg csrStiSvcKey(HKEY_LOCAL_MACHINE, STISVC_REG_PATH, FALSE, KEY_READ);
|
|
CSimpleStringWide cswStiSvcPrincipalName = csrStiSvcKey.Query(L"ObjectName", L"NT Authority\\LocalService");
|
|
|
|
RPC_SECURITY_QOS RpcSecQos = {0};
|
|
|
|
RpcSecQos.Version = RPC_C_SECURITY_QOS_VERSION_1;
|
|
RpcSecQos.Capabilities = RPC_C_QOS_CAPABILITIES_MUTUAL_AUTH;
|
|
RpcSecQos.IdentityTracking = RPC_C_QOS_IDENTITY_STATIC;
|
|
RpcSecQos.ImpersonationType = RPC_C_IMP_LEVEL_IMPERSONATE;
|
|
|
|
dwError = RpcBindingSetAuthInfoExW(m_RpcBindingHandle,
|
|
(WCHAR*)cswStiSvcPrincipalName.String(),
|
|
RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
|
|
RPC_C_AUTHN_WINNT,
|
|
NULL,
|
|
RPC_C_AUTHZ_NONE,
|
|
&RpcSecQos);
|
|
if (dwError == RPC_S_OK)
|
|
{
|
|
DBG_TRC(("AsyncRPC Connection established to server"));
|
|
|
|
dwError = OpenClientConnection(m_RpcBindingHandle, &m_SyncClientContext, &m_AsyncClientContext);
|
|
if (dwError == RPC_S_OK)
|
|
{
|
|
DBG_TRC(("Got my context %p from server\n", m_AsyncClientContext));
|
|
}
|
|
else
|
|
{
|
|
DBG_ERR(("Runtime event: Received error 0x%08X trying to open connection to server", dwError));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DBG_ERR(("Error 0x%08X trying to set RPC Authentication Info", dwError));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Free the binding string since we no longer need it
|
|
//
|
|
if (pszBinding != NULL)
|
|
{
|
|
DWORD dwErr = RpcStringFree(&pszBinding);
|
|
pszBinding = NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DBG_ERR(("Runtime event Error: Could not create binding string to establish connection to server, error 0x%08X", dwError));
|
|
}
|
|
}
|
|
RpcExcept (1)
|
|
{
|
|
// TBD: Should we catch all exceptions? Probably not...
|
|
DWORD status = RpcExceptionCode();
|
|
hr = HRESULT_FROM_WIN32(status);
|
|
}
|
|
RpcEndExcept
|
|
|
|
return hr;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* @doc INTERNAL
|
|
*
|
|
* @mfunc HRESULT | AsyncRPCEventTransport | CloseConnectionToServer |
|
|
*
|
|
* This method is imlemented by sub-classes to close any resources used to
|
|
* connect to the WIA service in <mf AsyncRPCEventTransport::OpenConnectionToServer>.
|
|
*
|
|
* @rvalue S_OK |
|
|
* The method succeeded.
|
|
* @rvalue E_XXXXXXXX |
|
|
* We received an error closing the connection to the server.
|
|
*****************************************************************************/
|
|
HRESULT AsyncRPCEventTransport::CloseConnectionToServer()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
DWORD dwError = 0;
|
|
if (m_RpcBindingHandle)
|
|
{
|
|
RpcTryExcept
|
|
{
|
|
CloseClientConnection(m_RpcBindingHandle, m_SyncClientContext);
|
|
|
|
dwError = RpcBindingFree(&m_RpcBindingHandle);
|
|
if (dwError == RPC_S_OK)
|
|
{
|
|
DBG_TRC(("Closed Async connection to server"));
|
|
}
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_WIN32(dwError);
|
|
DBG_ERR(("Runtime event Error: Got return code 0x%08X freeing RPC binding handle", dwError));
|
|
}
|
|
}
|
|
RpcExcept (1) {
|
|
// TBD: Should we catch all exceptions? Probably not...
|
|
DWORD status = RpcExceptionCode();
|
|
hr = HRESULT_FROM_WIN32(status);
|
|
}
|
|
RpcEndExcept
|
|
m_RpcBindingHandle = NULL;
|
|
}
|
|
DBG_TRC(("Closed connection to server"));
|
|
return hr;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* @doc INTERNAL
|
|
*
|
|
* @mfunc HRESULT | AsyncRPCEventTransport | OpenNotificationChannel |
|
|
*
|
|
* Sub-classes use this method to set up the mechanism by which the client
|
|
* will receive notifications.
|
|
*
|
|
* @rvalue S_OK |
|
|
* The method succeeded.
|
|
*****************************************************************************/
|
|
HRESULT AsyncRPCEventTransport::OpenNotificationChannel()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
DBG_TRC(("Opened Async notification channel..."));
|
|
|
|
RpcTryExcept
|
|
{
|
|
hr = RpcAsyncInitializeHandle(&m_AsyncState, sizeof(m_AsyncState));
|
|
if (hr == RPC_S_OK)
|
|
{
|
|
m_AsyncState.UserInfo = NULL;
|
|
m_AsyncState.u.hEvent = m_hPendingEvent;
|
|
m_AsyncState.NotificationType = RpcNotificationTypeEvent;
|
|
|
|
//
|
|
// Make the Async RPC call. When this call completes, it typically
|
|
// signifies that we have an event notification. However, it will
|
|
// also complete on error conditions such as whent the server dies.
|
|
// Therefore, it is important to check how it completed.
|
|
//
|
|
WiaGetRuntimetEventDataAsync(&m_AsyncState,
|
|
m_RpcBindingHandle,
|
|
m_AsyncClientContext,
|
|
&m_AsyncEventNotifyData);
|
|
}
|
|
else
|
|
{
|
|
DBG_ERR(("Runtime event Error: Could not initialize the RPC_ASYNC_STATE structure, err = 0x%08X", hr));
|
|
}
|
|
}
|
|
RpcExcept (1) {
|
|
// TBD: Should we catch all exceptions? Probably not...
|
|
DWORD status = RpcExceptionCode();
|
|
hr = HRESULT_FROM_WIN32(status);
|
|
}
|
|
RpcEndExcept
|
|
return hr;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* @doc INTERNAL
|
|
*
|
|
* @mfunc HRESULT | AsyncRPCEventTransport | CloseNotificationChannel |
|
|
*
|
|
* If we have a pending AsyncRPC call, cancel it immediately (i.e. don't wait
|
|
* for server to respond).
|
|
*
|
|
* @rvalue RPC_S_OK |
|
|
* The method succeeded.
|
|
* @rvalue E_XXXXXXX |
|
|
* Failed to cancel the call.
|
|
*****************************************************************************/
|
|
HRESULT AsyncRPCEventTransport::CloseNotificationChannel()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
DBG_TRC(("Closed Async Notification channel"));
|
|
|
|
RpcTryExcept
|
|
{
|
|
if (RpcAsyncGetCallStatus(&m_AsyncState) == RPC_S_ASYNC_CALL_PENDING)
|
|
{
|
|
hr = RpcAsyncCancelCall(&m_AsyncState,
|
|
TRUE); // Return immediately - don't wait for server to respond.
|
|
}
|
|
}
|
|
RpcExcept (1) {
|
|
// TBD: Should we catch all exceptions? Probably not...
|
|
DWORD status = RpcExceptionCode();
|
|
hr = HRESULT_FROM_WIN32(status);
|
|
}
|
|
RpcEndExcept
|
|
|
|
return hr;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* @doc INTERNAL
|
|
*
|
|
* @mfunc HRESULT | AsyncRPCEventTransport | SendRegisterUnregisterInfo |
|
|
*
|
|
* Sends the registration information to the WIA Service via a synchronous RPC
|
|
* call.
|
|
*
|
|
* @parm EventRegistrationInfo* | pEventRegistrationInfo |
|
|
* The address of a caller's event registration information.
|
|
*
|
|
* @rvalue S_OK |
|
|
* The method succeeded.
|
|
* @rvalue E_XXXXXXXX |
|
|
* Could not successfully send the registration info.
|
|
*****************************************************************************/
|
|
HRESULT AsyncRPCEventTransport::SendRegisterUnregisterInfo(
|
|
EventRegistrationInfo *pEventRegistrationInfo)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
RpcTryExcept
|
|
{
|
|
if (pEventRegistrationInfo)
|
|
{
|
|
WIA_ASYNC_EVENT_REG_DATA wiaAsyncEventRegData = {0};
|
|
|
|
wiaAsyncEventRegData.dwFlags = pEventRegistrationInfo->getFlags();
|
|
wiaAsyncEventRegData.guidEvent = pEventRegistrationInfo->getEventGuid();
|
|
wiaAsyncEventRegData.bstrDeviceID = pEventRegistrationInfo->getDeviceID();
|
|
wiaAsyncEventRegData.ulCallback = pEventRegistrationInfo->getCallback();
|
|
hr = RegisterUnregisterForEventNotification(m_RpcBindingHandle,
|
|
m_SyncClientContext,
|
|
&wiaAsyncEventRegData);
|
|
|
|
DBG_TRC(("Sent RPC Register/Unregister information."));
|
|
}
|
|
else
|
|
{
|
|
DBG_ERR(("Runtime event Error: NULL event reg data passed to Transport."));
|
|
hr = E_POINTER;
|
|
}
|
|
}
|
|
RpcExcept (1) {
|
|
// TBD: Should we catch all exceptions? Probably not...
|
|
DWORD status = RpcExceptionCode();
|
|
hr = HRESULT_FROM_WIN32(status);
|
|
}
|
|
RpcEndExcept
|
|
return hr;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* @doc INTERNAL
|
|
*
|
|
* @mfunc HRESULT | AsyncRPCEventTransport | FillEventData |
|
|
*
|
|
* Description goes here
|
|
*
|
|
* @parm WiaEventInfo* | pWiaEventInfo |
|
|
* Address of the caller allocated pWiaEventInfo. The members of this class
|
|
* are filled out with the relevant event info. It is the caller's
|
|
* responsibility to free and memory allocated for the structure members.
|
|
*
|
|
* @rvalue S_OK |
|
|
* The method succeeded.
|
|
*****************************************************************************/
|
|
HRESULT AsyncRPCEventTransport::FillEventData(
|
|
WiaEventInfo *pWiaEventInfo)
|
|
{
|
|
HRESULT hr = RPC_S_ASYNC_CALL_PENDING;
|
|
|
|
if (pWiaEventInfo)
|
|
{
|
|
RpcTryExcept
|
|
{
|
|
DWORD dwRet = 0;
|
|
//
|
|
// First check that the call is not still pending. If it isn't, we
|
|
// complete the call and check whether is was successful or not.
|
|
// Only if it was successful do we fill out the event data.
|
|
//
|
|
if (RpcAsyncGetCallStatus(&m_AsyncState) != RPC_S_ASYNC_CALL_PENDING)
|
|
{
|
|
hr = RpcAsyncCompleteCall(&m_AsyncState, &dwRet);
|
|
if (hr == RPC_S_OK)
|
|
{
|
|
//
|
|
// We successfully received an event from the server.
|
|
// Fill out the event data for the caller.
|
|
//
|
|
pWiaEventInfo->setEventGuid(m_AsyncEventNotifyData.EventGuid);
|
|
pWiaEventInfo->setEventDescription(m_AsyncEventNotifyData.bstrEventDescription);
|
|
pWiaEventInfo->setDeviceID(m_AsyncEventNotifyData.bstrDeviceID);
|
|
pWiaEventInfo->setDeviceDescription(m_AsyncEventNotifyData.bstrDeviceDescription);
|
|
pWiaEventInfo->setDeviceType(m_AsyncEventNotifyData.dwDeviceType);
|
|
pWiaEventInfo->setFullItemName(m_AsyncEventNotifyData.bstrFullItemName);
|
|
pWiaEventInfo->setEventType(m_AsyncEventNotifyData.ulEventType);
|
|
|
|
//
|
|
// Free any data allocated for the m_AsyncEventNotifyData structure.
|
|
//
|
|
FreeAsyncEventNotifyData();
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Clear the m_AsyncEventNotifyData since the info contained in their
|
|
// is undefined when the server throws an error.
|
|
//
|
|
memset(&m_AsyncEventNotifyData, 0, sizeof(m_AsyncEventNotifyData));
|
|
DBG_ERR(("Runtime event Error: The server returned an error 0x%08X completing the call", hr));
|
|
}
|
|
|
|
//
|
|
// Our AsyncRPC call is a one-shot deal: once the call is completed, we have to make
|
|
// another call to receive the next notification. So we simply call
|
|
// OpenNotificationChannel again.
|
|
//
|
|
if (hr == RPC_S_OK)
|
|
{
|
|
dwRet = OpenNotificationChannel();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DBG_ERR(("Runtime event Error: The async call is still pending."));
|
|
hr = RPC_S_ASYNC_CALL_PENDING;
|
|
}
|
|
}
|
|
RpcExcept (1) {
|
|
// TBD: Should we catch all exceptions? Probably not...
|
|
DWORD status = RpcExceptionCode();
|
|
hr = HRESULT_FROM_WIN32(status);
|
|
}
|
|
RpcEndExcept
|
|
}
|
|
else
|
|
{
|
|
DBG_ERR(("Runtime event Error: FillEventData cannot fill in a NULL argument."));
|
|
hr = E_INVALIDARG;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* @doc INTERNAL
|
|
*
|
|
* @mfunc VOID | AsyncRPCEventTransport | FreeAsyncEventNotifyData |
|
|
*
|
|
* Releases any memory that was allocated for the members contained in
|
|
* <md AsyncRPCEventTransport::m_AsyncEventNotifyData> and zeros out
|
|
* the members.
|
|
*
|
|
* This method is not thread safe: the caller of this class is expected to
|
|
* synchronize access to it.
|
|
*
|
|
*****************************************************************************/
|
|
VOID AsyncRPCEventTransport::FreeAsyncEventNotifyData()
|
|
{
|
|
if (m_AsyncEventNotifyData.bstrEventDescription)
|
|
{
|
|
SysFreeString(m_AsyncEventNotifyData.bstrEventDescription);
|
|
m_AsyncEventNotifyData.bstrEventDescription = NULL;
|
|
}
|
|
if (m_AsyncEventNotifyData.bstrDeviceID)
|
|
{
|
|
SysFreeString(m_AsyncEventNotifyData.bstrDeviceID);
|
|
m_AsyncEventNotifyData.bstrDeviceID = NULL;
|
|
}
|
|
if (m_AsyncEventNotifyData.bstrDeviceDescription)
|
|
{
|
|
SysFreeString(m_AsyncEventNotifyData.bstrDeviceDescription);
|
|
m_AsyncEventNotifyData.bstrDeviceDescription = NULL;
|
|
}
|
|
if (m_AsyncEventNotifyData.bstrFullItemName)
|
|
{
|
|
SysFreeString(m_AsyncEventNotifyData.bstrFullItemName);
|
|
m_AsyncEventNotifyData.bstrFullItemName = NULL;
|
|
}
|
|
|
|
memset(&m_AsyncEventNotifyData, 0, sizeof(m_AsyncEventNotifyData));
|
|
}
|
|
|