Thursday 26 April 2012

Recycling Application Pool through Code


Today I'm going to show you how we can recycle application pool programmatically.

Scenario: 
I have a Windows Workflow 4.0 re-hosted designer in which user can create different workflow services and host them at IIS. Every time user create and publish the workflow service, they should be available to use in IIS without manually resetting IIS.

Resolution: We will follow three basic solutions to approach the problem. 

First Solution: Use of ServerManager() class
·        Add a reference of Microsoft.Web.Administration.dll from c:\Windows\System32/inetsrv (I am using Windows7. It could be different depending on the OS installed on your machine)
·        Here's the code snippet that does the thing for you...
               
            var server = new ServerManager();
                var site = server.Sites.FirstOrDefault(s => s.Name == "Default Web Site");
                if (site != null)
                {
         site.Stop();
         if (site.State != ObjectState.Stopped)
         {
             throw new InvalidOperationException("Could not stop website!");
          }
          //restart the site...
           site.Start();
                }
                else
                {
                     throw new InvalidOperationException("Could not find website!");
                }


Drawback: This code can work only on IIS7 or higher. So if you want to make backward compatibility then go for next solution


Second Solution: Use ProcessStartInfo() class

·        The following lines of code dose the thing for you

var startInfo = new ProcessStartInfo("iisreset.exe");
Process.Start(startInfo);
var stopInfo = new ProcessStartInfo("iisreset.exe", " /start");
Process.Start(stopInfo);

Drawback: You should have a cmd window running until iis has been reset properly.


Third Solution: Use of DirectoryService()

·        Add a reference of System.DirectoryService in your application
·        Here the code snippet that does the things for you

StopAppPool();
           int status = CheckAppPoolStatus();
           if (status == 4) StartAppPool();
           while (status != 4)    //4 = AppPool has stopped, 2 = AppPool is Running
           {
               status = CheckAppPoolStatus();
               if (status == 4)
               {
                    StartAppPool();
                }
           }

            private void StopAppPool()
            {
                        try
                        {
                                    var w3Svc = new DirectoryEntry(_appPoolPath);
            w3Svc.Invoke("Stop", null);
                        }
                        catch (Exception ex)
                        {
                                    Utility.ShowMessagebox(DakotaPopupType.Error, ex.Message);
                        }
            }

            private void StartAppPool()
            {
                        try
                        {
                                    var w3Svc = new DirectoryEntry(_appPoolPath);
                                    w3Svc.Invoke("Start", null);
                        }
                        catch (Exception ex)
                        {
                                    Utility.ShowMessagebox(DakotaPopupType.Error, ex.Message);
                        }
            }

            private int CheckAppPoolStatus()
            {
                        int intStatus = 0;
                        try
                        {
                                    var w3Svc = new DirectoryEntry(_appPoolPath);
                                    intStatus = (int)w3Svc.InvokeGet("AppPoolState");
                        }
                        catch (Exception ex)
                        {
                                    Utility.ShowMessagebox(DakotaPopupType.Error, ex.Message);
                        }
                        return intStatus;
            }


The third solution can work on IIS6 as well. I didn’t try it on IIS5 but I think it should work

Wednesday 7 March 2012

Nitin's blog: How to Access ApplicationResources in Windows host...

Nitin's blog: How to Access ApplicationResources in Windows host...: Scenerio : - If you have a WPF application and you are running it standalone, you can see all static as well as dynamic resources like Image...

How to Access ApplicationResources in Windows hosted WPF application

Scenerio: - If you have a WPF application and you are running it standalone, you can see all static as well as dynamic resources like Images, converters etc. When you host this it to native Win32 or Windows application all images vanish from the UI.

Reason: - The reason is very simple. When you run WPF application, there's a class called App.xaml (app.xaml.cs) which is the entry point of any WPF application during build. Apparently, this class does not has any information how to run the application, But if you goto App.g.cs in obj folder of WPF app, you can see what I mean.
When you host WPF app to windows app, MSbuild knows nothing about how to set the entry point of WPF application rather its set the entry point of windows app which is obvious.

Solution: - Load the WPF application resources at runtime. Open the usercontrol.cs (it could be any form or control) file where your are hosting WPF control inside ElementHost. Now, after initializeComponent() in the constructor load the resource dictionary using the following code.


StreamResourceInfo sri = System.Windows.Application.GetResourceStream(
    new Uri("/assemblyName;component/Resources/ButtonStyles.xaml", UriKind.Relative));
            var resources = (ResourceDictionary)BamlReader.Load(sri.Stream);

assemblyName is the name of your assembly. BamlReader is a static class used to load resources from stream


public static class BamlReader
    {
        public static object Load(Stream stream)
       {
           var pc = new ParserContext();
           MethodInfo loadBamlMethod = typeof (XamlReader).GetMethod("LoadBaml",
                                                                      BindingFlags.NonPublic | BindingFlags.Static);
            return loadBamlMethod.Invoke(null, new object[] { stream, pc, null, false });
       }
    }


So now, we have resources with us, we need to set it to the appropriate control

var designerView = new WorkflowDesignerView();
designerView.Resources.MergedDictionaries.Add(resources);

Where designerView is the WPF control you are hosting in windows app.

Lastly add this designer to Elementhost as a child

elementHost1.Child = designerView;

and you're done. Now you can see all images & converters are available and working properly.

Tuesday 6 March 2012

Nitin's blog: Host Workflows stored in database as WCF services ...

Nitin's blog: Host Workflows stored in database as WCF services ...: Today I am going to show you how to host WF4 workflows in WCF which are stored in database. We'll be using ASP.Net MVC as the solution proje...

Thursday 23 February 2012

How to register third party assemblies into GAC


Today, I'll present how to register the third party dll files into GAC. If you try to do so, you'll get an error saying that assembly should be strongly named. The problem is because the referenced third party dll is not signed or strongly named.
Well, here are the necessary steps how to proceed: -
  1. Open the Visual Studio Command Prompt. I know everyone can do it.
  2. Copy the dlls you want to register into the VC folder. Usually it is "c:\Program Files (x86)\microsoft visual studio 10.0\vc" in windows7. It is better to copy the dll in into the VC folder otherwise you need to provide the full path of the assembly.
  3. Create a key pair to sign the assembly – sn.exe –k key.snk . If you are not giving the path of the strong name file, it will be generated in VC folder.
  4. Disassemble the dll using ILDASM – ILDASM.exe Yourdll.dll /out=Yourdll.il . This will create a ‘Yourdll.il’ file containing the IL for the assembly. It will also extract the assembly so you will now have a folder with lots of files, you will also see a .res file containing the resources for the dll.
  5. Reassemble the dll using ILASM to sign the output with the key created above –ILASM.exe Yourdll.il /dll /out=Yourdll2.dll /key=key.snk

You should now have a signed version of the dll, you can now deploy this to the GAC either using gacutil /i Yourdll2.dll or by just dragging the dll file to c:\windows\assembly.

Wednesday 15 February 2012

Host Workflows stored in database as WCF services in ASP.Net MVC

Today I am going to show you how to host WF4 workflows in WCF which are stored in database. We'll be using ASP.Net MVC as the solution project.

On internet, there are several examples available on how to host compiled workflows as WCF service but my requirement was to get the workflow from database and expose as WCF end points. 
After a lot of research and hacking, I was able to create a small sample project that does the work. My entire code goes in global.asax.cs file.


When you open global.asax.cs file and find an entry where mvc register the routes to the Routecollection. This routing  helps IIS to redirect the incoming request with the route specified. As I already told my workflows are stored in database in table Workflows so now I'm going to get them from database and add to Routecollection as ServiceRoute.

    foreach(var wf in GetAllWorkflows())
            {
                routes.Add(new ServiceRoute(wf.Name, new TestWorkflowServiceHostFactory(wf.Name), typeof(Workflow1)));
            }
GetAllWorkflows() is the method which returns a list of workflows from database. I store them as a list of custom class of workflows. I'm not explaining here how to get the data from database for the sake of simplicity but you can use ADO.Net classes or Ado.Net Entity data model or some other mode.

Here are some interesting bits to understand about the code. The ServiceRoute class enables the creation of service routes over Http for WCF services with support for extension-less base addresses. What does it mean. It means when an incoming request is received by IIS, it route the request using this Url. And how does it do that...Well, when the ServiceRoute constructor is called, it adds the corresponding route prefix (url pattern) and a (hidden) route handler to the ASP.NET RouteCollection, and cache the corresponding route prefix, service host factory and service type info into an internal hash table for future service activation. The  three arguments used here are defined below: - 

  1. RoutePrefix -> WCF Endpoint will be exposed with this name.
  2. TestWorkflowServiceHostFactory -> Inherited from WorkflowServiceHostFactory. Factory that provides instances of WorkflowServiceHost in managed hosting environments (e.g. IIS) where the host instance is created dynamically in response to incoming messages. It means a serviceHostFactory that is used by IIS to create a new instance of service host everytime a new request is received. This class accepts workflow name as string which is the name of workflow stored in database and is further used to expose the endpoints. (e.g. If the workflow name is GetMyName then the endpoints exposed would be something like http://localhost:1997/GetMyName)
  3. ServiceType -> Used to get the type of service. Since we are creating workflows from database so we can't have types for workflows. So what do we pass here? Now this is a bit hacky. I added a workflow.xaml class in project and refer it here (I tried passing with typeof(object) but it didn't work somehow...however didn't get enough time to explore the reason).


Now we come to the implementation of WorkflowServiceHostFactory. TestWorkflowServiceHostFactory is inherited by WorkflowServiceHostFactory class. This class declare two constants to set/get temp files to stores the workflow/'workflow service' definitions.
        private const string TempXamlFile = "temp.xaml";
        private const string TempXamlService = "temp.xamlx";


The constructor holds the name of workflow passed at the time of registering routes.


   public TriboldWorkflowServiceHostFactory(string routePrefix)
        {
            XamlRoutePrefix = routePrefix;
            ...
        }


Override CreateServiceHost method to create the host and return to IIS. This is the main method that does the magic.

public override ServiceHostBase CreateServiceHost(string constructorString, Uri[] baseAddresses)
        {
            ...

            //WorkflowService service = LoadWFService(XamlRoutePrefix);
            //var host = new WorkflowServiceHost(service, baseAddresses);


              Activity activity1 = LoadActivity(XamlRoutePrefix);
              var host = new WorkflowServiceHost(activity1, baseAddresses);
              ...



            host.Description.Behaviors.Add(new AspNetCompatibilityRequirementsAttribute() { RequirementsMode = AspNetCompatibilityRequirementsMode.Required });
            host.Description.Behaviors.Add(new ServiceMetadataBehavior() { HttpGetEnabled = true });
            return host;

}

The LoadActivity method get the workflowDefinition from DB and create a temp.xaml file. Create the file stream and writes the workflow definition into the stream. (I was getting 'File in use' error while loading xaml file from stream so I closed the stream and again open in read mode to load the .xaml file) ActivityXamlServices.Load method uses this stream to load and return Activity object

        ...
  var stream = new FileStream(Path.Combine(tempPath, TempXamlFile), FileMode.Create);
        byte[] bytes = Encoding.ASCII.GetBytes(workflowDefinition);
        stream.Write(bytes, 0, Encoding.ASCII.GetByteCount(workflowDefinition));
        stream.Flush();
        stream.Close();

        stream = File.OpenRead(Path.Combine(tempPath, TempXamlFile));
        var service = ActivityXamlServices.Load(stream);
        stream.Flush();
        stream.Close();
        return service as Activity;

Now, we've got the activity, so we use WorkflowServiceHost class and pass this activity with baseAddress thus creating the host and return to the IIS.

And that's all, We've done the job. In the same manner we can load Workflow service. The only difference is we need to pass workflowservice object in WorkflowServiceHost as mentioned above (commented code)




Here's the complete code...

public class TestWorkflowServiceHostFactory : WorkflowServiceHostFactory
    {
        private const string TempXamlFile = "temp.xaml";
        private const string TempXamlService = "temp.xamlx";

        public string XamlRoutePrefix { get; set; }
        
        public TriboldWorkflowServiceHostFactory(string routePrefix)
        {
            XamlRoutePrefix = routePrefix;
        }

        public override ServiceHostBase CreateServiceHost(string constructorString, Uri[] baseAddresses)
        {
            var type = GetObjectOrThrowNotFound(() => Type.GetType(constructorString));
            var activity = GetObjectOrThrowNotFound(() => Activator.CreateInstance(type) as Activity);

            Activity activity1 = LoadActivity(XamlRoutePrefix);
            var host = new WorkflowServiceHost(activity1, baseAddresses);

            //WorkflowService service = LoadWFService(XamlRoutePrefix);
            //var host = new WorkflowServiceHost(service, baseAddresses);

            host.Description.Behaviors.Add(new AspNetCompatibilityRequirementsAttribute() { RequirementsMode = AspNetCompatibilityRequirementsMode.Required });
            host.Description.Behaviors.Add(new ServiceMetadataBehavior() { HttpGetEnabled = true });

            return host;
        }

        private Activity LoadActivity(string searchString)
        {
            var input = new Dictionary<string, object>();
            input["@WorkflowName"] = searchString;
            DataTable dt = MvcApplication.FetchData.Fetch(input, "GetWorkflowList");
            string workflowDefinition = dt.Rows[0][1].ToString();
            string tempPath = Path.GetTempPath();

            //if file already exists then delete it
            if (File.Exists(Path.Combine(tempPath, TempXamlFile)))
                File.Delete(Path.Combine(tempPath, TempXamlFile));

            var stream = new FileStream(Path.Combine(tempPath, TempXamlFile), FileMode.Create);
            byte[] bytes = Encoding.ASCII.GetBytes(workflowDefinition);
            stream.Write(bytes, 0, Encoding.ASCII.GetByteCount(workflowDefinition));
            stream.Flush();
            stream.Close();

            stream = File.OpenRead(Path.Combine(tempPath, TempXamlFile));
            var service = ActivityXamlServices.Load(stream);
            stream.Flush();
            stream.Close();
            return service as Activity;
        }

        private WorkflowService LoadWFService(string searchString)
        {
            var input = new Dictionary<string, object>();
            input["@WorkflowName"] = searchString;
            DataTable dt = MvcApplication.FetchData.Fetch(input, "GetWorkflowList");
            string workflowDefinition = dt.Rows[0][1].ToString();
            string tempPath = Path.GetTempPath();

            //if file already exists then delete it
            if (File.Exists(Path.Combine(tempPath, TempXamlService)))
                File.Delete(Path.Combine(tempPath, TempXamlService));

            var stream = new FileStream(Path.Combine(tempPath, TempXamlService), FileMode.Create);
            byte[] bytes = Encoding.ASCII.GetBytes(workflowDefinition);
            stream.Write(bytes, 0, Encoding.ASCII.GetByteCount(workflowDefinition));
            stream.Flush(); 
            stream.Close();

            stream = File.OpenRead(Path.Combine(tempPath, TempXamlService));
            var service = XamlServices.Load(stream);
            stream.Flush();
            stream.Close();
            return service as WorkflowService;
        }



 public static void RegisterRoutes(RouteCollection routes)
        {
            foreach(var wf in GetAllWorkflows())
            {
                routes.Add(new ServiceRoute(wf.Name, new TestWorkflowServiceHostFactory(wf.Name), typeof(Workflow1)));
            }
        }

Thursday 23 June 2011

Could not load file or assembly XX. System.BadImageFormatException

I got this error when I was working with Silverlight RIA application which works on MVVM pattern. Everything was running smoothly before when I was using VSTS2010 internal web server. As soon as I host my asp.net application which host silverlight application to IIS 7 of windows 7 I got the above error.

Although I was using dll which was built on any CPU platform. After some digging I found out that there is a setting in IIS 7 in Application Pool that you can set.
Open IIS using InetMgr command, locate & click your web application. In the action tab select Advanced Settings and view your application pool.
Now goto application pool and locate the pool viewed in last step. Click your pool and select Advanced Settings from action tab. In General settings Enable 32-Bit Applications set this value to true.
After setting this when I tried to run the application, I got "Login failed for user 'IIS APPPOOL\ASP.NET v4.0'" error. You may overcome this by going to Process Model in Application pool and in Identity setting, change ApplicationPoolIdentity to NetworkService or LocalSystem and you're go.

Make sure to remove anonymous access and add windows authentication (if using windows auth) to your asp.net web application.