5

I am trying to port the following C++ code:

BOOL SyskeyGetClassBytes(HKEY hKeyReg,LPSTR keyName,LPSTR valueName,LPBYTE classBytes) {
HKEY hKey,hSubKey;
DWORD dwDisposition=0,classSize;
BYTE classStr[16];
LONG ret;
BOOL isSuccess = FALSE;

ret = RegCreateKeyEx(hKeyReg,keyName,0,NULL,REG_OPTION_NON_VOLATILE,KEY_QUERY_VALUE,NULL,&hKey,&dwDisposition);

if(ret!=ERROR_SUCCESS) 
    return FALSE;
else if(dwDisposition!=REG_OPENED_EXISTING_KEY) {
    RegCloseKey(hKey);
    return FALSE;
}
else {
    if(RegOpenKeyEx(hKey,valueName,0,KEY_READ,&hSubKey)==ERROR_SUCCESS) {
        classSize = 8+1;
        ret = RegQueryInfoKey(hSubKey,(LPTSTR)classStr,&classSize,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
        if((ret==ERROR_SUCCESS)&&(classSize==8)) {
            classBytes[0]= (HexDigitToByte(classStr[0]) << 4) | HexDigitToByte(classStr[1]);
            classBytes[1]= (HexDigitToByte(classStr[2]) << 4) | HexDigitToByte(classStr[3]);
            classBytes[2]= (HexDigitToByte(classStr[4]) << 4) | HexDigitToByte(classStr[5]);
            classBytes[3]= (HexDigitToByte(classStr[6]) << 4) | HexDigitToByte(classStr[7]);
            isSuccess = TRUE;
        }
        RegCloseKey(hSubKey);
    }
    RegCloseKey(hKey);
}

return isSuccess;

}

I spent like 5 hours trying to figure out my problem, with no success. I know for a fact that I am properly calling this method. My C# code is

 unsafe static bool SyskeyGetClassBytes(RegistryHive hKeyReg, string keyName, string valueName, byte* classBytes)
    {
        UIntPtr hSubKey;
        UIntPtr hKey;
        RegResult tmp; ;
        uint classSize;
        StringBuilder classStr = new StringBuilder();
        int ret;
        bool isSuccess = false;
        ret = RegCreateKeyEx(hKeyReg, keyName, 0, null, RegOption.NonVolatile, RegSAM.QueryValue, UIntPtr.Zero, out hKey, out tmp);

        if (ret != 0)
        {
            return false;

        }
        else if (tmp != RegResult.OpenedExistingKey)
        {
            return false;
        }
        else
        {
            int res = RegOpenKeyEx(hKey, valueName, 0, (int)RegSAM.Read, out hSubKey);
            if (res == 0)
            {
                classSize = 8 + 1;
                ret = RegQueryInfoKey(hSubKey, out classStr, ref classSize, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);

                if ((classSize == 8))
                {
                    classBytes[0] = (byte)((byte)(HexDigitToByte(classStr[0]) << (byte)4) | HexDigitToByte(classStr[1]));
                    classBytes[1] = (byte)((byte)(HexDigitToByte(classStr[2]) << (byte)4) | HexDigitToByte(classStr[3]));
                    classBytes[2] = (byte)((byte)(HexDigitToByte(classStr[4]) << (byte)4) | HexDigitToByte(classStr[5]));
                    classBytes[3] = (byte)((byte)(HexDigitToByte(classStr[6]) << (byte)4) | HexDigitToByte(classStr[7]));
                    isSuccess = true;
                }
                RegCloseKey(hSubKey);
            }
            else
            {
                return false;
            }
            RegCloseKey(hKey);
        }
        return isSuccess;
    }

Its a little bit hard for me to debug, but eventually I determined that the problem is occurring at this line. Execution seems to halt afterwards.

 ret = RegQueryInfoKey(hSubKey, out classStr, ref classSize, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);

I know this is not a problem with permissions, as this C# program is running with admin perms AND as the local system account. The method that I need that the .Net APIs don't offer is RegQueryInfoKey. My P/Invoke signatures and types used are:

  [StructLayout(LayoutKind.Sequential)]
    public struct SECURITY_ATTRIBUTES
    {
        public int nLength;
        public unsafe byte* lpSecurityDescriptor;
        public int bInheritHandle;
    }
    [Flags]
    public enum RegOption
    {
        NonVolatile = 0x0,
        Volatile = 0x1,
        CreateLink = 0x2,
        BackupRestore = 0x4,
        OpenLink = 0x8
    }

    [Flags]
    public enum RegSAM
    {
        QueryValue = 0x0001,
        SetValue = 0x0002,
        CreateSubKey = 0x0004,
        EnumerateSubKeys = 0x0008,
        Notify = 0x0010,
        CreateLink = 0x0020,
        WOW64_32Key = 0x0200,
        WOW64_64Key = 0x0100,
        WOW64_Res = 0x0300,
        Read = 0x00020019,
        Write = 0x00020006,
        Execute = 0x00020019,
        AllAccess = 0x000f003f
    }

    public enum RegResult
    {
        CreatedNewKey = 0x00000001,
        OpenedExistingKey = 0x00000002
    }
    [DllImport("advapi32.dll", CharSet = CharSet.Auto)]
    public static extern int RegOpenKeyEx(
      UIntPtr hKey,
      string subKey,
      int ulOptions,
      int samDesired,
      out UIntPtr hkResult);
    [DllImport("advapi32.dll", SetLastError = true)]
    public static extern int RegCloseKey(
        UIntPtr hKey);
    [DllImport("advapi32.dll", SetLastError = true)]
    static extern int RegCreateKeyEx(
                RegistryHive hKey,
                string lpSubKey,
                int Reserved,
                string lpClass,
                RegOption dwOptions,
                RegSAM samDesired,
                UIntPtr lpSecurityAttributes,
                out UIntPtr phkResult,
                out RegResult lpdwDisposition);
    [DllImport("advapi32.dll", EntryPoint = "RegQueryInfoKey", CallingConvention = CallingConvention.Winapi, SetLastError = true)]
    extern private static int RegQueryInfoKey(
        UIntPtr hkey,
        out StringBuilder lpClass,
        ref uint lpcbClass,
        IntPtr lpReserved,
        IntPtr lpcSubKeys,
        IntPtr lpcbMaxSubKeyLen,
        IntPtr lpcbMaxClassLen,
        IntPtr lpcValues,
        IntPtr lpcbMaxValueNameLen,
        IntPtr lpcbMaxValueLen,
        IntPtr lpcbSecurityDescriptor,
        IntPtr lpftLastWriteTime);
user1454902
  • 750
  • 9
  • 24

3 Answers3

3

The lpClass parameter is declared incorrectly. Pass the StringBuilder by value.

[DllImport("advapi32.dll")]
extern private static int RegQueryInfoKey(
    UIntPtr hkey,
    StringBuilder lpClass,
    ref uint lpcbClass,
    IntPtr lpReserved,
    IntPtr lpcSubKeys,
    IntPtr lpcbMaxSubKeyLen,
    IntPtr lpcbMaxClassLen,
    IntPtr lpcValues,
    IntPtr lpcbMaxValueNameLen,
    IntPtr lpcbMaxValueLen,
    IntPtr lpcbSecurityDescriptor,
    IntPtr lpftLastWriteTime
);

You also need to allocate the StringBuilder instance to have the desired capacity. So, allocate the StringBuilder like this:

StringBuilder classStr = new StringBuilder(255);//or whatever length you like

And then set classSize like this:

classSize = classStr.Capacity+1;

I removed the parameters to DllImport. Most are not necessary, and the SetLastError is incorrect.

There may be other issues with your code, but with these changes at least the call to RegQueryInfoKey will match your C++ code.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
0

Try this signature for RegQueryInfoKey:

[DllImport("advapi32.dll", EntryPoint="RegQueryInfoKey", CallingConvention=CallingConvention.Winapi, SetLastError=true)]
 extern private static int RegQueryInfoKey(
 UIntPtr 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,
 IntPtr lpftLastWriteTime);

You are not declaring them as out params and in the RegQueryInfoKey Win32 call they are _Out_opt_.

edtheprogrammerguy
  • 5,957
  • 6
  • 28
  • 47
  • I originally tried that, however looking at the C++ code it passes NULL pointers. Using the out keyword would pass a pointer to what ever variable was referenced after out, and can not get it pass a null pointer to the function. Anyways I will continue to debug and see if I can get to the bottom of why its not working – user1454902 Jun 04 '13 at 23:42
0

You need to initialize your StringBuilder with enough capacity to store a classSize number of characters.

classSize = 8 + 1;
classStr.Capacity = classSize;
ret = RegQueryInfoKey(hSubKey, out classStr, ref classSize, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);

The marshaller will use the capacity set on the StringBuilder to send a buffer of the capacity size to the RegQueryInfoKey function. Without that you are probably corrupted memory.

See http://msdn.microsoft.com/en-us/library/s9ts558h.aspx#cpcondefaultmarshalingforstringsanchor3

shf301
  • 31,086
  • 2
  • 52
  • 86