Jump to content
Xtreme .Net Talk

Determine which registry key is newer (RegEnumKeyEx?) [C# 2002]


Recommended Posts

Posted

I'm converting code from VC++6 to VC#2002 and have encountered my first roadblock with respect to using "RegEnumKeyEx" to compare the time stamps of registry

 

keys (when they were created to find the newest one).

 

Currently I do the following in VC++6 to determine which key is the newest:

  FILETIME ft, mrft; // mr = most recent
  mrft.dwLowDateTime = 0;
  mrft.dwHighDateTime = 0;

  while (ERROR_SUCCESS == (rc = RegEnumKeyEx( hkBase,
                                      Index++,
                                      Name,
                                      &Length,
                                      NULL,
                                      NULL,
                                      NULL,
                                      &ft))))
     {
     if (CompareFileTime (&mrft, &ft))
        {
        mrft.dwLowDateTime = ft.dwLowDateTime;
        mrft.dwHighDateTime = ft.dwHighDateTime;
        newestName = Name;
        }
  }
  ...
  ...

 

So my problem is how to accomplish this in C#?

Is there an alternative approach I should be using under the DotNet platform for C#? Or is there a way I can include "RegEnumKeyEx" somehow (like I would do a DECLARE with coredll.dll in VB or something) in the C# project so I can use the function as-is (continue the same way I was before)?

 

Any help would be greatly appreciated.

Thanks,

Posted (edited)

Platform invoke

 

The framework provides the Registry and RegistryKey classes in the Microsoft.Win32 namespace. I have never used these types myself, but they do not appear to expose key timestamps, so might not be useful in this case.

 

The alternative is to use platform invoke. The declare would look something like this:

 

[DllImport("Advapi32.dll", EntryPoint="RegEnumKeyExW")]
private static extern int RegEnumKeyEx(
   IntPtr hKey,
   int dwIndex,
   [MarshalAs(UnmanagedType.LPWStr)] StringBuilder lpName,
   ref int lpcName,
   int lpReserved,
   int lpClass,
   int lpcClass,
   [MarshalAs(UnmanagedType.LPStruct)] out FILETIME lpftLastWriteTime
);

 

To use, you would need to obtain the hKey to query, using some other platform invoke function. Then initialize a StringBuilder with enough space to receive the key name, which is passed in as the lpName parameter. Since your C++ code passes NULL for lpClass and lpcClass, I have declared them of type int so that you may pass 0, but if you wished to receive the class string the same types as lpName and lpcName could be used. The FILETIME structure is provided in the System.Runtime.InteropServices namespace.

 

I hope this gives you a good starting point.

 

Good luck :cool:

Edited by MrPaul
Never trouble another for what you can do for yourself.
Posted (edited)

That was a great help - and you also mentioned another problem I really had not thought about - the "hKey" - this also doesn't seem to exist in C#. You mention that I might be able to get this by some other platform invoke, correct me if I am wrong but do you mean doing another DLLImport on something like the "OpenSubKey" function (which is what I use to get my hkey in VC++6)?

 

OpenSubKey (HKEY_CURRENT_USER, 
                 "Software\\INFORMATION", 
                 &hkBase,
                 FALSE); 

 

And how would I create the "hkBase" variable if the concept of HKEY doesn't exist in C#? This is getting more complicated then I expected....

 

 

And why do you use "RegEnumKeyExW" and not simply "RegEnumKeyEx" as I did in VC++6 for the DLLImport Entry Point?

 

Thanks a lot...

Edited by Shaitan00
Posted

Platform invoke

 

In C++, the HKEY type is defined as an int, so you can use the int datatype in C#. In the code I posted above I used IntPtr since technically HKEY is a handle and not just any old number, and using IntPtr helps to keep this in mind.

 

The constants HKEY_CURRENT_USER etc are predefined HKEY values:

 

#define HKEY_CLASSES_ROOT          0x80000000
#define HKEY_CURRENT_USER          0x80000001
#define HKEY_LOCAL_MACHINE         0x80000002
#define HKEY_USERS                 0x80000003
#define HKEY_PERFORMANCE_DATA      0x80000004
#define HKEY_PERFORMANCE_TEXT      0x80000050
#define HKEY_PERFORMANCE_NLSTEXT   0x80000060
#define HKEY_CURRENT_CONFIG        0x80000005
#define HKEY_DYN_DATA              0x80000006

 

These definitions are easily converted to C#.

 

As for any other Win32 API functions, they will also need to be declared in C#:

 

[DllImport("Advapi32.dll", EntryPoint="RegOpenKeyExW")]
private static extern int RegOpenKeyEx(
   IntPtr hKey,
   [MarshalAs(UnmanagedType.LPWStr)] string lpSubKey,
   int ulOptions,
   int samDesired,
   out IntPtr phkResult
);

 

I've never heard of the OpenSubKey function.

 

Note that the majority of registry operations can be performed using the Microsoft.Win32 registry types, but as I said above I don't think they expose timestamps.

 

Good luck :)

Never trouble another for what you can do for yourself.
Posted

I added the DLLImports to the top and added the following code to my C# 2002 application:

 

int nLength = 256;
int nIndex = 0;
long rc = 0;
string sName, sMostRecentName;
FILETIME ft, ftMostRecent;
ftMostRecent.dwLowDateTime = 0;
ftMostRecent.dwHighDateTime = 0;

IntPtr nptrHKEY = new IntPtr(0x80000001); // For HKCU
IntPtr nptrResult = new IntPtr(0); // Am I doing this right?

if (0 == RegOpenKeyEx(nptrHKEY, "Software\\INFORMATION", 0, 0, nptrResult))
{
while (0 == (rc = RegEnumKeyEx(nptrHKEY, nIndex++, sName, nLength, 0, 0, 0, ft)))
	{
	//Do the compares of the FT times and all that here
	ftMostRecent.dwLowDateTime = ft.dwLowDateTime;
	ftMostRecent.dwHighDateTime = ft.dwHighDateTime;
	sMostRecentName = sName;
	}
}

 

This generates the following errors:

>> The best overloaded method match for 'RegOpenKeyEx(System.IntPtr, string, int, int, out System.IntPtr)' has some invalid arguments

>>Argument '5': cannot convert from 'System.IntPtr' to 'out System.IntPtr'

Now I could be way off but if I change the DLLImport and remove the word "out" from the "out IntPtr phkResult" it seems to compile fine (work or not I don't know - so I wanted to ask you ....). And is it ok the way I am declaring and using "nptrResult"?

 

 

And - why not post it all - "RegEnumKeyEx" generates the following errors:

>> The best overloaded method match for RegEnumKeyEx(System.IntPtr, int, System.Text.StringBuilder, ref int, int, int, int, System.Runtime.InteropServices.FILETIME)' has some invalid arguments

>> Argument '4': cannot convert from 'int' to 'ref int' // how do I solve this?

>> Argument '3': cannot convert from 'string' to 'System.Text.StringBuilder'

(which is just because I have never used a StringBuilder but I should be able to figure that out).

 

So I am still doing something wrong - anything stand out?

Thanks a lot for all the help - really appreciated...

Posted

I was testing out my IntPtr for HKEY_CURRENT_USER and it throws an exception...

 

IntPtr nptrHKEY = new IntPtr(0x80000001);

 

Exception thrown:

System.OverflowException: Arithmetic operation resulted in an overflow.

at System.IntPtr..ctor(Int64 value)

So I guess the HKCU value doesn't fit in the Int64 value ... How do you get around to solving this dilemna?

 

On top of what I asked above - any clues on this one?

Thanks again ...

Posted

Okay...

 

And why do you use "RegEnumKeyExW" and not simply "RegEnumKeyEx" as I did in VC++6 for the DLLImport Entry Point?

 

Most if not all Win32 API functions involving strings come in two flavours - A for ANSI and W for UNICODE. In C++ the headers will define RegEnumKeyEx as RegEnumKeyExA if the application is being compiled in ANSI mode, or RegEnumKeyExW if in UNICODE mode. With platform invoke you need to be explicit of both the exact name and the parameter types.

 

This generates the following errors:

>> The best overloaded method match for 'RegOpenKeyEx(System.IntPtr, string, int, int, out System.IntPtr)' has some invalid arguments

>>Argument '5': cannot convert from 'System.IntPtr' to 'out System.IntPtr'

 

Now I could be way off but if I change the DLLImport and remove the word "out" from the "out IntPtr phkResult" it seems to compile fine (work or not I don't know - so I wanted to ask you ....). And is it ok the way I am declaring and using "nptrResult"?

 

Yes it will compile but it is unlikely to execute correctly. The out modifier specifies that the function will return a value in this parameter, and must be specified both in the declaration and when being invoked:

 

if (0 == RegOpenKeyEx(nptrHKEY, "Software\\INFORMATION", 0, 0, [color=red]out[/color] nptrResult))
{
   //....

 

 

And - why not post it all - "RegEnumKeyEx" generates the following errors:

>> The best overloaded method match for RegEnumKeyEx(System.IntPtr, int, System.Text.StringBuilder, ref int, int, int, int, System.Runtime.InteropServices.FILETIME)' has some invalid arguments

>> Argument '4': cannot convert from 'int' to 'ref int' // how do I solve this?

 

As with out, you must specify ref in both the declaration and where the method is invoked - ref denotes a parameter which must be assigned a value before it is passed and which is used to return a value. Note that the final parameter should probably also be marked as out.

 

while (0 == (rc = RegEnumKeyEx(nptrHKEY, nIndex++, sName, [color=red]ref[/color] nLength, 0, 0, 0, [color=red]out[/color] ft)))
{
   //....

Never trouble another for what you can do for yourself.
Posted

So the only funky things left are the exception I am getting from "nptrHKEY" (as described above).

 

Seriously - would have taken me 100x the time to solve this alone - you taking the time to type and answer all this is really much appreciate and is making a lot of sense...

Posted

Overflow...

 

Exception thrown:

System.OverflowException: Arithmetic operation resulted in an overflow.

at System.IntPtr..ctor(Int64 value)

 

At least one of these will work:

 

IntPtr nptrHKEY = new IntPtr((int) 0x80000001);

IntPtr nptrHKEY = new IntPtr(int.MinValue + 1);

 

Good luck :)

Never trouble another for what you can do for yourself.
Posted

StringBuilder 101

 

Basic use of a StringBuilder:

 

//**** First of all, using:
using System.Text;

//**** Usage:
StringBuilder buff = new StringBuilder(size_of_buffer);

//Call platfom invoke here

text = buff.ToString();

 

:cool:

Never trouble another for what you can do for yourself.
Posted (edited)

IntPtr nptrHKEY = new IntPtr((int) 0x80000001);

>> Constant value '2147483649' cannot be converted to a 'int' (use 'unchecked' syntax to override)

 

IntPtr nptrHKEY = new IntPtr(int.MinValue + 1);

This compiles okay but fails on "RegOpenKeyEx" with a return code of 5 (ERROR_ACCESS_DENIED);

How would that represent HKEY_CURRENT_USER?

 

Anyways - I assume Access Denied could also be because I am not passing in the "REGSAM samDesired" falgs to define what kind I want - (for example seeing as I am only reading = KEY_READ)... How do I find out what KEY_READ is as an INTEGER value (because obviously just using "KEY_READ" won't work).

 

Thanks,

Edited by Shaitan00
Posted

Access denied error

 

IntPtr nptrHKEY = new IntPtr(int.MinValue + 1);

This compiles okay but fails on "RegOpenKeyEx" with a return code of 5.

 

Also - how would that represent HKEY_CURRENT_USER?

Thanks,

 

int.MinValue has a value of 0x80000000, and add 1 to that to get 0x80000001. As for returning error 5, this denotes an access denied error.

Never trouble another for what you can do for yourself.
Posted

Ya- you answer faster then I can edit hehehehe - I edited my post to say that 5 = Access Denied and I assume that is because I am not passing in "REGSAM samDesired" flags (currently pass in 0) to define what kind I want - (for example seeing as I am only reading the time stamp and name = KEY_READ)... How do I find out what KEY_READ is as an INTEGER value (because obviously just using "KEY_READ" won't work).

 

I need to find out how I can pass it in...

Thanks,

Posted

Key_read

 

Anyways - I assume Access Denied could also be because I am not passing in the "REGSAM samDesired" falgs to define what kind I want - (for example seeing as I am only reading = KEY_READ)... How do I find out what KEY_READ is as an INTEGER value (because obviously just using "KEY_READ" won't work).

 

It looks like KEY_READ is:

 

int KEY_READ = 0x00020000 | 0x0001 | 0x0008 | 0x0010;

 

Note all these constants are available in the Win32 header files (winnt.h for these ones).

Never trouble another for what you can do for yourself.
Posted (edited)

Now we are getting somewhere (all thanks to you) - looks like "RegOpenKeyEx" is working fine - I think this might be the last issue - with "RegEnumKeyEx" -when I debug I get the following exception thrown:

 

System.Runtime.InteropServices.MarshalDirectiveException: Can not marshal parameter #8: Invalid managed/unmanaged type combination (this value type must be paired with Struct).

at RegEnumKeyEx(IntPtr hKey, Int32 dwIndex, StringBuilder lpName, Int32& lpcName, Int32 lpReserved, Int32 lpClass, Int32 lpcClass, FILETIME& lpftLastWriteTime)

 

 

Parameter 8 is "ft" ... I'm was going to try to guess (to not look too stupid) but I got no clue on this one...

 

FILETIME ft, ftMostRecent;
ftMostRecent.dwLowDateTime = 0;
ftMostRecent.dwHighDateTime = 0;

while (0 == (rc = RegEnumKeyEx(nptrHKEY, nIndex++, sbBuff, ref nLength, 0, 0, 0, out ft)))

Edited by Shaitan00
Posted

Marshalling problems

 

Annoyingly, the FILETIME in System.Runtime.InteropServices has been deprecated, and you should now use the FILETIME from System.Runtime.InteropServices.ComTypes - very confusing I know. You should be able to just change your using statement.

 

If that doesn't fix it, then try changing the way the parameter is declared in the function declaration:

 

//Before
[MarshalAs(UnmanagedType.LPStruct)] out FILETIME lpftLastWriteTime

//Try
[MarshalAs(UnmanagedType.Struct | UnmanagedType.LPStruct)] out FILETIME lpftLastWriteTime
//Or
[MarshalAs(UnmanagedType.Struct)] out FILETIME lpftLastWriteTime

 

Working out marshalling specifics can be a bit of a black art at times.

 

:)

Never trouble another for what you can do for yourself.
Posted

[MarshalAs(UnmanagedType.Struct)] out FILETIME lpftLastWriteTime worked like a charm (or so it seems)... Compiles and runs...

 

Absolute last question (as it all seems to work - I just want to ensure I am not causing any leaks or handle issues) - typically I would need to call "LONG RegCloseKey(HKEY hKey);" after doing a "RegOpenKeyEx" - is this somehow no longer needed or do I do to do a DLLImport for it also and close the IntPtr "nptrResult" when I am done with all this?

Posted

RegCloseKey

 

[DllImport("Advapi32.dll")]
private static extern int RegCloseKey(
   IntPtr hKey
);

 

Finally there! :cool:

Never trouble another for what you can do for yourself.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...