Reactos
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
34DEFINE_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 */
39static 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
124static 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
131static ULONG WINAPI notif_AddRef(IMMNotificationClient *iface)
132{
133 ok(0, "Unexpected AddRef call\n");
134 return 2;
135}
136
137static ULONG WINAPI notif_Release(IMMNotificationClient *iface)
138{
139 ok(0, "Unexpected Release call\n");
140 return 1;
141}
142
143static 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
150static 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
157static 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
164static 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
171static 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
178static 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
189static IMMNotificationClient notif = { ¬if_vtbl };
190
191/* Only do parameter tests here, the actual MMDevice testing should be a separate test */
192START_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}