po: Update Korean translation.
[wine/multimedia.git] / dlls / mmdevapi / devenum.c
blob35c6a785168051686aa88cc26f95af09fc5e71f2
1 /*
2 * Copyright 2009 Maarten Lankhorst
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 #include "config.h"
21 #include <stdarg.h>
23 #ifdef HAVE_AL_AL_H
24 #include <AL/al.h>
25 #include <AL/alc.h>
26 #elif defined(HAVE_OPENAL_AL_H)
27 #include <OpenAL/al.h>
28 #include <OpenAL/alc.h>
29 #endif
31 #define NONAMELESSUNION
32 #define COBJMACROS
33 #include "windef.h"
34 #include "winbase.h"
35 #include "winnls.h"
36 #include "winreg.h"
37 #include "wine/debug.h"
38 #include "wine/unicode.h"
40 #include "ole2.h"
41 #include "mmdeviceapi.h"
42 #include "dshow.h"
43 #include "dsound.h"
44 #include "audioclient.h"
45 #include "endpointvolume.h"
46 #include "audiopolicy.h"
48 #include "mmdevapi.h"
49 #include "devpkey.h"
51 WINE_DEFAULT_DEBUG_CHANNEL(mmdevapi);
53 static const WCHAR software_mmdevapi[] =
54 { 'S','o','f','t','w','a','r','e','\\',
55 'M','i','c','r','o','s','o','f','t','\\',
56 'W','i','n','d','o','w','s','\\',
57 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
58 'M','M','D','e','v','i','c','e','s','\\',
59 'A','u','d','i','o',0};
60 static const WCHAR reg_render[] =
61 { 'R','e','n','d','e','r',0 };
62 static const WCHAR reg_capture[] =
63 { 'C','a','p','t','u','r','e',0 };
64 static const WCHAR reg_devicestate[] =
65 { 'D','e','v','i','c','e','S','t','a','t','e',0 };
66 static const WCHAR reg_properties[] =
67 { 'P','r','o','p','e','r','t','i','e','s',0 };
69 static HKEY key_render;
70 static HKEY key_capture;
72 typedef struct MMDevPropStoreImpl
74 IPropertyStore IPropertyStore_iface;
75 LONG ref;
76 MMDevice *parent;
77 DWORD access;
78 } MMDevPropStore;
80 typedef struct MMDevEnumImpl
82 IMMDeviceEnumerator IMMDeviceEnumerator_iface;
83 LONG ref;
84 } MMDevEnumImpl;
86 static MMDevEnumImpl *MMDevEnumerator;
87 static MMDevice **MMDevice_head;
88 static MMDevice *MMDevice_def_rec, *MMDevice_def_play;
89 static DWORD MMDevice_count;
90 static const IMMDeviceEnumeratorVtbl MMDevEnumVtbl;
91 static const IMMDeviceCollectionVtbl MMDevColVtbl;
92 static const IMMDeviceVtbl MMDeviceVtbl;
93 static const IPropertyStoreVtbl MMDevPropVtbl;
94 static const IMMEndpointVtbl MMEndpointVtbl;
96 typedef struct MMDevColImpl
98 IMMDeviceCollection IMMDeviceCollection_iface;
99 LONG ref;
100 EDataFlow flow;
101 DWORD state;
102 } MMDevColImpl;
104 typedef struct IPropertyBagImpl {
105 IPropertyBag IPropertyBag_iface;
106 GUID devguid;
107 } IPropertyBagImpl;
109 static const IPropertyBagVtbl PB_Vtbl;
111 static HRESULT MMDevPropStore_Create(MMDevice *This, DWORD access, IPropertyStore **ppv);
113 static inline MMDevPropStore *impl_from_IPropertyStore(IPropertyStore *iface)
115 return CONTAINING_RECORD(iface, MMDevPropStore, IPropertyStore_iface);
118 static inline MMDevEnumImpl *impl_from_IMMDeviceEnumerator(IMMDeviceEnumerator *iface)
120 return CONTAINING_RECORD(iface, MMDevEnumImpl, IMMDeviceEnumerator_iface);
123 static inline MMDevColImpl *impl_from_IMMDeviceCollection(IMMDeviceCollection *iface)
125 return CONTAINING_RECORD(iface, MMDevColImpl, IMMDeviceCollection_iface);
128 static inline IPropertyBagImpl *impl_from_IPropertyBag(IPropertyBag *iface)
130 return CONTAINING_RECORD(iface, IPropertyBagImpl, IPropertyBag_iface);
133 /* Creates or updates the state of a device
134 * If GUID is null, a random guid will be assigned
135 * and the device will be created
137 static void MMDevice_Create(MMDevice **dev, WCHAR *name, GUID *id, EDataFlow flow, DWORD state, BOOL setdefault)
139 HKEY key, root;
140 MMDevice *cur;
141 WCHAR guidstr[39];
142 DWORD i;
144 for (i = 0; i < MMDevice_count; ++i)
146 cur = MMDevice_head[i];
147 if (cur->flow == flow && !lstrcmpW(cur->alname, name))
149 LONG ret;
150 /* Same device, update state */
151 cur->state = state;
152 StringFromGUID2(&cur->devguid, guidstr, sizeof(guidstr)/sizeof(*guidstr));
153 ret = RegOpenKeyExW(flow == eRender ? key_render : key_capture, guidstr, 0, KEY_WRITE, &key);
154 if (ret == ERROR_SUCCESS)
156 RegSetValueExW(key, reg_devicestate, 0, REG_DWORD, (const BYTE*)&state, sizeof(DWORD));
157 RegCloseKey(key);
159 goto done;
163 /* No device found, allocate new one */
164 cur = HeapAlloc(GetProcessHeap(), 0, sizeof(*cur));
165 if (!cur)
166 return;
167 cur->alname = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(name)+1)*sizeof(WCHAR));
168 if (!cur->alname)
170 HeapFree(GetProcessHeap(), 0, cur);
171 return;
173 lstrcpyW(cur->alname, name);
174 cur->IMMDevice_iface.lpVtbl = &MMDeviceVtbl;
175 cur->IMMEndpoint_iface.lpVtbl = &MMEndpointVtbl;
176 cur->ref = 0;
177 InitializeCriticalSection(&cur->crst);
178 cur->crst.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": MMDevice.crst");
179 cur->flow = flow;
180 cur->state = state;
181 cur->device = NULL;
182 if (!id)
184 id = &cur->devguid;
185 CoCreateGuid(id);
187 cur->devguid = *id;
188 StringFromGUID2(id, guidstr, sizeof(guidstr)/sizeof(*guidstr));
189 if (flow == eRender)
190 root = key_render;
191 else
192 root = key_capture;
193 if (!RegCreateKeyExW(root, guidstr, 0, NULL, 0, KEY_WRITE|KEY_READ, NULL, &key, NULL))
195 HKEY keyprop;
196 RegSetValueExW(key, reg_devicestate, 0, REG_DWORD, (const BYTE*)&state, sizeof(DWORD));
197 if (!RegCreateKeyExW(key, reg_properties, 0, NULL, 0, KEY_WRITE|KEY_READ, NULL, &keyprop, NULL))
199 PROPVARIANT pv;
200 pv.vt = VT_LPWSTR;
201 pv.u.pwszVal = name;
202 MMDevice_SetPropValue(id, flow, (const PROPERTYKEY*)&DEVPKEY_Device_FriendlyName, &pv);
203 MMDevice_SetPropValue(id, flow, (const PROPERTYKEY*)&DEVPKEY_Device_DeviceDesc, &pv);
204 RegCloseKey(keyprop);
206 RegCloseKey(key);
208 if (!MMDevice_head)
209 MMDevice_head = HeapAlloc(GetProcessHeap(), 0, sizeof(*MMDevice_head));
210 else
211 MMDevice_head = HeapReAlloc(GetProcessHeap(), 0, MMDevice_head, sizeof(*MMDevice_head)*(1+MMDevice_count));
212 MMDevice_head[MMDevice_count++] = cur;
214 done:
215 if (setdefault)
217 if (flow == eRender)
218 MMDevice_def_play = cur;
219 else
220 MMDevice_def_rec = cur;
222 if (dev)
223 *dev = cur;
226 static void MMDevice_Destroy(MMDevice *This)
228 DWORD i;
229 TRACE("Freeing %s\n", debugstr_w(This->alname));
230 /* Since this function is called at destruction time, reordering of the list is unimportant */
231 for (i = 0; i < MMDevice_count; ++i)
233 if (MMDevice_head[i] == This)
235 MMDevice_head[i] = MMDevice_head[--MMDevice_count];
236 break;
239 #ifdef HAVE_OPENAL
240 if (This->device)
241 palcCloseDevice(This->device);
242 #endif
243 This->crst.DebugInfo->Spare[0] = 0;
244 DeleteCriticalSection(&This->crst);
245 HeapFree(GetProcessHeap(), 0, This->alname);
246 HeapFree(GetProcessHeap(), 0, This);
249 static inline MMDevice *impl_from_IMMDevice(IMMDevice *iface)
251 return CONTAINING_RECORD(iface, MMDevice, IMMDevice_iface);
254 static HRESULT WINAPI MMDevice_QueryInterface(IMMDevice *iface, REFIID riid, void **ppv)
256 MMDevice *This = impl_from_IMMDevice(iface);
257 TRACE("(%p)->(%s,%p)\n", iface, debugstr_guid(riid), ppv);
259 if (!ppv)
260 return E_POINTER;
261 *ppv = NULL;
262 if (IsEqualIID(riid, &IID_IUnknown)
263 || IsEqualIID(riid, &IID_IMMDevice))
264 *ppv = This;
265 else if (IsEqualIID(riid, &IID_IMMEndpoint))
266 *ppv = &This->IMMEndpoint_iface;
267 if (*ppv)
269 IUnknown_AddRef((IUnknown*)*ppv);
270 return S_OK;
272 WARN("Unknown interface %s\n", debugstr_guid(riid));
273 return E_NOINTERFACE;
276 static ULONG WINAPI MMDevice_AddRef(IMMDevice *iface)
278 MMDevice *This = impl_from_IMMDevice(iface);
279 LONG ref;
281 ref = InterlockedIncrement(&This->ref);
282 TRACE("Refcount now %i\n", ref);
283 return ref;
286 static ULONG WINAPI MMDevice_Release(IMMDevice *iface)
288 MMDevice *This = impl_from_IMMDevice(iface);
289 LONG ref;
291 ref = InterlockedDecrement(&This->ref);
292 TRACE("Refcount now %i\n", ref);
293 return ref;
296 static HRESULT WINAPI MMDevice_Activate(IMMDevice *iface, REFIID riid, DWORD clsctx, PROPVARIANT *params, void **ppv)
298 HRESULT hr = E_NOINTERFACE;
300 #ifdef HAVE_OPENAL
301 MMDevice *This = impl_from_IMMDevice(iface);
303 TRACE("(%p)->(%p,%x,%p,%p)\n", iface, riid, clsctx, params, ppv);
305 if (!ppv)
306 return E_POINTER;
308 if (!openal_loaded)
310 WARN("OpenAL is still not loaded\n");
311 hr = AUDCLNT_E_SERVICE_NOT_RUNNING;
313 else if (IsEqualIID(riid, &IID_IAudioClient))
314 hr = AudioClient_Create(This, (IAudioClient**)ppv);
315 else if (IsEqualIID(riid, &IID_IAudioEndpointVolume))
316 hr = AudioEndpointVolume_Create(This, (IAudioEndpointVolume**)ppv);
317 else if (IsEqualIID(riid, &IID_IAudioSessionManager)
318 || IsEqualIID(riid, &IID_IAudioSessionManager2))
320 FIXME("IID_IAudioSessionManager unsupported\n");
322 else if (IsEqualIID(riid, &IID_IBaseFilter))
324 if (This->flow == eRender)
325 hr = CoCreateInstance(&CLSID_DSoundRender, NULL, clsctx, riid, ppv);
326 else
327 ERR("Not supported for recording?\n");
328 if (SUCCEEDED(hr))
330 IPersistPropertyBag *ppb;
331 hr = IUnknown_QueryInterface((IUnknown*)*ppv, &IID_IPersistPropertyBag, (void*)&ppb);
332 if (SUCCEEDED(hr))
334 /* ::Load cannot assume the interface stays alive after the function returns,
335 * so just create the interface on the stack, saves a lot of complicated code */
336 IPropertyBagImpl bag = { { &PB_Vtbl }, This->devguid };
337 hr = IPersistPropertyBag_Load(ppb, &bag.IPropertyBag_iface, NULL);
338 IPersistPropertyBag_Release(ppb);
339 if (FAILED(hr))
340 IBaseFilter_Release((IBaseFilter*)*ppv);
342 else
344 FIXME("Wine doesn't support IPersistPropertyBag on DSoundRender yet, ignoring..\n");
345 hr = S_OK;
349 else if (IsEqualIID(riid, &IID_IDeviceTopology))
351 FIXME("IID_IDeviceTopology unsupported\n");
353 else if (IsEqualIID(riid, &IID_IDirectSound)
354 || IsEqualIID(riid, &IID_IDirectSound8))
356 if (This->flow == eRender)
357 hr = CoCreateInstance(&CLSID_DirectSound8, NULL, clsctx, riid, ppv);
358 if (SUCCEEDED(hr))
360 hr = IDirectSound_Initialize((IDirectSound*)*ppv, &This->devguid);
361 if (FAILED(hr))
362 IDirectSound_Release((IDirectSound*)*ppv);
365 else if (IsEqualIID(riid, &IID_IDirectSoundCapture)
366 || IsEqualIID(riid, &IID_IDirectSoundCapture8))
368 if (This->flow == eCapture)
369 hr = CoCreateInstance(&CLSID_DirectSoundCapture8, NULL, clsctx, riid, ppv);
370 if (SUCCEEDED(hr))
372 hr = IDirectSoundCapture_Initialize((IDirectSoundCapture*)*ppv, &This->devguid);
373 if (FAILED(hr))
374 IDirectSoundCapture_Release((IDirectSoundCapture*)*ppv);
377 else
378 ERR("Invalid/unknown iid %s\n", debugstr_guid(riid));
379 #else
380 if (!ppv) return E_POINTER;
381 hr = AUDCLNT_E_SERVICE_NOT_RUNNING;
382 #endif
384 if (FAILED(hr))
385 *ppv = NULL;
387 TRACE("Returning %08x\n", hr);
388 return hr;
391 static HRESULT WINAPI MMDevice_OpenPropertyStore(IMMDevice *iface, DWORD access, IPropertyStore **ppv)
393 MMDevice *This = impl_from_IMMDevice(iface);
394 TRACE("(%p)->(%x,%p)\n", This, access, ppv);
396 if (!ppv)
397 return E_POINTER;
398 return MMDevPropStore_Create(This, access, ppv);
401 static HRESULT WINAPI MMDevice_GetId(IMMDevice *iface, WCHAR **itemid)
403 MMDevice *This = impl_from_IMMDevice(iface);
404 WCHAR *str;
405 GUID *id = &This->devguid;
406 static const WCHAR formatW[] = { '{','0','.','0','.','0','.','0','0','0','0','0','0','0','0','}','.',
407 '{','%','0','8','X','-','%','0','4','X','-',
408 '%','0','4','X','-','%','0','2','X','%','0','2','X','-',
409 '%','0','2','X','%','0','2','X','%','0','2','X','%','0','2','X',
410 '%','0','2','X','%','0','2','X','}',0 };
412 TRACE("(%p)->(%p)\n", This, itemid);
413 if (!itemid)
414 return E_POINTER;
415 *itemid = str = CoTaskMemAlloc(56 * sizeof(WCHAR));
416 if (!str)
417 return E_OUTOFMEMORY;
418 wsprintfW( str, formatW, id->Data1, id->Data2, id->Data3,
419 id->Data4[0], id->Data4[1], id->Data4[2], id->Data4[3],
420 id->Data4[4], id->Data4[5], id->Data4[6], id->Data4[7] );
421 return S_OK;
424 static HRESULT WINAPI MMDevice_GetState(IMMDevice *iface, DWORD *state)
426 MMDevice *This = impl_from_IMMDevice(iface);
427 TRACE("(%p)->(%p)\n", iface, state);
429 if (!state)
430 return E_POINTER;
431 *state = This->state;
432 return S_OK;
435 static const IMMDeviceVtbl MMDeviceVtbl =
437 MMDevice_QueryInterface,
438 MMDevice_AddRef,
439 MMDevice_Release,
440 MMDevice_Activate,
441 MMDevice_OpenPropertyStore,
442 MMDevice_GetId,
443 MMDevice_GetState
446 static inline MMDevice *impl_from_IMMEndpoint(IMMEndpoint *iface)
448 return CONTAINING_RECORD(iface, MMDevice, IMMEndpoint_iface);
451 static HRESULT WINAPI MMEndpoint_QueryInterface(IMMEndpoint *iface, REFIID riid, void **ppv)
453 MMDevice *This = impl_from_IMMEndpoint(iface);
454 return IMMDevice_QueryInterface(&This->IMMDevice_iface, riid, ppv);
457 static ULONG WINAPI MMEndpoint_AddRef(IMMEndpoint *iface)
459 MMDevice *This = impl_from_IMMEndpoint(iface);
460 return IMMDevice_AddRef(&This->IMMDevice_iface);
463 static ULONG WINAPI MMEndpoint_Release(IMMEndpoint *iface)
465 MMDevice *This = impl_from_IMMEndpoint(iface);
466 return IMMDevice_Release(&This->IMMDevice_iface);
469 static HRESULT WINAPI MMEndpoint_GetDataFlow(IMMEndpoint *iface, EDataFlow *flow)
471 MMDevice *This = impl_from_IMMEndpoint(iface);
472 if (!flow)
473 return E_POINTER;
474 *flow = This->flow;
475 return S_OK;
478 static const IMMEndpointVtbl MMEndpointVtbl =
480 MMEndpoint_QueryInterface,
481 MMEndpoint_AddRef,
482 MMEndpoint_Release,
483 MMEndpoint_GetDataFlow
486 static HRESULT MMDevCol_Create(IMMDeviceCollection **ppv, EDataFlow flow, DWORD state)
488 MMDevColImpl *This;
490 This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
491 *ppv = NULL;
492 if (!This)
493 return E_OUTOFMEMORY;
494 This->IMMDeviceCollection_iface.lpVtbl = &MMDevColVtbl;
495 This->ref = 1;
496 This->flow = flow;
497 This->state = state;
498 *ppv = &This->IMMDeviceCollection_iface;
499 return S_OK;
502 static void MMDevCol_Destroy(MMDevColImpl *This)
504 HeapFree(GetProcessHeap(), 0, This);
507 static HRESULT WINAPI MMDevCol_QueryInterface(IMMDeviceCollection *iface, REFIID riid, void **ppv)
509 MMDevColImpl *This = impl_from_IMMDeviceCollection(iface);
511 if (!ppv)
512 return E_POINTER;
513 if (IsEqualIID(riid, &IID_IUnknown)
514 || IsEqualIID(riid, &IID_IMMDeviceCollection))
515 *ppv = This;
516 else
517 *ppv = NULL;
518 if (!*ppv)
519 return E_NOINTERFACE;
520 IUnknown_AddRef((IUnknown*)*ppv);
521 return S_OK;
524 static ULONG WINAPI MMDevCol_AddRef(IMMDeviceCollection *iface)
526 MMDevColImpl *This = impl_from_IMMDeviceCollection(iface);
527 LONG ref = InterlockedIncrement(&This->ref);
528 TRACE("Refcount now %i\n", ref);
529 return ref;
532 static ULONG WINAPI MMDevCol_Release(IMMDeviceCollection *iface)
534 MMDevColImpl *This = impl_from_IMMDeviceCollection(iface);
535 LONG ref = InterlockedDecrement(&This->ref);
536 TRACE("Refcount now %i\n", ref);
537 if (!ref)
538 MMDevCol_Destroy(This);
539 return ref;
542 static HRESULT WINAPI MMDevCol_GetCount(IMMDeviceCollection *iface, UINT *numdevs)
544 MMDevColImpl *This = impl_from_IMMDeviceCollection(iface);
545 DWORD i;
547 TRACE("(%p)->(%p)\n", This, numdevs);
548 if (!numdevs)
549 return E_POINTER;
551 *numdevs = 0;
552 for (i = 0; i < MMDevice_count; ++i)
554 MMDevice *cur = MMDevice_head[i];
555 if ((cur->flow == This->flow || This->flow == eAll)
556 && (cur->state & This->state))
557 ++(*numdevs);
559 return S_OK;
562 static HRESULT WINAPI MMDevCol_Item(IMMDeviceCollection *iface, UINT n, IMMDevice **dev)
564 MMDevColImpl *This = impl_from_IMMDeviceCollection(iface);
565 DWORD i = 0, j = 0;
567 TRACE("(%p)->(%u, %p)\n", This, n, dev);
568 if (!dev)
569 return E_POINTER;
571 for (j = 0; j < MMDevice_count; ++j)
573 MMDevice *cur = MMDevice_head[j];
574 if ((cur->flow == This->flow || This->flow == eAll)
575 && (cur->state & This->state)
576 && i++ == n)
578 *dev = &cur->IMMDevice_iface;
579 IMMDevice_AddRef(*dev);
580 return S_OK;
583 WARN("Could not obtain item %u\n", n);
584 *dev = NULL;
585 return E_INVALIDARG;
588 static const IMMDeviceCollectionVtbl MMDevColVtbl =
590 MMDevCol_QueryInterface,
591 MMDevCol_AddRef,
592 MMDevCol_Release,
593 MMDevCol_GetCount,
594 MMDevCol_Item
597 static const WCHAR propkey_formatW[] = {
598 '{','%','0','8','X','-','%','0','4','X','-',
599 '%','0','4','X','-','%','0','2','X','%','0','2','X','-',
600 '%','0','2','X','%','0','2','X','%','0','2','X','%','0','2','X',
601 '%','0','2','X','%','0','2','X','}',',','%','d',0 };
603 static HRESULT MMDevPropStore_OpenPropKey(const GUID *guid, DWORD flow, HKEY *propkey)
605 WCHAR buffer[39];
606 LONG ret;
607 HKEY key;
608 StringFromGUID2(guid, buffer, 39);
609 if ((ret = RegOpenKeyExW(flow == eRender ? key_render : key_capture, buffer, 0, KEY_READ|KEY_WRITE, &key)) != ERROR_SUCCESS)
611 WARN("Opening key %s failed with %u\n", debugstr_w(buffer), ret);
612 return E_FAIL;
614 ret = RegOpenKeyExW(key, reg_properties, 0, KEY_READ|KEY_WRITE, propkey);
615 RegCloseKey(key);
616 if (ret != ERROR_SUCCESS)
618 WARN("Opening key %s failed with %u\n", debugstr_w(reg_properties), ret);
619 return E_FAIL;
621 return S_OK;
624 HRESULT MMDevice_GetPropValue(const GUID *devguid, DWORD flow, REFPROPERTYKEY key, PROPVARIANT *pv)
626 WCHAR buffer[80];
627 const GUID *id = &key->fmtid;
628 DWORD type, size;
629 HRESULT hr = S_OK;
630 HKEY regkey;
631 LONG ret;
633 hr = MMDevPropStore_OpenPropKey(devguid, flow, &regkey);
634 if (FAILED(hr))
635 return hr;
636 wsprintfW( buffer, propkey_formatW, id->Data1, id->Data2, id->Data3,
637 id->Data4[0], id->Data4[1], id->Data4[2], id->Data4[3],
638 id->Data4[4], id->Data4[5], id->Data4[6], id->Data4[7], key->pid );
639 ret = RegGetValueW(regkey, NULL, buffer, RRF_RT_ANY, &type, NULL, &size);
640 if (ret != ERROR_SUCCESS)
642 WARN("Reading %s returned %d\n", debugstr_w(buffer), ret);
643 RegCloseKey(regkey);
644 PropVariantClear(pv);
645 return S_OK;
648 switch (type)
650 case REG_SZ:
652 pv->vt = VT_LPWSTR;
653 pv->u.pwszVal = CoTaskMemAlloc(size);
654 if (!pv->u.pwszVal)
655 hr = E_OUTOFMEMORY;
656 else
657 RegGetValueW(regkey, NULL, buffer, RRF_RT_REG_SZ, NULL, (BYTE*)pv->u.pwszVal, &size);
658 break;
660 case REG_DWORD:
662 pv->vt = VT_UI4;
663 RegGetValueW(regkey, NULL, buffer, RRF_RT_REG_DWORD, NULL, (BYTE*)&pv->u.ulVal, &size);
664 break;
666 case REG_BINARY:
668 pv->vt = VT_BLOB;
669 pv->u.blob.cbSize = size;
670 pv->u.blob.pBlobData = CoTaskMemAlloc(size);
671 if (!pv->u.blob.pBlobData)
672 hr = E_OUTOFMEMORY;
673 else
674 RegGetValueW(regkey, NULL, buffer, RRF_RT_REG_BINARY, NULL, (BYTE*)pv->u.blob.pBlobData, &size);
675 break;
677 default:
678 ERR("Unknown/unhandled type: %u\n", type);
679 PropVariantClear(pv);
680 break;
682 RegCloseKey(regkey);
683 return hr;
686 HRESULT MMDevice_SetPropValue(const GUID *devguid, DWORD flow, REFPROPERTYKEY key, REFPROPVARIANT pv)
688 WCHAR buffer[80];
689 const GUID *id = &key->fmtid;
690 HRESULT hr;
691 HKEY regkey;
692 LONG ret;
694 hr = MMDevPropStore_OpenPropKey(devguid, flow, &regkey);
695 if (FAILED(hr))
696 return hr;
697 wsprintfW( buffer, propkey_formatW, id->Data1, id->Data2, id->Data3,
698 id->Data4[0], id->Data4[1], id->Data4[2], id->Data4[3],
699 id->Data4[4], id->Data4[5], id->Data4[6], id->Data4[7], key->pid );
700 switch (pv->vt)
702 case VT_UI4:
704 ret = RegSetValueExW(regkey, buffer, 0, REG_DWORD, (const BYTE*)&pv->u.ulVal, sizeof(DWORD));
705 break;
707 case VT_BLOB:
709 ret = RegSetValueExW(regkey, buffer, 0, REG_BINARY, pv->u.blob.pBlobData, pv->u.blob.cbSize);
710 TRACE("Blob %p %u\n", pv->u.blob.pBlobData, pv->u.blob.cbSize);
712 break;
714 case VT_LPWSTR:
716 ret = RegSetValueExW(regkey, buffer, 0, REG_SZ, (const BYTE*)pv->u.pwszVal, sizeof(WCHAR)*(1+lstrlenW(pv->u.pwszVal)));
717 break;
719 default:
720 ret = 0;
721 FIXME("Unhandled type %u\n", pv->vt);
722 hr = E_INVALIDARG;
723 break;
725 RegCloseKey(regkey);
726 TRACE("Writing %s returned %u\n", debugstr_w(buffer), ret);
727 return hr;
730 #ifdef HAVE_OPENAL
732 static void openal_setformat(MMDevice *This, DWORD freq)
734 HRESULT hr;
735 PROPVARIANT pv = { VT_EMPTY };
737 hr = MMDevice_GetPropValue(&This->devguid, This->flow, &PKEY_AudioEngine_DeviceFormat, &pv);
738 if (SUCCEEDED(hr) && pv.vt == VT_BLOB)
740 WAVEFORMATEX *pwfx;
741 pwfx = (WAVEFORMATEX*)pv.u.blob.pBlobData;
742 if (pwfx->nSamplesPerSec != freq)
744 pwfx->nSamplesPerSec = freq;
745 pwfx->nAvgBytesPerSec = freq * pwfx->nBlockAlign;
746 MMDevice_SetPropValue(&This->devguid, This->flow, &PKEY_AudioEngine_DeviceFormat, &pv);
748 CoTaskMemFree(pwfx);
750 else
752 WAVEFORMATEXTENSIBLE wfxe;
754 wfxe.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
755 wfxe.Format.nChannels = 2;
756 wfxe.Format.wBitsPerSample = 32;
757 wfxe.Format.nBlockAlign = wfxe.Format.nChannels * wfxe.Format.wBitsPerSample/8;
758 wfxe.Format.nSamplesPerSec = freq;
759 wfxe.Format.nAvgBytesPerSec = wfxe.Format.nSamplesPerSec * wfxe.Format.nBlockAlign;
760 wfxe.Format.cbSize = sizeof(wfxe)-sizeof(WAVEFORMATEX);
761 wfxe.Samples.wValidBitsPerSample = 32;
762 wfxe.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
763 wfxe.dwChannelMask = SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT;
765 pv.vt = VT_BLOB;
766 pv.u.blob.cbSize = sizeof(wfxe);
767 pv.u.blob.pBlobData = (BYTE*)&wfxe;
768 MMDevice_SetPropValue(&This->devguid, This->flow, &PKEY_AudioEngine_DeviceFormat, &pv);
769 MMDevice_SetPropValue(&This->devguid, This->flow, &PKEY_AudioEngine_OEMFormat, &pv);
773 static int blacklist_pulse;
775 static int blacklist(const char *dev) {
776 #ifdef __linux__
777 if (!strncmp(dev, "OSS ", 4))
778 return 1;
779 #endif
780 if (blacklist_pulse && !strncmp(dev, "PulseAudio ", 11))
781 return 1;
782 if (!strncmp(dev, "ALSA ", 5) && strstr(dev, "hw:"))
783 return 1;
784 if (!strncmp(dev, "PortAudio ", 10))
785 return 1;
786 return 0;
789 static void pulse_fixup(const char *devstr, const char **defstr, int render) {
790 static int warned;
791 int default_pulse;
793 if (render && !blacklist_pulse && !local_contexts)
794 blacklist_pulse = 1;
796 if (!blacklist_pulse || !devstr || !*devstr)
797 return;
799 default_pulse = !strncmp(*defstr, "PulseAudio ", 11);
801 while (*devstr && !strncmp(devstr, "PulseAudio ", 11))
802 devstr += strlen(devstr) + 1;
804 /* Could still be a newer version, so check for 1.11 if more devices are enabled */
805 if (render && *devstr) {
806 ALCdevice *dev = palcOpenDevice(devstr);
807 ALCcontext *ctx = palcCreateContext(dev, NULL);
808 if (ctx) {
809 const char *ver;
811 setALContext(ctx);
812 ver = palGetString(AL_VERSION);
813 popALContext();
814 palcDestroyContext(ctx);
816 if (!strcmp(ver, "1.1 ALSOFT 1.11.753")) {
817 blacklist_pulse = 0;
818 palcCloseDevice(dev);
819 return;
822 if (dev)
823 palcCloseDevice(dev);
826 if (!warned++) {
827 ERR("Disabling pulseaudio because of old openal version\n");
828 ERR("Please upgrade to openal-soft v1.12 or newer\n");
830 TRACE("New default: %s\n", devstr);
831 if (default_pulse)
832 *defstr = devstr;
835 static void openal_scanrender(void)
837 WCHAR name[MAX_PATH];
838 ALCdevice *dev;
839 const ALCchar *devstr, *defaultstr;
840 int defblacklisted;
841 EnterCriticalSection(&openal_crst);
842 if (palcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT")) {
843 defaultstr = palcGetString(NULL, ALC_DEFAULT_ALL_DEVICES_SPECIFIER);
844 devstr = palcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER);
845 } else {
846 defaultstr = palcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER);
847 devstr = palcGetString(NULL, ALC_DEVICE_SPECIFIER);
849 pulse_fixup(devstr, &defaultstr, 1);
850 defblacklisted = blacklist(defaultstr);
851 if (defblacklisted)
852 WARN("Disabling blacklist because %s is blacklisted\n", defaultstr);
853 if (devstr)
854 for (; *devstr; devstr += strlen(devstr)+1) {
855 MMDevice *mmdev;
856 MultiByteToWideChar( CP_UNIXCP, 0, devstr, -1,
857 name, sizeof(name)/sizeof(*name)-1 );
858 name[sizeof(name)/sizeof(*name)-1] = 0;
859 /* Only enable blacklist if the default device isn't blacklisted */
860 if (!defblacklisted && blacklist(devstr)) {
861 WARN("Not adding %s: device is blacklisted\n", devstr);
862 continue;
864 TRACE("Adding %s\n", devstr);
865 dev = palcOpenDevice(devstr);
866 MMDevice_Create(&mmdev, name, NULL, eRender, dev ? DEVICE_STATE_ACTIVE : DEVICE_STATE_NOTPRESENT, !strcmp(devstr, defaultstr));
867 if (dev)
869 ALint freq = 44100;
870 palcGetIntegerv(dev, ALC_FREQUENCY, 1, &freq);
871 openal_setformat(mmdev, freq);
872 palcCloseDevice(dev);
874 else
875 WARN("Could not open device: %04x\n", palcGetError(NULL));
877 LeaveCriticalSection(&openal_crst);
880 static void openal_scancapture(void)
882 WCHAR name[MAX_PATH];
883 ALCdevice *dev;
884 const ALCchar *devstr, *defaultstr;
885 int defblacklisted;
887 EnterCriticalSection(&openal_crst);
888 devstr = palcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER);
889 defaultstr = palcGetString(NULL, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER);
890 pulse_fixup(devstr, &defaultstr, 0);
891 defblacklisted = blacklist(defaultstr);
892 if (defblacklisted)
893 WARN("Disabling blacklist because %s is blacklisted\n", defaultstr);
894 if (devstr && *devstr)
895 for (; *devstr; devstr += strlen(devstr)+1) {
896 MMDevice *mmdev;
897 ALint freq = 44100;
898 MultiByteToWideChar( CP_UNIXCP, 0, devstr, -1,
899 name, sizeof(name)/sizeof(*name)-1 );
900 name[sizeof(name)/sizeof(*name)-1] = 0;
901 if (!defblacklisted && blacklist(devstr)) {
902 WARN("Not adding %s: device is blacklisted\n", devstr);
903 continue;
905 TRACE("Adding %s\n", devstr);
906 dev = palcCaptureOpenDevice(devstr, freq, AL_FORMAT_MONO16, 65536);
907 MMDevice_Create(&mmdev, name, NULL, eCapture, dev ? DEVICE_STATE_ACTIVE : DEVICE_STATE_NOTPRESENT, !strcmp(devstr, defaultstr));
908 if (dev) {
909 openal_setformat(mmdev, freq);
910 palcCaptureCloseDevice(dev);
911 } else
912 WARN("Could not open device: %04x\n", palcGetError(NULL));
914 LeaveCriticalSection(&openal_crst);
916 #endif /*HAVE_OPENAL*/
918 HRESULT MMDevEnum_Create(REFIID riid, void **ppv)
920 MMDevEnumImpl *This = MMDevEnumerator;
922 if (!This)
924 DWORD i = 0;
925 HKEY root, cur;
926 LONG ret;
927 DWORD curflow;
929 This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
930 *ppv = NULL;
931 if (!This)
932 return E_OUTOFMEMORY;
933 This->ref = 1;
934 This->IMMDeviceEnumerator_iface.lpVtbl = &MMDevEnumVtbl;
935 MMDevEnumerator = This;
937 ret = RegCreateKeyExW(HKEY_LOCAL_MACHINE, software_mmdevapi, 0, NULL, 0, KEY_WRITE|KEY_READ, NULL, &root, NULL);
938 if (ret == ERROR_SUCCESS)
939 ret = RegCreateKeyExW(root, reg_capture, 0, NULL, 0, KEY_READ|KEY_WRITE, NULL, &key_capture, NULL);
940 if (ret == ERROR_SUCCESS)
941 ret = RegCreateKeyExW(root, reg_render, 0, NULL, 0, KEY_READ|KEY_WRITE, NULL, &key_render, NULL);
942 RegCloseKey(root);
943 cur = key_capture;
944 curflow = eCapture;
945 if (ret != ERROR_SUCCESS)
947 RegCloseKey(key_capture);
948 key_render = key_capture = NULL;
949 WARN("Couldn't create key: %u\n", ret);
950 return E_FAIL;
952 else do {
953 WCHAR guidvalue[39];
954 GUID guid;
955 DWORD len;
956 PROPVARIANT pv = { VT_EMPTY };
958 len = sizeof(guidvalue)/sizeof(guidvalue[0]);
959 ret = RegEnumKeyExW(cur, i++, guidvalue, &len, NULL, NULL, NULL, NULL);
960 if (ret == ERROR_NO_MORE_ITEMS)
962 if (cur == key_capture)
964 cur = key_render;
965 curflow = eRender;
966 i = 0;
967 continue;
969 break;
971 if (ret != ERROR_SUCCESS)
972 continue;
973 if (SUCCEEDED(CLSIDFromString(guidvalue, &guid))
974 && SUCCEEDED(MMDevice_GetPropValue(&guid, curflow, (const PROPERTYKEY*)&DEVPKEY_Device_FriendlyName, &pv))
975 && pv.vt == VT_LPWSTR)
977 MMDevice_Create(NULL, pv.u.pwszVal, &guid, curflow,
978 DEVICE_STATE_NOTPRESENT, FALSE);
979 CoTaskMemFree(pv.u.pwszVal);
981 } while (1);
982 #ifdef HAVE_OPENAL
983 if (openal_loaded)
985 openal_scanrender();
986 openal_scancapture();
988 else
989 FIXME("OpenAL support not enabled, application will not find sound devices\n");
990 #else
991 ERR("OpenAL support not compiled in, application will not find sound devices\n");
992 #endif /*HAVE_OPENAL*/
994 return IUnknown_QueryInterface((IUnknown*)This, riid, ppv);
997 void MMDevEnum_Free(void)
999 while (MMDevice_count)
1000 MMDevice_Destroy(MMDevice_head[0]);
1001 RegCloseKey(key_render);
1002 RegCloseKey(key_capture);
1003 key_render = key_capture = NULL;
1004 HeapFree(GetProcessHeap(), 0, MMDevEnumerator);
1005 MMDevEnumerator = NULL;
1008 static HRESULT WINAPI MMDevEnum_QueryInterface(IMMDeviceEnumerator *iface, REFIID riid, void **ppv)
1010 MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
1012 if (!ppv)
1013 return E_POINTER;
1014 if (IsEqualIID(riid, &IID_IUnknown)
1015 || IsEqualIID(riid, &IID_IMMDeviceEnumerator))
1016 *ppv = This;
1017 else
1018 *ppv = NULL;
1019 if (!*ppv)
1020 return E_NOINTERFACE;
1021 IUnknown_AddRef((IUnknown*)*ppv);
1022 return S_OK;
1025 static ULONG WINAPI MMDevEnum_AddRef(IMMDeviceEnumerator *iface)
1027 MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
1028 LONG ref = InterlockedIncrement(&This->ref);
1029 TRACE("Refcount now %i\n", ref);
1030 return ref;
1033 static ULONG WINAPI MMDevEnum_Release(IMMDeviceEnumerator *iface)
1035 MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
1036 LONG ref = InterlockedDecrement(&This->ref);
1037 if (!ref)
1038 MMDevEnum_Free();
1039 TRACE("Refcount now %i\n", ref);
1040 return ref;
1043 static HRESULT WINAPI MMDevEnum_EnumAudioEndpoints(IMMDeviceEnumerator *iface, EDataFlow flow, DWORD mask, IMMDeviceCollection **devices)
1045 MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
1046 TRACE("(%p)->(%u,%u,%p)\n", This, flow, mask, devices);
1047 if (!devices)
1048 return E_POINTER;
1049 *devices = NULL;
1050 if (flow >= EDataFlow_enum_count)
1051 return E_INVALIDARG;
1052 if (mask & ~DEVICE_STATEMASK_ALL)
1053 return E_INVALIDARG;
1054 return MMDevCol_Create(devices, flow, mask);
1057 static HRESULT WINAPI MMDevEnum_GetDefaultAudioEndpoint(IMMDeviceEnumerator *iface, EDataFlow flow, ERole role, IMMDevice **device)
1059 MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
1060 TRACE("(%p)->(%u,%u,%p)\n", This, flow, role, device);
1062 if (!device)
1063 return E_POINTER;
1064 *device = NULL;
1066 if (flow == eRender)
1067 *device = &MMDevice_def_play->IMMDevice_iface;
1068 else if (flow == eCapture)
1069 *device = &MMDevice_def_rec->IMMDevice_iface;
1070 else
1072 WARN("Unknown flow %u\n", flow);
1073 return E_INVALIDARG;
1076 if (!*device)
1077 return E_NOTFOUND;
1078 IMMDevice_AddRef(*device);
1079 return S_OK;
1082 static HRESULT WINAPI MMDevEnum_GetDevice(IMMDeviceEnumerator *iface, const WCHAR *name, IMMDevice **device)
1084 MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
1085 DWORD i=0;
1086 IMMDevice *dev = NULL;
1088 TRACE("(%p)->(%s,%p)\n", This, debugstr_w(name), device);
1089 for (i = 0; i < MMDevice_count; ++i)
1091 WCHAR *str;
1092 dev = &MMDevice_head[i]->IMMDevice_iface;
1093 IMMDevice_GetId(dev, &str);
1095 if (str && !lstrcmpW(str, name))
1097 CoTaskMemFree(str);
1098 break;
1100 CoTaskMemFree(str);
1102 if (dev)
1104 IUnknown_AddRef(dev);
1105 *device = dev;
1106 return S_OK;
1108 WARN("Could not find device %s\n", debugstr_w(name));
1109 return E_NOTFOUND;
1112 static HRESULT WINAPI MMDevEnum_RegisterEndpointNotificationCallback(IMMDeviceEnumerator *iface, IMMNotificationClient *client)
1114 MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
1115 TRACE("(%p)->(%p)\n", This, client);
1116 FIXME("stub\n");
1117 return S_OK;
1120 static HRESULT WINAPI MMDevEnum_UnregisterEndpointNotificationCallback(IMMDeviceEnumerator *iface, IMMNotificationClient *client)
1122 MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
1123 TRACE("(%p)->(%p)\n", This, client);
1124 FIXME("stub\n");
1125 return S_OK;
1128 static const IMMDeviceEnumeratorVtbl MMDevEnumVtbl =
1130 MMDevEnum_QueryInterface,
1131 MMDevEnum_AddRef,
1132 MMDevEnum_Release,
1133 MMDevEnum_EnumAudioEndpoints,
1134 MMDevEnum_GetDefaultAudioEndpoint,
1135 MMDevEnum_GetDevice,
1136 MMDevEnum_RegisterEndpointNotificationCallback,
1137 MMDevEnum_UnregisterEndpointNotificationCallback
1140 static HRESULT MMDevPropStore_Create(MMDevice *parent, DWORD access, IPropertyStore **ppv)
1142 MMDevPropStore *This;
1143 if (access != STGM_READ
1144 && access != STGM_WRITE
1145 && access != STGM_READWRITE)
1147 WARN("Invalid access %08x\n", access);
1148 return E_INVALIDARG;
1150 This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
1151 *ppv = &This->IPropertyStore_iface;
1152 if (!This)
1153 return E_OUTOFMEMORY;
1154 This->IPropertyStore_iface.lpVtbl = &MMDevPropVtbl;
1155 This->ref = 1;
1156 This->parent = parent;
1157 This->access = access;
1158 return S_OK;
1161 static void MMDevPropStore_Destroy(MMDevPropStore *This)
1163 HeapFree(GetProcessHeap(), 0, This);
1166 static HRESULT WINAPI MMDevPropStore_QueryInterface(IPropertyStore *iface, REFIID riid, void **ppv)
1168 MMDevPropStore *This = impl_from_IPropertyStore(iface);
1170 if (!ppv)
1171 return E_POINTER;
1172 if (IsEqualIID(riid, &IID_IUnknown)
1173 || IsEqualIID(riid, &IID_IPropertyStore))
1174 *ppv = This;
1175 else
1176 *ppv = NULL;
1177 if (!*ppv)
1178 return E_NOINTERFACE;
1179 IUnknown_AddRef((IUnknown*)*ppv);
1180 return S_OK;
1183 static ULONG WINAPI MMDevPropStore_AddRef(IPropertyStore *iface)
1185 MMDevPropStore *This = impl_from_IPropertyStore(iface);
1186 LONG ref = InterlockedIncrement(&This->ref);
1187 TRACE("Refcount now %i\n", ref);
1188 return ref;
1191 static ULONG WINAPI MMDevPropStore_Release(IPropertyStore *iface)
1193 MMDevPropStore *This = impl_from_IPropertyStore(iface);
1194 LONG ref = InterlockedDecrement(&This->ref);
1195 TRACE("Refcount now %i\n", ref);
1196 if (!ref)
1197 MMDevPropStore_Destroy(This);
1198 return ref;
1201 static HRESULT WINAPI MMDevPropStore_GetCount(IPropertyStore *iface, DWORD *nprops)
1203 MMDevPropStore *This = impl_from_IPropertyStore(iface);
1204 WCHAR buffer[50];
1205 DWORD i = 0;
1206 HKEY propkey;
1207 HRESULT hr;
1209 TRACE("(%p)->(%p)\n", iface, nprops);
1210 if (!nprops)
1211 return E_POINTER;
1212 hr = MMDevPropStore_OpenPropKey(&This->parent->devguid, This->parent->flow, &propkey);
1213 if (FAILED(hr))
1214 return hr;
1215 *nprops = 0;
1216 do {
1217 DWORD len = sizeof(buffer)/sizeof(*buffer);
1218 if (RegEnumKeyExW(propkey, i, buffer, &len, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
1219 break;
1220 i++;
1221 } while (0);
1222 RegCloseKey(propkey);
1223 TRACE("Returning %i\n", i);
1224 *nprops = i;
1225 return S_OK;
1228 static HRESULT WINAPI MMDevPropStore_GetAt(IPropertyStore *iface, DWORD prop, PROPERTYKEY *key)
1230 MMDevPropStore *This = impl_from_IPropertyStore(iface);
1231 WCHAR buffer[50];
1232 DWORD len = sizeof(buffer)/sizeof(*buffer);
1233 HRESULT hr;
1234 HKEY propkey;
1236 TRACE("(%p)->(%u,%p)\n", iface, prop, key);
1237 if (!key)
1238 return E_POINTER;
1240 hr = MMDevPropStore_OpenPropKey(&This->parent->devguid, This->parent->flow, &propkey);
1241 if (FAILED(hr))
1242 return hr;
1244 if (RegEnumKeyExW(propkey, prop, buffer, &len, NULL, NULL, NULL, NULL) != ERROR_SUCCESS
1245 || len <= 40)
1247 WARN("GetAt %u failed\n", prop);
1248 return E_INVALIDARG;
1250 RegCloseKey(propkey);
1251 buffer[39] = 0;
1252 CLSIDFromString(buffer, &key->fmtid);
1253 key->pid = atoiW(&buffer[40]);
1254 return S_OK;
1257 static HRESULT WINAPI MMDevPropStore_GetValue(IPropertyStore *iface, REFPROPERTYKEY key, PROPVARIANT *pv)
1259 MMDevPropStore *This = impl_from_IPropertyStore(iface);
1260 TRACE("(%p)->(\"%s,%u\", %p\n", This, debugstr_guid(&key->fmtid), key ? key->pid : 0, pv);
1262 if (!key || !pv)
1263 return E_POINTER;
1264 if (This->access != STGM_READ
1265 && This->access != STGM_READWRITE)
1266 return STG_E_ACCESSDENIED;
1268 /* Special case */
1269 if (IsEqualPropertyKey(*key, PKEY_AudioEndpoint_GUID))
1271 pv->vt = VT_LPWSTR;
1272 pv->u.pwszVal = CoTaskMemAlloc(39 * sizeof(WCHAR));
1273 if (!pv->u.pwszVal)
1274 return E_OUTOFMEMORY;
1275 StringFromGUID2(&This->parent->devguid, pv->u.pwszVal, 39);
1276 return S_OK;
1279 return MMDevice_GetPropValue(&This->parent->devguid, This->parent->flow, key, pv);
1282 static HRESULT WINAPI MMDevPropStore_SetValue(IPropertyStore *iface, REFPROPERTYKEY key, REFPROPVARIANT pv)
1284 MMDevPropStore *This = impl_from_IPropertyStore(iface);
1286 if (!key || !pv)
1287 return E_POINTER;
1289 if (This->access != STGM_WRITE
1290 && This->access != STGM_READWRITE)
1291 return STG_E_ACCESSDENIED;
1292 return MMDevice_SetPropValue(&This->parent->devguid, This->parent->flow, key, pv);
1295 static HRESULT WINAPI MMDevPropStore_Commit(IPropertyStore *iface)
1297 FIXME("stub\n");
1298 return E_NOTIMPL;
1301 static const IPropertyStoreVtbl MMDevPropVtbl =
1303 MMDevPropStore_QueryInterface,
1304 MMDevPropStore_AddRef,
1305 MMDevPropStore_Release,
1306 MMDevPropStore_GetCount,
1307 MMDevPropStore_GetAt,
1308 MMDevPropStore_GetValue,
1309 MMDevPropStore_SetValue,
1310 MMDevPropStore_Commit
1314 /* Property bag for IBaseFilter activation */
1315 static HRESULT WINAPI PB_QueryInterface(IPropertyBag *iface, REFIID riid, void **ppv)
1317 ERR("Should not be called\n");
1318 *ppv = NULL;
1319 return E_NOINTERFACE;
1322 static ULONG WINAPI PB_AddRef(IPropertyBag *iface)
1324 ERR("Should not be called\n");
1325 return 2;
1328 static ULONG WINAPI PB_Release(IPropertyBag *iface)
1330 ERR("Should not be called\n");
1331 return 1;
1334 static HRESULT WINAPI PB_Read(IPropertyBag *iface, LPCOLESTR name, VARIANT *var, IErrorLog *log)
1336 static const WCHAR dsguid[] = { 'D','S','G','u','i','d', 0 };
1337 IPropertyBagImpl *This = impl_from_IPropertyBag(iface);
1338 TRACE("Trying to read %s, type %u\n", debugstr_w(name), var->n1.n2.vt);
1339 if (!lstrcmpW(name, dsguid))
1341 WCHAR guidstr[39];
1342 StringFromGUID2(&This->devguid, guidstr,sizeof(guidstr)/sizeof(*guidstr));
1343 var->n1.n2.vt = VT_BSTR;
1344 var->n1.n2.n3.bstrVal = SysAllocString(guidstr);
1345 return S_OK;
1347 ERR("Unknown property '%s' queried\n", debugstr_w(name));
1348 return E_FAIL;
1351 static HRESULT WINAPI PB_Write(IPropertyBag *iface, LPCOLESTR name, VARIANT *var)
1353 ERR("Should not be called\n");
1354 return E_FAIL;
1357 static const IPropertyBagVtbl PB_Vtbl =
1359 PB_QueryInterface,
1360 PB_AddRef,
1361 PB_Release,
1362 PB_Read,
1363 PB_Write