// Copyright (c) 1996-2000 Microsoft Corporation // -------------------------------------------------------------------------- // // lresobj // // LresultFromObject and ObjectFromLresult. // // -------------------------------------------------------------------------- #include "oleacc_p.h" #ifndef WMOBJ_SAMETHREAD #define WMOBJ_SAMETHREAD 0xFFFFFFFF #endif // This structure is used by the NT version of SharedBuffer_Allocate/Free, // it acts as a header, and is followed immedialtey by the marshal data. // // (The 9x version of SharedBuffer_Allocate currently stores the data // without a header.) // // Be careful with packing and alignment here - this needs to look the same // on 32bit and 64-bit compiles. // typedef struct { DWORD m_dwSig; DWORD m_dwSrcPID; DWORD m_dwDstPID; DWORD m_cbSize; } XFERDATA, *PXFERDATA; #define MSAAXFERSIG 0xAA1CF00D static BOOL ParseHexStr( LPCTSTR pStr, DWORD * pdw ); static BOOL EncodeToAtom( DWORD dwSrcPID, HANDLE dwSrcHandle, DWORD * pdw ); static BOOL DecodeFromAtom( DWORD dw, DWORD * pdwSrcPID, HANDLE * pdwSrcHandle ); // // Note on SharedBuffer_Allocate and SharedBuffer_Free: // // LresultFromObject and ObjectFromLresult call SharedBuffer_Allocate and // SharedBuffer_Free respectively to do the actual work of transferring // memory between processes. // // // SharedBuffer_Allocate is given a pointer to the marshalled interface data, // and its length (pData, cbData), and the pid of the process that wants the // object (dwDstPID, which may be 0 if the destination is unknown - while // Oleacc's AccessibleObjectFromWindow sends WM_GETOBJECT with wParam as the // pid, some legacy code already out there sends WM_GETOBJECT directly with // wParam = 0.) // // SharedBuffer_Allocate then stores that marshalled data however it needs // to, and returns an LRESULT that can later be used to get back to that data. // Note that since this LRESULT may be used by a 32-bit process, it must be // only 32-bit significant. Also, it must look like a SUCCESS HRESULT - ie. // bit 31 must be 0. // // // SharedBuffer_Free is given a DWORD ref - which is the LRESULT that // SharedBuffer_Allocate previously returned - the destination process pid - // which may be 0 if ObjectFromLresult was called directly by legacy code - // and also an riid. // // SharedBuffer_Free needs to use that DWORD ref to get at the memory that // SharedBuffer_Allocate set up, and then call the utility function // UnmarshalInterface() on that memory with the riid to return an interface // pointer. SharedBuffer_Free must also deallocate the associated memory as // appropriate. // // Different versions of _Allocate and _Free exist on 9x and NT, denoted by // _Win95 and _NT suffixes. A generic _Allocate and _Free called the // appropriate one based on compile options and the global fWindows95 flag. // static LRESULT WINAPI SharedBuffer_Allocate_NT( const BYTE * pData, DWORD cbData, DWORD dwDstPID ); static HRESULT WINAPI SharedBuffer_Free_NT( DWORD ref, DWORD dwDstPID, REFIID riid, LPVOID * ppv ); #ifndef NTONLYBUILD static LRESULT WINAPI SharedBuffer_Allocate_Win95( const BYTE * pData, DWORD cbData, DWORD dwDstPID ); static HRESULT WINAPI SharedBuffer_Free_Win95( DWORD ref, DWORD dwDstPID, REFIID riid, LPVOID * ppv ); #endif // NTONLYBUILD static inline LRESULT WINAPI SharedBuffer_Allocate( const BYTE * pData, DWORD cbData, DWORD dwDstPID ) { #ifndef NTONLYBUILD if( fWindows95 ) { return SharedBuffer_Allocate_Win95( pData, cbData, dwDstPID ); } else #endif // NTONLYBUILD { return SharedBuffer_Allocate_NT( pData, cbData, dwDstPID ); } } static inline HRESULT WINAPI SharedBuffer_Free( DWORD ref, DWORD dwDstPID, REFIID riid, LPVOID * ppv ) { #ifndef NTONLYBUILD if( fWindows95 ) { return SharedBuffer_Free_Win95( ref, dwDstPID, riid, ppv ); } else #endif // NTONLYBUILD { return SharedBuffer_Free_NT( ref, dwDstPID, riid, ppv ); } } // -------------------------------------------------------------------------- // // LresultFromObject_Local(), ObjectFromLresult_Local() // // These are the same-thread optimizations of LFromO and OFromL. // // The key thing is to bit-twiddle the interface pointer so that it does // not look like an error HRESULT - ie. bit31 must be 0. // We take advantage of the fact that since these are pointers, bit 0 will // be 0, and we are free to use it for our own use in our encoding. // // The mapping scheme is as follows: // // Mapping to Lresult from Object, punk -> LRESULT // // top 32 bits unchanged // bit 31 set to 0 (so that it looks like a success HRESULT) // bits 30..0 correspond to bits 31..1 of the input value. // bit 0 of the original value is lost; assumed to be 0. // // Mapping to Object from Lresult, LRESULT -> punk // // top 32 bits unchanged // bits 31..1 correspond to bits 30..0 of the input value. // bit 0 set to 0 // // This will work on Win64, and on Win32 with memory above 2G enabled. // // -------------------------------------------------------------------------- static LRESULT LresultFromObject_Local( IUnknown * punk ) { // Do the work using DWORD_PTRs - thery're unsigned, so we don't get // unexpected nasty sign-extension effects, esp. when shifting... DWORD_PTR in = (DWORD_PTR) punk; // Mask off lower 32 bits to get the upper 32 bits (NOP on W32)... DWORD_PTR out = in & ~(DWORD_PTR)0xFFFFFFFF; // Now add in the lower 31 bits (excluding bit 0) shifted by one so // that bit 31 is 0... out |= ( in & (DWORD_PTR)0xFFFFFFFF ) >> 1; return (LRESULT) out; } static IUnknown * ObjectFromLresult_Local( LRESULT lres ) { Assert( SUCCEEDED( lres ) ); DWORD_PTR in = (DWORD_PTR) lres; // Mask off lower 32 bits to get the upper 32 bits (NOP on W32)... DWORD_PTR out = in & ~(DWORD_PTR)0xFFFFFFFF; // Now add in the lower 31 bits, shifted back to their original // position... out |= ( in & (DWORD_PTR)0x7FFFFFFF ) << 1; return (IUnknown *) out; } // -------------------------------------------------------------------------- // // LresultFromObject() // // Encodes an interface pointer into an LRESULT. // // If the client and server are on the same thread, an optimized version is // used; the pointer is effectively AddRef()'d and returned as the LRESULT. // (some bit-shifting takes place to prevent it looking like an error HRESULT, // ObjectFromLresult reverses this bit=shifting.) // // If the client and server are not on the same thread, the interface is // marshaled, and Shared_Allocate is used to save a copy of that marshaled // data, returning an opaque 32-bit identifier that the client process can // pass to ObjectFromLresult to get back the interface. // // -------------------------------------------------------------------------- EXTERN_C LRESULT WINAPI LresultFromObject(REFIID riid, WPARAM wParam, LPUNKNOWN punk) { SMETHOD( TEXT("LresultFromObject"), TEXT("wParam=%d punk=%p"), wParam, punk ); // Optimization for when client and server are the same thread; no need to // marshal/unmarshal, we can pass the pointer directly. // Casting to DWORD to avoid sign extention issues - WMOBJ_SAMETHREAD is a // 32-bit value, but wParam is 64-bit on 64. if( (DWORD)wParam == (DWORD)WMOBJ_SAMETHREAD ) { // We addref here to hold onto the object on behalf of the client caller. // This allows the server to safely release() the object after they've used // LresultfromObject to 'convert' it into a LRESULT punk->AddRef(); return LresultFromObject_Local( punk ); } // Cross-proc or cross-thread case, need to marshal the interface, save it // to a buffer, and return some sort of reference to that buffer... const BYTE * pData; DWORD cbData; MarshalState mstate; HRESULT hr = MarshalInterface( riid, punk, MSHCTX_LOCAL, MSHLFLAGS_NORMAL, & pData, & cbData, & mstate ); if( FAILED( hr ) ) { return hr; } DWORD dwDestPid = (DWORD) wParam; // Got the marhalled data, now call SharedBuffer_Allocate to wrap it into // some short of shared memory and return a suitable reference to that. LRESULT lResult = SharedBuffer_Allocate( pData, cbData, dwDestPid ); MarshalInterfaceDone( & mstate ); return lResult; } // -------------------------------------------------------------------------- // // ObjectFromLresult() // // This function converts the 32-bit opaque value returned from LresultFromObject // into a marshalled interface pointer. // // -------------------------------------------------------------------------- EXTERN_C HRESULT WINAPI ObjectFromLresult( LRESULT ref, REFIID riid, WPARAM wParam, void **ppvObject ) { SMETHOD( TEXT("ObjectFromLResult"), TEXT("ref=%p wParam=%d"), ref, wParam ); // Do a basic sanity check on parameters if( ppvObject == NULL ) { TraceParam( TEXT("ObjectFromLresult: ppvObject should be non-NULL") ); return E_POINTER; } *ppvObject = NULL; if( FAILED( ref ) ) { TraceParam( TEXT("ObjectFromLresult: failure lResult was passed in (%08lx)"), ref ); return E_INVALIDARG; } // If the client and server are in the same thread, LresultFromObject is // optimized to return the original interface pointer since no marshalling // is needed. // Casts used to avoid any 32/64 sign extension issues. We only use the low // DWORD of wParam, even on w64. if( (DWORD)wParam == (DWORD)WMOBJ_SAMETHREAD ) { // Use the bit-mangling in-proc optimization... IUnknown * punk = ObjectFromLresult_Local( ref ); if( punk == NULL ) { TraceError( TEXT("ObjectFromLresult: (inproc case) lresult translates to NULL pointer") ); return E_INVALIDARG; } // Some apps was responding to WM_GETOBJECT message with 1. This can cause // a problem for folks responding to events in-context, since we expect // it to be a pointer - so need to check that it's valid... if( IsBadReadPtr( punk, 1 ) ) { TraceError( TEXT("ObjectFromLresult: (inproc case) lresult translates to invalid pointer (%p)"), punk ); return E_INVALIDARG; } HRESULT hr = punk->QueryInterface( riid, ppvObject ); if( FAILED( hr ) ) { TraceErrorHR( hr, TEXT("ObjectFromLresult: (inproc case) QI'ing translated pointer") ); } punk->Release(); return hr; } // cross-proc case, call SharedBuffer_Free to access the buffer indicated by // ref, and to unmarshal the interface... // (The cast is for when we're on W64 - convert from (64/42-bit) LRESULT to // 32-bit buffer reference...) return SharedBuffer_Free( (DWORD) ref, (DWORD) wParam, riid, ppvObject ); } // -------------------------------------------------------------------------- // Following static functions are local to this module // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- // // LRESULT WINAPI SharedBuffer_Allocate_NT( const BYTE * pData, // DWORD cbData, // DWORD dwDstPID ); // // IN const BYTE * pData // pointer to marshal data to store // // IN DWORD cbData // length of marshaled data // // IN DWORD dwDstPID // process id of processing requesting the data. May be 0 if not known // // Returns LRESULT // 32-bit opaque token (with bit 31 clear) that can be passed to // SharedBuffer_Free to get back the interface pointer. // // // See notes near top of file on how SharedBuffer_Alocate/Free works. // // The NT version uses memory-mapped files - we create a file mapping, // copy the marshal data into it, and then return the handle. // // If we know the caller'd pid, we DuplicateHandle() the handle to them, // and return the duplicated handle. // // If we don't know their pid, we encode the handle and our pid as a string, // and convert that to an atom, and return the atom. (This is a 'clever' // way of squeezing two 32-bit pieces of information into a single 32-bit // LRESULT!) // // -------------------------------------------------------------------------- static LRESULT WINAPI SharedBuffer_Allocate_NT( const BYTE * pData, DWORD cbData, DWORD dwDstPID ) { HRESULT hr = E_FAIL; // if things don't work out... // Note that we don't Close this handle explicitly here, // DuplicateHandle(DUPLICATE_CLOSE_SOURCE) will code it, whether it is duplicated // here (if we know the caller's pid), or in SharedBuffer_Free (if dwDstPID is 0). HANDLE hfm = CreateFileMapping( INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, cbData + sizeof( XFERDATA ), NULL ); if( hfm == NULL ) { TraceErrorW32( TEXT("SharedBuffer_Allocate_NT: CreateFileMapping failed") ); return E_FAIL; } PXFERDATA pxd = (PXFERDATA) MapViewOfFile( hfm, FILE_MAP_WRITE, 0, 0, 0 ); if( pxd == NULL ) { TraceErrorW32( TEXT("SharedBuffer_Allocate_NT: MapViewOfFile failed") ); return E_FAIL; } // Got a pointer to the memory. Fill in the header, and copy the marshal data... pxd->m_dwSig = MSAAXFERSIG; pxd->m_dwSrcPID = GetCurrentProcessId(); // Don't actually need this... pxd->m_dwDstPID = dwDstPID; pxd->m_cbSize = cbData; memcpy( pxd + 1, pData, cbData ); UnmapViewOfFile( pxd ); // If we know who the caller is, we can just DUP a handle to them, closing our // side, and and return them the DUP's handle... if( dwDstPID ) { HANDLE hDstProc = OpenProcess( PROCESS_DUP_HANDLE, FALSE, dwDstPID ); if( ! hDstProc ) { TraceErrorW32( TEXT("SharedBuffer_Allocate_NT: OpenProcess(pid=%d) failed"), dwDstPID ); CloseHandle( hfm ); return E_FAIL; } HANDLE hTarget = NULL; BOOL b = DuplicateHandle( GetCurrentProcess(), hfm, hDstProc, & hTarget, 0, FALSE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS ); CloseHandle( hDstProc ); if( ! b ) { TraceErrorW32( TEXT("SharedBuffer_Allocate_NT: DuplicateHandle (to pid=%d) failed"), dwDstPID ); // No tidy up to do at this stage. The mapping has been unmapped above; and // DuplicateHandle with _CLOSE_SOURCE always closes the source handle, even // if it failes to dup it. return E_FAIL; } // Shift right by one to ensure hight-bit is clear (to avoid looking like an error // HRESULT). Client will shift-back before use in SharedBuffer_Free. hr = (DWORD)HandleToLong(hTarget) >> 1; } else { // wParam == 0 means we don't know the caller's PID - encode our pid and the handle // is an atom and send that back instead. See comments near EncodeToAtom for full // explanation. DWORD dwRet; if( ! EncodeToAtom( GetCurrentProcessId(), hfm, & dwRet ) ) { TraceError( TEXT("SharedBuffer_Allocate_NT: EncodeToAtom failed") ); return E_FAIL; } // Succeeded - so return the atom as the lresult. Atoms are 16-bit, so we don't // have to worry about colliding with error HRESULTs. hr = dwRet; } return hr; } // -------------------------------------------------------------------------- // // LRESULT WINAPI SharedBuffer_Free_NT( DWORD ref, // DWORD dwDstPID, // REFIID riid, // LPVOID * ppv ); // // IN DWORD ref // Cookie from SharedBuffer_Allocate // // IN DWORD dwDstPID // process id of processing requesting the data. May be 0 if not known // // IN REFIID riid // desired interface to be returned // // OUT LPVOID * ppv // returned interface pointer // // Returns HRESULT // S_OK on success. // // // See notes near top of file on how SharedBuffer_Alocate/Free works. // // Basic alg: the ref is either a handle to shared memory, or an atom referencing // a handle in another process to shared memory, and that pid. In the latter case, // we need to DuplicateHandle that handle to one that we can use. // // Once we've got the handle, map it, check the header of the buffer, and then // unmarshal the data to get the interface pointer. // // -------------------------------------------------------------------------- static HRESULT WINAPI SharedBuffer_Free_NT( DWORD ref, DWORD dwDstPID, REFIID riid, LPVOID * ppv ) { // sanity check on ref parameter... if( FAILED( ref ) ) { TraceError( TEXT("SharedBuffer_Free_NT: ref is failure HRESULT") ); return E_INVALIDARG; } HRESULT hr; HANDLE hfm; // Extract the handle from ref - two different ways this can happen... if( dwDstPID != 0 ) { // Normal case - where we've sent the server our pid and it send us back // a handle it has dup'd for us. // Server shifted the handle right by one to avoid clashing with error HRESULTS - // shift it back before we use it... hfm = LongToHandle( ref << 1 ); } else { // dwDstPid - which is the wParam passed to ObjectFromLresut - is 0, so we don't // know the source process's pid. Treat the lresult 'ref' as an atom, and decode // that to get the source pid and handle... // Extract the source process's PID and its handle from the atom name... DWORD dwSrcPID; HANDLE hRemoteHandle; if( ! DecodeFromAtom( ref, & dwSrcPID, & hRemoteHandle ) ) { return E_FAIL; } // Now use DuplicateHandle plus the src's pid to convert its src-relative handle // to one we can use... HANDLE hSrcProc = OpenProcess( PROCESS_DUP_HANDLE, FALSE, dwSrcPID ); if( ! hSrcProc ) { TraceErrorW32( TEXT("SharedBuffer_Free_NT: OpenProcess(pid=%d) failed"), dwSrcPID ); return E_FAIL; } BOOL fDupHandle = DuplicateHandle( hSrcProc, hRemoteHandle, GetCurrentProcess(), & hfm, 0, FALSE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS ); CloseHandle( hSrcProc ); if( ! fDupHandle || ! hfm ) { TraceErrorW32( TEXT("SharedBuffer_Free_NT: DuplicateHandle(from pid=%d) failed"), dwSrcPID ); return E_FAIL; } // Got it! Now carry on as normal, with hfm == our handle. } // At this stage, we have the handle. Now map it, so we can extract the data... PXFERDATA pxd = (PXFERDATA) MapViewOfFile( hfm, FILE_MAP_ALL_ACCESS, 0, 0, 0 ); if( pxd == NULL ) { TraceErrorW32( TEXT("SharedBuffer_Free_NT: MapViewOfFile failed") ); // We should only close the handle if it turns out to point to valid // shared memory. Otherwise a rogue client could return a handle value // that refers to an existing handle in this process - and we'd close // that instead. UnmapViewOfFile( pxd ); return E_FAIL; } // Sanity-check the data: // Verify that dest is what we expect them to be. // Only check the dstpid if it's non-0... if( pxd->m_dwSig != MSAAXFERSIG || ( dwDstPID != 0 && pxd->m_dwDstPID != GetCurrentProcessId() ) ) { TraceError( TEXT("SharedBuffer_Free_NT: Signature of shared mem block is invalid") ); // don't close handle - see note above... UnmapViewOfFile( pxd ); return E_FAIL; } BYTE * pData = (BYTE *) ( pxd + 1 ); DWORD cbData = pxd->m_cbSize; // We have the size of the data and the address of the data, unmarshal it // make a stream out of it. hr = UnmarshalInterface( pData, cbData, riid, ppv ); UnmapViewOfFile( pxd ); CloseHandle( hfm ); return hr; } #ifndef NTONLYBUILD // -------------------------------------------------------------------------- // // LRESULT WINAPI SharedBuffer_Allocate_Win95( const BYTE * pData, // DWORD cbData, // DWORD dwDstPID ); // // IN const BYTE * pData // pointer to marshal data to store // // IN DWORD cbData // length of marshaled data // // IN DWORD dwDstPID // process id of processing requesting the data. May be 0 if not known // // Returns LRESULT // 32-bit opaque token (with bit 31 clear) that can be passed to // SharedBuffer_Free to get back the interface pointer. // // // See notes near top of file on how SharedBuffer_Alocate/Free works. // // The 9x version uses SharedAlloc, and returns a bit-mangled pointer to // the shared buffer. // // -------------------------------------------------------------------------- static LRESULT WINAPI SharedBuffer_Allocate_Win95( const BYTE * pData, DWORD cbData, DWORD unused( dwDstPID ) ) { // Since we know we are on Win95, we can specify NULL for // the hwnd and hProcess parameters here. PVOID pv = SharedAlloc( cbData, NULL, NULL ); if( pv == NULL ) { TraceErrorW32( TEXT("SharedBuffer_Allocate_Win95: SharedAlloc failed") ); return HRESULT_FROM_WIN32( GetLastError() ); } memcpy( pv, pData, cbData ); // Force the high-bit off to indicate a successful return value return (LRESULT) ((DWORD) pv) & ~HEAP_GLOBAL; } // -------------------------------------------------------------------------- // // LRESULT WINAPI SharedBuffer_Free_NT( DWORD ref, // DWORD dwDstPID, // REFIID riid, // LPVOID * ppv ); // // IN DWORD ref // Cookie from SharedBuffer_Allocate // // IN DWORD dwDstPID // process id of processing requesting the data. May be 0 if not known // // IN REFIID riid // desired interface to be returned // // OUT LPVOID * ppv // returned interface pointer // // Returns HRESULT // S_OK on success. // // // See notes near top of file on how SharedBuffer_Alocate/Free works. // // The 9x version un-mangles the pointer to the shared buffer, unmarshalls the marshal // data, then frees the shared buffer. // // -------------------------------------------------------------------------- static HRESULT WINAPI SharedBuffer_Free_Win95(DWORD ref, DWORD unused( dwDstPID ), REFIID riid, LPVOID * ppv ) { // Get address of shared memory block BYTE * pData = (BYTE *) (ref | HEAP_GLOBAL); // Turn the high-bit back on // Get the size of the block in the shared heap. DWORD cbData = HeapSize( hheapShared, 0, pData ); HRESULT hr = UnmarshalInterface( pData, cbData, riid, ppv ); // we know we are on Win95, can use NULL hProcess... SharedFree( pData, NULL); return hr; } #endif // NTONLYBUILD // -------------------------------------------------------------------------- // // BOOL ParseHexStr( LPCTSTR pStr, DWORD * pdw ) // // // IN LPCTSTR pStr // pointer to string to parse // // OUT DWORD * pdw // returns the hex value of the string. // // Returns BOOL // TRUE on success. // // // Decodes a string of 8 hex digits. This uses EXACTLY 8 hex didits, and // fails (returns FALSE) if any invalid (non 0..9A..F) char is encountered. // // Used by DecodeFromAtom(). // // -------------------------------------------------------------------------- static BOOL ParseHexStr( LPCTSTR pStr, DWORD * pdw ) { DWORD dw = 0; for( int c = 0 ; c < 8 ; c++, pStr++ ) { dw <<= 4; if( *pStr >= '0' && *pStr <= '9' ) { dw += *pStr - '0'; } else if( *pStr >= 'A' && *pStr <= 'F' ) { dw += *pStr - 'A' + 10; } else if( *pStr >= 'a' && *pStr <= 'f' ) { dw += *pStr - 'a' + 10; } else { // invalid hex digit return FALSE; } } *pdw = dw; return TRUE; } // What this 'atom encoding' is all about... // // If the WM_GETOBJECT is being sent from OLEACC (AccessibleObjectFromWindow), // OLEACC will send the pid of the client process in the wParam. The server can // use this with DuplicateHandle to create a handle that the destination/client // process can use, which the server then returns. // // However, some clients send WM_GETOBJECT directly with wParam == 0; or, // in the case of Trident, a provate registered message "WM_HTML_GETOBJECT" // is used, with wParam == 0. // In this case, the server doesn't know who the client is, so can't Dup a // handle to it (in LresultFromObject). Also, the client code, (in // ObjectFromLresult) doesn't know who the server is - all it has is the // returned DWORD (and a wParam of 0!) - so even if it had a server-relative // handle, it couldn't DuplicateHandle it to one that it could use, since that // requires knowing the server's pid. // // The solution/workaround here is to special-case the case where wParam is 0. // If wParam is non-0 (and is not the SAMETHREAD special value), we use it as the // pid and the server dups the handle to one that the cient can use and returns it. // // If the wParam is 0, the server instead builds a string containing the server's // pid and the handle, in the following format: // // "MSAA:00000000:00000000:" // // The first 8-digit hex number is the server's pid; the second 8-digit hex number // is the server's handle to the memory. (Handles are only 32-bit significant, even // on Win64.) // // The server then adds this to the global atom table, using GlobalAddAtom, which // returns an ATOM. (Atoms are typedef'd a SHORTs, and will comfortably fit inside // the returned DWORD, leaving the high bit 0, avoinding confusion with error // HRESULTS.) // // The server returns the atom back to the client. // The client code in ObjectFromLresult notices that wParam is 0, so treats the // lresult as an Atom, uses globalGetAtomName() to retreive the above string, // checks that is has the expected format, and decodes the two hex numbers. // // The client now has the server's PID and the server-relative handle to the // memory containing the marshalled interface pointer. The client can then use // these with DuplicateHandle to generate a handle that it can use. // // Now that the client has a handle to the marshalled interface memory, it can // continue on as usual to unmarshal the interface which it returns to the caller. // // Expected format: "MSAA:00000000:00000000:" // Defines for offsets into this string. Lengths do not include terminating NULs. #define ATOM_STRING_LEN (4 + 1 + 8 + 1 + 8 + 1) #define ATOM_STRING_PREFIX TEXT("MSAA:") #define ATOM_STRING_PREFIX_LEN 5 #define ATOM_PID_OFFSET 5 #define ATOM_COLON2_OFFSET 13 #define ATOM_HANDLE_OFFSET 14 #define ATOM_COLON3_OFFSET 22 #define ATOM_NUL_OFFSET 23 // -------------------------------------------------------------------------- // // BOOL EncodeToAtom( DWORD dwSrcPID, HANDLE dwSrcHandle, DWORD * pdw ) // // // IN DWORD dwSrcPID // process id to encode // // IN HANDLE dwSrcHandle // handle in source process to encode // // OUT DWORD * pdw // returns the resulting atom value // // Returns BOOL // TRUE on success. // // // Encodes the dwSrcPID and dwSrcHandle into a string of the form: // // "MSAA:00000000:00000000:" // // where the first part is the pid and the second part is the handle, and // then gets an atom for this string, and returns the atom. // // -------------------------------------------------------------------------- static BOOL EncodeToAtom( DWORD dwSrcPID, HANDLE dwSrcHandle, DWORD * pdw ) { TCHAR szAtomName[ ATOM_STRING_LEN + 1 ]; // +1 for terminating NUL wsprintf( szAtomName, TEXT("MSAA:%08X:%08X:"), dwSrcPID, dwSrcHandle ); ATOM atom = GlobalAddAtom( szAtomName ); // atoms are unsigned words - make sure they get converted properly to // unsigned DWORD/HRESULTs... // At least bit32 must be clear, to avoid confusion with error hresults. // (Also, atoms are never 0, so no ambiguity over hr=0 return value, which // indicates WM_GETOBJECT not supported.) *pdw = (DWORD) atom; return TRUE; } // -------------------------------------------------------------------------- // // BOOL DecodeFromAtom( DWORD dw, DWORD * pdwSrcPID, HANDLE * pdwSrcHandle ) // // // IN DWORD dw // specifies the atom to be decoded // // OUT DWORD * pdwSrcPID // returns the source process id // // OUT HANDLE * pdwSrcHandle // returns the handle in source process // // Returns BOOL // TRUE on success. // // // Gets ths string for the atom represented by dw, and decodes it to get // the source process id and handle value. // // -------------------------------------------------------------------------- static BOOL DecodeFromAtom( DWORD dw, DWORD * pdwSrcPID, HANDLE * pdwSrcHandle ) { // Sanity check that dw looks like an atom - it's a short (WORD), so high word // should be zero... if( HIWORD( dw ) != 0 || LOWORD( dw ) == 0 ) { TraceError( TEXT("DecodeFromAtom: value doesn't look like atom (%08lx) - high word should be clear"), dw ); return FALSE; } ATOM atom = (ATOM)dw; TCHAR szAtomName[ ATOM_STRING_LEN + 1 ]; // +1 for terminating NUL int len = GlobalGetAtomName( atom, szAtomName, ARRAYSIZE( szAtomName ) ); if( len != ATOM_STRING_LEN ) { TraceError( TEXT("DecodeFromAtom: atom string is incorrect length - %d instead of %d"), len, ATOM_STRING_LEN ); return FALSE; } // Check for expected format... if( memcmp( szAtomName, ATOM_STRING_PREFIX, ATOM_STRING_PREFIX_LEN * sizeof( TCHAR ) ) != 0 || szAtomName[ ATOM_COLON2_OFFSET ] != ':' || szAtomName[ ATOM_COLON3_OFFSET ] != ':' || szAtomName[ ATOM_NUL_OFFSET ] != '\0' ) { TraceError( TEXT("DecodeFromAtom: atom string has incorrect format (%s)"), szAtomName ); return FALSE; } // Extract the source process's PID and its handle from the atom name... DWORD dwSrcPID; DWORD dwRemoteHandle; if( ! ParseHexStr( & szAtomName[ 5 ], & dwSrcPID ) || ! ParseHexStr( & szAtomName[ 14 ], & dwRemoteHandle ) ) { TraceError( TEXT("DecodeFromAtom: atom string contains bad hex (%s)"), szAtomName ); return FALSE; } // Done with the atom - can delete it now... GlobalDeleteAtom( atom ); *pdwSrcPID = dwSrcPID; *pdwSrcHandle = LongToHandle( dwRemoteHandle ); return TRUE; }