VS2010 Setup Project run .exe or C# code on program unistall - visual-studio-2010

I am working on Visual Studio 2010. I've developed a WPF C# application which will be deployed to customers trough a website. After downloading and installing it they will have to register the application which will send info to the server and also write some registry entries.
I have created a Setup Project in order to create the installer package for my app. It installs correctly and auto-registers the app in Control Panel/Add Remove programs, which is great if the user wishes to uninstall the program some day.
Question: How can I force the uninstaller to execute some code or launch another application in order to send info to the website that the current user is deactivating and uninstalling the program?
The Problem: If the user uninstalls the program as it is right now, only the files are deleted, and eventually the registry values, but the website will continue to think the user still has an active copy of the software and if he wants to download it again, the website would not let him do it.

You have to implement install actions, and run them in the appropriate set up event.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Deployment.WindowsInstaller;
namespace CustomAction1
{
public class CustomActions
{
[CustomAction]
public static ActionResult CustomAction1(Session session)
{
session.Log("Begin CustomAction1"); // Here you can write your own custom action code
return ActionResult.Success;
}
}
}
Select the CustomAction1.CA.dll file and add it to your Advanced Installer project
Go to Custom Actions Page, add a “New Installed Custom Action” with sequence from Add Custom Action Tab or the toolbar and select CustomAction1.CA.dll
In the "Function Name" field from the "Custom Action Properties" view select CustomAction1
Build the project and test it
http://www.advancedinstaller.com/user-guide/qa-c-sharp-ca.html

If your setup project is a Visual Studio setup project, then basically you just need to add a custom action for install and one for uninstall. This is old but still relevant - you can arrange to run a program at the end of the install and on an uninstall:
Getting Stsrted with Setup Projects

Related

TFS Client side hook

We have using TFS as version control. Currently the project management is done through our local PMS application. We have a requirement like when a team member check in the project, show a custom dialog for entering task details and update our local pms database. I want to show a custom dialog box that and/or after the check-in command is invoked. Also ensure that only successful submission of task details through this custom dialog boxes, we can checkin the projects.
Creating your own checkin policy
You can do this through a Check-in policy. Check out my blog post on interacting with the user on check-in. The basic trick is to raise a check-in warning which the user can click, on the click event, you can show UI to the user and use standard Windows Forms functionality to interact with your system.
The most basic example code can be found in this StackOverflow question.
My multiple Branch policy uses this trick to ask a user to confirm whether the user really wants to check in code to multiple branches at the same time without having to use the bypass policy validation checkbox.
A note on installing Checkin policies
After configuring the Team project to use the checkin policy, Visual Studio will complain when the policy is not installed on the machine upon checkin. There is no easy way to distribute checkin policies to ensure that every user has it installed on their machine, or will automatically get it installed from TFS.
In your checkin policy you can provide a URI to the location of the policy package through the IPolicyDefinition.InstallationInstructions. The package can be a .msi, a .vsix or just a zip file with a script to copy the assembly and add the required registry key. Visual Studio doesn't care about the method you choose. If your company hosts their own Visual Studio gallery, it could point to the download location there as well.
If you pick an .msi you could have them distributed through existing provisioning tools that might be available in your organisation.
Clients that do not have the policy installed will automatically trigger a warning on checkin that can only be dismissed by checking the "bypass checkin policy" checkbox. That permission can be revoked, requiring all users to setup their machine correctly.
There is one more way to distribute checkin policies, which is through the Visual Studio Power tools. By checkin in the policies in Source Control in a specific folder and telling the Power Tools to download custom binaries (work item controls and checkin polciies), they will be installed automatically. Since these tools are not installed by default, not configured by default they need about the same amount of work to make this scenario work for you.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Microsoft.TeamFoundation.VersionControl.Client;
namespace TFSPMSIntegration
{
[Serializable]
public class CheckForPMSDetails : PolicyBase
{
public bool pmsDetailsConfirmed=false;
private static frmPmsDetails _frm = null;
public override string Description
{
get { return "Remind users to add PMS details to their checkins"; }
}
// This is a string that is stored with the policy definition on the source
// control server. If a user does not have our policy plugin installed, this string
// will be displayed. We can use this as an opportunity to explain to the user
// how they might go about installing our policy plugin.
public override string InstallationInstructions
{
get { return "To install this policy, follow the instructions in CheckForPMSDetails.cs."; }
}
// This string is the type of our policy. It will be displayed to the user in a list
// of all installed policy types when they are creating a new policy.
public override string Type
{
get { return "Check for PMS Details"; }
}
// This string is a description of the type of our policy. It will be displayed to the
// user when they select our policy type in the list of policies installed on the system
// as mentioned above.
public override string TypeDescription
{
get { return "This policy will prompt the user to decide whether or not they should be allowed to check in."; }
}
// This method is invoked by the policy framework when the user creates a new checkin
// policy or edits an existing checkin policy. We can use this as an opportunity to
// display UI specific to this policy type allowing the user to change the parameters
// of the policy.
public override bool Edit(IPolicyEditArgs args)
{
// no configuration to save
return true;
}
// This method performs the actual evaluation. It is called by the policy framework at various points in time
// when policy should be evaluated. In this example, we invoke this method ourselves when various asyc
// events occur that may have invalidated the current list of failures.
public override PolicyFailure[] Evaluate()
{
if (!pmsDetailsConfirmed)
{
return new PolicyFailure[] {
new PolicyFailure("Please provide PMS Details about your checkin", this),
};
}
else
{
//frmPmsDetails frm = Application.OpenForms["frmPmsDetails"] as frmPmsDetails;
if(_frm!=null)
PendingCheckin.PendingChanges.Comment = _frm.txtDescription.Text;
return new PolicyFailure[0];
}
}
// This method is called if the user double-clicks on a policy failure in the UI.
// We can handle this as we please, potentially prompting the user to perform
// some activity that would eliminate the policy failure.
public override void Activate(PolicyFailure failure)
{
//The Singleton design pattern is used for maitntain only one instance of Form class(UI).But everytime we have passing new value to the custom policy class.
//Singleton approach needed , otherwise each time we click on policy message error, it will open multiple forms(UI)
if (_frm == null)
_frm = new frmPmsDetails(this);
else
_frm.CheckForPMSDetails = this;
_frm.WindowState = FormWindowState.Minimized;
_frm.TopMost = true;
_frm.Show();
_frm.ClearAll();
_frm.WindowState = FormWindowState.Normal;
_frm.BringToFront();
}
public void fn_Evaluate()
{
pmsDetailsConfirmed = true;
base.OnPolicyStateChanged(Evaluate());
}
// This method is called if the user presses F1 when a policy failure is active in the UI.
// We can handle this as we please, displaying help in whatever format is appropriate.
// For this example, we'll just pop up a dialog.
public override void DisplayHelp(PolicyFailure failure)
{
MessageBox.Show("This policy helps you to remember to add PMS details to your checkins.", "Prompt Policy Help");
}
}
}
Custom checkin policy deployment
First create class library for custom checkin policy with your
business logic
Deploy your check-in policies to customers using VSIX packages,
which are easy to install.
You want to build your custom check-in policy for VS 2010, VS 2012,
and VS 2013, then you’ll need a machine with all three of those
Visual Studio versions installed side-by-side, and you’ll need the
Visual Studio SDK installed for each one. The VSIX project
template only available after successful installation of Visual
Studio SDK.
Steps for creating check-in policies using VSIX packages in visual studio 2012 version
Create new project in visual studio 2012 version.
Select the Extensibility project template and choose the VSIX Project template.
In the VSIX project add a reference to your custom checkin policy project
You should add some items to the project like icon bitmap and License.txt, but only one of these is mandatory “policies.pkgdef”
You just add a text file, and name it policies.pkgdef. The name can be anything, but the extension should be “pkgdef”.
Edit the policies.pkgdef to look like this:
[$RootKey$\TeamFoundation\SourceControl\Checkin Policies]
"TFSPMSIntegration"="$PackageFolder$\TFSPMSIntegration.dll"
Part 1: This is a name you choose to give your policy package, and which will be the name of the key in the registry
Part 2: This is the assembly name as defined in the project properties, Application/Assembly name field.
At last you have to set up the vsixmanifest file, the one named source.extension.vsixmanifest.
Choose the Assets tab and add the “ policies.pkgdef” file using “Add new asset” window
You have to build the solution, and install the VSIX by double-clicking it. Note that it first takes effect when you restart Visual Studio.
*In order to disable and uninstall the package go to visual studio tools menu and choose “Extension and Updates“
Choose the extension and do the appropriate actions

Find Install directory and working directory of VSTO Outlook Addin; or any Office Addin

I created a VSTO Outlook Addin that uses a library Html2Xhtml.dll (.NET) which calls another Html2xhtml.exe by executing System.Diagnostic.Process.Start().
However, it fails to call Html2xhtml.exe (i think) because the working directory even when launched from Visual Studio is the current user My Documents folder. I have no control over the code in Html2Xhtml.dll so I cannot use absolute path; but I suppose I can change the working directory of the Add-in at runtime.
However, If I install this via ClickOnce or some other means where I do not know the install path the user is going to choose, how am I suppose to find my Html2xhtml.exe?
I found the answer here, full credits to robindotnet.wordpress.com.
//Get the assembly information
System.Reflection.Assembly assemblyInfo = System.Reflection.Assembly.GetExecutingAssembly();
//Location is where the assembly is run from
string assemblyLocation = assemblyInfo.Location;
//CodeBase is the location of the ClickOnce deployment files
Uri uriCodeBase = new Uri(assemblyInfo.CodeBase);
string ClickOnceLocation = Path.GetDirectoryName(uriCodeBase.LocalPath.ToString());
I've had a similar problem and solved it the same way as described by Christoph, I would also like to know whether there are any alternative ways of doing this but if you don't find anything here's an example
1)Create a custom actions library with the following InstallerClass
using System;
using System.Collections;
using System.ComponentModel;
using System.Configuration.Install;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using Microsoft.VisualStudio.Tools.Applications;
using Microsoft.Win32;
namespace Setup.CustomActions
{
[RunInstaller(true)]
public partial class AddCustomization : Installer
{
static readonly Guid solutionID = new Guid("d6680661-c31e-4c24-9492-5919dc0uagt5");
public override void Install(IDictionary stateSaver)
{
string installPath = Context.Parameters["installPath"];
if(!String.IsNullOrEmpty(installPath))
{
AddTemplateToAvailableTemplates(installPath);
}
base.Install(stateSaver);
}
public override void Rollback(IDictionary savedState)
{
}
public override void Uninstall(IDictionary savedState)
{
}
private void AddTemplateToAvailableTemplates(string installPath)
{
//The example below is very basic, put in checks to see whether the registry key already exists and so on
RegistryKey key = Registry.CurrentUser.OpenSubKey(#"Software\Microsoft\Office\14.0\Common", true);
RegistryKey acturisKey = key.CreateSubKey(#"Spotlight\MyAppInstallPath");
acturisKey.SetValue("InstallPath", installPath);h);
}
}
}
2)In the setup project create a key on the Install custom action which points to the install directory:
If you need more info or would like to download the source have a look at this msdn post by Open Xml MVP Wouter Van Wugt titled "Deploying a Visual Studio 2010 Tools for Office Solution Using Windows Installer"
That is a real problem I had to fight with for quite some time. The solution used in an AddIn I had to work with was to write the install dir into the registry and read the value from there. That way things brought along which could not be embedded into the exe could be found. This is not a good solution but it worked.
Why MS sticks to this stupid "security mechanism" of copying the DLL to a random directory is a secret they will probably never reveal.
While writing my comment I actually had an idea which I did not try so far: Make your installer copy the files you need later on to %appdir%\YourCompany\YourApplication\libs or some such. You should be able to find your stuff then during runtime.
Had the same issue for ClickOnce applications. Here is what you need to do to get the deployment path of the addin:
Add System.Deployment.Application reference in your application
next is to use this property to retrieve the deployment path:
ApplicationDeployment.CurrentDeployment.UpdateLocation.ToString()
and there you go!
For COM plugins System.Reflection.Assembly.Location doesnt stable deliver what we need.
But even if it's possible to save the installation directory anyhow in the registry, it's not neccessary. Because:
A COM plugin has usualy a ID. You can define it with the GuidAttribute.
During installation/registration of your plugin, informations about this assembly are stored under:
Computer\HKEY_CLASSES_ROOT\CLSID\{...myPlugin id ....}\InprocServer32
in attribute "Codebase" you find the path to your file.
e.g.: file:///C:/Program Files/myPlugin.dll

Getting started with Watin

I am trying to do the quick start example, I bring in the ref's using NuGet in VS2010, I scrape the sample code on the webpage, I see my NUnit Session window, I click the green arrow, but the browser doesn't get invoked (doesn't start). What am I missing?
using System;
using NUnit.Framework;
using WatiN.Core;
namespace FirstWatinProject
{
[TestFixture]
public class Class1
{
[Test]
[STAThread]
public void SearchForWatiNOnGoogle()
{
using (var browser = new IE("http://www.google.com"))
{
browser.TextField(Find.ByName("q")).TypeText("WatiN");
browser.Button(Find.ByName("btnK")).Click();
Assert.IsTrue(browser.ContainsText("WatiN"));
}
}
}
}
I am getting the following error in NUnit Sessions window;
SearchForWaitOnGoogle Failed: System.IO.FileNotFoundException: Could
not load file or assembly 'Interop.SHDocVw, Version=1.1.0.0,
Culture=neutral etc...
Okay, solved the error, it is as the following other overflow thread concludes;
WatiN System.IO.FileNotFoundException Interop.SHDocVw
BUT, a key action in the sequence is to build the class library project AFTER setting the Interop.SHDocVw dlls' 'Embed Interop Types' property to 'False';
Then you can hit the green arrow in NUNIT Sessions window and you will see the IE browser startup after a second or two. Simply click it and you will see whatever actions you have programmed.
God is in the details!
Setting the Interop.SHDocVw, Microsoft.mshtml dlls' 'Embed Interop Types' property to 'False';
Of course Visual Studio would give you warnings on how to amend.
The Use of Nugget Package Manager
Check for the availability of Interop.SHDocVw.dll,
Make sure that your project has a reference to Interop.SHDocVw.dll and the dll is present in the Bin/Release Folder depending upon how you are running..
Just select Build from the main menu...then select Configuration Manager. In the list select you project and change its Plateform 'Any CPU' etc to x86.
If you have only Only CPU option, you can use ... option when chosing platform and create new setting, that is for X86 platform and then choose it.
ref:
Change target to x86 in VS2010
I already had a reference to Interop.SHDocVw. The easiest fix for me was simply changing .NET from 4.5 to 3.5 in Visual Studio settings. After making the change, it worked fine.

Custom Action not working - Visual Studio Setup Project

In the past we have used Advanced Installer to build our .msi installers for a particular project. Our yearly license for Advanced Installer has expired, so to avoid the renewal cost, and because I think the same can be accomplished with Visual Studio, I am attempting to use a Visual Studio 2010 Setup Project to build my .msi.
For the most part, the installer I have built with Visual Studio works fine. However, one thing we need the installer to do is run a couple of .reg files to add a large collection of settings to the registry (It may be worth noting that this is old software that is only being maintained and updated until it is replaced entirely in the near future. It is not practical to change our method of storing settings). With Advanced Installer, we were able to execute a .cmd file as an "Install" Custom Action that would run these .reg files that were also included in the installation. VS Setup Projects have Custom Actions, but it appears that here they are required to be either .dll or .exe files, so I must find an alternative to using a .bat or .cmd file.
First, I tried adding a Command Line project to my solution that consisted only of the following lines in the main() method:
using (Process registryInput = Process.Start("regedit.exe", "/s Settings1.reg"))
{
registryInput.WaitForExit();
}
using (Process registryInput= Process.Start("regedit.exe", "/s Settings2.reg"))
{
registryInput.WaitForExit();
}
I added the Primary Output of this project to the "Install" folder of the "Custom Actions" editor. Tried to run the installer, but the command line process never seemed to run and no registry settings were installed. If I manually ran the command line executable from the application directory where it was installed, it added the registry entries as intended - so the problem is not with the code I'm using to call the .reg files.
I turned to MSDN and changed my solution to be modeled after their Custom Actions Walkthrough. I created a Class Library project (and removed my Command Line project) and added an Installer Class. Instead of starting up a browser using Microsoft's website URL in the Commit() method as shown in their example, I added the code above to the Install() method. Here is what I ended up with:
[RunInstaller(true)]
public partial class Installer1 : System.Configuration.Install.Installer
{
public Installer1()
{
InitializeComponent();
}
[System.Security.Permissions.SecurityPermission(System.Security.Permissions.SecurityAction.Demand)]
public override void Install(IDictionary stateSaver)
{
base.Install(stateSaver);
using (Process registryInput = Process.Start("regedit.exe", "/s Settings1.reg"))
{
registryInput.WaitForExit();
}
using (Process registryInput = Process.Start("regedit.exe", "/s Settings2.reg"))
{
registryInput.WaitForExit();
}
}
[System.Security.Permissions.SecurityPermission(System.Security.Permissions.SecurityAction.Demand)]
public override void Commit(IDictionary savedState)
{
base.Commit(savedState);
}
[System.Security.Permissions.SecurityPermission(System.Security.Permissions.SecurityAction.Demand)]
public override void Rollback(IDictionary savedState)
{
base.Rollback(savedState);
}
[System.Security.Permissions.SecurityPermission(System.Security.Permissions.SecurityAction.Demand)]
public override void Uninstall(IDictionary savedState)
{
base.Uninstall(savedState);
}
}
I added the Primary Output of this new Class Library project to the "Install" folder of the "Custom Actions" editor. Still, when I run the installer, the code does not appear to be executed and my registry settings are not added. I have tried this installer both set to "Install for all users" and "This user only".
Any help to either get this Custom Action working or an alternative method to get a .reg file to run on install will be greatly appreciated. Thank you in advance.
I just ran across this same issue, re: the custom action not being picked up by the installer. The resolution was to run Visual Studio as an administrator.
Even though I'm a full admin on my machine without any restrictions (AFAIK), the installer would never pick up the custom actions. As soon as I closed down Visual Studio and then restarted as an administrator (right click > run as administrator), the custom actions were immediately picked up by the installer.
I banged my head on the keyboard for a bit on this one - and only after putting my custom installation actions in the Constructor of the Installer Class made it run.
I followed the tutorial I found here: http://msdn.microsoft.com/en-us/library/d9k65z2d(v=VS.100).aspx
See if this link helps, it helped me:
Visual Studio 2008 Installer, Custom Action. Breakpoint not firing
Basically, code after the
base.Install(stateSaver);
is not getting executed. So put the base.Install(stateSaver); as the last line in the method.
This may seem obvious but it caught me out for a while, so might as well post it.
I was just right-clicking on the installer project and then "Install" and "Uninstall". However, you have to rebuild the Installer project after changing the code! (and probably the project with the installer class as well)

Create Custom Action to Start Application and Exit Installer

Thanks to StackOverflow I found out yesterday how to add a custom action to the Visual Studio Installer to start my program after an update. The problem I now face is that at the end of the installer the program does open but the installer never finishes until I exit my app.
Is there a way to ensure the app starts only after the user clicks finish on the MSI package?
Or the program starts at finish of installer but installer completes and exits?
I am running Visual Studio 2010 in case it matters.
After some Googling, I found out that the custom action for the Visual Studio Installer might need to point to an Installer Class. So I created a new project of type class in my solution. I deleted the class1.cs file and added an installer class to the new project with the following code (mental note: need to use security.permissions at some point):
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration.Install;
using System.Linq;
using System.Diagnostics;
using System.Security.Permissions;
namespace AppName
{
[RunInstaller(true)]
public partial class InstallerClass : System.Configuration.Install.Installer
{
public InstallerClass()
{
InitializeComponent();
}
public override void Commit(System.Collections.IDictionary savedState)
{
base.Commit(savedState);
System.Diagnostics.Process.Start(Context.Parameters["TARGETDIR"].ToString() + "application.exe");
// Very important! Removes all those nasty temp files.
base.Dispose();
}
}
}
After the InstallerClass was added, I right clicked on the installer project and selected Add > Project Output and added the installer class. I then right clicked on the installed project and did View > Custom Action. I added the installer class to both the Install and Commit folders (if you only add it to Commit, you will get an Error 1001: could not find file InstallState. because of the override commit, it will only run on commit. apparently InstallState is created at stage 2 so if if its not in both it will fail miserably).
You must add a CustomActionData entry. To do so, select the "Primary Output from InstallerClass" and go to the Properites tab. Paste the following in CustomActionData:
/TARGETDIR="[TARGETDIR]\"
After that was added the app runs properly when the install finishes and you can close the installer instead of waiting on the app to exit!
Just what I needed. Thank you Google for saving my bacon.
The one issue I noticed was the installer now creates multiple .tmp files and a .InstallState file in my ApplicationFolder. I am wondering if there is something in addition that needs to be added to the installer class to get rid of these useless files?
Figured out how to get rid of the temp files. Updated code with Dispose().
I followed the instructions here:
http://msdn.microsoft.com/en-us/library/d9k65z2d.aspx
..And got the "Error 1001: could not find file InstallState" error.
After reading ThaKidd's answer above, I realized that I would have to:
Add the installer class to both the Install and Commit folders.
Really important. Just leaving this here for future visitors (I would have added a comment if SO allowed me to...)
This is a step by step guide to answer the Question and clean up all files on an uninstall Event. I hope this can help people who are new to Visual Studio Installer Projects.
In your solution (.sln) you should have at least 2 projects. One being your program and the other one the setup. Given the Question this guide does not include basics on how to add a setup or the primary output(Here is a video if you need to catch up).
Follow these steps:
Right click on the main project (not the setup) -> Add -> Component -> Installer Class
Name the Component and click Add
Open the file click on switch to code view (or select ComponentName.cs -> ComponentName.Desinger.cs -> ComponentName -> ComponentName())
Take a look at the code below, add the using statements, the commit and uninstall method. Run the code.
Right click on the setup project -> View -> Custom Actions
Right click Install -> Add Custom Actions... -> Application Folder -> Primary Output from yourProject (Active) -> Ok -> Right click the added file and select properties Window (or F4) -> set CustomActionData to /TARGETDIR="[TARGETDIR]\" (one backslash at the end) and make sure that Installer Class is set to True
Repeat the above step for the Custom Actions Commit, Rollback and Uninstall
Rebuild the setup
Install the setup
You may need the System.Configuration.Install NuGet
Full Code
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration.Install;
using System.Diagnostics;
using System.IO;
namespace Your_Namespace
{
[RunInstaller(true)]
public partial class ComponentName : System.Configuration.Install.Installer
{
public ComponentName()
{
InitializeComponent();
}
//Source: https://stackoverflow.com/questions/3172406/create-custom-action-to-start-application-and-exit-installer
public override void Commit(System.Collections.IDictionary savedState)
{
base.Commit(savedState);
System.Diagnostics.Process.Start(Context.Parameters["TARGETDIR"].ToString() + "your main program.exe");
//Remove temp files
base.Dispose();
}
//Delete the file .InstallState (created when using custom actions in setup)
//Could also delete this after the install (protected override void onAfterInstall) but not tested
//Source: https://stackoverflow.com/questions/46786297/visual-studio-setup-project-remove-files-created-at-runtime-when-uninstall?rq=1
public override void Uninstall(IDictionary savedState)
{
base.Uninstall(savedState);
File.Delete(Context.Parameters["TARGETDIR"].ToString() + "your main program.InstallState");
//Remove temp files
base.Dispose();
}
}
}
Additional stuff for a Visual Studio Installer Projects (2017, 2019) that might save you a headache later:
Move the default installation from [ProgramFilesFolder] to [LocalAppDataFolder] so you don't have to ask for admin privileges every time a file is modified (View -> File System -> Properties of Application Folder -> Default location)
Shortcuts are missing the full context menu
Update removing some files especially shortcuts
The Uninstaller may not remove all (.dll) files. The only fix I know of is to change the ProductName in the Setup

Resources