How to use .NET WebBrowser to take screenshots

Do you need to take snapshot of some urls? You just want a headless browser? You are using .NET? You don’t want to rely on node/phantom or on a .net library (awesomium, cefsharp, webkitdotnet)? You love IE? Yes to all? This post is for you!

Because we can’t do a screenshot in modern browsers

If you have control of your webapplication / website that needs to take the screenshots, you know that is no such thing in current browser, no standard whatsoever (but the WebRTC is coming !). Maybe you encountered the client library : html2canvas (and  cansvg if you are dealing with svg) but this is not enough. It’s good for svg and canvas, but as soon as some html5 controls are in or complex style, it won’t do a nice job. You need a true browser that renders the page nicely, is waiting for ajax, pictures and so on, then call a method on it to get a snapshot.

Let’s emulate IE (yes, we want!)

Hopefully for you, .NET WebBrowser control is there. It is not suitable in every situation (take a coffee and read this stackoverflow post), but if you just want a snapshot of a given url, it won’t be a problem.

STAThread

First of all, you need a thread in STA (Single Thread Apartment) mode. If you want to take a snapshot from WCF or a console app, or any non-STA thread program, you need to create this kind of thread like this :

        Dim ARE As New AutoResetEvent(False)
        Dim tWebBrowserSnapshot As Bitmap = Nothing
        Dim t = New Thread(Sub()
                               Try
                                   Using tWebBrowser = New WebBrowser()
                                   ...
                                   End Using
                               Finally
                                   ARE.Set()
                               End Try
                           End Sub)
        t.SetApartmentState(ApartmentState.STA)
        t.Start()
        ARE.WaitOne()
        Return tWebBrowserSnapshot

Snapshot me

The browser is created, yeepee! You can now add some code to give it some style/size.

tWebBrowser.ScrollBarsEnabled = False
tWebBrowser.ScriptErrorsSuppressed = True
tWebBrowser.Width = myWidth
tWebBrowser.Height = myHeight
tWebBrowser.Navigate(myUrl)

Then you can take a snapshot because it has a nice method: DrawToBitmap.

Dim tBitmap = New Bitmap(tWebBrowser.Width, tWebBrowser.Height)
tWebBrowser.DrawToBitmap(tBitmap, New Rectangle(0, 0, tWebBrowser.Width, tWebBrowser.Height))

Snapshot logic

The main logic is there, but if you try that, you can run into severals issues. (such as blank image)

First, don’t forget that it’s a ‘true’ browser that renders the page. So, it takes time for it to load the page, then if it has ajax or images, it will call them, process the response etc. There is no magic flag that is set when everything is loaded on the page (to wait to call DrawToBitmap). WebBrowser has some events (such as DocumentCompleted) but it’s like the jquery $(document).on(‘ready’, fn), nothing is done yet. If you don’t know the targetted url, you don’t have a lot of choices :
– wait a finite amount of time to be almost sure everything is loaded
– maybe you can try to catch every ajax (by injecting some script into the document) the website is doing then count how many came back (but still, add a max timeout otherwise you could wait an infinite time).
– if you control the website on the url, create a variable that will be set to some value (true), and just wait for that.

Windows Registry is there for you

If you did that, you can still have blank pages. Why ? Because WebBrowser can use a old IE engine to render the page, which is not compatible. Because your server is up to date, you want to use IE11. To do that, you need to add a key in the registry.

HKEY_LOCAL_MACHINE
   SOFTWARE
      Microsoft
         Internet Explorer
            Main
               FeatureControl
                  FEATURE_BROWSER_EMULATION

Add a REG_DWORD with the process name (if you are using IIS, the process is w3wp.exe; if you are using a console app, put the name of your exe) and as data: 0x2AF9 (check msdn to see the possible values, IE10 is 0x2711).

IIS will help too

Last thing, if it’s a WCF that does the screenshot, and if you are using IIS, you need to change who is running the pool to LocalService.

Have a nice screenshot !

404 error on Chrome when getting jquery minified map

With a recent upgrade of Chrome we have been seeing the following happening each time we open the console:

GET https://ajax.aspnetcdn.com/ajax/jQuery/jquery.min.map 404 (Not Found)

 

As it turns out, Chrome is trying to get the debug map for the minified version of jQuery automatically if the following setting is enabled:

Capture

 

If you do not want to see these in your console just disable this option et voila….

 

Fiddle me this! Add a header to all requests regardless of calling application…

At ClicData we love Fiddler… For those that don’t know Fiddler is a windows application that allows you to see what’s going on between your browser and the server.  Think of it as the app that keeps an eye on everything that your computer sends and receives to the internet.

Fiddler is one of the few tools that does this  job very well and when debugging web services, web sites, security issues, and all types of http and https communication, it offers tremendous power and flexibility.

Nicolas already blogged about ways to debug WCF Services with it in this blog.  Today I would like to let you know about one feature that is quite cool.

When using secured web services like we do at ClicData sometimes we need to send “hidden” tokens in the header.  So testing the web service using a browser becomes quite difficult since we typically get Access Denied (401) or a validation error.

So with Fliddler you can just access Composer and type your URL and using GET/POST, etc… build your header:

2013-06-06 20-50-42

So for example, if we wanted to ensure that the web service returned JSON data as opposed to the default (in our case XML) we could add this “accept: application/json” to the Request Headers text area.

However, my issue was that I wanted to use Microsoft Excel to connect directly to the web service.  As it turns out we are currently working on supporting OData so you can connect to your data directly with Excel but our security was not letting Excel connect to it.

So after consulting this entry in Fiddler’s web site it was fairly easy to modify Fiddler to automatically add a header regardless of the application that calls it.  In our case we wanted to add oSession.oRequest[“AuthorizationKey”] = “xxxxxxx-xxxxx-xxxxxxxxx-xxxxxx”; to each request.

2013-06-06 20-49-16

Access Fiddler’s Rules –> Customize Rules menu

2013-06-06 20-49-43Write a little script to add a header to each request before sending 

If you are using Windows Azure, a similar technique is used in this blog post to access storage data using Excel.

How to vertical-align an element in css using flexbox

Forget about :

  • display: table
  • display: table-cell
  • height: 100px / line-height: 100px / line-height: 1
  • top: 50% / margin-top: -25px
  • or why not some JS ? parent.style.marginTop = “-” + (parent.clientHeight/2) +”px”;

You can use the css flexbox layout to do that without any trick, and being more configurable.
You just have to set some flexbox properties on the container that contains what you wish to center.

If you haven’t heard of flexbox, go to read W3C Spec or more friendly Using CSS flexible boxes for instance.


<div class="container">
    <span>I wish I could be centered</span>
</div>
<hr />
<div class="container">
  <span>I wish I could be centered</span>
  <span>I wish I could be centered</span>
</div>

.container {
   display: flex;
   flex-direction: row; /* default is row */
   justify-content:center; /* default is stretch */
   align-items: center; /* default is flex-start, center is like a text-align: center in our case (because we are in row mode) */
   height: 200px;
   width: 500px;
   background: #aac;
}
.container > * { padding: 5px; border: 1px solid black; background: white; }

The result is http://jsfiddle.net/derste/jhuH5/1/ :

flex hcenter
Using flex-direction: column, the result would be :

flex vcenter

Chrome support

Just add the well known “webkit-” in front of flex properties.

.container {
    display: -webkit-flex;
    -webkit-justify-content: center;
    -webkit-align-items: center;
    height: 200px;
    width: 500px;
    background: #aac;
}

Firefox support

Using firefox, you need to enable the flexbox (disabled by default) (yes, ask your users to do it, right) :
firefoxflex

Cross-browser

Well, for now, only Chrome (>=21) has a good support using -webkit prefix, Firefox is using the W3C notation (>=22) with the trick in about:config, Opera (>=12.10) works, IE10 and Safari are using the old draft of flexbox, so not compatible.

Have fun !

Start Visual Studio 2012 in administrator mode even from shortcuts

In Windows 7 and 8 you most likely want to start Visual Studio in administrator mode all the time.  That means, starting VS from the taskbar, start menu, double clicking on the .sln file or from a shortcut to a solution fle.

Here is the sure fire way to do it:

1. Go to C:Program Files (x86)Microsoft Visual Studio 11.0Common7IDE and find devenv.exe

2.  Right click on devenv.exe and select “Troubleshoot compatibility

Image

3. Once the “Program Compatibility Troublehsooter” windows appears…

Image

select “Troubleshoot program“, click Next

4. At the next prompt check the “The program requires additional permissions

Image

and click the Next button.

5. At the next prompt click the “Test the program…” button

Image

6. Visual Studio will start, the User Account Control (UAC) window will popup asking for permission.

7. Return to the troubleshooter window and click “Next”

8. Click on “Yes, save these settings for this program”

Image

9. Click on “Close” and you are done.

Now you can double click on a solution file (.sln) or a shortcut to a solution file and VS will always start in administrator mode.

Gradient backgrounds in CSS

Gradient background

We are used to css3 gradient backgrounds, such as this one :

background: #258dc8; /* Old browsers */
background: -moz-linear-gradient(top, hsla(202,69%,47%,1) 0%, hsla(202,69%,47%,1) 100%); /* FF3.6+ */
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,hsla(202,69%,47%,1)), color-stop(100%,hsla(202,69%,47%,1))); /* Chrome,Safari4+ */
background: -webkit-linear-gradient(top, hsla(202,69%,47%,1) 0%,hsla(202,69%,47%,1) 100%); /* Chrome10+,Safari5.1+ */
background: -o-linear-gradient(top, hsla(202,69%,47%,1) 0%,hsla(202,69%,47%,1) 100%); /* Opera 11.10+ */
background: -ms-linear-gradient(top, hsla(202,69%,47%,1) 0%,hsla(202,69%,47%,1) 100%); /* IE10+ */
background: linear-gradient(to bottom, hsla(202,69%,47%,1) 0%,hsla(202,69%,47%,1) 100%); /* W3C */
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#258dc8', endColorstr='#258dc8',GradientType=0 ); /* IE6-9 */

gradient0

This is pasted from http://www.colorzilla.com/gradient-editor/ (preset Blue Flat #1)
This is the best online editor to make gradients and contains a lot of presets.


Stops

A gradient has a list of gradient-stop : at least 2, for the beginning and for the end, and it can have others between to add variations.
For Blue Flat, if we look only the W3C css :

background: linear-gradient(to bottom,
hsla(202,69%,47%,1) 0%, /* beginning */
hsla(202,69%,47%,1) 100% /* end */
);

For instance, let’s add 2 between :

background: linear-gradient(to bottom,
hsla(212,67%,36%,1) 0%, /* beginning */
hsla(207,69%,51%,1) 50%, /* one at 50 */
hsla(208,73%,46%,1) 51%, /* one at 51 */
hsla(206,70%,70%,1) 100%); /* end */

gradient1

This kind of stops (one just next to the other) permits to create a direct break in the gradient : to change the color from 50% to 51%, it’s only 1% of distance of the container, so, almost invisible, you just see a break.


Direction

The other parameter is the direction of the gradient. In thoses examples, it’s “to bottom”(vertical), but you can set it horizontally “to right”,  or specify an angle 135deg, or else, make a radial gradient that starts from the center : ellipse at center or radial-gradient(at 50% 50%) or wherever you want using %.

gradient1

gradient2

gradient3

gradient4


Cross-browser issues

This is beautiful but we all know that the browsers handles css differently. For instance, to make a radial, you can find the notation : (center, ellipse cover,.., for a vertical gradient, you can find : “to bottom” for “top”. Moreover, the way to specify the gradient stops is also different : in Chrome it’s color-stop(0%,hsla(212,67%,36%,1)). And we don’t talk about <IE10 with the filter: progid:DXImageTransform.Microsoft.gradient( startColorstr=’#1e5799′, endColorstr=’#7db9e8′,GradientType=0 );

It’s for this you should use a generator that won’t do mistakes.

The good news, is that if you support only recent browsers (such as IE10, FF20, Chrome 27), know that the W3C notation is working on those one (I don’t know since which version exactly), no need for all the variations.


background-size

You can combine the gradient with the property background-size. I’m not talking about “cover” and “contain” value, but the px and % notation (mostly the px for that).
For instance :

/* background-size: <width>px <height>px */

div {
    background: linear-gradient(to bottom, hsla(192,69%,47%,1) 0%,hsla(202,69%,47%,1) 100%);
    background-size: 100px 10px;
  /* background-repeat: repeat; this is by default but it is needed for the effect! */
}

This will render a nice tiled background of my gradient (a repeated tile of 100px 2px) (http://jsfiddle.net/xQxbe/)
gradient5

You can create things like an interlaced overlay, or old school effect with a radial gradient (http://jsfiddle.net/zhp3H/)
gradient6


Test it!

Give a try on this jsfiddle to see all that in action !

gradient7

http://jsfiddle.net/DVqmG/1/


Multiple background

Know that the gradients are compatible with the css3 multiple background. For browser support, check http://caniuse.com/#feat=multibackgrounds (it’s been a while it’s around). For instance, let’s make a tiled radial of 20px*20px a bit faded on its left (http://jsfiddle.net/fP2tR/):

    background:
        linear-gradient(to right, rgb(255, 255, 255) 0%, rgba(255, 255, 255, 0) 100%),
        radial-gradient(ellipse at center, hsla(212,67%,36%,1) 0%,hsla(206,70%,70%,1) 100%);
    background-size:
        100% 100%,
        20px 20px;

gradient8

You can combine that with traditional background url(…), background-repeat, background-position properties to make crazy stuff. (http://jsfiddle.net/2TpFT/)

body {
    background:
        radial-gradient(at 74px 32px, rgb(255, 255, 255) 0%, rgba(255, 255, 255, 0) 30%),
        url(http://clicdata.com/Images/clicdata2.png),
        linear-gradient(to right, rgb(255, 200, 200) 0%, rgba(255, 155, 155, 0) 50%, rgb(255, 200, 200) 100%),
        url(http://clicdata.com/Images/pricing/pricing.jpg),
        linear-gradient(to right, rgb(255, 255, 255) 0%, rgba(255, 255, 255, 0) 100%),
        radial-gradient(ellipse at center, hsla(212,67%,36%,1) 0%,hsla(206,70%,70%,1) 100%);
    background-size:
        100px 100px,
        200px 30px,
        60px 200px,
        200px 200px,
        100% 100%,
        20px 20px;
    background-position: 150px 20px, 50px 50px, 50px 200px, 50px 200px, top left, top left;
    background-repeat: no-repeat, no-repeat, repeat-x, repeat-x, repeat, repeat;
}

gradient9

Have fun !

$.ajax errors handling

Why jqXHR.responseText is not enough

You have a HTML/JS application, and some RESTful JSON webservices behind. When an error occurs in your webservices, you throw an WebFaultException exception 404, 500 or whatever you want, with a nice message or a proper error structure such as :

        [WebGet(UriTemplate = "Crash")]
        public List<string> CrashMe()
        {
            throw new WebFaultException<CustomError>(new CustomError() { Code = 42, Message = "I just crash" }, System.Net.HttpStatusCode.Forbidden);
            // or you can just send a string
            // throw new WebFaultException<string>("I just crash as string", System.Net.HttpStatusCode.Forbidden);
        }

Then JS side :

 var tGeneralWS = $.get('Crash')
    .done(...)
    .fail(function (e) { ... });

So, my webservice crash and here what I got :

standard error js

That’s nice, but I have to parse this string if I want to do something with that.

parsejson

And because I don’t want to do that in all my error handler for multiple reasons : I could have different error type to handle, I could do special thing according to the error, and the more important, I want to write my logic only once in the application in case that changes, so let’s fix that.

Let’s try to create a property .ErrorMessage in the jqXHR we could access in our error handler if needed.


$.ajaxError

You could think to use the global handler $.ajaxError this way :

$(document).ajaxError(function (event, jqXHR, settings, exception) {
  // parse the responseText to .ErrorMessage
  try { jqXHR.ErrorMessage = JSON.parse(jqXHR.responseText); }
  catch (e) { }
});

But it will be too late if you need to .ErrorMessage in your own handler .error() or .fail; they will be called before the global error handler so .ErrorMessage won’t be set.

Just to remind you the order : (source: http://api.jquery.com/Ajax_Events/)

  • ajaxStart (Global Event)
    • beforeSend (Local Event)
    • ajaxSend (Global Event)
    • success (Local Event)
    • ajaxSuccess (Global Event)
    • error (Local Event)
    • ajaxError (Global Event)
    • complete (Local Event)
    • ajaxComplete (Global Event)
  • ajaxStop (Global Event)

Override the local error handler

Because the global ajaxError is too late, we need to use the error handler. But because it’s almost sure that there is already an error handler attached to the jqXHR, we will just call the existing one in our own.

$.ajaxPrefilter(function (options, originalOptions, jqXHR) {
  // if you are not using error: function(e) { ... } in $.ajax, this is not needed
  var error = options.error;

  // set the .error handler to add custom logic (and calls existing .error at the end if there is already an .error handler)
  options.error = function (jqXHR, textStatus, errorThrown) {
    // parse the responseText to .ErrorMessage
    try { jqXHR.ErrorMessage = JSON.parse(jqXHR.responseText); }
    catch (e) { }
    // override error handling if exists
    if ($.isFunction(error)) {
      return $.proxy(error, this)(jqXHR, textStatus, errorThrown);
    }
  };
});

Basically :
– we save the old error handler
– we set the new error handler that will
* parse the responseText into ErrorMessage
* call the old error handler if exists

So, in your custom error handler, you have your custom variable, and whatever you want to add to the jqXHR at this time according to the error.


New variable available in jquery recent versions

All that to said that this trick is now useless in modern version of jquery (>= 1.10.1), because you have now access to a new variable in your error handler : jqXHR.responseJSON and its xml equivalent jqXHR.responseXML that will contain the parsed version of the responseText.

recent jquery json error

Have fun !

Including SQL Server 2012 SMO libraries in your .net solution

SQL Server 2012

Our issue is  that we are deploying a set of WCF to web servers and we need to distribute the required DLLs to access Microsoft Access SQL Server 2012 using SQL Server Management Objects (SMO).  Using SMO presents numerous advantages versus SQL string concatenation and replaces long and confusing strings containing CREATE and ALTER statements with a nice object oriented approach to schema creation and changes.

Specifically we are looking at the following DLLs (the ones for 2008 R/2 – our previous version – and the ones for 2012).

  • Microsoft.SqlServer.ConnectionInfo.dll
  • Microsoft.SqlServer.ConnectionInfo.dll(2008)
  • Microsoft.SqlServer.ConnectionInfoExtended.dll
  • Microsoft.SqlServer.Management.Sdk.Sfc.dll
  • Microsoft.SqlServer.Management.Sdk.Sfc.dll(2008)
  • Microsoft.SqlServer.Smo.dll
  • Microsoft.SqlServer.Smo.dll(2008)
  • Microsoft.SqlServer.SmoExtended.dll
  • Microsoft.SqlServer.SqlClrProvider.dll
  • Microsoft.SqlServer.SqlClrProvider.dll(2008)
  • Microsoft.SqlServer.SqlEnum.dll
  • Microsoft.SqlServer.SqlEnum.dll(2008)

All of the above DLLs are easily found either by installing the SMO SDK from the feature pack (http://www.microsoft.com/en-us/download/details.aspx?id=29065) or by looking at the install folder of SQL Server 2012 Developer edition.  However one of them is particularly hard to find since it is in the GAC – Microsoft.SqlServer.SqlClrProvider.dll.

This one you need to go to “C:WindowsassemblyGAC_MSILMicrosoft.SqlServer.SqlClrProvider11.0.0.0__89845dcd8080cc91” but to go there you may need to disable the GAC shell extension (see this blog).

Not sure why this DLL is not readily available as it is a pre-requisite to other DLLs.