explorerframe: Implement RemoveRoot and RemoveAllRoots.
[wine] / dlls / setupapi / query.c
1 /*
2  * setupapi query functions
3  *
4  * Copyright 2006 James Hawkins
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include <stdarg.h>
22
23 #include "windef.h"
24 #include "winbase.h"
25 #include "winuser.h"
26 #include "winreg.h"
27 #include "setupapi.h"
28 #include "advpub.h"
29 #include "winnls.h"
30 #include "wine/debug.h"
31 #include "wine/unicode.h"
32 #include "setupapi_private.h"
33
34 WINE_DEFAULT_DEBUG_CHANNEL(setupapi);
35
36 #ifdef __i386__
37 static const WCHAR source_disks_names_platform[] =
38     {'S','o','u','r','c','e','D','i','s','k','s','N','a','m','e','s','.','x','8','6',0};
39 static const WCHAR source_disks_files_platform[] =
40     {'S','o','u','r','c','e','D','i','s','k','s','F','i','l','e','s','.','x','8','6',0};
41 #elif defined(__x86_64)
42 static const WCHAR source_disks_names_platform[] =
43     {'S','o','u','r','c','e','D','i','s','k','s','N','a','m','e','s','.','a','m','d','6','4',0};
44 static const WCHAR source_disks_files_platform[] =
45     {'S','o','u','r','c','e','D','i','s','k','s','F','i','l','e','s','.','a','m','d','6','4',0};
46 #else  /* FIXME: other platforms */
47 static const WCHAR source_disks_names_platform[] =
48     {'S','o','u','r','c','e','D','i','s','k','s','N','a','m','e','s',0};
49 static const WCHAR source_disks_files_platform[] =
50     {'S','o','u','r','c','e','D','i','s','k','s','F','i','l','e','s',0};
51 #endif
52 static const WCHAR source_disks_names[] =
53     {'S','o','u','r','c','e','D','i','s','k','s','N','a','m','e','s',0};
54 static const WCHAR source_disks_files[] =
55     {'S','o','u','r','c','e','D','i','s','k','s','F','i','l','e','s',0};
56
57 /* fills the PSP_INF_INFORMATION struct fill_info is TRUE
58  * always returns the required size of the information
59  */
60 static BOOL fill_inf_info(HINF inf, PSP_INF_INFORMATION buffer, DWORD size, DWORD *required)
61 {
62     LPCWSTR filename = PARSER_get_inf_filename(inf);
63     DWORD total_size = FIELD_OFFSET(SP_INF_INFORMATION, VersionData)
64                         + (lstrlenW(filename) + 1) * sizeof(WCHAR);
65
66     if (required) *required = total_size;
67
68     /* FIXME: we need to parse the INF file to find the correct version info */
69     if (buffer)
70     {
71         if (size < total_size)
72         {
73             SetLastError(ERROR_INSUFFICIENT_BUFFER);
74             return FALSE;
75         }
76         buffer->InfStyle = INF_STYLE_WIN4;
77         buffer->InfCount = 1;
78         /* put the filename in buffer->VersionData */
79         lstrcpyW((LPWSTR)&buffer->VersionData[0], filename);
80     }
81     return TRUE;
82 }
83
84 static HINF search_for_inf(LPCVOID InfSpec, DWORD SearchControl)
85 {
86     HINF hInf = INVALID_HANDLE_VALUE;
87     WCHAR inf_path[MAX_PATH];
88
89     static const WCHAR infW[] = {'\\','i','n','f','\\',0};
90     static const WCHAR system32W[] = {'\\','s','y','s','t','e','m','3','2','\\',0};
91
92     if (SearchControl == INFINFO_REVERSE_DEFAULT_SEARCH)
93     {
94         GetWindowsDirectoryW(inf_path, MAX_PATH);
95         lstrcatW(inf_path, system32W);
96         lstrcatW(inf_path, InfSpec);
97
98         hInf = SetupOpenInfFileW(inf_path, NULL,
99                                  INF_STYLE_OLDNT | INF_STYLE_WIN4, NULL);
100         if (hInf != INVALID_HANDLE_VALUE)
101             return hInf;
102
103         GetWindowsDirectoryW(inf_path, MAX_PATH);
104         lstrcpyW(inf_path, infW);
105         lstrcatW(inf_path, InfSpec);
106
107         return SetupOpenInfFileW(inf_path, NULL,
108                                  INF_STYLE_OLDNT | INF_STYLE_WIN4, NULL);
109     }
110
111     return INVALID_HANDLE_VALUE;
112 }
113
114 /***********************************************************************
115  *      SetupGetInfInformationA    (SETUPAPI.@)
116  *
117  */
118 BOOL WINAPI SetupGetInfInformationA(LPCVOID InfSpec, DWORD SearchControl,
119                                     PSP_INF_INFORMATION ReturnBuffer,
120                                     DWORD ReturnBufferSize, PDWORD RequiredSize)
121 {
122     LPWSTR inf = (LPWSTR)InfSpec;
123     DWORD len;
124     BOOL ret;
125
126     if (InfSpec && SearchControl >= INFINFO_INF_NAME_IS_ABSOLUTE)
127     {
128         len = lstrlenA(InfSpec) + 1;
129         inf = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
130         MultiByteToWideChar(CP_ACP, 0, InfSpec, -1, inf, len);
131     }
132
133     ret = SetupGetInfInformationW(inf, SearchControl, ReturnBuffer,
134                                   ReturnBufferSize, RequiredSize);
135
136     if (SearchControl >= INFINFO_INF_NAME_IS_ABSOLUTE)
137         HeapFree(GetProcessHeap(), 0, inf);
138
139     return ret;
140 }
141
142 /***********************************************************************
143  *      SetupGetInfInformationW    (SETUPAPI.@)
144  * 
145  * BUGS
146  *   Only handles the case when InfSpec is an INF handle.
147  */
148 BOOL WINAPI SetupGetInfInformationW(LPCVOID InfSpec, DWORD SearchControl,
149                                      PSP_INF_INFORMATION ReturnBuffer,
150                                      DWORD ReturnBufferSize, PDWORD RequiredSize)
151 {
152     HINF inf;
153     BOOL ret;
154     DWORD infSize;
155
156     TRACE("(%p, %d, %p, %d, %p)\n", InfSpec, SearchControl, ReturnBuffer,
157            ReturnBufferSize, RequiredSize);
158
159     if (!InfSpec)
160     {
161         if (SearchControl == INFINFO_INF_SPEC_IS_HINF)
162             SetLastError(ERROR_INVALID_HANDLE);
163         else
164             SetLastError(ERROR_INVALID_PARAMETER);
165
166         return FALSE;
167     }
168
169     switch (SearchControl)
170     {
171         case INFINFO_INF_SPEC_IS_HINF:
172             inf = (HINF)InfSpec;
173             break;
174         case INFINFO_INF_NAME_IS_ABSOLUTE:
175         case INFINFO_DEFAULT_SEARCH:
176             inf = SetupOpenInfFileW(InfSpec, NULL,
177                                     INF_STYLE_OLDNT | INF_STYLE_WIN4, NULL);
178             break;
179         case INFINFO_REVERSE_DEFAULT_SEARCH:
180             inf = search_for_inf(InfSpec, SearchControl);
181             break;
182         case INFINFO_INF_PATH_LIST_SEARCH:
183             FIXME("Unhandled search control: %d\n", SearchControl);
184
185             if (RequiredSize)
186                 *RequiredSize = 0;
187
188             return FALSE;
189         default:
190             SetLastError(ERROR_INVALID_PARAMETER);
191             return FALSE;
192     }
193
194     if (inf == INVALID_HANDLE_VALUE)
195     {
196         SetLastError(ERROR_FILE_NOT_FOUND);
197         return FALSE;
198     }
199
200     ret = fill_inf_info(inf, ReturnBuffer, ReturnBufferSize, &infSize);
201     if (!ReturnBuffer && (ReturnBufferSize >= infSize))
202     {
203         SetLastError(ERROR_INVALID_PARAMETER);
204         ret = FALSE;
205     }
206     if (RequiredSize) *RequiredSize = infSize;
207
208     if (SearchControl >= INFINFO_INF_NAME_IS_ABSOLUTE)
209         SetupCloseInfFile(inf);
210
211     return ret;
212 }
213
214 /***********************************************************************
215  *      SetupQueryInfFileInformationA    (SETUPAPI.@)
216  */
217 BOOL WINAPI SetupQueryInfFileInformationA(PSP_INF_INFORMATION InfInformation,
218                                           UINT InfIndex, PSTR ReturnBuffer,
219                                           DWORD ReturnBufferSize, PDWORD RequiredSize)
220 {
221     LPWSTR filenameW;
222     DWORD size;
223     BOOL ret;
224
225     ret = SetupQueryInfFileInformationW(InfInformation, InfIndex, NULL, 0, &size);
226     if (!ret)
227         return FALSE;
228
229     filenameW = HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR));
230
231     ret = SetupQueryInfFileInformationW(InfInformation, InfIndex,
232                                         filenameW, size, &size);
233     if (!ret)
234     {
235         HeapFree(GetProcessHeap(), 0, filenameW);
236         return FALSE;
237     }
238
239     if (RequiredSize)
240         *RequiredSize = size;
241
242     if (!ReturnBuffer)
243     {
244         HeapFree(GetProcessHeap(), 0, filenameW);
245         if (ReturnBufferSize)
246         {
247             SetLastError(ERROR_INVALID_PARAMETER);
248             return FALSE;
249         }
250
251         return TRUE;
252     }
253
254     if (size > ReturnBufferSize)
255     {
256         HeapFree(GetProcessHeap(), 0, filenameW);
257         SetLastError(ERROR_INSUFFICIENT_BUFFER);
258         return FALSE;
259     }
260
261     WideCharToMultiByte(CP_ACP, 0, filenameW, -1, ReturnBuffer, size, NULL, NULL);
262     HeapFree(GetProcessHeap(), 0, filenameW);
263
264     return ret;
265 }
266
267 /***********************************************************************
268  *      SetupQueryInfFileInformationW    (SETUPAPI.@)
269  */
270 BOOL WINAPI SetupQueryInfFileInformationW(PSP_INF_INFORMATION InfInformation,
271                                           UINT InfIndex, PWSTR ReturnBuffer,
272                                           DWORD ReturnBufferSize, PDWORD RequiredSize) 
273 {
274     DWORD len;
275     LPWSTR ptr;
276
277     TRACE("(%p, %u, %p, %d, %p) Stub!\n", InfInformation, InfIndex,
278           ReturnBuffer, ReturnBufferSize, RequiredSize);
279
280     if (!InfInformation)
281     {
282         SetLastError(ERROR_INVALID_PARAMETER);
283         return FALSE;
284     }
285
286     if (InfIndex != 0)
287         FIXME("Appended INF files are not handled\n");
288
289     ptr = (LPWSTR)&InfInformation->VersionData[0];
290     len = lstrlenW(ptr);
291
292     if (RequiredSize)
293         *RequiredSize = len + 1;
294
295     if (!ReturnBuffer)
296         return TRUE;
297
298     if (ReturnBufferSize < len)
299     {
300         SetLastError(ERROR_INSUFFICIENT_BUFFER);
301         return FALSE;
302     }
303
304     lstrcpyW(ReturnBuffer, ptr);
305     return TRUE;
306 }
307
308 /***********************************************************************
309  *            SetupGetSourceFileLocationA   (SETUPAPI.@)
310  */
311
312 BOOL WINAPI SetupGetSourceFileLocationA( HINF hinf, PINFCONTEXT context, PCSTR filename,
313                                          PUINT source_id, PSTR buffer, DWORD buffer_size,
314                                          PDWORD required_size )
315 {
316     BOOL ret = FALSE;
317     WCHAR *filenameW = NULL, *bufferW = NULL;
318     DWORD required;
319     INT size;
320
321     TRACE("%p, %p, %s, %p, %p, 0x%08x, %p\n", hinf, context, debugstr_a(filename), source_id,
322           buffer, buffer_size, required_size);
323
324     if (filename && *filename && !(filenameW = strdupAtoW( filename )))
325         return FALSE;
326
327     if (!SetupGetSourceFileLocationW( hinf, context, filenameW, source_id, NULL, 0, &required ))
328         goto done;
329
330     if (!(bufferW = HeapAlloc( GetProcessHeap(), 0, required * sizeof(WCHAR) )))
331         goto done;
332
333     if (!SetupGetSourceFileLocationW( hinf, context, filenameW, source_id, bufferW, required, NULL ))
334         goto done;
335
336     size = WideCharToMultiByte( CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL );
337     if (required_size) *required_size = size;
338
339     if (buffer)
340     {
341         if (buffer_size >= size)
342             WideCharToMultiByte( CP_ACP, 0, bufferW, -1, buffer, buffer_size, NULL, NULL );
343         else
344         {
345             SetLastError( ERROR_INSUFFICIENT_BUFFER );
346             goto done;
347         }
348     }
349     ret = TRUE;
350
351  done:
352     HeapFree( GetProcessHeap(), 0, filenameW );
353     HeapFree( GetProcessHeap(), 0, bufferW );
354     return ret;
355 }
356
357 static LPWSTR get_source_id( HINF hinf, PINFCONTEXT context, PCWSTR filename )
358 {
359     DWORD size;
360     LPWSTR source_id;
361
362     if (!SetupFindFirstLineW( hinf, source_disks_files_platform, filename, context ) &&
363         !SetupFindFirstLineW( hinf, source_disks_files, filename, context ))
364         return NULL;
365
366     if (!SetupGetStringFieldW( context, 1, NULL, 0, &size ))
367         return NULL;
368
369     if (!(source_id = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) )))
370         return NULL;
371
372     if (!SetupGetStringFieldW( context, 1, source_id, size, NULL ))
373     {
374         HeapFree( GetProcessHeap(), 0, source_id );
375         return NULL;
376     }
377
378     if (!SetupFindFirstLineW( hinf, source_disks_names_platform, source_id, context ) &&
379         !SetupFindFirstLineW( hinf, source_disks_names, source_id, context ))
380     {
381         HeapFree( GetProcessHeap(), 0, source_id );
382         return NULL;
383     }
384     return source_id;
385 }
386
387 /***********************************************************************
388  *            SetupGetSourceFileLocationW   (SETUPAPI.@)
389  */
390
391 BOOL WINAPI SetupGetSourceFileLocationW( HINF hinf, PINFCONTEXT context, PCWSTR filename,
392                                          PUINT source_id, PWSTR buffer, DWORD buffer_size,
393                                          PDWORD required_size )
394 {
395     INFCONTEXT ctx;
396     WCHAR *end, *source_id_str;
397
398     TRACE("%p, %p, %s, %p, %p, 0x%08x, %p\n", hinf, context, debugstr_w(filename), source_id,
399           buffer, buffer_size, required_size);
400
401     if (!context) context = &ctx;
402
403     if (!(source_id_str = get_source_id( hinf, context, filename )))
404         return FALSE;
405
406     *source_id = strtolW( source_id_str, &end, 10 );
407     if (end == source_id_str || *end)
408     {
409         HeapFree( GetProcessHeap(), 0, source_id_str );
410         return FALSE;
411     }
412     HeapFree( GetProcessHeap(), 0, source_id_str );
413
414     if (SetupGetStringFieldW( context, 4, buffer, buffer_size, required_size ))
415         return TRUE;
416
417     if (required_size) *required_size = 1;
418     if (buffer)
419     {
420         if (buffer_size >= 1) buffer[0] = 0;
421         else
422         {
423             SetLastError( ERROR_INSUFFICIENT_BUFFER );
424             return FALSE;
425         }
426     }
427     return TRUE;
428 }
429
430 /***********************************************************************
431  *            SetupGetSourceInfoA  (SETUPAPI.@)
432  */
433
434 BOOL WINAPI SetupGetSourceInfoA( HINF hinf, UINT source_id, UINT info,
435                                  PSTR buffer, DWORD buffer_size, LPDWORD required_size )
436 {
437     BOOL ret = FALSE;
438     WCHAR *bufferW = NULL;
439     DWORD required;
440     INT size;
441
442     TRACE("%p, %d, %d, %p, %d, %p\n", hinf, source_id, info, buffer, buffer_size,
443           required_size);
444
445     if (!SetupGetSourceInfoW( hinf, source_id, info, NULL, 0, &required ))
446         return FALSE;
447
448     if (!(bufferW = HeapAlloc( GetProcessHeap(), 0, required * sizeof(WCHAR) )))
449         return FALSE;
450
451     if (!SetupGetSourceInfoW( hinf, source_id, info, bufferW, required, NULL ))
452         goto done;
453
454     size = WideCharToMultiByte( CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL );
455     if (required_size) *required_size = size;
456
457     if (buffer)
458     {
459         if (buffer_size >= size)
460             WideCharToMultiByte( CP_ACP, 0, bufferW, -1, buffer, buffer_size, NULL, NULL );
461         else
462         {
463             SetLastError( ERROR_INSUFFICIENT_BUFFER );
464             goto done;
465         }
466     }
467     ret = TRUE;
468
469  done:
470     HeapFree( GetProcessHeap(), 0, bufferW );
471     return ret;
472 }
473
474 /***********************************************************************
475  *            SetupGetSourceInfoW  (SETUPAPI.@)
476  */
477
478 BOOL WINAPI SetupGetSourceInfoW( HINF hinf, UINT source_id, UINT info,
479                                  PWSTR buffer, DWORD buffer_size, LPDWORD required_size )
480 {
481     INFCONTEXT ctx;
482     WCHAR source_id_str[11];
483     static const WCHAR fmt[] = {'%','d',0};
484     DWORD index;
485
486     TRACE("%p, %d, %d, %p, %d, %p\n", hinf, source_id, info, buffer, buffer_size,
487           required_size);
488
489     sprintfW( source_id_str, fmt, source_id );
490
491     if (!SetupFindFirstLineW( hinf, source_disks_names_platform, source_id_str, &ctx ) &&
492         !SetupFindFirstLineW( hinf, source_disks_names, source_id_str, &ctx ))
493         return FALSE;
494
495     switch (info)
496     {
497     case SRCINFO_PATH:          index = 4; break;
498     case SRCINFO_TAGFILE:       index = 2; break;
499     case SRCINFO_DESCRIPTION:   index = 1; break;
500     default:
501         WARN("unknown info level: %d\n", info);
502         return FALSE;
503     }
504
505     if (SetupGetStringFieldW( &ctx, index, buffer, buffer_size, required_size ))
506         return TRUE;
507
508     if (required_size) *required_size = 1;
509     if (buffer)
510     {
511         if (buffer_size >= 1) buffer[0] = 0;
512         else
513         {
514             SetLastError( ERROR_INSUFFICIENT_BUFFER );
515             return FALSE;
516         }
517     }
518     return TRUE;
519 }
520
521 /***********************************************************************
522  *            SetupGetTargetPathA   (SETUPAPI.@)
523  */
524
525 BOOL WINAPI SetupGetTargetPathA( HINF hinf, PINFCONTEXT context, PCSTR section, PSTR buffer,
526                                  DWORD buffer_size, PDWORD required_size )
527 {
528     BOOL ret = FALSE;
529     WCHAR *sectionW = NULL, *bufferW = NULL;
530     DWORD required;
531     INT size;
532
533     TRACE("%p, %p, %s, %p, 0x%08x, %p\n", hinf, context, debugstr_a(section), buffer,
534           buffer_size, required_size);
535
536     if (section && !(sectionW = strdupAtoW( section )))
537         return FALSE;
538
539     if (!SetupGetTargetPathW( hinf, context, sectionW, NULL, 0, &required ))
540         goto done;
541
542     if (!(bufferW = HeapAlloc( GetProcessHeap(), 0, required * sizeof(WCHAR) )))
543         goto done;
544
545     if (!SetupGetTargetPathW( hinf, context, sectionW, bufferW, required, NULL ))
546         goto done;
547
548     size = WideCharToMultiByte( CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL );
549     if (required_size) *required_size = size;
550
551     if (buffer)
552     {
553         if (buffer_size >= size)
554             WideCharToMultiByte( CP_ACP, 0, bufferW, -1, buffer, buffer_size, NULL, NULL );
555         else
556         {
557             SetLastError( ERROR_INSUFFICIENT_BUFFER );
558             goto done;
559         }
560     }
561     ret = TRUE;
562
563  done:
564     HeapFree( GetProcessHeap(), 0, sectionW );
565     HeapFree( GetProcessHeap(), 0, bufferW );
566     return ret;
567 }
568
569 /***********************************************************************
570  *            SetupGetTargetPathW   (SETUPAPI.@)
571  */
572
573 BOOL WINAPI SetupGetTargetPathW( HINF hinf, PINFCONTEXT context, PCWSTR section, PWSTR buffer,
574                                  DWORD buffer_size, PDWORD required_size )
575 {
576     static const WCHAR destination_dirs[] =
577         {'D','e','s','t','i','n','a','t','i','o','n','D','i','r','s',0};
578     static const WCHAR default_dest_dir[]  =
579         {'D','e','f','a','u','l','t','D','e','s','t','D','i','r',0};
580
581     INFCONTEXT ctx;
582     WCHAR *dir, systemdir[MAX_PATH];
583     unsigned int size;
584     BOOL ret = FALSE;
585
586     TRACE("%p, %p, %s, %p, 0x%08x, %p\n", hinf, context, debugstr_w(section), buffer,
587           buffer_size, required_size);
588
589     if (context) ret = SetupFindFirstLineW( hinf, destination_dirs, NULL, context );
590     else if (section)
591     {
592         if (!(ret = SetupFindFirstLineW( hinf, destination_dirs, section, &ctx )))
593             ret = SetupFindFirstLineW( hinf, destination_dirs, default_dest_dir, &ctx );
594     }
595     if (!ret || !(dir = PARSER_get_dest_dir( context ? context : &ctx )))
596     {
597         GetSystemDirectoryW( systemdir, MAX_PATH );
598         dir = systemdir;
599     }
600     size = strlenW( dir ) + 1;
601     if (required_size) *required_size = size;
602
603     if (buffer)
604     {
605         if (buffer_size >= size)
606             lstrcpyW( buffer, dir );
607         else
608         {
609             SetLastError( ERROR_INSUFFICIENT_BUFFER );
610             HeapFree( GetProcessHeap(), 0, dir );
611             return FALSE;
612         }
613     }
614     if (dir != systemdir) HeapFree( GetProcessHeap(), 0, dir );
615     return TRUE;
616 }
617
618 /***********************************************************************
619  *            SetupQueryInfOriginalFileInformationA   (SETUPAPI.@)
620  */
621 BOOL WINAPI SetupQueryInfOriginalFileInformationA(
622     PSP_INF_INFORMATION InfInformation, UINT InfIndex,
623     PSP_ALTPLATFORM_INFO AlternativePlatformInfo,
624     PSP_ORIGINAL_FILE_INFO_A OriginalFileInfo)
625 {
626     BOOL ret;
627     SP_ORIGINAL_FILE_INFO_W OriginalFileInfoW;
628
629     TRACE("(%p, %d, %p, %p)\n", InfInformation, InfIndex,
630         AlternativePlatformInfo, OriginalFileInfo);
631
632     if (OriginalFileInfo->cbSize != sizeof(*OriginalFileInfo))
633     {
634         WARN("incorrect OriginalFileInfo->cbSize of %d\n", OriginalFileInfo->cbSize);
635         SetLastError( ERROR_INVALID_USER_BUFFER );
636         return FALSE;
637     }
638
639     OriginalFileInfoW.cbSize = sizeof(OriginalFileInfoW);
640     ret = SetupQueryInfOriginalFileInformationW(InfInformation, InfIndex,
641         AlternativePlatformInfo, &OriginalFileInfoW);
642     if (ret)
643     {
644         WideCharToMultiByte(CP_ACP, 0, OriginalFileInfoW.OriginalInfName, -1,
645             OriginalFileInfo->OriginalInfName, MAX_PATH, NULL, NULL);
646         WideCharToMultiByte(CP_ACP, 0, OriginalFileInfoW.OriginalCatalogName, -1,
647             OriginalFileInfo->OriginalCatalogName, MAX_PATH, NULL, NULL);
648     }
649
650     return ret;
651 }
652
653 /***********************************************************************
654  *            SetupQueryInfOriginalFileInformationW   (SETUPAPI.@)
655  */
656 BOOL WINAPI SetupQueryInfOriginalFileInformationW(
657     PSP_INF_INFORMATION InfInformation, UINT InfIndex,
658     PSP_ALTPLATFORM_INFO AlternativePlatformInfo,
659     PSP_ORIGINAL_FILE_INFO_W OriginalFileInfo)
660 {
661     LPCWSTR inf_name;
662     LPCWSTR inf_path;
663     HINF hinf;
664     static const WCHAR wszVersion[] = { 'V','e','r','s','i','o','n',0 };
665     static const WCHAR wszCatalogFile[] = { 'C','a','t','a','l','o','g','F','i','l','e',0 };
666
667     FIXME("(%p, %d, %p, %p): semi-stub\n", InfInformation, InfIndex,
668         AlternativePlatformInfo, OriginalFileInfo);
669
670     if (OriginalFileInfo->cbSize != sizeof(*OriginalFileInfo))
671     {
672         WARN("incorrect OriginalFileInfo->cbSize of %d\n", OriginalFileInfo->cbSize);
673         SetLastError(ERROR_INVALID_USER_BUFFER);
674         return FALSE;
675     }
676
677     inf_path = (LPWSTR)&InfInformation->VersionData[0];
678
679     /* FIXME: we should get OriginalCatalogName from CatalogFile line in
680      * the original inf file and cache it, but that would require building a
681      * .pnf file. */
682     hinf = SetupOpenInfFileW(inf_path, NULL, INF_STYLE_WIN4, NULL);
683     if (hinf == INVALID_HANDLE_VALUE) return FALSE;
684
685     if (!SetupGetLineTextW(NULL, hinf, wszVersion, wszCatalogFile,
686                            OriginalFileInfo->OriginalCatalogName,
687                            sizeof(OriginalFileInfo->OriginalCatalogName)/sizeof(OriginalFileInfo->OriginalCatalogName[0]),
688                            NULL))
689     {
690         OriginalFileInfo->OriginalCatalogName[0] = '\0';
691     }
692     SetupCloseInfFile(hinf);
693
694     /* FIXME: not quite correct as we just return the same file name as
695      * destination (copied) inf file, not the source (original) inf file.
696      * to fix it properly would require building a .pnf file */
697     /* file name is stored in VersionData field of InfInformation */
698     inf_name = strrchrW(inf_path, '\\');
699     if (inf_name) inf_name++;
700     else inf_name = inf_path;
701
702     strcpyW(OriginalFileInfo->OriginalInfName, inf_name);
703
704     return TRUE;
705 }