3

Is there any way to access the build datetime of a dll from C#?

In between formal releases of a dll, I'd like to be able to somehow store the build time/date of a DLL so that I can check if the currently executing build is the one I'm expecting. I'm working on both Debug and Release versions of an Add-In and I can easily forget which one I'm currently using - especially across a few development, test and pre-production machines. I want to be able to go to my "About" box and display it there.

TIA, Paolo

PaoloFCantoni
  • 967
  • 2
  • 12
  • 26

4 Answers4

3

You can use TimeDateStamp field of the IMAGE_FILE_HEADER which represent the date and time the image was created by the linker. The value is useful because it is independent from the date/time of the file in the file system.

The corresponding .NET 4.0 code can be about the following:

using System;
using System.IO.MemoryMappedFiles;
using System.Runtime.InteropServices;
using System.IO;

namespace GetPeLinkTime {
    class Program {
        static void Main (string[] args) {
            if (args.Length != 1) {
                Console.WriteLine ("Usage:" + Environment.NewLine + "    GetPeLinkTime ExePathToExamine");
                return;
            }
            string filePath = args[0];

            FileStream file = File.OpenRead (filePath);
            using (var mmf = MemoryMappedFile.CreateFromFile (file, null, 
                                                              file.Length,
                                                              MemoryMappedFileAccess.Read,
                                                              null, HandleInheritability.None, false)) {
                NativeMethods.IMAGE_DOS_HEADER dosHeader = new NativeMethods.IMAGE_DOS_HEADER ();
                using (var accessor = mmf.CreateViewAccessor (0,
                                                              Marshal.SizeOf (dosHeader),
                                                              MemoryMappedFileAccess.Read)) {
                    accessor.Read<NativeMethods.IMAGE_DOS_HEADER>(0, out dosHeader);
                    if (dosHeader.e_magic != NativeMethods.IMAGE_DOS_SIGNATURE) {
                        Console.WriteLine ("The input file is not an Executable file.");
                        return;
                    }
                }
                int signature = 0;
                NativeMethods.IMAGE_FILE_HEADER imageFileHeader = new NativeMethods.IMAGE_FILE_HEADER();
                using (var accessor = mmf.CreateViewAccessor (dosHeader.e_lfanew,
                                                              Marshal.SizeOf (signature) + Marshal.SizeOf (imageFileHeader),
                                                              MemoryMappedFileAccess.Read)) {
                    signature = accessor.ReadInt32 (0);
                    if (signature != NativeMethods.IMAGE_NT_SIGNATURE) {
                        Console.WriteLine ("The input file is not a Program Executable file.");
                        return;
                    }
                    accessor.Read<NativeMethods.IMAGE_FILE_HEADER> (Marshal.SizeOf (signature), out imageFileHeader);
                }
                // convert a Unix timestamp to DateTime
                DateTime origin = new DateTime(1970, 1, 1, 0, 0, 0, 0);
                TimeSpan localOffset = TimeZone.CurrentTimeZone.GetUtcOffset (origin);
                DateTime originUTC = origin.AddHours(localOffset.Hours);
                DateTime linkTime = originUTC.AddSeconds ((double)imageFileHeader.TimeDateStamp);
                Console.WriteLine ("Link time of the file '{0}' is: {1}", filePath, linkTime);
            }
        }
    }
    internal static class NativeMethods {
        internal const int IMAGE_DOS_SIGNATURE = 0x5A4D;    // MZ
        internal const int IMAGE_NT_SIGNATURE = 0x00004550; // PE00

        [StructLayout (LayoutKind.Sequential)]
        internal struct IMAGE_DOS_HEADER {  // DOS .EXE header
            internal short e_magic;         // Magic number
            internal short e_cblp;          // Bytes on last page of file
            internal short e_cp;            // Pages in file
            internal short e_crlc;          // Relocations
            internal short e_cparhdr;       // Size of header in paragraphs
            internal short e_minalloc;      // Minimum extra paragraphs needed
            internal short e_maxalloc;      // Maximum extra paragraphs needed
            internal short e_ss;            // Initial (relative) SS value
            internal short e_sp;            // Initial SP value
            internal short e_csum;          // Checksum
            internal short e_ip;            // Initial IP value
            internal short e_cs;            // Initial (relative) CS value
            internal short e_lfarlc;        // File address of relocation table
            internal short e_ovno;          // Overlay number
            internal short e_res1;          // Reserved words
            internal short e_res2;          // Reserved words
            internal short e_res3;          // Reserved words
            internal short e_res4;          // Reserved words
            internal short e_oemid;         // OEM identifier (for e_oeminfo)
            internal short e_oeminfo;       // OEM information; e_oemid specific
            internal short e_res20;         // Reserved words
            internal short e_res21;         // Reserved words
            internal short e_res22;         // Reserved words
            internal short e_res23;         // Reserved words
            internal short e_res24;         // Reserved words
            internal short e_res25;         // Reserved words
            internal short e_res26;         // Reserved words
            internal short e_res27;         // Reserved words
            internal short e_res28;         // Reserved words
            internal short e_res29;         // Reserved words
            internal int e_lfanew;          // File address of new exe header
        }
        [StructLayout (LayoutKind.Sequential)]
        internal struct IMAGE_FILE_HEADER {
            internal short Machine;
            internal short NumberOfSections;
            internal int TimeDateStamp;
            internal int PointerToSymbolTable;
            internal int NumberOfSymbols;
            internal short SizeOfOptionalHeader;
            internal short Characteristics;
        }
        //struct _IMAGE_NT_HEADERS {
        //    DWORD Signature;
        //    IMAGE_FILE_HEADER FileHeader;
        //    IMAGE_OPTIONAL_HEADER32 OptionalHeader;
        //}
        //struct _IMAGE_NT_HEADERS64 {
        //    DWORD Signature;
        //    IMAGE_FILE_HEADER FileHeader;
        //    IMAGE_OPTIONAL_HEADER64 OptionalHeader;
        //}
    }
}
Oleg
  • 220,925
  • 34
  • 403
  • 798
2

You can use the FileInfo class.

FileInfo file = new FileInfo(pathToDll);
DateTime dllCreateTime = file.CreationTime;
Jan Sverre
  • 4,617
  • 1
  • 22
  • 28
  • As I understand it, Jannis, this is the creation time of the file in the file system. That can vary as I move the file between machines. I need a value that is INTERNAL to the file - such as Oleg has mentioned. – PaoloFCantoni Nov 29 '10 at 01:04
1

Any reason you don't want to use the last bit of the assembly version number? You can access that through the Version.Revision property. It's a pretty simple way to identify a build, IMO.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Yes, Jon. I need this to independent of any external process. As Oleg points out in his answer the value I'm after is placed there by the linker itself. – PaoloFCantoni Nov 29 '10 at 01:03
0

Something similar here, using the Assembly info description

Community
  • 1
  • 1
Pero P.
  • 25,813
  • 9
  • 61
  • 85