Visual Studio Setup, Custom Action to copy the setup file - c#

I am developing a visual studio setup project. So far all has been good except uninstalling. The program did not create its own uninstaller, but instead the uninstall feature is inside of the setup.msi. So what I need to do is during the install, I need to copy the running setup.msi to the [TARGETDIR]. The easiest way I can think of is to use custom actions. I'm pretty lost on custom actions though, I don't understand where they go and every time I try to code one, its full of errors. Looking at other questions and answers, I have come up with the following:
[RunInstaller(true)]
public partial class CustomInstaller : System.Configuration.Install.Installer
{
public override void Install(System.Collections.IDictionary stateSaver)
{
base.Install(stateSaver);
string path = this.Context.Parameters["targetdir"];
// Do something with path.
}
}
But I don't understand it. I see how it gets targetdir as that gets passed via the customactiondata. But, RunInstaller isn't known, nor is System.COnfiguration.Install.Installer. Where am I supposed to put this .cs file to make it work? Very confused.

This class should be placed in your application project. I believe it can be included in any project whose DLL is packaged by the setup project, but it usually makes the most sense to put it in the application project.
Make sure your application project references System.Configuration.Install.dll. That will resolve the reference to Installer.
RunInstaller is in the System.ComponentModel namespace (System.dll), so make sure you have a using System.ComponentModel statement at the top of the class file.
This question may help you understand custom actions more:
Why won't my Setup Project Perform my Custom Registration Process

This might help you get started, although it doesn't cover installer classes.
https://www.simple-talk.com/dotnet/visual-studio/getting-started-with-setup-projects/
Error 2835 is a bogus error related to trying to show a dialog, probably the error dialog, so it's masking whatever went wrong, most likely your code.
Make a log using msiexec /I /l*v and look for your error before the 2835.
You pass that targetdir value to your custom action like an argument list, so you'd say /targetdir="[TARGETDIR]\" and that's case-sensitive and including the brackets. It's picky because it gets merged with other arguments.
Beware that your code is not running in an application context. It's a call from the msiexec process running with the system account and a working directory of sysetm32. That means full paths need to be specified and any use of a profile-specific item (like Desktop, user profile location) will fail.

Related

C# Starting a program from another program

I have this method
private void StartSDCBackupSet() {
using (Process p = new Process()) {
p.StartInfo.FileName = "SDCBackup";
try {
p.Start();
BackIcon.ShowBalloonTip(5000, "Backup", "Editor for settings startet", ToolTipIcon.Info);
} catch (Exception) {
MessageBox.Show("Program til settings blev ikke fundet");
}
}
}
According to the documentation I can find - and quite a lot of answers here -, it should start another program - SDCBackup.exe.
It doesn't. And I HAVE tried adding the .exe to the filename, but it does not make a difference. I get the messagebox with the message...
I have checked p and StartInfo and everything looks right. StartInfo.UseShellExecute is true.
Debugging reveals, that it is the line p.Start(); that produces the exception. And the Exception is a System.ComponentModel.Win32Exception with message:
File not found
Documentation says, that the code above does the excat same thing as Run in Windows menu, but it obviously does not.
If I write SDCBackup in the RUN section of Windows menu, SDCBackup.exe is started as it should.
(And SDCBackup.exe is a ClickOnce installation, that nobody really knows where to find - other than Windows itself...)
So why does my code not do the trick?
Use
p.StartInfo.FileName = "SDCBackup.exe";
Windows tests out known file types, and if it's an .exe (or any other known type, such as .bat, msc, scr, etc.) it runs it. As far as I know .NET does not do that.
You could try using Path.GetFullPath (https://msdn.microsoft.com/de-de/library/system.io.path.getfullpath(v=vs.110).aspx) but I think it should work with relative paths as well.
Also make sure the file is inside your debug/release folder (whatever folder your .exe that is starting SDCBackup is in), since Windows looks in known directories that are contained in the PATH variable (such as C:\windows\System32, etc), and .NET does not. If it is not in that folder, provide a fully qualified path and it should work.
It seems no one knows how to reference a ClickOnce installed application in another application. (Asked the question at Microsoft, and got no useful response there either.)
It seems ClickOnce apps are installed somewhere in the current users area, e.g.:
C:\Users\[user]\AppData\Local\Apps\2.0
and from there 3 randomly named folders down - and not the same folder names for different users; so there seem to be no way to solve this.
But I did get a way to create a regular installer from Microsoft, for Visual Studio C# solutions.
I used it to set the install path of my apps, so they can find it each other - and it functions.
Install projecttype Installer
and follow the explanation
It involves adding a Installer project to the solution.
I found, that in addition to the explanation,
you have to go to Options for the installer-project,
select **"Configuration Manager",
and set it to be included when building
I also found, that version control is not automatic. You must manually set version number - and for the sake of simplicity, this is a different format than the one Visual Studio uses. If you do set the version, it will automatically uninstall previous versions of the same app.

How to register a server on install and then unregister it on uninstall?

So I've got an OPC-DA server in my program. When the user first runs the program or (even better) installs the program I want just one line of code to be run: myServer.registerServer;. And similarly when they want to uninstall the program it should run the opposite command: myserver.unregisterServer.
Now I'm guessing to do this I'm going to have to do something with a command line argument? maybe check to see if the passed value is equal to something and then if so I can register/unregister accordingly. Then if the passed value matches neither it just continues my program like normal. Issue is I'm not quite sure how to work this into the installer I'm using. Speaking of, I'm using Visual Studio Installer and using their 'Setup Project' project. Can what I'm asking for be done with this or do I need to make a separate installer. I've never had to do anything like this so any information you guys have is really appreciated.
The way you'd do this with a Visual Studio setup is to run the program as an install custom action with the command line that registers it, and an uninstall custom action with the command line that unregisters it.
Ideally you wouldn't run the program at all. Many (if not most) installation programs know what the required registry entries are and they add them to the registry entries that (in Visual Studio's case) are in the Registry view in the IDE. This data is typically always static and can be added to the system without running code.

Changing source code in a class library seems to have no effect on application

I was given a project in which I am supposed to debug a problem in a Windows Forms application. I found where the problem is located but it is within a Class Library which is included as a component of my Windows Forms application solution.
How can I add/modify code in the Class Library project and actually run it live so that I can debug it? If I make any changes to the Class Library as is, the application ignores the changes and resorts to the original source code.
The only things contained in the Class Library's folder are plain source code files, some settings files, and a .vbproj. I just want to make changes to the Class Library and actually be able to debug them. If anyone could please explain what I have to do, it would be greatly appreciated!
Your application is not loading the assembly produced by compiling the class library. It is loading another copy from somewhere.
One quick way to find out where is to start the application from Visual Studio, break into the debugger and then bring up the Modules windows (Debug>Windows>Modules). Look for the class library in the Name column and check the Path.
If it is under C:\Windows\Microsoft.NET\assembly... then there is an older version being loading from the GAC. If it's another location, you will need to ensure the class library project output is going to that location.
Does the startup project have a project reference to the class library in the solution? You could always remove and re-add the reference to the class library in the startup application project and ensure you add it as a project reference.
Be careful though, there may be a good reason why this wasn't the case originally.
EDIT
A full explanation of how assemblies are located is way beyond the scope of an SO post - you'll need to study How the Runtime Locates Assemblies.
With no changes made to typical solution defaults, a library is most likely to be loaded from the same folder where the start-up executable is located. Setting a project reference to a library causes it to be compiled and copied to that project's bin folder - so make sure the startup project has a project reference to your class library project. (Right-click startup project and check Add References... dialog. The reference should come from the Solution section).
You'll need to examine the project property pages to see if something special has been configured.
99.99% of the time, building the WHOLE solution and hitting run should work. If it doesn't work, something is messed up in the solution and/or there is some kind of custom deployment set up.
There are simply so many ways to deviate from the default deployment that I just can't give specific guidance here; you best bet is to get someone knowledgeable who can take a look in person, or to whom you can send the source for inspection.
I just went to Project Properties ....Project Dependencies and checked(ticked ) the class /assembly(.dll) name... It worked for me. Now i dont need to run the class project for the changes to reflect in the Startup Project ..

Installed Win Service not displaying in Service Manager

I have a Windows Service created in c#.
It's relatively simple compared to some of the other ones that I've worked on.
I built a setup project to install it for some testing.
I added the primary output from the service project and all the dependencies were added correctly.
I went to View > Custom Actions and added my Primary output to Install, Commit, Rollback, and Uninstall.
The project built and I right clicked the project and clicked Install.
The installation came back successful, I can view the service on the control panel under Add/Remove programs, but when I go into the Service Manager... nothing...
Can anyone provide some insite or anything else that may cause a successfuly installed service to NOT display on the Service Manager.
Forgive me if this goes without saying, but you haven't mentioned what code you are executing in your custom actions. Your service assembly must have a class which derives from System.Configuration.Install.Installer, and that class must have the [RunInstaller(true)] attribute on it. Within that class, you will need to create an instance of System.ServiceProcess.ServiceInstaller and System.ServiceProcess.ServiceProcessInstaller, set the appropriate parameters on those instances, and add them to the Installers collection. The ServiceInstaller and ServiceProcessInstaller MSDN pages have a very basic example, but it should be enough to get you there if this is what is needed.
Make sure you had provided some value in "Display name" property.
Use the following command "sc query <service_name>" from command prompt to see whether your windows service got properly installed. If you are not sure on the service name use the following command "sc query state= all >c:\ServicesList.txt" after executing this command search the ServicesList.txt in your C:\ drive.
If that too doesn't work try looking for the service name in registry under HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services
You said you added your primary output to Install, etc. But did you create an Installer derived class to do the actual installing of the windows service? I'm not talking the setup project itself, but in your project there should be an installer class that actually does the service install for you.
I had a post on my blog about creating a framework for easy installable services, it has examples on creating the isntaler class.
http://blackrabbitcoder.net/archive/2010/10/07/c-windows-services-2-of-2-self-installing-windows-service-template.aspx
In my case, solution of the problem was simple, i forgot to add access modifier 'Public' to the class. After adding access modifier, service is visible now in services list.

Detect silent install in .NET Custom Action

How do you detect that the install is running in silent mode?
I have a custom application that I've added a .msi setup project. I run the .msi file with the /qb switch, and in my custom installer c# code I would like to be able to detect this.
Edit: nobugs says to test the UILevel property. How do I access the UILevel property from a class derived from the System.Configuration.Install.Installer class?
Taking the hint from nobugz, I did the following:
On the Custom Actions view of the .msi setup project, I added the following to my CustomActionData (to pass the UILevel through to my custom installer):
/UILevel="[UILevel]"
Within my C# code for the code derived from base class Installer, I added code to get the value:
string uiLevelString = Context.Parameters["UILevel"];
It was then simple to parse the string for an int value. If the value is <= 3, it is a silent install.
First I would point out that InstallUtil is a very bad pattern. They run out of process, tatoo the process with a CLR version and when they fail they raise a 1001 error modal dialog even during a silent install.
Instead you should use WiX's DTF pattern.
MsiGetMode ( Session.Mode ) is limited during deferred execution so you will have to serialize and deserialize the UILevel.
http://www.msifaq.com/a/1044.htm

Categories

Resources