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 */