How to detect what Application Pool I am currently running under? (IIS6) - c#

I need to know how I can detect the current application pool I am running under, so I can do a Recycle on it programmatically.
Does anyone know how to do this for IIS6?
My current code for recycling the app-pool is:
/// <summary>
/// Recycle an application pool
/// </summary>
/// <param name="IIsApplicationPool"></param>
public static void RecycleAppPool(string IIsApplicationPool) {
ManagementScope scope = new ManagementScope(#"\\localhost\root\MicrosoftIISv2");
scope.Connect();
ManagementObject appPool = new ManagementObject(scope, new ManagementPath("IIsApplicationPool.Name='W3SVC/AppPools/" + IIsApplicationPool + "'"), null);
appPool.InvokeMethod("Recycle", null, null);
}

And after searching I found the answer myself:
public string GetAppPoolName() {
string AppPath = Context.Request.ServerVariables["APPL_MD_PATH"];
AppPath = AppPath.Replace("/LM/", "IIS://localhost/");
DirectoryEntry root = new DirectoryEntry(AppPath);
if ((root == null)) {
return " no object got";
}
string AppPoolId = (string)root.Properties["AppPoolId"].Value;
return AppPoolId;
}
Hmm. They need a way to let me set my own answer as THE answer.

I found this one as well and it worked for me. Note you might need to include a reference for using System.DirectoryServices;
private static string GetCurrentApplicationPoolId()
{
string virtualDirPath = AppDomain.CurrentDomain.FriendlyName;
virtualDirPath = virtualDirPath.Substring(4);
int index = virtualDirPath.Length + 1;
index = virtualDirPath.LastIndexOf("-", index - 1, index - 1);
index = virtualDirPath.LastIndexOf("-", index - 1, index - 1);
virtualDirPath = "IIS://localhost/" + virtualDirPath.Remove(index);
DirectoryEntry virtualDirEntry = new DirectoryEntry(virtualDirPath);
return virtualDirEntry.Properties["AppPoolId"].Value.ToString();
}

Related

Assign App pool to site in IIS6 using c#

I am trying to create and assign application pool to a website in IIS6 and IIS7 using c#. I managed to do this for IIS7 using Microsoft.Web.Administration; but since its not supported in IIS6 i want to accomplish this using System.DirectoryServices;. I have managed to create the application pool but the assigning application pool does not work.
Here is what I have so far...
private static string DefaultAppPool = "DefaultAppPool";
public static void SetApplicationPoolIIS6(string websiteName)
{
string metabasePath = "IIS://localhost/W3SVC";
string appPoolName = GetSavedApplicationPoolName(websiteName);
if (string.IsNullOrEmpty(appPoolName) || appPoolName.Equals(DefaultAppPool))
appPoolName = websiteName + "AppPool";
bool isAppPoolCreated = IsAppPoolCreatedIIS6(websiteName, appPoolName);
if (!isAppPoolCreated)
CreateApplicationPoolIIS6(appPoolName, metabasePath);
DirectoryEntry w3svc = new DirectoryEntry(metabasePath);
w3svc.RefreshCache();
foreach (DirectoryEntry item in w3svc.Children)
{
string siteName = item.Properties["ServerComment"].Value == null ? "" : item.Properties["ServerComment"].Value.ToString();
if (string.IsNullOrEmpty(siteName))
continue;
if (siteName.Equals(websiteName))
{
item.Invoke("Stop", null);
item.Properties["AppPoolId"].Value = appPoolName;
item.CommitChanges();
item.Invoke("Start", null);
break;
}
}
w3svc.CommitChanges();
DirectoryEntry w3svc2 = new DirectoryEntry(metabasePath);
w3svc2.RefreshCache();
SaveWebSiteSettingsIIS6(websiteName, appPoolName);
}
Any idea why the below lines don't assign the created app pool ?
item.Properties["AppPoolId"].Value = appPoolName;
item.CommitChanges();
Any help is much appreciated. Thank you.

Programatically create an IIS website on button click [duplicate]

We have been able to create a web site. We did this using the information in this link:
https://msdn.microsoft.com/en-us/library/ms525598.aspx
However, we would like to use a port number other that port 80. How do we do this?
We are using IIS 6
If you're using IIS 7, there is a new managed API called Microsoft.Web.Administration
An example from the above blog post:
ServerManager iisManager = new ServerManager();
iisManager.Sites.Add("NewSite", "http", "*:8080:", "d:\\MySite");
iisManager.CommitChanges();
If you're using IIS 6 and want to do this, it's more complex unfortunately.
You will have to create a web service on every server, a web service that handles the creation of a website because direct user impersonation over the network won't work properly (If I recall this correctly).
You will have to use Interop Services and do something similar to this (This example uses two objects, server and site, which are instances of custom classes that store a server's and site's configuration):
string metabasePath = "IIS://" + server.ComputerName + "/W3SVC";
DirectoryEntry w3svc = new DirectoryEntry(metabasePath, server.Username, server.Password);
string serverBindings = ":80:" + site.HostName;
string homeDirectory = server.WWWRootPath + "\\" + site.FolderName;
object[] newSite = new object[] { site.Name, new object[] { serverBindings }, homeDirectory };
object websiteId = (object)w3svc.Invoke("CreateNewSite", newSite);
// Returns the Website ID from the Metabase
int id = (int)websiteId;
See more here
Heres the solution.
Blog article : How to add new website in IIS 7
On Button click :
try
{
ServerManager serverMgr = new ServerManager();
string strWebsitename = txtwebsitename.Text; // abc
string strApplicationPool = "DefaultAppPool"; // set your deafultpool :4.0 in IIS
string strhostname = txthostname.Text; //abc.com
string stripaddress = txtipaddress.Text;// ip address
string bindinginfo = stripaddress + ":80:" + strhostname;
//check if website name already exists in IIS
Boolean bWebsite = IsWebsiteExists(strWebsitename);
if (!bWebsite)
{
Site mySite = serverMgr.Sites.Add(strWebsitename.ToString(), "http", bindinginfo, "C:\\inetpub\\wwwroot\\yourWebsite");
mySite.ApplicationDefaults.ApplicationPoolName = strApplicationPool;
mySite.TraceFailedRequestsLogging.Enabled = true;
mySite.TraceFailedRequestsLogging.Directory = "C:\\inetpub\\customfolder\\site";
serverMgr.CommitChanges();
lblmsg.Text = "New website " + strWebsitename + " added sucessfully";
}
else
{
lblmsg.Text = "Name should be unique, " + strWebsitename + " is already exists. ";
}
}
catch (Exception ae)
{
Response.Redirect(ae.Message);
}
Looping over sites whether name already exists
public bool IsWebsiteExists(string strWebsitename)
{
Boolean flagset = false;
SiteCollection sitecollection = serverMgr.Sites;
foreach (Site site in sitecollection)
{
if (site.Name == strWebsitename.ToString())
{
flagset = true;
break;
}
else
{
flagset = false;
}
}
return flagset;
}
Try the following Code to Know the unUsed PortNo
DirectoryEntry root = new DirectoryEntry("IIS://localhost/W3SVC");
// Find unused ID PortNo for new web site
bool found_valid_port_no = false;
int random_port_no = 1;
do
{
bool regenerate_port_no = false;
System.Random random_generator = new Random();
random_port_no = random_generator.Next(9000,15000);
foreach (DirectoryEntry e in root.Children)
{
if (e.SchemaClassName == "IIsWebServer")
{
int site_id = Convert.ToInt32(e.Name);
//For each detected ID find the port Number
DirectoryEntry vRoot = new DirectoryEntry("IIS://localhost/W3SVC/" + site_id);
PropertyValueCollection pvcServerBindings = vRoot.Properties["serverbindings"];
String bindings = pvcServerBindings.Value.ToString().Replace(":", "");
int port_no = Convert.ToInt32(bindings);
if (port_no == random_port_no)
{
regenerate_port_no = true;
break;
}
}
}
found_valid_port_no = !regenerate_port_no;
} while (!found_valid_port_no);
int newportId = random_port_no;
I have gone though all answer here and also tested. Here is the most clean smarter version of answer for this question. However this still cant work on IIS 6.0. so IIS 8.0 or above is required.
string domainName = "";
string appPoolName = "";
string webFiles = "C:\\Users\\John\\Desktop\\New Folder";
if (IsWebsiteExists(domainName) == false)
{
ServerManager iisManager = new ServerManager();
iisManager.Sites.Add(domainName, "http", "*:8080:", webFiles);
iisManager.ApplicationDefaults.ApplicationPoolName = appPoolName;
iisManager.CommitChanges();
}
else
{
Console.WriteLine("Name Exists already");
}
public static bool IsWebsiteExists(string strWebsitename)
{
ServerManager serverMgr = new ServerManager();
Boolean flagset = false;
SiteCollection sitecollection = serverMgr.Sites;
flagset = sitecollection.Any(x => x.Name == strWebsitename);
return flagset;
}
This simplified method will create a site with default binding settings, and also create the application pool if needed:
public void addIISApplication(string siteName, string physicalPath, int port, string appPoolName)
{
using (var serverMgr = new ServerManager())
{
var sitecollection = serverMgr.Sites;
if (!sitecollection.Any(x => x.Name.ToLower() == siteName.ToLower()))
{
var appPools = serverMgr.ApplicationPools;
if (!appPools.Any(x => x.Name.ToLower() == appPoolName.ToLower()))
{
serverMgr.ApplicationPools.Add(appPoolName);
}
var mySite = serverMgr.Sites.Add(siteName, physicalPath, port);
mySite.ApplicationDefaults.ApplicationPoolName = appPoolName;
serverMgr.CommitChanges();
}
}
}
In properties of site select "Web Site" tab and specify TCP Port.
In studio to debug purpose specify http://localhost:<port>/<site> at tab Web for "Use Local IIS Web Server"

Create an Amazon AMI using C# SDK but include only root volume

I have written the code to create an AMI using the C# SDK but haven't been able to create the AMI with only the Root Volume. By default it creates the AMI with all the EBS volumes attached to the instance. E.g. If it has 4 volumes it would create an AMI with 4 snapshots associated to it whereas I only need the C:\drive (Root Drive) included. I know that you have to use BlockDeviceMapping to specify the volume but not sure about the implementation any help would be appreciated.
public static void CreateAMI(string InstanceID, string AMIName, string AMIDescription)
{
try
{
Console.WriteLine("Creating AMI for InstanceID" + InstanceID);
AmazonEC2Config config = new AmazonEC2Config();
config.ProxyHost = ConfigurationManager.AppSettings["PROXYHOST"];
config.ProxyPort = Convert.ToInt32(ConfigurationManager.AppSettings["PROXYPORT"])
config.ServiceURL = "https://ec2." + Program.options.Region + ".amazonaws.com";
AmazonEC2 ec2 = AWSClientFactory.CreateAmazonEC2Client(Program.options.AccessKey, Program.options.SecretKey, config);
CreateImageRequest rq = new CreateImageRequest();
rq.InstanceId = InstanceID;
rq.Name = AMIName;
rq.Description = AMIDescription;
rq.NoReboot = true;
/* BlockDeviceMapping BMapping = new BlockDeviceMapping();
BMapping.DeviceName = "/dev/sda1";
rq.BlockDeviceMapping.Add(BMapping); */
CreateImageResponse rs = ec2.CreateImage(rq);
string AMIid = rs.CreateImageResult.ImageId;
Console.WriteLine("AMI Created with AMIid: " + AMIid);
Console.ReadLine();
}
catch (Exception err)
{
Console.WriteLine(err.Message + "/n" + err.StackTrace);
}
}
I think you need to specif your other device(s) EBS property to Null and NoDevice to something to make them not apart of your AMI. Your code should looks something like this.
//Create Drive Mapping List
var blockDeviceMappingList = new List<BlockDeviceMapping>();
//Create Mappings
var blockDeviceMapping = new BlockDeviceMapping();
var blockDeviceMapping2 = new BlockDeviceMapping();
//Specif a mount point of the drive you want (root)
blockDeviceMapping.DeviceName = "/dev/sda1";
var ebsBlockDevice = new EbsBlockDevice();
//Set something other than null constructor or u get an error about EBS not set. Likely has to do with how they build the request to send to the server
ebsBlockDevice.VolumeType = VolumeType.Standard;
blockDeviceMapping.Ebs = ebsBlockDevice;
//Specif a mount point of the unwanted drive and set EBS to null and NoDevice
blockDeviceMapping2.DeviceName = "/dev/sdf";
blockDeviceMapping2.Ebs = null;
blockDeviceMapping2.NoDevice = string.Empty;
//Add the mappings to the list
blockDeviceMappingList.Add(blockDeviceMapping);
blockDeviceMappingList.Add(blockDeviceMapping2);
//Setup Request
createImageRequest.BlockDeviceMappings = blockDeviceMappingList;
=)

Check if a service exists on a particular machine without using exception handling

Don't know if there is a better way to do this, so that is the reason for the question. I can check if a service exists on a particular machine with the following code:
bool DoesServiceExist(string serviceName, string machineName)
{
ServiceController controller = null;
try
{
controller = new ServiceController(serviceName, machineName);
controller.Status;
return true;
}
catch(InvalidOperationException)
{
return false;
}
finally
{
if (controller != null)
{
controller.Dispose();
}
}
}
but this seems like an ineffecient solution to me (due to the exception handling). Is there a better way to check if a service exists. Note - I have recently switched to .Net 4.0 so if someone knows of a better solution in 4.0 that would be acceptable.
EDIT:
Here is a sample c# console app to test the performance of my example as well as the GetServices code sample. In my testing I found that the GetServices is much more performative in the case where the service does not exist, but is twice as slow when the service does exist:
static void Main(string[] args)
{
string serviceName = string.Empty;
string machineName = string.Empty;
var sw = new Stopwatch();
sw.Reset();
sw.Start();
for (int i = 0; i < 1000; i++)
{
ServiceExistsException(serviceName, machineName);
}
sw.Stop();
Console.WriteLine("Elapsed time: " + sw.ElapsedMilliseconds.ToString());
sw.Reset();
sw.Start();
for (int i = 0; i < 1000; i++)
{
ServiceExistsGetList(serviceName, machineName);
}
sw.Stop();
Console.WriteLine("Elapsed time: " + sw.ElapsedMilliseconds.ToString());
Console.WriteLine("Done");
Console.ReadLine();
}
static bool ServiceExistsException(string serviceName, string machineName)
{
ServiceController controller = null;
try
{
controller = new ServiceController(serviceName, machineName);
string name = controller.DisplayName;
return true;
}
catch (InvalidOperationException)
{
return false;
}
finally
{
if (controller != null)
{
controller.Dispose();
}
}
}
static bool ServiceExistsGetList(string serviceName, string machineName)
{
ServiceController[] services = null;
try
{
services = ServiceController.GetServices(machineName);
var service = services.FirstOrDefault(s => s.ServiceName == serviceName);
return service != null;
}
finally
{
if (services != null)
{
foreach (ServiceController controller in services)
{
controller.Dispose();
}
}
}
}
}
You can use the ServiceController.GetServices() method to get all of the services on the machine, then look through them to see if one exists named what you are looking for:
bool DoesServiceExist(string serviceName, string machineName)
{
ServiceController[] services = ServiceController.GetServices(machineName);
var service = services.FirstOrDefault(s => s.ServiceName == serviceName);
return service != null;
}
The FirstOrDefault() extension method (from System.Linq) will return either the first service with the given name, or a null if there is no match.
To address your speed issue:
The difference between the two approaches for a single method call is negligible, regardless of whether the service is found or not. It will only be a problem if you are calling this method thousands of times—in which case get the list of services once and remember it.
Same approach as adrianbanks but a slight more compact code.
If your're using LINQ you can use any statement to return what you want.
In addition if you are checking on local computer there's no need to give computer name.
bool DoesServiceExist(string serviceName)
{
return ServiceController.GetServices().Any(serviceController => serviceController.ServiceName.Equals(serviceName));
}
Built on top of Mike's answer. Same concept as Dictionary.TryGetValue.
/// <summary>
/// Gets a <see cref="ServiceController"/> given the specified <see cref="pServiceName"/>.
/// </summary>
/// <param name="pServiceName">The name of the service.</param>
/// <param name="pService">The <see cref="ServiceController"/> associated with the name.</param>
/// <returns>
/// <see cref="bool.True"/> if the <see cref="ServiceController"/> exists; otherwise <see cref="bool.False"/>.
/// </returns>
private static bool TryGetService(string pServiceName, out ServiceController pService)
{
pService = ServiceController.GetServices()
.FirstOrDefault(serviceController => serviceController.ServiceName == pServiceName);
return pService != null;
}

Is there a way to check how many messages are in a MSMQ Queue?

I was wondering if there is a way to programmatically check how many messages are in a private or public MSMQ using C#? I have code that checks if a queue is empty or not using the peek method wrapped in a try/catch, but I've never seen anything about showing the number of messages in the queue. This would be very helpful for monitoring if a queue is getting backed up.
You can read the Performance Counter value for the queue directly from .NET:
using System.Diagnostics;
// ...
var queueCounter = new PerformanceCounter(
"MSMQ Queue",
"Messages in Queue",
#"machinename\private$\testqueue2");
Console.WriteLine( "Queue contains {0} messages",
queueCounter.NextValue().ToString());
There is no API available, but you can use GetMessageEnumerator2 which is fast enough. Sample:
MessageQueue q = new MessageQueue(...);
int count = q.Count();
Implementation
public static class MsmqEx
{
public static int Count(this MessageQueue queue)
{
int count = 0;
var enumerator = queue.GetMessageEnumerator2();
while (enumerator.MoveNext())
count++;
return count;
}
}
I also tried other options, but each has some downsides
Performance counter may throw exception "Instance '...' does not exist in the specified Category."
Reading all messages and then taking count is really slow, it also removes the messages from queue
There seems to be a problem with Peek method which throws an exception
If you need a fast method (25k calls/second on my box), I recommend Ayende's version based on MQMgmtGetInfo() and PROPID_MGMT_QUEUE_MESSAGE_COUNT:
for C#
https://github.com/hibernating-rhinos/rhino-esb/blob/master/Rhino.ServiceBus/Msmq/MsmqExtensions.cs
for VB
https://gist.github.com/Lercher/5e1af6a2ba193b38be29
The origin was probably http://functionalflow.co.uk/blog/2008/08/27/counting-the-number-of-messages-in-a-message-queue-in/ but I'm not convinced that this implementation from 2008 works any more.
We use the MSMQ Interop. Depending on your needs you can probably simplify this:
public int? CountQueue(MessageQueue queue, bool isPrivate)
{
int? Result = null;
try
{
//MSMQ.MSMQManagement mgmt = new MSMQ.MSMQManagement();
var mgmt = new MSMQ.MSMQManagementClass();
try
{
String host = queue.MachineName;
Object hostObject = (Object)host;
String pathName = (isPrivate) ? queue.FormatName : null;
Object pathNameObject = (Object)pathName;
String formatName = (isPrivate) ? null : queue.Path;
Object formatNameObject = (Object)formatName;
mgmt.Init(ref hostObject, ref formatNameObject, ref pathNameObject);
Result = mgmt.MessageCount;
}
finally
{
mgmt = null;
}
}
catch (Exception exc)
{
if (!exc.Message.Equals("Exception from HRESULT: 0xC00E0004", StringComparison.InvariantCultureIgnoreCase))
{
if (log.IsErrorEnabled) { log.Error("Error in CountQueue(). Queue was [" + queue.MachineName + "\\" + queue.QueueName + "]", exc); }
}
Result = null;
}
return Result;
}
//here queue is msmq queue which you have to find count.
int index = 0;
MSMQManagement msmq = new MSMQManagement() ;
object machine = queue.MachineName;
object path = null;
object formate=queue.FormatName;
msmq.Init(ref machine, ref path,ref formate);
long count = msmq.MessageCount();
This is faster than you selected one.
You get MSMQManagement class refferance inside "C:\Program Files (x86)\Microsoft SDKs\Windows" just brows in this address you will get it. for more details you can visit http://msdn.microsoft.com/en-us/library/ms711378%28VS.85%29.aspx.
I had real trouble getting the accepted answer working because of the xxx does not exist in the specified Category error. None of the solutions above worked for me.
However, simply specifying the machine name as below seems to fix it.
private long GetQueueCount()
{
try
{
var queueCounter = new PerformanceCounter("MSMQ Queue", "Messages in Queue", #"machineName\private$\stream")
{
MachineName = "machineName"
};
return (long)queueCounter.NextValue();
}
catch (Exception e)
{
return 0;
}
}
The fastest method I have found to retrieve a message queue count is to use the peek method from the following site:
protected Message PeekWithoutTimeout(MessageQueue q, Cursor cursor, PeekAction action)
{
Message ret = null;
try
{
ret = q.Peek(new TimeSpan(1), cursor, action);
}
catch (MessageQueueException mqe)
{
if (!mqe.Message.ToLower().Contains("timeout"))
{
throw;
}
}
return ret;
}
protected int GetMessageCount(MessageQueue q)
{
int count = 0;
Cursor cursor = q.CreateCursor();
Message m = PeekWithoutTimeout(q, cursor, PeekAction.Current);
{
count = 1;
while ((m = PeekWithoutTimeout(q, cursor, PeekAction.Next)) != null)
{
count++;
}
}
return count;
}
This worked for me. Using a Enumarator to make sure the queue is empty first.
Dim qMsg As Message ' instance of the message to be picked
Dim privateQ As New MessageQueue(svrName & "\Private$\" & svrQName) 'variable svrnme = server name ; svrQName = Server Queue Name
privateQ.Formatter = New XmlMessageFormatter(New Type() {GetType(String)}) 'Formating the message to be readable the body tyep
Dim t As MessageEnumerator 'declared a enumarater to enable to count the queue
t = privateQ.GetMessageEnumerator2() 'counts the queues
If t.MoveNext() = True Then 'check whether the queue is empty before reading message. otherwise it will wait forever
qMsg = privateQ.Receive
Return qMsg.Body.ToString
End If
If you want a Count of a private queue, you can do this using WMI.
This is the code for this:
// You can change this query to a more specific queue name or to get all queues
private const string WmiQuery = #"SELECT Name,MessagesinQueue FROM Win32_PerfRawdata_MSMQ_MSMQQueue WHERE Name LIKE 'private%myqueue'";
public int GetCount()
{
using (ManagementObjectSearcher wmiSearch = new ManagementObjectSearcher(WmiQuery))
{
ManagementObjectCollection wmiCollection = wmiSearch.Get();
foreach (ManagementBaseObject wmiObject in wmiCollection)
{
foreach (PropertyData wmiProperty in wmiObject.Properties)
{
if (wmiProperty.Name.Equals("MessagesinQueue", StringComparison.InvariantCultureIgnoreCase))
{
return int.Parse(wmiProperty.Value.ToString());
}
}
}
}
}
Thanks to the Microsoft.Windows.Compatibility package this also works in netcore/netstandard.
The message count in the queue can be found using the following code.
MessageQueue messageQueue = new MessageQueue(".\\private$\\TestQueue");
var noOFMessages = messageQueue.GetAllMessages().LongCount();

Categories