Marshalling Helper APIs
I am told that our APIs are not part of our documentation. :-( I know for sure we documented these, but I'm told there is a documentation update coming soon, so they must only have made it into the update. My apologies on behalf of Microsoft. Keep an eye out for update notifications inside Platform Builder / Visual Studio.
In the meantime, to help you out, I am posting the comments from some of our code. This is part of %_WINCEROOT%\private\winceos\coreos\core\thunks which does not appear to be part of our shared source. :-(
//
// Access-checks and marshals a buffer pointer from the source process, so
// that it may be accessed by the current process. Returns the marshalled
// pointer. This function allocates resources which must be freed by a
// subsequent call to CeCloseCallerBuffer.
//
// Duplication prevents asynchronous modification of the buffer by the caller.
// If duplication is not required for security purposes, don't use it. Then
// CeOpenCallerBuffer can select the most efficient marshalling method for
// best performance.
//
// If duplication is required, allocates a new heap buffer, copies data from the
// source buffer to the heap buffer [if necessary due to "in" descriptors
// ARG_I* or ARG_IO*], and returns the new heap buffer. If duplication is not
// required, CeOpenCallerBuffer may still duplicate the buffer, or it may
// allocate virtual address space in the current process (VirtualAlloc) and
// point it at the caller process' memory (VirtualCopy) to create an alias to
// the same memory. In all cases, any required write-back to the source buffer
// will be managed by CeCloseCallerBuffer [if necessary due to "out" descriptors
// ARG_IO* or ARG_O*].
//
// This call uses ReadProcessMemory and WriteProcessMemory to do its work. If
// your code is running at a low enough privilege level that it does not have
// access to those APIs, this call will fail with E_ACCESSDENIED.
//
// Does not allocate any resources if the call fails, or if the source buffer
// was NULL. If this call fails for any reason, the pointer returned in
// *ppDestMarshalled is NULL.
//
// This function opens the caller buffer for synchronous access during an API
// call. You must call CeAllocAsynchronousBuffer in order to use the buffer
// returned by CeOpenCallerBuffer asynchronously. Do not close the caller
// buffer until after you have called CeFreeAsynchronousBuffer.
//
// This function is protected by __try/__except so as not to throw an exception
// while accessing the input pointer pSrcUnmarshalled.
//
// Possible return values:
// E_INVALIDARG pSrcUnmarshalled was NULL, the length was 0, or some other
// argument was invalid.
// E_ACCESSDENIED The source buffer was an invalid address, or your code does
// not have sufficient privilege to access the memory.
// E_OUTOFMEMORY The memory allocation failed.
// S_OK The allocation (and duplication, if necessary) succeeded.
//
// It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test
// the return value of this function.
//
HRESULT
CeOpenCallerBuffer(
PVOID* ppDestMarshalled, // Receives a pointer that the current
// process can use to access the buffer
// synchronously.
PVOID pSrcUnmarshalled, // Pointer to the caller's data,
// to be access checked, marshalled, and
// possibly duplicated.
DWORD cbSrc, // Size of the caller's buffer, in bytes.
// If the ArgumentDescriptor is a WSTR
// or ASTR, then a size of 0 can be used.
// If the size of a string is non-zero, then
// it must include the terminating NULL.
DWORD ArgumentDescriptor, // Descriptor explaining what kind of API
// argument the buffer is, eg. ARG_I_WSTR,
// ARG_O_PTR, etc. ARG_DW is NOT a valid
// descriptor for marshalling!
BOOL ForceDuplicate // Set to TRUE to require a temporary heap
// buffer to be allocated in the current
// process. Set to FALSE to allow
// CeOpenCallerBuffer to select the most
// efficient marshalling method.
)
//
// Frees any resources that were allocated by CeOpenCallerBuffer.
// Performs any required write-back to the caller buffer. (Due to "out"
// descriptors ARG_IO* or ARG_O*)
//
// This function is protected by __try/__except so as not to throw an exception
// while accessing the input pointer pSrcUnmarshalled.
//
// Possible return values:
// E_INVALIDARG pSrcUnmarshalled was NULL, the length was 0, or some other
// argument was invalid.
// E_ACCESSDENIED Required write-back could not be performed. If this error
// occurs, resources are still released and the marshalled
// pointer is no longer accessible.
// S_OK The free succeeded.
//
// It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test
// the return value of this function.
//
HRESULT
CeCloseCallerBuffer(
PVOID pDestMarshalled, // Pointer to the buffer that was allocated by
// CeOpenCallerBuffer.
PVOID pSrcUnmarshalled, // The source pointer that was passed to
// CeOpenCallerBuffer.
DWORD cbSrc, // The buffer size that was passed to
// CeOpenCallerBuffer.
DWORD ArgumentDescriptor // The descriptor that was passed to
// CeOpenCallerBuffer.
)
//
// Re-marshals a buffer that was already marshalled by CeOpenCallerBuffer, so
// that the server can use it asynchronously after the API call has returned.
// Call this function synchronously before your API call returns. You can not
// call this function asynchronously. This function allocates resources which
// must be freed by a subsequent call to CeFreeAsynchronousBuffer.
//
// API parameter access (KERNEL MODE ONLY):
// You can use CeAllocAsynchronousBuffer to get asynchronous access to an API
// parameter (which would have already been marshalled by the kernel).
// However if there is any chance that your code will run in user mode, then
// don't do this. Instead follow the user mode instructions below.
// API parameter access (USER MODE):
// To access an API parameter asynchronously, define the API function
// signature so that the parameter is declared as an ARG_DW value, so that
// the kernel does not automatically marshal the parameter for you. Then call
// CeOpenCallerBuffer to marshal the parameter. The asynchronous buffer will
// become inaccessible if you close the marshaled buffer by calling
// CeCloseCallerBuffer, so you should call CeFreeAsynchronousBuffer before
// calling CeCloseCallerBuffer. In other words, do not call
// CeCloseCallerBuffer until after you have called CeFreeAsynchronousBuffer.
//
// CeAllocAsynchronousBuffer is not required for buffers that have been
// duplicated by CeAllocDuplicateBuffer. You do not need to do anything in
// order to use those buffers asynchronously. Those buffers can be used until
// they are closed/freed. But if you choose to call CeAllocAsynchronousBuffer
// on a duplicated buffer, it will work. In that case you must not call
// CeFreeDuplicateBuffer until after you have called CeFreeAsynchronousBuffer.
//
// Does not allocate any resources if the call fails, or if the source buffer
// was NULL. If duplication is required but no memory is allocated, the pointer
// returned by CeFreeAsynchronousBuffer is NULL.
//
// This function is protected by __try/__except so as not to throw an exception
// while accessing the input pointer pSrcSyncMarshalled.
//
// Possible return values:
// E_INVALIDARG pSrcUnmarshalled was NULL, or the length was 0.
// E_ACCESSDENIED The source buffer was an invalid address.
// E_OUTOFMEMORY The memory allocation failed.
// S_OK The allocation (and duplication, if necessary) succeeded.
//
// It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test
// the return value of this function.
//
HRESULT
CeAllocAsynchronousBuffer(
PVOID* ppDestAsyncMarshalled, // Receives a pointer that the current
// process can use to access the buffer
// asynchronously.
PVOID pSrcSyncMarshalled, // Pointer to the buffer that has already
// been marshalled for synchronous access
// by the current process.
DWORD cbSrc, // Size of the marshalled buffer, in bytes.
// If the ArgumentDescriptor is a WSTR
// or ASTR, then a size of 0 can be used.
// If the size of a string is non-zero, then
// it must include the terminating NULL.
DWORD ArgumentDescriptor // Descriptor explaining what kind of API
// argument the buffer is, eg. ARG_I_WSTR,
// ARG_O_PTR, etc. ARG_DW is NOT a valid
// descriptor for marshalling!
)
//
// Frees any resources that were allocated by CeAllocAsynchronousBuffer.
// Performs any required write-back to the source buffer. (Due to "out"
// descriptors ARG_IO* or ARG_O*)
//
// This function is protected by __try/__except so as not to throw an exception
// while accessing the input pointer pSrcSyncMarshalled.
//
// Possible return values:
// E_FAIL Required write-back could not be performed. If this error
// occurs, resources are still released and the marshalled
// pointer is no longer accessible.
// S_OK The allocation (and duplication, if necessary) succeeded.
//
// It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test
// the return value of this function.
//
HRESULT
CeFreeAsynchronousBuffer(
PVOID pDestAsyncMarshalled,// Pointer to the buffer that was allocated by
// CeAllocAsynchronousBuffer.
PVOID pSrcSyncMarshalled, // The source pointer that was passed to
// CeAllocAsynchronousBuffer.
DWORD cbSrc, // The buffer size that was passed to
// CeAllocAsynchronousBuffer.
DWORD ArgumentDescriptor // The descriptor that was passed to
// CeAllocAsynchronousBuffer.
)
//
// Flushes any changed data between source and destination buffer allocated by
// CeAllocAsynchronousBuffer.
// ARG_O_PTR: Writes back data from asynchronous buffer into source buffer.
// ARG_IO_PTR: Writes back data from asynchronous buffer into source buffer.
// Does NOT read from source buffer.
// Others: Fail with ERROR_NOT_SUPPORTED
//
// This function is protected by __try/__except so as not to throw an exception
// while accessing the input pointer pSrcSyncMarshalled.
//
// Possible return values:
// E_FAIL Required read or write-back could not be performed.
// S_OK The read or write succeeded.
//
// It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test
// the return value of this function.
//
HRESULT
CeFlushAsynchronousBuffer(
PVOID pDestAsyncMarshalled,// Pointer to the buffer that was allocated by
// CeAllocAsynchronousBuffer.
PVOID pSrcSyncMarshalled, // The source pointer that was passed to
// CeAllocAsynchronousBuffer.
PVOID pSrcUnmarshalled, // The source pointer that was passed to
// CeOpenCallerBuffer, or NULL if the buffer
// was an API parameter than never came from
// CeOpenCallerBuffer (kernel mode only).
DWORD cbSrc, // The buffer size that was passed to
// CeAllocAsynchronousBuffer.
DWORD ArgumentDescriptor // The descriptor that was passed to
// CeAllocAsynchronousBuffer.
)
//
// This function abstracts the work required to make secure-copies of API
// arguments. Don't use it for buffers other than API arguments. Don't
// expect the duplicated buffer to be accessible after the API call returns.
//
// Allocates a new heap buffer, copies data from the source buffer to the heap
// buffer [if necessary due to "in" descriptors ARG_I* or ARG_IO*], and returns
// the new heap buffer. This function allocates resources which must be freed
// by a subsequent call to CeFreeDuplicateBuffer. Any required write-back to
// the source buffer will be managed by CeFreeDuplicateBuffer [if necessary due
// to "out" descriptors ARG_IO* or ARG_O*].
//
// Duplication prevents asynchronous modification of the buffer by the caller.
// If duplication is not required for security purposes, don't use it. Just
// access the caller's buffer as passed to your API.
//
// Does not allocate any memory if the call fails, or if the source buffer was
// NULL. If no memory is allocated, the pointer returned in *ppDestDuplicate
// is NULL.
//
// Do not use CeAllocDuplicateBuffer with a buffer marshalled by
// CeOpenCallerBuffer. Instead have CeOpenCallerBuffer do the duplication.
//
// You do not need to call CeAllocAsynchronousBuffer in order to use the buffer
// returned by CeAllocDuplicateBuffer asynchronously. The duplicate buffer can
// be used until it is closed by CeCloseCallerBuffer. CeAllocAsynchronousBuffer
// will not work on buffers that have been duplicated by CeAllocDuplicateBuffer.
//
// This function is protected by __try/__except so as not to throw an exception
// while accessing the input pointer pSrcMarshalled.
//
// Possible return values:
// E_INVALIDARG pSrcMarshalled was NULL, or the length was 0.
// E_ACCESSDENIED The source buffer was an invalid address, possibly a pointer
// that has not been marshalled.
// E_OUTOFMEMORY The memory allocation failed.
// S_OK The allocation and copy succeeded.
//
// It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test
// the return value of this function.
//
HRESULT
CeAllocDuplicateBuffer(
PVOID* ppDestDuplicate, // Receives a pointer to a newly-allocated heap
// buffer.
PVOID pSrcMarshalled, // Pointer to the caller's data, that has
// already been marshalled.
DWORD cbSrc, // Size of the caller's buffer, in bytes.
// If the ArgumentDescriptor is a WSTR
// or ASTR, then a size of 0 can be used.
// If the size of a string is non-zero, then
// it must include the terminating NULL.
DWORD ArgumentDescriptor // Descriptor explaining what kind of API
// argument the buffer is, eg. ARG_I_WSTR,
// ARG_O_PTR, etc. ARG_DW is NOT a valid
// descriptor for duplicating!
)
//
// Frees a duplicate buffer that was allocated by CeAllocDuplicateBuffer.
// Performs any required write-back to the source buffer. (Due to "out"
// descriptors ARG_IO* or ARG_O*)
//
// This function is protected by __try/__except so as not to throw an exception
// while accessing the input pointer pSrcMarshalled.
//
// Possible return values:
// E_FAIL Required write-back could not be performed. If this error
// occurs, resources are still released and the duplicated
// pointer is no longer accessible.
// S_OK The free (and write-back, if necessary) succeeded.
//
// It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test
// the return value of this function.
//
HRESULT
CeFreeDuplicateBuffer(
PVOID pDestDuplicate, // Pointer to the buffer that was allocated by
// CeAllocDuplicateBuffer.
PVOID pSrcMarshalled, // The source pointer that was passed to
// CeAllocDuplicateBuffer.
DWORD cbSrc, // The buffer size that was passed to
// CeAllocDuplicateBuffer.
DWORD ArgumentDescriptor // The descriptor that was passed to
// CeAllocDuplicateBuffer.
)
And here are the C++ classes from %_WINCEROOT%\public\common\oak\inc\marshal.hpp:
// This class is a wrapper for CeOpenCallerBuffer / CeCloseCallerBuffer and
// CeAllocAsynchronousBuffer / CeFreeAsynchronousBuffer.
// You should only use it with embedded pointers that have NOT already been
// access-checked or marshalled by the kernel. To duplicate a buffer that has
// already been marshalled, use DuplicatedBuffer_t. To gain asynchronous
// access to a buffer that has already been marshalled, use AsynchronousBuffer_t.
class MarshalledBuffer_t {
public:
//
// Access-checks and marshals a buffer pointer from the source process, so
// that it may be accessed by the current process. Exposes the marshalled
// pointer via the ptr() accessor. Any allocated resources related to the
// marshalling are freed only by a subsequent call to Unmarshal(), or by the
// destructor.
//
// Typically, you would either use the default constructor plus Marshal()
// to marshal the buffer, or you would use the marshalling constructor to
// accomplish the same task. Use the former method if you require an
// HRESULT. Similarly, you can allow the destructor to release marshalling
// resources, or use Unmarshal(). If an HRESULT is required, use the
// Unmarshal function.
//
MarshalledBuffer_t();
~MarshalledBuffer_t();
// Please see the description of CeOpenCallerBuffer and
// CeAllocAsynchronousBuffer for more information about the operation of
// this function.
//
// If marshalling fails, ptr() will return NULL and size() will return zero.
// Otherwise the marshalled buffer will be accessible via ptr() and size().
MarshalledBuffer_t(
PVOID pSrcUnmarshalled,
DWORD cbSrc,
DWORD ArgumentDescriptor,
BOOL ForceDuplicate = TRUE,
BOOL Asynchronous = FALSE
);
// Takes a const pSrcUnmarshalled, can only be used with ARG_I_* types
MarshalledBuffer_t(
PCVOID pSrcUnmarshalled,
DWORD cbSrc,
DWORD ArgumentDescriptor,
BOOL ForceDuplicate = TRUE,
BOOL Asynchronous = FALSE
);
// Please see the description of CeOpenCallerBuffer and
// CeAllocAsynchronousBuffer for more information about the operation of
// this function.
//
// Once a MarshalledBuffer is marshalled (using the marshalling constructor
// or the Marshal() method, it cannot be re-used by calling Marshal(), until
// after Unmarshal() is called. An attempt to do so will return
// ERROR_ALREADY_EXISTS.
//
// If Marshal() fails, ptr() will return NULL and size() will return zero.
// Otherwise the marshalled buffer will be accessible via ptr() and size().
HRESULT
Marshal(
PVOID pSrcUnmarshalled,
DWORD cbSrc,
DWORD ArgumentDescriptor,
BOOL ForceDuplicate = TRUE,
BOOL Asynchronous = FALSE
);
// Takes a const pSrcUnmarshalled, can only be used with ARG_I_* types
HRESULT
Marshal(
PCVOID pSrcUnmarshalled,
DWORD cbSrc,
DWORD ArgumentDescriptor,
BOOL ForceDuplicate = TRUE,
BOOL Asynchronous = FALSE
);
// Please see the description of CeFlushAsynchronousBuffer for more
// information about the operation of this function.
//
// If the buffer has already been unmarshalled, or if it is not an
// asynchronous buffer, Flush will fail with ERROR_INVALID_PARAMETER.
HRESULT Flush();
// Please see the description of CeCloseCallerBuffer for more information
// about the operation of this function.
//
// If the buffer has already been unmarshalled, Unmarshal will fail with
// ERROR_ALREADY_EXISTS.
HRESULT Unmarshal();
// Returns a pointer to the marshalled buffer, or NULL if the buffer
// has not been marshalled or has already been unmarshalled.
LPVOID ptr() const;
// Returns the size of the marshalled buffer, or zero if the buffer
// has not been marshalled or has already been unmarshalled.
DWORD size() const;
};
class DuplicatedBuffer_t;
// This class is a wrapper for CeAllocDuplicateBuffer / CeFreeDuplicateBuffer.
// It should only be called with API arguments that have already been
// access-checked and automatically marshalled (if necessary) by the kernel.
// All other duplication can be done by MarshalledBuffer_t.
//
// You can either call the constructor to do the duplication, or use
// the default constructor and then duplicate using the Allocate() method.
// If an HRESULT is required, use Allocate(). Similarly, you can allow the
// destructor to release the duplicate memory, or use the Free() method. If
// an HRESULT is required, use Free().
//
// If Allocate() fails, ptr() will return NULL and size() will return zero.
// Otherwise the duplicated buffer will be accessible via ptr() and size().
//
// Once a DuplicatedBuffer is allocated (using the constructor or the
// Allocate() method), it cannot be re-used by calling Allocate(), until
// after Free() is called. An attempt to do so will return
// ERROR_ALREADY_EXISTS.
//
// If the buffer is not currently allocated, Free() will fail with
// ERROR_ALREADY_EXISTS.
//
// Please see the description of CeAllocDuplicateBuffer and
// CeFreeDuplicateBuffer for more information about the operation of
// the Allocate() and Free() methods.
//
// Public methods are:
// DuplicatedBuffer_t();
// DuplicatedBuffer_t(PVOID pSrcMarshalled, DWORD cbSrc, DWORD ArgumentDescriptor);
// DuplicatedBuffer_t(PCVOID pSrcMarshalled, DWORD cbSrc, DWORD ArgumentDescriptor);
// ~DuplicatedBuffer_t();
//
// HRESULT Free();
// HRESULT Allocate(PVOID pSrcMarshalled, DWORD cbSrc, DWORD ArgumentDescriptor);
// HRESULT Allocate(PCVOID pSrcMarshalled, DWORD cbSrc, DWORD ArgumentDescriptor);
//
// LPVOID ptr() const;
// DWORD size() const;
class AsynchronousBuffer_t;
// This class is a wrapper for CeAllocAsynchronousBuffer /
// CeFreeAsynchronousBuffer. It is meant to be used with pointers
// already access-checked or marshalled by the kernel that require
// asynchronous buffer access. It should ONLY be used in kernel mode!
// See the description of CeAllocAsynchronousBuffer for information on how to
// access a buffer asynchronously in user mode.
//
// You can either call the constructor to allocate the async buffer, or use
// the default constructor and then use the Allocate() method.
// If an HRESULT is required, use Allocate(). Similarly, you can allow the
// destructor to release the async buffer, or use the Free() method. If
// an HRESULT is required, use Free().
//
// If Allocate() fails, ptr() will return NULL and size() will return zero.
// Otherwise the async buffer will be accessible via ptr() and size().
//
// Once an AsynchronousBuffer is allocated (using the constructor or the
// Allocate() method), it cannot be re-used by calling Allocate(), until
// after Free() is called. An attempt to do so will return
// ERROR_ALREADY_EXISTS.
//
// If the buffer is not currently allocated, Free() will fail with
// ERROR_ALREADY_EXISTS.
//
// Please see the description of CeAllocAsynchronousBuffer and
// CeFreeAsynchronousBuffer for more information about the operation of
// the Allocate() and Free() methods.
//
// Public methods are:
// AsynchronousBuffer_t();
// AsynchronousBuffer_t(PVOID pSrcMarshalled, DWORD cbSrc, DWORD ArgumentDescriptor);
// AsynchronousBuffer_t(PCVOID pSrcMarshalled, DWORD cbSrc, DWORD ArgumentDescriptor);
// ~AsynchronousBuffer_t();
//
// HRESULT Free();
// HRESULT Allocate(PVOID pSrcMarshalled, DWORD cbSrc, DWORD ArgumentDescriptor);
// HRESULT Allocate(PCVOID pSrcMarshalled, DWORD cbSrc, DWORD ArgumentDescriptor);
// HRESULT Flush();
//
// LPVOID ptr() const;
// DWORD size() const;
Also see %_WINCEROOT%\public\common\oak\inc\pkfuncs.h for the ARG_* values to pass as an ArgumentDescriptor:
ARG_I_PTR // input only pointer, size in the next argument
ARG_I_WSTR // input only, unicode string
ARG_I_ASTR // input only, ascii string
ARG_I_PDW // input only, ptr to DWORD
ARG_O_PTR // output only pointer, size in the next argument
ARG_O_PDW // output only, pointer to DWORD
ARG_O_PI64 // output only, pointer to 64 bit value
ARG_IO_PTR // I/O pointer, size in the next argument
ARG_IO_PDW // I/O pointer to DWORD
ARG_IO_PI64 // I/O pointer to 64 bit value