2 * Implementation of VER.DLL
4 * Copyright 1996,1997 Marcus Meissner
5 * Copyright 1997 David Cuthbert
25 #define LZREAD(what) \
26 if (sizeof(*what)!=LZRead32(lzfd,what,sizeof(*what))) return 0;
27 #define LZTELL(lzfd) LZSeek32(lzfd, 0, SEEK_CUR);
29 /******************************************************************************
32 * char const * prologue,
33 * char const * teststring,
34 * char const * epilogue )
36 * This function will print via dprintf_ver to stddeb the prologue string,
37 * followed by the address of teststring and the string it contains if
38 * teststring is non-null or "(null)" otherwise, and then the epilogue
42 * 30-May-1997 Dave Cuthbert (dacut@ece.cmu.edu)
43 * Original implementation as dprintf[_]ver_string
44 * 05-Jul-1997 Dave Cuthbert (dacut@ece.cmu.edu)
45 * Fixed problem that caused bug with tools/make_debug -- renaming
46 * this function should fix the problem.
48 *****************************************************************************/
50 static void ver_dstring(
51 char const * prologue
,
52 char const * teststring
,
53 char const * epilogue
)
55 dprintf_ver(stddeb
, "%s", prologue
);
58 dprintf_ver(stddeb
, "%p (\"%s\")", (void const *) teststring
,
61 dprintf_ver(stddeb
, "(null)");
63 dprintf_ver(stddeb
, "%s", epilogue
);
68 /******************************************************************************
70 * int testFileExistence(
74 * Tests whether a given path/file combination exists. If the file does
75 * not exist, the return value is zero. If it does exist, the return
79 * 30-May-1997 Dave Cuthbert (dacut@ece.cmu.edu)
80 * Original implementation
82 *****************************************************************************/
84 static int testFileExistence(
93 fileinfo
.cBytes
= sizeof(OFSTRUCT
);
95 strcpy(filename
, path
);
96 filenamelen
= strlen(filename
);
98 /* Add a trailing \ if necessary */
100 if(filename
[filenamelen
- 1] != '\\')
101 strcat(filename
, "\\");
103 else /* specify the current directory */
104 strcpy(filename
, ".\\");
106 /* Create the full pathname */
107 strcat(filename
, file
);
109 if(OpenFile32(filename
, &fileinfo
, OF_EXIST
) == HFILE_ERROR32
)
117 /******************************************************************************
119 * int testFileExclusiveExistence(
121 * char const * file )
123 * Tests whether a given path/file combination exists and ensures that no
124 * other programs have handles to the given file. If the file does not
125 * exist or is open, the return value is zero. If it does exist, the
126 * return value is non-zero.
129 * 30-May-1997 Dave Cuthbert (dacut@ece.cmu.edu)
130 * Original implementation
132 *****************************************************************************/
134 static int testFileExclusiveExistence(
143 fileinfo
.cBytes
= sizeof(OFSTRUCT
);
145 strcpy(filename
, path
);
146 filenamelen
= strlen(filename
);
148 /* Add a trailing \ if necessary */
150 if(filename
[filenamelen
- 1] != '\\')
151 strcat(filename
, "\\");
153 else /* specify the current directory */
154 strcpy(filename
, ".\\");
156 /* Create the full pathname */
157 strcat(filename
, file
);
159 if(OpenFile32(filename
, &fileinfo
, OF_EXIST
| OF_SHARE_EXCLUSIVE
) ==
169 static int read_xx_header(HFILE32 lzfd
) {
170 IMAGE_DOS_HEADER mzh
;
173 LZSeek32(lzfd
,0,SEEK_SET
);
174 if (sizeof(mzh
)!=LZRead32(lzfd
,&mzh
,sizeof(mzh
)))
176 if (mzh
.e_magic
!=IMAGE_DOS_SIGNATURE
)
178 LZSeek32(lzfd
,mzh
.e_lfanew
,SEEK_SET
);
179 if (2!=LZRead32(lzfd
,magic
,2))
181 LZSeek32(lzfd
,mzh
.e_lfanew
,SEEK_SET
);
182 if (magic
[0] == 'N' && magic
[1] == 'E')
183 return IMAGE_OS2_SIGNATURE
;
184 if (magic
[0] == 'P' && magic
[1] == 'E')
185 return IMAGE_NT_SIGNATURE
;
186 fprintf(stderr
,"misc/ver.c:read_ne_header:can't handle %*s files.\n",2,magic
);
191 static int find_ne_resource(
192 HFILE32 lzfd
,SEGPTR
typeid,SEGPTR resid
,
193 BYTE
**resdata
,int *reslen
,DWORD
*off
195 IMAGE_OS2_HEADER nehd
;
202 nehdoffset
= LZTELL(lzfd
);
204 if (nehd
.resource_tab_offset
==nehd
.rname_tab_offset
) {
205 dprintf_ver(stddeb
,"no resources in NE dll\n");
208 LZSeek32(lzfd
,nehd
.resource_tab_offset
+nehdoffset
,SEEK_SET
);
210 dprintf_ver(stddeb
,"shiftcount is %d\n",shiftcount
);
211 dprintf_ver(stddeb
,"reading resource typeinfo dir.\n");
213 if (!HIWORD(typeid)) typeid = (SEGPTR
)(LOWORD(typeid) | 0x8000);
214 if (!HIWORD(resid
)) resid
= (SEGPTR
)(LOWORD(resid
) | 0x8000);
221 dprintf_ver(stddeb
," ti.typeid =%04x,count=%d\n",ti
.type_id
,ti
.count
);
224 if (!HIWORD(typeid)) {
225 if ((ti
.type_id
&0x8000)&&(typeid!=ti
.type_id
))
228 if (ti
.type_id
& 0x8000) {
235 whereleft
= LZTELL(lzfd
);
238 nehdoffset
+nehd
.resource_tab_offset
+ti
.type_id
,
243 if (len
!=LZRead32(lzfd
,str
,len
))
245 dprintf_ver(stddeb
,"read %s to compare it with %s\n",
246 str
,(char*)PTR_SEG_TO_LIN(typeid)
248 if (lstrcmpi32A(str
,(char*)PTR_SEG_TO_LIN(typeid)))
251 LZSeek32(lzfd
,whereleft
,SEEK_SET
);
255 LZSeek32(lzfd
,ti
.count
*sizeof(ni
),SEEK_CUR
);
258 for (i
=0;i
<ti
.count
;i
++) {
263 dprintf_ver(stddeb
," ni.id=%4x,offset=%d,length=%d\n",
264 ni
.id
,ni
.offset
,ni
.length
267 if (!HIWORD(resid
)) {
271 if (!(ni
.id
& 0x8000)) {
276 whereleft
= LZTELL(lzfd
);
279 nehdoffset
+nehd
.resource_tab_offset
+ni
.id
,
284 if (len
!=LZRead32(lzfd
,str
,len
))
286 dprintf_ver(stddeb
,"read %s to compare it with %s\n",
287 str
,(char*)PTR_SEG_TO_LIN(typeid)
289 if (!lstrcmpi32A(str
,(char*)PTR_SEG_TO_LIN(typeid)))
292 LZSeek32(lzfd
,whereleft
,SEEK_SET
);
297 LZSeek32(lzfd
,((int)ni
.offset
<<shiftcount
),SEEK_SET
);
298 *off
= (int)ni
.offset
<<shiftcount
;
299 len
= ni
.length
<<shiftcount
;
300 rdata
=(WORD
*)xmalloc(len
);
301 if (len
!=LZRead32(lzfd
,rdata
,len
)) {
305 dprintf_ver(stddeb
,"resource found.\n");
306 *resdata
= (BYTE
*)rdata
;
313 extern LPIMAGE_RESOURCE_DIRECTORY
GetResDirEntryW(
314 LPIMAGE_RESOURCE_DIRECTORY resdirptr
,LPCWSTR name
,DWORD root
317 /* Loads the specified PE resource.
318 * FIXME: shouldn't load the whole image
322 HFILE32 lzfd
,LPWSTR
typeid,LPWSTR resid
,
323 BYTE
**resdata
,int *reslen
,DWORD
*off
325 IMAGE_NT_HEADERS pehd
;
328 DWORD imagesize
,pehdoffset
;
330 IMAGE_DATA_DIRECTORY resdir
;
331 LPIMAGE_RESOURCE_DIRECTORY resourcedir
,xresdir
;
332 LPIMAGE_RESOURCE_DATA_ENTRY xresdata
;
333 LPIMAGE_SECTION_HEADER sections
;
335 pehdoffset
= LZTELL(lzfd
);
337 resdir
= pehd
.OptionalHeader
.DataDirectory
[IMAGE_FILE_RESOURCE_DIRECTORY
];
338 dprintf_ver(stddeb
,"find_pe_resource(.,%p,%p,....)\n",typeid,resid
);
340 fprintf(stderr
,"misc/ver.c:find_pe_resource() no resource directory found in PE file.\n");
343 imagesize
= pehd
.OptionalHeader
.SizeOfImage
;
344 image
= HeapAlloc(GetProcessHeap(),0,imagesize
);
345 nrofsections
= pehd
.FileHeader
.NumberOfSections
;
347 sections
= (LPIMAGE_SECTION_HEADER
)HeapAlloc(GetProcessHeap(),0,pehd
.FileHeader
.NumberOfSections
*sizeof(IMAGE_SECTION_HEADER
));
350 sizeof(DWORD
)+ /* Signature */
351 sizeof(IMAGE_FILE_HEADER
)+
352 pehd
.FileHeader
.SizeOfOptionalHeader
,
355 if ( nrofsections
*sizeof(IMAGE_SECTION_HEADER
)!=
356 LZRead32(lzfd
,sections
,nrofsections
*sizeof(IMAGE_SECTION_HEADER
))
358 HeapFree(GetProcessHeap(),0,image
);
361 for (i
=0;i
<nrofsections
;i
++) {
362 if (sections
[i
].Characteristics
& IMAGE_SCN_CNT_UNINITIALIZED_DATA
)
364 LZSeek32(lzfd
,sections
[i
].PointerToRawData
,SEEK_SET
);
365 if ( sections
[i
].SizeOfRawData
!=
366 LZRead32(lzfd
,image
+sections
[i
].VirtualAddress
,sections
[i
].SizeOfRawData
)
370 resourcedir
= (LPIMAGE_RESOURCE_DIRECTORY
)(image
+resdir
.VirtualAddress
);
371 xresdir
= GetResDirEntryW(resourcedir
,typeid,(DWORD
)resourcedir
);
373 dprintf_ver(stddeb
,"...no typeid entry found for %p\n",typeid);
374 HeapFree(GetProcessHeap(),0,image
);
377 xresdir
= GetResDirEntryW(xresdir
,resid
,(DWORD
)resourcedir
);
379 dprintf_ver(stddeb
,"...no resid entry found for %p\n",resid
);
380 HeapFree(GetProcessHeap(),0,image
);
384 xresdir
= GetResDirEntryW(xresdir
,0,(DWORD
)resourcedir
);
386 dprintf_ver(stddeb
,"...no 0 (default language) entry found for %p\n",resid
);
387 HeapFree(GetProcessHeap(),0,image
);
390 xresdata
= (LPIMAGE_RESOURCE_DATA_ENTRY
)xresdir
;
391 *reslen
= xresdata
->Size
;
392 *resdata
= (LPBYTE
)xmalloc(*reslen
);
393 memcpy(*resdata
,image
+xresdata
->OffsetToData
,*reslen
);
394 /* find physical address for virtual offset */
395 for (i
=0;i
<nrofsections
;i
++) {
396 if (sections
[i
].Characteristics
& IMAGE_SCN_CNT_UNINITIALIZED_DATA
)
398 if ( (xresdata
->OffsetToData
>= sections
[i
].VirtualAddress
)&&
399 (xresdata
->OffsetToData
< sections
[i
].VirtualAddress
+sections
[i
].SizeOfRawData
)
401 *off
= (DWORD
)(xresdata
->OffsetToData
)-(DWORD
)(sections
[i
].VirtualAddress
)+(DWORD
)(sections
[i
].PointerToRawData
);
405 HeapFree(GetProcessHeap(),0,image
);
406 HeapFree(GetProcessHeap(),0,sections
);
410 /* GetFileResourceSize [VER.2] */
411 DWORD WINAPI
GetFileResourceSize(LPCSTR filename
,SEGPTR restype
,SEGPTR resid
,
416 BYTE
*resdata
= NULL
;
420 dprintf_ver(stddeb
,"GetFileResourceSize(%s,%lx,%lx,%p)\n",
421 filename
,(LONG
)restype
,(LONG
)resid
,off
423 lzfd
=LZOpenFile32A(filename
,&ofs
,OF_READ
);
426 switch (read_xx_header(lzfd
)) {
430 case IMAGE_OS2_SIGNATURE
:
431 res
=find_ne_resource(lzfd
,restype
,resid
,&resdata
,&reslen
,off
);
433 case IMAGE_NT_SIGNATURE
:
434 res
=find_pe_resource(lzfd
,(LPWSTR
)restype
,(LPWSTR
)resid
,&resdata
,&reslen
,off
);
447 /* GetFileResource [VER.3] */
448 DWORD WINAPI
GetFileResource(LPCSTR filename
,SEGPTR restype
,SEGPTR resid
,
449 DWORD off
,DWORD datalen
,LPVOID data
)
457 dprintf_ver(stddeb
,"GetFileResource(%s,%lx,%lx,%ld,%ld,%p)\n",
458 filename
,(LONG
)restype
,(LONG
)resid
,off
,datalen
,data
461 lzfd
=LZOpenFile32A(filename
,&ofs
,OF_READ
);
465 switch (read_xx_header(lzfd
)) {
468 case IMAGE_OS2_SIGNATURE
:
469 res
= find_ne_resource(lzfd
,restype
,resid
,&resdata
,&reslen
,&off
);
471 case IMAGE_NT_SIGNATURE
:
472 res
= find_pe_resource(lzfd
,(LPWSTR
)restype
,(LPWSTR
)resid
,&resdata
,&reslen
,&off
);
478 if (reslen
>datalen
) reslen
= datalen
;
479 memcpy(data
,resdata
,reslen
);
483 LZSeek32(lzfd
,off
,SEEK_SET
);
484 reslen
= LZRead32(lzfd
,data
,datalen
);
489 /* GetFileVersionInfoSize [VER.6] */
490 DWORD WINAPI
GetFileVersionInfoSize16(LPCSTR filename
,LPDWORD handle
)
492 DWORD len
,ret
,isuni
=0;
494 VS_FIXEDFILEINFO
*vffi
;
496 dprintf_ver(stddeb
,"GetFileVersionInfoSize16(%s,%p)\n",filename
,handle
);
497 len
=GetFileResourceSize(filename
,VS_FILE_INFO
,VS_VERSION_INFO
,handle
);
501 filename
,VS_FILE_INFO
,VS_VERSION_INFO
,*handle
,sizeof(buf
),buf
506 vffi
=(VS_FIXEDFILEINFO
*)(buf
+0x14);
507 if (vffi
->dwSignature
!= VS_FFI_SIGNATURE
) {
508 /* unicode resource */
509 if (vffi
->dwSignature
== 0x004f0049) {
511 vffi
= (VS_FIXEDFILEINFO
*)(buf
+0x28);
513 fprintf(stderr
,"vffi->dwSignature is 0x%08lx, but not 0x%08lx!\n",
514 vffi
->dwSignature
,VS_FFI_SIGNATURE
519 if (*(WORD
*)buf
< len
)
521 dprintf_ver(stddeb
," structversion=0x%lx.0x%lx,\n fileversion=0x%lx.0x%lx,\n productversion=0x%lx.0x%lx,\n flagmask=0x%lx,\n flags=",
522 (vffi
->dwStrucVersion
>>16),vffi
->dwStrucVersion
&0xFFFF,
523 vffi
->dwFileVersionMS
,vffi
->dwFileVersionLS
,
524 vffi
->dwProductVersionMS
,vffi
->dwProductVersionLS
,
525 vffi
->dwFileFlagsMask
527 if (vffi
->dwFileFlags
& VS_FF_DEBUG
)
528 dprintf_ver(stddeb
,"DEBUG,");
529 if (vffi
->dwFileFlags
& VS_FF_PRERELEASE
)
530 dprintf_ver(stddeb
,"PRERELEASE,");
531 if (vffi
->dwFileFlags
& VS_FF_PATCHED
)
532 dprintf_ver(stddeb
,"PATCHED,");
533 if (vffi
->dwFileFlags
& VS_FF_PRIVATEBUILD
)
534 dprintf_ver(stddeb
,"PRIVATEBUILD,");
535 if (vffi
->dwFileFlags
& VS_FF_INFOINFERRED
)
536 dprintf_ver(stddeb
,"INFOINFERRED,");
537 if (vffi
->dwFileFlags
& VS_FF_SPECIALBUILD
)
538 dprintf_ver(stddeb
,"SPECIALBUILD,");
539 dprintf_ver(stddeb
,"\n OS=0x%lx.0x%lx (",
540 (vffi
->dwFileOS
&0xFFFF0000)>>16,
541 vffi
->dwFileOS
&0x0000FFFF
543 switch (vffi
->dwFileOS
&0xFFFF0000) {
544 case VOS_DOS
:dprintf_ver(stddeb
,"DOS,");break;
545 case VOS_OS216
:dprintf_ver(stddeb
,"OS/2-16,");break;
546 case VOS_OS232
:dprintf_ver(stddeb
,"OS/2-32,");break;
547 case VOS_NT
:dprintf_ver(stddeb
,"NT,");break;
550 dprintf_ver(stddeb
,"UNKNOWN(0x%lx),",vffi
->dwFileOS
&0xFFFF0000);break;
552 switch (vffi
->dwFileOS
& 0xFFFF) {
553 case VOS__BASE
:dprintf_ver(stddeb
,"BASE");break;
554 case VOS__WINDOWS16
:dprintf_ver(stddeb
,"WIN16");break;
555 case VOS__WINDOWS32
:dprintf_ver(stddeb
,"WIN32");break;
556 case VOS__PM16
:dprintf_ver(stddeb
,"PM16");break;
557 case VOS__PM32
:dprintf_ver(stddeb
,"PM32");break;
558 default:dprintf_ver(stddeb
,"UNKNOWN(0x%lx)",vffi
->dwFileOS
&0xFFFF);break;
560 dprintf_ver(stddeb
,")\n ");
561 switch (vffi
->dwFileType
) {
564 dprintf_ver(stddeb
,"filetype=Unknown(0x%lx)",vffi
->dwFileType
);
566 case VFT_APP
:dprintf_ver(stddeb
,"filetype=APP,");break;
567 case VFT_DLL
:dprintf_ver(stddeb
,"filetype=DLL,");break;
569 dprintf_ver(stddeb
,"filetype=DRV,");
570 switch(vffi
->dwFileSubtype
) {
573 dprintf_ver(stddeb
,"UNKNOWN(0x%lx)",vffi
->dwFileSubtype
);
575 case VFT2_DRV_PRINTER
:
576 dprintf_ver(stddeb
,"PRINTER");
578 case VFT2_DRV_KEYBOARD
:
579 dprintf_ver(stddeb
,"KEYBOARD");
581 case VFT2_DRV_LANGUAGE
:
582 dprintf_ver(stddeb
,"LANGUAGE");
584 case VFT2_DRV_DISPLAY
:
585 dprintf_ver(stddeb
,"DISPLAY");
588 dprintf_ver(stddeb
,"MOUSE");
590 case VFT2_DRV_NETWORK
:
591 dprintf_ver(stddeb
,"NETWORK");
593 case VFT2_DRV_SYSTEM
:
594 dprintf_ver(stddeb
,"SYSTEM");
596 case VFT2_DRV_INSTALLABLE
:
597 dprintf_ver(stddeb
,"INSTALLABLE");
600 dprintf_ver(stddeb
,"SOUND");
603 dprintf_ver(stddeb
,"COMM");
605 case VFT2_DRV_INPUTMETHOD
:
606 dprintf_ver(stddeb
,"INPUTMETHOD");
611 dprintf_ver(stddeb
,"filetype=FONT.");
612 switch (vffi
->dwFileSubtype
) {
614 dprintf_ver(stddeb
,"UNKNOWN(0x%lx)",vffi
->dwFileSubtype
);
616 case VFT2_FONT_RASTER
:dprintf_ver(stddeb
,"RASTER");break;
617 case VFT2_FONT_VECTOR
:dprintf_ver(stddeb
,"VECTOR");break;
618 case VFT2_FONT_TRUETYPE
:dprintf_ver(stddeb
,"TRUETYPE");break;
621 case VFT_VXD
:dprintf_ver(stddeb
,"filetype=VXD");break;
622 case VFT_STATIC_LIB
:dprintf_ver(stddeb
,"filetype=STATIC_LIB");break;
624 dprintf_ver(stddeb
,"\n filedata=0x%lx.0x%lx\n",vffi
->dwFileDateMS
,vffi
->dwFileDateLS
);
628 /* GetFileVersionInfoSize32A [VERSION.1] */
629 DWORD WINAPI
GetFileVersionInfoSize32A(LPCSTR filename
,LPDWORD handle
)
631 dprintf_ver(stddeb
,"GetFileVersionInfoSize32A(%s,%p)\n",filename
,handle
);
632 return GetFileVersionInfoSize16(filename
,handle
);
635 /* GetFileVersionInfoSize32W [VERSION.2] */
636 DWORD WINAPI
GetFileVersionInfoSize32W( LPCWSTR filename
, LPDWORD handle
)
638 LPSTR xfn
= HEAP_strdupWtoA( GetProcessHeap(), 0, filename
);
639 DWORD ret
= GetFileVersionInfoSize16( xfn
, handle
);
640 HeapFree( GetProcessHeap(), 0, xfn
);
644 /* GetFileVersionInfo [VER.7] */
645 DWORD WINAPI
GetFileVersionInfo16(LPCSTR filename
,DWORD handle
,DWORD datasize
,
648 dprintf_ver(stddeb
,"GetFileVersionInfo16(%s,%ld,%ld,%p)\n->",
649 filename
,handle
,datasize
,data
651 return GetFileResource(
652 filename
,VS_FILE_INFO
,VS_VERSION_INFO
,handle
,datasize
,data
656 /* GetFileVersionInfoA [VERSION.0] */
657 DWORD WINAPI
GetFileVersionInfo32A(LPCSTR filename
,DWORD handle
,
658 DWORD datasize
,LPVOID data
)
660 return GetFileVersionInfo16(filename
,handle
,datasize
,data
);
663 /* GetFileVersionInfoW [VERSION.3] */
664 DWORD WINAPI
GetFileVersionInfo32W( LPCWSTR filename
, DWORD handle
,
665 DWORD datasize
, LPVOID data
)
667 LPSTR fn
= HEAP_strdupWtoA( GetProcessHeap(), 0, filename
);
668 DWORD ret
= GetFileVersionInfo16( fn
, handle
, datasize
, data
);
669 HeapFree( GetProcessHeap(), 0, fn
);
673 /*****************************************************************************
675 * VerFindFile() [VER.8]
676 * Determines where to install a file based on whether it locates another
677 * version of the file in the system. The values VerFindFile returns are
678 * used in a subsequent call to the VerInstallFile function.
681 * 30-May-1997 Dave Cuthbert (dacut@ece.cmu.edu)
682 * Reimplementation of VerFindFile from original stub.
684 ****************************************************************************/
686 DWORD WINAPI
VerFindFile16(
692 UINT16
*lpuCurDirLen
,
694 UINT16
*lpuDestDirLen
)
699 unsigned int curDirSizeReq
;
700 unsigned int destDirSizeReq
;
704 /* Print out debugging information */
705 dprintf_ver(stddeb
, "VerFindFile() called with parameters:\n"
706 "\tflags = %x", flags
);
707 if(flags
& VFFF_ISSHAREDFILE
)
708 dprintf_ver(stddeb
, " (VFFF_ISSHAREDFILE)\n");
710 dprintf_ver(stddeb
, "\n");
712 ver_dstring("\tlpszFilename = ", lpszFilename
, "\n");
713 ver_dstring("\tlpszWinDir = ", lpszWinDir
, "\n");
714 ver_dstring("\tlpszAppDir = ", lpszAppDir
, "\n");
716 dprintf_ver(stddeb
, "\tlpszCurDir = %p\n", lpszCurDir
);
718 dprintf_ver(stddeb
, "\tlpuCurDirLen = %p (%u)\n",
719 lpuCurDirLen
, *lpuCurDirLen
);
721 dprintf_ver(stddeb
, "\tlpuCurDirLen = (null)\n");
723 dprintf_ver(stddeb
, "\tlpszDestDir = %p\n", lpszDestDir
);
725 dprintf_ver(stddeb
, "\tlpuDestDirLen = %p (%u)\n",
726 lpuDestDirLen
, *lpuDestDirLen
);
728 /* Figure out where the file should go; shared files default to the
734 if(flags
& VFFF_ISSHAREDFILE
&& !getuid()) {
735 GetSystemDirectory32A(destDir
, 256);
737 /* Were we given a filename? If so, try to find the file. */
739 if(testFileExistence(destDir
, lpszFilename
)) {
740 strcpy(curDir
, destDir
);
742 if(!testFileExclusiveExistence(destDir
, lpszFilename
))
743 retval
|= VFF_FILEINUSE
;
745 else if(lpszAppDir
&& testFileExistence(lpszAppDir
,
747 strcpy(curDir
, lpszAppDir
);
748 retval
|= VFF_CURNEDEST
;
750 if(!testFileExclusiveExistence(lpszAppDir
, lpszFilename
))
751 retval
|= VFF_FILEINUSE
;
755 else if(!(flags
& VFFF_ISSHAREDFILE
)) { /* not a shared file */
758 GetSystemDirectory32A(systemDir
, 256);
760 strcpy(destDir
, lpszAppDir
);
763 if(testFileExistence(lpszAppDir
, lpszFilename
)) {
764 strcpy(curDir
, lpszAppDir
);
766 if(!testFileExclusiveExistence(lpszAppDir
, lpszFilename
))
767 retval
|= VFF_FILEINUSE
;
769 else if(testFileExistence(systemDir
, lpszFilename
)) {
770 strcpy(curDir
, systemDir
);
771 retval
|= VFF_CURNEDEST
;
773 if(!testFileExclusiveExistence(systemDir
, lpszFilename
))
774 retval
|= VFF_FILEINUSE
;
780 curDirSizeReq
= strlen(curDir
) + 1;
781 destDirSizeReq
= strlen(destDir
) + 1;
785 /* Make sure that the pointers to the size of the buffers are
786 valid; if not, do NOTHING with that buffer. If that pointer
787 is valid, then make sure that the buffer pointer is valid, too! */
789 if(lpuDestDirLen
&& lpszDestDir
) {
790 if(*lpuDestDirLen
< destDirSizeReq
) {
791 retval
|= VFF_BUFFTOOSMALL
;
792 strncpy(lpszDestDir
, destDir
, *lpuDestDirLen
- 1);
793 lpszDestDir
[*lpuDestDirLen
- 1] = '\0';
796 strcpy(lpszDestDir
, destDir
);
798 *lpuDestDirLen
= destDirSizeReq
;
801 if(lpuCurDirLen
&& lpszCurDir
) {
802 if(*lpuCurDirLen
< curDirSizeReq
) {
803 retval
|= VFF_BUFFTOOSMALL
;
804 strncpy(lpszCurDir
, curDir
, *lpuCurDirLen
- 1);
805 lpszCurDir
[*lpuCurDirLen
- 1] = '\0';
808 strcpy(lpszCurDir
, curDir
);
810 *lpuCurDirLen
= curDirSizeReq
;
813 dprintf_ver(stddeb
, "VerFindFile() ret = %lu ",
817 dprintf_ver(stddeb
, "( ");
819 if(retval
& VFF_CURNEDEST
)
820 dprintf_ver(stddeb
, "VFF_CURNEDEST ");
821 if(retval
& VFF_FILEINUSE
)
822 dprintf_ver(stddeb
, "VFF_FILEINUSE ");
823 if(retval
& VFF_BUFFTOOSMALL
)
824 dprintf_ver(stddeb
, "VFF_BUFFTOOSMALL ");
826 dprintf_ver(stddeb
, ")");
829 ver_dstring("\n\t(Exit) lpszCurDir = ", lpszCurDir
, "\n");
831 dprintf_ver(stddeb
, "\t(Exit) lpuCurDirLen = %p (%u)\n",
832 lpuCurDirLen
, *lpuCurDirLen
);
834 dprintf_ver(stddeb
, "\t(Exit) lpuCurDirLen = (null)\n");
836 ver_dstring("\t(Exit) lpszDestDir = ", lpszDestDir
, "\n");
838 dprintf_ver(stddeb
, "\t(Exit) lpuDestDirLen = %p (%u)\n",
839 lpuDestDirLen
, *lpuDestDirLen
);
844 /* VerFindFileA [VERSION.5] */
845 DWORD WINAPI
VerFindFile32A(
846 UINT32 flags
,LPCSTR filename
,LPCSTR windir
,LPCSTR appdir
,
847 LPSTR curdir
,UINT32
*pcurdirlen
,LPSTR destdir
,UINT32
*pdestdirlen
)
849 UINT16 curdirlen
, destdirlen
;
850 DWORD ret
= VerFindFile16(flags
,filename
,windir
,appdir
,
851 curdir
,&curdirlen
,destdir
,&destdirlen
);
852 *pcurdirlen
= curdirlen
;
853 *pdestdirlen
= destdirlen
;
857 /* VerFindFileW [VERSION.6] */
858 DWORD WINAPI
VerFindFile32W(
859 UINT32 flags
,LPCWSTR filename
,LPCWSTR windir
,LPCWSTR appdir
,
860 LPWSTR curdir
,UINT32
*pcurdirlen
,LPWSTR destdir
,UINT32
*pdestdirlen
)
862 UINT16 curdirlen
, destdirlen
;
863 LPSTR wfn
,wwd
,wad
,wdd
,wcd
;
866 wfn
= HEAP_strdupWtoA( GetProcessHeap(), 0, filename
);
867 wwd
= HEAP_strdupWtoA( GetProcessHeap(), 0, windir
);
868 wad
= HEAP_strdupWtoA( GetProcessHeap(), 0, appdir
);
869 wcd
= HeapAlloc( GetProcessHeap(), 0, *pcurdirlen
);
870 wdd
= HeapAlloc( GetProcessHeap(), 0, *pdestdirlen
);
871 ret
= VerFindFile16(flags
,wfn
,wwd
,wad
,wcd
,&curdirlen
,wdd
,&destdirlen
);
872 lstrcpynAtoW(curdir
,wcd
,*pcurdirlen
);
873 lstrcpynAtoW(destdir
,wdd
,*pdestdirlen
);
874 *pcurdirlen
= strlen(wcd
);
875 *pdestdirlen
= strlen(wdd
);
876 HeapFree( GetProcessHeap(), 0, wfn
);
877 HeapFree( GetProcessHeap(), 0, wwd
);
878 HeapFree( GetProcessHeap(), 0, wad
);
879 HeapFree( GetProcessHeap(), 0, wcd
);
880 HeapFree( GetProcessHeap(), 0, wdd
);
884 /* VerInstallFile [VER.9] */
885 DWORD WINAPI
VerInstallFile16(
886 UINT16 flags
,LPCSTR srcfilename
,LPCSTR destfilename
,LPCSTR srcdir
,
887 LPCSTR destdir
,LPCSTR curdir
,LPSTR tmpfile
,UINT16
*tmpfilelen
)
890 DWORD ret
= VerInstallFile32A(flags
,srcfilename
,destfilename
,srcdir
,
891 destdir
,curdir
,tmpfile
,&filelen
);
893 *tmpfilelen
= filelen
;
897 /* VerInstallFileA [VERSION.7] */
899 _fetch_versioninfo(LPSTR fn
,VS_FIXEDFILEINFO
**vffi
) {
905 buf
= xmalloc(alloclen
);
907 ret
= GetFileVersionInfo32A(fn
,0,alloclen
,buf
);
912 if (alloclen
<*(WORD
*)buf
) {
914 alloclen
= *(WORD
*)buf
;
915 buf
= xmalloc(alloclen
);
917 *vffi
= (VS_FIXEDFILEINFO
*)(buf
+0x14);
918 if ((*vffi
)->dwSignature
== 0x004f0049) /* hack to detect unicode */
919 *vffi
= (VS_FIXEDFILEINFO
*)(buf
+0x28);
920 if ((*vffi
)->dwSignature
!= VS_FFI_SIGNATURE
)
921 fprintf(stderr
,"_fetch_versioninfo:bad VS_FIXEDFILEINFO signature 0x%08lx\n",(*vffi
)->dwSignature
);
928 _error2vif(DWORD error
) {
930 case ERROR_ACCESS_DENIED
:
931 return VIF_ACCESSVIOLATION
;
932 case ERROR_SHARING_VIOLATION
:
933 return VIF_SHARINGVIOLATION
;
942 DWORD WINAPI
VerInstallFile32A(
943 UINT32 flags
,LPCSTR srcfilename
,LPCSTR destfilename
,LPCSTR srcdir
,
944 LPCSTR destdir
,LPCSTR curdir
,LPSTR tmpfile
,UINT32
*tmpfilelen
)
947 char destfn
[260],tmpfn
[260],srcfn
[260];
949 DWORD attr
,ret
,xret
,tmplast
;
953 fprintf(stddeb
,"VerInstallFile(%x,%s,%s,%s,%s,%s,%p,%d)\n",
954 flags
,srcfilename
,destfilename
,srcdir
,destdir
,curdir
,tmpfile
,*tmpfilelen
957 sprintf(srcfn
,"%s\\%s",srcdir
,srcfilename
);
958 if (!destdir
|| !*destdir
) pdest
= srcdir
;
959 else pdest
= destdir
;
960 sprintf(destfn
,"%s\\%s",pdest
,destfilename
);
961 hfsrc
=LZOpenFile32A(srcfn
,&ofs
,OF_READ
);
962 if (hfsrc
==HFILE_ERROR32
)
963 return VIF_CANNOTREADSRC
;
964 sprintf(tmpfn
,"%s\\%s",pdest
,destfilename
);
965 tmplast
=strlen(pdest
)+1;
966 attr
= GetFileAttributes32A(tmpfn
);
968 if (attr
& FILE_ATTRIBUTE_READONLY
) {
970 return VIF_WRITEPROT
;
972 /* FIXME: check if file currently in use and return VIF_FILEINUSE */
975 if (flags
& VIFF_FORCEINSTALL
) {
977 sprintf(tmpfn
,"%s\\%s",pdest
,tmpfile
);
978 tmplast
= strlen(pdest
)+1;
979 attr
= GetFileAttributes32A(tmpfn
);
980 /* if it exists, it has been copied by the call before.
981 * we jump over the copy part...
988 GetTempFileName32A(pdest
,"ver",0,tmpfn
); /* should not fail ... */
989 s
=strrchr(tmpfn
,'\\');
994 hfdst
= OpenFile32(tmpfn
,&ofs
,OF_CREATE
);
995 if (hfdst
== HFILE_ERROR32
) {
997 return VIF_CANNOTCREATE
; /* | translated dos error */
999 ret
= LZCopy32(hfsrc
,hfdst
);
1001 if (((long) ret
) < 0) {
1002 /* translate LZ errors into VIF_xxx */
1004 case LZERROR_BADINHANDLE
:
1006 case LZERROR_BADVALUE
:
1007 case LZERROR_UNKNOWNALG
:
1008 ret
= VIF_CANNOTREADSRC
;
1010 case LZERROR_BADOUTHANDLE
:
1012 ret
= VIF_OUTOFMEMORY
; /* FIXME: correct? */
1014 case LZERROR_GLOBALLOC
:
1015 case LZERROR_GLOBLOCK
:
1016 ret
= VIF_OUTOFSPACE
;
1018 default: /* unknown error, should not happen */
1029 if (!(flags
& VIFF_FORCEINSTALL
)) {
1030 VS_FIXEDFILEINFO
*destvffi
,*tmpvffi
;
1031 buf1
= _fetch_versioninfo(destfn
,&destvffi
);
1033 buf2
= _fetch_versioninfo(tmpfn
,&tmpvffi
);
1040 /* compare file versions */
1041 if ((destvffi
->dwFileVersionMS
> tmpvffi
->dwFileVersionMS
)||
1042 ((destvffi
->dwFileVersionMS
==tmpvffi
->dwFileVersionMS
)&&
1043 (destvffi
->dwFileVersionLS
> tmpvffi
->dwFileVersionLS
)
1046 xret
|= VIF_MISMATCH
|VIF_SRCOLD
;
1047 /* compare filetypes and filesubtypes */
1048 if ((destvffi
->dwFileType
!=tmpvffi
->dwFileType
) ||
1049 (destvffi
->dwFileSubtype
!=tmpvffi
->dwFileSubtype
)
1051 xret
|= VIF_MISMATCH
|VIF_DIFFTYPE
;
1052 if (VerQueryValue32A(buf1
,"\\VarFileInfo\\Translation",(LPVOID
*)&tbuf1
,&len1
) &&
1053 VerQueryValue32A(buf2
,"\\VarFileInfo\\Translation",(LPVOID
*)&tbuf2
,&len2
)
1055 /* irgendwas mit tbuf1 und tbuf2 machen
1056 * generiert DIFFLANG|MISMATCH
1061 xret
=VIF_MISMATCH
|VIF_SRCOLD
;
1066 if (*tmpfilelen
<strlen(tmpfn
+tmplast
)) {
1067 xret
|=VIF_BUFTOSMALL
;
1068 DeleteFile32A(tmpfn
);
1070 strcpy(tmpfile
,tmpfn
+tmplast
);
1071 *tmpfilelen
= strlen(tmpfn
+tmplast
)+1;
1075 if (-1!=GetFileAttributes32A(destfn
))
1076 if (!DeleteFile32A(destfn
)) {
1077 xret
|=_error2vif(GetLastError())|VIF_CANNOTDELETE
;
1078 DeleteFile32A(tmpfn
);
1082 if ((!(flags
& VIFF_DONTDELETEOLD
)) &&
1085 lstrcmpi32A(curdir
,pdest
)
1089 sprintf(curfn
,"%s\\%s",curdir
,destfilename
);
1090 if (-1!=GetFileAttributes32A(curfn
)) {
1091 /* FIXME: check if in use ... if it is, VIF_CANNOTDELETECUR */
1092 if (!DeleteFile32A(curfn
))
1093 xret
|=_error2vif(GetLastError())|VIF_CANNOTDELETECUR
;
1096 if (!MoveFile32A(tmpfn
,destfn
)) {
1097 xret
|=_error2vif(GetLastError())|VIF_CANNOTRENAME
;
1098 DeleteFile32A(tmpfn
);
1105 /* VerInstallFileW [VERSION.8] */
1106 DWORD WINAPI
VerInstallFile32W(
1107 UINT32 flags
,LPCWSTR srcfilename
,LPCWSTR destfilename
,LPCWSTR srcdir
,
1108 LPCWSTR destdir
,LPCWSTR curdir
,LPWSTR tmpfile
,UINT32
*tmpfilelen
)
1110 LPSTR wsrcf
,wsrcd
,wdestf
,wdestd
,wtmpf
,wcurd
;
1113 wsrcf
= HEAP_strdupWtoA( GetProcessHeap(), 0, srcfilename
);
1114 wsrcd
= HEAP_strdupWtoA( GetProcessHeap(), 0, srcdir
);
1115 wdestf
= HEAP_strdupWtoA( GetProcessHeap(), 0, destfilename
);
1116 wdestd
= HEAP_strdupWtoA( GetProcessHeap(), 0, destdir
);
1117 wtmpf
= HEAP_strdupWtoA( GetProcessHeap(), 0, tmpfile
);
1118 wcurd
= HEAP_strdupWtoA( GetProcessHeap(), 0, curdir
);
1119 ret
= VerInstallFile32A(flags
,wsrcf
,wdestf
,wsrcd
,wdestd
,wcurd
,wtmpf
,tmpfilelen
);
1121 lstrcpynAtoW(tmpfile
,wtmpf
,*tmpfilelen
);
1122 HeapFree( GetProcessHeap(), 0, wsrcf
);
1123 HeapFree( GetProcessHeap(), 0, wsrcd
);
1124 HeapFree( GetProcessHeap(), 0, wdestf
);
1125 HeapFree( GetProcessHeap(), 0, wdestd
);
1126 HeapFree( GetProcessHeap(), 0, wtmpf
);
1128 HeapFree( GetProcessHeap(), 0, wcurd
);
1136 /* in memory structure... */
1137 char name
[1]; /* padded to dword alignment */
1139 char data[datalen]; padded to dword alignment
1140 BYTE subdirdata[]; until nextoff
1144 #define DATA_OFFSET_A(db) ((4+(strlen((db)->name)+4))&~3)
1149 WORD btext
; /* type of data */
1150 /* in memory structure... */
1151 WCHAR name
[1]; /* padded to dword alignment */
1153 WCHAR data[datalen]; padded to dword alignment
1154 BYTE subdirdata[]; until nextoff
1161 * WCHAR szKey[]; (zero terminated)
1162 * PADDING (round up to nearest 32bit boundary)
1164 #define DATA_OFFSET_W(db) ((2+2+2+((lstrlen32W((db)->name)+1)*2+3))&~3)
1166 /* this one used for Win16 resources, which are always in ASCII format */
1168 _find_dataA(BYTE
*block
,LPCSTR str
, int buff_remain
) {
1170 int substrlen
, inc_size
;
1173 while (*str
&& *str
=='\\')
1175 if (NULL
!=(nextslash
=strchr(str
,'\\')))
1176 substrlen
=nextslash
-str
;
1178 substrlen
=strlen(str
);
1179 if (nextslash
!=NULL
) {
1180 while (*nextslash
&& *nextslash
=='\\')
1188 db
=(struct dbA
*)block
;
1189 dprintf_ver(stddeb
,"db=%p,db->nextoff=%d,db->datalen=%d,db->name=%s\n",
1190 db
,db
->nextoff
,db
->datalen
,db
->name
1192 if ((!db
->nextoff
) || (buff_remain
<=0)) /* no more entries ? */
1195 dprintf_ver(stddeb
,"comparing with %s\n",db
->name
);
1196 if (!lstrncmpi32A(db
->name
,str
,substrlen
)) {
1198 inc_size
=DATA_OFFSET_A(db
)+((db
->datalen
+3)&~3);
1199 return _find_dataA(block
+inc_size
,nextslash
,
1200 db
->nextoff
-inc_size
);
1204 inc_size
= ((db
->nextoff
+3)&~3);
1206 buff_remain
-= inc_size
;
1210 /* this one used for Win32 resources, which are always in UNICODE format */
1211 extern LPWSTR
CRTDLL_wcschr(LPCWSTR str
,WCHAR xchar
);
1213 _find_dataW(BYTE
*block
,LPCWSTR str
, int buff_remain
) {
1215 int substrlen
, inc_size
;
1219 while (*str
&& *str
=='\\')
1221 if (NULL
!=(nextslash
=CRTDLL_wcschr(str
,'\\')))
1222 substrlen
=nextslash
-str
;
1224 substrlen
=lstrlen32W(str
);
1225 if (nextslash
!=NULL
) {
1226 while (*nextslash
&& *nextslash
=='\\')
1235 db
=(struct dbW
*)block
;
1236 xs
= HEAP_strdupWtoA(GetProcessHeap(),0,db
->name
);
1239 vs
= HEAP_strdupWtoA(GetProcessHeap(),0,(WCHAR
*)((block
+DATA_OFFSET_W(db
))));
1241 vs
= HEAP_strdupA(GetProcessHeap(),0,"not a string");
1243 vs
= HEAP_strdupA(GetProcessHeap(),0,"no data");
1245 dprintf_ver(stddeb
,"db->nextoff=%d,db->name=%s,db->data=\"%s\"\n",
1248 HeapFree(GetProcessHeap(),0,vs
);
1249 HeapFree(GetProcessHeap(),0,xs
);
1250 if ((!db
->nextoff
) || (buff_remain
<=0)) /* no more entries ? */
1253 if (!lstrncmpi32W(db
->name
,str
,substrlen
)) {
1255 /* DATA_OFFSET_W(db) (padded to 32bit already)
1257 * PADDING (round up to nearest 32bit boundary)
1258 * --> next level structs
1260 inc_size
=DATA_OFFSET_W(db
)+((db
->datalen
+3)&~3);
1261 return _find_dataW( block
+inc_size
,nextslash
,
1262 db
->nextoff
-inc_size
);
1266 /* skip over this block, round up to nearest 32bit boundary */
1267 inc_size
= ((db
->nextoff
+3)&~3);
1269 buff_remain
-= inc_size
;
1273 /* VerQueryValue [VER.11] */
1274 /* take care, 'buffer' is NOT a SEGPTR, it just points to one */
1275 DWORD WINAPI
VerQueryValue16(SEGPTR segblock
,LPCSTR subblock
,SEGPTR
*buffer
,
1279 BYTE
*block
=PTR_SEG_TO_LIN(segblock
),*b
;
1281 dprintf_ver(stddeb
,"VerQueryValue16(%p,%s,%p,%d)\n",
1282 block
,subblock
,buffer
,*buflen
1285 s
=(char*)xmalloc(strlen("VS_VERSION_INFO\\")+strlen(subblock
)+1);
1286 strcpy(s
,"VS_VERSION_INFO\\");strcat(s
,subblock
);
1287 /* check for UNICODE version */
1288 if ( (*(DWORD
*)(block
+0x14) != VS_FFI_SIGNATURE
) &&
1289 (*(DWORD
*)(block
+0x28) == VS_FFI_SIGNATURE
)
1295 wstr
= HEAP_strdupAtoW(GetProcessHeap(),0,s
);
1296 b
=_find_dataW(block
,wstr
,*(WORD
*)block
);
1297 HeapFree(GetProcessHeap(),0,wstr
);
1299 fprintf(stderr
,"key %s not found in versionresource.\n",s
);
1304 b
= b
+DATA_OFFSET_W(db
);
1305 *buflen
= db
->datalen
;
1307 xs
= HEAP_strdupWtoA(GetProcessHeap(),0,(WCHAR
*)b
);
1308 dprintf_ver(stderr
,"->%s\n",xs
);
1309 HeapFree(GetProcessHeap(),0,xs
);
1311 dprintf_ver(stderr
,"->%p\n",b
);
1314 b
=_find_dataA(block
,s
,*(WORD
*)block
);
1316 fprintf(stderr
,"key %s not found in versionresource.\n",s
);
1321 b
= b
+DATA_OFFSET_A(db
);
1322 *buflen
= db
->datalen
;
1323 /* the string is only printable, if it is below \\StringFileInfo*/
1324 if (!lstrncmpi32A("VS_VERSION_INFO\\StringFileInfo\\",s
,strlen("VS_VERSION_INFO\\StringFileInfo\\")))
1325 dprintf_ver(stddeb
," -> %s=%s\n",subblock
,b
);
1327 dprintf_ver(stddeb
," -> %s=%p\n",subblock
,b
);
1329 *buffer
= (b
-block
)+segblock
;
1334 DWORD WINAPI
VerQueryValue32A(LPVOID vblock
,LPCSTR subblock
,
1335 LPVOID
*vbuffer
,UINT32
*buflen
)
1337 BYTE
*b
,*block
=(LPBYTE
)vblock
,**buffer
=(LPBYTE
*)vbuffer
;
1340 dprintf_ver(stddeb
,"VerQueryValue32A(%p,%s,%p,%d)\n",
1341 block
,subblock
,buffer
,*buflen
1344 s
=(char*)xmalloc(strlen("VS_VERSION_INFO\\")+strlen(subblock
)+1);
1345 strcpy(s
,"VS_VERSION_INFO\\");strcat(s
,subblock
);
1347 /* check for UNICODE version */
1348 if ( (*(DWORD
*)(block
+0x14) != VS_FFI_SIGNATURE
) &&
1349 (*(DWORD
*)(block
+0x28) == VS_FFI_SIGNATURE
)
1355 wstr
= HEAP_strdupAtoW(GetProcessHeap(),0,s
);
1356 b
=_find_dataW(block
,wstr
,*(WORD
*)block
);
1357 HeapFree(GetProcessHeap(),0,wstr
);
1359 fprintf(stderr
,"key %s not found in versionresource.\n",s
);
1363 db
= (struct dbW
*)b
;
1364 *buflen
= db
->datalen
;
1365 b
= b
+DATA_OFFSET_W(db
);
1367 xs
= HEAP_strdupWtoA(GetProcessHeap(),0,(WCHAR
*)b
);
1368 dprintf_ver(stderr
,"->%s\n",xs
);
1369 HeapFree(GetProcessHeap(),0,xs
);
1371 dprintf_ver(stderr
,"->%p\n",b
);
1374 b
=_find_dataA(block
,s
,*(WORD
*)block
);
1376 fprintf(stderr
,"key %s not found in versionresource.\n",subblock
);
1381 *buflen
= db
->datalen
;
1382 b
= b
+DATA_OFFSET_A(db
);
1384 /* the string is only printable, if it is below \\StringFileInfo*/
1385 if (!lstrncmpi32A("VS_VERSION_INFO\\StringFileInfo\\",s
,strlen("VS_VERSION_INFO\\StringFileInfo\\")))
1386 dprintf_ver(stddeb
," -> %s=%s\n",subblock
,b
);
1388 dprintf_ver(stddeb
," -> %s=%p\n",subblock
,b
);
1395 DWORD WINAPI
VerQueryValue32W(LPVOID vblock
,LPCWSTR subblock
,LPVOID
*vbuffer
,
1401 sb
= HEAP_strdupWtoA( GetProcessHeap(), 0, subblock
);
1402 ret
= VerQueryValue32A(vblock
,sb
,vbuffer
,buflen
);
1403 HeapFree( GetProcessHeap(), 0, sb
);
1406 /* 20 GETFILEVERSIONINFORAW */