Delete RegistryKey Win64/32 - delphi

I am trying to delete a Windows key using Delphi , however unsuccessfully , follows the code I'm using
Function DeleteKeyAPI(hRoot: HKEY; sPath: String; IsReg64: BOOL): BOOL;
Var
iRet: Integer;
Begin
If IsReg64 Then
iRet := RegDeleteKeyEx(hRoot, PChar(sPath), KEY_WOW64_64KEY, 0)
Else
iRet := RegDeleteKeyEx(hRoot, PChar(sPath), KEY_WOW64_32KEY, 0);
If iRet = ERROR_SUCCESS Then
Result := True
Else
Result := False;
End;
in some keys I can delete more than one has in own Regedit me of an access denied error
how can I fix this problem ?
Edited -----------------------
I tried to do as follows , but without success
function SHDeleteKey(key: HKEY; pszSubKey: LPCTSTR): DWORD; stdcall;
implementation
{$R *.dfm}
function SHDeleteKey; external 'shlwapi.dll' name 'SHDeleteKeyA';
procedure TForm1.FormCreate(Sender: TObject);
begin
SHDeleteKey(HKEY_LOCAL_MACHINE, 'SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Run\AdobeCS6ServiceManager');
end;

You should inspect the value of iRet to learn more. This is a Win32 error code that will give more details of the reason for failure.
From the documentation:
The subkey to be deleted must not have subkeys. To delete a key and all its subkeys, you need to enumerate the subkeys and delete them individually. To delete keys recursively, use the RegDeleteTree or SHDeleteKey function.
This is one common failure mode, namely that the subkey you are attempting to delete has subkeys itself. I'm not sure what error code is, perhaps ERROR_DIR_NOT_EMPTY.
Another common failure mode is that the named subkey does not exist. That would lead to error code ERROR_PATH_NOT_FOUND or perhaps ERROR_FILE_NOT_FOUND.
Finally, and what I guess is the real problem, you cannot obtain delete rights to the key. Since you are using alternate registry flags, I suspect you are trying to delete a subkey under HKLM. Your process needs sufficient rights to be able to do this. Typically that means running as an elevated admin user. You will get ERROR_ACCESS_DENIED if you have insufficient rights.
Regarding your edit, AdobeCS6ServiceManager is a value rather than a key. The function you need is RegDeleteValue. Read about the registry to learn what these terms mean: https://msdn.microsoft.com/en-us/library/windows/desktop/ms724182.aspx
Furthermore, it is disappointing that you ask a question about RegDeleteKeyEx and then edit to show code that calls SHDeleteKey. Please try to stick to the original question.

Related

Can't read key from registry

I heared that Windows creates a unique key for a PC which is called "MachineID". I found two locations in my registry. Only the location "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography" should be correct. I try to read the value by this function:
Function GetMaschineID:string;
var
Reg : TRegistry;
//HKEY_CURRENT_USER\Software\Microsoft\MSNMessenger = {dd239a44-fa0d-43ff-a51c-5561d3e39de3}
//HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography = a06b0ee0-b639-4f55-9972-146776bcd5e4
begin
Reg := TRegistry.Create(KEY_READ);
try
Reg.Rootkey:=HKEY_LOCAL_MACHINE; //Hauptschlüssel
//Reg.RootKey:=HKEY_CURRENT_USER;
if Reg.OpenKey('SOFTWARE\Microsoft\Cryptography\',false) then //Unterschlüssel öffnen
//if Reg.OpenKey('Software\Microsoft\MSNMessenger\',false) then //Unterschlüssel öffnen
begin
Result:=Reg.ReadString('MachineGuid');
end;
finally
Reg.Free;
end;
end;
This version results in an empty string; you see as comment the result from the registry. The second version for "hkey_Current_user" brings the expected string result.
What is wrong with my code or are parts of the registry read protected?
Possible explanation 1
For HKLM you are subject to registry redirection. You have a 32 bit process and are trying to read a key from the 64 bit view of the registry. By default, your 32 bit process is redirected to the 32 bit view, which (implementation detail) lives under Wow6432Node.
Use the KEY_WOW64_64KEY access flag to read from the 64 bit view. As detailed here: How can I read 64-bit registry key from a 32-bit process?
Possible explanation 2
Your call to OpenKey fails for keys under HKLM because you are requesting write access and standard user does not have write access to HKLM. Use OpenKeyReadOnly instead.
Other advice
At the very least you should have debugged this a bit more. Does the call to Reg.OpenKey succeed or fail? You should have debugged enough to know that. Perhaps you did but did not say. If Reg.OpenKey failed then explanation 2 is most likely. Even then, you may subsequently suffer from the other problem.
Note also that your function does not assign to the function result variable, or raise an error, if the call to Reg.OpenKey fails. I would expect that the compiler would have warned you about that.
procedure TForm1.Button1Click(Sender: TObject);
var
registry: TRegistry;
begin
Registry := TRegistry.Create(KEY_READ OR KEY_WOW64_64KEY);
try
registry.RootKey := HKEY_LOCAL_MACHINE;
if (registry.KeyExists('\SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL')) and
(registry.OpenKeyReadOnly('\SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL')) then
begin
showmessage(registry.ReadString('SQLEXPRESS'));
registry.CloseKey;
end
else showmessage('failed');
finally
registry.Free;
end;
end;

SHDeleteKey Function

I am having one Delphi XE2 Project for Windows Registry Operation. I need to delete all subnodes under **HKEY_CLASSES_ROOT\CLSID\{00000000-0000-0000-0000-000000000001}** , so I have defined the following codes :
function SHDeleteKey(key: HKEY; SubKey: PWideChar): Integer; stdcall; external 'shlwapi.dll' name 'SHDeleteKeyW';
..
..
..
..
..
procedure TMainForm.BitBtn02Click(Sender: TObject);
var
RegistryEntry : TRegistry;
begin
RegistryEntry := TRegistry.Create(KEY_READ or KEY_WOW64_64KEY);
RegistryEntry.RootKey := HKEY_CLASSES_ROOT;
if (RegistryEntry.KeyExists('CLSID\{00000000-0000-0000-0000-000000000001}\')) then
begin
Memo01.Font.Color := 3992580;
Memo01.Lines.Add('Windows Registry Entry Has Been Found In Your System');
RegistryEntry.Access:= KEY_WRITE or KEY_WOW64_64KEY;
SHDeleteKey(HKEY_CLASSES_ROOT, PWideChar('CLSID\{00000000-0000-0000-0000-000000000001}'));
RegistryEntry.CloseKey();
RegistryEntry.Free;
Memo01.Font.Color := 16756480;
Memo01.Lines.Add('Windows Registry Entry Has Been Deleted Successfully');
end
else
begin
Memo01.Font.Color := 7864575;
Memo01.Lines.Add('Windows Registry Entry Has Not Been Found In Your System');
end;
end;
But nothing is happening. Then I have tried
function SHDeleteKey(key: HKEY; SubKey: PChar): Integer; stdcall; external 'shlwapi.dll';
but here is another problem is telling "Entry Point not found".
Your function import is failing because the function is named SHDeleteKeyW where the W specifies that you want to use Unicode characters. So your function declaration should be
function SHDeleteKey(hKey: HKEY; pszSubKey: PWideChar): Integer; stdcall;
external 'shlwapi.dll' name 'SHDeleteKeyW';
Once that is fixed, the two most common failure modes are:
Your process does not have admin rights.
Your process runs in a 32 bit process on a 64 bit system and so cannot see the 64 bit view of the registry.
Based on your earlier question, option 2 seems most likely.
You said "nothing is happening", but I'm sure something is happening. The function is failing and returning an error status to you. But you did not check the return value of the call to SHDeleteKey. Whenever you call a Windows API, check the return value. If it fails, the return value allows you to diagnose that failure.
Assuming the issue is the registry redirector for your 32 bit process, your options include:
Run the code from a 64 bit process.
Use RegDeleteTree.
Empty the key's subkeys first, and then use TRegistry.DeleteKey.
Note that the code where you specify KEY_WOW64_64KEY only has effect when using the TRegistry methods. Since SHDeleteKey is a Windows API function, it is independent from that class.
For your second problem, you may want to try ShDeleteKeyW instead (explicitly selecting the wide string variant).
In both cases, however, you should check the result to see why it failed.
You don't mention what O/S this is on but there appears to be several platform-specific quirks with this function as can be seen in the comments here.

event handling in delphi

I got a sample of code im trying to understand in delphi,can anyone explain this means below,i know it creates and sets events but im trying to understand why the (nil,true,false,'pishu') i want to know the significance of the nil,true,false and the word in inverted comma's it seems i can write any word in there.
var
Form1: TForm1;
h:thandle;
st:string;
fopen:textfile;
countstr:integer;
implementation
{$R *.dfm}
procedure TForm1.Button2Click(Sender: TObject);
begin
setEvent(h);
CloseHandle(h);
end;
procedure TForm1.Button3Click(Sender: TObject);
begin
h:=createevent(nil,true,false,'pishu');
resetevent(h);
end;
end.
procedure TForm1.Button1Click(Sender: TObject);
begin
h:=createevent(nil,true,true,'pishu');
waitforsingleobject(h,infinite);
image1.Canvas.Brush.Color:=clblack;
image1.Canvas.Ellipse(random(image1.Width-100),random(image1.Width),random(image1.Width),random(image1.Width));
image1.Canvas.Brush.Color:=clyellow;
image1.Canvas.Ellipse(random(image1.Width-100),random(image1.Width),random(image1.Width),random(image1.Width));
image1.Canvas.Brush.Color:=clblue;
image1.Canvas.Ellipse(random(image1.Width-100),random(image1.Width),random(image1.Width),random(image1.Width));
image1.Canvas.Brush.Color:=clred;
image1.Canvas.Ellipse(random(image1.Width-100),random(image1.Width),random(image1.Width),random(image1.Width));
image1.Canvas.Brush.Color:=clgreen;
image1.Canvas.Ellipse(random(image1.Width-100),random(image1.Width),random(image1.Width),random(image1.Width));
//рисуем квадраты
image1.Canvas.Brush.Color:=clblack;
image1.Canvas.Rectangle(random(image1.Width-100),random(image1.Width),random(image1.Width),random(image1.Width));
image1.Canvas.Brush.Color:=clyellow;
image1.Canvas.Rectangle(random(image1.Width-100),random(image1.Width),random(image1.Width),random(image1.Width));
image1.Canvas.Brush.Color:=clblue;
image1.Canvas.Rectangle(random(image1.Width-100),random(image1.Width),random(image1.Width),random(image1.Width));
image1.Canvas.Brush.Color:=clred;
image1.Canvas.Rectangle(random(image1.Width-100),random(image1.Width),random(image1.Width),random(image1.Width));
image1.Canvas.Brush.Color:=clgreen;
image1.Canvas.Rectangle(random(image1.Width-100),random(image1.Width),random(image1.Width),random(image1.Width));
CloseHandle(h);
end;
end.
CreateEvent takes several parameters. It's defined on MSDN as HANDLE CreateEvent(
LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, BOOL bInitialState, LPTSTR lpName). The parameters are:
lpEventAttributes
==================
Ignored. Must be NULL.
bManualReset
============
Boolean that specifies whether a manual-reset or auto-reset
event object is created. If TRUE, then you must use the
ResetEvent function to manually reset the state to nonsignaled.
If FALSE, the system automatically resets the state to nonsignaled
after a single waiting thread has been released.
bInitialState
=============
Boolean that specifies the initial state of the event object.
If TRUE, the initial state is signaled; otherwise, it is nonsignaled.
lpName
======
Pointer to a null-terminated string that specifies the name of the
event object. The name is limited to MAX_PATH characters and can contain
any character except the backslash path-separator character (\). Name
comparison is case sensitive.
If lpName matches the name of an existing named event object, the bManualReset
and bInitialState parameters are ignored because they have already been set by
the creating process.
If lpName is NULL, the event object is created without a name.
If lpName matches the name of an existing semaphore, mutex, waitable timer,
job, or file-mapping object, the function fails and the GetLastError function
returns ERROR_INVALID_HANDLE. This occurs because these objects share the same
name space.
This explains why you can type almost anything as the "word with inverted commas" (the lpName).
For more information, you can see the MSDN web site's documentation on CreateEvent
here.
MSDN is your friend when it comes to API documentation questions .See this for answers to your question about CreateEvent.

How can my program detect whether it's running on a particular domain?

I have the need to restrict specific functions of an application based on the location of the currently logged in user. As I have to implement this logic in Delphi, I'd prefer not to go overboard with full active directory/LDAP queries.
My curent thought is to utilize DsGetDcName, and use the GUID returned in the DOMAIN_CONTROLLER_INFO structure and compare it to a hard coded constant. It seems to reason that a domain GUID would only change if the domain is recreated, so this would provide functionality that I desire with limited overhead. My only concern is that I can't find any documentation on MSDN confirming my assumption.
type
EAccessDenied = Exception;
EInvalidOwner = Exception;
EInsufficientBuffer = Exception;
ELibraryNotFound = Exception;
NET_API_STATUS = Integer;
TDomainControllerInfoA = record
DomainControllerName: LPSTR;
DomainControllerAddress: LPSTR;
DomainControllerAddressType: ULONG;
DomainGuid: TGUID;
DomainName: LPSTR;
DnsForestName: LPSTR;
Flags: ULONG;
DcSiteName: LPSTR;
ClientSiteName: LPSTR;
end;
PDomainControllerInfoA = ^TDomainControllerInfoA;
const
NERR_Success = 0;
procedure NetCheck(ErrCode: NET_API_STATUS);
begin
if ErrCode <> NERR_Success then
begin
case ErrCode of
ERROR_ACCESS_DENIED:
raise EAccessDenied.Create('Access is Denied');
ERROR_INVALID_OWNER:
raise EInvalidOwner.Create('Cannot assign the owner of this object.');
ERROR_INSUFFICIENT_BUFFER:
raise EInsufficientBuffer.Create('Buffer passed was too small');
else
raise Exception.Create('Error Code: ' + IntToStr(ErrCode) + #13 +
SysErrorMessage(ErrCode));
end;
end;
end;
function IsInternalDomain: Boolean;
var
NTNetDsGetDcName: function(ComputerName, DomainName: PChar; DomainGuid: PGUID; SiteName: PChar; Flags: ULONG; var DomainControllerInfo: PDomainControllerInfoA): NET_API_STATUS; stdcall;
NTNetApiBufferFree: function (lpBuffer: Pointer): NET_API_STATUS; stdcall;
LibHandle: THandle;
DomainControllerInfo: PDomainControllerInfoA;
ErrMode: Word;
const
NTlib = 'NETAPI32.DLL';
DS_IS_FLAT_NAME = $00010000;
DS_RETURN_DNS_NAME = $40000000;
INTERNAL_DOMAIN_GUID: TGUID = '{????????-????-????-????-????????????}';
begin
if Win32Platform = VER_PLATFORM_WIN32_NT then
begin
ErrMode := SetErrorMode(SEM_NOOPENFILEERRORBOX);
LibHandle := LoadLibrary(NTlib);
SetErrorMode(ErrMode);
if LibHandle = 0 then
raise ELibraryNotFound.Create('Unable to map library: ' + NTlib);
try
#NTNetDsGetDcName := GetProcAddress(Libhandle, 'DsGetDcNameA');
#NTNetApiBufferFree := GetProcAddress(Libhandle,'NetApiBufferFree');
try
NetCheck(NTNetDsGetDcName(nil, nil, nil, nil, DS_IS_FLAT_NAME or DS_RETURN_DNS_NAME, DomainControllerInfo));
Result := (DomainControllerInfo.DomainName = 'foo.com') and (CompareMem(#DomainControllerInfo.DomainGuid,#INTERNAL_DOMAIN_GUID, SizeOf(TGuid)));//WideCharToString(pDomain);
finally
NetCheck(NTNetApiBufferFree(DomainControllerInfo));
end;
finally
FreeLibrary(LibHandle);
end;
end
else
Result := False;
end;
Added a related question on ServerFault as suggested.
Found another interesting read on Technet which also seems to hint at me being right, but isn't specifically scoped at domain SID's.
Create a service account on the domain;
Get the GUID of the service account and encrypt it and save it somewhere (registry) maybe as part of enterprise install process to validate a license agreement.
On startup of the client app query for the Domain Service Account GUID and validate it with the saved GUID.
Or create your own enterprise 'key' server.
Doing an LDAP query is easier than doing all the domain controller crap.
If I correct understand your requirement the best API in your case is GetUserNameEx. You can choose the value of NameFormat parameter of the type EXTENDED_NAME_FORMAT which you can better verify. Another function GetComputerNameEx is helpful if you want additionally verify the information about the computer where the program is running.
I have the need to restrict specific
functions of an application based on
the location of the currently logged
in user
If you are trying to find out the location of the currently logged in user, you shouldn't be using DsGetDcName.
Your computer can be joined to domainA. Your logon user can be from domainB. Calling DsGetDcName on your computer doesn't give you domainB GUID but it will give you domainA GUID
Therefore, I think you should use LookupAccountName instead. The LookupAccountName gives you the currently logged in user's SID. Then, you can extract the domain SID from the user SID. That domain SID is really the domain where this user coming from. For the details of how to extract a domain SID from a user SID, please check here
Regarding to your original question about the uniqueness of the domain GUID, I am sorry that I don't have answer on it. AFAIK, there is no tool available allowing you to change the domain SID nor the GUID. I am not sure how hard to hack into it and change it.

how to quickly verify the case sensitive filename really exists

I have to make a unix compatible windows delphi routine that confirms if a file name exists in filesystem exactly in same CaSe as wanted, e.g. "John.txt" is there, not "john.txt".
If I check "FileExists('john.txt')" its always true for John.txt and JOHN.TXT due windows .
How can I create "FileExistsCaseSensitive(myfile)" function to confirm a file is really what its supposed to be.
DELPHI Sysutils.FileExists uses the following function to see if file is there, how to change it to double check file name is on file system is lowercase and exists:
function FileAge(const FileName: string): Integer;
var
Handle: THandle;
FindData: TWin32FindData;
LocalFileTime: TFileTime;
begin
Handle := FindFirstFile(PChar(FileName), FindData);
if Handle <> INVALID_HANDLE_VALUE then
begin
Windows.FindClose(Handle);
if (FindData.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) = 0 then
begin
FileTimeToLocalFileTime(FindData.ftLastWriteTime, LocalFileTime);
if FileTimeToDosDateTime(LocalFileTime, LongRec(Result).Hi,
LongRec(Result).Lo) then Exit;
end;
end;
Result := -1;
end;
function FileExistsEx(const FileName: string): Integer;
var
Handle: THandle;
FindData: TWin32FindData;
LocalFileTime: TFileTime;
begin
Handle := FindFirstFile(PChar(FileName), FindData);
if Handle <> INVALID_HANDLE_VALUE then
begin
Windows.FindClose(Handle);
if (FindData.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) = 0 then
begin
FileTimeToLocalFileTime(FindData.ftLastWriteTime, LocalFileTime);
if FileTimeToDosDateTime(LocalFileTime, LongRec(Result).Hi, LongRec(Result).Lo) then
if AnsiSameStr(FindData.cFileName, ExtractFileName(FileName)) then Exit;
end;
end;
Result := -1;
end;
Tom, I'm also intrigued by your use case. I tend to agree with Motti that it would be counter intuitive and might strike your users as odd.
On windows file names are not case sensitive so I don't see what you can gain from treating file names as if they were case sensitive.
In any case you can't have two files named "John.txt" and "john.txt" and failing to find "John.txt" when "john.txt" exists will probably result in very puzzled users.
Trying to enforce case sensitivity in this context is un-intuitive and I can't see a viable use-case for it (if you have one I'll be happy to hear what it is).
I dealt with this issue a while back, and even if I'm sure that there are neater solutions out there, I just ended up doing an extra check to see if the given filename was equal to the name of the found file, using the case sensitive string comparer...
I ran into a similar problem using Java. Ultimately I ended up pulling up a list of the directory's contents (which loaded the correct case of filenames for each file) and then doing string compare on the filenames of each of the files.
It's an ugly hack, but it worked.
Edit: I tried doing what Banang describes but in Java at least, if you open up file "a.txt" you'r program will stubbornly report it as "a.txt" even if the underlying file system names it "A.txt".
You can implement the approach mention by Kris using Delphi's FindFirst and FindNext routines.
See this article

Resources