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}
> {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
> {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
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)