9

How can I detect which version of Delphi was used to compile a pre-existing .exe file? I don't necessarily need to do this with code.

I tried PEiD but it cannot detect if version is newer than Delphi 7.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
Pol
  • 5,064
  • 4
  • 32
  • 51

3 Answers3

14

Download the various files for the IDR (Interactive Delphi Reconstructor), extract them to a folder and run it. Load your compiled executable inside IDR and it'll tell you what version the executable was compiled in (has proven accurate for me).

Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
LaKraven
  • 5,804
  • 2
  • 23
  • 49
  • Worked as expected. However D2010 support is not free and at the moment no support for newer versions that D2010. But my exe was compiled in D2007 :) – Pol Dec 03 '11 at 22:47
  • The D2010 file is in fact free. For some reason, the author wants you to e-mail him for the password (which I'd distribute here but that may be illegal). – LaKraven Dec 04 '11 at 00:22
  • 3
    I am able to load exe using IDR but where I can get delphi version information? – Nalu Aug 21 '12 at 19:26
9

Alternative solution:

Open your compiled executable in XN Resource Editor. Under "RC Data" in the tree you will find DVCLAL. Expand it and select "Language Neutral". You will now see a nonsense string with an equally meaningless HEX equivalent.

This HEX actually identifies the compiler version on which the executable was built. You can Google this HEX with the word Delphi, and you should be quite quickly able to determine what compiler the HEX came from.

Enjoy!

LaKraven
  • 5,804
  • 2
  • 23
  • 49
  • 3
    Each solution is different, as opposed to a variation of the same. It's my understanding that the idea is to submit one answer for each entirely different solution. Is this not the case? – LaKraven Dec 03 '11 at 16:54
  • Fair enough. I just presumed that was the "standard practise". Probably ascribing my own love of neat separation onto others. – LaKraven Dec 03 '11 at 17:08
  • 4
    @David: [The official word on multiple answers](http://meta.stackexchange.com/questions/25209/what-is-the-official-etiquette-on-answering-a-question-twice). Let the community vote up the better off the two. – afrazier Dec 03 '11 at 17:15
  • @afrazier did you read Jeff's comments at that meta question? – David Heffernan Dec 03 '11 at 17:18
  • David: Yes, but I think he's ignoring the fact that many tunes there's really more than one right answer to a question. That the community appears to disagree with Jeff is telling. – afrazier Dec 03 '11 at 17:20
  • @David, Atwood's comment doesn't really give a reason, and his answer is just nonsense — as though it makes a difference whether two different answers come from the same person or different people. In the occasions where I've answered multiple times (e.g., [1](http://stackoverflow.com/q/2362844/33732), [2](http://stackoverflow.com/q/1519391/33732), [3](http://stackoverflow.com/q/301246/33732)), editing to add my second answer would not have amplified my first answer; it would have muddied it with conflicting advice. – Rob Kennedy Dec 03 '11 at 20:57
  • 3
    I think this string only identifies edition of Delphi (like Enterprise, Professional), not its version. For example it's the same for XE2 Professional and 2007 Professional. Maybe for older versions (like D3) it was useful to distinguish version. – Pol Dec 03 '11 at 21:00
  • I was not aware of this! The applications I was using this method to check Delphi version were circa Delphi 3-7... where the hex was unique for each version (not just the edition). – LaKraven Dec 03 '11 at 21:27
  • @LaKraven, AFAIK DVCLAL is an edition marker, not a version. Separation of such answers is ok. – OnTheFly Dec 04 '11 at 18:12
  • @LaKraven are the comments correct? Is this answer in fact incorrect? – David Heffernan Dec 04 '11 at 19:41
  • @DavidHeffernan I'm Googling to see if I can find more info to enhance this answer. I do certainly know this worked up to and including Delphi 7... beyond that, I've never tried it myself! – LaKraven Dec 04 '11 at 22:19
  • DVCLAL only identifies the edition. There are only 3 possible values. More detail here: https://stackoverflow.com/a/45023723/488539 – Daniel Marschall Nov 06 '22 at 18:06
6

You can use this source to give you the Assembly information. In the main assembly, you can see the Name and Version, which will give you the Compiler's name and version. It is a console application where param 1 is the exe file.

The version can be checked here:

http://wiki.delphi-jedi.org/wiki/JEDI_Help:CompilerVersions

to see which version it was vbuild with; i.e.,

12.xxxx - Delphi 2009 and 15.xxxx - Delphi XE

However, this only works up to XE. With XE2, things have changed in the exe.

 program versionchk;

    {$APPTYPE CONSOLE}

    uses
      ActiveX,
      Classes,
      Windows,
      Variants,
      ComObj,
      StrUtils,
      SysUtils;

    type
      TAssemblyIdentity=record
        &type : string;
        name    : string;
        language: string;
        processorArchitecture   : string;
        version : string;
        publicKeyToken: string;
      end;

      TRequestedExecutionLevel=record
        level    : string;
        uiAccess : string;
      end;

      TManifiestReader=class
      private
        FFileName: string;
        FManifest: AnsiString;
        FMainAssemblyIdentity: TAssemblyIdentity;
        FHasManifest: Boolean;
        FDependentAssembly: TAssemblyIdentity;
        FManifestVersion: string;
        FRequestedExecutionLevel: TRequestedExecutionLevel;
        procedure GetManifest;
        procedure LoadManifestData;
        function  VarNullToStr(Value:OleVariant):string;
      public
        property FileName : string read FFileName;
        property Manifest : AnsiString read FManifest;
        property ManifestVersion : string read FManifestVersion;
        property MainAssemblyIdentity : TAssemblyIdentity read FMainAssemblyIdentity;
        property DependentAssembly : TAssemblyIdentity read FDependentAssembly;
        property HasManifest : Boolean read FHasManifest;
        property RequestedExecutionLevel : TRequestedExecutionLevel read FRequestedExecutionLevel;
        constructor Create(const AFileName:string);
      end;

    { TReadManifiest }

    constructor TManifiestReader.Create(const AFileName: string);
    begin
      FFileName:=AFileName;
      FHasManifest:=False;
      GetManifest;
      LoadManifestData;
    end;

    procedure TManifiestReader.GetManifest;
    var
      hModule  : THandle;
      Resource : TResourceStream;
    begin
      FManifest:='';
      hModule:=LoadLibraryEx(PChar(FileName),0,LOAD_LIBRARY_AS_DATAFILE);
      try
         if hModule=0 then RaiseLastOSError;
         if FindResource(hModule, MakeIntResource(1), RT_MANIFEST)<>0 then
         begin
           Resource:=TResourceStream.CreateFromID(hModule,1,RT_MANIFEST);
           try
             SetString(FManifest, PAnsiChar(Resource.Memory),Resource.Size);
             FHasManifest:=True;
           finally
             Resource.Free;
           end;
         end;
      finally
          FreeLibrary(hModule);
      end;
    end;

    procedure TManifiestReader.LoadManifestData;
    const
     assembly_namespace_V1='urn:schemas-microsoft-com:asm.v1';
     assembly_namespace_V2='urn:schemas-microsoft-com:asm.v2';
     assembly_namespace_V3='urn:schemas-microsoft-com:asm.v3';
    var
      XmlDoc : OleVariant;
      ns     : string;
      Node   : OleVariant;
    begin
      if Trim(FManifest)='' then exit;
      XmlDoc       := CreateOleObject('Msxml2.DOMDocument.6.0');
      XmlDoc.Async := False;
      try
        XmlDoc.LoadXML(FManifest);
        XmlDoc.SetProperty('SelectionLanguage','XPath');

        if (XmlDoc.parseError.errorCode <> 0) then
         raise Exception.CreateFmt('Error in Xml Data %s',[XmlDoc.parseError]);

        //set the namespaces alias
        ns := Format('xmlns:a=%s xmlns:b=%s xmlns:c=%s',[QuotedStr(assembly_namespace_V1),QuotedStr(assembly_namespace_V2),QuotedStr(assembly_namespace_V3)]);
        XmlDoc.setProperty('SelectionNamespaces', ns);

        //get the version of the manifest
        Node:=XmlDoc.selectSingleNode('/a:assembly/@manifestVersion');
        if not VarIsNull(Node) and not VarIsClear(Node) then
        FManifestVersion:=Node.text;

        Node:=XmlDoc.selectSingleNode('/a:assembly/a:assemblyIdentity');
        if not VarIsNull(Node) and not VarIsClear(Node) then
        begin
          FMainAssemblyIdentity.&type   :=Node.getAttribute('type');
          FMainAssemblyIdentity.name    :=Node.getAttribute('name');
          FMainAssemblyIdentity.language:=VarNullToStr(Node.getAttribute('language'));
          FMainAssemblyIdentity.version :=Node.getAttribute('version');
          FMainAssemblyIdentity.processorArchitecture:=VarNullToStr(Node.getAttribute('processorArchitecture'));
          FMainAssemblyIdentity.publicKeyToken       :=VarNullToStr(Node.getAttribute('publicKeyToken'));
        end;

        Node:=XmlDoc.selectSingleNode('/a:assembly/a:dependency/a:dependentAssembly/a:assemblyIdentity');
        if not VarIsNull(Node) and not VarIsClear(Node) then
        begin
          FDependentAssembly.&type   :=Node.getAttribute('type');
          FDependentAssembly.name    :=Node.getAttribute('name');
          FDependentAssembly.language:=VarNullToStr(Node.getAttribute('language'));
          FDependentAssembly.version :=Node.getAttribute('version');
          FDependentAssembly.processorArchitecture:=VarNullToStr(Node.getAttribute('processorArchitecture'));
          FDependentAssembly.publicKeyToken       :=VarNullToStr(Node.getAttribute('publicKeyToken'));
        end;

        Node:=XmlDoc.selectSingleNode('/a:assembly/b:trustInfo/b:security/b:requestedPrivileges/b:requestedExecutionLevel');
        if VarIsNull(Node) or VarIsClear(Node) then
          Node:=XmlDoc.selectSingleNode('/a:assembly/c:trustInfo/c:security/c:requestedPrivileges/c:requestedExecutionLevel');
        if not VarIsNull(Node) and not VarIsClear(Node) then
        begin
          FRequestedExecutionLevel.level   :=Node.getAttribute('level');
          FRequestedExecutionLevel.uiAccess:=VarNullToStr(Node.getAttribute('uiAccess'));
        end;

      finally
        XmlDoc:=Unassigned;
      end;
    end;

    function TManifiestReader.VarNullToStr(Value: OleVariant): string;
    begin
      if VarIsNull(Value) then
        Result:=''
      else
        Result:=VarToStr(Value);
    end;

    Var
      ManifestReader : TManifiestReader;
    begin
     try
        CoInitialize(nil);
        try
          ManifestReader:=TManifiestReader.Create(ParamStr(1));
          try
            //Writeln(ManifestReader.Manifest);

            Writeln('Manifest version '+ManifestReader.ManifestVersion);
            Writeln('Main Assembly Identity');
            Writeln('----------------------');
            Writeln('type     '+ManifestReader.MainAssemblyIdentity.&type);
            Writeln('name     '+ManifestReader.MainAssemblyIdentity.name);
            Writeln('language '+ManifestReader.MainAssemblyIdentity.language);
            Writeln('version  '+ManifestReader.MainAssemblyIdentity.version);
            Writeln('processorArchitecture '+ManifestReader.MainAssemblyIdentity.processorArchitecture);
            Writeln('publicKeyToken        '+ManifestReader.MainAssemblyIdentity.publicKeyToken);
            Writeln('');

            Writeln('Dependent Assembly Identity');
            Writeln('---------------------------');
            Writeln('type     '+ManifestReader.DependentAssembly.&type);
            Writeln('name     '+ManifestReader.DependentAssembly.name);
            Writeln('language '+ManifestReader.DependentAssembly.language);
            Writeln('version  '+ManifestReader.DependentAssembly.version);
            Writeln('processorArchitecture '+ManifestReader.DependentAssembly.processorArchitecture);
            Writeln('publicKeyToken        '+ManifestReader.DependentAssembly.publicKeyToken);
            Writeln('');

            Writeln('Requested Execution Level');
            Writeln('---------------------------');
            Writeln('level     '+ManifestReader.RequestedExecutionLevel.level);
            Writeln('uiAccess  '+ManifestReader.RequestedExecutionLevel.uiAccess);

          finally
            ManifestReader.Free;
          end;
        finally
          CoUninitialize;
        end;
     except
        on E:Exception do
            Writeln(E.Classname, ':', E.Message);
     end;
      Readln;
    end.
PeeHaa
  • 71,436
  • 58
  • 190
  • 262
  • Very useful piece of code! However works only when manifest xml was not changed in a way that compiler information was removed (that was in my case). Anyway, thanks for this! – Pol Dec 04 '11 at 09:46
  • @MarioVermeulen, if the code is not yours or is found in an article you must credit to the original author. original article [How get and parse a manifest of an external application using delphi](http://theroadtodelphi.wordpress.com/2011/06/13/how-get-and-parse-a-manifest-of-an-external-application-using-delphi/) – RRUZ Mar 31 '12 at 04:03
  • What version of Delphi should this code be compiled with? Doesn't compile in D7 and with XE10.2 produces output with what looks like headers but no data. – Toby Mar 07 '19 at 23:38