locked
How do I write a date/time into a REG_BINARY registry value?

    質問

  • I would like to write a date/time into a REG_BINARY value in the registry.  Specifically, I am trying to modify the Start time in Performance Monitor settings which are stored in the following key:
                    HKLM\SYSTEM\CurrentControlSet\Services\SysmonLog\Log Queries\{long ID number} in the 'Start' value.

    If I set the Start value through the Windows GUI to '2:00:00PM 2/27/2008' the registry value looks like this:
                    01,00,01,00,02,00,00,00,00,30,c0,0a,49,79,c8,01

    If I want to write a value in, how do I convert a date/time and then write it as the Start value?  I am using VB 2008.

    Thanks in advance for your help.

    Quay

    2008年12月2日 6:48

回答

  • Ok, I understand your requirement - so long as you understand the risks associated with modifing the registry directly.

    As to your problem...

    The date data would not be encoded as text - that's why the sample provided doesn't work.  I've been playing around with this a little bit -since there are too many bytes for just a date, it took a little figuring...

    I'm not sure what the first 8 bytes are - they appear to be flags that determine behavior about the start date.  I notice they are the same in both examples, so as long as you have used the GUI to set the start date as you want, you should be able to simply repeat the current first 8 bytes.

    The second 8 bytes contain the date value serialized in Windows FileTime format.  However, this date does appear to be set to UTC before being encoded.

    So, assuming you can hard code the start date flags, you can use code similar to the following to translate the binary string to and from a date (note the button.click code is just to give an example of the other methods):

        Private Sub Button1_Click(ByVal sender As System.ObjectByVal e As System.EventArgs) Handles Button1.Click  
            Dim dateString As String = "01,00,01,00,02,00,00,00,00,30,c0,0a,49,79,c8,01" 
     
            Dim actualDate As Date = Me.BinaryRegistryStringToDate(dateString)  
     
            MessageBox.Show(actualDate.ToString("MM/dd/yyyy hh:mm:ss"))  
            MessageBox.Show(Me.DateToBinaryRegistryString(actualDate))  
        End Sub 
     
        Private Function BinaryRegistryStringToDate(ByVal registryDateString As StringAs Date 
            Dim dateDataStrings() As String = registryDateString.Split(",")  
            Dim dateDataBytes() As Byte = Nothing 
            dateDataBytes = Array.ConvertAll(Of StringByte)(dateDataStrings, AddressOf StringToByte)  
     
            Dim targetDate As Date 
            targetDate = Date.FromFileTimeUtc(BitConverter.ToInt64(dateDataBytes, 8))  
     
            Return targetDate  
        End Function 
     
        Private Function DateToBinaryRegistryString(ByVal targetDate As DateAs String 
            Dim dateDataBytes As New List(Of Byte)  
            dateDataBytes.AddRange(New Byte() {1, 0, 1, 0, 2, 0, 0, 0})  
            dateDataBytes.AddRange(BitConverter.GetBytes(targetDate.ToFileTimeUtc))  
            Return String.Join(",", Array.ConvertAll(Of ByteString)(dateDataBytes.ToArray, AddressOf ByteToString))  
        End Function 
     
        Private Function StringToByte(ByVal target As StringAs Byte 
            Dim result As Byte 
            If Byte.TryParse(target, Globalization.NumberStyles.AllowHexSpecifier, Nothing, result) Then 
                Return result  
            Else 
                Throw New ArgumentException("Value of " & target & " cannot be converted to byte")  
            End If 
        End Function 
     
        Private Function ByteToString(ByVal target As ByteAs String 
            Return target.ToString("X2")  
        End Function 

    Note the key part of this is that the date portion is the last 8 bytes of data in the byte string and it is encoded using the System.Date.ToFileTimeUtc/FromFileTimeUtc methods.

    Hope that helps!
    Reed Kimble - "When you do things right, people won't be sure you've done anything at all"
    • 回答としてマーク Xingwei Hu 2008年12月8日 5:53
    2008年12月3日 17:35
    モデレータ

すべての返信

  • some code, might help you

     Dim myKey As Microsoft.Win32.RegistryKey = Microsoft.Win32.Registry.LocalMachine.OpenSubKey("System\CurrentControlSet\Services\SysmonLog\Log Queries\", True)
            Dim date1 As String = DateTime.Now().ToString
            Dim enc As New System.Text.UTF8Encoding()
            Dim byts() As Byte
            byts = enc.GetBytes(date1)
            myKey.SetValue("12121", byts, Microsoft.Win32.RegistryValueKind.Binary)

            Dim v As String = enc.GetString(myKey.GetValue("12121", Nothing))
            Dim dt As DateTime = DateTime.Parse(v)

            MessageBox.Show(v)

    Arjun Paudel
    2008年12月2日 8:47
  • Well, that definitely did allow me to put a date/time in as the start time.  Maybe there is more in the registry value than just the date and time.

    If I set the Start time to 5:41:42 PM on 12/2/2008 using the code above, the hex string looks like this:
                  
                    "Start"=hex:31,32,2f,32,2f,32,30,30,38,20,35,3a,34,31,3a,34,32,20,50,4d

    and in the Perfmon GUI, the log does not appear green (i.e. not scheduled to run at a future time).  If I open the dialog to edit the log settings properties, the time and date is initialized to the time and date when I started Perfmon.  This is usually what happens if the log was not already scheduled.

    If I set the same date/time using the GUI, the hex string looks quite a bit different:
     
                    "Start"=hex:01,00,01,00,02,00,00,00,00,8f,9c,3c,a5,54,c9,01

    I'm not sure what to make of it.
    2008年12月2日 9:49
  • There is probably a much better way to interface with perfmon than directly through the registry - really you don't ever want to modify a program through the registiry if there is any other way.

    I would expect WMI can be used for this purpose.  Come to think of it, you should be able to use System.Diagnositcs to access performance monitor information in a managed fashion...
    Reed Kimble - "When you do things right, people won't be sure you've done anything at all"
    2008年12月2日 19:20
    モデレータ
  • Yes, I can definitely collect the performance counter data using System.Diagnostics.  But I will be collecting data for about 60 counters from up to 100 machines over a period of days and I don't want to collect that data to one location.  I have good reason not to collect the data myself.  I want to let each of the machines collect their own data and I want to deal with the data files.

    Really its not complicated what I am trying to do.  I am just trying to deposit log settings in the registry and let Windows do the rest.  If I can put the start time in, the data will be collected automatically.
    2008年12月3日 0:13
  • Ok, I understand your requirement - so long as you understand the risks associated with modifing the registry directly.

    As to your problem...

    The date data would not be encoded as text - that's why the sample provided doesn't work.  I've been playing around with this a little bit -since there are too many bytes for just a date, it took a little figuring...

    I'm not sure what the first 8 bytes are - they appear to be flags that determine behavior about the start date.  I notice they are the same in both examples, so as long as you have used the GUI to set the start date as you want, you should be able to simply repeat the current first 8 bytes.

    The second 8 bytes contain the date value serialized in Windows FileTime format.  However, this date does appear to be set to UTC before being encoded.

    So, assuming you can hard code the start date flags, you can use code similar to the following to translate the binary string to and from a date (note the button.click code is just to give an example of the other methods):

        Private Sub Button1_Click(ByVal sender As System.ObjectByVal e As System.EventArgs) Handles Button1.Click  
            Dim dateString As String = "01,00,01,00,02,00,00,00,00,30,c0,0a,49,79,c8,01" 
     
            Dim actualDate As Date = Me.BinaryRegistryStringToDate(dateString)  
     
            MessageBox.Show(actualDate.ToString("MM/dd/yyyy hh:mm:ss"))  
            MessageBox.Show(Me.DateToBinaryRegistryString(actualDate))  
        End Sub 
     
        Private Function BinaryRegistryStringToDate(ByVal registryDateString As StringAs Date 
            Dim dateDataStrings() As String = registryDateString.Split(",")  
            Dim dateDataBytes() As Byte = Nothing 
            dateDataBytes = Array.ConvertAll(Of StringByte)(dateDataStrings, AddressOf StringToByte)  
     
            Dim targetDate As Date 
            targetDate = Date.FromFileTimeUtc(BitConverter.ToInt64(dateDataBytes, 8))  
     
            Return targetDate  
        End Function 
     
        Private Function DateToBinaryRegistryString(ByVal targetDate As DateAs String 
            Dim dateDataBytes As New List(Of Byte)  
            dateDataBytes.AddRange(New Byte() {1, 0, 1, 0, 2, 0, 0, 0})  
            dateDataBytes.AddRange(BitConverter.GetBytes(targetDate.ToFileTimeUtc))  
            Return String.Join(",", Array.ConvertAll(Of ByteString)(dateDataBytes.ToArray, AddressOf ByteToString))  
        End Function 
     
        Private Function StringToByte(ByVal target As StringAs Byte 
            Dim result As Byte 
            If Byte.TryParse(target, Globalization.NumberStyles.AllowHexSpecifier, Nothing, result) Then 
                Return result  
            Else 
                Throw New ArgumentException("Value of " & target & " cannot be converted to byte")  
            End If 
        End Function 
     
        Private Function ByteToString(ByVal target As ByteAs String 
            Return target.ToString("X2")  
        End Function 

    Note the key part of this is that the date portion is the last 8 bytes of data in the byte string and it is encoded using the System.Date.ToFileTimeUtc/FromFileTimeUtc methods.

    Hope that helps!
    Reed Kimble - "When you do things right, people won't be sure you've done anything at all"
    • 回答としてマーク Xingwei Hu 2008年12月8日 5:53
    2008年12月3日 17:35
    モデレータ
  • Oh, I should note that I did not bother to look at the perfmon gui for setting start time - you can most likely infer the purpose of the first 8 bytes by looking at the start time options in the gui and seeing how the values you set correspond to the values of those first 8 bytes.
    Reed Kimble - "When you do things right, people won't be sure you've done anything at all"
    2008年12月3日 17:39
    モデレータ