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 "wine/test.h"
20
21 #define COBJMACROS
22
23 #include "initguid.h"
24 #ifndef __REACTOS__
25 #include "endpointvolume.h"
26 #endif
27 #include "mmdeviceapi.h"
28 #include "audioclient.h"
29 #include "audiopolicy.h"
30 #include "dshow.h"
31 #include "dsound.h"
32 #include "devpkey.h"
33
34 DEFINE_GUID(GUID_NULL,0,0,0,0,0,0,0,0,0,0,0);
35
36 /* Some of the QueryInterface tests are really just to check if I got the IIDs right :) */
37
38 /* IMMDeviceCollection appears to have no QueryInterface method and instead forwards to mme */
test_collection(IMMDeviceEnumerator * mme,IMMDeviceCollection * col)39 static void test_collection(IMMDeviceEnumerator *mme, IMMDeviceCollection *col)
40 {
41 IMMDeviceCollection *col2;
42 IMMDeviceEnumerator *mme2;
43 IUnknown *unk;
44 HRESULT hr;
45 ULONG ref;
46 UINT numdev;
47 IMMDevice *dev;
48
49 /* collection doesn't keep a ref on parent */
50 IMMDeviceEnumerator_AddRef(mme);
51 ref = IMMDeviceEnumerator_Release(mme);
52 ok(ref == 2, "Reference count on parent is %u\n", ref);
53
54 ref = IMMDeviceCollection_AddRef(col);
55 IMMDeviceCollection_Release(col);
56 ok(ref == 2, "Invalid reference count %u on collection\n", ref);
57
58 hr = IMMDeviceCollection_QueryInterface(col, &IID_IUnknown, NULL);
59 ok(hr == E_POINTER, "Null ppv returns %08x\n", hr);
60
61 hr = IMMDeviceCollection_QueryInterface(col, &IID_IUnknown, (void**)&unk);
62 ok(hr == S_OK, "Cannot query for IID_IUnknown: 0x%08x\n", hr);
63 if (hr == S_OK)
64 {
65 ok((IUnknown*)col == unk, "Pointers are not identical %p/%p/%p\n", col, unk, mme);
66 IUnknown_Release(unk);
67 }
68
69 hr = IMMDeviceCollection_QueryInterface(col, &IID_IMMDeviceCollection, (void**)&col2);
70 ok(hr == S_OK, "Cannot query for IID_IMMDeviceCollection: 0x%08x\n", hr);
71 if (hr == S_OK)
72 IMMDeviceCollection_Release(col2);
73
74 hr = IMMDeviceCollection_QueryInterface(col, &IID_IMMDeviceEnumerator, (void**)&mme2);
75 ok(hr == E_NOINTERFACE, "Query for IID_IMMDeviceEnumerator returned: 0x%08x\n", hr);
76 if (hr == S_OK)
77 IMMDeviceEnumerator_Release(mme2);
78
79 hr = IMMDeviceCollection_GetCount(col, NULL);
80 ok(hr == E_POINTER, "GetCount returned 0x%08x\n", hr);
81
82 hr = IMMDeviceCollection_GetCount(col, &numdev);
83 ok(hr == S_OK, "GetCount returned 0x%08x\n", hr);
84
85 dev = (void*)(LONG_PTR)0x12345678;
86 hr = IMMDeviceCollection_Item(col, numdev, &dev);
87 ok(hr == E_INVALIDARG, "Asking for too high device returned 0x%08x\n", hr);
88 ok(dev == NULL, "Returned non-null device\n");
89
90 if (numdev)
91 {
92 hr = IMMDeviceCollection_Item(col, 0, NULL);
93 ok(hr == E_POINTER, "Query with null pointer returned 0x%08x\n", hr);
94
95 hr = IMMDeviceCollection_Item(col, 0, &dev);
96 ok(hr == S_OK, "Valid Item returned 0x%08x\n", hr);
97 ok(dev != NULL, "Device is null!\n");
98 if (dev != NULL)
99 {
100 char temp[128];
101 WCHAR *id = NULL;
102 if (IMMDevice_GetId(dev, &id) == S_OK)
103 {
104 IMMDevice *dev2;
105
106 temp[sizeof(temp)-1] = 0;
107 WideCharToMultiByte(CP_ACP, 0, id, -1, temp, sizeof(temp)-1, NULL, NULL);
108 trace("Device found: %s\n", temp);
109
110 hr = IMMDeviceEnumerator_GetDevice(mme, id, &dev2);
111 ok(hr == S_OK, "GetDevice failed: %08x\n", hr);
112
113 IMMDevice_Release(dev2);
114
115 CoTaskMemFree(id);
116 }
117 }
118 if (dev)
119 IMMDevice_Release(dev);
120 }
121 IMMDeviceCollection_Release(col);
122 }
123
notif_QueryInterface(IMMNotificationClient * iface,const GUID * riid,void ** obj)124 static HRESULT WINAPI notif_QueryInterface(IMMNotificationClient *iface,
125 const GUID *riid, void **obj)
126 {
127 ok(0, "Unexpected QueryInterface call\n");
128 return E_NOTIMPL;
129 }
130
notif_AddRef(IMMNotificationClient * iface)131 static ULONG WINAPI notif_AddRef(IMMNotificationClient *iface)
132 {
133 ok(0, "Unexpected AddRef call\n");
134 return 2;
135 }
136
notif_Release(IMMNotificationClient * iface)137 static ULONG WINAPI notif_Release(IMMNotificationClient *iface)
138 {
139 ok(0, "Unexpected Release call\n");
140 return 1;
141 }
142
notif_OnDeviceStateChanged(IMMNotificationClient * iface,const WCHAR * device_id,DWORD new_state)143 static HRESULT WINAPI notif_OnDeviceStateChanged(IMMNotificationClient *iface,
144 const WCHAR *device_id, DWORD new_state)
145 {
146 ok(0, "Unexpected OnDeviceStateChanged call\n");
147 return E_NOTIMPL;
148 }
149
notif_OnDeviceAdded(IMMNotificationClient * iface,const WCHAR * device_id)150 static HRESULT WINAPI notif_OnDeviceAdded(IMMNotificationClient *iface,
151 const WCHAR *device_id)
152 {
153 ok(0, "Unexpected OnDeviceAdded call\n");
154 return E_NOTIMPL;
155 }
156
notif_OnDeviceRemoved(IMMNotificationClient * iface,const WCHAR * device_id)157 static HRESULT WINAPI notif_OnDeviceRemoved(IMMNotificationClient *iface,
158 const WCHAR *device_id)
159 {
160 ok(0, "Unexpected OnDeviceRemoved call\n");
161 return E_NOTIMPL;
162 }
163
notif_OnDefaultDeviceChanged(IMMNotificationClient * iface,EDataFlow flow,ERole role,const WCHAR * device_id)164 static HRESULT WINAPI notif_OnDefaultDeviceChanged(IMMNotificationClient *iface,
165 EDataFlow flow, ERole role, const WCHAR *device_id)
166 {
167 ok(0, "Unexpected OnDefaultDeviceChanged call\n");
168 return E_NOTIMPL;
169 }
170
notif_OnPropertyValueChanged(IMMNotificationClient * iface,const WCHAR * device_id,const PROPERTYKEY key)171 static HRESULT WINAPI notif_OnPropertyValueChanged(IMMNotificationClient *iface,
172 const WCHAR *device_id, const PROPERTYKEY key)
173 {
174 ok(0, "Unexpected OnPropertyValueChanged call\n");
175 return E_NOTIMPL;
176 }
177
178 static IMMNotificationClientVtbl notif_vtbl = {
179 notif_QueryInterface,
180 notif_AddRef,
181 notif_Release,
182 notif_OnDeviceStateChanged,
183 notif_OnDeviceAdded,
184 notif_OnDeviceRemoved,
185 notif_OnDefaultDeviceChanged,
186 notif_OnPropertyValueChanged
187 };
188
189 static IMMNotificationClient notif = { ¬if_vtbl };
190
191 /* Only do parameter tests here, the actual MMDevice testing should be a separate test */
START_TEST(mmdevenum)192 START_TEST(mmdevenum)
193 {
194 static const WCHAR not_a_deviceW[] = {'n','o','t','a','d','e','v','i','c','e',0};
195
196 HRESULT hr;
197 IUnknown *unk = NULL;
198 IMMDeviceEnumerator *mme, *mme2;
199 ULONG ref;
200 IMMDeviceCollection *col;
201 IMMDevice *dev;
202
203 CoInitializeEx(NULL, COINIT_MULTITHREADED);
204 hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, (void**)&mme);
205 if (FAILED(hr))
206 {
207 skip("mmdevapi not available: 0x%08x\n", hr);
208 return;
209 }
210
211 /* Odd behavior.. bug? */
212 ref = IMMDeviceEnumerator_AddRef(mme);
213 ok(ref == 3, "Invalid reference count after incrementing: %u\n", ref);
214 IMMDeviceEnumerator_Release(mme);
215
216 hr = IMMDeviceEnumerator_QueryInterface(mme, &IID_IUnknown, (void**)&unk);
217 ok(hr == S_OK, "returned 0x%08x\n", hr);
218 if (hr != S_OK) return;
219
220 ok( (LONG_PTR)mme == (LONG_PTR)unk, "Pointers are unequal %p/%p\n", unk, mme);
221 IUnknown_Release(unk);
222
223 /* Proving that it is static.. */
224 hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, (void**)&mme2);
225 ok(hr == S_OK, "CoCreateInstance failed: 0x%08x\n", hr);
226 IMMDeviceEnumerator_Release(mme2);
227 ok(mme == mme2, "Pointers are not equal!\n");
228
229 hr = IMMDeviceEnumerator_QueryInterface(mme, &IID_IUnknown, NULL);
230 ok(hr == E_POINTER, "Null pointer on QueryInterface returned %08x\n", hr);
231
232 hr = IMMDeviceEnumerator_QueryInterface(mme, &GUID_NULL, (void**)&unk);
233 ok(!unk, "Unk not reset to null after invalid QI\n");
234 ok(hr == E_NOINTERFACE, "Invalid hr %08x returned on IID_NULL\n", hr);
235
236 hr = IMMDeviceEnumerator_GetDevice(mme, not_a_deviceW, NULL);
237 ok(hr == E_POINTER, "GetDevice gave wrong error: %08x\n", hr);
238
239 hr = IMMDeviceEnumerator_GetDevice(mme, NULL, &dev);
240 ok(hr == E_POINTER, "GetDevice gave wrong error: %08x\n", hr);
241
242 hr = IMMDeviceEnumerator_GetDevice(mme, not_a_deviceW, &dev);
243 ok(hr == E_INVALIDARG, "GetDevice gave wrong error: %08x\n", hr);
244
245 col = (void*)(LONG_PTR)0x12345678;
246 hr = IMMDeviceEnumerator_EnumAudioEndpoints(mme, 0xffff, DEVICE_STATEMASK_ALL, &col);
247 ok(hr == E_INVALIDARG, "Setting invalid data flow returned 0x%08x\n", hr);
248 ok(col == NULL, "Collection pointer non-null on failure\n");
249
250 hr = IMMDeviceEnumerator_EnumAudioEndpoints(mme, eAll, DEVICE_STATEMASK_ALL+1, &col);
251 ok(hr == E_INVALIDARG, "Setting invalid mask returned 0x%08x\n", hr);
252
253 hr = IMMDeviceEnumerator_EnumAudioEndpoints(mme, eAll, DEVICE_STATEMASK_ALL, NULL);
254 ok(hr == E_POINTER, "Invalid pointer returned: 0x%08x\n", hr);
255
256 hr = IMMDeviceEnumerator_EnumAudioEndpoints(mme, eAll, DEVICE_STATEMASK_ALL, &col);
257 ok(hr == S_OK, "Valid EnumAudioEndpoints returned 0x%08x\n", hr);
258 if (hr == S_OK)
259 {
260 ok(!!col, "Returned null pointer\n");
261 if (col)
262 test_collection(mme, col);
263 }
264
265 hr = IMMDeviceEnumerator_RegisterEndpointNotificationCallback(mme, NULL);
266 ok(hr == E_POINTER, "RegisterEndpointNotificationCallback failed: %08x\n", hr);
267
268 hr = IMMDeviceEnumerator_RegisterEndpointNotificationCallback(mme, ¬if);
269 ok(hr == S_OK, "RegisterEndpointNotificationCallback failed: %08x\n", hr);
270
271 hr = IMMDeviceEnumerator_RegisterEndpointNotificationCallback(mme, ¬if);
272 ok(hr == S_OK, "RegisterEndpointNotificationCallback failed: %08x\n", hr);
273
274 hr = IMMDeviceEnumerator_UnregisterEndpointNotificationCallback(mme, NULL);
275 ok(hr == E_POINTER, "UnregisterEndpointNotificationCallback failed: %08x\n", hr);
276
277 hr = IMMDeviceEnumerator_UnregisterEndpointNotificationCallback(mme, (IMMNotificationClient*)0xdeadbeef);
278 ok(hr == E_NOTFOUND, "UnregisterEndpointNotificationCallback failed: %08x\n", hr);
279
280 hr = IMMDeviceEnumerator_UnregisterEndpointNotificationCallback(mme, ¬if);
281 ok(hr == S_OK, "UnregisterEndpointNotificationCallback failed: %08x\n", hr);
282
283 hr = IMMDeviceEnumerator_UnregisterEndpointNotificationCallback(mme, ¬if);
284 ok(hr == S_OK, "UnregisterEndpointNotificationCallback failed: %08x\n", hr);
285
286 hr = IMMDeviceEnumerator_UnregisterEndpointNotificationCallback(mme, ¬if);
287 ok(hr == E_NOTFOUND, "UnregisterEndpointNotificationCallback failed: %08x\n", hr);
288
289 IMMDeviceEnumerator_Release(mme);
290 }
291