summaryrefslogtreecommitdiff
path: root/general/toaster/toastpkg/toastva/util.c
blob: e2f68c5bfef50b246d08097d05c3c6bd5e4c1b74 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
/*++

Copyright (c) Microsoft Corporation.  All rights reserved.

    THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
    KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
    IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
    PURPOSE.

Module Name:

    util.c

Abstract:

    Utility routines used by the TOASTVA sample.

--*/

#include "precomp.h"
#pragma hdrstop

/*++

    Here's the description for the CMP_WaitNoPendingInstallEvents API used to
    suppress execution of this app (e.g., via autorun upon CD insertion) while
    device installation is underway...
    
    DWORD
    CMP_WaitNoPendingInstallEvents(
        _In_ DWORD dwTimeout
        );
    
    Routine Description:

        This routine waits until there are no pending device install events.
        If a timeout value is specified then it will return either when no
        install events are pending or when the timeout period has expired,
        whichever comes first. This routine is intended to be called after
        user-logon, only.

        NOTE: New install events can occur at anytime, this routine just
        indicates that there are no install events at this moment.

    Parameters:

        dwTimeout - Specifies the time-out interval, in milliseconds. The 
            function returns if the interval elapses, even if there are still 
            pending install events. If dwTimeout is zero, the function just 
            tests whether there are pending install events and returns 
            immediately. If dwTimeout is INFINITE, the function's time-out
            interval never elapses.

    Return Value:

        If the function succeeds, the return value indicates the event that 
        caused the function to return. If the function fails, the return value 
        is WAIT_FAILED. To get extended error information, call GetLastError.
        The return value on success is one of the following values:

        WAIT_ABANDONED  The specified object is a mutex object that was not
                        released by the thread that owned the mutex object 
                        before the owning thread terminated. Ownership of the 
                        mutex object is granted to the calling thread, and the 
                        mutex is set to nonsignaled.
        WAIT_OBJECT_0   The state of the specified object is signaled.
        WAIT_TIMEOUT    The time-out interval elapsed, and the object's state is
                        nonsignaled.

--*/

typedef DWORD (WINAPI *CMP_WAITNOPENDINGINSTALLEVENTS_PROC)(
    _In_ DWORD dwTimeout
    );


BOOL
IsDeviceInstallInProgress(VOID)

/*++

Routine Description:

    This routine dynamically retrieves the entrypoint to a Windows 2000 (and
    later) Configuration Manager (CM) API that can be used to detect whether 
    there are presently any device installations in-progress.  If this API is 
    not available (it may be obsoleted on future releases of the OS), then the 
    API assumes there are no device installations in progress.  This is OK, 
    because future versions of the OS will suppress auto-run when "Found New 
    Hardware" wizard is up, thus eliminating the need for apps to do their own 
    checking.

Arguments:

    none

Return Value:

    If there is presently a device installation in progress, the return value
    is TRUE.
    
    If there is not presently a device installation in progress, or we were
    unsuccessful in retrieving the necessary Configuration Manager API, then
    the return value is FALSE.

--*/

{
    HMODULE hModule;
    CMP_WAITNOPENDINGINSTALLEVENTS_PROC pCMP_WaitNoPendingInstallEvents;
    
    hModule = GetModuleHandle(L"setupapi.dll");
    
    if(!hModule) {
        //
        // Should never happen since we're linked to setupapi, but...
        //
        return FALSE;
    }
    
    pCMP_WaitNoPendingInstallEvents = 
        (CMP_WAITNOPENDINGINSTALLEVENTS_PROC)GetProcAddress(
                                               hModule,
                                               "CMP_WaitNoPendingInstallEvents"
                                               );
    if(!pCMP_WaitNoPendingInstallEvents) {
        //
        // We're running on a release of the OS that doesn't supply this API.
        // Trust the OS to suppress autorun when appropriate.
        //
        return FALSE;
    }

    return (pCMP_WaitNoPendingInstallEvents(0) == WAIT_TIMEOUT);
}


VOID
MarkDevicesAsNeedReinstall(
    _In_ HDEVINFO DeviceInfoSet
    )

/*++

Routine Description:

    This routine enumerates every device information element in the specified
    list and sets the CONFIGFLAG_REINSTALL registry flag for each one.

Arguments:

    DeviceInfoSet - Supplies a handle to the device information set whose 
        members are to be marked as need-reinstall.

Return Value:

    none

--*/

{
    SP_DEVINFO_DATA DeviceInfoData;
    DWORD i, ConfigFlags;
    
    DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
    
    for(i = 0;
        SetupDiEnumDeviceInfo(DeviceInfoSet, i, &DeviceInfoData);
        i++)
    {
        ConfigFlags = GetDeviceConfigFlags(DeviceInfoSet, &DeviceInfoData);
        ConfigFlags |= CONFIGFLAG_REINSTALL;
        SetDeviceConfigFlags(DeviceInfoSet, &DeviceInfoData, ConfigFlags);
    }
}


DWORD
GetDeviceConfigFlags(
    _In_ HDEVINFO         DeviceInfoSet,
    _In_ PSP_DEVINFO_DATA DeviceInfoData
    )
    
/*++

Routine Description:

    This routine retrieves the ConfigFlags registry property for the specified
    device info element, or zero if the property cannot be retrieved (e.g.,
    because ConfigFlags haven't yet been set by Found New Hardware process).

Arguments:

    DeviceInfoSet - Supplies a handle to the device information set containing
        the device of interest.
        
    DeviceInfoData - Supplies context of a device info element for which
        ConfigFlags is to be retrieved.

Return Value:

    If device's REG_DWORD ConfigFlags property can be retrieved, it is returned.
    Otherwise, zero is returned.

--*/

{
    DWORD ConfigFlags, RegDataType;
    
    if(!SetupDiGetDeviceRegistryProperty(DeviceInfoSet,
                                         DeviceInfoData,
                                         SPDRP_CONFIGFLAGS,
                                         &RegDataType,
                                         (PBYTE)&ConfigFlags,
                                         sizeof(ConfigFlags),
                                         NULL)
       || (RegDataType != REG_DWORD))
    {
        //
        // It's possible that this property isn't there, although we should
        // never enounter other problems like wrong datatype or data length
        // longer than sizeof(DWORD).  In any event, just return zero.
        //
        ConfigFlags = 0;
    }
    
    return ConfigFlags;
}


VOID
SetDeviceConfigFlags(
    _In_ HDEVINFO         DeviceInfoSet,
    _In_ PSP_DEVINFO_DATA DeviceInfoData, 
    _In_ DWORD            ConfigFlags
    )

/*++

Routine Description:

    This routine sets a device's ConfigFlags property to the specified value.

Arguments:

    DeviceInfoSet - Supplies a handle to the device information set containing
        the device of interest.
        
    DeviceInfoData - Supplies context of a device info element for which
        ConfigFlags is to be set.
        
    ConfigFlags - Specifies the value to be stored to the device's ConfigFlags
        property.

Return Value:

    none

--*/

{
    SetupDiSetDeviceRegistryProperty(DeviceInfoSet,
                                     DeviceInfoData,
                                     SPDRP_CONFIGFLAGS,
                                     (PBYTE)&ConfigFlags,
                                     sizeof(ConfigFlags)
                                    );
}


HDEVINFO
GetNonPresentDevices(
    _In_  LPCWSTR   Enumerator OPTIONAL,
    _In_  LPCWSTR   HardwareID
    )
    
/*++

Routine Description:

    This routine retrieves any non-present devices matching the specified 
    criteria, and returns them in a device information set.

Arguments:

    Enumerator - Optionally, supplies the name of the Enumerator under which 
        this device may be found.  If the device may show up under more than 
        one enumerator, the routine can be called with Enumerator specified as
        NULL, in which case all device instances in the registry are examined.
        
    HardwareID - Supplies the hardware ID to be searched for.  This will be
        compared against each of the hardware IDs for all device instances in
        the system (potentially filtered based on Enumerator), present or not.

Return Value:

    If any non-present devices are discovered, this routine returns a device
    information set containing those devices.  This set must be freed via
    SetupDiDestroyDeviceInfoList by the caller.
    
    If no such devices are encountered (or if an error occurs), the return
    value is INVALID_HANDLE_VALUE.  GetLastError will indicate the cause of
    failure.

--*/

{
    HDEVINFO AllDevs, ExistingNonPresentDevices;
    DWORD i, Err;
    SP_DEVINFO_DATA DeviceInfoData;
    LPWSTR HwIdBuffer, CurId;
    DWORD HwIdBufferLen, RegDataType, RequiredSize;
    BOOL bRet;
    ULONG Status, Problem;
    TCHAR DeviceInstanceId[MAX_DEVNODE_ID_LEN];

    ExistingNonPresentDevices = INVALID_HANDLE_VALUE;
    
    AllDevs = SetupDiGetClassDevs(NULL,
                                  Enumerator,
                                  NULL,
                                  DIGCF_ALLCLASSES
                                 );
                                 
    if(AllDevs == INVALID_HANDLE_VALUE) {
        //
        // last error has already been set during the above call.
        //
        return INVALID_HANDLE_VALUE;
    }
                                  
    //
    // Iterate through each device we found, comparing its hardware ID(s)
    // against the one we were passed in.
    //
    DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
    
    HwIdBuffer = NULL;
    HwIdBufferLen = 0;
    Err = NO_ERROR;
    bRet = FALSE;
    
    i = 0;
        
    while(SetupDiEnumDeviceInfo(AllDevs, i, &DeviceInfoData)) {
        //
        // Retrieve the HardwareID property for this device info element
        //
        if(!SetupDiGetDeviceRegistryProperty(AllDevs,
                                             &DeviceInfoData,
                                             SPDRP_HARDWAREID,
                                             &RegDataType,
                                             (PBYTE)HwIdBuffer,
                                             HwIdBufferLen,
                                             &RequiredSize)) {
            //
            // If the failure was due to buffer-too-small, we can resize and
            // try again.
            //
            if(GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
            
                if(HwIdBuffer) {
                    GlobalFree(HwIdBuffer);
                }
                
                HwIdBuffer = GlobalAlloc(0, RequiredSize);
                if(HwIdBuffer) {
                    HwIdBufferLen = RequiredSize;
                    //
                    // try again
                    //
                    continue;
                } else {
                    //
                    // We failed to allocate the buffer we needed.  This is
                    // considered a critical failure that should cause us to
                    // bail.
                    //
                    Err = ERROR_NOT_ENOUGH_MEMORY;
                    break;
                }
                
            } else {
                //
                // We failed to retrieve the property for some other reason.
                // Skip this device and move on to the next.
                //
                i++;
                continue;
            }
        }

        if((RegDataType != REG_MULTI_SZ) || (RequiredSize < sizeof(TCHAR))) {
            //
            // Data is invalid--this should never happen, but we'll skip the
            // device in this case...
            //
            i++;
            continue;
        }
        
        //
        // If we get to here, then we successfully retrieved the multi-sz
        // hardware id list for this device.  Compare each of those IDs with
        // the caller-supplied one.
        //
        for(CurId = HwIdBuffer; CurId && *CurId; CurId += (lstrlen(CurId) + 1)) {
        
            if(!lstrcmpi(CurId, HardwareID)) {
                //
                // We found a match!
                //
                bRet = TRUE;
                
                //
                // If the device isn't currently present (as indicated by
                // failure to retrieve its status), then add it to the list of
                // such devices to be returned to the caller.
                //
                if(CR_SUCCESS != CM_Get_DevNode_Status(&Status,
                                                       &Problem,
                                                       (DEVNODE)DeviceInfoData.DevInst,
                                                       0))
                {
                    if(ExistingNonPresentDevices == INVALID_HANDLE_VALUE) {
                        //
                        // This is the first non-present device we've 
                        // encountered--we need to create the HDEVINFO set.
                        //
                        ExistingNonPresentDevices = 
                            SetupDiCreateDeviceInfoList(NULL, NULL);
                        
                        if(ExistingNonPresentDevices == INVALID_HANDLE_VALUE) {
                            //
                            // Failure to create this set is a critical error!
                            //
                            Err = GetLastError();
                            bRet = FALSE;
                            break;
                        }
                    }
                        
                    //
                    // We need to get the device instance's name so we can
                    // open it up into our "non-present devices" list
                    //
                    if(!SetupDiGetDeviceInstanceId(AllDevs,
                                                   &DeviceInfoData,
                                                   DeviceInstanceId,
                                                   sizeof(DeviceInstanceId) / sizeof(TCHAR),
                                                   NULL)) {
                        //
                        // Should never fail, but considered critical if it
                        // does...
                        //
                        Err = GetLastError();
                        bRet = FALSE;
                        break;
                    }

                    //
                    // Now open up the non-present device into our list.
                    //
                    if(!SetupDiOpenDeviceInfo(ExistingNonPresentDevices,
                                              DeviceInstanceId,
                                              NULL,
                                              0,
                                              NULL)) {
                        //
                        // This failure is also considered critical!
                        //                          
                        Err = GetLastError();
                        bRet = FALSE;
                    }

                    break;
                }
            }
        }

        if(Err != NO_ERROR) {
            //
            // Critical error encountered--bail!
            //
            break;
        }

        //
        // Move onto the next device instance
        //
        i++;
    }
    
    if(HwIdBuffer) {
        GlobalFree(HwIdBuffer);
    }

    //
    // We can now destroy our temporary list of all devices under consideration
    //
    SetupDiDestroyDeviceInfoList(AllDevs);

    if((Err != NO_ERROR) && 
       (ExistingNonPresentDevices != INVALID_HANDLE_VALUE)) {
        //
        // We encountered a critical error, so we need to destroy the (partial)
        // list of non-present devices we'd built.
        //
        SetupDiDestroyDeviceInfoList(ExistingNonPresentDevices);
        ExistingNonPresentDevices = INVALID_HANDLE_VALUE;
    }

    SetLastError(Err);

    return ExistingNonPresentDevices;
}