Run a single-threaded apartment method with parameters that returns a value within a web service

by Heathesh 21. July 2010 20:22

The need arose for me to run a method that used the WebBrowser object within a web service. The problem that I had is that you cannot run a WebBrowser object unless it's run in a single-threaded apartment. Within a windows forms or console application it's easy enough to set the apartment state by decorating your method with the [STAThread] attribute or using:

//set the current thread's apartment state
Thread.CurrentThread.SetApartmentState(ApartmentState.STA);


That does not work within a web service. My method also needed to accept parameters and to return a value. The method I needed to run looked like this:

public static byte[] GenerateScreenshot(string url, CustomImageFormat imageFormat, int width, int height)

This method was contained within a manager class I had created called GraphicsManager. As you can see it returns a byte[] and needs to accept four parameters. To call this method from the web service I first created a static private variable (make sure you create static variables) in my web service class for each of the parameters as well as the byte array like so:

        /// <summary>
        /// The returned image
        /// </summary>
        private static byte[] _returnedImage = null;
        /// <summary>
        /// The url
        /// </summary>
        private static string _url = string.Empty;
        /// <summary>
        /// The custom image format
        /// </summary>
        private static CustomImageFormat _imageFormat = CustomImageFormat.Gif;
        /// <summary>
        /// The width
        /// </summary>
        private static int _width = -1;
        /// <summary>
        /// The height
        /// </summary>
        private static int _height = -1;


I set default values for the variables simply out of habit. Next I created a single-threaded apartment method to execute my manager method like so. Notice that I decorated the method with the [STAThread] attribute and that the method is also a static method:

        /// <summary>
        /// Static method to run under Apartment State
        /// </summary>
        [STAThread]
        private static void generateScreenShot()
        {
            _returnedImage = GraphicsManager.GenerateScreenshot(_url, _imageFormat, _width, _height);
        }


As you can see I'm using the private variables I setup above in call to GraphicsManager.GenerateScreenshot. The last thing I needed to do was to create the WebMethod on my web service itself. I did this like so:

        /// <summary>
        /// Generates a portion of a screen shot image of the URL specified with with width and height specified and returns the byte stream of it in the specified format
        /// </summary>
        /// <param name="url">String of the URL to thumb nail</param>
        /// <param name="imageFormat">CustomImageFormat enum of the image format to return</param>
        /// <param name="width">Int of the width of the image to be returned</param>
        /// <param name="height">Int of the height of the image to be returned</param>
        /// <returns>Byte array of the thumb nail in the CustomImageFormat specified</returns>
        [WebMethod(Description = @"Generates a portion of a screen shot image of the URL specified with with width and height specified and returns the byte stream of it in the specified format.")]
        public byte[] GenerateWebsiteScreenshot(string url, CustomImageFormat imageFormat, int width, int height)
        {
            //set the private property values to the values passed in
            _url = url;
            _imageFormat = imageFormat;
            _width = width;
            _height = height;

            Thread apartmentThread = new Thread(new ThreadStart(generateScreenShot));

            //set the apartment state to single-threaded apartment
            apartmentThread.SetApartmentState(ApartmentState.STA);
            apartmentThread.Start();

            //block the calling thread so as to wait for the method to complete processing
            apartmentThread.Join();

            //return the private property value that was generated in the apartment state thread
            return _returnedImage;
        }


That was it. My web service method could now run and utilize the web browser object with no problems...

Happy threading!

Tags: , , ,

.Net | Web Services

Comments

9/1/2010 2:30:59 PM #

Daniel Blendea

I'm having the same problem with a Syncfusion component, html 2 pdf.
I get an "ActiveX control '8856f961-340a-11d0-a96b-00c04fd705a2' cannot be instantiated because the current thread is not in a single-threaded apartment."
I tried your solution, but it still doesn't work.

Apparently, because of the threaded way, the method that is supposed to generate the pdf, returns before actually generating the pdf, so all I get is a blank pdf.

Does your GraphicsManager.GenerateScreenshot(_url, _imageFormat, _width, _height); actually work?

Thanks,
Daniel

Daniel Blendea United States

9/1/2010 2:52:43 PM #

Heathesh Bhandari

Hi Daniel...

Yes the method does work. I've sent you an email, if you send me more details I might be able to help you figure out the problem.

Thanks,
Heathesh

Heathesh Bhandari South Africa

Comments are closed



Powered by BlogEngine.NET 1.5.0.7 (with enhancements by Heathesh)
Theme by Mads Kristensen (with tweeks by Heathesh)

Certifications

Microsoft Certified Professional

Microsoft Certified Technology Specialist

Answer Questions

 

Tag cloud

Calendar

<<  May 2017  >>
MoTuWeThFrSaSu
24252627282930
1234567
891011121314
15161718192021
22232425262728
2930311234

View posts in large calendar

http://heathesh.com