I'm using InstallShield 2013 to make a Basic MSI installer for an application that requires Windows Platform Update KB2670838.
For .NET frameworks and other requirements, I select them in InstallShield in the Redistributables section. KB2670838 is not available.
If I download KB2670838 from Microsoft I get a .msu file. Can that be included in the installer somehow so that it automatically installs if needed? If not, is there a way to stop the install and tell the user that "KB2670838 is required but not installed. Get it here..."?
In InstallShield, you should typically deliver this sort of update as a prerequisite (Tools > Prerequisite Editor), or as a package included in a Suite (reference [SystemFolder]wusa.exe to install an .msu file). In both cases this keeps the redistributable installation logically separate from your package's installation, while providing your users a single installer experience.
Glytzhkof mentions several really good points about how to determine whether the update has been installed. You will want to incorporate these into your conditions (on the prerequisite or suite package), and also into detecting the update or lack thereof in your .msi package so it can abort if the required update has not been installed by the time the .msi is launched.
The Add/Remove programs list in the registry could help you get a rough idea of what's installed:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall
It seems this doesn't provide a full list of what is installed though: http://social.technet.microsoft.com/Forums/windows/en-US/d913471a-d7fb-448d-869b-da9025dcc943/where-does-addremove-programs-get-its-information-from-in-the-registry?forum=w7itprogeneral
Another way may be to use the file information from the knowledge base article:
http://support.microsoft.com/kb/2670838/en (For More Information : File Information) and use WIX / MSI's AppSearch / LaunchCondition feature. That should do the trick, though I find the syntax a bit counterintuitive.
Another approach is to write a custom action and combine these two sources (add /remove entry and file info). Such a custom action will make no changes to the system, and is hence less problematic than other custom actions that cause rollback-problems. I find it easier to test and maintain a custom action in case there are further prerequisites that are needed at some point. This is a matter of taste though. I just find it easier to run a prerequisite script against a selection of files to test that it identifies them correctly and run through as expected than to keep running the MSI file for every test.
Here is a similar question with some pointers from superuser.com:
https://superuser.com/questions/521175/determine-if-windows-hotfix-has-been-applied
And another link to serverfault.com (system administration site). Nice approach using PowerShell which can certainly be migrated to a custom action:
https://serverfault.com/questions/312778/determine-if-user-has-hotfix-981889-installed
Even more serverfault.com stuff involving update.exe, WMI and a Powershell script to view all installed hotfixes:
https://serverfault.com/questions/263847/how-can-i-query-my-system-via-command-line-to-see-if-a-kb-patch-is-installed . Recommended read. Microsoft: http://technet.microsoft.com/en-us/library/hh849836.aspx
PSInfo appears to be able to show installed hotfixes: http://technet.microsoft.com/en-us/sysinternals/bb897550
#Glytzhkof Good point. So how do I get InstallShield to abort and give the user a nice message so they know what to do? – shoelzer 1 hour ago
I will just add a new answer then - too long to write in a comment.
Locate the file details you need to scan for under "For More
Information : File Information" in this kdb article:
http://support.microsoft.com/kb/2670838/en
Select a few files to scan for and add as file searches in Installshield (see below screenshot). You specify a property for each file (FILE1FOUND, FILE2FOUND, FILE3FOUND, etc...), and if the search matches the file details (version, size, date, etc...) the property is set to the full path of the file. Otherwise the property is undefined or set to a default value (screenshot shows predefined search, and not file search, but you get the idea).
Finally you add LaunchCondition entries for each file to ensure that all files you have selected to check are the correct version or higher. I guess this is in Prerequisites or similar - I can't recall. Open the compiled MSI and check that it looks like the LaunchConditon table.
For the record: (not part of above suggestion)
Personally I am in favor of coding a single script for complex logic like this to ensure the logic can be inspected as a whole and crucially tested as a whole outside the MSI file. It is also good to add comments to such code to explain what the script is checking, and why (helps corporate deployment). A script can be run through dozens of tests against the machine directly without recompiling the MSI. This can save a lot of time if the logic is complex. If you write a compiled dll you can show a message box and attach the visual studio debugger to the msiexec.exe process (client or server depending on what context your custom action is running in) and step-through the code whilst embedded in the MSI, but this seems out of scope for your scenario. Just want to mention it for other people who might read this. Also check Stefan Kruger's installsite.com for more information on complex setup debugging like this.
It is important to note that scripting is never generally recommended for scenarios where the script makes changes to the system - if there is a built-in MSI way to achieve the same result. The reason for this is that a script that makes changes to a machine will need a separate rollback-operation to be specified for it for the MSI to follow best practice. This can be a spectacular amount of work and complexity to get right. The above script would only check system conditions, so there is no need for rollback support.
Let me try and add a reference style answer since my other answer is a bit organic to say the least at this point - I will leave it in since it contains an MSI discussion. See MSI recommendation in the middle section below:
WMI:
wmic qfe where "HotfixID = 'KB973687'"
PowerShell: (just get-hotfix for full list)
get-hotfix | findstr "981889"
SystemInfo (remove arguments for list format):
systeminfo /fo csv
PSInfo (seems to not list everything on all machines, and may not run silently properly):
PSinfo -h
Registry (apparently not complete list of hotfixes):
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall
For MSI custom action use, I would actually use a custom action that inspects file versions as explained in my other answer. Very reliable, and takes into account that the hotfix may be deprecated whilst the files are still up to date.
References:
Recommended: https://serverfault.com/questions/263847/how-can-i-query-my-system-via-command-line-to-see-if-a-kb-patch-is-installed
How do I get a list of installed updates and hotfixes?
SystemInfo: https://serverfault.com/questions/334552/how-to-check-that-a-known-windows-vulnerability-has-been-patched
http://windowsitpro.com/scripting/get-hotfix-information-quickly-wmic
https://serverfault.com/questions/69467/list-all-hotfixes-applied-to-windows-server
wmic in general: http://technet.microsoft.com/en-us/library/bb742610.aspx
Recommended: http://www.dedoimedo.com/computers/windows-wmic.html
http://www.techsupportalert.com/content/quick-and-easy-way-list-all-windows-updates-installed-your-system.htm
http://pario.no/2011/06/19/list-installed-windows-updates-using-wmic/
Had the same issue and solved it by adding a prerequisite of a PowerShell script and a batch file to execute it.
The pre.ps1 file looks something like this:
function TestConnection
{
Test-Connection -ComputerName "8.8.8.8" -Quiet
}
get-hotfix -id KB2670838
if(!$?){
#SourceURI = "https://download.microsoft.com/download/1/4/9/14936FE9-4D16-4019-A093-5E00182609EB/Windows6.1-KB2670838-x64.msu";
#$FileName = $SourceURI .Split('/')[-1]
#$BinPath = Join-Path $DownloadPath -ChildPath $FileName
Invoke-Webrequest -Uri $SourceURI -OutFile $BinPath
#Start-Process -FilePath $BinPath -ArgumentList "/q /norestart" -Wait -NoNewWindow
}
the pre.cmd file looks something like this:
#echo off
::set PS_FILE=%~dp0Prerequisite.ps1
set PS_FILE=%~dpn0.ps1
set PS_EXEC_PATH=%SystemRoot%\sysnative\WindowsPowerShell\v1.0\
set PS_EXEC_PATH=%SystemRoot%\System32\WindowsPowerShell\v1.0\
::set PS_EXEC_PATH=%SystemRoot%\SysWOW64\WindowsPowerShell\v1.0\
set PS_EXEC_PATH=
set PS_EXEC=%PS_EXEC_PATH%powershell.exe
echo %PS_EXEC%
echo %PS_FILE%
::%PS_EXEC% -file %PS_FILE% set-executionpolicy remotesigned
::%PS_EXEC% -NoProfile -ExecutionPolicy Bypass -Command "& '%PS_FILE%'"
::This is with admin rights
%PS_EXEC% -NoProfile -Command "& {Start-Process PowerShell.exe -ArgumentList '-NoProfile -ExecutionPolicy Bypass -File ""%PS_FILE%""' -Verb RunAs}"
::pause
Related
I have slow PowerShell console startup times (always more than 5 second wait) and was hoping for advice on troubleshooting steps to find out where the bottlenecks might be?
I have read that for running scripts, -NoProfile is important to prevent Modules etc loading, but how, in general, should we approach finding out what is slowing things down? I don't have many Modules installed and I know that since PowerShell 3.0, Modules are just referenced at startup and not fully loaded (a Module is only fully loaded when a function from a given Module is invoked) so I just can't understand why it takes 5+ seconds to start a bare console (my $profile also is empty).
Any advice on various steps that I can look at to debug the console startup process would be appreciated? Also, are there maybe some Microsoft or third-party tools that exist to debug the various steps in the console startup process to look for bottlenecks?
When PowerShell starts to become slow at startup, an update of the .NET framework might be the cause.
To speed up again, use ngen.exe on PowerShell's assemblies.
It generate native images for an assembly and its dependencies and install them in the Native Images Cache.
Run this as Administrator
$env:PATH = [Runtime.InteropServices.RuntimeEnvironment]::GetRuntimeDirectory()
[AppDomain]::CurrentDomain.GetAssemblies() | ForEach-Object {
$path = $_.Location
if ($path) {
$name = Split-Path $path -Leaf
Write-Host -ForegroundColor Yellow "`r`nRunning ngen.exe on '$name'"
ngen.exe install $path /nologo
}
}
Hope that helps
Step 1: Stop using PowerShell.
Now, seriously, something that needs ~13 seconds (YMMV) on an quad-core i7 cpu to launch off an ssd drive is an abomination of software architecture.
But yes, I hear you, "no viable alternative" etc...
... but if forced, bribed or blackmailed to still use it, check if your Windows has DNS cache service enabled.
For me, with DNS cache disabled and powershell executable firewalled, the built-in 5.1.19041.906 version starts quickly, but the new pwsh 7.1.4 would take around 13 seconds to get responsive to keyboard input under the same circumstances. It's so desperate to call home that it would just synchronously wait for some network timeout while blocking all user input, as if threads were a thing for the weak.
My resolution was to stick with the olden powershell 5.
My work computer stored the main profile on a remote server. Another minor problem was that it imported duplicate modules from 4 different profile.ps1 files.
Use the following commands to see where your profiles and modules are stored. Delete the unnecessary profile.ps1 and move all your modules into one directory.
echo $env:PSModulePath
$profile | select *
My loading time was reduced from 21000ms to 1300ms.
Found this solution when I googled having the same problem, but in 2022. Unfortunately this did not fix my issue.
Our systems have a group policy security requirement to "Turn on PowerShell Transcription". The policy requires that we specify "the Transcript output directory to point to a Central Log Server or another secure location". The server name changed and no one updated the policy. As soon as I updated the GPO with the new location, PowerShell opened instantly again.
Press Windows+R
Type %temp% and hit enter
C+A & SHIFT+DEL
That should do it
I want to zip up a folder. This is easy on operating systems like Linux where it's easy for the system admin to install the command line zip tool like apt install zip. Unfortunately on Windows it's not that straight forward.
A simple workaround I've found is that I can use Compress-Archive in a powershell script like this:
Compress-Archive -Path $folder -CompressionLevel Optimal -DestinationPath $zipPath
and then invoke this with the folder and zipPath before or after project building.
To do this, my CMakeLists.txt will have a line like:
execute_process(COMMAND powershell ${powershellScriptPath} "\"${folderPathToZip}\"" "\"${outputZipPath}\"" OUTPUT_QUIET)
This works fine, except I have to allow scripts to run globally and unsafely on my computer.
I want this to just work on any Windows computer without having to require the people to do any tinkering of settings. I also don't like the idea of getting other developers to allow scripts globally and unsafely for obvious reasons. I'd like my developers to be able to open the project and press build and it all works, and this is the last thing standing in my way from making it happen.
Is there a way to be able to get this script to run by changing the way the program is invoked? Or am I stuck and have to find another way to do this? Or is there some way to whitelist this specific file automatically without the user having to do any extra steps?
The only other hacky way around this would be for me to write my own zip utility that is invoked on every build to zip up the stuff, but that is a bunch of work for something that feels so close to being operational.
Edit: I can safely make the assumption that the computer has powershell installed and is relatively modern
You can bypass the effective PowerShell execution policy by passing -ExecutionPolicy Bypass to the PowerShell CLI (powershell.exe in the case of Windows PowerShell):
execute_process(COMMAND powershell -ExecutionPolicy Bypass -File ${powershellScriptPath} ${folderPathToZip} ${outputZipPath} OUTPUT_QUIET)
Also note that I'm using -File so as to instruct Windows PowerShell to execute a script file, because it defaults to -Command[1], which changes the interpretation of the arguments[2]; I'm not familiar with cmake, but the hope is that it provides double-quoting around the arguments by default / as necessary, which with -File should be sufficient.
[1] Note that PowerShell Core defaults to -File.
[2] For details, see the 2nd and subsequent sections of this answer.
I am very new to using Powershell but keen to learn.
I am attempting to install a MSI package, using PowerShell and passing in some variables. The end result is for this to be an unattended installation deployed via Jenkins using PowerShell. Please keep in mind I have changed the port numbers for this example:
msiexec /i /quiet $SYSTEMID ="PC01" $PORT1 =0000 $PORT2 =0001 $TARGETDIR ="C:\Application\" "C:\MSIPackage64bit.msi"
When trying to run the above I get presented with a Windows ® Installer. pop up which lists a load of MSIExec variable options.
I have been looking on the web for quite some time and now believe I'm having issues due to my lack of understanding when it comes to PowerShell.
/I needs to be followed by the path to the MSI to be installed. Also get rid of the $ in front of the property names. Finally TARGETDIR isn't always TARGETDIR. Some MSIs are authored as INSTALLDIR, INSTALLLOCATION and other possible directory table entry names. Adding logging ( /l*v path_to_log ) is usually a good choice also.
PS- Please note that since you are doing a silent installation you need to either be installer per-user without need for elevation or you need to already be elevated.
I cant figure out the parameter(s) to change the default install path C:\Program Files\ of Notepad++ to the drive I want it to install to when I run my Powershell script. I am trying to do a silent install and can't change it manually. Does anyone know what parameters I can add to install the program to the E:\ drive? Below is my code and what I am trying in powershell. I have been messing around a lot with the -ArgumentList parameters to figure out if I can point it to the E:\ drive but no luck so far.
function install-Notepadpp()
{
$install="*PATH*\npp.6.6.2.Installer.exe"
Start-Process -FilePath $install -ArgumentList '/InstallDirectoryPath:"E:\"','/S' -Wait -Verb RunAs
Write-Host “Notepad++ has been installed.” -ForegroundColor Green}
##### This is Windows calling the function to install the script
install-Notepadpp
Do you really require an installation? You have the standalone package out there on dev site, you can copy it wherever you want. As for the 'cool' context menu addons, they are simple enough to add manually.
Below is the brief description of steps you need to take to get this done:
Have your Powershell copy the content of standalone package to the
desired folder.
Generate a new GUID using Powershell
[guid]::NewGUID()
Write down the GUID to variable (so you can reference it later).
Under HKEY_CLASSES_ROOT\CLSID\ add GUID entry in the same way the
rest of GUIDs are added.
Create a subcontainers InprocServer32 and Settings
In the InprocServer32 set the (Default) value to desired
Notepad++ installation path pointing directly to NppShell_06.dll (ex.
C:\Program Files\Notepad++\NppShell_06.dll)
Add ThreadingModel REG_SZ entry and set its' value to Apartment
In the Settings subcontainer, set the values accordingly - for a
list of valid values, please reference a machine with Notepad++
installed. The most "interesting" ones are "Path" and "Title"
Add the GUID entry you generated earlier to subcontainer "ANotepad++"
in HKEY_CLASSES_ROOT*\shellex\ContextMenuHandlers\ under (Default)
value
This should do it. Although I did not test the above on my machine, I am pretty confident that this will sort out the "manual" installation issue. As a sidenote, it could be worth as a suggestion to developer (or, as a best way, write it on your own!) to add some silent installation configuration switches so that we don't have to bother with the above 'workarounds'. Should you run into some issues setting this up, let me know.
Is it possible to create an executable file.exe file from a PowerShell Script?
No. At least, not yet, and after 5 versions of PowerShell, it seems unlikely ever.
I wish I could just leave it at that, but other people have provided a bunch of "workaround" type answers I feel compelled to address:
You can wrap your .ps1 script in another script type to make it double-clickable, and you can even generate an executable with the script embedded (see the other answers on this thread) ... but those "executables" require the right version of PowerShell to be already present on the system, so you're not gaining anything by doing that, and you loose a lot of the features of PowerShell (like streaming object output, help documentation, and automatic parameter handling with tab completion for users).
Here is a simple .cmd batch file wrapper (you could extend this to allow parameters):
REM <#
copy %0 %0.ps1
PowerShell.exe -ExecutionPolicy Unrestricted -NoProfile -Command "&{Set-Alias REM Write-Host; .\%0.ps1}"
del %0.ps1
exit
REM #>
### Your PowerShell script goes below here.
### I've put a couple of lines as an example ...
ls | sort length -desc | select -first 5 | ft
ps | sort ws -desc | select -first 10 | ft
I know ...
With Portable PowerShell, it would probably be possible to package up a sort of self-extracting zip that would contain the right version of PowerShell and a script and would work. That's not an executable in any normal sense of the word -- it's a bit like if Valve had decided to just ship a vmware image on a thumbdrive as their solution to letting Linux users play Half Life. However, the product seems abandoned.
With PrimalScript (or PowerShell Studio) or PowerGui or pShellExec, your script can be encrypted, so it's slightly secured against prying eyes ... but this is basically just obfuscation, and essentially no different than the batch file, and in some ways worse.
Out of the box - no. However I have built a PowerShell script that can take a script and create an EXE wrapper around it. I created this script a while ago but decided to blog it here for folks to check out.
Use PowerGUI's Script Editor (it is free and works). Open your script in the PowerGUI Script Editor > Tools > Compile script > Choose whatever options you would like for your .exe (password protect source code, automatically close console after .exe runs, etc.).
Yes, there is a option with PS2EXE to create such *.exe Files.
Usage
The whole thing is really simple and well explained nevertheless
here is a snippet of it:
C:\Scripts\PS2EXE\PS2EXE_0.5.0.0.0\ps2exe.ps1
-inputFile C:\Scripts\ExampleEXE\example.ps1
-outputFile C:\Scripts\ExampleEXE\example.exe -sta -noConsole -runtime40 -verbose -x64
The only bad thing is that the project is depreciated. No Updates or new Versions since 2015.
EDIT:
This projected has been picked up and is being maintained by a new person now. You can find the updated code here, last updated 01/04/2018 as of this edit.
https://gallery.technet.microsoft.com/scriptcenter/PS2EXE-GUI-Convert-e7cb69d5
Version Information
For adding and editing the version information use something like VERPATCH.
UPDATE 2019
Shout out to a git-repo which is called PythonEXE.
It demonstrates how to create an executable from a Python project and also provides a YouTube Step-By-Step Guide.
I understood your question as "Can my PowerShell script generate an executable?" That is what I was trying to do when I found this post. That is possible using the Add-Type command.
Add-Type -TypeDefinition #"
using System;
class Program {
public static void Main(string[] args) {
Console.WriteLine("Hello World!");
}
}
"# -CompilerParameters #{
"GenerateExecutable" = $true
"OutputAssembly" = "test2.exe"
}
PrimalScript from Sapien will generate an exe from a PowerShell script. The machine one which the executable is run must have PowerShell installed.
The solution I found best to distribute a PowerShell script as exe was to wrap it in a NSIS executable.
I write a .nsi script like this:
Name "Maintenance task"
OutFile "maintenance.exe"
ShowInstDetails show
Section "Main"
;Executes the "script-to-run.ps1" PowerShell script
InitPluginsDir
SetOutPath "$pluginsdir\MyOrg" ;It is better to put stuff in $pluginsdir, $temp is shared
;extract the .ps1 and run it, collecting output into details.
File script-to-run.ps1
nsExec::ExecToLog 'powershell -inputformat none -ExecutionPolicy RemoteSigned -File "$pluginsdir\MyOrg\script-to-run.ps1" '
SetOutPath $exedir
SectionEnd
I just have to compile the script to an exe with the NSIS toolchain, and it will run on any OS that has PowerShell, no matter what is the execution policy.
This was inspired by this question How to call PowerShell in NSIS.
There's a project called Portable PowerShell that is still in beta after a couple of years ... might be worth looking at for your needs.
http://shelltools.wik.is/Portable_PowerShell