Mega Search
23.2 Million


Sign Up

Make a donation  
Why does NtCreateKey fail in 64bit program?  
News Group: embarcadero.public.delphi.nativeapi

Using Delphi XE5.

The code below works correctly when compiled as a 32bit program; the registry key is either created, or opened if it already existed. However when compiled as a 64bit program, NtCreateKey fails with status:
"Invalid access to memory location"
When raised as an exception, this shows as Exception EOSError: System Error. Code 998.

Information on how I can change the code to make it work correctly on x64 appreciated.

{code}
program CreateKey;

uses
  Windows,
  SysUtils;

type
  NTSTATUS = LongWord;

  TUnicodeString = packed record
    Length       : Word;
    MaximumLength: Word;
    Buffer       : PChar;
  end;
  UNICODE_STRING = TUnicodeString;
  PUNICODE_STRING = ^UNICODE_STRING;

  TObjectAttributes = packed record
    Length                  : Cardinal;
    RootDirectory           : THandle;
    ObjectName              : PUNICODE_STRING;
    Attributes              : Cardinal;
    SecurityDescriptor      : Pointer;
    SecurityQualityOfService: Pointer;
  end;
  OBJECT_ATTRIBUTES = TObjectAttributes;
  POBJECT_ATTRIBUTES = ^OBJECT_ATTRIBUTES;

const
  ntdll = 'ntdll.dll';
  OBJ_CASE_INSENSITIVE = $00000040;

procedure RtlInitUnicodeString(sDest: PUNICODE_STRING; sSource: PChar); stdcall; external ntdll;
function  RtlNtStatusToDosError(Status: NTSTATUS): Integer; stdcall; external ntdll;
function  NtCreateKey(var KeyHandle: THandle; DesiredAccess: LongWord;
          var ObjectAttributes: OBJECT_ATTRIBUTES; TitleIndex: LongWord;
          Class_: PUNICODE_STRING; CreateOptions: LongWord;
          var Disposition: LongWord): NTSTATUS; stdcall; external ntdll;
function  NtClose(KeyHandle: Thandle): NTSTATUS; stdcall; external ntdll;

function NT_SUCCESS(Status: LongInt): boolean;
begin
  Result := Status >= 0;
end;

procedure InitializeObjectAttributes(var InitializedAttributes: TObjectAttributes;
           ObjectName: PUNICODE_STRING; Attributes: Cardinal;
           RootDirectory: THandle; SecurityDescriptor: Pointer);
begin
  InitializedAttributes.Length := SizeOf(TObjectAttributes);
  InitializedAttributes.RootDirectory := RootDirectory;
  InitializedAttributes.Attributes := Attributes;
  InitializedAttributes.ObjectName := ObjectName;
  InitializedAttributes.SecurityDescriptor := SecurityDescriptor;
  InitializedAttributes.SecurityQualityOfService := nil;
end;

var
  Status: NTSTATUS;
  KeyHandle: THandle;
  KeyName: TUnicodeString;
  ObjectAttributes: TObjectAttributes;
  Disposition: cardinal;

const
  sKeyName = '\Registry\Machine\SOFTWARE\keytest';

begin
  RtlInitUnicodeString(@KeyName, PChar(sKeyName));
  InitializeObjectAttributes(ObjectAttributes, @KeyName, OBJ_CASE_INSENSITIVE,
                             0, nil);
  Status := NtCreateKey(KeyHandle, KEY_WRITE, ObjectAttributes, 0,
                        @KeyName, REG_OPTION_NON_VOLATILE, Disposition);
  if NT_SUCCESS(Status) then
  begin
    if Disposition = REG_CREATED_NEW_KEY then
      MessageBox(0, 'new key created', 'CreateKey', MB_OK)
    else
      MessageBox(0, 'existing key opened', 'CreateKey', MB_OK);
    NtClose(KeyHandle);
  end
  else
  begin
    MessageBox(0, PChar(SysErrorMessage(RtlNtStatusToDosError(Status))),
               'CreateKey', MB_OK);
  end;
end.
{code}

Vote for best question.
Score: 0  # Vote:  0
Date Posted: 18-Oct-2014, at 6:21 AM EST
From: Nigel Thomas
 
Re: Why does NtCreateKey fail in 64bit program?  
News Group: embarcadero.public.delphi.nativeapi
> {quote:title=Remy Lebeau (TeamB) wrote:}{quote}
> 
> Try removing 'packed' from your records..
> 

Bingo. The simple removal of 'packed' fixed the issue. Thanks Remy.

> And then stop resorting to NtCreateKey()

As I explained to John, I need to use the Native API for a good reason.

--
Nigel

Vote for best answer.
Score: 0  # Vote:  0
Date Posted: 18-Oct-2014, at 1:30 PM EST
From: Nigel Thomas
 
Re: Why does NtCreateKey fail in 64bit program?  
News Group: embarcadero.public.delphi.nativeapi
> {quote:title=John Treder wrote:}{quote}
> 
> Try using the TRegistry class. It's in System.Win.Registry.  It'll be a lot less work for you.
> 
> -- 
> Tredmill

I should have mentioned: I'm using the Native API for a good reason, I need it to deal with keys that have been created with nulls or other unicode characters that the Windows API cannot handle, in the keyname. The "invalid memory location" error crops up on NtOpenKey as well as NtCreateKey, so I just created one example to show the problem.

--
Nigel

Vote for best answer.
Score: 0  # Vote:  0
Date Posted: 18-Oct-2014, at 1:24 PM EST
From: Nigel Thomas
 
Re: Why does NtCreateKey fail in 64bit program?  
News Group: embarcadero.public.delphi.nativeapi
Nigel wrote:

> Information on how I can change the code to make it work correctly on
> x64 appreciated.

Try removing 'packed' from your records, and replace Cardinal with LongWord 
or ULONG.

And then stop resorting to NtCreateKey() and use RegCreateKeyEx() instead 
(or TRegistry, though it does not provide Disposition info):

{code}
program CreateKey;

uses
  Windows,
  SysUtils;

var
  Status: Longint;
  KeyHandle: HKEY;
  Disposition: DWORD;

const
  sKeyName = 'SOFTWARE\keytest';

begin
  Status := RegCreateKeyEx(HKEY_LOCAL_MACHINE, PChar(sKeyName), 0, nil, REG_OPTION_NON_VOLATILE, 
KEY_WRITE, nil, @KeyHandle, @Disposition);

  if Status = ERROR_SUCCESS then
  begin
    if Disposition = REG_CREATED_NEW_KEY then
      MessageBox(0, 'new key created', 'CreateKey', MB_OK)
    else
      MessageBox(0, 'existing key opened', 'CreateKey', MB_OK);
    RegCloseKey(KeyHandle);
  end
  else
  begin
    MessageBox(0, PChar(SysErrorMessage(Status)), 'CreateKey', MB_OK);
  end;
end.
{code}

--
Remy Lebeau (TeamB)

Vote for best answer.
Score: 0  # Vote:  0
Date Posted: 18-Oct-2014, at 10:45 AM EST
From: Remy Lebeau (TeamB)
 
Re: Why does NtCreateKey fail in 64bit program?  
News Group: embarcadero.public.delphi.nativeapi
Nigel Thomas wrote:

> Using Delphi XE5.
> 
> The code below works correctly when compiled as a 32bit program; the registry key is either created, or opened if it already existed. However when compiled as a 64bit program, NtCreateKey fails with status:
> "Invalid access to memory location"
> When raised as an exception, this shows as Exception EOSError: System Error. Code 998.
> 
> Information on how I can change the code to make it work correctly on x64 appreciated.
> 
> {code}
> program CreateKey;
> 
> uses
>   Windows,
>   SysUtils;
> 
> type
>   NTSTATUS = LongWord;
> 
>   TUnicodeString = packed record
>     Length       : Word;
>     MaximumLength: Word;
>     Buffer       : PChar;
>   end;
>   UNICODE_STRING = TUnicodeString;
>   PUNICODE_STRING = ^UNICODE_STRING;
> 
>   TObjectAttributes = packed record
>     Length                  : Cardinal;
>     RootDirectory           : THandle;
>     ObjectName              : PUNICODE_STRING;
>     Attributes              : Cardinal;
>     SecurityDescriptor      : Pointer;
>     SecurityQualityOfService: Pointer;
>   end;
>   OBJECT_ATTRIBUTES = TObjectAttributes;
>   POBJECT_ATTRIBUTES = ^OBJECT_ATTRIBUTES;
> 
> const
>   ntdll = 'ntdll.dll';
>   OBJ_CASE_INSENSITIVE = $00000040;
> 
> procedure RtlInitUnicodeString(sDest: PUNICODE_STRING; sSource: PChar); stdcall; external ntdll;
> function  RtlNtStatusToDosError(Status: NTSTATUS): Integer; stdcall; external ntdll;
> function  NtCreateKey(var KeyHandle: THandle; DesiredAccess: LongWord;
>           var ObjectAttributes: OBJECT_ATTRIBUTES; TitleIndex: LongWord;
>           Class_: PUNICODE_STRING; CreateOptions: LongWord;
>           var Disposition: LongWord): NTSTATUS; stdcall; external ntdll;
> function  NtClose(KeyHandle: Thandle): NTSTATUS; stdcall; external ntdll;
> 
> function NT_SUCCESS(Status: LongInt): boolean;
> begin
>   Result := Status >= 0;
> end;
> 
> procedure InitializeObjectAttributes(var InitializedAttributes: TObjectAttributes;
>            ObjectName: PUNICODE_STRING; Attributes: Cardinal;
>            RootDirectory: THandle; SecurityDescriptor: Pointer);
> begin
>   InitializedAttributes.Length := SizeOf(TObjectAttributes);
>   InitializedAttributes.RootDirectory := RootDirectory;
>   InitializedAttributes.Attributes := Attributes;
>   InitializedAttributes.ObjectName := ObjectName;
>   InitializedAttributes.SecurityDescriptor := SecurityDescriptor;
>   InitializedAttributes.SecurityQualityOfService := nil;
> end;
> 
> var
>   Status: NTSTATUS;
>   KeyHandle: THandle;
>   KeyName: TUnicodeString;
>   ObjectAttributes: TObjectAttributes;
>   Disposition: cardinal;
> 
> const
>   sKeyName = '\Registry\Machine\SOFTWARE\keytest';
> 
> begin
>   RtlInitUnicodeString(@KeyName, PChar(sKeyName));
>   InitializeObjectAttributes(ObjectAttributes, @KeyName, OBJ_CASE_INSENSITIVE,
>                              0, nil);
>   Status := NtCreateKey(KeyHandle, KEY_WRITE, ObjectAttributes, 0,
>                         @KeyName, REG_OPTION_NON_VOLATILE, Disposition);
>   if NT_SUCCESS(Status) then
>   begin
>     if Disposition = REG_CREATED_NEW_KEY then
>       MessageBox(0, 'new key created', 'CreateKey', MB_OK)
>     else
>       MessageBox(0, 'existing key opened', 'CreateKey', MB_OK);
>     NtClose(KeyHandle);
>   end
>   else
>   begin
>     MessageBox(0, PChar(SysErrorMessage(RtlNtStatusToDosError(Status))),
>                'CreateKey', MB_OK);
>   end;
> end.
> {code}

Try using the TRegistry class. It's in System.Win.Registry.  It'll be a lot less work for you.

-- 
Tredmill

Vote for best answer.
Score: 0  # Vote:  0
Date Posted: 18-Oct-2014, at 10:09 AM EST
From: John Treder