How to delete a registry key if empty with CMD script? - windows

Looking for a way to delete a registry key if it is empty, aside from the standard (Default) value within.
For example, if there is something along these lines in the registry...
v HKLM
v SOFTWARE
v Vendor Name
v Application Name 1
- (Default), REG_SZ, (value not set)
- ProcessName, REG_SZ, "C:\Path\To\Executable\File.exe"
- SupportingDLL, REG_SZ, "C:\Path\To\Supporting.dll"
- UninstallAllowed, REG_DWORD, 0x00000000
v Application Name 2
- (Default), REG_SZ, (value not set)
v Application Name 3
- (Default), REG_SZ, (value not set)
- ProcessName, REG_SZ, "C:\Path\To\Executable\File.exe"
- SupportingDLL, REG_SZ, "C:\Path\To\Supporting.dll"
- UninstallAllowed, REG_DWORD, 0x00000001
And the "Application Name 2" key is empty, except for the standard (Default) value, I need to be able to delete it. Sadly, it has to be via CMD script because I'm only able to modify an existing script.
Is this possible with a CMD script or am I simply out of luck?
Thanks in advance.

Related

ShellExecute bat file elevated (FMX, Win32)

I want to spawn a batch file from my FMX app (on Win32) with elevated privileges. From Remy's answer at the bottom of this thread on ShellExecute I found how to launch the batch file. Now, i can't figure out how to launch it with elevated privilege. Below is my code:
String Prog = "c:\\Users\\rwp\\Desktop\\test.bat";
int nErrorCode = (int) ShellExecute(NULL, L"runas", Prog.c_str(), NULL, NULL, SW_SHOWNORMAL);
if (nErrorCode <= 32) {
ShowMessage("an error occured");
}
I added "runas" for the second argument after reading this to no avail. Running the batch file manually (right-click and run as admin) works. Here is content of the batch file fyi (just kicks of a system imaging):
c:\Windows\system32\wbAdmin.exe start backup -backupTarget:D: -include:C: -allCritical -quiet
How can i ShellExecute this batch file as admin?
UPDATE 1: I'm attempting to use CreateProcess per Remy suggestion. Here is my code (based on this example):
//Code is inside a __fastcall button click
PROCESS_INFORMATION piProcInfo;
STARTUPINFO siStartInfo;
siStartInfo.cb = sizeof(STARTUPINFO);
siStartInfo.lpReserved = NULL;
siStartInfo.lpReserved2 = NULL;
siStartInfo.cbReserved2 = 0;
siStartInfo.lpDesktop = NULL;
siStartInfo.dwFlags = 0;
// String strCmdLine = "C:\\Users\\rwpatter\\Desktop\\test.bat";
String strCmdLine = "C:\\Windows\\System32\\wbAdmin.exe start backup -backupTarget:T: -include:C: -allCritical -quiet";
// Create the child process.
int rtrn = CreateProcess(
NULL,
strCmdLine.c_str(),
NULL, // process security attributes
NULL, // primary thread security attributes
0, // handles are inherited
0, // creation flags
0, // use parent's environment
0, // use parent's current directory
&siStartInfo, // STARTUPINFO pointer
&piProcInfo); // receives PROCESS_INFORMATION
// Wait for the processs to finish
DWORD rc = WaitForSingleObject(
piProcInfo.hProcess, // process handle
INFINITE);
ShowMessage(IntToStr(rtrn));
If I run it as shown (right-click on exe and run as admin) it returns 0 which means it failed. If I run it by putting the wbAdmin command line in the test.bat file (see commented line right above String strCmdLine in the code) then CreateProcess returns a 1 (success) but wbAdmin is still not running. It flashed a DOS window and i captured it as shown in the picture below. It shows oriental characters in the title bar and says not recognized as internal or external command. But, if i run that test.bat directly (elevated) it runs wbAdmin no problem.
Any ideas on what is wrong? Besides me obviously being ignorant. (p.s. i'll get to testing Golvind's answer on the ShellExecute after this...)
Running the batch file manually (right-click and run as admin) works.
Because you are running the 64-bit version of cmd when you start it manually.
It shows oriental characters in the title bar and says not recognized
as internal or external command.
Because your application is 32-bit. A 32-bit application does not see the same System32 folder as 64-bit applications. You can access the 64-bit System32 folder in 32-bit applications with the virtual sysnative folder.
#include <shellapi.h>
...
String strCmdLine = "wbAdmin.exe start backup -backupTarget:T: -include:C: -allCritical -quiet";
int rtrn = CreateProcess(
NULL,
strCmdLine.c_str(),
NULL, // process security attributes
NULL, // primary thread security attributes
0, // handles are inherited
0, // creation flags
0, // use parent's environment
0, // use parent's current directory
&siStartInfo, // STARTUPINFO pointer
&piProcInfo); // receives PROCESS_INFORMATION
if (!rtrn)
{
String newCmdLine = "c:\\windows\\sysnative\\wbAdmin.exe start backup -backupTarget:T: -include:C: -allCritical -quiet";
rtrn = CreateProcess(
NULL,
newCmdLine.c_str(),
NULL, // process security attributes
NULL, // primary thread security attributes
0, // handles are inherited
0, // creation flags
0, // use parent's environment
0, // use parent's current directory
&siStartInfo, // STARTUPINFO pointer
&piProcInfo); // receives PROCESS_INFORMATION
}
Or compile your application to 64-bit.
You need to launch CMD.exe as Administrator with "runas", and specify the batch file as a "run-me-then-exit" (i.e. /c) argument to command prompt, as so:
WCHAR wszCmdPath[MAX_PATH];
GetEnvironmentVariableW(L"ComSpec", wszCmdPath, MAX_PATH);
ShellExecuteW(NULL, L"runas", wszCmdPath, L"/c \"C:\\Path\\BatchFile.bat\"", L"", SW_SHOW);
Both functions called here can fail, and robust code would test for success before proceeding.

Inno Setup: Generated folder name works in Windows XP but not Windows 7

In the [Files] section, I use a Pascal scripting which calls my own DLL to generate a folder name where I want my program documentation to be installed. My DLL and my Pascal script appear to be working properly, but when I run my installer on Windows 7, Inno Setup uses the directory name as the file name instead of appending the file name to the directory name, and I end up getting all 3 documentation files copied into a file that has the name that I wanted the directory to have. Oddly, the code does work properly when I run it on Windows XP.
Here is some of the relevant code:
The [Files] section:
[Files]
Source: "doc 1.pdf"; DestDir: "{code:DocumentFolder}";
Source: "doc 2.pdf"; DestDir: "{code:DocumentFolder}";
Source: "doc 3.pdf"; DestDir: "{code:DocumentFolder}";
The Pascal script:
// Get the path to the documentation folder
// DocPath() returns a path name without a trailing backslash
// unless it returns a null string.
function DocumentFolder(Param: String) : String;
var
s : String;
k : integer;
begin
SetLength(s, 255);
k := DocPath(s); // Path to "MyCompany\MyProg" folder or something like it
if 0 = k then s := ExpandConstant('{app}'); // Just use the program folder if there is no public folder
Result := s;
end;
The reason I am scripting at all is that I'd like to have the documentation go into a folder within the system's public folder if there is one, but alternately into the program folder on a system with no public folder.
If I have missed some totally easy way to do that, kindly let me know.
Anyway, when I run this on a Windows 7 system, here is what I get, according to Inno Setup's debug log:
[10:47:42.406] Dest filename: C:\Users\Public\MyCompany\MyProgDocs
[10:47:42.409] Time stamp of our file: 2002-07-10 10:33:02.000
[10:47:42.412] Installing the file.
[10:47:42.453] Successfully installed the file.
[10:47:42.458] -- File entry --
[10:47:44.595] Dest filename: C:\Users\Public\MyCompany\MyProgDocs
[10:47:44.598] Time stamp of our file: 2014-09-13 21:14:36.000
[10:47:44.600] Dest file exists.
[10:47:44.601] Time stamp of existing file: 2002-07-10 10:33:02.000
[10:47:44.603] Version of our file: (none)
[10:47:44.609] Version of existing file: (none)
[10:47:44.611] Installing the file.
[10:47:44.637] Successfully installed the file.
[10:47:44.640] -- File entry --
[10:47:45.603] Dest filename: C:\Users\Public\MyCompany\MyProgDocs
[10:47:45.606] Time stamp of our file: 2014-09-16 14:51:26.000
[10:47:45.608] Dest file exists.
[10:47:45.610] Time stamp of existing file: 2014-09-13 21:14:36.000
[10:47:45.612] Version of our file: (none)
[10:47:45.615] Version of existing file: (none)
[10:47:45.617] Installing the file.
[10:47:45.710] Successfully installed the file.
As you can see, each of my 3 PDF files have been copied into a file named C:\Users\Public\MyCompany\MyProgDocs instead of C:\Users\Public\MyCompany\MyProgDocs\doc 1.pdf etc. as I intended. The destination file is created by the first line in the [Files] section, and is then overwritten by the second, and overwritten again by the third.
By stepping thru with the debugger, I see that my Pascal script and the DLL that supports it are working properly.
The call DocPath(s) returns the number of characters in the string, and sets its parameter s to the string value I want. On XP, it returns a zero and sets s to the null string. On Windows 7, DocPath(s) returns 36 and sets s to C:\Users\Public\MyCompany\MyProgDocs.
How can I fix this?
EDIT:
Here is some of the relevant code from my DLL:
#define FOLDERNAME _T("MyCompany")
static CString GetPublicPath()
{
TCHAR pubpath[_MAX_PATH] = {_T("")};
int nameSize = ::GetEnvironmentVariable(_T("public"), pubpath, countof(pubpath));
if (0 < nameSize)
{
TCHAR* wdbuf = _tgetcwd(NULL, 0);
_tchdir(pubpath);
_tmkdir(FOLDERNAME);
_tchdir(FOLDERNAME);
_tcscat(pubpath, _T("\\"));
_tcscat(pubpath, FOLDERNAME);
_tchdir(wdbuf);
free(wdbuf);
}
return CString(pubpath);
}
int STDCALL DocPath(wchar_t** x)
{
CString docpath = GetPublicPath();
docpath = StripBackslash(docpath);
if (0 < docpath.GetLength())
{
docpath += _T("\\MyProgDocs");
}
_tcscpy(*x, docpath.GetBuffer());
::MessageBox(0, *x, _T("DLL DocPath()"), MB_OK);
return _tcslen(*x);
}
I don't have the call to MessageBox() in the production version but it has been useful for debugging. The macro countof is like sizeof but returns an array count rather than a byte size so it works properly for wide characters.
If k > 0 you must set the lenght of s to k.
But it is much easier to get the environment variable from inno (and unnecessary to write a dll) - for example:
function DocumentFolder(dummy: String): String;
var
s: String;
begin
s := GetEnv('public');
if Length(s) > 0 then
s := s + '\MyCompany\MyProgDocs'
else
s := ExpandConstant('{app}');
Result := s;
end;

Navigate Shell command not working when the path includes an hash

I'm having problem using the Navigate Shell command when the path include an # sign.
; this will create 2 folders at the root of your C: drive
myPath1 := "C:\delete_me\"
myPath2 := "C:\delete#me\"
if !FileExist(myPath1)
FileCreateDir, %myPath1%
if !FileExist(myPath2)
FileCreateDir, %myPath2%
; make an Explorer active and press Alt-1 and Alt-2
return
!1::
strWinId := WinExist("A")
TrayTip, %myPath1%, %strWinId%
For pExp in ComObjCreate("Shell.Application").Windows
if (pExp.hwnd = strWinId)
try pExp.Navigate(myPath1)
return
!2::
strWinId := WinExist("A")
TrayTip, %myPath2%, %strWinId%
For pExp in ComObjCreate("Shell.Application").Windows
if (pExp.hwnd = strWinId)
try pExp.Navigate(myPath2)
return
Alt-1 works well. But, with Alt-2, the Navigate command returns "file:///C:/delete#me/ » not found.".
If there is no "/" after the "#" (eg myPath := "C:\delete#me"), it works. But this cannot be a solution because the destination path can be deeper in a subfolder (eg. "C:\delete#me\xyz").
I tried to encode the "#", replacing it with "%23", without success. Found nothing on the web or MSDN about that. Any idea?
[keywords: haskmark, hashtag, number sign or pound]
I have what looks to be a working solution for this, which I've also posted here:
4 options to change the current folder in Windows Explorer - Page 3 - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=526&p=153676#p153676
;links:
;Explorer Windows Manipulations - Page 5 - Scripts and Functions - AutoHotkey Community
;https://autohotkey.com/board/topic/19039-explorer-windows-manipulations/page-5#entry297581
;Navigate2 Method (IWebBrowser2)
;https://msdn.microsoft.com/en-us/library/aa752134(v=vs.85).aspx
;4 options to change the current folder in Windows Explorer - AutoHotkey Community
;https://autohotkey.com/boards/viewtopic.php?f=5&t=526
;windows - Navigate Shell command not working when the path includes an hash - Stack Overflow
;https://stackoverflow.com/questions/22868546/navigate-shell-command-not-working-when-the-path-includes-an-hash
;an AutoHotkey v1.1 script
;note: will create folder: %A_Desktop%\abc#def\abc#def
;q:: ;explorer - navigate to folder (tested on Windows 7)
WinGet, hWnd, ID, A
WinGetClass, vWinClass, % "ahk_id " hWnd
if !(vWinClass = "CabinetWClass") && !(vWinClass = "ExploreWClass")
return
vDir = %A_Desktop%\abc#def\abc#def
;vDir = %A_Desktop%\abc def\abc def
if !FileExist(vDir)
FileCreateDir, % vDir
DllCall("shell32\SHParseDisplayName", WStr,vDir, Ptr,0, PtrP,vPIDL, UInt,0, Ptr,0)
for oWin in ComObjCreate("Shell.Application").Windows
if (oWin.HWND = hWnd)
{
if !InStr(vDir, "#")
oWin.Navigate(vDir)
else
{
VarSetCapacity(SAFEARRAY, A_PtrSize=8?32:24, 0)
NumPut(1, SAFEARRAY, 0, "UShort")
NumPut(1, SAFEARRAY, 4, "UShort")
NumPut(vPIDL, SAFEARRAY, A_PtrSize=8?16:12, "Ptr")
NumPut(DllCall("shell32\ILGetSize", Ptr,vPIDL, UInt), SAFEARRAY, A_PtrSize=8?24:16, "Int")
oWin.Navigate2(ComObject(0x2011,&SAFEARRAY))
DllCall("shell32\ILFree", Ptr,vPIDL)
}
break
}
return
If you want to open a new window, there's no need for COM or unreliable workarounds: just run the folder.
Run C:\delete#me
If you want to open the path in an existing window which is already active, the simplest and most effective workaround is this:
SendInput {F4}{Esc}{Raw}C:\delete#me`n
So in the context of your script, you could use the following function to work around the # when it is present:
Navigate(pExp, myPath2)
;...
Navigate(Exp, Path)
{
if RegExMatch(Path, "#.*\\")
SendInput {F4}{Esc}{Raw}%Path%`n
else
Exp.Navigate(Path)
}
Unfortunately, there does not seem to be a solution to this. Shell.Application Navigate command fails if the path includes a hash (# as in C:\C#Projects).
Using AutoHotkey, the workaround would be to rely on the "second best" approach as identified by the tests in this thread: http://ahkscript.org/boards/viewtopic.php?f=5&t=526.
run, Explorer.exe
Sleep, 500
strFolder := A_ScriptDir
Send, {F4}{Esc}
Sleep, 500
ControlSetText, Edit1, C:\delete#me, A
ControlSend, Edit1, {Enter}, A
When I saw that Navigate couldn't handle hash, I was shocked,
but sure enough I replicated the error.
I thought I'd try the short form path just in case. It works!
if vDir contains #
Loop, %vDir%, 2, 0 ;(0/1/2=files/both/folders, 0/1=recurse no/yes)
vDir := A_LoopFileShortPath
The following approach doesn't require a visible address bar, or SendInput,
also the previous navigation history is maintained.
In the worst-case scenario of a hash in the short-form path of the dir above the target dir,
a go-between folder is used which is navigated to.
A link is created there, invoked, and deleted.
Below, the workaround code is indented, to separate it from the standard code.
A hotkey of ctrl+q, when an Explorer window is active, launches the script.
-
^q:: ;explorer - navigate to directory (use go-between dir if short-form path of dir above target contains #)
WinGet, hWnd, ID, A
WinGetClass, vWinClass, ahk_id %hWnd%
if vWinClass not in CabinetWClass,ExploreWClass
Return
vDir2 = %A_Desktop%\Go-Between ;go-between dir
vDir3 = C:\delete#me ;target dir
if (SubStr(vDir3, 1-1) = "\")
vDir3 := SubStr(vDir3, 1, -1)
if !InStr(FileExist(vDir3), "D")
Return
vPathLnk := ""
if vDir3 contains #
Loop, %vDir3%, 2, 0 ;(0/1/2=files/both/folders, 0/1=recurse no/yes)
vDir3 := A_LoopFileShortPath
;vDir4 is the short-form path of the dir above the target
;paths of problem target dirs are of the form: *#*\*
;where there is at least one hash with a backslash to its right
SplitPath, vDir3, , vDir4
if vDir4 contains #
{
if !InStr(FileExist(vDir2), "D")
FileCreateDir, %vDir2%
if !InStr(FileExist(vDir2), "D")
{
MsgBox error`, go-between dir not found:`r`n%vDir2%
Return
}
vNameLnk = Go-Between.lnk
vPathLnk = %vDir2%\%vNameLnk%
FileCreateShortcut, %vDir3%, %vPathLnk%
}
for oWin in ComObjCreate("Shell.Application").Windows
if (hWnd = oWin.Hwnd)
{
vDir1 := oWin.Document.Folder.Self.Path
if (vDir1 = vDir3)
break
if vDir3 contains #
{
if !(vDir1 = vDir2)
oWin.Navigate(vDir2)
while !(oWin.ReadyState = 4)
Sleep 10
oItem := oWin.Document.Folder.Items.Item(vNameLnk)
oItem.InvokeVerbEx("open")
break
}
oWin.Navigate(vDir3)
break
}
oWin := ""
if !(vPathLnk = "")
FileRecycle, %vPathLnk% ;send to recycle bin
;if !(vPathLnk = "")
;FileDelete, %vPathLnk% ;delete
Return

Prevent console window from being created in custom node.js build

I'm creating a custom build of node.js that should not show a console window to the user.
I've tried changing the linker config in the gyp file to 2 (which should set the linker flag /SUBSYSTEM:WINDOWS), but I still get a console window when I run the resulting node.exe binary.
How can I prevent the console window from appearing?
Edit: Further investigation reveals that the linker config in node.gyp is not taking effect. The generated node.vcxproj still has <Link><SubSystem>Console</SubSystem></Link> (which is very strange to me, since adding 'UACUIAccess': 'true' in the same part of node.gyp did take effect), so the built binary is incorrectly linked.
Solution 1
Save this one line of text as file invisible.vbs:
CreateObject(“Wscript.Shell”).Run “”"” & WScript.Arguments(0) & “”"”, 0, False
To run any program or batch file invisibly, use it like this:
wscript.exe “C:\Wherever\invisible.vbs” “C:\Some Other Place\MyBatchFile.bat”
To also be able to pass-on/relay a list of arguments use only two double quotes
CreateObject(“Wscript.Shell”).Run “” & WScript.Arguments(0) & “”, 0, False
eg: Invisible.vbs “Kill.vbs ME.exe”
Solution 2
Use a command line tool to silently launch a process : Quiet.
Solution 3
Roll your own C++ Win32 App:
PROCESS_INFORMATION procInfo = {0};
STARTUPINFOstartupInfo = {0};
SECURITY_ATTRIBUTESsaAttr = {0};
HANDLEhStdIn = GetStdHandle(STD_INPUT_HANDLE);
HANDLEhStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
HANDLEhStdErr = GetStdHandle(STD_ERROR_HANDLE);
// build up security attributes
saAttr.nLength = sizeof(saAttr);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
// set file handles for process to be created
startupInfo.cb = sizeof(startupInfo);
startupInfo.dwFlags = STARTF_USESTDHANDLES;
startupInfo.hStdInput = hStdIn;
startupInfo.hStdOutput = hStdOut;
startupInfo.hStdError = hStdErr;
// build command line: format is [cmd.exe /c "%batchScript%" %batchArgs%]
if (-1 == _snprintf_s(cmd, sizeof(cmd),"cmd.exe /c \"%s\" %s", batchScript, batchArgs))
errorExit("_snprintf_s(\"cmd.exe /c \"%%s\" %%s\"), \"%s\", \"%s\") failed.", batchScript, batchArgs);
rc = CreateProcess(NULL, cmd, NULL, &saAttr, TRUE, CREATE_NO_WINDOW, NULL, tempPath, &startupInfo, &procInfo);
You have to change the SubSystem field value in node.exe PE optional header. The current value is 3 which is defined as Windows Console. If you change it to 2 (which is defined as Windows GUI) there would be no console window. In order to patch the executable file, you have to use utilities to change Optional Header of PE.
One example of such a tool is PE tools.
Click on Optinal Header and then change the Subsystem from 3 to 2.
That`s all.
Remember that with this change you can only run js files. You can not use interactive mode.
It appears that you must:
Comment out the 'SubSystem': 1 line in common.gypi. (Changing it to 2 causes the build to fail in mksnapshot.)
Change SubSystem to 2 in node.gyp
Also add 'EntryPointSymbol': 'wmainCRTStartup' to node.gyp.
This builds a node.exe that does not create a console window.

what is wrong with this code to read specific registry value in a subkey then do a if exist/if not statement

well with some help from this site and dream in code i fixed my error, but am now the code just plain doesn't seem to be working. Here is the code:
using (RegistryKey Key = Registry.LocalMachine.OpenSubKey(#"SOFTWARE\Microsoft\Windows\CurrentVersion\Run\"))
if (Key != null)
{
string val = (string)Key.GetValue("COMODO Internet Security");
if (val == null)
{
MessageBox.Show("value not found");
}
else
{
// use the value
}
}
else
{
MessageBox.Show("key not found");
}
and when i use it, it give the "value not found" message box like its supposed to if it doesn't exist, only problem is the value DOES exist... i checked manually through regedit. so whats wrong?
Probably you have the problem to query registry values of HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run key on 64-bit operation system from a 32-bit application. In the case you will see values existion only under HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Run (see http://msdn.microsoft.com/en-us/library/aa384232.aspx). If you use unmanaged API (or invoce) you can open Run key with RegOpenKeyEx and KEY_QUERY_VALUE | KEY_WOW64_64KEY flags (see http://msdn.microsoft.com/en-us/library/aa384129.aspx). You should test whether the application run under 64-bit operation system before using KEY_QUERY_VALUE | KEY_WOW64_64KEY flag. In case of 32-bit operation system you should use KEY_QUERY_VALUE flag only.

Resources