Force remove the user.config during uninstall? - c#

how do i code the custom action during the uninstall?
Would it require a batch file?
thanks!

The user.config data gets stored in the %APPDATA%\ProjectName folder.
If you want to remove the user.config data when you uninstall then you can just use the System.IO.Directory.Delete("%APPDATA%\ProjectName");
Note: You can get the installed path using the following Context.Parameters["assemblypath"] this is the path that the user selects to install the project.

This worked for me, based on the answer above. For my app I only allow per user installs, not "all users" so I don't have to worry about uninstalling for multiple users, or for users other than the current user running uninstall. If you allow "all user" installs, you'll have some issues to work out.
public override void Uninstall(System.Collections.IDictionary savedState)
{
String p = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "CompanyName");
string[] ss = Directory.GetDirectories(p, "ProjectName.*");
foreach (string s in ss)
{
if(MessageBox.Show("Delete " + s + "?","Delete Settings?",MessageBoxButtons.YesNo) == DialogResult.Yes)
Directory.Delete(s, true);
}
base.Uninstall(savedState);
}
I'm not actually going to leave the prompt in there, that is just for testing to make sure I'm not deleting the wrong folders on my PC.. until this code has been fully tested. CompanyName and ProjectName need to be changed to match your project.
I might add a page to uninstall UI or just a prompt to ask if they want to delete all settings (so they can choose not to if they are going to reinstall).

var filePath = Environment.ExpandEnvironmentVariables(#"%userprofile%\APPDATA/ProjectName");
System.IO.Directory.Delete(filePath );

You can write a custom action to trigger an executable while install or uninstall or both.
for eg: create an .exe which will delete the user.config folder. Add this exe in binary table. Add an entry in CustomAction table with Source being the foreign key to name in Binary table and TArget being the actual exe file name and type = 2. Now add this action in InstallExecuteSequence with whatever sequence order you wish to trigger the .exe in installation process.

1) Create a custom action (article includes pictures)
2) Handle the Uninstall event of the custom action
[System.Security.Permissions.SecurityPermission(System.Security.Permissions.SecurityAction.Demand)]
public override void Uninstall(IDictionary savedState)
{
try
{
string path = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
path = Path.Combine(path, "{Your application folder name}");
Directory.Delete(path, true);
}
catch(Exception)
{
}
base.Uninstall(savedState);
}

Related

Using WixSharp how to get the proper InstallDir path during a Custom Action which is executed at uninstall

I created a msi Setup with WiX using WixSharp. It includes several Custom Actions.
For instance during installation time I am executing some batch files which are installing and starting a service. And during uninstall it should stop and uninstall the service again.
var dir = new InstallDir(#"%ProgramFiles%\MyCompany\MyProduct",
new Files(#"..\..\..\AllMyFiles\*.*"));
var project = new Project("MyProduct", dir) {
GUID = new Guid("7f22db65-2b23-4df2-b2b2-495f2d369c3d"),
Version = new Version(1, 0, 0, 0),
UI = WUI.WixUI_InstallDir,
Platform = Platform.x64
};
project.Actions = new WixSharp.Action[] {
new ElevatedManagedAction(CustomActions.InstallService,Return.check, When.Before, Step.InstallFinalize, Condition.NOT_Installed),
new ElevatedManagedAction(CustomActions.StartService,Return.check, When.After, Step.PreviousAction, Condition.NOT_Installed),
new ElevatedManagedAction(CustomActions.StopService,Return.check, When.Before, Step.RemoveFiles, Condition.Installed),
new ElevatedManagedAction(CustomActions.UninstallService,Return.check, When.After, Step.PreviousAction, Condition.Installed)
};
Now here comes the crucial part. I need to execute a batch file during install and uninstall which is located somewhere in INSTALLDIR:
[CustomAction]
public static ActionResult StartService(Session session) {
string installDir = session.Property("INSTALLDIR"); //<--this works on install even when using a custom path
string workingDir = Path.Combine(installDir, #"\SomePathToTheBatchFile");
RunCmdMethode(workingDir, "something.bat -some arguments");
return ActionResult.Success;
}
[CustomAction]
public static ActionResult UninstallService(Session session) {
string installDir = session.Property("INSTALLDIR"); //<--this does not give back the right path on uninstall in case the default path was changed during installation
string workingDir = Path.Combine(installDir, #"\SomePathToTheBatchFile");
RunCmdMethode(workingDir, "something.bat -some arguments");
return ActionResult.Success;
}
Everything runs smoothly when using the default path for installation. But if I change the default install path during installation to some custom path the installation step properly finds the .bat and executes it but during uninstall it searches for the .bat file in the default folder. Although the Uninstaller properly removes the files on the correct location. So the custom install path must be saved somewhere. How do I access it properly?
I finally could solve the problem by myself and with some help of Oleg (https://github.com/oleg-shilo/wixsharp/issues/486).
Since session.Property("INSTALLDIR") should actually work and so I did not make a mistake at that point, I could figure out the root cause, which is setting the IsInstallDir property to true by using the InstallDir class instead of the Dir class. It overwrote the INSTALLDIR property when uninstalling back to the hard coded default path.
This explains why the setup worked fine as long as using the default path and also why it worked for all the install-custom steps even when using a custom path but not for uninstalling anymore. The reason tho, why I set the IsInstallDir property to true in the first place is because of some weird behavior when adding all the files to the setup using wildcards. As long as there are multiple files and folders in the source directory it would work just as expected, getting all paths right and so on. But once the source folder only contains a single folder inside which then contains the rest of the setup files within, it sets the inner folder to be the new root folder (kinda strange but once you know about this behavior things start making sense) and so screws up many necessary paths. Using InstallDir instead of Dir fixed that.
I might put some work into restructuring the whole thing (if this is even possible in my use case), but for now simply adding a readme file on the same level as the single inner folder solves that problem and that way I could go back using Dir in the first line:
var dir = new Dir(#"%ProgramFiles%\MyCompany\MyProduct",
new Files(#"..\..\..\AllMyFiles\*.*"));
That happens because you call the action "after" the uninstall. it should be "When.Before"
new ManagedAction(CustomActions.UninstallService,Return.check, When.Before, Step.InstallFinalize, Condition.Installed)

How can i save files in folder within the IIS of outside of the Application folder in asp.net

In my web application, i have some files those are saving within application it's creating a folder for saving files but i need to save those file outside of the application and inside of IIS.how can i do this?
With in application Folder we are using below code
Server.MapPath(Path)
For Saving in IIS How can i Write?
Thank you
you need to create a virtual directory that points ti the folder outside.
Go to IIS right click on your website. click on Add Virtual directry from the menu.Give an alias for the directory select your desired folder and you are done. it will consider this outside folder as an internal folder and work the same way. check this link How to: Create and Configure Virtual Directories in IIS 7.0
Disclaimer: but you will have to do this after hosting to iis i.e publishing. while using visual studio in dev environment i.e debugging it will store in internal directories only
Edit: for creating virtual directories this is the code. I have not tested its validity.
static void CreateVDir(string metabasePath, string vDirName, string physicalPath)
{
// metabasePath is of the form "IIS://<servername>/<service>/<siteID>/Root[/<vdir>]"
// for example "IIS://localhost/W3SVC/1/Root"
// vDirName is of the form "<name>", for example, "MyNewVDir"
// physicalPath is of the form "<drive>:\<path>", for example,"C:\Inetpub\Wwwroot"
try
{
DirectoryEntry site = new DirectoryEntry(metabasePath);
string className = site.SchemaClassName.ToString();
if ((className.EndsWith("Server")) || (className.EndsWith("VirtualDir")))
{
DirectoryEntries vdirs = site.Children;
DirectoryEntry newVDir = vdirs.Add(vDirName, (className.Replace("Service", "VirtualDir")));
newVDir.Properties["Path"][0] = physicalPath;
newVDir.Properties["AccessScript"][0] = true;
// These properties are necessary for an application to be created.
newVDir.Properties["AppFriendlyName"][0] = vDirName;
newVDir.Properties["AppIsolated"][0] = "1";
newVDir.Properties["AppRoot"][0] = "/LM" + metabasePath.Substring(metabasePath.IndexOf("/", ("IIS://".Length)));
newVDir.CommitChanges();
}
else
}
catch (Exception ex)
{
}
}
Normally you can not create a folder outside the root path i.e. if you have your application in say C:\inetpub\testapp you can only create a folder inside testapp. This restriction is for security reason where a web server is not supposed to allow access to anything above root folder.
Moreover it's not recommended to write any folders/files in the root folder as writing to root folder cause appdomain to recycle after certain number of writes (default is 15) causing session loss. See my answer here.
However there is a workaround
Add a path of your server to web.config and then fetch it in your code.Use something like below in the appsettings section of web.config
<add key="logfilesPath" value="C:\inetpub\MyAppLogs" />
Create a folder of above path and add Users group to your folder and give that group full permission (read/write). (Adding permission is very important)
In your code you can fetch as below
string loggerPath = (ConfigurationManager.AppSettings["logfilesPath"]);
Hope this helps

Directory.CreateDirectory access to path is denied?

I have server-client application, it's a file manager
my problem is when I go inside a folder which requires access control like system folders, it becomes to read-only, but I need to move/delete or create new folder, how can I get the permission to do that?
here's how I create a new folder at the server side
public void NewFolder(string path)
{
try
{
string name = #"\New Folder";
string current = name;
int i = 0;
while (Directory.Exists(path + current))
{
i++;
current = String.Format("{0} {1}", name, i);
}
Directory.CreateDirectory(path + current);
Explore(path); //this line is to refresh the items in the client side after creating the new folder
}
catch (Exception e)
{
sendInfo(e.Message, "error");
}
}
There are often directories on a drive that even a user with administrator privileges cannot access. A directory with a name like "HDDRecovery" is quite likely to be troublesome like this. Surely it contains sensitive data that helps the user recover from disk failure. Another directory that fits this category is "c:\system volume information", it contains restore point data.
An admin can change the permissions on folders like this. But of course that doesn't solve the real problem nor is it a wise thing to do. Your user can't and shouldn't. Be sure to write code that deals with permission problems like this, simply catch the IOExeption. Keep the user out of trouble by never showing a directory that has the Hidden or System attribute set. They are the "don't mess with me" attributes.
If you want to remove directory read-only attribute use this: http://social.msdn.microsoft.com/Forums/en/vblanguage/thread/cb75ea00-f9c1-41e5-ac8e-296c302827a4
If you want to access system folders you can run your program as local administrator.
I had a similar problem (asp.net MVC vs2017) with this code:
Directory.CreateDirectory("~/temp");
Here is my solution:
// Create path on your web server
System.IO.Directory.CreateDirectory(System.Web.HttpContext.Current.Server.MapPath("~/temp"));
I also ran into an issue similar to this, but I was able to manually navigate through Windows Explorer and create directories.
However, my web app, running in VS on my laptop, hosted through my local IIS and not the built-in IIS deal for VS, was triggering the Access Denied issue.
So when I was hitting the error in code, I drilled down to glean more data from the System.Environment object and found the user, which of course was the App Pool that my app was running under in IIS.
So I opened IIS and opened the Advanced Settings for the app pool in question and changed the Identity to run under Network Service. Click OK. "cmd -> iisreset" for good measure. Try the app again, and SUCCESS!!!!
I had the same issue when creating a directory. I used DirectorySecurity as shown below:
DirectorySecurity securityRules = new DirectorySecurity();
securityRules.AddAccessRule(new FileSystemAccessRule(#"Domain\AdminAccount1", FileSystemRights.Read, AccessControlType.Allow));
securityRules.AddAccessRule(new FileSystemAccessRule(#"Domain\YourAppAllowedGroup", FileSystemRights.FullControl, AccessControlType.Allow));
DirectoryInfo di = Directory.CreateDirectory(path + current, securityRules);
Also keep in mind about the security as explained by Hans Passant's answer.
Full details can be found on MSDN.
So the complete code:
public void NewFolder(string path)
{
try
{
string name = #"\New Folder";
string current = name;
int i = 0;
while (Directory.Exists(path + current))
{
i++;
current = String.Format("{0} {1}", name, i);
}
//Directory.CreateDirectory(path + current);
DirectorySecurity securityRules = new DirectorySecurity();
securityRules.AddAccessRule(new FileSystemAccessRule(#"Domain\AdminAccount1", FileSystemRights.Read, AccessControlType.Allow));
securityRules.AddAccessRule(new FileSystemAccessRule(#"Domain\YourAppAllowedGroup", FileSystemRights.FullControl, AccessControlType.Allow));
DirectoryInfo di = Directory.CreateDirectory(path + current, securityRules);
Explore(path); //this line is to refresh the items in the client side after creating the new folder
}
catch (Exception e)
{
sendInfo(e.Message, "error");
}
}
My suspicion is that when you are running the application in client/server mode, the server portion needs to be running as Administrator, in addition to possibly removing read-only or system flags, to be able to do what you want.
That said, I agree with #HansPassant- it sounds like what you are trying to do is ill-advised.
Solved:
Directory created on remote server using below code & setting.
Share folder and give the full permission rights also in Advance
setting in the folder.
DirectoryInfo di = Directory.CreateDirectory(#"\\191.168.01.01\Test\Test1");
Test is destination folder where to create new Test1 folder(directory)

How to build an installer to update an ASP.NET's web.config, DLL, files, etc

I have an add-on for a commercial ASP.NET website. My add-on requires people to merge entries into their web.config, add/overwrite existing files, and add some DLL files to the bin folder.
Is there a good and safe way to create an installer than can do this with a wizard type of installation? It would really help non-technical people install the add-on easily. Maybe even a web-based installer would be good?
Any help or suggestions would be greatly appreciated.
Had a similar problem...
Web.Config
Created a .NET command line program that you can call from your installer passing it the web.config path and other args to match what I'm trying to do
In the command line program you can then modify the web.config to your needs... Below is an example of setting a connection string & the stmp from address in a web.config
public static void SetConnectionString(string name, string connString, string webConfigPath)
{
string directory = System.IO.Path.GetDirectoryName(webConfigPath);
VirtualDirectoryMapping vdm = new VirtualDirectoryMapping(directory, true);
WebConfigurationFileMap wcfm = new WebConfigurationFileMap();
wcfm.VirtualDirectories.Add("/", vdm);
System.Configuration.Configuration webConfig = System.Web.Configuration.WebConfigurationManager.OpenMappedWebConfiguration(wcfm, "/");
webConfig.ConnectionStrings.ConnectionStrings[name].ConnectionString = connString;
webConfig.Save();
}
public static void SetFromAddress(string email, string webConfigPath)
{
string directory = System.IO.Path.GetDirectoryName(webConfigPath);
VirtualDirectoryMapping vdm = new VirtualDirectoryMapping(directory, true);
WebConfigurationFileMap wcfm = new WebConfigurationFileMap();
wcfm.VirtualDirectories.Add("/", vdm);
System.Configuration.Configuration webConfig = System.Web.Configuration.WebConfigurationManager.OpenMappedWebConfiguration(wcfm, "/");
System.Net.Configuration.MailSettingsSectionGroup mailSettings = (System.Net.Configuration.MailSettingsSectionGroup)webConfig.GetSectionGroup("system.net/mailSettings");
mailSettings.Smtp.From = email;
webConfig.Save();
}
Installer
I used NSIS (http://nsis.sourceforge.net/Main_Page). Use HM NIS Edit as a good starting point as it has a wizard that will generate scripts for you. From there you can modify up the scripts to your needs. In my case I called my command line program after the files where installed. Example NSIS script below.
Section "My Config Wizard" SecWizard
ExecWait '"$INSTDIR\Bin\My.Config.Wizard.exe" "$INSTDIR"'
Return
SectionEnd
Good luck! Need more examples just hit me up. :P
The web.config is the tricky part. Your first installer will deploy an XML file and then a user will change something in it. Meanwhile you have another build where the developer makes changes to the XML and now the installer has to try to figure out how that should merge all back together.
Out of the box, it can't.
2 strategies that I've used over the years:
1) Have the installer smart enough to pick out key pieces of information from the xml before replacing the xml. Then apply the information back.
2) Design your software to have 2 XML files. One that the installer can safely always overwrite and the other to act as an override that the user can modify safely.

How can I get another application's installation path programmatically?

I'd like to know where the installation path for an application is. I know it usually is in ...\Program Files... but I guess some people install it in different locations. I do know the name of the application.
Thank you.
The ideal way to find a program's installation path (on Windows) is to read it from the registry. Most installers will create a registry key for that program that contains the installation path. Exactly where this key is and what it will be named varies depending on the program in question.
To find if the program has a key in the registry, open 'regedit' and use the Edit > Find option to try and locate a key with the program name. If such a key exists, you can read it using the RegistryKey class in the .NET Framework library.
If the program does not have a registry key then another option is just to ask the user to locate the .exe file with the OpenFileDialog, although this is obviously not ideal.
Many (most?) programs create an App Paths registry key. Have a look at
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths
If you know the application in question (as compared to any application) registry key is the probably the best option (if one exists).
The install might put in its own custom "install path key" somewhere (so do a find as Fara mentioned) or it might be in the uninstall section for installed programs, so you could check:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall
But be aware that any new version of an install could change the key it writes out, both for a custom key or for the uninstall entry. So checking the registry should probably be only for a known install\version.
tep
Best way is to use Installer APIs to find the program location.
You can write a Managed wrapper over the APIs
Search for MsiGetProductInfo
Reference: http://msdn.microsoft.com/en-us/library/aa369558(VS.85).aspx
You can use MSI (I wrote a C# wrapper for it here https://github.com/alialavia/MSINet). Here is a simple example:
var location = "";
foreach (var p in InstalledProduct.Enumerate())
{
try
{
if (p.InstalledProductName.Contains("AppName"))
{
location = p.InstallLocation;
break;
}
}
catch { }
}
Take a look in the registry.
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\
or
HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\
Each of the above contain a list of sub-keys, one for each installed application (as it appears, for example, in the "Programs and Features" applet)
You can search for your application there, or if you know the product code, access it directly.
public string GetInstallPath(string applicationName)
{
var installPath = FindApplicationPath(#"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall", applicationName);
if (installPath == null)
{
installPath = FindApplicationPath(#"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall", applicationName);
}
return installPath;
}
private string FindApplicationPath(string keyPath, string applicationName)
{
var hklm = Registry.LocalMachine;
var uninstall = hklm.OpenSubKey(keyPath);
foreach (var productSubKey in uninstall.GetSubKeyNames())
{
var product = uninstall.OpenSubKey(productSubKey);
var displayName = product.GetValue("DisplayName");
if (displayName != null && displayName.ToString() == applicationName)
{
return product.GetValue("InstallLocation").ToString();
}
}
return null;
}

Categories