Leaked source code of windows server 2003
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

/*****************************************************************************
* (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));
}