2 * Implementation of VER.DLL
4 * Copyright 1996,1997 Marcus Meissner
5 * Copyright 1997 David Cuthbert
23 #define LZREAD(what) \
24 if (sizeof(*what)!=LZRead32(lzfd,what,sizeof(*what))) return 0;
25 #define LZTELL(lzfd) LZSeek32(lzfd, 0, SEEK_CUR);
27 /******************************************************************************
30 * char const * prologue,
31 * char const * teststring,
32 * char const * epilogue )
34 * This function will print via dprintf[_]ver to stddeb the prologue string,
35 * followed by the address of teststring and the string it contains if
36 * teststring is non-null or "(null)" otherwise, and then the epilogue
37 * string followed by a new line.
40 * 30-May-1997 Dave Cuthbert (dacut@ece.cmu.edu)
41 * Original implementation as dprintf[_]ver_string
42 * 05-Jul-1997 Dave Cuthbert (dacut@ece.cmu.edu)
43 * Fixed problem that caused bug with tools/make_debug -- renaming
44 * this function should fix the problem.
45 * 15-Feb-1998 Dimitrie Paun (dimi@cs.toronto.edu)
46 * Modified it to make it print the message using only one
49 *****************************************************************************/
51 static void ver_dstring(
52 char const * prologue
,
53 char const * teststring
,
54 char const * epilogue
)
56 TRACE(ver
, "%s %p (\"%s\") %s\n", prologue
,
57 (void const *) teststring
,
58 teststring
? teststring
: "(null)",
62 /******************************************************************************
64 * This function will print via dprintf[_]ver to stddeb debug info regarding
65 * the file info structure vffi.
66 * 15-Feb-1998 Dimitrie Paun (dimi@cs.toronto.edu)
67 * Added this function to clean up the code.
69 *****************************************************************************/
70 static void print_vffi_debug(VS_FIXEDFILEINFO
*vffi
)
72 dbg_decl_str(ver
, 1024);
74 TRACE(ver
," structversion=%u.%u, fileversion=%u.%u.%u.%u, productversion=%u.%u.%u.%u, flagmask=0x%lx, flags=%s%s%s%s%s%s\n",
75 HIWORD(vffi
->dwStrucVersion
),LOWORD(vffi
->dwStrucVersion
),
76 HIWORD(vffi
->dwFileVersionMS
),LOWORD(vffi
->dwFileVersionMS
),
77 HIWORD(vffi
->dwFileVersionLS
),LOWORD(vffi
->dwFileVersionLS
),
78 HIWORD(vffi
->dwProductVersionMS
),LOWORD(vffi
->dwProductVersionMS
),
79 HIWORD(vffi
->dwProductVersionLS
),LOWORD(vffi
->dwProductVersionLS
),
80 vffi
->dwFileFlagsMask
,
81 (vffi
->dwFileFlags
& VS_FF_DEBUG
) ? "DEBUG," : "",
82 (vffi
->dwFileFlags
& VS_FF_PRERELEASE
) ? "PRERELEASE," : "",
83 (vffi
->dwFileFlags
& VS_FF_PATCHED
) ? "PATCHED," : "",
84 (vffi
->dwFileFlags
& VS_FF_PRIVATEBUILD
) ? "PRIVATEBUILD," : "",
85 (vffi
->dwFileFlags
& VS_FF_INFOINFERRED
) ? "INFOINFERRED," : "",
86 (vffi
->dwFileFlags
& VS_FF_SPECIALBUILD
) ? "SPECIALBUILD," : ""
89 dsprintf(ver
," OS=0x%x.0x%x ",
90 HIWORD(vffi
->dwFileOS
),
91 LOWORD(vffi
->dwFileOS
)
93 switch (vffi
->dwFileOS
&0xFFFF0000) {
94 case VOS_DOS
:dsprintf(ver
,"DOS,");break;
95 case VOS_OS216
:dsprintf(ver
,"OS/2-16,");break;
96 case VOS_OS232
:dsprintf(ver
,"OS/2-32,");break;
97 case VOS_NT
:dsprintf(ver
,"NT,");break;
100 dsprintf(ver
,"UNKNOWN(0x%lx),",vffi
->dwFileOS
&0xFFFF0000);break;
102 switch (LOWORD(vffi
->dwFileOS
)) {
103 case VOS__BASE
:dsprintf(ver
,"BASE");break;
104 case VOS__WINDOWS16
:dsprintf(ver
,"WIN16");break;
105 case VOS__WINDOWS32
:dsprintf(ver
,"WIN32");break;
106 case VOS__PM16
:dsprintf(ver
,"PM16");break;
107 case VOS__PM32
:dsprintf(ver
,"PM32");break;
108 default:dsprintf(ver
,"UNKNOWN(0x%x)",LOWORD(vffi
->dwFileOS
));break;
110 TRACE(ver
, "(%s)\n", dbg_str(ver
));
113 switch (vffi
->dwFileType
) {
116 dsprintf(ver
,"filetype=Unknown(0x%lx)",vffi
->dwFileType
);
118 case VFT_APP
:dsprintf(ver
,"filetype=APP,");break;
119 case VFT_DLL
:dsprintf(ver
,"filetype=DLL,");break;
121 dsprintf(ver
,"filetype=DRV,");
122 switch(vffi
->dwFileSubtype
) {
125 dsprintf(ver
,"UNKNOWN(0x%lx)",vffi
->dwFileSubtype
);
127 case VFT2_DRV_PRINTER
:
128 dsprintf(ver
,"PRINTER");
130 case VFT2_DRV_KEYBOARD
:
131 dsprintf(ver
,"KEYBOARD");
133 case VFT2_DRV_LANGUAGE
:
134 dsprintf(ver
,"LANGUAGE");
136 case VFT2_DRV_DISPLAY
:
137 dsprintf(ver
,"DISPLAY");
140 dsprintf(ver
,"MOUSE");
142 case VFT2_DRV_NETWORK
:
143 dsprintf(ver
,"NETWORK");
145 case VFT2_DRV_SYSTEM
:
146 dsprintf(ver
,"SYSTEM");
148 case VFT2_DRV_INSTALLABLE
:
149 dsprintf(ver
,"INSTALLABLE");
152 dsprintf(ver
,"SOUND");
155 dsprintf(ver
,"COMM");
157 case VFT2_DRV_INPUTMETHOD
:
158 dsprintf(ver
,"INPUTMETHOD");
163 dsprintf(ver
,"filetype=FONT.");
164 switch (vffi
->dwFileSubtype
) {
166 dsprintf(ver
,"UNKNOWN(0x%lx)",vffi
->dwFileSubtype
);
168 case VFT2_FONT_RASTER
:dsprintf(ver
,"RASTER");break;
169 case VFT2_FONT_VECTOR
:dsprintf(ver
,"VECTOR");break;
170 case VFT2_FONT_TRUETYPE
:dsprintf(ver
,"TRUETYPE");break;
173 case VFT_VXD
:dsprintf(ver
,"filetype=VXD");break;
174 case VFT_STATIC_LIB
:dsprintf(ver
,"filetype=STATIC_LIB");break;
176 TRACE(ver
, "%s\n", dbg_str(ver
));
178 TRACE(ver
, " filedata=0x%lx.0x%lx\n",
179 vffi
->dwFileDateMS
,vffi
->dwFileDateLS
);
182 /******************************************************************************
184 * int testFileExistence(
186 * char const * file )
188 * Tests whether a given path/file combination exists. If the file does
189 * not exist, the return value is zero. If it does exist, the return
193 * 30-May-1997 Dave Cuthbert (dacut@ece.cmu.edu)
194 * Original implementation
196 *****************************************************************************/
198 static int testFileExistence(
207 fileinfo
.cBytes
= sizeof(OFSTRUCT
);
209 strcpy(filename
, path
);
210 filenamelen
= strlen(filename
);
212 /* Add a trailing \ if necessary */
214 if(filename
[filenamelen
- 1] != '\\')
215 strcat(filename
, "\\");
217 else /* specify the current directory */
218 strcpy(filename
, ".\\");
220 /* Create the full pathname */
221 strcat(filename
, file
);
223 if(OpenFile32(filename
, &fileinfo
, OF_EXIST
) == HFILE_ERROR32
)
231 /******************************************************************************
233 * int testFileExclusiveExistence(
235 * char const * file )
237 * Tests whether a given path/file combination exists and ensures that no
238 * other programs have handles to the given file. If the file does not
239 * exist or is open, the return value is zero. If it does exist, the
240 * return value is non-zero.
243 * 30-May-1997 Dave Cuthbert (dacut@ece.cmu.edu)
244 * Original implementation
246 *****************************************************************************/
248 static int testFileExclusiveExistence(
257 fileinfo
.cBytes
= sizeof(OFSTRUCT
);
259 strcpy(filename
, path
);
260 filenamelen
= strlen(filename
);
262 /* Add a trailing \ if necessary */
264 if(filename
[filenamelen
- 1] != '\\')
265 strcat(filename
, "\\");
267 else /* specify the current directory */
268 strcpy(filename
, ".\\");
270 /* Create the full pathname */
271 strcat(filename
, file
);
273 if(OpenFile32(filename
, &fileinfo
, OF_EXIST
| OF_SHARE_EXCLUSIVE
) ==
283 static int read_xx_header(HFILE32 lzfd
) {
284 IMAGE_DOS_HEADER mzh
;
287 LZSeek32(lzfd
,0,SEEK_SET
);
288 if (sizeof(mzh
)!=LZRead32(lzfd
,&mzh
,sizeof(mzh
)))
290 if (mzh
.e_magic
!=IMAGE_DOS_SIGNATURE
)
292 LZSeek32(lzfd
,mzh
.e_lfanew
,SEEK_SET
);
293 if (2!=LZRead32(lzfd
,magic
,2))
295 LZSeek32(lzfd
,mzh
.e_lfanew
,SEEK_SET
);
296 if (magic
[0] == 'N' && magic
[1] == 'E')
297 return IMAGE_OS2_SIGNATURE
;
298 if (magic
[0] == 'P' && magic
[1] == 'E')
299 return IMAGE_NT_SIGNATURE
;
301 WARN(ver
,"Can't handle %s files.\n",magic
);
306 static int find_ne_resource(
307 HFILE32 lzfd
,SEGPTR
typeid,SEGPTR resid
,
308 BYTE
**resdata
,int *reslen
,DWORD
*off
310 IMAGE_OS2_HEADER nehd
;
317 nehdoffset
= LZTELL(lzfd
);
319 if (nehd
.resource_tab_offset
==nehd
.rname_tab_offset
) {
320 TRACE(ver
,"no resources in NE dll\n");
323 LZSeek32(lzfd
,nehd
.resource_tab_offset
+nehdoffset
,SEEK_SET
);
325 TRACE(ver
,"shiftcount is %d\n",shiftcount
);
326 TRACE(ver
,"reading resource typeinfo dir.\n");
328 if (!HIWORD(typeid)) typeid = (SEGPTR
)(LOWORD(typeid) | 0x8000);
329 if (!HIWORD(resid
)) resid
= (SEGPTR
)(LOWORD(resid
) | 0x8000);
336 TRACE(ver
," ti.typeid =%04x,count=%d\n",ti
.type_id
,ti
.count
);
339 if (!HIWORD(typeid)) {
340 if ((ti
.type_id
&0x8000)&&(typeid!=ti
.type_id
))
343 if (ti
.type_id
& 0x8000) {
350 whereleft
= LZTELL(lzfd
);
353 nehdoffset
+nehd
.resource_tab_offset
+ti
.type_id
,
358 if (len
!=LZRead32(lzfd
,str
,len
))
360 TRACE(ver
,"read %s to compare it with %s\n",
361 str
,(char*)PTR_SEG_TO_LIN(typeid)
363 if (lstrcmpi32A(str
,(char*)PTR_SEG_TO_LIN(typeid)))
366 LZSeek32(lzfd
,whereleft
,SEEK_SET
);
370 LZSeek32(lzfd
,ti
.count
*sizeof(ni
),SEEK_CUR
);
373 for (i
=0;i
<ti
.count
;i
++) {
378 TRACE(ver
," ni.id=%4x,offset=%d,length=%d\n",
379 ni
.id
,ni
.offset
,ni
.length
382 if (!HIWORD(resid
)) {
386 if (!(ni
.id
& 0x8000)) {
391 whereleft
= LZTELL(lzfd
);
394 nehdoffset
+nehd
.resource_tab_offset
+ni
.id
,
399 if (len
!=LZRead32(lzfd
,str
,len
))
401 TRACE(ver
,"read %s to compare it with %s\n",
402 str
,(char*)PTR_SEG_TO_LIN(typeid)
404 if (!lstrcmpi32A(str
,(char*)PTR_SEG_TO_LIN(typeid)))
407 LZSeek32(lzfd
,whereleft
,SEEK_SET
);
412 LZSeek32(lzfd
,((int)ni
.offset
<<shiftcount
),SEEK_SET
);
413 *off
= (int)ni
.offset
<<shiftcount
;
414 len
= ni
.length
<<shiftcount
;
415 rdata
=(WORD
*)xmalloc(len
);
416 if (len
!=LZRead32(lzfd
,rdata
,len
)) {
420 TRACE(ver
,"resource found.\n");
421 *resdata
= (BYTE
*)rdata
;
428 /* Loads the specified PE resource.
429 * FIXME: shouldn't load the whole image
433 HFILE32 lzfd
,LPWSTR
typeid,LPWSTR resid
,
434 BYTE
**resdata
,int *reslen
,DWORD
*off
436 IMAGE_NT_HEADERS pehd
;
439 DWORD imagesize
,pehdoffset
;
441 IMAGE_DATA_DIRECTORY resdir
;
442 PIMAGE_RESOURCE_DIRECTORY resourcedir
,xresdir
;
443 PIMAGE_RESOURCE_DATA_ENTRY xresdata
;
444 PIMAGE_SECTION_HEADER sections
;
446 pehdoffset
= LZTELL(lzfd
);
448 resdir
= pehd
.OptionalHeader
.DataDirectory
[IMAGE_FILE_RESOURCE_DIRECTORY
];
449 TRACE(ver
,"(.,type=%p, id=%p, len=%u, off=%lu)\n",typeid,resid
,*reslen
,*off
);
451 WARN(ver
,"No resource directory found in PE file.\n");
454 imagesize
= pehd
.OptionalHeader
.SizeOfImage
;
455 image
= HeapAlloc(GetProcessHeap(),0,imagesize
);
456 nrofsections
= pehd
.FileHeader
.NumberOfSections
;
458 sections
= (PIMAGE_SECTION_HEADER
)HeapAlloc(GetProcessHeap(),0,pehd
.FileHeader
.NumberOfSections
*sizeof(IMAGE_SECTION_HEADER
));
461 sizeof(DWORD
)+ /* Signature */
462 sizeof(IMAGE_FILE_HEADER
)+
463 pehd
.FileHeader
.SizeOfOptionalHeader
,
466 if ( nrofsections
*sizeof(IMAGE_SECTION_HEADER
)!=
467 LZRead32(lzfd
,sections
,nrofsections
*sizeof(IMAGE_SECTION_HEADER
))
469 HeapFree(GetProcessHeap(),0,image
);
472 for (i
=0;i
<nrofsections
;i
++) {
473 if (sections
[i
].Characteristics
& IMAGE_SCN_CNT_UNINITIALIZED_DATA
)
475 LZSeek32(lzfd
,sections
[i
].PointerToRawData
,SEEK_SET
);
476 if ( sections
[i
].SizeOfRawData
!=
477 LZRead32(lzfd
,image
+sections
[i
].VirtualAddress
,sections
[i
].SizeOfRawData
)
479 HeapFree(GetProcessHeap(),0,image
);
483 resourcedir
= (PIMAGE_RESOURCE_DIRECTORY
)(image
+resdir
.VirtualAddress
);
484 xresdir
= GetResDirEntryW(resourcedir
,typeid,(DWORD
)resourcedir
,FALSE
);
486 TRACE(ver
,"...no typeid entry found for %p\n",typeid);
487 HeapFree(GetProcessHeap(),0,image
);
490 xresdir
= GetResDirEntryW(xresdir
,resid
,(DWORD
)resourcedir
,FALSE
);
492 TRACE(ver
,"...no resid entry found for %p\n",resid
);
493 HeapFree(GetProcessHeap(),0,image
);
497 xresdir
= GetResDirEntryW(xresdir
,0,(DWORD
)resourcedir
,TRUE
);
499 TRACE(ver
,"...no 0 (default language) entry found for %p\n",resid
);
500 HeapFree(GetProcessHeap(),0,image
);
503 xresdata
= (PIMAGE_RESOURCE_DATA_ENTRY
)xresdir
;
504 *reslen
= xresdata
->Size
;
505 *resdata
= (LPBYTE
)xmalloc(*reslen
);
506 memcpy(*resdata
,image
+xresdata
->OffsetToData
,*reslen
);
507 /* find physical address for virtual offset */
508 for (i
=0;i
<nrofsections
;i
++) {
509 if (sections
[i
].Characteristics
& IMAGE_SCN_CNT_UNINITIALIZED_DATA
)
511 if ( (xresdata
->OffsetToData
>= sections
[i
].VirtualAddress
)&&
512 (xresdata
->OffsetToData
< sections
[i
].VirtualAddress
+sections
[i
].SizeOfRawData
)
514 *off
= (DWORD
)(xresdata
->OffsetToData
)-(DWORD
)(sections
[i
].VirtualAddress
)+(DWORD
)(sections
[i
].PointerToRawData
);
518 HeapFree(GetProcessHeap(),0,image
);
519 HeapFree(GetProcessHeap(),0,sections
);
520 TRACE(ver
,"-- found at off=%lu\n",*off
);
524 /* GetFileResourceSize [VER.2] */
525 DWORD WINAPI
GetFileResourceSize(LPCSTR filename
,SEGPTR restype
,SEGPTR resid
,
530 BYTE
*resdata
= NULL
;
534 TRACE(ver
,"(%s,type=0x%lx,id=0x%lx,off=%p)\n",
535 filename
,(LONG
)restype
,(LONG
)resid
,off
537 lzfd
=LZOpenFile32A(filename
,&ofs
,OF_READ
);
540 switch (read_xx_header(lzfd
)) {
544 case IMAGE_OS2_SIGNATURE
:
545 res
=find_ne_resource(lzfd
,restype
,resid
,&resdata
,&reslen
,off
);
547 case IMAGE_NT_SIGNATURE
:
548 res
=find_pe_resource(lzfd
,(LPWSTR
)restype
,(LPWSTR
)resid
,&resdata
,&reslen
,off
);
561 /* GetFileResource [VER.3] */
562 DWORD WINAPI
GetFileResource(LPCSTR filename
,SEGPTR restype
,SEGPTR resid
,
563 DWORD off
,DWORD datalen
,LPVOID data
)
571 TRACE(ver
,"(%s,type=0x%lx,id=0x%lx,off=%ld,len=%ld,date=%p)\n",
572 filename
,(LONG
)restype
,(LONG
)resid
,off
,datalen
,data
575 lzfd
=LZOpenFile32A(filename
,&ofs
,OF_READ
);
579 switch (read_xx_header(lzfd
)) {
582 case IMAGE_OS2_SIGNATURE
:
583 res
= find_ne_resource(lzfd
,restype
,resid
,&resdata
,&reslen
,&off
);
585 case IMAGE_NT_SIGNATURE
:
586 res
= find_pe_resource(lzfd
,(LPWSTR
)restype
,(LPWSTR
)resid
,&resdata
,&reslen
,&off
);
592 if (reslen
>datalen
) reslen
= datalen
;
593 memcpy(data
,resdata
,reslen
);
595 TRACE(ver
,"--[1] len=%u\n", reslen
);
598 LZSeek32(lzfd
,off
,SEEK_SET
);
599 reslen
= LZRead32(lzfd
,data
,datalen
);
601 TRACE(ver
,"--[2] len=%u\n", reslen
);
605 /* GetFileVersionInfoSize [VER.6] */
606 DWORD WINAPI
GetFileVersionInfoSize16(LPCSTR filename
,LPDWORD handle
)
608 DWORD len
,ret
,isuni
=0;
610 VS_FIXEDFILEINFO
*vffi
;
612 TRACE(ver
,"(%s,%p)\n",filename
,handle
);
613 len
=GetFileResourceSize(filename
,VS_FILE_INFO
,VS_VERSION_INFO
,handle
);
617 filename
,VS_FILE_INFO
,VS_VERSION_INFO
,*handle
,sizeof(buf
),buf
622 vffi
=(VS_FIXEDFILEINFO
*)(buf
+0x14);
623 if (vffi
->dwSignature
!= VS_FFI_SIGNATURE
) {
624 /* unicode resource */
625 if (vffi
->dwSignature
== 0x004f0049) {
627 vffi
= (VS_FIXEDFILEINFO
*)(buf
+0x28);
629 WARN(ver
,"vffi->dwSignature is 0x%08lx, but not 0x%08lx!\n",
630 vffi
->dwSignature
,VS_FFI_SIGNATURE
635 if (*(WORD
*)buf
< len
)
639 print_vffi_debug(vffi
);
644 /* GetFileVersionInfoSize32A [VERSION.1] */
645 DWORD WINAPI
GetFileVersionInfoSize32A(LPCSTR filename
,LPDWORD handle
)
647 TRACE(ver
,"(%s,%p)\n",filename
,handle
);
648 return GetFileVersionInfoSize16(filename
,handle
);
651 /* GetFileVersionInfoSize32W [VERSION.2] */
652 DWORD WINAPI
GetFileVersionInfoSize32W( LPCWSTR filename
, LPDWORD handle
)
654 LPSTR xfn
= HEAP_strdupWtoA( GetProcessHeap(), 0, filename
);
655 DWORD ret
= GetFileVersionInfoSize16( xfn
, handle
);
656 HeapFree( GetProcessHeap(), 0, xfn
);
660 /* GetFileVersionInfo [VER.7] */
661 DWORD WINAPI
GetFileVersionInfo16(LPCSTR filename
,DWORD handle
,DWORD datasize
,
664 TRACE(ver
,"(%s,%ld,size=%ld,data=%p)\n",
665 filename
,handle
,datasize
,data
667 return GetFileResource(
668 filename
,VS_FILE_INFO
,VS_VERSION_INFO
,handle
,datasize
,data
672 /* GetFileVersionInfoA [VERSION.0] */
673 DWORD WINAPI
GetFileVersionInfo32A(LPCSTR filename
,DWORD handle
,
674 DWORD datasize
,LPVOID data
)
676 return GetFileVersionInfo16(filename
,handle
,datasize
,data
);
679 /* GetFileVersionInfoW [VERSION.3] */
680 DWORD WINAPI
GetFileVersionInfo32W( LPCWSTR filename
, DWORD handle
,
681 DWORD datasize
, LPVOID data
)
683 LPSTR fn
= HEAP_strdupWtoA( GetProcessHeap(), 0, filename
);
684 DWORD ret
= GetFileVersionInfo16( fn
, handle
, datasize
, data
);
685 HeapFree( GetProcessHeap(), 0, fn
);
689 /*****************************************************************************
691 * VerFindFile() [VER.8]
692 * Determines where to install a file based on whether it locates another
693 * version of the file in the system. The values VerFindFile returns are
694 * used in a subsequent call to the VerInstallFile function.
697 * 30-May-1997 Dave Cuthbert (dacut@ece.cmu.edu)
698 * Reimplementation of VerFindFile from original stub.
700 ****************************************************************************/
702 DWORD WINAPI
VerFindFile16(
708 UINT16
*lpuCurDirLen
,
710 UINT16
*lpuDestDirLen
)
715 unsigned int curDirSizeReq
;
716 unsigned int destDirSizeReq
;
720 /* Print out debugging information */
721 TRACE(ver
, "called with parameters:\n"
722 "\tflags = %x", flags
);
723 if(flags
& VFFF_ISSHAREDFILE
)
724 TRACE(ver
, " (VFFF_ISSHAREDFILE)\n");
728 ver_dstring("\tlpszFilename = ", lpszFilename
, "");
729 ver_dstring("\tlpszWinDir = ", lpszWinDir
, "");
730 ver_dstring("\tlpszAppDir = ", lpszAppDir
, "");
732 TRACE(ver
, "\tlpszCurDir = %p\n", lpszCurDir
);
734 TRACE(ver
, "\tlpuCurDirLen = %p (%u)\n",
735 lpuCurDirLen
, *lpuCurDirLen
);
737 TRACE(ver
, "\tlpuCurDirLen = (null)\n");
739 TRACE(ver
, "\tlpszDestDir = %p\n", lpszDestDir
);
741 TRACE(ver
, "\tlpuDestDirLen = %p (%u)\n",
742 lpuDestDirLen
, *lpuDestDirLen
);
744 /* Figure out where the file should go; shared files default to the
750 if(flags
& VFFF_ISSHAREDFILE
) {
751 GetSystemDirectory32A(destDir
, 256);
753 /* Were we given a filename? If so, try to find the file. */
755 if(testFileExistence(destDir
, lpszFilename
)) {
756 strcpy(curDir
, destDir
);
758 if(!testFileExclusiveExistence(destDir
, lpszFilename
))
759 retval
|= VFF_FILEINUSE
;
761 else if(lpszAppDir
&& testFileExistence(lpszAppDir
,
763 strcpy(curDir
, lpszAppDir
);
764 retval
|= VFF_CURNEDEST
;
766 if(!testFileExclusiveExistence(lpszAppDir
, lpszFilename
))
767 retval
|= VFF_FILEINUSE
;
771 else if(!(flags
& VFFF_ISSHAREDFILE
)) { /* not a shared file */
774 GetSystemDirectory32A(systemDir
, 256);
776 strcpy(destDir
, lpszAppDir
);
779 if(testFileExistence(lpszAppDir
, lpszFilename
)) {
780 strcpy(curDir
, lpszAppDir
);
782 if(!testFileExclusiveExistence(lpszAppDir
, lpszFilename
))
783 retval
|= VFF_FILEINUSE
;
785 else if(testFileExistence(systemDir
, lpszFilename
)) {
786 strcpy(curDir
, systemDir
);
787 retval
|= VFF_CURNEDEST
;
789 if(!testFileExclusiveExistence(systemDir
, lpszFilename
))
790 retval
|= VFF_FILEINUSE
;
796 curDirSizeReq
= strlen(curDir
) + 1;
797 destDirSizeReq
= strlen(destDir
) + 1;
801 /* Make sure that the pointers to the size of the buffers are
802 valid; if not, do NOTHING with that buffer. If that pointer
803 is valid, then make sure that the buffer pointer is valid, too! */
805 if(lpuDestDirLen
&& lpszDestDir
) {
806 if(*lpuDestDirLen
< destDirSizeReq
) {
807 retval
|= VFF_BUFFTOOSMALL
;
808 strncpy(lpszDestDir
, destDir
, *lpuDestDirLen
- 1);
809 lpszDestDir
[*lpuDestDirLen
- 1] = '\0';
812 strcpy(lpszDestDir
, destDir
);
814 *lpuDestDirLen
= destDirSizeReq
;
817 if(lpuCurDirLen
&& lpszCurDir
) {
818 if(*lpuCurDirLen
< curDirSizeReq
) {
819 retval
|= VFF_BUFFTOOSMALL
;
820 strncpy(lpszCurDir
, curDir
, *lpuCurDirLen
- 1);
821 lpszCurDir
[*lpuCurDirLen
- 1] = '\0';
824 strcpy(lpszCurDir
, curDir
);
826 *lpuCurDirLen
= curDirSizeReq
;
829 TRACE(ver
, "ret = %lu (%s%s%s)\n", retval
,
830 (retval
& VFF_CURNEDEST
) ? "VFF_CURNEDEST " : "",
831 (retval
& VFF_FILEINUSE
) ? "VFF_FILEINUSE " : "",
832 (retval
& VFF_BUFFTOOSMALL
) ? "VFF_BUFFTOOSMALL " : "");
834 ver_dstring("\t(Exit) lpszCurDir = ", lpszCurDir
, "");
836 TRACE(ver
, "\t(Exit) lpuCurDirLen = %p (%u)\n",
837 lpuCurDirLen
, *lpuCurDirLen
);
839 TRACE(ver
, "\t(Exit) lpuCurDirLen = (null)\n");
841 ver_dstring("\t(Exit) lpszDestDir = ", lpszDestDir
, "");
843 TRACE(ver
, "\t(Exit) lpuDestDirLen = %p (%u)\n",
844 lpuDestDirLen
, *lpuDestDirLen
);
849 /* VerFindFileA [VERSION.5] */
850 DWORD WINAPI
VerFindFile32A(
851 UINT32 flags
,LPCSTR filename
,LPCSTR windir
,LPCSTR appdir
,
852 LPSTR curdir
,UINT32
*pcurdirlen
,LPSTR destdir
,UINT32
*pdestdirlen
)
854 UINT16 curdirlen
, destdirlen
;
857 curdirlen
= (UINT16
)*pcurdirlen
;
858 destdirlen
= (UINT16
)*pdestdirlen
;
860 ret
= VerFindFile16(flags
,filename
,windir
,appdir
,
861 curdir
,&curdirlen
,destdir
,&destdirlen
);
862 *pcurdirlen
= curdirlen
;
863 *pdestdirlen
= destdirlen
;
867 /* VerFindFileW [VERSION.6] */
868 DWORD WINAPI
VerFindFile32W(
869 UINT32 flags
,LPCWSTR filename
,LPCWSTR windir
,LPCWSTR appdir
,
870 LPWSTR curdir
,UINT32
*pcurdirlen
,LPWSTR destdir
,UINT32
*pdestdirlen
)
872 UINT16 curdirlen
, destdirlen
;
873 LPSTR wfn
,wwd
,wad
,wdd
,wcd
;
876 wfn
= HEAP_strdupWtoA( GetProcessHeap(), 0, filename
);
877 wwd
= HEAP_strdupWtoA( GetProcessHeap(), 0, windir
);
878 wad
= HEAP_strdupWtoA( GetProcessHeap(), 0, appdir
);
879 wcd
= HeapAlloc( GetProcessHeap(), 0, *pcurdirlen
);
880 wdd
= HeapAlloc( GetProcessHeap(), 0, *pdestdirlen
);
881 ret
= VerFindFile16(flags
,wfn
,wwd
,wad
,wcd
,&curdirlen
,wdd
,&destdirlen
);
882 lstrcpynAtoW(curdir
,wcd
,*pcurdirlen
);
883 lstrcpynAtoW(destdir
,wdd
,*pdestdirlen
);
884 *pcurdirlen
= strlen(wcd
);
885 *pdestdirlen
= strlen(wdd
);
886 HeapFree( GetProcessHeap(), 0, wfn
);
887 HeapFree( GetProcessHeap(), 0, wwd
);
888 HeapFree( GetProcessHeap(), 0, wad
);
889 HeapFree( GetProcessHeap(), 0, wcd
);
890 HeapFree( GetProcessHeap(), 0, wdd
);
894 /* VerInstallFile [VER.9] */
895 DWORD WINAPI
VerInstallFile16(
896 UINT16 flags
,LPCSTR srcfilename
,LPCSTR destfilename
,LPCSTR srcdir
,
897 LPCSTR destdir
,LPCSTR curdir
,LPSTR tmpfile
,UINT16
*tmpfilelen
)
900 DWORD ret
= VerInstallFile32A(flags
,srcfilename
,destfilename
,srcdir
,
901 destdir
,curdir
,tmpfile
,&filelen
);
903 *tmpfilelen
= filelen
;
908 _fetch_versioninfo(LPSTR fn
,VS_FIXEDFILEINFO
**vffi
) {
914 buf
= xmalloc(alloclen
);
916 ret
= GetFileVersionInfo32A(fn
,0,alloclen
,buf
);
921 if (alloclen
<*(WORD
*)buf
) {
923 alloclen
= *(WORD
*)buf
;
924 buf
= xmalloc(alloclen
);
926 *vffi
= (VS_FIXEDFILEINFO
*)(buf
+0x14);
927 if ((*vffi
)->dwSignature
== 0x004f0049) /* hack to detect unicode */
928 *vffi
= (VS_FIXEDFILEINFO
*)(buf
+0x28);
929 if ((*vffi
)->dwSignature
!= VS_FFI_SIGNATURE
)
930 WARN(ver
,"Bad VS_FIXEDFILEINFO signature 0x%08lx\n",(*vffi
)->dwSignature
);
937 _error2vif(DWORD error
) {
939 case ERROR_ACCESS_DENIED
:
940 return VIF_ACCESSVIOLATION
;
941 case ERROR_SHARING_VIOLATION
:
942 return VIF_SHARINGVIOLATION
;
949 /******************************************************************************
950 * VerInstallFile32A [VERSION.7]
952 DWORD WINAPI
VerInstallFile32A(
953 UINT32 flags
,LPCSTR srcfilename
,LPCSTR destfilename
,LPCSTR srcdir
,
954 LPCSTR destdir
,LPCSTR curdir
,LPSTR tmpfile
,UINT32
*tmpfilelen
)
957 char destfn
[260],tmpfn
[260],srcfn
[260];
959 DWORD attr
,ret
,xret
,tmplast
;
963 TRACE(ver
,"(%x,%s,%s,%s,%s,%s,%p,%d)\n",
964 flags
,srcfilename
,destfilename
,srcdir
,destdir
,curdir
,tmpfile
,*tmpfilelen
967 sprintf(srcfn
,"%s\\%s",srcdir
,srcfilename
);
968 if (!destdir
|| !*destdir
) pdest
= srcdir
;
969 else pdest
= destdir
;
970 sprintf(destfn
,"%s\\%s",pdest
,destfilename
);
971 hfsrc
=LZOpenFile32A(srcfn
,&ofs
,OF_READ
);
972 if (hfsrc
==HFILE_ERROR32
)
973 return VIF_CANNOTREADSRC
;
974 sprintf(tmpfn
,"%s\\%s",pdest
,destfilename
);
975 tmplast
=strlen(pdest
)+1;
976 attr
= GetFileAttributes32A(tmpfn
);
978 if (attr
& FILE_ATTRIBUTE_READONLY
) {
980 return VIF_WRITEPROT
;
982 /* FIXME: check if file currently in use and return VIF_FILEINUSE */
985 if (flags
& VIFF_FORCEINSTALL
) {
987 sprintf(tmpfn
,"%s\\%s",pdest
,tmpfile
);
988 tmplast
= strlen(pdest
)+1;
989 attr
= GetFileAttributes32A(tmpfn
);
990 /* if it exists, it has been copied by the call before.
991 * we jump over the copy part...
998 GetTempFileName32A(pdest
,"ver",0,tmpfn
); /* should not fail ... */
999 s
=strrchr(tmpfn
,'\\');
1004 hfdst
= OpenFile32(tmpfn
,&ofs
,OF_CREATE
);
1005 if (hfdst
== HFILE_ERROR32
) {
1007 return VIF_CANNOTCREATE
; /* | translated dos error */
1009 ret
= LZCopy32(hfsrc
,hfdst
);
1011 if (((long) ret
) < 0) {
1012 /* translate LZ errors into VIF_xxx */
1014 case LZERROR_BADINHANDLE
:
1016 case LZERROR_BADVALUE
:
1017 case LZERROR_UNKNOWNALG
:
1018 ret
= VIF_CANNOTREADSRC
;
1020 case LZERROR_BADOUTHANDLE
:
1022 ret
= VIF_OUTOFMEMORY
; /* FIXME: correct? */
1024 case LZERROR_GLOBALLOC
:
1025 case LZERROR_GLOBLOCK
:
1026 ret
= VIF_OUTOFSPACE
;
1028 default: /* unknown error, should not happen */
1039 if (!(flags
& VIFF_FORCEINSTALL
)) {
1040 VS_FIXEDFILEINFO
*destvffi
,*tmpvffi
;
1041 buf1
= _fetch_versioninfo(destfn
,&destvffi
);
1043 buf2
= _fetch_versioninfo(tmpfn
,&tmpvffi
);
1050 /* compare file versions */
1051 if ((destvffi
->dwFileVersionMS
> tmpvffi
->dwFileVersionMS
)||
1052 ((destvffi
->dwFileVersionMS
==tmpvffi
->dwFileVersionMS
)&&
1053 (destvffi
->dwFileVersionLS
> tmpvffi
->dwFileVersionLS
)
1056 xret
|= VIF_MISMATCH
|VIF_SRCOLD
;
1057 /* compare filetypes and filesubtypes */
1058 if ((destvffi
->dwFileType
!=tmpvffi
->dwFileType
) ||
1059 (destvffi
->dwFileSubtype
!=tmpvffi
->dwFileSubtype
)
1061 xret
|= VIF_MISMATCH
|VIF_DIFFTYPE
;
1062 if (VerQueryValue32A(buf1
,"\\VarFileInfo\\Translation",(LPVOID
*)&tbuf1
,&len1
) &&
1063 VerQueryValue32A(buf2
,"\\VarFileInfo\\Translation",(LPVOID
*)&tbuf2
,&len2
)
1065 /* irgendwas mit tbuf1 und tbuf2 machen
1066 * generiert DIFFLANG|MISMATCH
1071 xret
=VIF_MISMATCH
|VIF_SRCOLD
;
1076 if (*tmpfilelen
<strlen(tmpfn
+tmplast
)) {
1077 xret
|=VIF_BUFFTOOSMALL
;
1078 DeleteFile32A(tmpfn
);
1080 strcpy(tmpfile
,tmpfn
+tmplast
);
1081 *tmpfilelen
= strlen(tmpfn
+tmplast
)+1;
1085 if (-1!=GetFileAttributes32A(destfn
))
1086 if (!DeleteFile32A(destfn
)) {
1087 xret
|=_error2vif(GetLastError())|VIF_CANNOTDELETE
;
1088 DeleteFile32A(tmpfn
);
1092 if ((!(flags
& VIFF_DONTDELETEOLD
)) &&
1095 lstrcmpi32A(curdir
,pdest
)
1099 sprintf(curfn
,"%s\\%s",curdir
,destfilename
);
1100 if (-1!=GetFileAttributes32A(curfn
)) {
1101 /* FIXME: check if in use ... if it is, VIF_CANNOTDELETECUR */
1102 if (!DeleteFile32A(curfn
))
1103 xret
|=_error2vif(GetLastError())|VIF_CANNOTDELETECUR
;
1106 if (!MoveFile32A(tmpfn
,destfn
)) {
1107 xret
|=_error2vif(GetLastError())|VIF_CANNOTRENAME
;
1108 DeleteFile32A(tmpfn
);
1116 /* VerInstallFileW [VERSION.8] */
1117 DWORD WINAPI
VerInstallFile32W(
1118 UINT32 flags
,LPCWSTR srcfilename
,LPCWSTR destfilename
,LPCWSTR srcdir
,
1119 LPCWSTR destdir
,LPCWSTR curdir
,LPWSTR tmpfile
,UINT32
*tmpfilelen
)
1121 LPSTR wsrcf
,wsrcd
,wdestf
,wdestd
,wtmpf
,wcurd
;
1124 wsrcf
= HEAP_strdupWtoA( GetProcessHeap(), 0, srcfilename
);
1125 wsrcd
= HEAP_strdupWtoA( GetProcessHeap(), 0, srcdir
);
1126 wdestf
= HEAP_strdupWtoA( GetProcessHeap(), 0, destfilename
);
1127 wdestd
= HEAP_strdupWtoA( GetProcessHeap(), 0, destdir
);
1128 wtmpf
= HEAP_strdupWtoA( GetProcessHeap(), 0, tmpfile
);
1129 wcurd
= HEAP_strdupWtoA( GetProcessHeap(), 0, curdir
);
1130 ret
= VerInstallFile32A(flags
,wsrcf
,wdestf
,wsrcd
,wdestd
,wcurd
,wtmpf
,tmpfilelen
);
1132 lstrcpynAtoW(tmpfile
,wtmpf
,*tmpfilelen
);
1133 HeapFree( GetProcessHeap(), 0, wsrcf
);
1134 HeapFree( GetProcessHeap(), 0, wsrcd
);
1135 HeapFree( GetProcessHeap(), 0, wdestf
);
1136 HeapFree( GetProcessHeap(), 0, wdestd
);
1137 HeapFree( GetProcessHeap(), 0, wtmpf
);
1139 HeapFree( GetProcessHeap(), 0, wcurd
);
1147 /* in memory structure... */
1148 char name
[1]; /* padded to dword alignment */
1150 char data[datalen]; padded to dword alignment
1151 BYTE subdirdata[]; until nextoff
1155 #define DATA_OFFSET_A(db) ((4+(strlen((db)->name)+4))&~3)
1160 WORD btext
; /* type of data */
1161 /* in memory structure... */
1162 WCHAR name
[1]; /* padded to dword alignment */
1164 WCHAR data[datalen]; padded to dword alignment
1165 BYTE subdirdata[]; until nextoff
1172 * WCHAR szKey[]; (zero terminated)
1173 * PADDING (round up to nearest 32bit boundary)
1175 #define DATA_OFFSET_W(db) ((2+2+2+((lstrlen32W((db)->name)+1)*2+3))&~3)
1177 /* this one used for Win16 resources, which are always in ASCII format */
1179 _find_dataA(BYTE
*block
,LPCSTR str
, int buff_remain
) {
1181 int substrlen
, inc_size
;
1184 while (*str
&& *str
=='\\')
1186 if (NULL
!=(nextslash
=strchr(str
,'\\')))
1187 substrlen
=nextslash
-str
;
1189 substrlen
=strlen(str
);
1190 if (nextslash
!=NULL
) {
1191 while (*nextslash
&& *nextslash
=='\\')
1195 } else if (*str
== 0)
1200 db
=(struct dbA
*)block
;
1201 TRACE(ver
,"db=%p,db->nextoff=%d,db->datalen=%d,db->name=%s\n",
1202 db
,db
->nextoff
,db
->datalen
,db
->name
1204 if ((!db
->nextoff
) || (buff_remain
<=0)) /* no more entries ? */
1207 TRACE(ver
,"comparing with %s\n",db
->name
);
1208 if (!lstrncmpi32A(db
->name
,str
,substrlen
)) {
1210 inc_size
=DATA_OFFSET_A(db
)+((db
->datalen
+3)&~3);
1211 return _find_dataA(block
+inc_size
,nextslash
,
1212 db
->nextoff
-inc_size
);
1216 inc_size
= ((db
->nextoff
+3)&~3);
1218 buff_remain
-= inc_size
;
1222 /* this one used for Win32 resources, which are always in UNICODE format */
1223 extern LPWSTR __cdecl
CRTDLL_wcschr(LPCWSTR str
,WCHAR xchar
);
1225 _find_dataW(BYTE
*block
,LPCWSTR str
, int buff_remain
) {
1227 int substrlen
, inc_size
;
1231 while (*str
&& *str
=='\\')
1233 if (NULL
!=(nextslash
=CRTDLL_wcschr(str
,'\\')))
1234 substrlen
=nextslash
-str
;
1236 substrlen
=lstrlen32W(str
);
1237 if (nextslash
!=NULL
) {
1238 while (*nextslash
&& *nextslash
=='\\')
1242 } else if (*str
== 0)
1248 db
=(struct dbW
*)block
;
1249 xs
= HEAP_strdupWtoA(GetProcessHeap(),0,db
->name
);
1252 vs
= HEAP_strdupWtoA(GetProcessHeap(),0,(WCHAR
*)((block
+DATA_OFFSET_W(db
))));
1254 vs
= HEAP_strdupA(GetProcessHeap(),0,"not a string");
1256 vs
= HEAP_strdupA(GetProcessHeap(),0,"no data");
1258 TRACE(ver
,"db->nextoff=%d,db->name=%s,db->data=\"%s\"\n",
1261 HeapFree(GetProcessHeap(),0,vs
);
1262 HeapFree(GetProcessHeap(),0,xs
);
1263 if ((!db
->nextoff
) || (buff_remain
<=0)) /* no more entries ? */
1266 if (!lstrncmpi32W(db
->name
,str
,substrlen
)) {
1268 /* DATA_OFFSET_W(db) (padded to 32bit already)
1270 * PADDING (round up to nearest 32bit boundary)
1271 * --> next level structs
1273 inc_size
=DATA_OFFSET_W(db
)+((db
->datalen
+3)&~3);
1274 return _find_dataW( block
+inc_size
,nextslash
,
1275 db
->nextoff
-inc_size
);
1279 /* skip over this block, round up to nearest 32bit boundary */
1280 inc_size
= ((db
->nextoff
+3)&~3);
1282 buff_remain
-= inc_size
;
1286 /* VerQueryValue [VER.11] */
1287 /* take care, 'buffer' is NOT a SEGPTR, it just points to one */
1288 DWORD WINAPI
VerQueryValue16(SEGPTR segblock
,LPCSTR subblock
,SEGPTR
*buffer
,
1292 BYTE
*block
=PTR_SEG_TO_LIN(segblock
),*b
;
1294 TRACE(ver
,"(%p,%s,%p,%d)\n",
1295 block
,subblock
,buffer
,*buflen
1298 s
=(char*)xmalloc(strlen("VS_VERSION_INFO\\")+strlen(subblock
)+1);
1299 strcpy(s
,"VS_VERSION_INFO\\");strcat(s
,subblock
);
1300 /* check for UNICODE version */
1301 if ( (*(DWORD
*)(block
+0x14) != VS_FFI_SIGNATURE
) &&
1302 (*(DWORD
*)(block
+0x28) == VS_FFI_SIGNATURE
)
1308 wstr
= HEAP_strdupAtoW(GetProcessHeap(),0,s
);
1309 b
=_find_dataW(block
,wstr
,*(WORD
*)block
);
1310 HeapFree(GetProcessHeap(),0,wstr
);
1312 WARN(ver
,"key %s not found in versionresource.\n",s
);
1318 b
= b
+DATA_OFFSET_W(db
);
1319 *buflen
= db
->datalen
;
1321 xs
= HEAP_strdupWtoA(GetProcessHeap(),0,(WCHAR
*)b
);
1322 TRACE(ver
,"->%s\n",xs
);
1323 HeapFree(GetProcessHeap(),0,xs
);
1325 TRACE(ver
,"->%p\n",b
);
1328 b
=_find_dataA(block
,s
,*(WORD
*)block
);
1330 WARN(ver
,"key %s not found in versionresource.\n",s
);
1336 b
= b
+DATA_OFFSET_A(db
);
1337 *buflen
= db
->datalen
;
1338 /* the string is only printable, if it is below \\StringFileInfo*/
1339 if (!lstrncmpi32A("VS_VERSION_INFO\\StringFileInfo\\",s
,strlen("VS_VERSION_INFO\\StringFileInfo\\")))
1340 TRACE(ver
," -> %s=%s\n",subblock
,b
);
1342 TRACE(ver
," -> %s=%p\n",subblock
,b
);
1344 *buffer
= (b
-block
)+segblock
;
1349 DWORD WINAPI
VerQueryValue32A(LPVOID vblock
,LPCSTR subblock
,
1350 LPVOID
*vbuffer
,UINT32
*buflen
)
1352 BYTE
*b
,*block
=(LPBYTE
)vblock
,**buffer
=(LPBYTE
*)vbuffer
;
1355 TRACE(ver
,"(%p,%s,%p,%d)\n",
1356 block
,subblock
,buffer
,*buflen
1359 s
=(char*)xmalloc(strlen("VS_VERSION_INFO\\")+strlen(subblock
)+1);
1360 strcpy(s
,"VS_VERSION_INFO\\");strcat(s
,subblock
);
1362 /* check for UNICODE version */
1363 if ( (*(DWORD
*)(block
+0x14) != VS_FFI_SIGNATURE
) &&
1364 (*(DWORD
*)(block
+0x28) == VS_FFI_SIGNATURE
)
1370 wstr
= HEAP_strdupAtoW(GetProcessHeap(),0,s
);
1371 b
=_find_dataW(block
,wstr
,*(WORD
*)block
);
1372 HeapFree(GetProcessHeap(),0,wstr
);
1374 WARN(ver
,"key %s not found in versionresource.\n",s
);
1379 db
= (struct dbW
*)b
;
1380 *buflen
= db
->datalen
;
1381 b
= b
+DATA_OFFSET_W(db
);
1383 xs
= HEAP_strdupWtoA(GetProcessHeap(),0,(WCHAR
*)b
);
1384 TRACE(ver
,"->%s\n",xs
);
1385 HeapFree(GetProcessHeap(),0,xs
);
1387 /* This is a leak. */
1388 b
= HEAP_strdupWtoA(GetProcessHeap(),0,(WCHAR
*)b
);
1390 TRACE(ver
,"->%p\n",b
);
1393 b
=_find_dataA(block
,s
,*(WORD
*)block
);
1395 WARN(ver
,"key %s not found in versionresource.\n",subblock
);
1401 *buflen
= db
->datalen
;
1402 b
= b
+DATA_OFFSET_A(db
);
1404 /* the string is only printable, if it is below \\StringFileInfo*/
1405 if (!lstrncmpi32A("VS_VERSION_INFO\\StringFileInfo\\",s
,strlen("VS_VERSION_INFO\\StringFileInfo\\")))
1406 TRACE(ver
," -> %s=%s\n",subblock
,b
);
1408 TRACE(ver
," -> %s=%p\n",subblock
,b
);
1415 DWORD WINAPI
VerQueryValue32W(LPVOID vblock
,LPCWSTR subblock
,LPVOID
*vbuffer
,
1421 sb
= HEAP_strdupWtoA( GetProcessHeap(), 0, subblock
);
1422 ret
= VerQueryValue32A(vblock
,sb
,vbuffer
,buflen
);
1423 HeapFree( GetProcessHeap(), 0, sb
);
1426 /* 20 GETFILEVERSIONINFORAW */