How can I check if some IIS modules are present - c#

I am working on an installer and am looking to programatically check in IIS (7 and above) to see if certain modules are present.
This will have to work on Windows server 2008 R2 and above.
I am looking for AppWarmupModule and DynamicCompressionModule.
Any info or suggestions would be much appreciated.

Load the contents of the file:
C:\Windows\System32\inetsrv\config\applicationHost.config
You can put it into a XML document and use xPath to find
<configuration><system.webServer><globalModules><add name="DynamicCompressionModule"
or string-parse or Regex the content otherwise to find out whether the strings you are looking at are present.

Solving similar problem for IIS10, but also for installation to remote servers. Direct reading xml file is not solution for me.
In my case, I need to find if AspNetCoreModuleV2 is installed or not, but this should works for every module.
Based on Microsoft docs I used approach below. It returns list of globalModules in applicationHost.config for selected server. (with using Microsoft.Web.Administration)
internal static ConfigurationElementCollection GetModules(string remoteServerName = null)
{
ServerManager mgr;
mgr = string.IsNullOrEmpty(remoteServerName) ? new ServerManager() : ServerManager.OpenRemote(remoteServerName);
Configuration config = mgr.GetApplicationHostConfiguration();
ConfigurationSection globalModulesSection = config.GetSection("system.webServer/globalModules");
ConfigurationElementCollection globalModulesCollection = globalModulesSection.GetCollection();
return globalModulesCollection;
}
And then simple enumerate:
ConfigurationElementCollection modules = GetModules();
foreach (var item in modules)
{
System.Diagnostics.Debug.WriteLine(item.Attributes["name"].Value);
// do whatever you want
}

Related

Readout mailroot-path for IIS 6 & 7 using c#

I am trying to read out the mailroot-path for the Drop and Pickup folders for my SMTP Virtual Server Domain. I currently need this for domains created with IIS 6.0 but in the long run will also need my Application to be compatable with IIS 7. Can anybody give me some pointers on how to do this?
Thanks!
Further details:
What I have:
SMTP Virtual Server Domain with a local alias: mydomain
What I want:
The mailroot path (default: C:\inetpub\mailroot) where the Badmail, Drop, Pickup and Queue folders are to be found. If that is not possible, then at least the path to the Drop folder
Why do I want this?
E.g. using IIS 6.0, users can change the Drop directory to a user specific location. I therefore do not want to hardcode the path, but read it out using c#.
I have tried to find this information by:
Reading out virtual directories
Checking the iisWebSite Properties
SmtpDeliveryMethod.PickupDirecotryFomIis
System.Net.Mail.IisPickupDirectory.GetPickupDirectory() --> cannot find Method
Reading the smtp.PickupDirecotryLocation into a string --> returns an empty string
Would really appriciate any help/ideas!
I am not sure but this might help you. As both IIS6 and IIS7 have same kind of setup.
http://www.kbcafe.com/articles/HowTo.SMTP.CSharp.pdf
The information you are looking for is in IIS metabase. There are managed classes to access the metabase, but you can also just read file directly. Here's a sample code:
public static string GetSmtpPickupFolder()
{
var doc = new XmlDocument();
var fileName = Path.Combine(Environment.GetFolderPath(
Environment.SpecialFolder.Windows), #"System32\inetsrv\MetaBase.xml");
if (!File.Exists(fileName))
{
return null;
}
doc.Load(fileName);
var nsMgr = new XmlNamespaceManager(doc.NameTable);
nsMgr.AddNamespace("c", doc.DocumentElement.NamespaceURI);
var node = doc.SelectSingleNode(
"/c:configuration/c:MBProperty/c:IIsSmtpServer", nsMgr);
if (node == null)
{
return null;
}
var attr = node.Attributes["PickupDirectory"];
if (attr == null)
{
return null;
}
return attr.Value;
}
Make sure that you run it as 64 bit application, otherwise file system redirection will send you to c:\windows\SysWOW64 instead

Check if there is any kind of PDF Reader installed

I have a Help function in my Application, that consists of one webbrowser control. That webbrowser control gets filled with a .pdf file, the source for that .pdf file is our own website.
The problem is, that not everyone will have a PDF Reader installed on their machine, so I want to check whether one is installed: Yes or No. I searched the internet and I mostly saw that users on Stackoverflow where wanting to check if Adobe Reader was installed, that is not what I want. I need to know IF there is a PDF Reader somewhere installed on the machine.
I did find the following code, that can possibly help me:
public void CheckPdfReaderAvailable()
{
RegistryKey key = Registry.ClassesRoot.OpenSubKey(".pdf");
Assert.IsNotNull(key);
}
As I look at the above code, my thoughts are that the code checks if the registry does know the .pdf format, but I'am not sure.
Can somebody tell me how to use the code above or provide me an example, about how I should take down this problem?
Thanks in advance!
EDIT:
The following answer helped my out: https://stackoverflow.com/a/774482/1661209
Another way to solve this problem, is to add a pdf reader lite to the prerequisites and make the users install that first, you don't have to check for a pdf Reader, because you know one is installed then, if it isn't you could say it is the mistake of the user that they can't use the help function, because you offered them a way to install the pdf reader easily using the published project.
Apart from whether it is useful to know or not, you could probable check the following registry key:
HKEY_CLASSES_ROOT\MIME\Database\Content Type\application/pdf
This will have an entry CLSID, which points to the class ID of the default application.
If the registry key or CLSID value is not present, then the MIME type is unknown, or there is no default application to handle the MIME type application/pdf files.
You can query the registry directly but the recommended solution is to use the IQueryAssociations interface to see if there is a program registered to open pdf's. An example can be found on pinvoke.net.
C# implementation of the approach suggested by John Willemse (won't recognize Edge as default viewer on non-N version of Windows 10) :
private bool CanOpenPDFFiles
{
get
{
bool CLSIDpresent = false;
try
{
using (Microsoft.Win32.RegistryKey applicationPDF = Microsoft.Win32.Registry.ClassesRoot.OpenSubKey(#"MIME\Database\Content Type\application/pdf"))
{
if (applicationPDF != null)
{
var CLSID = applicationPDF.GetValue("CLSID");
if (CLSID != null)
{
CLSIDpresent = true;
}
}
}
}
catch (Exception)
{
}
return CLSIDpresent;
}
}

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.

Sharepoint web.config changes using SPWebConfigModification

I've written a sharepoint application that needs to change web.config
I have a feature that is supposed to make all these configurations. The code for that feature is like this:
SPSite site = properties.Feature.Parent as SPSite;
List<SPWebConfigModification> modifications = new List<SPWebConfigModification>();
modifications.AddRange(CustomErrorsModeConfig.Modifications);
webConfigModificationHelper.AddWebConfigModifications(site.WebApplication, modifications);
CustomErrorsModeConfig.Modifications property contains this code:
public static SPWebConfigModification[] Modifications = {
new SPWebConfigModification()
{
Owner = WebConfigModificationOwner,
Name = "mode",
Type = SPWebConfigModification.SPWebConfigModificationType.EnsureAttribute,
Path = "system.web/customErrors",
Sequence = 0,
Value = "Off"
}
};
Then finally the webConfigModificationHelper.AddWebConfigModifications method:
foreach (SPWebConfigModification modification in modifications)
{
webApp.WebConfigModifications.Add(modification);
}
webApp.Farm.Services.GetValue<SPWebService>().ApplyWebConfigModifications();
webApp.Update();
The problem is that I keep getting this error:
Name cannot begin with the ''' character, hexadecimal value 0x27. Line 1, position 1453
Could this be a problem with the web.config before I try to apply my changes ?
Could the SPWebConfigModification property be incorrectly defined ?
Is there some glitch in my code that leads to this error ?
Might there be some property I am missing (e.g. web.AllowUnsafeUpdates) ?
Some sharepoint site configuration ?
I've been trying to solve this issue for some time now with no luck :( Any ideas ?
I can recommend using stsadmwebconfig for making changes to web.config files. I've implemented this in many features and it has always been a pain, especially while developing. Using this tool makes it a lot easier.
Ive seen this before when the file format is not correctly set between the file and the declaration.
Open the web.config file into a advanced text editor (Notepad++ or Visual Studio) and manually force the file type to match what is specified. Usually its going to be UTF-8.
For more info:
http://www.dumpsterdoggy.com/tutorials/?xmlexception-name-cannot-begin-with
Try taking the List template and for loop out and set the property using straightforward syntax. Here's a post for setting the property in your example, see if you can get this to work and then progress to building up a more generic solution with a List and iteration over the items in the list.
http://www.sharepointkings.com/2008/05/how-to-modify-webconfig-file-in.html

Changing values in Web.config with a Batch file or in .NET code

I have a web.config file on my computer.
There are alot of things i need to change and add in the file.
(I am actually working with my SharePoint web.config file)
Can i do this with a Batch file, if so how would i do it.
Or how would i do it using VB.NET or C# code?
Any ideas guys?
Edit: i need to create a program to alter a web.config of lets say i web.config laying on my deskop and not the actual web.config of my project
Regards
Etienne
You can modify it from C# code, for example:
Configuration configuration = WebConfigurationManager.OpenWebConfiguration("~");
AppSettingsSection appSettingsSection = (AppSettingsSection)configuration.GetSection("appSettings");
if (appSettingsSection != null)
{
appSettingsSection.Settings["foo"].Value = "bar";
config.Save();
}
where foo is the key and bar the value of the key to set, obviously. To remove a value, use Settings.Remove(key);
See the msdn documentation for more information about the OpenWebConfiguration method and more.
The context in which you want to change the file really affects how you should do it. If you're looking at performing changes relatively frequently, but in an administrative domain, then some sort of command-line tool makes sense, and in this case I'd agree with JaredPar that PowerShell would be a valuable tool.
If, on the other hand, you find yourself in a situation where you need to modify the web.config in a more programmatic environment (e.g., as part of a setup program), then using programmatic technologies might make more sense. I recently had to do such a thing and Linq to Xml proved very convenient.
For example, to open a document "C:\foo\bar.xml" you could do something like (untested, no convenient build environment at the moment):
XDocument config = XDocument.Load(#"C:\foo\bar.xml");
You could then carry on in the usual fashion with the API. Note that this may be overkill if you're doing an administrative task as opposed to a programmatic task-- there are big, long-term advantages to learning a tool like PowerShell.
Finally, if you're modifying the web.config from within the program that the web.config is being used for, and you aren't doing anything too fancy or dynamic, then using the built-in Settings or ConfigurationManager may be the way to go.
Your best bet might to change it using a MSBuild Script and the MsBuild Community Tasks XML Mass update task
I would personally recommend using PowerShell. This is the next gen command line from Microsoft and it's sits right on top of .Net. It was built to do items like batch edits across large sets of files.
For a change in web.config in a SharePoint environment you have classes specially developed for this task. You only have to search for SPWebConfigModification class.
To load an arbitrary .NET config file
string configLocation = #"C:\myconfigFile.Config";
ExeConfigurationFileMap configFileMap = new ExeConfigurationFileMap();
configFileName = configLocation;
configFileMap.ExeConfigFilename = configFileName;
Configuration configuration= ConfigurationManager.OpenMappedExeConfiguration(configFileMap, ConfigurationUserLevel.None);
Then use Razzie's code to alter the actual config setting
AppSettingsSection appSettingsSection = (AppSettingsSection)configuration.GetSection("appSettings");
if (appSettingsSection != null)
{
appSettingsSection.Settings["foo"].Value = "bar";
configuration.Save();
}
This is what i needed to do.......thanks for all the help!!!
// Read in Xml-file
XmlDocument doc = new XmlDocument();
doc.Load("C:/Web.config");
//SaveControl tag..........................................................
XmlNode n = doc.SelectSingleNode("/configuration/SharePoint/SafeControls");
XmlElement elemWeb = doc.CreateElement("SafeControl");
elemWeb.SetAttribute("Assembly", "SamrasWebOption4");
elemWeb.SetAttribute("Namespace", "SamrasWebOption4");
elemWeb.SetAttribute("TypeName", "*");
elemWeb.SetAttribute("Safe", "True");
XmlElement elemSmartPart = doc.CreateElement("SafeControl");
elemSmartPart.SetAttribute("Assembly", "Machine_Totals");
elemSmartPart.SetAttribute("Namespace", "Machine_Totals");
elemSmartPart.SetAttribute("TypeName", "*");
elemSmartPart.SetAttribute("Safe", "True");
//Appending the Nodes......................................................
n.AppendChild(elemWeb);
n.AppendChild(elemSmartPart);
//Saving the document......................................................
doc.Save("C:/Web.config");

Categories