1686 lines
46 KiB
C++
1686 lines
46 KiB
C++
|
//--------------------------------------------------------------
|
||
|
//
|
||
|
// File: async.cxx
|
||
|
//
|
||
|
// Contents: Test proxy and stub for async
|
||
|
//
|
||
|
//---------------------------------------------------------------
|
||
|
|
||
|
#include <windows.h>
|
||
|
#include <stdio.h>
|
||
|
#include <sys/types.h>
|
||
|
#include <sys/stat.h>
|
||
|
#include <errno.h>
|
||
|
#include <io.h>
|
||
|
#include <malloc.h>
|
||
|
|
||
|
#include <ole2.h>
|
||
|
#include <coguid.h>
|
||
|
#include "async.h"
|
||
|
|
||
|
typedef struct SAsyncData
|
||
|
{
|
||
|
BOOL bLate;
|
||
|
BOOL bSleep;
|
||
|
BOOL bFail;
|
||
|
};
|
||
|
|
||
|
class CProxy;
|
||
|
class CProxyComplete;
|
||
|
class CStubComplete;
|
||
|
|
||
|
class CPCInnerUnknown : public IUnknown
|
||
|
{
|
||
|
public:
|
||
|
// IUnknown methods
|
||
|
STDMETHOD (QueryInterface) ( REFIID riid, LPVOID FAR* ppvObj);
|
||
|
STDMETHOD_(ULONG,AddRef) ( void );
|
||
|
STDMETHOD_(ULONG,Release) ( void );
|
||
|
|
||
|
// Constructor
|
||
|
CPCInnerUnknown( CProxyComplete *pParent );
|
||
|
|
||
|
DWORD _iRef;
|
||
|
CProxyComplete *_pParent;
|
||
|
};
|
||
|
|
||
|
class CProxyComplete : public IAsyncManager, public ICancelMethodCalls
|
||
|
{
|
||
|
public:
|
||
|
STDMETHOD (QueryInterface) ( REFIID riid, LPVOID FAR* ppvObj);
|
||
|
STDMETHOD_(ULONG,AddRef) ( void );
|
||
|
STDMETHOD_(ULONG,Release) ( void );
|
||
|
STDMETHOD (Cancel) ( void );
|
||
|
STDMETHOD (TestCancel) ( void );
|
||
|
STDMETHOD (CompleteCall) ( HRESULT result );
|
||
|
STDMETHOD (GetCallContext) ( REFIID riid, void **pInterface );
|
||
|
STDMETHOD (GetState) ( DWORD *pState );
|
||
|
STDMETHOD (SetCancelTimeout) ( ULONG lSec ) { return E_NOTIMPL; }
|
||
|
CProxyComplete( IUnknown *pControl, BOOL fAutoComplete, HRESULT *hr );
|
||
|
~CProxyComplete();
|
||
|
|
||
|
CPCInnerUnknown _Inner;
|
||
|
IUnknown *_pSyncInner;
|
||
|
IUnknown *_pControl;
|
||
|
RPCOLEMESSAGE _Message;
|
||
|
BOOL _fAutoComplete;
|
||
|
IRpcChannelBuffer3 *_pChannel;
|
||
|
};
|
||
|
|
||
|
class CSCInnerUnknown : public IUnknown
|
||
|
{
|
||
|
public:
|
||
|
// IUnknown methods
|
||
|
STDMETHOD (QueryInterface) ( REFIID riid, LPVOID FAR* ppvObj);
|
||
|
STDMETHOD_(ULONG,AddRef) ( void );
|
||
|
STDMETHOD_(ULONG,Release) ( void );
|
||
|
|
||
|
// Constructor
|
||
|
CSCInnerUnknown( CStubComplete *pParent );
|
||
|
|
||
|
DWORD _iRef;
|
||
|
CStubComplete *_pParent;
|
||
|
};
|
||
|
|
||
|
class CStubComplete : public IAsyncManager, public IAsyncSetup,
|
||
|
public ICancelMethodCalls
|
||
|
{
|
||
|
public:
|
||
|
STDMETHOD (QueryInterface) ( REFIID riid, LPVOID FAR* ppvObj);
|
||
|
STDMETHOD_(ULONG,AddRef) ( void );
|
||
|
STDMETHOD_(ULONG,Release) ( void );
|
||
|
STDMETHOD (Cancel) ( void );
|
||
|
STDMETHOD (CompleteCall) ( HRESULT result );
|
||
|
STDMETHOD (GetCallContext) ( REFIID riid, void **pInterface );
|
||
|
STDMETHOD (GetState) ( DWORD *pState );
|
||
|
STDMETHOD (SetCancelTimeout) ( ULONG lSec ) { return E_NOTIMPL; }
|
||
|
STDMETHOD (TestCancel) ( void );
|
||
|
STDMETHOD (GetAsyncManager) ( REFIID riid,
|
||
|
IUnknown *pOuter,
|
||
|
DWORD dwFlags,
|
||
|
IUnknown **pInner,
|
||
|
IAsyncManager **pComplete );
|
||
|
CStubComplete( IUnknown *pControl, IRpcChannelBuffer3 *, RPCOLEMESSAGE *,
|
||
|
HRESULT *hr );
|
||
|
~CStubComplete();
|
||
|
|
||
|
CSCInnerUnknown _Inner;
|
||
|
IUnknown *_pSyncInner;
|
||
|
IUnknown *_pControl;
|
||
|
RPCOLEMESSAGE _Message;
|
||
|
IRpcChannelBuffer3 *_pChannel;
|
||
|
};
|
||
|
|
||
|
class CAsync : public IAsync
|
||
|
{
|
||
|
public:
|
||
|
STDMETHOD (QueryInterface) ( REFIID riid, LPVOID FAR* ppvObj);
|
||
|
STDMETHOD_(ULONG,AddRef) ( void );
|
||
|
STDMETHOD_(ULONG,Release) ( void );
|
||
|
STDMETHOD (Async) ( IAsyncManager **pCall, BOOL, BOOL, BOOL );
|
||
|
STDMETHOD (RecurseAsync) ( IAsyncManager **pCall, IAsync *, DWORD );
|
||
|
CAsync( IUnknown *pControl, CProxy *pProxy );
|
||
|
~CAsync();
|
||
|
|
||
|
private:
|
||
|
IUnknown *_pControl;
|
||
|
CProxy *_pProxy;
|
||
|
};
|
||
|
|
||
|
class CProxy: public IRpcProxyBuffer, IAsyncSetup
|
||
|
{
|
||
|
public:
|
||
|
STDMETHOD (QueryInterface) ( REFIID riid, LPVOID FAR* ppvObj);
|
||
|
STDMETHOD_(ULONG,AddRef) ( void );
|
||
|
STDMETHOD_(ULONG,Release) ( void );
|
||
|
STDMETHOD (Connect) ( IRpcChannelBuffer *pRpcChannelBuffer );
|
||
|
STDMETHOD_(void,Disconnect)( void );
|
||
|
STDMETHOD (GetAsyncManager)( REFIID riid,
|
||
|
IUnknown *pOuter,
|
||
|
DWORD dwFlags,
|
||
|
IUnknown **pInner,
|
||
|
IAsyncManager **pComplete );
|
||
|
CProxy( IUnknown *pControl );
|
||
|
|
||
|
CAsync _Async;
|
||
|
DWORD _iRef;
|
||
|
IRpcChannelBuffer3 *_pChannel;
|
||
|
};
|
||
|
|
||
|
class CStub : public IRpcStubBuffer
|
||
|
{
|
||
|
public:
|
||
|
STDMETHOD (QueryInterface) ( REFIID riid, LPVOID FAR* ppvObj);
|
||
|
STDMETHOD_(ULONG,AddRef) ( void );
|
||
|
STDMETHOD_(ULONG,Release) ( void );
|
||
|
STDMETHOD (Connect) ( IUnknown *pServer );
|
||
|
STDMETHOD_(void, Disconnect) ( void );
|
||
|
STDMETHOD (Invoke) ( RPCOLEMESSAGE *pMessage,
|
||
|
IRpcChannelBuffer *pChannel );
|
||
|
STDMETHOD_(IRpcStubBuffer *,IsIIDSupported)( REFIID riid );
|
||
|
STDMETHOD_(ULONG,CountRefs) ( void );
|
||
|
STDMETHOD (DebugServerQueryInterface) ( void **ppv );
|
||
|
STDMETHOD_(void,DebugServerRelease) ( void *ppv );
|
||
|
CStub( IAsync *pServer );
|
||
|
|
||
|
private:
|
||
|
DWORD _iRef;
|
||
|
IAsync *_pAsync;
|
||
|
};
|
||
|
|
||
|
class CPSFactory : public IPSFactoryBuffer
|
||
|
{
|
||
|
public:
|
||
|
STDMETHOD (QueryInterface) ( REFIID riid, LPVOID FAR* ppvObj);
|
||
|
STDMETHOD_(ULONG,AddRef) ( void );
|
||
|
STDMETHOD_(ULONG,Release) ( void );
|
||
|
STDMETHOD (CreateProxy) ( IUnknown *pUnkOuter,
|
||
|
REFIID riid,
|
||
|
IRpcProxyBuffer **ppProxy,
|
||
|
void **ppv );
|
||
|
STDMETHOD (CreateStub) ( REFIID riid,
|
||
|
IUnknown *pUnkServer,
|
||
|
IRpcStubBuffer **ppStub );
|
||
|
CPSFactory();
|
||
|
|
||
|
private:
|
||
|
DWORD _iRef;
|
||
|
};
|
||
|
|
||
|
const IID IID_IAsync = {0x70000000,0x76d7,0x11Cf,{0x9a,0xf1,0x00,0x20,0xaf,0x6e,0x72,0xf4}};
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: DllCanUnloadNow
|
||
|
//
|
||
|
// Synopsis: Dll entry point
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
STDAPI DllCanUnloadNow()
|
||
|
{
|
||
|
return S_FALSE;
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: DllGetClassObject
|
||
|
//
|
||
|
// Synopsis: Dll entry point
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
STDAPI DllGetClassObject(REFCLSID clsid, REFIID iid, void **ppv)
|
||
|
{
|
||
|
CPSFactory *pFactory;
|
||
|
|
||
|
// Check for IPSFactoryBuffer
|
||
|
if (clsid == IID_IAsync && iid == IID_IPSFactoryBuffer)
|
||
|
{
|
||
|
pFactory = new CPSFactory();
|
||
|
*ppv = pFactory;
|
||
|
if (pFactory == NULL)
|
||
|
return E_OUTOFMEMORY;
|
||
|
else
|
||
|
return S_OK;
|
||
|
}
|
||
|
return E_NOTIMPL;
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CPSFactory::AddRef, public
|
||
|
//
|
||
|
// Synopsis: Adds a reference to an interface
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
STDMETHODIMP_(ULONG) CPSFactory::AddRef()
|
||
|
{
|
||
|
InterlockedIncrement( (long *) &_iRef );
|
||
|
return _iRef;
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CPSFactory::CPSFactory
|
||
|
//
|
||
|
// Synopsis: Constructor
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
CPSFactory::CPSFactory()
|
||
|
{
|
||
|
_iRef = 1;
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CPSFactory::CreateProxy, public
|
||
|
//
|
||
|
// Synopsis: Create an instance of the requested proxy
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
STDMETHODIMP CPSFactory::CreateProxy( IUnknown *pUnkOuter, REFIID riid,
|
||
|
IRpcProxyBuffer **ppProxy, void **ppv )
|
||
|
{
|
||
|
CProxy *pProxy;
|
||
|
|
||
|
// Validate the parameters.
|
||
|
if (ppv == NULL || riid != IID_IAsync || pUnkOuter == NULL)
|
||
|
return E_INVALIDARG;
|
||
|
*ppv = NULL;
|
||
|
|
||
|
// Create a proxy.
|
||
|
pProxy = new CProxy( pUnkOuter );
|
||
|
if (pProxy == NULL)
|
||
|
return E_OUTOFMEMORY;
|
||
|
*ppProxy = pProxy;
|
||
|
*ppv = &pProxy->_Async;
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CPSFactory::CreateStub, public
|
||
|
//
|
||
|
// Synopsis: Create an instance of the requested stub
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
STDMETHODIMP CPSFactory::CreateStub( REFIID riid, IUnknown *pUnkServer,
|
||
|
IRpcStubBuffer **ppStub )
|
||
|
{
|
||
|
IAsync *pAsync;
|
||
|
CStub *pStub;
|
||
|
HRESULT hr;
|
||
|
|
||
|
// Validate the parameters.
|
||
|
if (riid != IID_IAsync)
|
||
|
return E_INVALIDARG;
|
||
|
|
||
|
// Get the IAsync interface.
|
||
|
hr = pUnkServer->QueryInterface( IID_IAsync, (void **) &pAsync );
|
||
|
if (FAILED(hr))
|
||
|
return hr;
|
||
|
|
||
|
// Create a stub.
|
||
|
pStub = new CStub( pAsync );
|
||
|
pAsync->Release();
|
||
|
if (pStub == NULL)
|
||
|
return E_OUTOFMEMORY;
|
||
|
*ppStub = pStub;
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CPSFactory::QueryInterface, public
|
||
|
//
|
||
|
// Synopsis: Returns a pointer to the requested interface.
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
STDMETHODIMP CPSFactory::QueryInterface( REFIID riid, LPVOID FAR* ppvObj)
|
||
|
{
|
||
|
if (IsEqualIID(riid, IID_IUnknown) ||
|
||
|
IsEqualIID(riid, IID_IPSFactoryBuffer))
|
||
|
*ppvObj = (IPSFactoryBuffer *) this;
|
||
|
else
|
||
|
{
|
||
|
*ppvObj = NULL;
|
||
|
return E_NOINTERFACE;
|
||
|
}
|
||
|
|
||
|
AddRef();
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CPSFactory::Release, public
|
||
|
//
|
||
|
// Synopsis: Releases an interface
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
STDMETHODIMP_(ULONG) CPSFactory::Release()
|
||
|
{
|
||
|
ULONG lRef = _iRef - 1;
|
||
|
|
||
|
if (InterlockedDecrement( (long*) &_iRef ) == 0)
|
||
|
{
|
||
|
delete this;
|
||
|
return 0;
|
||
|
}
|
||
|
else
|
||
|
return lRef;
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CProxy::AddRef, public
|
||
|
//
|
||
|
// Synopsis: Adds a reference to an interface
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
STDMETHODIMP_(ULONG) CProxy::AddRef()
|
||
|
{
|
||
|
InterlockedIncrement( (long *) &_iRef );
|
||
|
return _iRef;
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CProxy::Connect, public
|
||
|
//
|
||
|
// Synopsis: Connects the proxy to a channel
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
STDMETHODIMP CProxy::Connect( IRpcChannelBuffer *pChannel )
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
|
||
|
// Get the other channel buffer interface.
|
||
|
hr = pChannel->QueryInterface( IID_IRpcChannelBuffer3, (void **) &_pChannel );
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CProxy::Disconnect, public
|
||
|
//
|
||
|
// Synopsis: Disconnects the proxy from a channel
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
STDMETHODIMP_(void) CProxy::Disconnect()
|
||
|
{
|
||
|
if (_pChannel != NULL)
|
||
|
{
|
||
|
_pChannel->Release();
|
||
|
_pChannel = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CProxy::CProxy, public
|
||
|
//
|
||
|
// Synopsis: Constructor
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
CProxy::CProxy( IUnknown *pControl ) :
|
||
|
_Async( pControl, this )
|
||
|
{
|
||
|
_iRef = 1;
|
||
|
_pChannel = NULL;
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CProxy::GetAsyncManager, public
|
||
|
//
|
||
|
// Synopsis: Creates a proxy completion object
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
STDMETHODIMP CProxy::GetAsyncManager( REFIID riid,
|
||
|
IUnknown *pOuter,
|
||
|
DWORD dwFlags,
|
||
|
IUnknown **ppInner,
|
||
|
IAsyncManager **ppComplete )
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
CProxyComplete *pComplete;
|
||
|
|
||
|
// Create a new proxy completion object.
|
||
|
pComplete = new CProxyComplete( pOuter, FALSE, &hr );
|
||
|
if (pComplete == NULL)
|
||
|
return E_OUTOFMEMORY;
|
||
|
else if (FAILED(hr))
|
||
|
{
|
||
|
delete pComplete;
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
// Get IAsyncManager.
|
||
|
*ppComplete = (IAsyncManager *) pComplete;
|
||
|
*ppInner = (IUnknown *) &pComplete->_Inner;
|
||
|
pComplete->AddRef();
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CProxy::QueryInterface, public
|
||
|
//
|
||
|
// Synopsis: Returns a pointer to the requested interface.
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
STDMETHODIMP CProxy::QueryInterface( REFIID riid, LPVOID FAR* ppvObj)
|
||
|
{
|
||
|
if (IsEqualIID(riid, IID_IUnknown) ||
|
||
|
IsEqualIID(riid, IID_IRpcProxyBuffer))
|
||
|
*ppvObj = (IRpcProxyBuffer *) this;
|
||
|
else if (IsEqualIID(riid, IID_IAsyncSetup))
|
||
|
*ppvObj = (IAsyncSetup *) this;
|
||
|
else
|
||
|
{
|
||
|
*ppvObj = NULL;
|
||
|
return E_NOINTERFACE;
|
||
|
}
|
||
|
|
||
|
AddRef();
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CProxy::Release, public
|
||
|
//
|
||
|
// Synopsis: Releases an interface
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
STDMETHODIMP_(ULONG) CProxy::Release()
|
||
|
{
|
||
|
ULONG lRef = _iRef - 1;
|
||
|
|
||
|
if (InterlockedDecrement( (long*) &_iRef ) == 0)
|
||
|
{
|
||
|
delete this;
|
||
|
return 0;
|
||
|
}
|
||
|
else
|
||
|
return lRef;
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CAsync::AddRef, public
|
||
|
//
|
||
|
// Synopsis: Adds a reference to an interface
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
STDMETHODIMP_(ULONG) CAsync::AddRef()
|
||
|
{
|
||
|
return _pControl->AddRef();
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CAsync::Async, public
|
||
|
//
|
||
|
// Synopsis: Marshal parameters for an async call.
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
STDMETHODIMP CAsync::Async( IAsyncManager **ppCall, BOOL bLate,
|
||
|
BOOL bSleep, BOOL bFail )
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
CProxyComplete *pComplete;
|
||
|
BOOL fFreeComplete = FALSE;
|
||
|
DWORD lIgnore;
|
||
|
SAsyncData *pData;
|
||
|
DWORD fFlags = RPC_BUFFER_ASYNC;
|
||
|
|
||
|
// If there is no async complete, create one. Aggregate in the
|
||
|
// correct ISignal.
|
||
|
if (ppCall == NULL || *ppCall == NULL)
|
||
|
{
|
||
|
// Initialize the out parameters.
|
||
|
if (ppCall != NULL)
|
||
|
*ppCall = NULL;
|
||
|
else
|
||
|
fFlags |= RPCFLG_AUTO_COMPLETE;
|
||
|
|
||
|
// Create a new proxy complete object.
|
||
|
pComplete = new CProxyComplete( NULL, ppCall == NULL, &hr );
|
||
|
if (pComplete == NULL)
|
||
|
return E_OUTOFMEMORY;
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
delete pComplete;
|
||
|
return hr;
|
||
|
}
|
||
|
fFreeComplete = TRUE;
|
||
|
if (ppCall != NULL)
|
||
|
*ppCall = pComplete;
|
||
|
}
|
||
|
|
||
|
// Otherwise the passed in completion object should be one of ours.
|
||
|
else
|
||
|
{
|
||
|
pComplete = (CProxyComplete *) *ppCall;
|
||
|
if (pComplete->_pChannel != NULL)
|
||
|
return E_FAIL;
|
||
|
}
|
||
|
|
||
|
// Get a buffer.
|
||
|
memset( &pComplete->_Message, 0, sizeof(pComplete->_Message) );
|
||
|
pComplete->_Message.cbBuffer = sizeof( SAsyncData );
|
||
|
pComplete->_Message.dataRepresentation = 0x10;
|
||
|
pComplete->_Message.iMethod = 3;
|
||
|
pComplete->_Message.rpcFlags = fFlags;
|
||
|
hr = _pProxy->_pChannel->GetBuffer( &pComplete->_Message, IID_IAsync );
|
||
|
if (FAILED(hr)) goto Done;
|
||
|
|
||
|
// Register async.
|
||
|
hr = _pProxy->_pChannel->RegisterAsync( &pComplete->_Message,
|
||
|
(IAsyncManager *) pComplete );
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
// Register async shouldn't fail.
|
||
|
DebugBreak();
|
||
|
}
|
||
|
|
||
|
// Fill in the buffer.
|
||
|
pData = (SAsyncData *) pComplete->_Message.Buffer;
|
||
|
pData->bLate = bLate;
|
||
|
pData->bSleep = bSleep;
|
||
|
pData->bFail = bFail;
|
||
|
|
||
|
// Send the buffer.
|
||
|
pComplete->_pChannel = _pProxy->_pChannel;
|
||
|
_pProxy->_pChannel->AddRef();
|
||
|
hr = _pProxy->_pChannel->Send( &pComplete->_Message, &lIgnore );
|
||
|
|
||
|
Done:
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
if (pComplete->_pChannel != NULL)
|
||
|
{
|
||
|
pComplete->_pChannel = NULL;
|
||
|
_pProxy->_pChannel->Release();
|
||
|
}
|
||
|
if (fFreeComplete)
|
||
|
{
|
||
|
delete pComplete;
|
||
|
if (ppCall != NULL)
|
||
|
*ppCall = NULL;
|
||
|
}
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CAsync::CAsync, public
|
||
|
//
|
||
|
// Synopsis: Constructor
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
CAsync::CAsync( IUnknown *pControl, CProxy *pProxy )
|
||
|
{
|
||
|
_pControl = pControl;
|
||
|
_pProxy = pProxy;
|
||
|
_pControl->AddRef();
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CAsync::~CAsync, public
|
||
|
//
|
||
|
// Synopsis: Destructor
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
CAsync::~CAsync()
|
||
|
{
|
||
|
_pControl->Release();
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CAsync::QueryInterface, public
|
||
|
//
|
||
|
// Synopsis: Returns a pointer to the requested interface.
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
STDMETHODIMP CAsync::QueryInterface( REFIID riid, LPVOID FAR* ppvObj)
|
||
|
{
|
||
|
return _pControl->QueryInterface( riid, ppvObj );
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CAsync::RecurseAsync, public
|
||
|
//
|
||
|
// Synopsis: Marshal parameters for a RecurseAsync call.
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
STDMETHODIMP CAsync::RecurseAsync( IAsyncManager **ppCall, IAsync *pCallback,
|
||
|
DWORD depth )
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
CProxyComplete *pComplete;
|
||
|
BOOL fFreeComplete = FALSE;
|
||
|
DWORD lIgnore;
|
||
|
HANDLE memory = NULL;
|
||
|
ULONG size;
|
||
|
IStream *pStream = NULL;
|
||
|
LARGE_INTEGER pos;
|
||
|
DWORD *pBuffer;
|
||
|
DWORD dwDestCtx;
|
||
|
VOID *pvDestCtx = NULL;
|
||
|
BOOL fFlags = RPC_BUFFER_ASYNC;
|
||
|
|
||
|
// If there is no async complete, create one. Aggregate in the
|
||
|
// correct ISignal.
|
||
|
if (ppCall == NULL || *ppCall == NULL)
|
||
|
{
|
||
|
// Initialize the out parameters.
|
||
|
if (ppCall != NULL)
|
||
|
*ppCall = NULL;
|
||
|
else
|
||
|
fFlags |= RPCFLG_AUTO_COMPLETE;
|
||
|
|
||
|
// Create a new proxy complete object.
|
||
|
pComplete = new CProxyComplete( NULL, ppCall == NULL, &hr );
|
||
|
if (pComplete == NULL)
|
||
|
return E_OUTOFMEMORY;
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
delete pComplete;
|
||
|
return hr;
|
||
|
}
|
||
|
fFreeComplete = TRUE;
|
||
|
if (ppCall != NULL)
|
||
|
*ppCall = pComplete;
|
||
|
}
|
||
|
|
||
|
// Otherwise the passed in completion object should be one of ours.
|
||
|
else
|
||
|
{
|
||
|
pComplete = (CProxyComplete *) *ppCall;
|
||
|
if (pComplete->_pChannel != NULL)
|
||
|
return E_FAIL;
|
||
|
}
|
||
|
|
||
|
// Get the destination context.
|
||
|
hr = _pProxy->_pChannel->GetDestCtx( &dwDestCtx, &pvDestCtx );
|
||
|
|
||
|
// Find out how much memory to allocate.
|
||
|
hr = CoGetMarshalSizeMax( &size, IID_IAsync, pCallback, dwDestCtx,
|
||
|
pvDestCtx, MSHLFLAGS_NORMAL );
|
||
|
if (FAILED(hr))
|
||
|
goto Done;
|
||
|
|
||
|
// Allocate memory.
|
||
|
memory = GlobalAlloc( GMEM_FIXED, size );
|
||
|
if (memory == NULL)
|
||
|
{
|
||
|
hr = E_OUTOFMEMORY;
|
||
|
goto Done;
|
||
|
}
|
||
|
|
||
|
// Create a stream.
|
||
|
hr = CreateStreamOnHGlobal( memory, TRUE, &pStream );
|
||
|
if (FAILED(hr))
|
||
|
goto Done;
|
||
|
memory = NULL;
|
||
|
|
||
|
// Marshal the object.
|
||
|
hr = CoMarshalInterface( pStream, IID_IAsync, pCallback, dwDestCtx,
|
||
|
pvDestCtx, MSHLFLAGS_NORMAL );
|
||
|
if (FAILED(hr))
|
||
|
goto Done;
|
||
|
|
||
|
// Seek back to the start of the stream.
|
||
|
pos.QuadPart = 0;
|
||
|
hr = pStream->Seek( pos, STREAM_SEEK_SET, NULL );
|
||
|
if (FAILED(hr))
|
||
|
goto Done;
|
||
|
|
||
|
// Get a buffer.
|
||
|
memset( &pComplete->_Message, 0, sizeof(pComplete->_Message) );
|
||
|
pComplete->_Message.cbBuffer = sizeof(depth) + size;
|
||
|
pComplete->_Message.dataRepresentation = 0x10;
|
||
|
pComplete->_Message.iMethod = 4;
|
||
|
pComplete->_Message.rpcFlags = fFlags;
|
||
|
hr = _pProxy->_pChannel->GetBuffer( &pComplete->_Message, IID_IAsync );
|
||
|
if (FAILED(hr)) goto Done;
|
||
|
|
||
|
// Register async.
|
||
|
hr = _pProxy->_pChannel->RegisterAsync( &pComplete->_Message,
|
||
|
(IAsyncManager *) pComplete );
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
// Register async shouldn't fail.
|
||
|
DebugBreak();
|
||
|
}
|
||
|
|
||
|
// Fill in the buffer.
|
||
|
pBuffer = (DWORD *) pComplete->_Message.Buffer;
|
||
|
*pBuffer = depth;
|
||
|
pBuffer += 1;
|
||
|
hr = pStream->Read( (void *) pBuffer, size, NULL );
|
||
|
if (FAILED(hr))
|
||
|
goto Done;
|
||
|
|
||
|
// Send the buffer.
|
||
|
pComplete->_pChannel = _pProxy->_pChannel;
|
||
|
_pProxy->_pChannel->AddRef();
|
||
|
hr = _pProxy->_pChannel->Send( &pComplete->_Message, &lIgnore );
|
||
|
|
||
|
Done:
|
||
|
if (pStream != NULL)
|
||
|
pStream->Release();
|
||
|
if (memory != NULL)
|
||
|
GlobalFree( memory );
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
if (pComplete->_pChannel != NULL)
|
||
|
{
|
||
|
pComplete->_pChannel = NULL;
|
||
|
_pProxy->_pChannel->Release();
|
||
|
}
|
||
|
if (fFreeComplete)
|
||
|
{
|
||
|
delete pComplete;
|
||
|
if (ppCall != NULL)
|
||
|
*ppCall = NULL;
|
||
|
}
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CAsync::Release, public
|
||
|
//
|
||
|
// Synopsis: Releases an interface
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
STDMETHODIMP_(ULONG) CAsync::Release()
|
||
|
{
|
||
|
return _pControl->Release();
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CPCInnerUnknown::AddRef, public
|
||
|
//
|
||
|
// Synopsis: Adds a reference to an interface
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
STDMETHODIMP_(ULONG) CPCInnerUnknown::AddRef()
|
||
|
{
|
||
|
InterlockedIncrement( (long *) &_iRef );
|
||
|
return _iRef;
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CPCInnerUnknown::CPCInnerUnknown
|
||
|
//
|
||
|
// Synopsis: Constructor
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
CPCInnerUnknown::CPCInnerUnknown( CProxyComplete *pParent )
|
||
|
{
|
||
|
_iRef = 1;
|
||
|
_pParent = pParent;
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CPCInnerUnknown::QueryInterface, public
|
||
|
//
|
||
|
// Synopsis: Returns a pointer to the requested interface.
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
STDMETHODIMP CPCInnerUnknown::QueryInterface( REFIID riid, LPVOID FAR* ppvObj)
|
||
|
{
|
||
|
IUnknown *pUnk;
|
||
|
|
||
|
if (IsEqualIID(riid, IID_IUnknown))
|
||
|
pUnk = (IUnknown *) this;
|
||
|
else if (IsEqualIID(riid, IID_IAsyncManager))
|
||
|
pUnk = (IAsyncManager *) _pParent;
|
||
|
else if (IsEqualIID(riid, IID_ICancelMethodCalls))
|
||
|
pUnk = (ICancelMethodCalls *) _pParent;
|
||
|
else if (_pParent->_pSyncInner != NULL)
|
||
|
return _pParent->_pSyncInner->QueryInterface( riid, ppvObj );
|
||
|
|
||
|
pUnk->AddRef();
|
||
|
*ppvObj = pUnk;
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CPCInnerUnknown::Release, public
|
||
|
//
|
||
|
// Synopsis: Releases an interface
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
STDMETHODIMP_(ULONG) CPCInnerUnknown::Release()
|
||
|
{
|
||
|
ULONG lRef = _iRef - 1;
|
||
|
|
||
|
if (InterlockedDecrement( (long*) &_iRef ) == 0)
|
||
|
{
|
||
|
delete _pParent;
|
||
|
return 0;
|
||
|
}
|
||
|
else
|
||
|
return lRef;
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CProxyComplete::AddRef, public
|
||
|
//
|
||
|
// Synopsis: Adds a reference to an interface
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
STDMETHODIMP_(ULONG) CProxyComplete::AddRef()
|
||
|
{
|
||
|
return _pControl->AddRef();
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CProxyComplete::Cancel, public
|
||
|
//
|
||
|
// Synopsis: Forward cancel to the channel
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
STDMETHODIMP CProxyComplete::Cancel()
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
|
||
|
if (_pChannel == NULL)
|
||
|
return RPC_E_CALL_COMPLETE;
|
||
|
hr = _pChannel->Cancel( &_Message );
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CProxyComplete::CompleteCall, public
|
||
|
//
|
||
|
// Synopsis: Receive the reply and parse the out parameters
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
STDMETHODIMP CProxyComplete::CompleteCall( HRESULT result )
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
HRESULT temp;
|
||
|
DWORD lIgnore;
|
||
|
|
||
|
// Fail if there is no call.
|
||
|
if (_pChannel == NULL)
|
||
|
return RPC_E_CALL_COMPLETE;
|
||
|
|
||
|
// Call receive.
|
||
|
hr = _pChannel->Receive( &_Message, 0, &lIgnore );
|
||
|
if (hr == RPC_S_CALLPENDING || hr == 0x15)
|
||
|
return hr;
|
||
|
if (FAILED(hr))
|
||
|
goto Done;
|
||
|
|
||
|
// Unmarshal the results.
|
||
|
if (_Message.cbBuffer < sizeof(HRESULT))
|
||
|
{
|
||
|
hr = RPC_E_INVALID_DATAPACKET;
|
||
|
goto Done;
|
||
|
}
|
||
|
temp = *((HRESULT *) _Message.Buffer);
|
||
|
|
||
|
// Free the buffer.
|
||
|
hr = _pChannel->FreeBuffer( &_Message );
|
||
|
if (SUCCEEDED(hr))
|
||
|
hr = temp;
|
||
|
|
||
|
// If auto complete, self release.
|
||
|
Done:
|
||
|
_pChannel->Release();
|
||
|
_pChannel = NULL;
|
||
|
if (_fAutoComplete)
|
||
|
_Inner.Release();
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CProxyComplete::CProxyComplete
|
||
|
//
|
||
|
// Synopsis: Constructor
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
CProxyComplete::CProxyComplete( IUnknown *pControl, BOOL fAutoComplete,
|
||
|
HRESULT *hr) :
|
||
|
_Inner( this )
|
||
|
{
|
||
|
// Save the easy fields
|
||
|
_pSyncInner = NULL;
|
||
|
_fAutoComplete = fAutoComplete;
|
||
|
_pChannel = NULL;
|
||
|
if (pControl == NULL)
|
||
|
_pControl = &_Inner;
|
||
|
else
|
||
|
{
|
||
|
_pControl = pControl;
|
||
|
_pControl->AddRef();
|
||
|
}
|
||
|
|
||
|
// Aggregate in an ISynchronize.
|
||
|
if (fAutoComplete)
|
||
|
*hr = CoCreateInstance( CLSID_Synchronize_AutoComplete,
|
||
|
&_Inner,
|
||
|
CLSCTX_INPROC_SERVER, IID_IUnknown,
|
||
|
(void **) &_pSyncInner );
|
||
|
else
|
||
|
*hr = CoCreateInstance( CLSID_Synchronize_ManualResetEvent,
|
||
|
&_Inner,
|
||
|
CLSCTX_INPROC_SERVER, IID_IUnknown,
|
||
|
(void **) &_pSyncInner );
|
||
|
if (SUCCEEDED(*hr))
|
||
|
{
|
||
|
// Aggregation requires some weird reference counting.
|
||
|
Release();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CProxyComplete::~CProxyComplete
|
||
|
//
|
||
|
// Synopsis: Destructor
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
CProxyComplete::~CProxyComplete()
|
||
|
{
|
||
|
// Make sure we don't get deleted twice.
|
||
|
_Inner._iRef = 0xdead;
|
||
|
if (_pSyncInner != NULL)
|
||
|
_pSyncInner->Release();
|
||
|
if (_pChannel != NULL)
|
||
|
_pChannel->Release();
|
||
|
if (_pControl != &_Inner)
|
||
|
_pControl->Release();
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CProxyComplete::GetCallContext, public
|
||
|
//
|
||
|
// Synopsis: Calls GetCallContext on the channel
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
STDMETHODIMP CProxyComplete::GetCallContext( REFIID riid, void **pInterface )
|
||
|
{
|
||
|
if (_pChannel == NULL)
|
||
|
return RPC_E_CALL_COMPLETE;
|
||
|
return _pChannel->GetCallContext( &_Message, riid, pInterface );
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CProxyComplete::GetState, public
|
||
|
//
|
||
|
// Synopsis: Returns a pointer to the requested interface.
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
STDMETHODIMP CProxyComplete::GetState( DWORD *pState )
|
||
|
{
|
||
|
if (_pChannel == NULL)
|
||
|
return RPC_E_CALL_COMPLETE;
|
||
|
return _pChannel->GetState( &_Message, pState );
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CProxyComplete::QueryInterface, public
|
||
|
//
|
||
|
// Synopsis: Returns a pointer to the requested interface.
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
STDMETHODIMP CProxyComplete::QueryInterface( REFIID riid, LPVOID FAR* ppvObj)
|
||
|
{
|
||
|
return _pControl->QueryInterface( riid, ppvObj );
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CProxyComplete::Release, public
|
||
|
//
|
||
|
// Synopsis: Releases an interface
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
STDMETHODIMP_(ULONG) CProxyComplete::Release()
|
||
|
{
|
||
|
return _pControl->Release();
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CProxyComplete::TestCancel, public
|
||
|
//
|
||
|
// Synopsis: Is call canceled?
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
STDMETHODIMP CProxyComplete::TestCancel()
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
DWORD state;
|
||
|
|
||
|
// The call is complete is already cleaned up.
|
||
|
if (_pChannel == NULL)
|
||
|
return RPC_E_CALL_COMPLETE;
|
||
|
|
||
|
// Ask the channel about the state of the call.
|
||
|
hr = _pChannel->GetState( &_Message, &state );
|
||
|
if (FAILED(hr))
|
||
|
return hr;
|
||
|
|
||
|
// Convert the flags to error codes.
|
||
|
if (state & DCOM_CALL_CANCELED)
|
||
|
return RPC_E_CALL_CANCELED;
|
||
|
else if (state & DCOM_CALL_COMPLETE)
|
||
|
return RPC_E_CALL_COMPLETE;
|
||
|
else
|
||
|
return RPC_S_CALLPENDING;
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CStub::AddRef, public
|
||
|
//
|
||
|
// Synopsis: Adds a reference to an interface
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
STDMETHODIMP_(ULONG) CStub::AddRef()
|
||
|
{
|
||
|
InterlockedIncrement( (long *) &_iRef );
|
||
|
return _iRef;
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CStub::Connect
|
||
|
//
|
||
|
// Synopsis: Connect the stub to the server
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
STDMETHODIMP CStub::Connect( IUnknown *pServer )
|
||
|
{
|
||
|
// Get the IAsync interface.
|
||
|
return pServer->QueryInterface( IID_IAsync, (void **) &_pAsync );
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CStub::CountRefs
|
||
|
//
|
||
|
// Synopsis: Unused
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
STDMETHODIMP_(ULONG) CStub::CountRefs()
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CStub::CStub
|
||
|
//
|
||
|
// Synopsis: Constructor
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
CStub::CStub( IAsync *pAsync )
|
||
|
{
|
||
|
_iRef = 1;
|
||
|
_pAsync = pAsync;
|
||
|
pAsync->AddRef();
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CStub::DebugServerQueryInterface
|
||
|
//
|
||
|
// Synopsis: Returns a pointer to the server without AddRefing.
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
STDMETHODIMP CStub::DebugServerQueryInterface( void **ppv )
|
||
|
{
|
||
|
*ppv = _pAsync;
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CStub::DebugServerRelease
|
||
|
//
|
||
|
// Synopsis: Paired with DebugServerQueryInterface. Does nothing.
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
STDMETHODIMP_(void) CStub::DebugServerRelease( void *ppv )
|
||
|
{
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CStub::Disconnect
|
||
|
//
|
||
|
// Synopsis: Releases the server.
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
STDMETHODIMP_(void) CStub::Disconnect()
|
||
|
{
|
||
|
if (_pAsync != NULL)
|
||
|
{
|
||
|
_pAsync->Release();
|
||
|
_pAsync = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CStub::Invoke
|
||
|
//
|
||
|
// Synopsis: Unmarshals the parameters for IAsync and calls it.
|
||
|
// Creates a completion stub.
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
STDMETHODIMP CStub::Invoke( RPCOLEMESSAGE *pMessage,
|
||
|
IRpcChannelBuffer *pChannel )
|
||
|
{
|
||
|
IRpcChannelBuffer3 *pChannel2 = NULL;
|
||
|
CStubComplete *pComplete;
|
||
|
HRESULT hr = S_OK;
|
||
|
SAsyncData *pData;
|
||
|
IAsync *pCallback = NULL;
|
||
|
DWORD depth;
|
||
|
DWORD *pBuffer;
|
||
|
HANDLE memory = NULL;
|
||
|
IStream *pStream = NULL;
|
||
|
LARGE_INTEGER pos;
|
||
|
|
||
|
// Get channel buffer 2.
|
||
|
hr = pChannel->QueryInterface( IID_IRpcChannelBuffer3, (void **)
|
||
|
&pChannel2 );
|
||
|
if (FAILED(hr))
|
||
|
return hr;
|
||
|
|
||
|
// Create completion stub.
|
||
|
pComplete = new CStubComplete( NULL, pChannel2, pMessage, &hr );
|
||
|
if (pComplete == NULL)
|
||
|
{
|
||
|
pChannel2->Release();
|
||
|
return E_OUTOFMEMORY;
|
||
|
}
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
delete pComplete;
|
||
|
pChannel2->Release();
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
// Register async.
|
||
|
hr = pComplete->_pChannel->RegisterAsync( &pComplete->_Message,
|
||
|
(IAsyncManager *) pComplete );
|
||
|
if (FAILED(hr))
|
||
|
DebugBreak();
|
||
|
|
||
|
// Unmarshal data for a call to Async.
|
||
|
if (pMessage->iMethod == 3)
|
||
|
{
|
||
|
if (pMessage->cbBuffer < sizeof(SAsyncData))
|
||
|
{
|
||
|
hr = RPC_E_INVALID_DATAPACKET;
|
||
|
goto Done;
|
||
|
}
|
||
|
pData = (SAsyncData *) pMessage->Buffer;
|
||
|
}
|
||
|
|
||
|
// Unmarshal data for a call to RecurseAsync.
|
||
|
else if (pMessage->iMethod == 4)
|
||
|
{
|
||
|
/*
|
||
|
if (un pitic)
|
||
|
{
|
||
|
mic mic mic;
|
||
|
}
|
||
|
*/
|
||
|
// Get the depth.
|
||
|
if (pMessage->cbBuffer < sizeof(DWORD))
|
||
|
{
|
||
|
hr = RPC_E_INVALID_DATAPACKET;
|
||
|
goto Done;
|
||
|
}
|
||
|
pBuffer = (DWORD *) pMessage->Buffer;
|
||
|
depth = *pBuffer;
|
||
|
pBuffer += 1;
|
||
|
|
||
|
// Allocate memory.
|
||
|
memory = GlobalAlloc( GMEM_FIXED, pMessage->cbBuffer - sizeof(DWORD) );
|
||
|
if (memory == NULL)
|
||
|
goto Done;
|
||
|
|
||
|
// Create a stream.
|
||
|
hr = CreateStreamOnHGlobal( memory, TRUE, &pStream );
|
||
|
if (FAILED(hr))
|
||
|
goto Done;
|
||
|
memory = NULL;
|
||
|
|
||
|
// Copy the data into the stream.
|
||
|
hr = pStream->Write( (void *) pBuffer,
|
||
|
pMessage->cbBuffer - sizeof(DWORD), NULL );
|
||
|
if (FAILED(hr))
|
||
|
goto Done;
|
||
|
|
||
|
// Seek back to the start of the stream.
|
||
|
pos.QuadPart = 0;
|
||
|
hr = pStream->Seek( pos, STREAM_SEEK_SET, NULL );
|
||
|
if (FAILED(hr))
|
||
|
goto Done;
|
||
|
|
||
|
// Unmarshal the object.
|
||
|
hr = CoUnmarshalInterface( pStream, IID_IAsync,
|
||
|
(void **) &pCallback );
|
||
|
if (FAILED(hr))
|
||
|
goto Done;
|
||
|
}
|
||
|
|
||
|
// Bad packet.
|
||
|
else
|
||
|
{
|
||
|
hr = RPC_E_INVALID_DATAPACKET;
|
||
|
goto Done;
|
||
|
}
|
||
|
|
||
|
// Call server.
|
||
|
if (pMessage->iMethod == 3)
|
||
|
_pAsync->Async( (IAsyncManager **) &pComplete, pData->bLate,
|
||
|
pData->bSleep, pData->bFail );
|
||
|
else
|
||
|
_pAsync->RecurseAsync( (IAsyncManager **) &pComplete, pCallback,
|
||
|
depth );
|
||
|
pComplete->Release();
|
||
|
|
||
|
// Cleanup
|
||
|
Done:
|
||
|
if (pStream != NULL)
|
||
|
pStream->Release();
|
||
|
if (memory != NULL)
|
||
|
GlobalFree( memory );
|
||
|
if (pCallback != NULL)
|
||
|
pCallback->Release();
|
||
|
if (FAILED(hr))
|
||
|
pChannel2->Cancel( &pComplete->_Message );
|
||
|
pChannel2->Release();
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CStub::IsIIDSupported
|
||
|
//
|
||
|
// Synopsis: Indicates which IIDs this stub can unmarshal.
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
STDMETHODIMP_(IRpcStubBuffer *) CStub::IsIIDSupported( REFIID riid )
|
||
|
{
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CStub::QueryInterface, public
|
||
|
//
|
||
|
// Synopsis: Returns a pointer to the requested interface.
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
STDMETHODIMP CStub::QueryInterface( REFIID riid, LPVOID FAR* ppvObj)
|
||
|
{
|
||
|
if (IsEqualIID(riid, IID_IUnknown) ||
|
||
|
IsEqualIID(riid, IID_IRpcStubBuffer))
|
||
|
*ppvObj = (IRpcStubBuffer *) this;
|
||
|
else
|
||
|
{
|
||
|
*ppvObj = NULL;
|
||
|
return E_NOINTERFACE;
|
||
|
}
|
||
|
|
||
|
AddRef();
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CStub::Release, public
|
||
|
//
|
||
|
// Synopsis: Releases an interface
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
STDMETHODIMP_(ULONG) CStub::Release()
|
||
|
{
|
||
|
ULONG lRef = _iRef - 1;
|
||
|
|
||
|
if (InterlockedDecrement( (long*) &_iRef ) == 0)
|
||
|
{
|
||
|
delete this;
|
||
|
return 0;
|
||
|
}
|
||
|
else
|
||
|
return lRef;
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CSCInnerUnknown::AddRef, public
|
||
|
//
|
||
|
// Synopsis: Adds a reference to an interface
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
STDMETHODIMP_(ULONG) CSCInnerUnknown::AddRef()
|
||
|
{
|
||
|
InterlockedIncrement( (long *) &_iRef );
|
||
|
return _iRef;
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CSCInnerUnknown::CSCInnerUnknown
|
||
|
//
|
||
|
// Synopsis: Constructor
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
CSCInnerUnknown::CSCInnerUnknown( CStubComplete *pParent )
|
||
|
{
|
||
|
_iRef = 1;
|
||
|
_pParent = pParent;
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CSCInnerUnknown::QueryInterface, public
|
||
|
//
|
||
|
// Synopsis: Returns a pointer to the requested interface.
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
STDMETHODIMP CSCInnerUnknown::QueryInterface( REFIID riid, LPVOID FAR* ppvObj)
|
||
|
{
|
||
|
IUnknown *pUnk;
|
||
|
|
||
|
if (IsEqualIID(riid, IID_IUnknown))
|
||
|
pUnk = (IUnknown *) this;
|
||
|
else if (IsEqualIID(riid, IID_IAsyncManager))
|
||
|
pUnk = (IAsyncManager *) _pParent;
|
||
|
else if (IsEqualIID(riid, IID_ICancelMethodCalls))
|
||
|
pUnk = (ICancelMethodCalls *) _pParent;
|
||
|
else if (IsEqualIID(riid, IID_IAsyncSetup))
|
||
|
pUnk = (IAsyncSetup *) _pParent;
|
||
|
else if (_pParent->_pSyncInner != NULL)
|
||
|
return _pParent->_pSyncInner->QueryInterface( riid, ppvObj );
|
||
|
|
||
|
pUnk->AddRef();
|
||
|
*ppvObj = pUnk;
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CSCInnerUnknown::Release, public
|
||
|
//
|
||
|
// Synopsis: Releases an interface
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
STDMETHODIMP_(ULONG) CSCInnerUnknown::Release()
|
||
|
{
|
||
|
ULONG lRef = _iRef - 1;
|
||
|
|
||
|
if (InterlockedDecrement( (long*) &_iRef ) == 0)
|
||
|
{
|
||
|
delete _pParent;
|
||
|
return 0;
|
||
|
}
|
||
|
else
|
||
|
return lRef;
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CStubComplete::AddRef, public
|
||
|
//
|
||
|
// Synopsis: Adds a reference to an interface
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
STDMETHODIMP_(ULONG) CStubComplete::AddRef()
|
||
|
{
|
||
|
return _pControl->AddRef();
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CStubComplete::Cancel, public
|
||
|
//
|
||
|
// Synopsis: Forward cancel to the channel
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
STDMETHODIMP CStubComplete::Cancel()
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
|
||
|
if (_pChannel == NULL)
|
||
|
return RPC_E_CALL_COMPLETE;
|
||
|
hr = _pChannel->Cancel( &_Message );
|
||
|
_pChannel->Release();
|
||
|
_pChannel = NULL;
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CStubComplete::CompleteCall, public
|
||
|
//
|
||
|
// Synopsis: Get a buffer and send the reply.
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
STDMETHODIMP CStubComplete::CompleteCall( HRESULT result )
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
DWORD lIgnore;
|
||
|
|
||
|
// Fail if there is no call.
|
||
|
if (_pChannel == NULL)
|
||
|
return RPC_E_CALL_COMPLETE;
|
||
|
|
||
|
// Get a buffer.
|
||
|
_Message.cbBuffer = 4;
|
||
|
hr = _pChannel->GetBuffer( &_Message, IID_IAsync );
|
||
|
if (FAILED(hr))
|
||
|
goto Done;
|
||
|
|
||
|
// Marshal the result.
|
||
|
*((HRESULT *) _Message.Buffer) = result;
|
||
|
|
||
|
// Send the reply.
|
||
|
hr = _pChannel->Send( &_Message, &lIgnore );
|
||
|
|
||
|
Done:
|
||
|
_pChannel->Release();
|
||
|
_pChannel = NULL;
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CStubComplete::CStubComplete
|
||
|
//
|
||
|
// Synopsis: Constructor
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
CStubComplete::CStubComplete( IUnknown *pControl,
|
||
|
IRpcChannelBuffer3 *pChannel,
|
||
|
RPCOLEMESSAGE *pMessage,
|
||
|
HRESULT *hr ) :
|
||
|
_Inner( this )
|
||
|
{
|
||
|
_Message = *pMessage;
|
||
|
_pSyncInner = NULL;
|
||
|
_pChannel = pChannel;
|
||
|
pChannel->AddRef();
|
||
|
if (pControl == NULL)
|
||
|
_pControl = &_Inner;
|
||
|
else
|
||
|
{
|
||
|
_pControl = pControl;
|
||
|
_pControl->AddRef();
|
||
|
}
|
||
|
|
||
|
// Aggregate in an ISynchronize.
|
||
|
*hr = CoCreateInstance( CLSID_Synchronize_ManualResetEvent,
|
||
|
&_Inner,
|
||
|
CLSCTX_INPROC_SERVER, IID_IUnknown,
|
||
|
(void **) &_pSyncInner );
|
||
|
if (SUCCEEDED(*hr))
|
||
|
{
|
||
|
// Aggregation requires some weird reference counting.
|
||
|
Release();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CStubComplete::~CStubComplete
|
||
|
//
|
||
|
// Synopsis: Destructor
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
CStubComplete::~CStubComplete()
|
||
|
{
|
||
|
// Make sure we don't get deleted twice.
|
||
|
_Inner._iRef = 0xdead;
|
||
|
if (_pSyncInner != NULL)
|
||
|
_pSyncInner->Release();
|
||
|
if (_pChannel != NULL)
|
||
|
_pChannel->Release();
|
||
|
if (_pControl != &_Inner)
|
||
|
_pControl->Release();
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CStubComplete::GetAsyncManager, public
|
||
|
//
|
||
|
// Synopsis: Creates a stub completion object and reregisters it
|
||
|
// in place of this one.
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
STDMETHODIMP CStubComplete::GetAsyncManager( REFIID riid,
|
||
|
IUnknown *pOuter,
|
||
|
DWORD dwFlags,
|
||
|
IUnknown **ppInner,
|
||
|
IAsyncManager **ppComplete )
|
||
|
{
|
||
|
CStubComplete *pComplete;
|
||
|
HRESULT hr;
|
||
|
|
||
|
// Fail if there is no call.
|
||
|
if (_pChannel == NULL)
|
||
|
return RPC_E_CALL_COMPLETE;
|
||
|
|
||
|
// Create a new stub completion object.
|
||
|
pComplete = new CStubComplete( pOuter, _pChannel, &_Message, &hr );
|
||
|
if (pComplete == NULL)
|
||
|
return E_OUTOFMEMORY;
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
delete pComplete;
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
// Register the new stub completion object.
|
||
|
hr = _pChannel->RegisterAsync( &pComplete->_Message, pComplete );
|
||
|
if (FAILED(hr))
|
||
|
DebugBreak();
|
||
|
|
||
|
// Disconnect this stub completion object.
|
||
|
_pChannel->Release();
|
||
|
_pChannel = NULL;
|
||
|
*ppComplete = pComplete;
|
||
|
*ppInner = &pComplete->_Inner;
|
||
|
return S_OK;
|
||
|
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CStubComplete::GetCallContext, public
|
||
|
//
|
||
|
// Synopsis: Calls GetCallContext on the channel
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
STDMETHODIMP CStubComplete::GetCallContext( REFIID riid, void **pInterface )
|
||
|
{
|
||
|
if (_pChannel == NULL)
|
||
|
return RPC_E_CALL_COMPLETE;
|
||
|
return _pChannel->GetCallContext( &_Message, riid, pInterface );
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CStubComplete::GetState, public
|
||
|
//
|
||
|
// Synopsis: Returns a pointer to the requested interface.
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
STDMETHODIMP CStubComplete::GetState( DWORD *pState )
|
||
|
{
|
||
|
if (_pChannel == NULL)
|
||
|
return RPC_E_CALL_COMPLETE;
|
||
|
return _pChannel->GetState( &_Message, pState );
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CStubComplete::QueryInterface, public
|
||
|
//
|
||
|
// Synopsis: Returns a pointer to the requested interface.
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
STDMETHODIMP CStubComplete::QueryInterface( REFIID riid, LPVOID FAR* ppvObj)
|
||
|
{
|
||
|
return _pControl->QueryInterface( riid, ppvObj );
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CStubComplete::Release, public
|
||
|
//
|
||
|
// Synopsis: Releases an interface
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
STDMETHODIMP_(ULONG) CStubComplete::Release()
|
||
|
{
|
||
|
return _pControl->Release();
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CStubComplete::TestCancel, public
|
||
|
//
|
||
|
// Synopsis: Is call canceled?
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
STDMETHODIMP CStubComplete::TestCancel()
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
DWORD state;
|
||
|
|
||
|
// The call is complete is already cleaned up.
|
||
|
if (_pChannel == NULL)
|
||
|
return RPC_E_CALL_COMPLETE;
|
||
|
|
||
|
// Ask the channel about the state of the call.
|
||
|
hr = _pChannel->GetState( &_Message, &state );
|
||
|
if (FAILED(hr))
|
||
|
return hr;
|
||
|
|
||
|
// Convert the flags to error codes.
|
||
|
if (state & DCOM_CALL_CANCELED)
|
||
|
return RPC_E_CALL_CANCELED;
|
||
|
else if (state & DCOM_CALL_COMPLETE)
|
||
|
return RPC_E_CALL_COMPLETE;
|
||
|
else
|
||
|
return RPC_S_CALLPENDING;
|
||
|
}
|
||
|
|
||
|
|