Главная страница | назад





Article #16495: re-mark a Delphi 16-Bit executable to be a Windows 4.0 application

 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