[C#] Lấy Install Date của ứng dụng

Standard

Bài viết này bắt đầu từ bước đã xác định được khóa Registry của ứng dụng.

Thông thường, ngày cài đặt được lưu ở biến InstallDate, như hình sau:

31-1-2012 23-37-37

Thứ tự là [Năm][Tháng][Ngày]:

31-1-2012 23-39-04

Tuy nhiên, có những trường hợp, biến InstallDate không tồn tại

31-1-2012 23-40-14

Nhưng các trình quản lý vẫn xác định được (rõ là dị =.=):

31-1-2012 23-41-22

Vì sao – Khởi My, Hoàng Rapper

Ngẫm nghĩ một hồi, mơ hồ về cái gọi là CreatedDate, LastModifiedDate, nhưng mà chuột phải vô cái khóa Registry thì lại chẳng thấy có cái dòng Properties đâu cả Confused smile:

31-1-2012 23-44-18

Bắt đầu bối rối… Nhìn đi nhìn lại, cái Bejeweled 3 đó nó chỉ có 3 biến, dựa vào 3 biến đó làm sao để tìm được InstallDate bây giờ?

Chỉ còn cái UninstallString là đáng để bận tâm thôi. Chợt thông minh lạ kỳ, nghĩ rằng có khi nó lại lấy cái CreatedDate của cái file uninstall ấy chứ Winking smile

31-1-2012 23-47-19

Quá phũ, không có cái nào là 30/01/2012 cả X_X


Một đêm khó ngủ, nhận ra một cái gì đó, sáng trước khi đi học lọ mọ mở máy, đổi cái dòng UninstallString qua “cmd”, để xem nó có thay đổi gì không:

31-1-2012 23-50-08

Và:

31-1-2012 23-50-48

Coi lại ngày của hệ thống:

Dường như ta đã – Mỹ Tâm

Vậy thì gần như chắc chắn rằng nó lấy cái LastModifiedDate rồi Open-mouthed smile

Nhiệm vụ của bài viết đến đây là đủ, về code thì có thể minh họa như sau (lưu ý rằng vì mình lười lắm, nên mấy cái khai báo API mình đều lượm trên PInvoke.net nhé Smile)

[DllImport("advapi32.dll", EntryPoint = "RegQueryInfoKey", CallingConvention = CallingConvention.Winapi, SetLastError = true)]
extern private static int RegQueryInfoKey(
    IntPtr hkey,
    out StringBuilder lpClass,
    ref uint lpcbClass,
    IntPtr lpReserved,
    out uint lpcSubKeys,
    out uint lpcbMaxSubKeyLen,
    out uint lpcbMaxClassLen,
    out uint lpcValues,
    out uint lpcbMaxValueNameLen,
    out uint lpcbMaxValueLen,
    out uint lpcbSecurityDescriptor,
    out System.Runtime.InteropServices.ComTypes.FILETIME lpftLastWriteTime);

[DllImport("kernel32.dll", CallingConvention = CallingConvention.Winapi, SetLastError = true)]
static extern bool FileTimeToSystemTime(
    [In] ref System.Runtime.InteropServices.ComTypes.FILETIME lpFileTime,
    out SYSTEMTIME lpSystemTime);

[StructLayout(LayoutKind.Sequential)]
private struct SYSTEMTIME
{
    [MarshalAs(UnmanagedType.U2)]
    public short Year;
    [MarshalAs(UnmanagedType.U2)]
    public short Month;
    [MarshalAs(UnmanagedType.U2)]
    public short DayOfWeek;
    [MarshalAs(UnmanagedType.U2)]
    public short Day;
    [MarshalAs(UnmanagedType.U2)]
    public short Hour;
    [MarshalAs(UnmanagedType.U2)]
    public short Minute;
    [MarshalAs(UnmanagedType.U2)]
    public short Second;
    [MarshalAs(UnmanagedType.U2)]
    public short Milliseconds;
}

public static RegistryKey OpenRegKey(string fullRegKey, bool needWritePermission)
{
    string mainKey = fullRegKey.Substring(0, fullRegKey.IndexOf("\\"));
    string subKey = fullRegKey.Substring(fullRegKey.IndexOf("\\") + 1);
    try
    {
        if (mainKey == "HKEY_CURRENT_USER")
        {
            return Registry.CurrentUser.OpenSubKey(subKey, needWritePermission);
        }
        else
        {
            return Registry.LocalMachine.OpenSubKey(subKey, needWritePermission);
        }
    }
    catch
    {
        return null;
    }
}

///
<summary> /// Get a pointer to a registry key.
/// Credit to: http://dotnetgalactics.wordpress.com/2010/05/10/accessing-64-bit-registry-from-a-32-bit-process/
/// </summary>
///Registry key to obtain the pointer of.
/// Pointer to the given registry key.
private static IntPtr GetRegistryKeyHandle(RegistryKey registryKey)
{
    //Get the type of the RegistryKey
    Type registryKeyType = typeof(RegistryKey);
    //Get the FieldInfo of the 'hkey' member of RegistryKey
    System.Reflection.FieldInfo fieldInfo =
    registryKeyType.GetField("hkey", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);

    //Get the handle held by hkey
    SafeHandle handle = (SafeHandle)fieldInfo.GetValue(registryKey);
    //Get the unsafe handle
    IntPtr dangerousHandle = handle.DangerousGetHandle();
    return dangerousHandle;
}

public static DateTime GetInstallDateFromRegKey(string regKeyString)
{
    RegistryKey regKey = RegFunc.OpenRegKey(regKeyString, false);
    IntPtr hKey = GetRegistryKeyHandle(regKey);

    StringBuilder lpClass = new StringBuilder();
    uint lpcbClass = 0;
    IntPtr lpReserved = IntPtr.Zero;
    uint lpcSubKeys;
    uint lpcbMaxSubKeyLen;
    uint lpcbMaxClassLen;
    uint lpcValues;
    uint lpcbMaxValueNameLen;
    uint lpcbMaxValueLen;
    uint lpcbSecurityDescriptor;
    System.Runtime.InteropServices.ComTypes.FILETIME ft = new System.Runtime.InteropServices.ComTypes.FILETIME();
    RegQueryInfoKey(hKey, out lpClass, ref lpcbClass, lpReserved, out lpcSubKeys, out lpcbMaxSubKeyLen, out lpcbMaxClassLen, out lpcValues, out lpcbMaxValueNameLen, out lpcbMaxValueLen, out lpcbSecurityDescriptor, out ft);

    SYSTEMTIME st = new SYSTEMTIME();
    FileTimeToSystemTime(ref ft, out st);

    return new DateTime(st.Year, st.Month, st.Day);
}

Test thấy cũng ổn lắm, nếu có biến InstallDate thì lấy của biến đó, còn không thì xài cách trên, so sánh không thấy sai lệch với Programs and Features của Windows.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s