Simon Anciaux
1194 posts
COM IID in C, unresolved external symbol IID_IAudioClient
Edited by Simon Anciaux on Reason: Initial post
I'm trying to compile a function that uses COM (for WASAPI) as a .c file and got some problems (it works as a .cpp file). After looking into the system headers (audioclient.h, mmdeviceapi.h) I found that by defining COBJMACROS before including the files I can get access to a C api. Changing the function calls made the codes compiles, but several IIDs are defined as extern and I don't know what I need to link to or include to get those symbols. Does anyone knows where to find them or if didn't understood how it's supposed to work ?
 1 2 3 For example in audioclient.h EXTERN_C const IID IID_IAudioClient; // main.obj : error LNK2001: unresolved external symbol IID_IAudioClient 

The cpp version of the function uses __uuidof to get those ids, but MSVC says that this keyword is only supported in C++.

  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 // To get the c interface even in .cpp // #define CINTERFACE #define COBJMACROS #include #include b32 sound_create( PlatformSpecific* platform, sound_Format* format, u32* bufferSizeInSamples ) { b32 result = false; IMMDeviceEnumerator* enumerator; /* TODO simon: make sure that Ole32.dll is present by default on windows. */ HMODULE oleDLL = LoadLibrary( "Ole32.dll" ); typedef HRESULT ( *CoCreateInstance_type )( REFCLSID rclsid, LPUNKNOWN pUnkOuter, DWORD dwClsContext, REFIID riid, LPVOID *ppv ); CoCreateInstance_type CoCreateInstance = ( CoCreateInstance_type ) GetProcAddress( oleDLL, "CoCreateInstance" ); #define C_INTERFACE #ifndef C_INTERFACE HRESULT error = CoCreateInstance( __uuidof( MMDeviceEnumerator ), NULL, CLSCTX_ALL, __uuidof( IMMDeviceEnumerator ), ( void** ) &enumerator ); #else HRESULT error = CoCreateInstance( &CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &IID_IMMDeviceEnumerator, ( void** ) &enumerator ); #endif FreeLibrary( oleDLL ); if ( error == S_OK ) { IMMDevice* device; #ifndef C_INTERFACE error = enumerator->GetDefaultAudioEndpoint( eRender, eConsole, &device ); #else error = IMMDeviceEnumerator_GetDefaultAudioEndpoint( enumerator, eRender, eConsole, &device ); #endif if ( error == S_OK ) { #ifndef C_INTERFACE error = device->Activate( __uuidof( IAudioClient ), CLSCTX_ALL, NULL, ( void** ) &platform->audioClient ); #else error = IMMDevice_Activate( device, &IID_IAudioClient, CLSCTX_ALL, NULL, ( void** ) &platform->audioClient ); #endif if ( error == S_OK ) { WAVEFORMATEXTENSIBLE bufferFormat; bufferFormat.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; bufferFormat.Format.nChannels = format->channelCount; bufferFormat.Format.nSamplesPerSec = format->samplesPerSecond; bufferFormat.Format.wBitsPerSample = format->containerSize * 8; bufferFormat.Format.nBlockAlign = bufferFormat.Format.nChannels * format->containerSize; bufferFormat.Format.nAvgBytesPerSec = bufferFormat.Format.nSamplesPerSec * bufferFormat.Format.nBlockAlign; bufferFormat.Format.cbSize = sizeof( bufferFormat ); bufferFormat.Samples.wValidBitsPerSample = format->bitsPerSample; bufferFormat.dwChannelMask = format->channelMask; WAVE_EXTENSIBLE_GUID( WAVE_FORMAT_PCM, ( u8* ) &bufferFormat.SubFormat ); /* NOTE simon: 1 reference_time = 100 nanoseconds = 1 sec / 10 000 000. */ REFERENCE_TIME bufferDuration = 10000000; #ifndef C_INTERFACE error = platform->audioClient->Initialize( AUDCLNT_SHAREMODE_SHARED, 0, bufferDuration, 0, &bufferFormat.Format, 0 ); #else error = IAudioClient_Initialize( platform->audioClient, AUDCLNT_SHAREMODE_SHARED, 0, bufferDuration, 0, &bufferFormat.Format, 0 ); #endif if ( error == S_OK ) { #ifndef C_INTERFACE error = platform->audioClient->GetBufferSize( bufferSizeInSamples ); #else error = IAudioClient_GetBufferSize( platform->audioClient, bufferSizeInSamples ); #endif _assert( *bufferSizeInSamples == format->samplesPerSecond ); #ifndef C_INTERFACE error = platform->audioClient->GetService( __uuidof( IAudioRenderClient ), ( void** ) &platform->audioRenderClient ); #else error = IAudioClient_GetService( platform->audioClient, &IID_IAudioRenderClient, ( void** ) &platform->audioRenderClient ); #endif if ( error == S_OK ) { result = true; } else { #ifndef C_INTERFACE platform->audioClient->Release( ); #else IAudioClient_Release( platform->audioClient ); #endif platform->audioClient = 0; } } else { #ifndef C_INTERFACE platform->audioClient->Release( ); #else IAudioClient_Release( platform->audioClient ); #endif platform->audioClient = 0; } } #ifndef C_INTERFACE device->Release( ); #else IMMDevice_Release( device ); #endif } #ifndef C_INTERFACE enumerator->Release( ); #else IMMDeviceEnumerator_Release( enumerator ); #endif } return result; } 
Mārtiņš Možeiko
2362 posts / 2 projects
COM IID in C, unresolved external symbol IID_IAudioClient
Edited by Mārtiņš Možeiko on
Yea, you need to define them yourself. Sometimes Microsoft gives static library with IID/CLSID values (like dxguid.lib), but sometimes not.

 1 2 3 #include // include this for DEFINE_GUID to create definition, not declaration DEFINE_GUID(my_IID_IAudioClient, 0x1CB9AD4C, 0xDBFA, 0x4c32, 0xB1, 0x78, 0xC2, 0xF5, 0x68, 0xA7, 0x03, 0xB2); 

To get IID value, look up IAudioClient class declaration in header file. it will have it's IID value one line above it.
Simon Anciaux
1194 posts
COM IID in C, unresolved external symbol IID_IAudioClient
Edited by Simon Anciaux on Reason: typos
Thanks.
I'm having another issue. This code is in a header that can be included in a .c or .cpp file and to have only one version of the code I defined CINTERFACE so that mmdeviceapi.h always generates the C functions even in CPP. The problem is that in CPP, REFIID is defined as a reference instead of a pointer and so the interface is different depending on the language.
 1 2 3 4 5 6 7 8 9 // From guiddef.h #ifndef _REFIID_DEFINED #define _REFIID_DEFINED #ifdef __cplusplus #define REFIID const IID & #else #define REFIID const IID * __MIDL_CONST #endif #endif 

I have two workarounds but I'm not sure if they are good ideas:
1. Define REFIID to be an IID pointer myself before including the files that use it. I'm not sure what consequences this have with other things that use COM.
 1 2 #define _REFIID_DEFINED #define REFIID const IID * __MIDL_CONST 

2. Use a macro to have different values if the language is CPP. This seems a better approach, but I'm not sure how it's working because a function prototype that uses a pointer in C is now a function using a reference in CPP. The generated assembly is the same in C and CPP, which makes sense since a reference is a pointer. I would have expected an error because of the different types. Is it working because the 2 function signatures generate the same code and that this code corresponds to the function signature of the dll loaded by COM ?
  1 2 3 4 5 6 7 8 9 10 11 12 13 #ifdef __cplusplus #define IID_IAudioClient_ref IID_IAudioClient #define IID_IAudioRenderClient_ref IID_IAudioRenderClient #else #define IID_IAudioClient_ref &IID_IAudioClient #define IID_IAudioRenderClient_ref &IID_IAudioRenderClient #endif /* In c the prototype is: HRESULT ( STDMETHODCALLTYPE *Activate )( IMMDevice * This, IID* iid, DWORD dwClsCtx, PROPVARIANT *pActivationParams, void **ppInterface ); In cpp the prototype is: HRESULT ( STDMETHODCALLTYPE *Activate )( IMMDevice * This, IID& iid, DWORD dwClsCtx, PROPVARIANT *pActivationParams, void **ppInterface );*/ 

Is there a better way to have REFIID defined as a pointer even in CPP ?
Mārtiņš Možeiko
2362 posts / 2 projects
COM IID in C, unresolved external symbol IID_IAudioClient
If this is intended to be put in header file (single-file-lib) which user includes in their code, then (1) approach seems bad, because it will override some defines which now may not work with rest of users code.

(2) approach should work fine. Yeah, they put in COM APIs only types that can be represented in C. So you won't see std::string or big structure passed by value.

Another approach is to define your own IAudioClient as C structure. Then use C style code that will work with both - C and C++ code
  1 2 3 4 5 6 7 8 9 10 11 struct mrmixer_IAudioClient_vtable { HRESULT (*QueryInferface)(..); ULONG (*AddRef)(...); .... }; struct mrmixer_IAudioClient { struct mrmixer_IAudioClient_vtable* vtbl; }; 
Simon Anciaux
1194 posts
COM IID in C, unresolved external symbol IID_IAudioClient
Thanks again. I ended up copying everything I needed from audioclient.h and mmdeviceapi.h so I don't need those headers anymore. Since it's only for my personal use it should be OK.