xref: /reactos/dll/win32/mmdevapi/devenum.c (revision 407c54ba)
1  /*
2   * Copyright 2009 Maarten Lankhorst
3   *
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.
8   *
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.
13   *
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
17   */
18  
19  #include "config.h"
20  
21  #include <stdarg.h>
22  
23  #define NONAMELESSUNION
24  #define COBJMACROS
25  #include "windef.h"
26  #include "winbase.h"
27  #include "winnls.h"
28  #include "winreg.h"
29  #include "wine/debug.h"
30  #include "wine/list.h"
31  #include "wine/unicode.h"
32  
33  #include "initguid.h"
34  #include "ole2.h"
35  #include "mmdeviceapi.h"
36  #include "dshow.h"
37  #include "dsound.h"
38  #include "audioclient.h"
39  #include "endpointvolume.h"
40  #include "audiopolicy.h"
41  
42  #include "mmdevapi.h"
43  #include "devpkey.h"
44  
45  WINE_DEFAULT_DEBUG_CHANNEL(mmdevapi);
46  
47  static const WCHAR software_mmdevapi[] =
48      { 'S','o','f','t','w','a','r','e','\\',
49        'M','i','c','r','o','s','o','f','t','\\',
50        'W','i','n','d','o','w','s','\\',
51        'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
52        'M','M','D','e','v','i','c','e','s','\\',
53        'A','u','d','i','o',0};
54  static const WCHAR reg_render[] =
55      { 'R','e','n','d','e','r',0 };
56  static const WCHAR reg_capture[] =
57      { 'C','a','p','t','u','r','e',0 };
58  static const WCHAR reg_devicestate[] =
59      { 'D','e','v','i','c','e','S','t','a','t','e',0 };
60  static const WCHAR reg_properties[] =
61      { 'P','r','o','p','e','r','t','i','e','s',0 };
62  static const WCHAR slashW[] = {'\\',0};
63  static const WCHAR reg_out_nameW[] = {'D','e','f','a','u','l','t','O','u','t','p','u','t',0};
64  static const WCHAR reg_vout_nameW[] = {'D','e','f','a','u','l','t','V','o','i','c','e','O','u','t','p','u','t',0};
65  static const WCHAR reg_in_nameW[] = {'D','e','f','a','u','l','t','I','n','p','u','t',0};
66  static const WCHAR reg_vin_nameW[] = {'D','e','f','a','u','l','t','V','o','i','c','e','I','n','p','u','t',0};
67  
68  static HKEY key_render;
69  static HKEY key_capture;
70  
71  typedef struct MMDevPropStoreImpl
72  {
73      IPropertyStore IPropertyStore_iface;
74      LONG ref;
75      MMDevice *parent;
76      DWORD access;
77  } MMDevPropStore;
78  
79  typedef struct MMDevEnumImpl
80  {
81      IMMDeviceEnumerator IMMDeviceEnumerator_iface;
82      LONG ref;
83  } MMDevEnumImpl;
84  
85  static MMDevEnumImpl *MMDevEnumerator;
86  static MMDevice **MMDevice_head;
87  static MMDevice *MMDevice_def_rec, *MMDevice_def_play;
88  static DWORD MMDevice_count;
89  static const IMMDeviceEnumeratorVtbl MMDevEnumVtbl;
90  static const IMMDeviceCollectionVtbl MMDevColVtbl;
91  static const IMMDeviceVtbl MMDeviceVtbl;
92  static const IPropertyStoreVtbl MMDevPropVtbl;
93  static const IMMEndpointVtbl MMEndpointVtbl;
94  
95  static IMMDevice info_device;
96  
97  typedef struct MMDevColImpl
98  {
99      IMMDeviceCollection IMMDeviceCollection_iface;
100      LONG ref;
101      EDataFlow flow;
102      DWORD state;
103  } MMDevColImpl;
104  
105  typedef struct IPropertyBagImpl {
106      IPropertyBag IPropertyBag_iface;
107      GUID devguid;
108  } IPropertyBagImpl;
109  
110  static const IPropertyBagVtbl PB_Vtbl;
111  
112  static HRESULT MMDevPropStore_Create(MMDevice *This, DWORD access, IPropertyStore **ppv);
113  
114  static inline MMDevPropStore *impl_from_IPropertyStore(IPropertyStore *iface)
115  {
116      return CONTAINING_RECORD(iface, MMDevPropStore, IPropertyStore_iface);
117  }
118  
119  static inline MMDevEnumImpl *impl_from_IMMDeviceEnumerator(IMMDeviceEnumerator *iface)
120  {
121      return CONTAINING_RECORD(iface, MMDevEnumImpl, IMMDeviceEnumerator_iface);
122  }
123  
124  static inline MMDevColImpl *impl_from_IMMDeviceCollection(IMMDeviceCollection *iface)
125  {
126      return CONTAINING_RECORD(iface, MMDevColImpl, IMMDeviceCollection_iface);
127  }
128  
129  static inline IPropertyBagImpl *impl_from_IPropertyBag(IPropertyBag *iface)
130  {
131      return CONTAINING_RECORD(iface, IPropertyBagImpl, IPropertyBag_iface);
132  }
133  
134  static const WCHAR propkey_formatW[] = {
135      '{','%','0','8','X','-','%','0','4','X','-',
136      '%','0','4','X','-','%','0','2','X','%','0','2','X','-',
137      '%','0','2','X','%','0','2','X','%','0','2','X','%','0','2','X',
138      '%','0','2','X','%','0','2','X','}',',','%','d',0 };
139  
140  static HRESULT MMDevPropStore_OpenPropKey(const GUID *guid, DWORD flow, HKEY *propkey)
141  {
142      WCHAR buffer[39];
143      LONG ret;
144      HKEY key;
145      StringFromGUID2(guid, buffer, 39);
146      if ((ret = RegOpenKeyExW(flow == eRender ? key_render : key_capture, buffer, 0, KEY_READ|KEY_WRITE|KEY_WOW64_64KEY, &key)) != ERROR_SUCCESS)
147      {
148          WARN("Opening key %s failed with %u\n", debugstr_w(buffer), ret);
149          return E_FAIL;
150      }
151      ret = RegOpenKeyExW(key, reg_properties, 0, KEY_READ|KEY_WRITE|KEY_WOW64_64KEY, propkey);
152      RegCloseKey(key);
153      if (ret != ERROR_SUCCESS)
154      {
155          WARN("Opening key %s failed with %u\n", debugstr_w(reg_properties), ret);
156          return E_FAIL;
157      }
158      return S_OK;
159  }
160  
161  static HRESULT MMDevice_GetPropValue(const GUID *devguid, DWORD flow, REFPROPERTYKEY key, PROPVARIANT *pv)
162  {
163      WCHAR buffer[80];
164      const GUID *id = &key->fmtid;
165      DWORD type, size;
166      HRESULT hr = S_OK;
167      HKEY regkey;
168      LONG ret;
169  
170      hr = MMDevPropStore_OpenPropKey(devguid, flow, &regkey);
171      if (FAILED(hr))
172          return hr;
173      wsprintfW( buffer, propkey_formatW, id->Data1, id->Data2, id->Data3,
174                 id->Data4[0], id->Data4[1], id->Data4[2], id->Data4[3],
175                 id->Data4[4], id->Data4[5], id->Data4[6], id->Data4[7], key->pid );
176      ret = RegGetValueW(regkey, NULL, buffer, RRF_RT_ANY, &type, NULL, &size);
177      if (ret != ERROR_SUCCESS)
178      {
179          WARN("Reading %s returned %d\n", debugstr_w(buffer), ret);
180          RegCloseKey(regkey);
181          PropVariantClear(pv);
182          return S_OK;
183      }
184  
185      switch (type)
186      {
187          case REG_SZ:
188          {
189              pv->vt = VT_LPWSTR;
190              pv->u.pwszVal = CoTaskMemAlloc(size);
191              if (!pv->u.pwszVal)
192                  hr = E_OUTOFMEMORY;
193              else
194                  RegGetValueW(regkey, NULL, buffer, RRF_RT_REG_SZ, NULL, (BYTE*)pv->u.pwszVal, &size);
195              break;
196          }
197          case REG_DWORD:
198          {
199              pv->vt = VT_UI4;
200              RegGetValueW(regkey, NULL, buffer, RRF_RT_REG_DWORD, NULL, (BYTE*)&pv->u.ulVal, &size);
201              break;
202          }
203          case REG_BINARY:
204          {
205              pv->vt = VT_BLOB;
206              pv->u.blob.cbSize = size;
207              pv->u.blob.pBlobData = CoTaskMemAlloc(size);
208              if (!pv->u.blob.pBlobData)
209                  hr = E_OUTOFMEMORY;
210              else
211                  RegGetValueW(regkey, NULL, buffer, RRF_RT_REG_BINARY, NULL, (BYTE*)pv->u.blob.pBlobData, &size);
212              break;
213          }
214          default:
215              ERR("Unknown/unhandled type: %u\n", type);
216              PropVariantClear(pv);
217              break;
218      }
219      RegCloseKey(regkey);
220      return hr;
221  }
222  
223  static HRESULT MMDevice_SetPropValue(const GUID *devguid, DWORD flow, REFPROPERTYKEY key, REFPROPVARIANT pv)
224  {
225      WCHAR buffer[80];
226      const GUID *id = &key->fmtid;
227      HRESULT hr;
228      HKEY regkey;
229      LONG ret;
230  
231      hr = MMDevPropStore_OpenPropKey(devguid, flow, &regkey);
232      if (FAILED(hr))
233          return hr;
234      wsprintfW( buffer, propkey_formatW, id->Data1, id->Data2, id->Data3,
235                 id->Data4[0], id->Data4[1], id->Data4[2], id->Data4[3],
236                 id->Data4[4], id->Data4[5], id->Data4[6], id->Data4[7], key->pid );
237      switch (pv->vt)
238      {
239          case VT_UI4:
240          {
241              ret = RegSetValueExW(regkey, buffer, 0, REG_DWORD, (const BYTE*)&pv->u.ulVal, sizeof(DWORD));
242              break;
243          }
244          case VT_BLOB:
245          {
246              ret = RegSetValueExW(regkey, buffer, 0, REG_BINARY, pv->u.blob.pBlobData, pv->u.blob.cbSize);
247              TRACE("Blob %p %u\n", pv->u.blob.pBlobData, pv->u.blob.cbSize);
248  
249              break;
250          }
251          case VT_LPWSTR:
252          {
253              ret = RegSetValueExW(regkey, buffer, 0, REG_SZ, (const BYTE*)pv->u.pwszVal, sizeof(WCHAR)*(1+lstrlenW(pv->u.pwszVal)));
254              break;
255          }
256          default:
257              ret = 0;
258              FIXME("Unhandled type %u\n", pv->vt);
259              hr = E_INVALIDARG;
260              break;
261      }
262      RegCloseKey(regkey);
263      TRACE("Writing %s returned %u\n", debugstr_w(buffer), ret);
264      return hr;
265  }
266  
267  static HRESULT set_driver_prop_value(GUID *id, const EDataFlow flow, const PROPERTYKEY *prop)
268  {
269      HRESULT hr;
270      PROPVARIANT pv;
271  
272      if (!drvs.pGetPropValue)
273          return E_NOTIMPL;
274  
275      hr = drvs.pGetPropValue(id, prop, &pv);
276  
277      if (SUCCEEDED(hr))
278      {
279          MMDevice_SetPropValue(id, flow, prop, &pv);
280          PropVariantClear(&pv);
281      }
282  
283      return hr;
284  }
285  
286  /* Creates or updates the state of a device
287   * If GUID is null, a random guid will be assigned
288   * and the device will be created
289   */
290  static MMDevice *MMDevice_Create(WCHAR *name, GUID *id, EDataFlow flow, DWORD state, BOOL setdefault)
291  {
292      HKEY key, root;
293      MMDevice *cur = NULL;
294      WCHAR guidstr[39];
295      DWORD i;
296  
297      static const PROPERTYKEY deviceinterface_key = {
298          {0x233164c8, 0x1b2c, 0x4c7d, {0xbc, 0x68, 0xb6, 0x71, 0x68, 0x7a, 0x25, 0x67}}, 1
299      };
300  
301      static const PROPERTYKEY devicepath_key = {
302          {0xb3f8fa53, 0x0004, 0x438e, {0x90, 0x03, 0x51, 0xa4, 0x6e, 0x13, 0x9b, 0xfc}}, 2
303      };
304  
305      for (i = 0; i < MMDevice_count; ++i)
306      {
307          MMDevice *device = MMDevice_head[i];
308          if (device->flow == flow && IsEqualGUID(&device->devguid, id)){
309              cur = device;
310              break;
311          }
312      }
313  
314      if(!cur){
315          /* No device found, allocate new one */
316          cur = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*cur));
317          if (!cur)
318              return NULL;
319  
320          cur->IMMDevice_iface.lpVtbl = &MMDeviceVtbl;
321          cur->IMMEndpoint_iface.lpVtbl = &MMEndpointVtbl;
322  
323          InitializeCriticalSection(&cur->crst);
324          cur->crst.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": MMDevice.crst");
325  
326          if (!MMDevice_head)
327              MMDevice_head = HeapAlloc(GetProcessHeap(), 0, sizeof(*MMDevice_head));
328          else
329              MMDevice_head = HeapReAlloc(GetProcessHeap(), 0, MMDevice_head, sizeof(*MMDevice_head)*(1+MMDevice_count));
330          MMDevice_head[MMDevice_count++] = cur;
331      }else if(cur->ref > 0)
332          WARN("Modifying an MMDevice with postitive reference count!\n");
333  
334      HeapFree(GetProcessHeap(), 0, cur->drv_id);
335      cur->drv_id = name;
336  
337      cur->flow = flow;
338      cur->state = state;
339      cur->devguid = *id;
340  
341      StringFromGUID2(&cur->devguid, guidstr, sizeof(guidstr)/sizeof(*guidstr));
342  
343      if (flow == eRender)
344          root = key_render;
345      else
346          root = key_capture;
347  
348      if (RegCreateKeyExW(root, guidstr, 0, NULL, 0, KEY_WRITE|KEY_READ|KEY_WOW64_64KEY, NULL, &key, NULL) == ERROR_SUCCESS)
349      {
350          HKEY keyprop;
351          RegSetValueExW(key, reg_devicestate, 0, REG_DWORD, (const BYTE*)&state, sizeof(DWORD));
352          if (!RegCreateKeyExW(key, reg_properties, 0, NULL, 0, KEY_WRITE|KEY_READ|KEY_WOW64_64KEY, NULL, &keyprop, NULL))
353          {
354              PROPVARIANT pv;
355  
356              pv.vt = VT_LPWSTR;
357              pv.u.pwszVal = name;
358              MMDevice_SetPropValue(id, flow, (const PROPERTYKEY*)&DEVPKEY_Device_FriendlyName, &pv);
359              MMDevice_SetPropValue(id, flow, (const PROPERTYKEY*)&DEVPKEY_DeviceInterface_FriendlyName, &pv);
360              MMDevice_SetPropValue(id, flow, (const PROPERTYKEY*)&DEVPKEY_Device_DeviceDesc, &pv);
361  
362              pv.u.pwszVal = guidstr;
363              MMDevice_SetPropValue(id, flow, &deviceinterface_key, &pv);
364  
365              set_driver_prop_value(id, flow, &devicepath_key);
366  
367              if (FAILED(set_driver_prop_value(id, flow, &PKEY_AudioEndpoint_FormFactor)))
368              {
369                  pv.vt = VT_UI4;
370                  pv.u.ulVal = (flow == eCapture) ? Microphone : Speakers;
371  
372                  MMDevice_SetPropValue(id, flow, &PKEY_AudioEndpoint_FormFactor, &pv);
373              }
374  
375              if (flow != eCapture)
376              {
377                  PROPVARIANT pv2;
378  
379                  PropVariantInit(&pv2);
380  
381                  /* make read-write by not overwriting if already set */
382                  if (FAILED(MMDevice_GetPropValue(id, flow, &PKEY_AudioEndpoint_PhysicalSpeakers, &pv2)) || pv2.vt != VT_UI4)
383                      set_driver_prop_value(id, flow, &PKEY_AudioEndpoint_PhysicalSpeakers);
384  
385                  PropVariantClear(&pv2);
386              }
387  
388              RegCloseKey(keyprop);
389          }
390          RegCloseKey(key);
391      }
392  
393      if (setdefault)
394      {
395          if (flow == eRender)
396              MMDevice_def_play = cur;
397          else
398              MMDevice_def_rec = cur;
399      }
400      return cur;
401  }
402  
403  static HRESULT load_devices_from_reg(void)
404  {
405      DWORD i = 0;
406      HKEY root, cur;
407      LONG ret;
408      DWORD curflow;
409  
410      ret = RegCreateKeyExW(HKEY_LOCAL_MACHINE, software_mmdevapi, 0, NULL, 0, KEY_WRITE|KEY_READ|KEY_WOW64_64KEY, NULL, &root, NULL);
411      if (ret == ERROR_SUCCESS)
412          ret = RegCreateKeyExW(root, reg_capture, 0, NULL, 0, KEY_READ|KEY_WRITE|KEY_WOW64_64KEY, NULL, &key_capture, NULL);
413      if (ret == ERROR_SUCCESS)
414          ret = RegCreateKeyExW(root, reg_render, 0, NULL, 0, KEY_READ|KEY_WRITE|KEY_WOW64_64KEY, NULL, &key_render, NULL);
415      RegCloseKey(root);
416      cur = key_capture;
417      curflow = eCapture;
418      if (ret != ERROR_SUCCESS)
419      {
420          RegCloseKey(key_capture);
421          key_render = key_capture = NULL;
422          WARN("Couldn't create key: %u\n", ret);
423          return E_FAIL;
424      }
425  
426      do {
427          WCHAR guidvalue[39];
428          GUID guid;
429          DWORD len;
430          PROPVARIANT pv = { VT_EMPTY };
431  
432          len = sizeof(guidvalue)/sizeof(guidvalue[0]);
433          ret = RegEnumKeyExW(cur, i++, guidvalue, &len, NULL, NULL, NULL, NULL);
434          if (ret == ERROR_NO_MORE_ITEMS)
435          {
436              if (cur == key_capture)
437              {
438                  cur = key_render;
439                  curflow = eRender;
440                  i = 0;
441                  continue;
442              }
443              break;
444          }
445          if (ret != ERROR_SUCCESS)
446              continue;
447          if (SUCCEEDED(CLSIDFromString(guidvalue, &guid))
448              && SUCCEEDED(MMDevice_GetPropValue(&guid, curflow, (const PROPERTYKEY*)&DEVPKEY_Device_FriendlyName, &pv))
449              && pv.vt == VT_LPWSTR)
450          {
451              DWORD size_bytes = (strlenW(pv.u.pwszVal) + 1) * sizeof(WCHAR);
452              WCHAR *name = HeapAlloc(GetProcessHeap(), 0, size_bytes);
453              memcpy(name, pv.u.pwszVal, size_bytes);
454              MMDevice_Create(name, &guid, curflow,
455                      DEVICE_STATE_NOTPRESENT, FALSE);
456              CoTaskMemFree(pv.u.pwszVal);
457          }
458      } while (1);
459  
460      return S_OK;
461  }
462  
463  static HRESULT set_format(MMDevice *dev)
464  {
465      HRESULT hr;
466      IAudioClient *client;
467      WAVEFORMATEX *fmt;
468      PROPVARIANT pv = { VT_EMPTY };
469  
470      hr = drvs.pGetAudioEndpoint(&dev->devguid, &dev->IMMDevice_iface, &client);
471      if(FAILED(hr))
472          return hr;
473  
474      hr = IAudioClient_GetMixFormat(client, &fmt);
475      if(FAILED(hr)){
476          IAudioClient_Release(client);
477          return hr;
478      }
479  
480      IAudioClient_Release(client);
481  
482      pv.vt = VT_BLOB;
483      pv.u.blob.cbSize = sizeof(WAVEFORMATEX) + fmt->cbSize;
484      pv.u.blob.pBlobData = (BYTE*)fmt;
485      MMDevice_SetPropValue(&dev->devguid, dev->flow,
486              &PKEY_AudioEngine_DeviceFormat, &pv);
487      MMDevice_SetPropValue(&dev->devguid, dev->flow,
488              &PKEY_AudioEngine_OEMFormat, &pv);
489      CoTaskMemFree(fmt);
490  
491      return S_OK;
492  }
493  
494  static HRESULT load_driver_devices(EDataFlow flow)
495  {
496      WCHAR **ids;
497      GUID *guids;
498      UINT num, def, i;
499      HRESULT hr;
500  
501      if(!drvs.pGetEndpointIDs)
502          return S_OK;
503  
504      hr = drvs.pGetEndpointIDs(flow, &ids, &guids, &num, &def);
505      if(FAILED(hr))
506          return hr;
507  
508      for(i = 0; i < num; ++i){
509          MMDevice *dev;
510          dev = MMDevice_Create(ids[i], &guids[i], flow, DEVICE_STATE_ACTIVE,
511                  def == i);
512          set_format(dev);
513      }
514  
515      HeapFree(GetProcessHeap(), 0, guids);
516      HeapFree(GetProcessHeap(), 0, ids);
517  
518      return S_OK;
519  }
520  
521  static void MMDevice_Destroy(MMDevice *This)
522  {
523      DWORD i;
524      TRACE("Freeing %s\n", debugstr_w(This->drv_id));
525      /* Since this function is called at destruction time, reordering of the list is unimportant */
526      for (i = 0; i < MMDevice_count; ++i)
527      {
528          if (MMDevice_head[i] == This)
529          {
530              MMDevice_head[i] = MMDevice_head[--MMDevice_count];
531              break;
532          }
533      }
534      This->crst.DebugInfo->Spare[0] = 0;
535      DeleteCriticalSection(&This->crst);
536      HeapFree(GetProcessHeap(), 0, This->drv_id);
537      HeapFree(GetProcessHeap(), 0, This);
538  }
539  
540  static inline MMDevice *impl_from_IMMDevice(IMMDevice *iface)
541  {
542      return CONTAINING_RECORD(iface, MMDevice, IMMDevice_iface);
543  }
544  
545  static HRESULT WINAPI MMDevice_QueryInterface(IMMDevice *iface, REFIID riid, void **ppv)
546  {
547      MMDevice *This = impl_from_IMMDevice(iface);
548      TRACE("(%p)->(%s,%p)\n", iface, debugstr_guid(riid), ppv);
549  
550      if (!ppv)
551          return E_POINTER;
552      *ppv = NULL;
553      if (IsEqualIID(riid, &IID_IUnknown)
554          || IsEqualIID(riid, &IID_IMMDevice))
555          *ppv = &This->IMMDevice_iface;
556      else if (IsEqualIID(riid, &IID_IMMEndpoint))
557          *ppv = &This->IMMEndpoint_iface;
558      if (*ppv)
559      {
560          IUnknown_AddRef((IUnknown*)*ppv);
561          return S_OK;
562      }
563      WARN("Unknown interface %s\n", debugstr_guid(riid));
564      return E_NOINTERFACE;
565  }
566  
567  static ULONG WINAPI MMDevice_AddRef(IMMDevice *iface)
568  {
569      MMDevice *This = impl_from_IMMDevice(iface);
570      LONG ref;
571  
572      ref = InterlockedIncrement(&This->ref);
573      TRACE("Refcount now %i\n", ref);
574      return ref;
575  }
576  
577  static ULONG WINAPI MMDevice_Release(IMMDevice *iface)
578  {
579      MMDevice *This = impl_from_IMMDevice(iface);
580      LONG ref;
581  
582      ref = InterlockedDecrement(&This->ref);
583      TRACE("Refcount now %i\n", ref);
584      return ref;
585  }
586  
587  static HRESULT WINAPI MMDevice_Activate(IMMDevice *iface, REFIID riid, DWORD clsctx, PROPVARIANT *params, void **ppv)
588  {
589      HRESULT hr = E_NOINTERFACE;
590      MMDevice *This = impl_from_IMMDevice(iface);
591  
592      TRACE("(%p)->(%s, %x, %p, %p)\n", iface, debugstr_guid(riid), clsctx, params, ppv);
593  
594      if (!ppv)
595          return E_POINTER;
596  
597      if (IsEqualIID(riid, &IID_IAudioClient)){
598          hr = drvs.pGetAudioEndpoint(&This->devguid, iface, (IAudioClient**)ppv);
599      }else if (IsEqualIID(riid, &IID_IAudioEndpointVolume) ||
600              IsEqualIID(riid, &IID_IAudioEndpointVolumeEx))
601          hr = AudioEndpointVolume_Create(This, (IAudioEndpointVolumeEx**)ppv);
602      else if (IsEqualIID(riid, &IID_IAudioSessionManager)
603               || IsEqualIID(riid, &IID_IAudioSessionManager2))
604      {
605          hr = drvs.pGetAudioSessionManager(iface, (IAudioSessionManager2**)ppv);
606      }
607      else if (IsEqualIID(riid, &IID_IBaseFilter))
608      {
609          if (This->flow == eRender)
610              hr = CoCreateInstance(&CLSID_DSoundRender, NULL, clsctx, riid, ppv);
611          else
612              ERR("Not supported for recording?\n");
613          if (SUCCEEDED(hr))
614          {
615              IPersistPropertyBag *ppb;
616              hr = IUnknown_QueryInterface((IUnknown*)*ppv, &IID_IPersistPropertyBag, (void **)&ppb);
617              if (SUCCEEDED(hr))
618              {
619                  /* ::Load cannot assume the interface stays alive after the function returns,
620                   * so just create the interface on the stack, saves a lot of complicated code */
621                  IPropertyBagImpl bag = { { &PB_Vtbl } };
622                  bag.devguid = This->devguid;
623                  hr = IPersistPropertyBag_Load(ppb, &bag.IPropertyBag_iface, NULL);
624                  IPersistPropertyBag_Release(ppb);
625                  if (FAILED(hr))
626                      IBaseFilter_Release((IBaseFilter*)*ppv);
627              }
628              else
629              {
630                  FIXME("Wine doesn't support IPersistPropertyBag on DSoundRender yet, ignoring..\n");
631                  hr = S_OK;
632              }
633          }
634      }
635      else if (IsEqualIID(riid, &IID_IDeviceTopology))
636      {
637          FIXME("IID_IDeviceTopology unsupported\n");
638      }
639      else if (IsEqualIID(riid, &IID_IDirectSound)
640               || IsEqualIID(riid, &IID_IDirectSound8))
641      {
642          if (This->flow == eRender)
643              hr = CoCreateInstance(&CLSID_DirectSound8, NULL, clsctx, riid, ppv);
644          if (SUCCEEDED(hr))
645          {
646              hr = IDirectSound_Initialize((IDirectSound*)*ppv, &This->devguid);
647              if (FAILED(hr))
648                  IDirectSound_Release((IDirectSound*)*ppv);
649          }
650      }
651      else if (IsEqualIID(riid, &IID_IDirectSoundCapture))
652      {
653          if (This->flow == eCapture)
654              hr = CoCreateInstance(&CLSID_DirectSoundCapture8, NULL, clsctx, riid, ppv);
655          if (SUCCEEDED(hr))
656          {
657              hr = IDirectSoundCapture_Initialize((IDirectSoundCapture*)*ppv, &This->devguid);
658              if (FAILED(hr))
659                  IDirectSoundCapture_Release((IDirectSoundCapture*)*ppv);
660          }
661      }
662      else
663          ERR("Invalid/unknown iid %s\n", debugstr_guid(riid));
664  
665      if (FAILED(hr))
666          *ppv = NULL;
667  
668      TRACE("Returning %08x\n", hr);
669      return hr;
670  }
671  
672  static HRESULT WINAPI MMDevice_OpenPropertyStore(IMMDevice *iface, DWORD access, IPropertyStore **ppv)
673  {
674      MMDevice *This = impl_from_IMMDevice(iface);
675      TRACE("(%p)->(%x,%p)\n", This, access, ppv);
676  
677      if (!ppv)
678          return E_POINTER;
679      return MMDevPropStore_Create(This, access, ppv);
680  }
681  
682  static HRESULT WINAPI MMDevice_GetId(IMMDevice *iface, WCHAR **itemid)
683  {
684      MMDevice *This = impl_from_IMMDevice(iface);
685      WCHAR *str;
686      GUID *id = &This->devguid;
687      static const WCHAR formatW[] = { '{','0','.','0','.','%','u','.','0','0','0','0','0','0','0','0','}','.',
688                                       '{','%','0','8','X','-','%','0','4','X','-',
689                                       '%','0','4','X','-','%','0','2','X','%','0','2','X','-',
690                                       '%','0','2','X','%','0','2','X','%','0','2','X','%','0','2','X',
691                                       '%','0','2','X','%','0','2','X','}',0 };
692  
693      TRACE("(%p)->(%p)\n", This, itemid);
694      if (!itemid)
695          return E_POINTER;
696      *itemid = str = CoTaskMemAlloc(56 * sizeof(WCHAR));
697      if (!str)
698          return E_OUTOFMEMORY;
699      wsprintfW( str, formatW, This->flow, id->Data1, id->Data2, id->Data3,
700                 id->Data4[0], id->Data4[1], id->Data4[2], id->Data4[3],
701                 id->Data4[4], id->Data4[5], id->Data4[6], id->Data4[7] );
702      TRACE("returning %s\n", wine_dbgstr_w(str));
703      return S_OK;
704  }
705  
706  static HRESULT WINAPI MMDevice_GetState(IMMDevice *iface, DWORD *state)
707  {
708      MMDevice *This = impl_from_IMMDevice(iface);
709      TRACE("(%p)->(%p)\n", iface, state);
710  
711      if (!state)
712          return E_POINTER;
713      *state = This->state;
714      return S_OK;
715  }
716  
717  static const IMMDeviceVtbl MMDeviceVtbl =
718  {
719      MMDevice_QueryInterface,
720      MMDevice_AddRef,
721      MMDevice_Release,
722      MMDevice_Activate,
723      MMDevice_OpenPropertyStore,
724      MMDevice_GetId,
725      MMDevice_GetState
726  };
727  
728  static inline MMDevice *impl_from_IMMEndpoint(IMMEndpoint *iface)
729  {
730      return CONTAINING_RECORD(iface, MMDevice, IMMEndpoint_iface);
731  }
732  
733  static HRESULT WINAPI MMEndpoint_QueryInterface(IMMEndpoint *iface, REFIID riid, void **ppv)
734  {
735      MMDevice *This = impl_from_IMMEndpoint(iface);
736      TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
737      return IMMDevice_QueryInterface(&This->IMMDevice_iface, riid, ppv);
738  }
739  
740  static ULONG WINAPI MMEndpoint_AddRef(IMMEndpoint *iface)
741  {
742      MMDevice *This = impl_from_IMMEndpoint(iface);
743      TRACE("(%p)\n", This);
744      return IMMDevice_AddRef(&This->IMMDevice_iface);
745  }
746  
747  static ULONG WINAPI MMEndpoint_Release(IMMEndpoint *iface)
748  {
749      MMDevice *This = impl_from_IMMEndpoint(iface);
750      TRACE("(%p)\n", This);
751      return IMMDevice_Release(&This->IMMDevice_iface);
752  }
753  
754  static HRESULT WINAPI MMEndpoint_GetDataFlow(IMMEndpoint *iface, EDataFlow *flow)
755  {
756      MMDevice *This = impl_from_IMMEndpoint(iface);
757      TRACE("(%p)->(%p)\n", This, flow);
758      if (!flow)
759          return E_POINTER;
760      *flow = This->flow;
761      return S_OK;
762  }
763  
764  static const IMMEndpointVtbl MMEndpointVtbl =
765  {
766      MMEndpoint_QueryInterface,
767      MMEndpoint_AddRef,
768      MMEndpoint_Release,
769      MMEndpoint_GetDataFlow
770  };
771  
772  static HRESULT MMDevCol_Create(IMMDeviceCollection **ppv, EDataFlow flow, DWORD state)
773  {
774      MMDevColImpl *This;
775  
776      This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
777      *ppv = NULL;
778      if (!This)
779          return E_OUTOFMEMORY;
780      This->IMMDeviceCollection_iface.lpVtbl = &MMDevColVtbl;
781      This->ref = 1;
782      This->flow = flow;
783      This->state = state;
784      *ppv = &This->IMMDeviceCollection_iface;
785      return S_OK;
786  }
787  
788  static void MMDevCol_Destroy(MMDevColImpl *This)
789  {
790      HeapFree(GetProcessHeap(), 0, This);
791  }
792  
793  static HRESULT WINAPI MMDevCol_QueryInterface(IMMDeviceCollection *iface, REFIID riid, void **ppv)
794  {
795      MMDevColImpl *This = impl_from_IMMDeviceCollection(iface);
796      TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
797  
798      if (!ppv)
799          return E_POINTER;
800      if (IsEqualIID(riid, &IID_IUnknown)
801          || IsEqualIID(riid, &IID_IMMDeviceCollection))
802          *ppv = &This->IMMDeviceCollection_iface;
803      else
804          *ppv = NULL;
805      if (!*ppv)
806          return E_NOINTERFACE;
807      IUnknown_AddRef((IUnknown*)*ppv);
808      return S_OK;
809  }
810  
811  static ULONG WINAPI MMDevCol_AddRef(IMMDeviceCollection *iface)
812  {
813      MMDevColImpl *This = impl_from_IMMDeviceCollection(iface);
814      LONG ref = InterlockedIncrement(&This->ref);
815      TRACE("Refcount now %i\n", ref);
816      return ref;
817  }
818  
819  static ULONG WINAPI MMDevCol_Release(IMMDeviceCollection *iface)
820  {
821      MMDevColImpl *This = impl_from_IMMDeviceCollection(iface);
822      LONG ref = InterlockedDecrement(&This->ref);
823      TRACE("Refcount now %i\n", ref);
824      if (!ref)
825          MMDevCol_Destroy(This);
826      return ref;
827  }
828  
829  static HRESULT WINAPI MMDevCol_GetCount(IMMDeviceCollection *iface, UINT *numdevs)
830  {
831      MMDevColImpl *This = impl_from_IMMDeviceCollection(iface);
832      DWORD i;
833  
834      TRACE("(%p)->(%p)\n", This, numdevs);
835      if (!numdevs)
836          return E_POINTER;
837  
838      *numdevs = 0;
839      for (i = 0; i < MMDevice_count; ++i)
840      {
841          MMDevice *cur = MMDevice_head[i];
842          if ((cur->flow == This->flow || This->flow == eAll)
843              && (cur->state & This->state))
844              ++(*numdevs);
845      }
846      return S_OK;
847  }
848  
849  static HRESULT WINAPI MMDevCol_Item(IMMDeviceCollection *iface, UINT n, IMMDevice **dev)
850  {
851      MMDevColImpl *This = impl_from_IMMDeviceCollection(iface);
852      DWORD i = 0, j = 0;
853  
854      TRACE("(%p)->(%u, %p)\n", This, n, dev);
855      if (!dev)
856          return E_POINTER;
857  
858      for (j = 0; j < MMDevice_count; ++j)
859      {
860          MMDevice *cur = MMDevice_head[j];
861          if ((cur->flow == This->flow || This->flow == eAll)
862              && (cur->state & This->state)
863              && i++ == n)
864          {
865              *dev = &cur->IMMDevice_iface;
866              IMMDevice_AddRef(*dev);
867              return S_OK;
868          }
869      }
870      WARN("Could not obtain item %u\n", n);
871      *dev = NULL;
872      return E_INVALIDARG;
873  }
874  
875  static const IMMDeviceCollectionVtbl MMDevColVtbl =
876  {
877      MMDevCol_QueryInterface,
878      MMDevCol_AddRef,
879      MMDevCol_Release,
880      MMDevCol_GetCount,
881      MMDevCol_Item
882  };
883  
884  HRESULT MMDevEnum_Create(REFIID riid, void **ppv)
885  {
886      MMDevEnumImpl *This = MMDevEnumerator;
887  
888      if (!This)
889      {
890          This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
891          *ppv = NULL;
892          if (!This)
893              return E_OUTOFMEMORY;
894          This->ref = 1;
895          This->IMMDeviceEnumerator_iface.lpVtbl = &MMDevEnumVtbl;
896          MMDevEnumerator = This;
897  
898          load_devices_from_reg();
899          load_driver_devices(eRender);
900          load_driver_devices(eCapture);
901      }
902      return IMMDeviceEnumerator_QueryInterface(&This->IMMDeviceEnumerator_iface, riid, ppv);
903  }
904  
905  void MMDevEnum_Free(void)
906  {
907      while (MMDevice_count)
908          MMDevice_Destroy(MMDevice_head[0]);
909      RegCloseKey(key_render);
910      RegCloseKey(key_capture);
911      key_render = key_capture = NULL;
912      HeapFree(GetProcessHeap(), 0, MMDevEnumerator);
913      MMDevEnumerator = NULL;
914  }
915  
916  static HRESULT WINAPI MMDevEnum_QueryInterface(IMMDeviceEnumerator *iface, REFIID riid, void **ppv)
917  {
918      MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
919      TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
920  
921      if (!ppv)
922          return E_POINTER;
923      if (IsEqualIID(riid, &IID_IUnknown)
924          || IsEqualIID(riid, &IID_IMMDeviceEnumerator))
925          *ppv = &This->IMMDeviceEnumerator_iface;
926      else
927          *ppv = NULL;
928      if (!*ppv)
929          return E_NOINTERFACE;
930      IUnknown_AddRef((IUnknown*)*ppv);
931      return S_OK;
932  }
933  
934  static ULONG WINAPI MMDevEnum_AddRef(IMMDeviceEnumerator *iface)
935  {
936      MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
937      LONG ref = InterlockedIncrement(&This->ref);
938      TRACE("Refcount now %i\n", ref);
939      return ref;
940  }
941  
942  static ULONG WINAPI MMDevEnum_Release(IMMDeviceEnumerator *iface)
943  {
944      MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
945      LONG ref = InterlockedDecrement(&This->ref);
946      if (!ref)
947          MMDevEnum_Free();
948      TRACE("Refcount now %i\n", ref);
949      return ref;
950  }
951  
952  static HRESULT WINAPI MMDevEnum_EnumAudioEndpoints(IMMDeviceEnumerator *iface, EDataFlow flow, DWORD mask, IMMDeviceCollection **devices)
953  {
954      MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
955      TRACE("(%p)->(%u,%u,%p)\n", This, flow, mask, devices);
956      if (!devices)
957          return E_POINTER;
958      *devices = NULL;
959      if (flow >= EDataFlow_enum_count)
960          return E_INVALIDARG;
961      if (mask & ~DEVICE_STATEMASK_ALL)
962          return E_INVALIDARG;
963      return MMDevCol_Create(devices, flow, mask);
964  }
965  
966  static HRESULT WINAPI MMDevEnum_GetDefaultAudioEndpoint(IMMDeviceEnumerator *iface, EDataFlow flow, ERole role, IMMDevice **device)
967  {
968      MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
969      WCHAR reg_key[256];
970      HKEY key;
971      HRESULT hr;
972  
973      TRACE("(%p)->(%u,%u,%p)\n", This, flow, role, device);
974  
975      if (!device)
976          return E_POINTER;
977  
978      if((flow != eRender && flow != eCapture) ||
979              (role != eConsole && role != eMultimedia && role != eCommunications)){
980          WARN("Unknown flow (%u) or role (%u)\n", flow, role);
981          return E_INVALIDARG;
982      }
983  
984      *device = NULL;
985  
986      if(!drvs.module_name[0])
987          return E_NOTFOUND;
988  
989      lstrcpyW(reg_key, drv_keyW);
990      lstrcatW(reg_key, slashW);
991      lstrcatW(reg_key, drvs.module_name);
992  
993      if(RegOpenKeyW(HKEY_CURRENT_USER, reg_key, &key) == ERROR_SUCCESS){
994          const WCHAR *reg_x_name, *reg_vx_name;
995          WCHAR def_id[256];
996          DWORD size = sizeof(def_id), state;
997  
998          if(flow == eRender){
999              reg_x_name = reg_out_nameW;
1000              reg_vx_name = reg_vout_nameW;
1001          }else{
1002              reg_x_name = reg_in_nameW;
1003              reg_vx_name = reg_vin_nameW;
1004          }
1005  
1006          if(role == eCommunications &&
1007                  RegQueryValueExW(key, reg_vx_name, 0, NULL,
1008                      (BYTE*)def_id, &size) == ERROR_SUCCESS){
1009              hr = IMMDeviceEnumerator_GetDevice(iface, def_id, device);
1010              if(SUCCEEDED(hr)){
1011                  if(SUCCEEDED(IMMDevice_GetState(*device, &state)) &&
1012                          state == DEVICE_STATE_ACTIVE){
1013                      RegCloseKey(key);
1014                      return S_OK;
1015                  }
1016              }
1017  
1018              TRACE("Unable to find voice device %s\n", wine_dbgstr_w(def_id));
1019          }
1020  
1021          if(RegQueryValueExW(key, reg_x_name, 0, NULL,
1022                      (BYTE*)def_id, &size) == ERROR_SUCCESS){
1023              hr = IMMDeviceEnumerator_GetDevice(iface, def_id, device);
1024              if(SUCCEEDED(hr)){
1025                  if(SUCCEEDED(IMMDevice_GetState(*device, &state)) &&
1026                          state == DEVICE_STATE_ACTIVE){
1027                      RegCloseKey(key);
1028                      return S_OK;
1029                  }
1030              }
1031  
1032              TRACE("Unable to find device %s\n", wine_dbgstr_w(def_id));
1033          }
1034  
1035          RegCloseKey(key);
1036      }
1037  
1038      if (flow == eRender)
1039          *device = &MMDevice_def_play->IMMDevice_iface;
1040      else
1041          *device = &MMDevice_def_rec->IMMDevice_iface;
1042  
1043      if (!*device)
1044          return E_NOTFOUND;
1045      IMMDevice_AddRef(*device);
1046      return S_OK;
1047  }
1048  
1049  static HRESULT WINAPI MMDevEnum_GetDevice(IMMDeviceEnumerator *iface, const WCHAR *name, IMMDevice **device)
1050  {
1051      MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
1052      DWORD i=0;
1053      IMMDevice *dev = NULL;
1054  
1055      static const WCHAR wine_info_deviceW[] = {'W','i','n','e',' ',
1056          'i','n','f','o',' ','d','e','v','i','c','e',0};
1057  
1058      TRACE("(%p)->(%s,%p)\n", This, debugstr_w(name), device);
1059  
1060      if(!name || !device)
1061          return E_POINTER;
1062  
1063      if(!lstrcmpW(name, wine_info_deviceW)){
1064          *device = &info_device;
1065          return S_OK;
1066      }
1067  
1068      for (i = 0; i < MMDevice_count; ++i)
1069      {
1070          HRESULT hr;
1071          WCHAR *str;
1072          dev = &MMDevice_head[i]->IMMDevice_iface;
1073          hr = IMMDevice_GetId(dev, &str);
1074          if (FAILED(hr))
1075          {
1076              WARN("GetId failed: %08x\n", hr);
1077              continue;
1078          }
1079  
1080          if (str && !lstrcmpW(str, name))
1081          {
1082              CoTaskMemFree(str);
1083              IMMDevice_AddRef(dev);
1084              *device = dev;
1085              return S_OK;
1086          }
1087          CoTaskMemFree(str);
1088      }
1089      TRACE("Could not find device %s\n", debugstr_w(name));
1090      return E_INVALIDARG;
1091  }
1092  
1093  struct NotificationClientWrapper {
1094      IMMNotificationClient *client;
1095      struct list entry;
1096  };
1097  
1098  static struct list g_notif_clients = LIST_INIT(g_notif_clients);
1099  static HANDLE g_notif_thread;
1100  
1101  static CRITICAL_SECTION g_notif_lock;
1102  static CRITICAL_SECTION_DEBUG g_notif_lock_debug =
1103  {
1104      0, 0, &g_notif_lock,
1105      { &g_notif_lock_debug.ProcessLocksList, &g_notif_lock_debug.ProcessLocksList },
1106        0, 0, { (DWORD_PTR)(__FILE__ ": g_notif_lock") }
1107  };
1108  static CRITICAL_SECTION g_notif_lock = { &g_notif_lock_debug, -1, 0, 0, 0, 0 };
1109  
1110  static void notify_clients(EDataFlow flow, ERole role, const WCHAR *id)
1111  {
1112      struct NotificationClientWrapper *wrapper;
1113      LIST_FOR_EACH_ENTRY(wrapper, &g_notif_clients,
1114              struct NotificationClientWrapper, entry)
1115          IMMNotificationClient_OnDefaultDeviceChanged(wrapper->client, flow,
1116                  role, id);
1117  
1118      /* Windows 7 treats changes to eConsole as changes to eMultimedia */
1119      if(role == eConsole)
1120          notify_clients(flow, eMultimedia, id);
1121  }
1122  
1123  static BOOL notify_if_changed(EDataFlow flow, ERole role, HKEY key,
1124                                const WCHAR *val_name, WCHAR *old_val, IMMDevice *def_dev)
1125  {
1126      WCHAR new_val[64], *id;
1127      DWORD size;
1128      HRESULT hr;
1129  
1130      size = sizeof(new_val);
1131      if(RegQueryValueExW(key, val_name, 0, NULL,
1132                  (BYTE*)new_val, &size) != ERROR_SUCCESS){
1133          if(old_val[0] != 0){
1134              /* set by user -> system default */
1135              if(def_dev){
1136                  hr = IMMDevice_GetId(def_dev, &id);
1137                  if(FAILED(hr)){
1138                      ERR("GetId failed: %08x\n", hr);
1139                      return FALSE;
1140                  }
1141              }else
1142                  id = NULL;
1143  
1144              notify_clients(flow, role, id);
1145              old_val[0] = 0;
1146              CoTaskMemFree(id);
1147  
1148              return TRUE;
1149          }
1150  
1151          /* system default -> system default, noop */
1152          return FALSE;
1153      }
1154  
1155      if(!lstrcmpW(old_val, new_val)){
1156          /* set by user -> same value */
1157          return FALSE;
1158      }
1159  
1160      if(new_val[0] != 0){
1161          /* set by user -> different value */
1162          notify_clients(flow, role, new_val);
1163          memcpy(old_val, new_val, sizeof(new_val));
1164          return TRUE;
1165      }
1166  
1167      /* set by user -> system default */
1168      if(def_dev){
1169          hr = IMMDevice_GetId(def_dev, &id);
1170          if(FAILED(hr)){
1171              ERR("GetId failed: %08x\n", hr);
1172              return FALSE;
1173          }
1174      }else
1175          id = NULL;
1176  
1177      notify_clients(flow, role, id);
1178      old_val[0] = 0;
1179      CoTaskMemFree(id);
1180  
1181      return TRUE;
1182  }
1183  
1184  static DWORD WINAPI notif_thread_proc(void *user)
1185  {
1186      HKEY key;
1187      WCHAR reg_key[256];
1188      WCHAR out_name[64], vout_name[64], in_name[64], vin_name[64];
1189      DWORD size;
1190  
1191      lstrcpyW(reg_key, drv_keyW);
1192      lstrcatW(reg_key, slashW);
1193      lstrcatW(reg_key, drvs.module_name);
1194  
1195      if(RegCreateKeyExW(HKEY_CURRENT_USER, reg_key, 0, NULL, 0,
1196                  MAXIMUM_ALLOWED, NULL, &key, NULL) != ERROR_SUCCESS){
1197          ERR("RegCreateKeyEx failed: %u\n", GetLastError());
1198          return 1;
1199      }
1200  
1201      size = sizeof(out_name);
1202      if(RegQueryValueExW(key, reg_out_nameW, 0, NULL,
1203                  (BYTE*)out_name, &size) != ERROR_SUCCESS)
1204          out_name[0] = 0;
1205  
1206      size = sizeof(vout_name);
1207      if(RegQueryValueExW(key, reg_vout_nameW, 0, NULL,
1208                  (BYTE*)vout_name, &size) != ERROR_SUCCESS)
1209          vout_name[0] = 0;
1210  
1211      size = sizeof(in_name);
1212      if(RegQueryValueExW(key, reg_in_nameW, 0, NULL,
1213                  (BYTE*)in_name, &size) != ERROR_SUCCESS)
1214          in_name[0] = 0;
1215  
1216      size = sizeof(vin_name);
1217      if(RegQueryValueExW(key, reg_vin_nameW, 0, NULL,
1218                  (BYTE*)vin_name, &size) != ERROR_SUCCESS)
1219          vin_name[0] = 0;
1220  
1221      while(1){
1222          if(RegNotifyChangeKeyValue(key, FALSE, REG_NOTIFY_CHANGE_LAST_SET,
1223                      NULL, FALSE) != ERROR_SUCCESS){
1224              ERR("RegNotifyChangeKeyValue failed: %u\n", GetLastError());
1225              RegCloseKey(key);
1226              g_notif_thread = NULL;
1227              return 1;
1228          }
1229  
1230          EnterCriticalSection(&g_notif_lock);
1231  
1232          notify_if_changed(eRender, eConsole, key, reg_out_nameW,
1233                  out_name, &MMDevice_def_play->IMMDevice_iface);
1234          notify_if_changed(eRender, eCommunications, key, reg_vout_nameW,
1235                  vout_name, &MMDevice_def_play->IMMDevice_iface);
1236          notify_if_changed(eCapture, eConsole, key, reg_in_nameW,
1237                  in_name, &MMDevice_def_rec->IMMDevice_iface);
1238          notify_if_changed(eCapture, eCommunications, key, reg_vin_nameW,
1239                  vin_name, &MMDevice_def_rec->IMMDevice_iface);
1240  
1241          LeaveCriticalSection(&g_notif_lock);
1242      }
1243  
1244      RegCloseKey(key);
1245  
1246      g_notif_thread = NULL;
1247  
1248      return 0;
1249  }
1250  
1251  static HRESULT WINAPI MMDevEnum_RegisterEndpointNotificationCallback(IMMDeviceEnumerator *iface, IMMNotificationClient *client)
1252  {
1253      MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
1254      struct NotificationClientWrapper *wrapper;
1255  
1256      TRACE("(%p)->(%p)\n", This, client);
1257  
1258      if(!client)
1259          return E_POINTER;
1260  
1261      wrapper = HeapAlloc(GetProcessHeap(), 0, sizeof(*wrapper));
1262      if(!wrapper)
1263          return E_OUTOFMEMORY;
1264  
1265      wrapper->client = client;
1266  
1267      EnterCriticalSection(&g_notif_lock);
1268  
1269      list_add_tail(&g_notif_clients, &wrapper->entry);
1270  
1271      if(!g_notif_thread){
1272          g_notif_thread = CreateThread(NULL, 0, notif_thread_proc, NULL, 0, NULL);
1273          if(!g_notif_thread)
1274              ERR("CreateThread failed: %u\n", GetLastError());
1275      }
1276  
1277      LeaveCriticalSection(&g_notif_lock);
1278  
1279      return S_OK;
1280  }
1281  
1282  static HRESULT WINAPI MMDevEnum_UnregisterEndpointNotificationCallback(IMMDeviceEnumerator *iface, IMMNotificationClient *client)
1283  {
1284      MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
1285      struct NotificationClientWrapper *wrapper;
1286  
1287      TRACE("(%p)->(%p)\n", This, client);
1288  
1289      if(!client)
1290          return E_POINTER;
1291  
1292      EnterCriticalSection(&g_notif_lock);
1293  
1294      LIST_FOR_EACH_ENTRY(wrapper, &g_notif_clients, struct NotificationClientWrapper, entry){
1295          if(wrapper->client == client){
1296              list_remove(&wrapper->entry);
1297              HeapFree(GetProcessHeap(), 0, wrapper);
1298              LeaveCriticalSection(&g_notif_lock);
1299              return S_OK;
1300          }
1301      }
1302  
1303      LeaveCriticalSection(&g_notif_lock);
1304  
1305      return E_NOTFOUND;
1306  }
1307  
1308  static const IMMDeviceEnumeratorVtbl MMDevEnumVtbl =
1309  {
1310      MMDevEnum_QueryInterface,
1311      MMDevEnum_AddRef,
1312      MMDevEnum_Release,
1313      MMDevEnum_EnumAudioEndpoints,
1314      MMDevEnum_GetDefaultAudioEndpoint,
1315      MMDevEnum_GetDevice,
1316      MMDevEnum_RegisterEndpointNotificationCallback,
1317      MMDevEnum_UnregisterEndpointNotificationCallback
1318  };
1319  
1320  static HRESULT MMDevPropStore_Create(MMDevice *parent, DWORD access, IPropertyStore **ppv)
1321  {
1322      MMDevPropStore *This;
1323      if (access != STGM_READ
1324          && access != STGM_WRITE
1325          && access != STGM_READWRITE)
1326      {
1327          WARN("Invalid access %08x\n", access);
1328          return E_INVALIDARG;
1329      }
1330      This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
1331      *ppv = &This->IPropertyStore_iface;
1332      if (!This)
1333          return E_OUTOFMEMORY;
1334      This->IPropertyStore_iface.lpVtbl = &MMDevPropVtbl;
1335      This->ref = 1;
1336      This->parent = parent;
1337      This->access = access;
1338      return S_OK;
1339  }
1340  
1341  static void MMDevPropStore_Destroy(MMDevPropStore *This)
1342  {
1343      HeapFree(GetProcessHeap(), 0, This);
1344  }
1345  
1346  static HRESULT WINAPI MMDevPropStore_QueryInterface(IPropertyStore *iface, REFIID riid, void **ppv)
1347  {
1348      MMDevPropStore *This = impl_from_IPropertyStore(iface);
1349      TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
1350  
1351      if (!ppv)
1352          return E_POINTER;
1353      if (IsEqualIID(riid, &IID_IUnknown)
1354          || IsEqualIID(riid, &IID_IPropertyStore))
1355          *ppv = &This->IPropertyStore_iface;
1356      else
1357          *ppv = NULL;
1358      if (!*ppv)
1359          return E_NOINTERFACE;
1360      IUnknown_AddRef((IUnknown*)*ppv);
1361      return S_OK;
1362  }
1363  
1364  static ULONG WINAPI MMDevPropStore_AddRef(IPropertyStore *iface)
1365  {
1366      MMDevPropStore *This = impl_from_IPropertyStore(iface);
1367      LONG ref = InterlockedIncrement(&This->ref);
1368      TRACE("Refcount now %i\n", ref);
1369      return ref;
1370  }
1371  
1372  static ULONG WINAPI MMDevPropStore_Release(IPropertyStore *iface)
1373  {
1374      MMDevPropStore *This = impl_from_IPropertyStore(iface);
1375      LONG ref = InterlockedDecrement(&This->ref);
1376      TRACE("Refcount now %i\n", ref);
1377      if (!ref)
1378          MMDevPropStore_Destroy(This);
1379      return ref;
1380  }
1381  
1382  static HRESULT WINAPI MMDevPropStore_GetCount(IPropertyStore *iface, DWORD *nprops)
1383  {
1384      MMDevPropStore *This = impl_from_IPropertyStore(iface);
1385      WCHAR buffer[50];
1386      DWORD i = 0;
1387      HKEY propkey;
1388      HRESULT hr;
1389  
1390      TRACE("(%p)->(%p)\n", iface, nprops);
1391      if (!nprops)
1392          return E_POINTER;
1393      hr = MMDevPropStore_OpenPropKey(&This->parent->devguid, This->parent->flow, &propkey);
1394      if (FAILED(hr))
1395          return hr;
1396      *nprops = 0;
1397      do {
1398          DWORD len = sizeof(buffer)/sizeof(*buffer);
1399          if (RegEnumValueW(propkey, i, buffer, &len, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
1400              break;
1401          i++;
1402      } while (1);
1403      RegCloseKey(propkey);
1404      TRACE("Returning %i\n", i);
1405      *nprops = i;
1406      return S_OK;
1407  }
1408  
1409  static HRESULT WINAPI MMDevPropStore_GetAt(IPropertyStore *iface, DWORD prop, PROPERTYKEY *key)
1410  {
1411      MMDevPropStore *This = impl_from_IPropertyStore(iface);
1412      WCHAR buffer[50];
1413      DWORD len = sizeof(buffer)/sizeof(*buffer);
1414      HRESULT hr;
1415      HKEY propkey;
1416  
1417      TRACE("(%p)->(%u,%p)\n", iface, prop, key);
1418      if (!key)
1419          return E_POINTER;
1420  
1421      hr = MMDevPropStore_OpenPropKey(&This->parent->devguid, This->parent->flow, &propkey);
1422      if (FAILED(hr))
1423          return hr;
1424  
1425      if (RegEnumValueW(propkey, prop, buffer, &len, NULL, NULL, NULL, NULL) != ERROR_SUCCESS
1426          || len <= 39)
1427      {
1428          WARN("GetAt %u failed\n", prop);
1429          return E_INVALIDARG;
1430      }
1431      RegCloseKey(propkey);
1432      buffer[38] = 0;
1433      CLSIDFromString(buffer, &key->fmtid);
1434      key->pid = atoiW(&buffer[39]);
1435      return S_OK;
1436  }
1437  
1438  static HRESULT WINAPI MMDevPropStore_GetValue(IPropertyStore *iface, REFPROPERTYKEY key, PROPVARIANT *pv)
1439  {
1440      MMDevPropStore *This = impl_from_IPropertyStore(iface);
1441      TRACE("(%p)->(\"%s,%u\", %p)\n", This, key ? debugstr_guid(&key->fmtid) : NULL, key ? key->pid : 0, pv);
1442  
1443      if (!key || !pv)
1444          return E_POINTER;
1445      if (This->access != STGM_READ
1446          && This->access != STGM_READWRITE)
1447          return STG_E_ACCESSDENIED;
1448  
1449      /* Special case */
1450      if (IsEqualPropertyKey(*key, PKEY_AudioEndpoint_GUID))
1451      {
1452          pv->vt = VT_LPWSTR;
1453          pv->u.pwszVal = CoTaskMemAlloc(39 * sizeof(WCHAR));
1454          if (!pv->u.pwszVal)
1455              return E_OUTOFMEMORY;
1456          StringFromGUID2(&This->parent->devguid, pv->u.pwszVal, 39);
1457          return S_OK;
1458      }
1459  
1460      return MMDevice_GetPropValue(&This->parent->devguid, This->parent->flow, key, pv);
1461  }
1462  
1463  static HRESULT WINAPI MMDevPropStore_SetValue(IPropertyStore *iface, REFPROPERTYKEY key, REFPROPVARIANT pv)
1464  {
1465      MMDevPropStore *This = impl_from_IPropertyStore(iface);
1466      TRACE("(%p)->(\"%s,%u\", %p)\n", This, key ? debugstr_guid(&key->fmtid) : NULL, key ? key->pid : 0, pv);
1467  
1468      if (!key || !pv)
1469          return E_POINTER;
1470  
1471      if (This->access != STGM_WRITE
1472          && This->access != STGM_READWRITE)
1473          return STG_E_ACCESSDENIED;
1474      return MMDevice_SetPropValue(&This->parent->devguid, This->parent->flow, key, pv);
1475  }
1476  
1477  static HRESULT WINAPI MMDevPropStore_Commit(IPropertyStore *iface)
1478  {
1479      MMDevPropStore *This = impl_from_IPropertyStore(iface);
1480      TRACE("(%p)\n", iface);
1481  
1482      if (This->access != STGM_WRITE
1483          && This->access != STGM_READWRITE)
1484          return STG_E_ACCESSDENIED;
1485  
1486      /* Does nothing - for mmdevapi, the propstore values are written on SetValue,
1487       * not on Commit. */
1488  
1489      return S_OK;
1490  }
1491  
1492  static const IPropertyStoreVtbl MMDevPropVtbl =
1493  {
1494      MMDevPropStore_QueryInterface,
1495      MMDevPropStore_AddRef,
1496      MMDevPropStore_Release,
1497      MMDevPropStore_GetCount,
1498      MMDevPropStore_GetAt,
1499      MMDevPropStore_GetValue,
1500      MMDevPropStore_SetValue,
1501      MMDevPropStore_Commit
1502  };
1503  
1504  
1505  /* Property bag for IBaseFilter activation */
1506  static HRESULT WINAPI PB_QueryInterface(IPropertyBag *iface, REFIID riid, void **ppv)
1507  {
1508      ERR("Should not be called\n");
1509      *ppv = NULL;
1510      return E_NOINTERFACE;
1511  }
1512  
1513  static ULONG WINAPI PB_AddRef(IPropertyBag *iface)
1514  {
1515      ERR("Should not be called\n");
1516      return 2;
1517  }
1518  
1519  static ULONG WINAPI PB_Release(IPropertyBag *iface)
1520  {
1521      ERR("Should not be called\n");
1522      return 1;
1523  }
1524  
1525  static HRESULT WINAPI PB_Read(IPropertyBag *iface, LPCOLESTR name, VARIANT *var, IErrorLog *log)
1526  {
1527      static const WCHAR dsguid[] = { 'D','S','G','u','i','d', 0 };
1528      IPropertyBagImpl *This = impl_from_IPropertyBag(iface);
1529      TRACE("Trying to read %s, type %u\n", debugstr_w(name), var->n1.n2.vt);
1530      if (!lstrcmpW(name, dsguid))
1531      {
1532          WCHAR guidstr[39];
1533          StringFromGUID2(&This->devguid, guidstr,sizeof(guidstr)/sizeof(*guidstr));
1534          var->n1.n2.vt = VT_BSTR;
1535          var->n1.n2.n3.bstrVal = SysAllocString(guidstr);
1536          return S_OK;
1537      }
1538      ERR("Unknown property '%s' queried\n", debugstr_w(name));
1539      return E_FAIL;
1540  }
1541  
1542  static HRESULT WINAPI PB_Write(IPropertyBag *iface, LPCOLESTR name, VARIANT *var)
1543  {
1544      ERR("Should not be called\n");
1545      return E_FAIL;
1546  }
1547  
1548  static const IPropertyBagVtbl PB_Vtbl =
1549  {
1550      PB_QueryInterface,
1551      PB_AddRef,
1552      PB_Release,
1553      PB_Read,
1554      PB_Write
1555  };
1556  
1557  static ULONG WINAPI info_device_ps_AddRef(IPropertyStore *iface)
1558  {
1559      return 2;
1560  }
1561  
1562  static ULONG WINAPI info_device_ps_Release(IPropertyStore *iface)
1563  {
1564      return 1;
1565  }
1566  
1567  static HRESULT WINAPI info_device_ps_GetValue(IPropertyStore *iface,
1568          REFPROPERTYKEY key, PROPVARIANT *pv)
1569  {
1570      TRACE("(static)->(\"%s,%u\", %p)\n", debugstr_guid(&key->fmtid), key ? key->pid : 0, pv);
1571  
1572      if (!key || !pv)
1573          return E_POINTER;
1574  
1575      if (IsEqualPropertyKey(*key, DEVPKEY_Device_Driver))
1576      {
1577          INT size = (lstrlenW(drvs.module_name) + 1) * sizeof(WCHAR);
1578          pv->vt = VT_LPWSTR;
1579          pv->u.pwszVal = CoTaskMemAlloc(size);
1580          if (!pv->u.pwszVal)
1581              return E_OUTOFMEMORY;
1582          memcpy(pv->u.pwszVal, drvs.module_name, size);
1583          return S_OK;
1584      }
1585  
1586      return E_INVALIDARG;
1587  }
1588  
1589  static const IPropertyStoreVtbl info_device_ps_Vtbl =
1590  {
1591      NULL,
1592      info_device_ps_AddRef,
1593      info_device_ps_Release,
1594      NULL,
1595      NULL,
1596      info_device_ps_GetValue,
1597      NULL,
1598      NULL
1599  };
1600  
1601  static IPropertyStore info_device_ps = {
1602      &info_device_ps_Vtbl
1603  };
1604  
1605  static ULONG WINAPI info_device_AddRef(IMMDevice *iface)
1606  {
1607      return 2;
1608  }
1609  
1610  static ULONG WINAPI info_device_Release(IMMDevice *iface)
1611  {
1612      return 1;
1613  }
1614  
1615  static HRESULT WINAPI info_device_OpenPropertyStore(IMMDevice *iface,
1616          DWORD access, IPropertyStore **ppv)
1617  {
1618      TRACE("(static)->(%x, %p)\n", access, ppv);
1619      *ppv = &info_device_ps;
1620      return S_OK;
1621  }
1622  
1623  static const IMMDeviceVtbl info_device_Vtbl =
1624  {
1625      NULL,
1626      info_device_AddRef,
1627      info_device_Release,
1628      NULL,
1629      info_device_OpenPropertyStore,
1630      NULL,
1631      NULL
1632  };
1633  
1634  static IMMDevice info_device = {
1635      &info_device_Vtbl
1636  };
1637