/***************************************************************************** * (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 * 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: * 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: * - Call . * - Call . * - Call . * * Also: * 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 * . * * If is not NULL, this method * will call to free it, * and then attempt to establish a new connection. * * If successful, callers should clean-up by calling * , 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 . * * @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 * 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)); }