Question and Answer Database FAQ1495D.txt — re-mark a Delphi 16-Bit executable to be a Windows 4.0 application Category :Delphi 1.x Platform :All Windows Product : Question: Is it possible to re-mark a Delphi 16-Bit executable to be a Windows 4.0 application, and why have I been instructed to do this? Is it also possible to tell what kind of executable image a given file is and it's expected windows version before I load it? Answer: There are several reasons why you would want to mark a 16-Bit executable to be a Windows 4.0 application. 1) Thunks created using the Microsoft Thunk Compiler will not work unless the 16-bit thunk DLL is marked for version 4.0. 2) Print drivers and other sub system driver files created for Windows 95 must be marked for version 4.0 even though they may be 16-bit executable images. 3) All standard control windows owned by a version 4.0 application are drawn with a chiseled 3D look. 4) Applications marked for Windows version 4.0 receive the full benefit of new user interface features available in Windows 95. 5) New window messages, such as WM_STYLECHANGED, are sent to applications that are marked as a version 4.0 application. 6) Printer DCs are reset during StartPage() in version 4.0 applications. 7) Executables marked as a Windows version 4.0 executable will load on Windows 95, Windows NT versions 3.5, Win32s version 1.15, and all later versions, but will not load on earlier versions of Windows such as Windows 3.1. Note: Windows applications marked as 3.1 executables allow other applications to share GDI resources such as font or bitmap handles. Windows 95 does not clean up objects left undeleted by a 16-bit version 3.x application. Objects created by a 16-bit version 4.0-based applications are not shared, and are freed automatically when the application terminates. The following is an example of how to read, examine, and remark the expected windows version of any Windows executable, dynamic link library, or .vxd file. Note: Any non-zero file checksums are not recalculated. You cannot mark a file that is currently in use. To build the project, you will need to create a form that contains two buttons, one label, an OpenDialog component, and two spin edit components. Example: unit Unit1; interface {$IFDEF WIN32} uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Spin; {$ELSE} uses WinTypes, WinProcs, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Spin; {$ENDIF} type TForm1 = class(TForm) SpinEdit1: TSpinEdit; SpinEdit2: TSpinEdit; Button1: TButton; Button2: TButton; OpenDialog1: TOpenDialog; Label1: TLabel; procedure FormCreate(Sender: TObject); procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); private { Private declarations } ExeType : word; hdroffset : longint; procedure GetExeHeader(FileName : string); procedure RemarkExeHeader(FileName : string); public { Public declarations } end; var Form1: TForm1; implementation {$R *.DFM} const IMAGE_UNKNOWN_SIGNATURE = $0; const IMAGE_DOS_SIGNATURE = $5A4D; {MZ} const IMAGE_OS2_SIGNATURE = $454E; {NE} const IMAGE_OS2_SIGNATURE_LE = $454C; {LE} const IMAGE_VXD_SIGNATURE = $454C; {LE} const IMAGE_NT_SIGNATURE = $4550; {PE} const NE_IMAGE_DLL = $8000; {File is a DLL} const IMAGE_NT_OPTIONAL_HDR_MAGIC = $10B; const IMAGE_ROM_OPTIONAL_HDR_MAGIC = $107; const IMAGE_FILE_RELOCS_STRIPPED = $0001; {Relocation info stripd} const IMAGE_FILE_EXECUTABLE_IMAGE = $0002; {File is executable} const IMAGE_FILE_LINE_NUMS_STRIPPED = $0004; {Line numbers stripped} const IMAGE_FILE_LOCAL_SYMS_STRIPPED = $0008; {Local symbols stripped} const IMAGE_FILE_BYTES_REVERSED_LO = $0080; {machine word bytes rev} const IMAGE_FILE_32BIT_MACHINE = $0100; {32 bit word machine} const IMAGE_FILE_DEBUG_STRIPPED = $0200; {Debug info stripped} const IMAGE_FILE_SYSTEM = $1000; {System File} const IMAGE_FILE_DLL = $2000; {File is a DLL} const IMAGE_FILE_BYTES_REVERSED_HI = $8000; {machine word bytes rev} const IMAGE_FILE_MACHINE_UNKNOWN = $0; const IMAGE_FILE_MACHINE_I386 = $14c; {Intel 386} const IMAGE_FILE_MACHINE_R3000B = $160; {MIPS big-endian} const IMAGE_FILE_MACHINE_R3000L = $162; {MIPS little-endian} const IMAGE_FILE_MACHINE_R4000 = $166; {MIPS little-endian} const IMAGE_FILE_MACHINE_R10000 = $168; {MIPS little-endian} const IMAGE_FILE_MACHINE_ALPHA = $184; {Alpha_AXP} const IMAGE_FILE_MACHINE_POWERPC = $1F0; {IBM PowerPC Little-Endian} const IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16; {$IFNDEF WIN32} type DWORD = LongInt; {$ENDIF} type PIMAGE_DOS_HEADER = ^TIMAGE_DOS_HEADER; TIMAGE_DOS_HEADER = {$IFDEF WIN32} packed {$ENDIF} record e_magic : word; {Magic number} e_cblp : word; {Bytes on last page of file} e_cp : word; {Pages in file} e_crlc : word; {Relocations} e_cparhdr : word; {Size of header in paragraphs} e_minalloc : word; {Minimum extra paragraphs needed} e_maxalloc : word; {Maximum extra paragraphs needed} e_ss : word; {Initial (relative) SS value} e_sp : word; {Initial SP value} e_csum : word; {Checksum} e_ip : word; {Initial IP value} e_cs : word; {Initial (relative) CS value} e_lfarlc : word; {File address of relocation table} e_ovno : word; {Overlay number} e_res : array[0..3] of word; {Reserved words} e_oemid : word; {OEM identifier (for e_oeminfo)} e_oeminfo : word; {OEM information; e_oemid specific} e_res2 : array[0..9] of word; {Reserved words} e_lfanew : longint; {File address of new exe header} end; type PIMAGE_OS2_HEADER = ^TIMAGE_OS2_HEADER; TIMAGE_OS2_HEADER = {$IFDEF WIN32} packed {$ENDIF} record ne_magic : word; {Magic number} ne_ver : char; {Version number} ne_rev : char; {Revision number} ne_enttab : word; {Offset of Entry Table} ne_cbenttab : word; {Number of bytes in Entry Table} ne_crc : longint; {Checksum of whole file} ne_flags : word; {Flag word} ne_autodata : word; {Automatic data segment number} ne_heap : word; {Initial heap allocation} ne_stack : word; {Initial stack allocation} ne_csip : longint; {Initial CS:IP setting} ne_sssp : longint; {Initial SS:SP setting} ne_cseg : word; {Count of file segments} ne_cmod : word; {Entries in Module Reference Table} ne_cbnrestab : word; {Size of non-resident name table} ne_segtab : word; {Offset of Segment Table} ne_rsrctab : word; {Offset of Resource Table} ne_restab : word; {Offset of resident name table} ne_modtab : word; {Offset of Module Reference Table} ne_imptab : word; {Offset of Imported Names Table} ne_nrestab : word; {Offset of Non-resident Names Table} ne_cmovent : word; {Count of movable entries} ne_align : word; {Segment alignment shift count} ne_cres : word; {Count of resource segments} ne_exetyp : word; {Target Operating system} ne_flagsothers : word; {Other .EXE flags} ne_pretthunks : word; {offset to return thunks} ne_psegrefbytes : word; {offset to segment ref. bytes} ne_swaparea : word; {Minimum code swap area size} ne_expver : word; {Expected Windows version number} end; type PIMAGE_VXD_HEADER = ^TIMAGE_VXD_HEADER; TIMAGE_VXD_HEADER = {$IFDEF WIN32} packed {$ENDIF} record e32_magic : word; {Magic number} e32_border : byte; {The byte ordering for the VXD} e32_worder : byte; {The word ordering for the VXD} e32_level : DWORD; {The EXE format level for now = 0} e32_cpu : word; {The CPU type} e32_os : word; {The OS type} e32_ver : DWORD; {Module version} e32_mflags : DWORD; {Module flags} e32_mpages : DWORD; {Module # pages} e32_startobj : DWORD; {Object # for instruction pointer} e32_eip : DWORD; {Extended instruction pointer} e32_stackobj : DWORD; {Object # for stack pointer} e32_esp : DWORD; {Extended stack pointer} e32_pagesize : DWORD; {VXD page size} e32_lastpagesize : DWORD; {Last page size in VXD} e32_fixupsize : DWORD; {Fixup section size} e32_fixupsum : DWORD; {Fixup section checksum} e32_ldrsize : DWORD; {Loader section size} e32_ldrsum : DWORD; {Loader section checksum} e32_objtab : DWORD; {Object table offset} e32_objcnt : DWORD; {Number of objects in module} e32_objmap : DWORD; {Object page map offset} e32_itermap : DWORD; {Object iterated data map offset} e32_rsrctab : DWORD; {Offset of Resource Table} e32_rsrccnt : DWORD; {Number of resource entries} e32_restab : DWORD; {Offset of resident name table} e32_enttab : DWORD; {Offset of Entry Table} e32_dirtab : DWORD; {Offset of Module Directive Table} e32_dircnt : DWORD; {Number of module directives} e32_fpagetab : DWORD; {Offset of Fixup Page Table} e32_frectab : DWORD; {Offset of Fixup Record Table} e32_impmod : DWORD; {Offset of Import Module Name Table} e32_impmodcnt : DWORD; {NumEntries in Import Module Name Table} e32_impproc : DWORD; {Offset of Import Procedure Name Table} e32_pagesum : DWORD; {Offset of Per-Page Checksum Table} e32_datapage : DWORD; {Offset of Enumerated Data Pages} e32_preload : DWORD; {Number of preload pages} e32_nrestab : DWORD; {Offset of Non-resident Names Table} e32_cbnrestab : DWORD; {Size of Non-resident Name Table} e32_nressum : DWORD; {Non-resident Name Table Checksum} e32_autodata : DWORD; {Object # for automatic data object} e32_debuginfo : DWORD; {Offset of the debugging information} e32_debuglen : DWORD; {length of the debugging info. in bytes} e32_instpreload : DWORD; {# of instance pages in preload section} e32_instdemand : DWORD; {# of inst pages in demand load section} e32_heapsize : DWORD; {Size of heap — for 16-bit apps} e32_res3 : array[0..11] of byte; {Reserved words} e32_winresoff : DWORD; e32_winreslen : DWORD; e32_devid : word; {Device ID for VxD} e32_ddkver : word; {DDK version for VxD} end; type PIMAGE_DATA_DIRECTORY = ^TIMAGE_DATA_DIRECTORY; TIMAGE_DATA_DIRECTORY = {$IFDEF WIN32} packed {$ENDIF} record VirtualAddress : DWORD; Size : DWORD; end; type PIMAGE_FILE_HEADER = ^TIMAGE_FILE_HEADER; TIMAGE_FILE_HEADER = {$IFDEF WIN32} packed {$ENDIF} record Machine : word; NumberOfSections : word; TimeDateStamp : DWORD; PointerToSymbolTable : DWORD; NumberOfSymbols : DWORD; SizeOfOptionalHeader : word; Characteristics : word; end; type PIMAGE_OPTIONAL_HEADER = ^TIMAGE_OPTIONAL_HEADER; TIMAGE_OPTIONAL_HEADER = {$IFDEF WIN32} packed {$ENDIF} record Magic : word; MajorLinkerVersion : byte; MinorLinkerVersion : byte; SizeOfCode : DWORD; SizeOfInitializedData : DWORD; SizeOfUninitializedData : DWORD; AddressOfEntryPoint : DWORD; BaseOfCode : DWORD; BaseOfData : DWORD; {NT additional fields} ImageBase : DWORD; SectionAlignment : DWORD; FileAlignment : DWORD; MajorOperatingSystemVersion : word; MinorOperatingSystemVersion : word; MajorImageVersion : word; MinorImageVersion : word; MajorSubsystemVersion : word; MinorSubsystemVersion : word; Reserved1 : DWORD; SizeOfImage : DWORD; SizeOfHeaders : DWORD; CheckSum : DWORD; Subsystem : word; DllCharacteristics : word; SizeOfStackReserve : DWORD; SizeOfStackCommit : DWORD; SizeOfHeapReserve : DWORD; SizeOfHeapCommit : DWORD; LoaderFlags : DWORD; NumberOfRvaAndSizes : DWORD; DataDirectory : array [0..IMAGE_NUMBEROF_DIRECTORY_ENTRIES — 1] of TIMAGE_DATA_DIRECTORY; end; type PIMAGE_ROM_OPTIONAL_HEADER = ^TIMAGE_ROM_OPTIONAL_HEADER; TIMAGE_ROM_OPTIONAL_HEADER = {$IFDEF WIN32} packed {$ENDIF} record Magic : word; MajorLinkerVersion : byte; MinorLinkerVersion : byte; SizeOfCode : DWORD; SizeOfInitializedData : DWORD; SizeOfUninitializedData : DWORD; AddressOfEntryPoint : DWORD; BaseOfCode : DWORD; BaseOfData : DWORD; BaseOfBss : DWORD; GprMask : DWORD; CprMask : array[0..3] of DWORD; GpValue : DWORD; end; {$IFNDEF WIN32} function MakeWord(A, B: Byte): Word; begin Result := A or B shl 8; end; {$ENDIF} procedure TForm1.FormCreate(Sender: TObject); begin Label1.Caption := ''; Button1.Caption := 'GetExeHdr'; Button2.Caption := 'MarkExe'; Button2.Enabled := false; SpinEdit1.MinValue := 0; SpinEdit1.MaxValue := 255; SpinEdit1.Value := 0; OpenDialog1.Options := [ofNoChangeDir, ofPathMustExist, ofFileMustExist]; OpenDialog1.Filter := 'All Files (*.*)|*.*'; Exetype := IMAGE_UNKNOWN_SIGNATURE; end; procedure TForm1.GetExeHeader(FileName : string); var f : file; image_dos_header : TIMAGE_DOS_HEADER; image_file_header : TIMAGE_FILE_HEADER; image_optional_header : TIMAGE_OPTIONAL_HEADER; Image_OS2_Header : TIMAGE_OS2_HEADER; Image_VXD_Header : TIMAGE_VXD_HEADER; Signature : word; w : word; begin Exetype := IMAGE_UNKNOWN_SIGNATURE; Label1.Caption := ''; AssignFile(f, FileName); Reset(f, 1); while Pos('\', FileName)> 0 do Delete(FileName, 1, Pos('\', FileName)); if FileSize(f) < sizeof(image_dos_header) then begin Label1.Caption := FileName + ' is an unknown file type'; CloseFile(f); Exit; end; BlockRead(f, Signature, sizeof(Signature)); if Signature <> IMAGE_DOS_SIGNATURE then begin Label1.Caption := FileName + ' is an unknown file type'; CloseFile(f); Exit; end; Label1.Caption := FileName + ' has the DOS executable header'; Seek(f, $18); BlockRead(f, w, sizeof(w)); if w <> $40 then begin CloseFile(f); Exit; end; Seek(f, 0); BlockRead(f, image_dos_header, sizeof(image_dos_header)); if FileSize(f) < (image_dos_header.e_lfanew + 2) then begin CloseFile(f); Exit; end; Seek(f, image_dos_header.e_lfanew); BlockRead(f, Signature, sizeof(Signature)); if Signature = IMAGE_OS2_SIGNATURE then begin if FileSize(f) < (image_dos_header.e_lfanew + sizeof(IMAGE_OS2_HEADER)) then begin CloseFile(f); Exit; end; Seek(f, FilePos(f) — 2); HdrOffset := FilePos(f); BlockRead(f, image_OS2_header, sizeof(image_OS2_header)); Exetype := IMAGE_OS2_SIGNATURE; if image_OS2_header.ne_flags and NE_IMAGE_DLL = NE_IMAGE_DLL then Label1.Caption := FileName + ' has the new executable DLL format' else Label1.Caption := FileName + ' has the new executable format'; SpinEdit1.Value := HiByte(image_OS2_header.ne_expver); SpinEdit2.Value := LoByte(image_OS2_header.ne_expver); end else if Signature = IMAGE_NT_SIGNATURE then begin if FileSize(f) < (image_dos_header.e_lfanew + sizeof(image_file_header) + sizeof(image_optional_header) + 2) then begin CloseFile(f); Exit; end; Seek(f, FilePos(f) + 2); BlockRead(f, image_file_header, sizeof(image_file_header)); HdrOffset := FilePos(f); BlockRead(f, image_optional_header, sizeof(image_optional_header)); Exetype := IMAGE_NT_SIGNATURE; if image_file_header.Characteristics and IMAGE_FILE_DLL = IMAGE_FILE_DLL then Label1.Caption := FileName + ' has the portable executable DLL format' else Label1.Caption := FileName + ' has the portable executable format'; SpinEdit1.Value := image_optional_header.MajorSubsystemVersion; SpinEdit2.Value := image_optional_header.MinorSubsystemVersion; end else if Signature = IMAGE_VXD_SIGNATURE then begin Seek(f, FilePos(f) — 2); HdrOffset := FilePos(f); BlockRead(f, image_VXD_header, sizeof(image_VXD_header)); Label1.Caption := FileName + ' has the VXD format'; Exetype := IMAGE_VXD_SIGNATURE; SpinEdit1.Value := LoByte(image_VXD_header.e32_os); SpinEdit2.Value := HiByte(image_VXD_header.e32_os); end; CloseFile(f); Button2.Enabled := true; end; procedure TForm1.RemarkExeHeader(FileName : string); var f : file; image_optional_header : TIMAGE_OPTIONAL_HEADER; Image_OS2_Header : TIMAGE_OS2_HEADER; Image_VXD_Header : TIMAGE_VXD_HEADER; begin AssignFile(f, FileName); Reset(f, 1); while Pos('\', FileName)> 0 do Delete(FileName, 1, Pos('\', FileName)); Seek(f, HdrOffset); case Exetype of IMAGE_OS2_SIGNATURE : begin BlockRead(f, image_OS2_header, sizeof(image_OS2_header)); image_OS2_header.ne_expver := MakeWord(Byte(SpinEdit2.Value), Byte(SpinEdit1.Value)); Seek(f, HdrOffset); BlockWrite(f, image_OS2_header, sizeof(image_OS2_header)); end; IMAGE_NT_SIGNATURE : begin HdrOffset := FilePos(f); BlockRead(f, image_optional_header, sizeof(image_optional_header)); image_optional_header.MajorSubsystemVersion := SpinEdit1.Value; image_optional_header.MinorSubsystemVersion := SpinEdit2.Value; Seek(f, HdrOffset); BlockWrite(f, image_optional_header, sizeof(image_optional_header)); end; IMAGE_VXD_SIGNATURE : begin BlockRead(f, image_VXD_header, sizeof(image_VXD_header)); image_VXD_header.e32_os := MakeWord(Byte(SpinEdit1.Value), Byte(SpinEdit2.Value)); Seek(f, HdrOffset); BlockWrite(f, image_VXD_header, sizeof(image_VXD_header)); end; end; CloseFile(f); ShowMessage(FileName + ' remarked'); end; procedure TForm1.Button1Click(Sender: TObject); begin if OpenDialog1.Execute then GetExeHeader(OpenDialog1.FileName); end; procedure TForm1.Button2Click(Sender: TObject); begin if Exetype <> IMAGE_UNKNOWN_SIGNATURE then ReMarkExeHeader(OpenDialog1.FileName); end; end. 4/2/99 11:28:24 AM
Last Modified: 01-SEP-99