inc/com_wrapper/com_wrapper.h (339 lines of code) (raw):
// Copyright (C) Microsoft Corporation. All rights reserved.
#ifndef COM_WRAPPER_H
#define COM_WRAPPER_H
#ifdef __cplusplus
#include <cstdlib>
#include <climits>
#else
#include <stdlib.h>
#include <limits.h>
#endif
#include "guiddef.h"
#include "windows.h"
#include "macro_utils/macro_utils.h"
#include "c_logging/logger.h"
#include "c_pal/gballoc_hl.h"
#include "c_pal/gballoc_hl_redirect.h"
#include "c_pal/interlocked.h"
#include "c_pal/refcount.h"
#include "c_pal/uuid.h"
#include "umock_c/umock_c_prod.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct COM_WRAPPER_IF_LOOKUP_ENTRY_TAG
{
IID* riid;
void** if_ptr;
} COM_WRAPPER_IF_LOOKUP_ENTRY;
#ifdef __cplusplus
#define COM_WRAPPER_REFIID(x) (&(x))
#define COM_WRAPPER_REFIID_TO_VALUE(x) (*(x))
#else
#define COM_WRAPPER_REFIID(x) (x)
#define COM_WRAPPER_REFIID_TO_VALUE(x) (x)
#endif
/* These 2 macros are used to check if a type is "void" or not */
#define COM_WRAPPER_TEST_void 0
#define COM_WRAPPER_IS_NOT_VOID(x) \
MU_EXPAND(MU_IF(MU_C2(COM_WRAPPER_TEST_,x), MU_NOEXPAND(1), MU_NOEXPAND(0)))
// this macro extracts from a COM_WRAPPER_INTERFACE(interface_name, ...) entry the interface name
#define EXTRACT_INTERFACE_NAME_ARG_COM_WRAPPER_INTERFACE(interface_name, ...) \
interface_name
// this macro extracts from a COM_WRAPPER_INTERFACE(interface_name, ...) entry the interface entries
#define EXTRACT_INTERFACE_ENTRIES_ARGS_COM_WRAPPER_INTERFACE(interface_name, ...) \
__VA_ARGS__
// this macro generates the setting of the interface Vtbl pointer to the generated Vtbl (by convention it is {wrapped_handle_type}_{implemented_interface}_Vtbl)
// it also sets the lookup table entry
#define ACTUAL_COM_WRAPPER_SET_IF_VTBL(wrapped_handle_type, implemented_interface, ...) \
result->MU_C2(implemented_interface, _if).lpVtbl = &MU_C4(wrapped_handle_type, _, implemented_interface, _Vtbl); \
result->if_lookup_table[i].riid = (IID*)(&MU_C2(IID_, implemented_interface)); \
result->if_lookup_table[i++].if_ptr = (void**)& result->MU_C2(implemented_interface, _if);
// this is a macro that simply takes each interface entry and gets it processed through ACTUAL_COM_WRAPPER_SET_IF_VTBL
#define COM_WRAPPER_SET_IF_VTBL(wrapped_handle_type, the_interface) \
ACTUAL_COM_WRAPPER_SET_IF_VTBL(wrapped_handle_type, MU_C2(EXTRACT_INTERFACE_NAME_ARG_, the_interface))
// The ellipsis is the entire definition of the interfaces for the object, we need to extract the interfaces out of it when we use them
// the common create function sets all the interface Vtbl pointers to the generated Vtbl entries
// it also sets the interface lookup entry to point to the interface so that we can find it during QueryInterface
// ... and it also initializes the ref count (duh)
/* Codes_SRS_COM_WRAPPER_01_007: [ If any error occurs, COM_WRAPPER_CREATE shall return NULL. ]*/
#define IMPLEMENT_COM_WRAPPER_TYPE_CREATE_COMMON(wrapped_handle_type, ...) \
MU_C2(wrapped_handle_type, _COM_WRAPPER)* MU_C2(COM_WRAPPER_TYPE_CREATE_COMMON_, wrapped_handle_type)(wrapped_handle_type wrapped_handle, MU_C2(wrapped_handle_type, _DESTROY_FUNC) destroy_func) \
{ \
MU_C2(wrapped_handle_type, _COM_WRAPPER)* result = (MU_C2(wrapped_handle_type, _COM_WRAPPER)*)malloc(sizeof(MU_C2(wrapped_handle_type, _COM_WRAPPER))); \
if (result == NULL) \
{ \
LogError("malloc failed for COM wrapper object (%zu bytes)", sizeof(MU_C2(wrapped_handle_type, _COM_WRAPPER))); \
} \
else \
{ \
size_t i = 0; \
result->object_data = wrapped_handle; \
result->destroy_func = destroy_func; \
MU_FOR_EACH_1_KEEP_1(COM_WRAPPER_SET_IF_VTBL, wrapped_handle_type, __VA_ARGS__) \
result->if_count = i; \
(void)interlocked_exchange(&result->ref_count, 1); \
} \
return result; \
}
// this generates a constructor which returns a specific type of interface
/* Codes_SRS_COM_WRAPPER_01_002: [ COM_WRAPPER_CREATE shall call the COM object constructor generated by DEFINE_COM_WRAPPER_OBJECT. ]*/
/* Codes_SRS_COM_WRAPPER_01_003: [ COM_WRAPPER_CREATE shall allocate enough memory to hold the reference count, the handle wrapped_handle and the implemented COM interface Vtbl pointers. ]*/
/* Codes_SRS_COM_WRAPPER_01_004: [ COM_WRAPPER_CREATE shall store wrapped_handle and wrapped_handle_destroy for future use. ]*/
/* Codes_SRS_COM_WRAPPER_01_005: [ COM_WRAPPER_CREATE shall initialize the reference count of the COM wrapper to 1. ]*/
/* Codes_SRS_COM_WRAPPER_01_006: [ On success COM_WRAPPER_CREATE shall return an interface pointer for returned_interface. ]*/
#define ACTUAL_IMPLEMENT_COM_WRAPPER_TYPE_CREATE(wrapped_handle_type, implemented_interface) \
MOCKABLE_FUNCTION_WITH_CODE(, implemented_interface*, MU_C4(COM_WRAPPER_TYPE_CREATE_, wrapped_handle_type, _, implemented_interface), wrapped_handle_type, wrapped_handle, MU_C2(wrapped_handle_type, _DESTROY_FUNC), destroy_func) \
{ \
implemented_interface* result; \
MU_C2(wrapped_handle_type, _COM_WRAPPER)* com_wrapper = MU_C2(COM_WRAPPER_TYPE_CREATE_COMMON_, wrapped_handle_type)(wrapped_handle, destroy_func); \
if (com_wrapper == NULL) \
{ \
LogError("COM wrapper allocation failed (interface=" MU_TOSTRING(implemented_interface) ")"); \
result = NULL; \
} \
else \
{ \
result = &com_wrapper->MU_C2(implemented_interface,_if); \
} \
return result; \
} \
MOCKABLE_FUNCTION_WITH_CODE_END(NULL)
// this is a bridge macro that extracts the interface name from each interface definition
#define IMPLEMENT_COM_WRAPPER_TYPE_CREATE(wrapped_handle_type, the_interface) \
ACTUAL_IMPLEMENT_COM_WRAPPER_TYPE_CREATE(wrapped_handle_type, MU_C2(EXTRACT_INTERFACE_NAME_ARG_, the_interface))
/* Codes_SRS_COM_WRAPPER_01_030: [ If COM_object is NULL, COM_WRAPPER_GET_WRAPPED_HANDLE shall return NULL. ]*/
/* Codes_SRS_COM_WRAPPER_01_031: [ Otherwise, it shall return the handle wrapped by COM_object. ]*/
#define ACTUAL_IMPLEMENT_COM_WRAPPER_TYPE_GET_WRAPPED_HANDLE(wrapped_handle_type, implemented_interface) \
wrapped_handle_type MU_C4(COM_WRAPPER_TYPE_GET_WRAPPED_HANDLE_, wrapped_handle_type, _, implemented_interface)(implemented_interface* com_object) \
{ \
wrapped_handle_type result; \
if (com_object == NULL) \
{ \
LogError("Invalid arguments: " MU_TOSTRING(implemented_interface) "* com_object=%p", com_object); \
result = NULL; \
} \
else \
{ \
MU_C2(wrapped_handle_type, _COM_WRAPPER)* com_wrapper = (MU_C2(wrapped_handle_type, _COM_WRAPPER)*)((unsigned char*)com_object - offsetof(MU_C2(wrapped_handle_type, _COM_WRAPPER), MU_C2(implemented_interface, _if))); \
result = com_wrapper->object_data; \
} \
return result; \
}
// this is a bridge macro that extracts the interface name from each interface definition
#define IMPLEMENT_COM_WRAPPER_TYPE_GET_WRAPPED_HANDLE(wrapped_handle_type, the_interface) \
ACTUAL_IMPLEMENT_COM_WRAPPER_TYPE_GET_WRAPPED_HANDLE(wrapped_handle_type, MU_C2(EXTRACT_INTERFACE_NAME_ARG_, the_interface))
// This macro allocates an array of size 0 or 1
// It compares 2 sizes: the size of the interface VTBL structure and the size of the structure with the function pointers given by the user VTBL
// If they match a 1 size array is allocated and filled with a dummy value.
// If they do not match a 0 size array is allocated and filled with a dummy value, resulting in a compiler error
#define ACTUAL_COM_WRAPPER_CHECK_INTERFACE_VTBL(wrapped_handle_type, implemented_interface) \
static const int MU_C5(interface_check_array, _, wrapped_handle_type, _, implemented_interface)[sizeof(MU_C2(implemented_interface, Vtbl)) == sizeof(MU_C4(wrapped_handle_type, _, implemented_interface, _Vtbl_CHECK_STRUCT)) ? 1 : 0] = { 0x42 }; \
// this is a bridge macro that extracts the implemented interface out of the definition
#define COM_WRAPPER_CHECK_INTERFACE_VTBL(wrapped_handle_type, the_interface) \
ACTUAL_COM_WRAPPER_CHECK_INTERFACE_VTBL(wrapped_handle_type, MU_C2(EXTRACT_INTERFACE_NAME_ARG_, the_interface))
#define DECLARE_COM_WRAPPER_TYPE_CREATE(wrapped_handle_type, implemented_interface) \
implemented_interface* MU_C4(COM_WRAPPER_TYPE_CREATE_, wrapped_handle_type, _, implemented_interface)(wrapped_handle_type wrapped_handle, MU_C2(wrapped_handle_type, _DESTROY_FUNC) destroy_func); \
#define DECLARE_COM_WRAPPER_TYPE_GET_WRAPPED_HANDLE(wrapped_handle_type, implemented_interface) \
wrapped_handle_type MU_C4(COM_WRAPPER_TYPE_GET_WRAPPED_HANDLE_, wrapped_handle_type, _, implemented_interface)(implemented_interface* com_object); \
#define ACTUAL_DECLARE_COM_WRAPPER_IF(wrapped_handle_type, implemented_interface, ...) \
DECLARE_COM_WRAPPER_TYPE_CREATE(wrapped_handle_type, implemented_interface) \
DECLARE_COM_WRAPPER_TYPE_GET_WRAPPED_HANDLE(wrapped_handle_type, implemented_interface)
// this macro builds up another function like macro call where the wrapped_handle_type is inserted first in the arg list
#define DECLARE_COM_WRAPPER_IF(wrapped_handle_type, the_interface) \
ACTUAL_DECLARE_COM_WRAPPER_IF(wrapped_handle_type, MU_C2(EXTRACT_INTERFACE_NAME_ARG_, the_interface))
/* Codes_SRS_COM_WRAPPER_01_027: [ DEFINE_COM_WRAPPER_OBJECT shall generate constructor prototypes for the COM object for each implemented interface. ]*/
#define DECLARE_COM_WRAPPER_OBJECT(wrapped_handle_type, ...) \
typedef void (*MU_C2(wrapped_handle_type, _DESTROY_FUNC))(wrapped_handle_type wrapped_handle); \
MU_FOR_EACH_1_KEEP_1(DECLARE_COM_WRAPPER_IF, wrapped_handle_type, __VA_ARGS__) \
// this macro generates one field in the COM wrapper structure
#define ACTUAL_COM_WRAPPER_INTERFACE_FIELD(implemented_interface) \
implemented_interface MU_C2(implemented_interface, _if);
// this macro generates one field in the COM wrapper structure
#define COM_WRAPPER_INTERFACE_FIELD(the_interface) \
ACTUAL_COM_WRAPPER_INTERFACE_FIELD(MU_C2(EXTRACT_INTERFACE_NAME_ARG_, the_interface))
#define TYPEDEF_COM_WRAPPER_OBJECT(wrapped_handle_type, ...) \
typedef struct MU_C2(wrapped_handle_type, _COM_WRAPPER_TAG) \
{ \
MU_FOR_EACH_1(COM_WRAPPER_INTERFACE_FIELD, __VA_ARGS__) \
COM_WRAPPER_IF_LOOKUP_ENTRY if_lookup_table[MU_COUNT_ARG(__VA_ARGS__)]; \
size_t if_count; \
volatile_atomic int32_t ref_count; \
MU_C2(wrapped_handle_type, _DESTROY_FUNC) destroy_func; \
wrapped_handle_type object_data; \
} MU_C2(wrapped_handle_type, _COM_WRAPPER); \
#define COM_WRAPPER_CREATE(wrapped_handle_type, returned_interface, wrapped_handle, wrapped_handle_destroy) \
MU_C4(COM_WRAPPER_TYPE_CREATE_, wrapped_handle_type, _, returned_interface)(wrapped_handle, wrapped_handle_destroy);
/* Codes_SRS_COM_WRAPPER_01_029: [ If riid is NULL, QueryInterface shall fail and return E_POINTER. ]*/
#ifdef __cplusplus
#define CHECK_RIID(implementing_interface)
#else
#define CHECK_RIID(implementing_interface) \
else if (riid == NULL) \
{ \
LogError("Invalid arguments: " MU_TOSTRING(implementing_interface) "* This=%p, _COM_Outptr_ void** ppvObject=%p", This, ppvObject); \
result = E_POINTER; \
}
#endif
/* Codes_SRS_COM_WRAPPER_01_009: [ If This is NULL, QueryInterface shall fail and return E_FAIL. ]*/
/* Codes_SRS_COM_WRAPPER_01_010: [ If ppvObject is NULL, QueryInterface shall fail and return E_POINTER. ]*/
/* Codes_SRS_COM_WRAPPER_01_011: [ If riid is one of the interfaces passed in DEFINE_COM_WRAPPER_OBJECT, QueryInterface shall set ppvObject to the interface pointer corresponding to riid and return S_OK. ]*/
/* Codes_SRS_COM_WRAPPER_01_012: [ If riid does not match any of the interfaces passed in DEFINE_COM_WRAPPER_OBJECT, QueryInterface shall return E_NOINTERFACE. ]*/
#define COM_WRAPPER_IMPLEMENT_QUERYINTERFACE_C_STYLE(wrapped_handle_type, implementing_interface) \
MOCKABLE_FUNCTION_WITH_CODE(STDMETHODCALLTYPE, HRESULT, MU_C4(wrapped_handle_type, _, implementing_interface, _QueryInterface), implementing_interface*, This, const IID*, riid, _COM_Outptr_ void**, ppvObject) \
{ \
HRESULT result; \
if (This == NULL) \
{ \
LogError("Invalid arguments: " MU_TOSTRING(implementing_interface) "* This=%p, const IID*=%p, _COM_Outptr_ void** ppvObject=%p", This, riid, ppvObject); \
result = E_FAIL; \
} \
CHECK_RIID(implementing_interface) \
else if (ppvObject == NULL) \
{ \
LogError("Invalid arguments: " MU_TOSTRING(implementing_interface) "* This=%p, REFIID riid=%p, _COM_Outptr_ void** ppvObject=%p", This, riid, ppvObject); \
result = E_POINTER; \
} \
else \
{ \
MU_C2(wrapped_handle_type, _COM_WRAPPER)* com_wrapper = (MU_C2(wrapped_handle_type, _COM_WRAPPER)*)((unsigned char*)This - offsetof(MU_C2(wrapped_handle_type, _COM_WRAPPER), MU_C2(implementing_interface, _if))); \
size_t i; \
for (i = 0; i < com_wrapper->if_count; i++) \
{ \
if (IsEqualIID(COM_WRAPPER_REFIID_TO_VALUE(riid), COM_WRAPPER_REFIID_TO_VALUE(com_wrapper->if_lookup_table[i].riid))) \
{ \
break; \
} \
} \
if (i == com_wrapper->if_count) \
{ \
LogInfo("Could not find interface %" GUID_FORMAT " on handle %p of type %s implementing %s", GUID_VALUES(*riid), This, MU_TOSTRING(wrapped_handle_type), MU_TOSTRING(implementing_interface)); \
result = E_NOINTERFACE; \
} \
else \
{ \
(void)interlocked_increment(&com_wrapper->ref_count); \
*ppvObject = com_wrapper->if_lookup_table[i].if_ptr; \
result = S_OK; \
} \
} \
return result; \
} \
MOCKABLE_FUNCTION_WITH_CODE_END(S_OK)
// this is a proxy to the C style function. Because C++ REFIID and C REFIID are different
// And umock cannot do address of reference.
#define COM_WRAPPER_IMPLEMENT_QUERYINTERFACE(wrapped_handle_type, implementing_interface) \
static HRESULT STDMETHODCALLTYPE MU_C4(wrapped_handle_type, _, implementing_interface, _QueryInterface_Entry)(implementing_interface* This, REFIID riid, _COM_Outptr_ void** ppvObject) \
{ \
return MU_C4(wrapped_handle_type, _, implementing_interface, _QueryInterface)(This, COM_WRAPPER_REFIID(riid), ppvObject); \
}
/* Codes_SRS_COM_WRAPPER_01_013: [ If This is NULL, AddRef shall fail and return 0. ]*/
/* Codes_SRS_COM_WRAPPER_01_014: [ Otherwise, AddRef shall increment the reference count for the object and return the new reference count. ]*/
#define COM_WRAPPER_IMPLEMENT_ADDREF(wrapped_handle_type, implementing_interface) \
static ULONG MU_C5(internal_, wrapped_handle_type, _, implementing_interface, _AddRef)(implementing_interface* This) \
{ \
ULONG result; \
if (This == NULL) \
{ \
LogError("Invalid arguments: This=%p", This); \
result = 0; \
} \
else \
{ \
MU_C2(wrapped_handle_type, _COM_WRAPPER)* com_wrapper = (MU_C2(wrapped_handle_type, _COM_WRAPPER)*)((unsigned char*)This - offsetof(MU_C2(wrapped_handle_type, _COM_WRAPPER), MU_C2(implementing_interface, _if))); \
result = (ULONG)interlocked_increment(&com_wrapper->ref_count); \
} \
return result; \
} \
MOCKABLE_FUNCTION_WITH_CODE(STDMETHODCALLTYPE, ULONG, MU_C4(wrapped_handle_type, _, implementing_interface, _AddRef), implementing_interface*, This) \
{ \
return MU_C5(internal_, wrapped_handle_type, _, implementing_interface, _AddRef)(This); \
} \
MOCKABLE_FUNCTION_WITH_CODE_END(0)
/* Codes_SRS_COM_WRAPPER_01_015: [ If This is NULL, Release shall fail and return ULONG_MAX. ]*/
/* Codes_SRS_COM_WRAPPER_01_016: [ Otherwise, Release shall decrement the reference count. ]*/
/* Codes_SRS_COM_WRAPPER_01_017: [ If the reference count reaches 0: ]*/
/* Codes_SRS_COM_WRAPPER_01_018: [ - Release shall call the destroy function passed in COM_WRAPPER_CREATE. ]*/
/* Codes_SRS_COM_WRAPPER_01_019: [ - Release shall free the memory associated with the COM wrapper. ]*/
#define COM_WRAPPER_IMPLEMENT_RELEASE(wrapped_handle_type, implementing_interface) \
ULONG MU_C5(internal_, wrapped_handle_type, _, implementing_interface, _Release)(implementing_interface* This) \
{ \
ULONG result; \
if (This == NULL) \
{ \
LogError("Invalid arguments: This=%p", This); \
result = ULONG_MAX; \
} \
else \
{ \
MU_C2(wrapped_handle_type, _COM_WRAPPER)* com_wrapper = (MU_C2(wrapped_handle_type, _COM_WRAPPER)*)((unsigned char*)This - offsetof(MU_C2(wrapped_handle_type, _COM_WRAPPER), MU_C2(implementing_interface, _if))); \
result = (ULONG)interlocked_decrement(&com_wrapper->ref_count); \
if (result == 0) \
{ \
com_wrapper->destroy_func(com_wrapper->object_data); \
free(com_wrapper); \
} \
} \
return result; \
} \
MOCKABLE_FUNCTION_WITH_CODE(STDMETHODCALLTYPE, ULONG, MU_C4(wrapped_handle_type, _, implementing_interface, _Release), implementing_interface*, This) \
{ \
return MU_C5(internal_, wrapped_handle_type, _, implementing_interface, _Release)(This); \
} \
MOCKABLE_FUNCTION_WITH_CODE_END(0)
#define PLACE_ARG_IN_COM_WRAPPER_SIGNATURE(arg_type, arg_name) ,arg_type, arg_name
#define PASS_WRAPPER_ARG(arg_type, arg_name) , arg_name
#define PLACE_ARGS_IN_WRAPPER_SIGNATURE_COM_WRAPPER_FUNCTION_WRAPPER(return_type, wrapper_function_name, ...) \
MU_FOR_EACH_2(PLACE_ARG_IN_COM_WRAPPER_SIGNATURE, __VA_ARGS__)
#define PLACE_ARGS_IN_WRAPPER_CALL_COM_WRAPPER_FUNCTION_WRAPPER(return_type, wrapper_function_name, ...) \
MU_FOR_EACH_2(PASS_WRAPPER_ARG, __VA_ARGS__)
#define EXTRACT_RETURN_TYPE_FROM_WRAPPER_COM_WRAPPER_FUNCTION_WRAPPER(return_type, wrapper_function_name, ...) \
return_type
#define EXTRACT_FUNCTION_NAME_FROM_WRAPPER_COM_WRAPPER_FUNCTION_WRAPPER(return_type, wrapper_function_name, ...) \
wrapper_function_name
// Note: The wrapper implementation and QueryInterface/AddRef/Release implementation use now MOCKABLE_FUNCTION_WITH_CODE,
// which is a temporary solution to mocking these functions. The real solution (pun intended) will come as a separate task:
// https://msazure.visualstudio.com/One/_workitems/edit/4726849
// Once the real support is in umock the wrapper implementation and QueryInterface/AddRef/Release implementations be will be switched
// to using that feature
/* Codes_SRS_COM_WRAPPER_01_020: [ The macro shall generate a wrapper function that looks like: ... ]*/
/* Codes_SRS_COM_WRAPPER_01_021: [ If This is NULL, the wrapper shall return without calling the underlying wrapper. ]*/
/* Codes_SRS_COM_WRAPPER_01_022: [ Otherwise the wrapper shall obtain the handle of the wrapped object. ]*/
/* Codes_SRS_COM_WRAPPER_01_023: [ The wrapper shall call the function wrapped_function_name, while passing the first argument to be the object handle and the rest of the arguments passed to the wrapper. ]*/
/* Codes_SRS_COM_WRAPPER_01_024: [ The wrapper shall return the result of wrapped_function_name. ]*/
#define COM_WRAPPER_IMPLEMENT_WRAPPER(wrapped_handle_type, implementing_interface, call) \
MOCKABLE_FUNCTION_WITH_CODE(STDMETHODCALLTYPE, MU_C2C(EXTRACT_RETURN_TYPE_FROM_WRAPPER_,call), MU_C5(wrapped_handle_type, _, implementing_interface, _, MU_C2C(EXTRACT_FUNCTION_NAME_FROM_WRAPPER_,call)), implementing_interface*, This MU_C2C(PLACE_ARGS_IN_WRAPPER_SIGNATURE_, call)) \
{ \
MU_EXPAND(MU_IF(COM_WRAPPER_IS_NOT_VOID(MU_C2C(EXTRACT_RETURN_TYPE_FROM_WRAPPER_,call)), MU_NOEXPAND(MU_C2C(EXTRACT_RETURN_TYPE_FROM_WRAPPER_,call) result;), MU_NOEXPAND())) \
if (This == NULL) \
{ \
LogError("NULL This"); \
MU_EXPAND(MU_IF(COM_WRAPPER_IS_NOT_VOID(MU_C2C(EXTRACT_RETURN_TYPE_FROM_WRAPPER_,call)), MU_NOEXPAND((void)memset((void*)&result, 0xFF, sizeof(result));), MU_NOEXPAND())) \
} \
else \
{ \
MU_C2C(wrapped_handle_type, _COM_WRAPPER)* com_wrapper = (MU_C2C(wrapped_handle_type, _COM_WRAPPER)*)((unsigned char*)This - offsetof(MU_C2C(wrapped_handle_type, _COM_WRAPPER), MU_C2C(implementing_interface, _if))); \
MU_EXPAND(MU_IF(COM_WRAPPER_IS_NOT_VOID(MU_C2C(EXTRACT_RETURN_TYPE_FROM_WRAPPER_,call)), MU_NOEXPAND(result = ), MU_NOEXPAND())) MU_C2C(EXTRACT_FUNCTION_NAME_FROM_WRAPPER_,call)(com_wrapper->object_data MU_C2C(PLACE_ARGS_IN_WRAPPER_CALL_,call)); \
} \
MU_EXPAND(MU_IF(COM_WRAPPER_IS_NOT_VOID(MU_C2C(EXTRACT_RETURN_TYPE_FROM_WRAPPER_,call)), MU_NOEXPAND(return result;), MU_NOEXPAND())) \
} \
MOCKABLE_FUNCTION_WITH_CODE_END(MU_EXPAND(MU_IF(COM_WRAPPER_IS_NOT_VOID(MU_C2(EXTRACT_RETURN_TYPE_FROM_WRAPPER_,call)), MU_NOEXPAND(0), MU_NOEXPAND())))
#define PASTE_ARG(arg) \
,arg
/* Codes_SRS_COM_WRAPPER_01_008: [ COM_WRAPPER_IUNKNOWN_APIS implements the QueryInterface, AddRef and Release for implementing_interface for the COM wrapper for the handle type wrapped_handle_type. ]*/
#define ACTUAL_IMPLEMENT_IF_ONE_ENTRY_COM_WRAPPER_IUNKNOWN_APIS(wrapped_handle_type, implementing_interface, call) \
COM_WRAPPER_IMPLEMENT_QUERYINTERFACE_C_STYLE(wrapped_handle_type, implementing_interface) \
COM_WRAPPER_IMPLEMENT_QUERYINTERFACE(wrapped_handle_type, implementing_interface) \
COM_WRAPPER_IMPLEMENT_ADDREF(wrapped_handle_type, implementing_interface) \
COM_WRAPPER_IMPLEMENT_RELEASE(wrapped_handle_type, implementing_interface)
#define ACTUAL_IMPLEMENT_IF_ONE_ENTRY_COM_WRAPPER_FUNCTION_WRAPPER(wrapped_handle_type, implementing_interface, call) \
COM_WRAPPER_IMPLEMENT_WRAPPER(wrapped_handle_type, implementing_interface, call)
#define EXTRACT_IF_ENTRY_ARGS_COM_WRAPPER_IUNKNOWN_APIS()
#define EXTRACT_IF_ENTRY_ARGS_COM_WRAPPER_FUNCTION_WRAPPER(...) \
, __VA_ARGS__
#define EXTRACT_IF_ENTRY_ARGS_NO_COMMA_COM_WRAPPER_IUNKNOWN_APIS()
#define EXTRACT_IF_ENTRY_ARGS_NO_COMMA_COM_WRAPPER_FUNCTION_WRAPPER(return_type, wrapper_function_name, ...) \
return_type, wrapper_function_name, __VA_ARGS__
#define IMPLEMENT_IF_ONE_ENTRY_COM_WRAPPER_IUNKNOWN_APIS() \
ACTUAL_IMPLEMENT_IF_ONE_ENTRY_COM_WRAPPER_IUNKNOWN_APIS
#define IMPLEMENT_IF_ONE_ENTRY_COM_WRAPPER_FUNCTION_WRAPPER(...) \
ACTUAL_IMPLEMENT_IF_ONE_ENTRY_COM_WRAPPER_FUNCTION_WRAPPER
// this macro generates the functions for one interface entry
// it either triggers IMPLEMENT_IF_ONE_ENTRY_COM_WRAPPER_IUNKNOWN_APIS or IMPLEMENT_IF_ONE_ENTRY_COM_WRAPPER_FUNCTION_WRAPPER
#define IMPLEMENT_IF_ENTRY(wrapped_handle_type, implementing_interface, call) \
MU_C2B(IMPLEMENT_IF_ONE_ENTRY_, call)(wrapped_handle_type, implementing_interface, call)
#define ACTUAL_IMPLEMENT_IF_ENTRY_IN_STRUCT_ONE_ENTRY_COM_WRAPPER_IUNKNOWN_APIS(wrapped_handle_type, implementing_interface, call) \
MU_C4(wrapped_handle_type, _, implementing_interface , _QueryInterface_Entry), \
MU_C4(wrapped_handle_type, _, implementing_interface , _AddRef), \
MU_C4(wrapped_handle_type, _, implementing_interface , _Release),
#define ACTUAL_IMPLEMENT_IF_ENTRY_IN_STRUCT_ONE_ENTRY_COM_WRAPPER_FUNCTION_WRAPPER(wrapped_handle_type, implementing_interface, call) \
MU_C5(wrapped_handle_type, _, implementing_interface , _, MU_C2(EXTRACT_FUNCTION_NAME_FROM_WRAPPER_, call)),
#define IMPLEMENT_IF_ENTRY_IN_STRUCT_ONE_ENTRY_COM_WRAPPER_IUNKNOWN_APIS() \
ACTUAL_IMPLEMENT_IF_ENTRY_IN_STRUCT_ONE_ENTRY_COM_WRAPPER_IUNKNOWN_APIS
#define IMPLEMENT_IF_ENTRY_IN_STRUCT_ONE_ENTRY_COM_WRAPPER_FUNCTION_WRAPPER(...) \
ACTUAL_IMPLEMENT_IF_ENTRY_IN_STRUCT_ONE_ENTRY_COM_WRAPPER_FUNCTION_WRAPPER
// this macro implements one struct entry in a Vtbl (prefix = {wrapped_handle_type}_{implementing_interface})
// it can either be the 3 known IUnknown APIs (pun intended :-)) or a wrapper function
#define IMPLEMENT_IF_ENTRY_IN_STRUCT(wrapped_handle_type, implementing_interface, call) \
MU_C2(IMPLEMENT_IF_ENTRY_IN_STRUCT_ONE_ENTRY_, call)(wrapped_handle_type, implementing_interface, call)
#define ACTUAL_IMPLEMENT_CHECK_VTBL_ENTRY_COM_WRAPPER_IUNKNOWN_APIS(call) \
void* QueryInterface; \
void* AddRef; \
void* Release; \
#define ACTUAL_IMPLEMENT_CHECK_VTBL_ENTRY_COM_WRAPPER_FUNCTION_WRAPPER(call) \
void* MU_C2(EXTRACT_FUNCTION_NAME_FROM_WRAPPER_, call);
#define IMPLEMENT_CHECK_VTBL_ENTRY_COM_WRAPPER_IUNKNOWN_APIS() \
ACTUAL_IMPLEMENT_CHECK_VTBL_ENTRY_COM_WRAPPER_IUNKNOWN_APIS
#define IMPLEMENT_CHECK_VTBL_ENTRY_COM_WRAPPER_FUNCTION_WRAPPER(...) \
ACTUAL_IMPLEMENT_CHECK_VTBL_ENTRY_COM_WRAPPER_FUNCTION_WRAPPER
// this macro implements one struct entry in a Vtbl (no prefix needed)
// it can either be the 3 known IUnknown APIs (pun intended :-)) or a wrapper function
#define IMPLEMENT_IF_ENTRY_IN_CHECK_VTBL_STRUCT(call) \
MU_C2(IMPLEMENT_CHECK_VTBL_ENTRY_, call)(call)
// this macro implements all struct entries in a Vtbl (prefix = {wrapped_handle_type}_{implementing_interface})
#define COM_WRAPPER_IMPLEMENT_VTBL_STRUCT_ENTRIES(wrapped_handle_type, implementing_interface, ...) \
MU_FOR_EACH_1_KEEP_2(IMPLEMENT_IF_ENTRY_IN_STRUCT, wrapped_handle_type, implementing_interface, __VA_ARGS__)
// this macro implements all struct entries in a Vtbl check struct (prefix = {wrapped_handle_type}_{implementing_interface})
#define COM_WRAPPER_IMPLEMENT_CHECK_VTBL_ENTRIES(...) \
MU_FOR_EACH_1(IMPLEMENT_IF_ENTRY_IN_CHECK_VTBL_STRUCT, __VA_ARGS__)
/* Codes_SRS_COM_WRAPPER_01_025: [ The order of the functions shall be the order of the function pointers in the Vtbl structure for implementing_interface. ]*/
/* Codes_SRS_COM_WRAPPER_01_026: [ COM_WRAPPER_INTERFACE shall ensure that enough entries are in ... to satisfy all functions in the interface. ]*/
#define ACTUAL_COM_WRAPPER_DEFINE_VTBL(wrapped_handle_type, implementing_interface, ...) \
MU_FOR_EACH_1_KEEP_2(IMPLEMENT_IF_ENTRY, wrapped_handle_type, implementing_interface, __VA_ARGS__) \
typedef struct MU_C4(wrapped_handle_type, _, implementing_interface, _Vtbl_CHECK_STRUCT_TAG) \
{ \
COM_WRAPPER_IMPLEMENT_CHECK_VTBL_ENTRIES(__VA_ARGS__) \
} MU_C4(wrapped_handle_type, _, implementing_interface, _Vtbl_CHECK_STRUCT); \
static MU_C2(implementing_interface, Vtbl) MU_C4(wrapped_handle_type, _, implementing_interface, _Vtbl) = \
{ \
COM_WRAPPER_IMPLEMENT_VTBL_STRUCT_ENTRIES(wrapped_handle_type, implementing_interface, __VA_ARGS__) \
};
// this is a bridge macro that calls the actual define Vtbl macro with the proper extracted interface name and the rest of the extracted interface entries
#define COM_WRAPPER_DEFINE_VTBL(wrapped_handle_type, the_interface) \
ACTUAL_COM_WRAPPER_DEFINE_VTBL(wrapped_handle_type, MU_C2A(EXTRACT_INTERFACE_NAME_ARG_, the_interface), MU_C2A(EXTRACT_INTERFACE_ENTRIES_ARGS_, the_interface))
// This generates all the code needed for a COM wrapper:
// - typedef for the structure
// - Vtbl struct and Vtbl check struct for each interface
// - Common create function
// - Create functions for each interface
/* Codes_SRS_COM_WRAPPER_01_001: [ DEFINE_COM_WRAPPER_OBJECT shall generate constructors for the COM object for each implemented interface. ]*/
/* Codes_SRS_COM_WRAPPER_01_028: [ DEFINE_COM_WRAPPER_OBJECT shall generate all the underlying Vtbl structures needed for the COM object. ]*/
/* Note "error C2466: cannot allocate an array of constant size 0" in the expansion of the macro denotes an unbalanced number of methods in the interface (idl) and the implementation (DEFINE_COM_WRAPPER_OBJECT). */
#define DEFINE_COM_WRAPPER_OBJECT(wrapped_handle_type, ...) \
TYPEDEF_COM_WRAPPER_OBJECT(wrapped_handle_type, __VA_ARGS__) \
MU_FOR_EACH_1_KEEP_1(COM_WRAPPER_DEFINE_VTBL, wrapped_handle_type, __VA_ARGS__) \
MU_FOR_EACH_1_KEEP_1(COM_WRAPPER_CHECK_INTERFACE_VTBL, wrapped_handle_type, __VA_ARGS__) \
IMPLEMENT_COM_WRAPPER_TYPE_CREATE_COMMON(wrapped_handle_type, __VA_ARGS__) \
MU_FOR_EACH_1_KEEP_1(IMPLEMENT_COM_WRAPPER_TYPE_CREATE, wrapped_handle_type, __VA_ARGS__) \
MU_FOR_EACH_1_KEEP_1(IMPLEMENT_COM_WRAPPER_TYPE_GET_WRAPPED_HANDLE, wrapped_handle_type, __VA_ARGS__) \
#define COM_WRAPPER_GET_WRAPPED_HANDLE(wrapped_handle_type, interface_name, COM_object) \
MU_C4(COM_WRAPPER_TYPE_GET_WRAPPED_HANDLE_, wrapped_handle_type, _, interface_name)(COM_object)
#ifdef __cplusplus
}
#endif
#endif /* COM_WRAPPER_H */