Batch file - IF EXISTS statement "The system cannot open the device or file specified." - windows

I have written a batch file to uninstall a faulty WiFi driver (Intel ProSet Wireless) and set up the appropriate wireless profile on a laptop. This script is intended to be run remotely through Symantec Management Agent.
The code starts by running a group policy update to pull down appropriate
network certificates from the server. Then the code checks to see if the WiFi driver is installed. If it is, the script uninstalls it. Afterwards, in either case, it will wipe the current wireless profiles and call another batch file to install the appropriate wireless profile.
My issue is, when I run the script, the console will report "The system cannot open the device or file specified." after the software is uninstalled and it will terminate. The IF EXIST statement checks to see if one of the software files is there.
Typically, I can just run the same script a second time, and the IF EXIST case will not be met, so the rest of the batch file will work properly.
I am attaching my code below -- am I using IF EXIST correctly?
gpupdate /force
IF EXIST "C:\ProgramData\Package Cache\{552523b2-40ad-46b3-94f6-2b99d0860d5c}\setup.exe" (
cd "C:\ProgramData\Package Cache\{552523b2-40ad-46b3-94f6-2b99d0860d5c}\"
start /wait setup.exe /uninstall
)
TIMEOUT /T 3 /nobreak >nul
netsh wlan delete profile name=*
cd "C:\Wireless_Settings\"
Mobile_Devices_profile.bat
I have researched other posts, and I do believe I am using the condition correctly. I don't see any other post that matches my case. It always correctly checks to see if the condition is met, however I don't understand why the program terminates after the software is uninstalled. All that I believe should happen is the case is no longer met, so the script continues on.

Possible solution : (your if statement appears to be correct)
Insert a pushd statement before the cd and a popd after the start.
This will ensure you return to your original directory after the if statement invokes setup.exe
IF EXIST "C:\ProgramData\Package Cache\{552523b2-40ad-46b3-94f6-2b99d0860d5c}\setup.exe" (
PUSHD
cd "C:\ProgramData\Package Cache\{552523b2-40ad-46b3-94f6-2b99d0860d5c}\"
start /wait setup.exe /uninstall
POPD
)
If it works, fine and good. If it doesn't, It's easy to undo.
Sure - in theory, you could change the cd to a PUSHD instead. There are many paths.
IF EXIST "C:\ProgramData\Package Cache\{552523b2-40ad-46b3-94f6-2b99d0860d5c}\setup.exe" (
PUSHD "C:\ProgramData\Package Cache\{552523b2-40ad-46b3-94f6-2b99d0860d5c}\"
start /wait setup.exe /uninstall
POPD
)
This should work.
Now that's interesting. I'm sure I've use pushd without arguments before. The documentation reads
Stores the current directory for use by the POPD command, then
changes to the specified directory.
PUSHD [path | ..]
which is not explicit when the option argument is missing.
I've also noticed that a dir list on my batch-development directory now lists a unicode-named file with spaces between the squares whereas it used to not contain those spaces. Maybe something has been silently changed... cmd version is dated 20170929

Setup.exe is trying to remove the directory from the Package Cache which happens to be your current working directory. Not a good way for a script to live.
#setlocal ENABLEEXTENSIONS
#rem #set prompt=$G
#set "_setupExe=C:\ProgramData\Package Cache\{552523b2-40ad-46b3-94f6-2b99d0860d5c}\setup.exe"
#gpupdate /force
#if exist "%_setupExe%" call %_setupExe% /uninstall
#TIMEOUT /T 3 /nobreak >nul
#netsh wlan delete profile name=*
#cd "C:\Wireless_Settings\"
#Mobile_Devices_profile.bat
I would add that debugging your script was complicated by the fact that you had a multi-line code block. You should avoid those at all costs. It's better to call a subroutine if you can fit it all on a single line.

Related

Batch | Why can't a script running in admin access other scripts? [duplicate]

I have a batch file that is in the same directory as the file I want to xcopy. But for some reason the file is not being found.
I thought that current directory was always where the batch file was located.
I run batch file as administrator. This occurs on a Windows 7 64-bit desktop computer.
Batch file:
#ECHO OFF
XCOPY /y "File1.txt" "File2.txt"
PAUSE
Error:
File not found - File1.txt
0 File(s) copied
Which directory is current working directory on starting a batch file with context menu item Run as administrator depends on User Account Control (UAC) setting for the current user.
This can be demonstrated with following small batch file C:\Temp\Test.bat:
#echo Current directory is: %CD%
#pause
With having selected in User Account Control Settings
Default - Notify me only when programs try to make changes to my computer
Don't notify me when I make changes to Windows settings
and using Run as administrator, Windows uses registry key
HKEY_CLASSES_ROOT\batfile\shell\runasuser\command
This registry key does not contain a default string for executing the batch file. Instead there is the string value DelegateExecute with the CLSID {ea72d00e-4960-42fa-ba92-7792a7944c1d}.
The result is opening a dialog window with title User Account Control and text:
Do you want to allow the following program to make changes to this computer?
Program name: Windows Command Processor
Verified publisher: Microsoft Windows
After confirmation by the user, Windows opens temporarily a new user session like when using on command line RunAs.
In this new user session the current working directory is %SystemRoot%\System32 on executing now the command defined in Windows registry with default string of key
HKEY_CLASSES_ROOT\batfile\shell\runas\command
which is:
%SystemRoot%\System32\cmd.exe /C "%1" %*
Therefore a console window is opened with title C:\Windows\System32\cmd.exe and the 2 lines:
Current directory is: C:\Windows\System32
Press any key to continue . . .
After hitting any key, batch execution finishes which results in closing cmd.exe which results in closing the user session.
But with having selected in User Account Control Settings
Never notify me when
Programs try to install software or make changes to my computer
I make changes to Windows settings
the behavior is different as the user has already elevated privileges.
Now Windows uses directly the command
%SystemRoot%\System32\cmd.exe /C "%1" %*
according to default string of key
HKEY_CLASSES_ROOT\batfile\shell\runas\command
in current user session.
The result is opening a console window also with title C:\Windows\System32\cmd.exe, but displayed in window is:
Current directory is: C:\Temp
Press any key to continue . . .
The current working directory of the parent process (Windows Explorer as desktop) is used for executing of the batch file because no switch to a different user session was necessary in this case.
PA has posted already 2 possible solutions in his answer which I replicate here with a small improvement (pushd with directory in double quotes) and with adding a third one.
Change current directory to directory of batch file using pushd and popd:
pushd "%~dp0"
%SystemRoot%\System32\xcopy.exe "File1.txt" "File2.txt" /Y
popd
This works also for UNC paths. Run in a command prompt window pushd /? for an explanation why this also works for UNC paths.
Use directory of batch file in source and destination specifications:
%SystemRoot%\System32\xcopy.exe "%~dp0File1.txt" "%~dp0File2.txt" /Y
Change working directory to directory of batch file using cd:
cd /D "%~dp0"
%SystemRoot%\System32\xcopy.exe "File1.txt" "File2.txt" /Y
This does not work for UNC paths because command interpreter cmd does not support a UNC path as current directory by default, see for example CMD does not support UNC paths as current directories for details.
The error message is very self explanatory. The file file1.txt is not found.
Because the file name does not include an absolute path, the system tries to find it on the current directory. Your current directory does not contain this file.
Your misconception is that the current directory is not the directory that contains the bat file. Those are two unrelated concepts.
You can easily check by adding this two commands in your bat file
echo BAT directory is %~dp0
echo Current directory is %CD%
you can notice they are different, and that there is a subtle difference in the way the last backslash is appended or not.
So, there are esentially two ways to cope with this problem
either change the current directory to match the expected one
pushd %~dp0
XCOPY /y "File1.txt" "File2.txt"
popd
or specify the full path in the command
XCOPY /y "%~dp0File1.txt" "%~dp0File2.txt"
For the sake of completeness and obscurity, I add another workaround, confirmed as working under Windows 8.1 and expected to work elsewhere, as it relies on documented functionality:
You can change the runas command definition keys
HKEY_CLASSES_ROOT\batfile\shell\runas\command and
HKEY_CLASSES_ROOT\cmdfile\shell\runas\command into
%SystemRoot%\System32\cmd.exe /S /C "(for %%G in (%1) do cd /D "%%~dpG") & "%1"" %*
Which results in the bat or cmd file starting in its containing directory when started using the runas verb, respectively the "Run as Administrator" menu entry.
What the additions to the original command exactly do:
cmd /S strips away first and last (double) quote in command string after /C
for %%G in (%1) do enumerates its single entry, the %1 argument,
making it available for expansion as %%G in the loop body; the letter is arbitrary but some may be "reserved"
%%~dpG expands to the drive and path of %%G, the ~ tilde stripping away quotes if present, which is why we add them back explicitly
cd /D changes both the drive and directory to its argument, and finally
& runs the second command "%1" %* regardless of success of the first one.
You can use pushd which will even support UNC paths, but a stray popd would land any script in the system32 directory, not a behavior I would be fond of.
It should be possible to do this for the exefile entry as well, but frankly, I'd rather live with the inconsistency than to attempt this on my system, as any error there could break a lot.
Enjoy defeating the security mechanics of your operating system :)

the installation package could not be open batch file

I've been working on a batch file all day, that I can't get to work open through GPO (another day, another question). So I decided to do it manually with every computer. I have two exe's and one MSI. The exe's work perfectly fine. They get installed, and it all works out. The MSI, however, doesn't. It gives me the error: the installation package could not be opened. Verify that the package exists and that you can access it, or contact the application vendor to verify that this is a valid Windows Installer package.
Now when I go to the network share and use it from there, it works perfectly fine. So there must be an issue with my code.
Here's the code:
#echo off
IF NOT EXIST "C:\Program Files (x86)\Citrix\ICA Client\" (
pushd "\\KOPI-DC01\ACCURO Cloudwerx\ACCURO\1\"
.\CitrixReceiver-4.4.1000.exe /silent
)
IF NOT EXIST "C:\Program Files (x86)\triCerat\Simplify Printing\ScrewDrivers Client v4\" (
pushd "\\KOPI-DC01\ACCURO Cloudwerx\ACCURO\2\"
msiexec.exe /i ".\Screwdriver.msi"
)
IF NOT EXIST "C:\Program Files\Cloudwerx\CloudwerxPlugin\" (
pushd "\\KOPI-DC01\ACCURO Cloudwerx\ACCURO\3\"
.\cloudwerx-setup.exe /silent
)
pause
Any help would be greatly appreciated, thanks.
I am guessing that your problem is the distinction in powershell between the current location (set by the pushd command) and the working directory (unaffected by the pushd command). You can see the working directory of the powershell process using the [Environment]::CurrentDirectory property:
# C:\> [Environment]::CurrentDirectory = "c:\"
# C:\> [Environment]::CurrentDirectory
c:\
# C:\> pushd C:\Temp
# C:\Temp> [Environment]::CurrentDirectory
c:\
# C:\Temp> Get-Location
Path
----
C:\Temp
WHat is probably happening is that msiexec.exe is using the working directory (i.e. [Environment]::CurrentDirectory) and not the current powershell location at invocation. I would just specify the full path to msiexec:
msiexec.exe /i "\\KOPI-DC01\ACCURO Cloudwerx\ACCURO\2\\Screwdriver.msi"
MSI installation packages build with an older WIX utility would throw the error whenever installation was attempted from a batch script that was accessed on a shared drive using UNC path instead of a mapped drive letter. On the other hand whenever the batch file was executed with a mapped drive letter the installation would work normally.
I'm not blaming WIX here because I'm not certain whether they are responsible. I'm just describing symptoms here. It might just be the result of invoking plain vanilla Windows batch script that in turn executes msiexec with a bunch of command line parameters.

Read variable from external file not working on running as scheduled task

I would like to run a batch file after resuming from sleep state in Windows.
If I start the batch file on command line everything works as expected.
But the batch script does not run properly as scheduled task.
What I have done:
External config file AutoMountConf.bat contains set Pass = Test
Local script file scheduleTask.bat contains
rem AutoMountConf.bat is in my intranet.
call X:\AutoMountConf.bat
start "" "C:\Program Files\TrueCrypt\TrueCrypt.exe" /auto favorites /p %Pass% /q
On command line the TrueCrypt container is mounted.
If I run the script from scheduled task I get the login screen to type the password manually.
There are two or perhaps even three issues.
The first one is set Pass = Test instead of set "Pass=Test" as Stephan reported already. For more details on how to assign a value right to an environment variable see my answer on Why is no string output with 'echo %var%' after using 'set var = text' on command line?
The second issue is caused by the fact that network drives once mapped by a user to a drive letter and remembered in registry by Windows are automatically disconnected by Windows on user logs off and are only reconnected if the same user logs on again.
For a scheduled task it is therefore very often necessary to use UNC paths for files and folders on a shared folder in network or connect the network drive and disconnect it in the batch file itself executed as scheduled task.
It is not possible to call a batch file with UNC path. Windows does not allow that. Therefore it is necessary to connect and disconnect to network share manually in the batch file. I offer 2 solutions for this problem.
The first one is using command net use:
%SystemRoot%\System32\net.exe use X: \\ComputerName\ShareName password /user:Domain\UserName /persistent:no
if not errorlevel 1 (
call X:\AutoMountConf.bat
%SystemRoot%\System32\net.exe use X: /delete
start "" /wait "C:\Program Files\TrueCrypt\TrueCrypt.exe" /auto favorites /p %Pass% /q
)
password and /user:Domain\UserName is necessary only if the scheduled task is not executed with a user account which has the permissions to access the batch file on the remote machine. In general it is much more secure to define the scheduled task with the right user account and safe the password also for this account together with the task. Windows stores the password for the task encrypted like it does it also for the user account itself.
Run in a command prompt windows net use /? for details on the required and optional options. /persistent:no is what avoids remembering the network share in Windows registry for automatic reconnect after login by same user.
The second one is using commands pushd and popd:
pushd \\ComputerName\ShareName
if not errorlevel 1 (
call AutoMountConf.bat
popd
start "" /wait "C:\Program Files\TrueCrypt\TrueCrypt.exe" /auto favorites /p %Pass% /q
)
Please execute in a command prompt window pushd /? and read the output help to understand why this works.
But this solution requires that the user account used for the scheduled task with correct password is one which has appropriate permissions on the share on the remote computer. Password and user name can't be specified with this solution in the batch file itself.
if not errorlevel 1 means if previous command exited NOT with a value greater or equal 1 meaning if exit code of previous command is 0 and therefore command execution was successful. It can always happen that the remote machine is currently not available on network and therefore it is always good to check success on connecting to share on remote machine.
There is perhaps one more reason why Pass is not defined after running AutoMountConf.bat.
AutoMountConf.bat contains setlocal and the variable Pass is defined after this command was executed and before endlocal is executed in same batch file or implicitly called by command processor on exiting AutoMountConf.bat.
setlocal results in creating always a copy of existing environment variables and all modifications on environment variables are done on this local copy. The previous environment variables are restored on execution of (matching) endlocal or when end of a batch file is reached in which case the command processor automatically restores previous environment.
Please execute in a command prompt window setlocal /? and read the output help.
For examples to understand environment management by commands setlocal and endlocal perhaps even better see answers on Echoing a URL in Batch and Why is my cd %myVar% being ignored?
set Pass = Test
sets a variable pass<space> with the Content <space>Test. So %pass% keeps empty.
use this Syntax:
set "Pass=Test"
to avoid any unintended spaces.

SCCM 2012 to distribute files (not application)

I want to use SCCM 2012 to distribute some files (not application, no executable there).
I know there is a way to create a batch file with xcopy command, and create a package to deploy that batch file to the clients. However, if I use xcopy I cannot leverage the BITS functionality and other benefits from SCCM.
Is this possible?
Hi I can see this is an old post, but I have used SCCM to copy files in the past.
A company I worked for in the past refused to use GP for any file copy (don't get me started) so I had to implement GP files over SCCM.
Here are some examples I used.
xcopy User.bmp "%ProgramData%\Microsoft\User Account Pictures" /c /y /r
REG ADD HKLM\Software\FILE_GP /v Copy_User_image /t REG_sz /d 1.0 /f
timeout 5
if not exist "%windir%\system32\oobe\Info\backgrounds" md "%windir%\system32\oobe\Info\backgrounds"
xcopy backgrounddefault.jpg "%windir%\system32\oobe\Info\backgrounds" /c /y /r
REG ADD HKLM\Software\File_GP /v Backgrounddefault /t REG_sz /d 1.0 /f
timeout 5
I had the script add the Registry key so I could use Application deployment and have the detection method look for the key as well as the file. So if a user deleted the file it would return.
The main reason for me to have the Reg Key was if you wanted to push an updated version of the file I could set the new copy script to push 1.1 to the reg key and have the detection look for that version, then I could retire the 1.0 version.
I found also for short scripts of 1 file of small size without the timeout line it would report failed immediately, if there were larger files I didn't need the timeout. The initial fail does clear after some time without the timeout line.
It should work if you chose your settings right.
In your batch make sure that all the paths and references are relative and never absolute.
Then when you create the deployment there is a point called "Distribution Points" where you can select the deployment options for fast as well as unreliable networks. There you would have to choose "Download content from distribution point and run locally" in both cases. Then your batch would basically copy the files from the CCM cache to the hard drive.
This method should work for packages, for applications I think it would be more difficult because you cannot directly specify deployment options for fast networks there.
Packages are downloaded to the clients with BITS by default. The XCOPY command would copy from CCMCACHE to final destination on the machine.
Not sure if you have tried it or not but if you have deployed any .exe file for application its ask for dependence file in that you mention your required file which need to copy and create a script to copy data from ccmcache to copy the data to destination folder. for this you can get the use of BITS and to check if the file are copied or not you can use a proper detection method.
I used a PowerShell script for this purpose and it works like charm.
Create a new script and change this one liner to your mods:
Copy-Item -Path "\\[NETWORK PATH]" -Destination "[DESTINATION PATH]" -Recurse
Other way around—if need to make a required package—could be to create a new application deployment and run this through PowerShell (ExecutionPolicy ByPass) and manually install from the client side; if you don't want user interaction, make it a required deploy.

Running batch file with /d option

I've been working for some time on the installer for my application (using Installshield), and some time ago I came upon the problem, when batch file, that I called using LaunchApplication failed to execute (to be specific - it executed, but in the wrong directory). I decided to dig this issue up and stumbled upon this article. The problem, it turns out, lies within Autorun registry key, which is defined in following matter:
cd /d C:\Blahblah\Yadayada
So, before the batch file was actually executed, this command changed directory.
The batch file is most basic one, something like this:
:start
foo.exe %1 --bar %2 --baz %3
if errorlevel 1 goto fail
ECHO Success
goto end
fail:
ECHO Fail
:end
So, basically this batch file expects that it will be launched from the correct directory, and it's not. I'm append INSTALDIR variable to the batchname in LaunchApplication call, just to be clear, and it works perfectly fine when Autorun key is not set up.
And, well, I finally got to the question - is there any way to provide launch options for individual batch file? I know that providing /d option will render Autorun useless, but it only works only on direct call.
For instance, let's assume that I have batch file with simple 'dir' command (let's call it foo.bat), and my Autorun key is defined as shown above. I run command-prompt with /d option (CMD /d), and then run 'dir' directly I'll get content of the folder I'm currently in (e.g. user folder); BUT, if I launch foo.bat, I'll get contents of C:\Blahblah\Yadayada, because Autorun command will execute first, set default folder, and only after that 'dir' command will be called.
So, personally I see a few options here. First - removing Autorun key, and that would be the most fitting and easiest solution if it had to be applied only for one machine - I can't possibly expect every user to take care of their Autorun key for themselves. Second, which should be applicable for everyone (but which I hadn't tested yet), would be providing path to installation folder as extra parameter to batch file, and then changing directory to that:
:start
%4
cd %5
foo.exe %1 --bar %2 --baz %3
...
Where %4 would be a disk letter and %5 would be a path. This seems to be a solution, but I find it counterproductive that I have to implicitly change path to the folder, given that it works perfectly well when Autorun key is absent.
So, I was wondering if there's any workaround about this Autorun key problem. Maybe, like I've mentioned in the title, kind of /d option for batch file so that when it runs it will override global option from Autorun and will actually launch from the place it's supposed to launch, or some kind of technique like that? Also, maybe there's some kind of option in LaunchApplication() function I'm not aware of?

Resources