A nearshore team from Uruguay, South America (GMT-3) August 2011 - Posts - UruIT Blog

UruIT Blog

A nearshore team working with Microsoft technologies.

Sponsors

News

UruIT at WEBLOGS asp.net

August 2011 - Posts

Visual Studio 2010 Addin: Setting a class breakpoint

Introduction


There comes a time in the life of every .NET developer when you need Visual Studio to do something that
can only be described as a "class breakpoint": a quick command to set a breakpoint on every access to a class.
Unfortunately, after googling this concept, you'll find out that there's no easy way to accomplish this in Visual Studio.
In this article I present an addin that I created, which adds this and another similar command to the debug menu of the
development environment:

ConsoleApplication1 - Microsoft Visual Studio (Administrator)_2011-08-03_20-40-57

When the command is activated, it sets a breakpoint on every function and property of every class in the current document:

ConsoleApplication1 - Microsoft Visual Studio (Administrator)_2011-08-03_20-38-46

 

This addin can be downloaded here: Installer | Source code


Also, note that this addin is language agnostic, meaning that it will work for C#, Visual Basic, and even native C++ applications.

In the rest of the article I'll show the basic steps to create a simple addin for Visual Studio 2010.

 

Creating an addin project

New Project_2011-08-03_20-48-32

Visual Studio makes it easy to create an addin project by providing a template. In the New project dialog,
select Other project types, extensibility, Visual Studio Add-In.
You'll see that a very simple project is created, with the core logic around a class named Connect. This class manages
the lifecycle of the addin through the methods OnConnection, OnDisconnection, etc.
The class field _applicationObject holds a DTE2 object through which we communicate with the environment.

 

Handling events

In this particular case we want to add a command to the Debug menu after a solution is loaded. Therefore, we will need
to wait until a solution is loaded. All the solution events are exposed through the DTE2.Events.SolutionEvents object:

    _solutionEvents = _applicationObject.Events.SolutionEvents;  
    _solutionEvents.Opened += new _dispSolutionEvents_OpenedEventHandler(OnSolutionOpened);
    _solutionEvents.AfterClosing += new _dispSolutionEvents_AfterClosingEventHandler(OnSolutionClosed);

There's a minor caveat here. I'm keeping the reference to the SolutionEvents object in a field of the Connect class.
If I didn't do this, the SolutionEvents object would be deleted by the garbage collector, and the events would never
be raised.

Adding commands

Once that we handle the opening event, we need to add the command to the user interface:

    object[] contextGUIDS = new object[] { };  
    Commands2 commands = (Commands2)_applicationObject.Commands;
    string debugMenuName = "Debug";
    //Place the command on the debug menu.  
    //Find the MenuBar command bar, which is the top-level command bar holding all the main menu items:

    Microsoft.VisualStudio.CommandBars.CommandBar menuBarCommandBar =
        ((Microsoft.VisualStudio.CommandBars.CommandBars)_applicationObject.CommandBars)["MenuBar"];
    //Find the Debug command bar on the MenuBar command bar:   
    CommandBarControl debugControl = menuBarCommandBar.Controls[debugMenuName];
    CommandBarPopup debugPopup = (CommandBarPopup)debugControl;
    _command = 
        commands.AddNamedCommand2
        (
            _addInInstance,
            "CommandName",
            "Text to show in the menu",
            "Description of the command",
            true,
            Type.Missing,
            ref contextGUIDS,
            (int)vsCommandStatus.vsCommandStatusSupported + (int)vsCommandStatus.vsCommandStatusEnabled,
            (int)vsCommandStyle.vsCommandStylePictAndText,
            vsCommandControlType.vsCommandControlTypeButton
        );
      
    _command.AddControl(debugPopup.CommandBar, 1);

This code grabs the Debug menu and adds the command with the specified parameters. This code should be wrapped in a
try-catch block to handle cases when the command already exists in the menu.
We can also add a keyboard shortcut to the command in the following way:

    _command.Bindings = "Text Editor::ctrl+d, z"; 

Here "Text Editor" defines the scope of the shortcut. For more information see http://msdn.microsoft.com/en-us/library/envdte.command.bindings.aspx

 

Browsing the code

Visual Studio automatically parses the current document and exposes a nice interface to browse the code. A code document
contains a tree of code elements. Each code element can be a namespace, a class, a method, etc, and it contains a
collection of child code elements in it. The root code elements can be accessed in this way:

    CodeElements elementsInDocument = this._applicationObject.ActiveDocument.ProjectItem.FileCodeModel.CodeElements 

   
To show the browsing algorithm, here's a recursive method that shows how to get all the classes in the current document:
   

    private static void RecursiveClassSearch(CodeElements elements, List<CodeClass> foundClasses) 
    {
        foreach (CodeElement codeElement in elements)
        {
            if (codeElement is CodeClass)
            {
                foundClasses.Add(codeElement as CodeClass);
            }
            RecursiveClassSearch(codeElement.Children, foundClasses);
        }
    }
   

 

Managing breakpoints

Managing breakpoints is very straighforward. The interface exposed through this._applicationObject.Debugger.Breakpoints
is pretty self descriptive, and it contains functionally to add, remove and browse through breakpoints.

 

Installer

Once you finished you addin, the best way to distribute it is to use a Visual Studio Installer project. An addin consists
of only two files: an *.AddIn xml file and a dll. The easiest way to distribute them is to install them in the same
directory, anywhere on the target machine (might be in ProgramFiles), and to add that directory to the addins directories
of Visual Studio. The latter can be done easily with a registry key: In the registry editor window of your installation
project, add a string key at "HKLM\Software\Microsoft\VisualStudio\10.0\AutomationOptions\LookInFolders" with the name
[TARGETDIR] and a descriptive name in the value. The installer will resolve the [TARGETDIR] placeholder at runtime.

 

Download

This addin can be downloaded here: Installer | Source code

 

CodePlex project

Here's the CodePlex site for this project: http://breakall.codeplex.com

 

Conclusions

In this article I presented a useful addin for Visual Studio and I also showed how to create customs addins. For more information on creating addins you can visit the MSDN: http://msdn.microsoft.com/en-us/vstudio/ff677564. I hope you find the addin useful as I do (maybe I'll publish a second version in the future) and I hope to see your great addins soon!

Thanks for reading!
Alfonso Cora



                                            
            
Cross-protocol login control with JSONP in SharePoint

Overview

This article describes the specific problem of creating a control in SharePoint, which will be located on an HTTP page and securely signs in a user through an SSL connection using HTTPS. Although the solution uses SharePoint exclusively, it can be easily modified for ASP.NET environments.

The problem

With the requirement that the user data should transferred securely to the server, the idea of a postback becomes unfeasible. At that point one might come with the idea of an asynchronous and secure request that carries the user data.

Those of you have worked with AJAX, might have stumbled upon asynchronous requests with jQuery more than once. While this type of requests allows building very rich internet applications, most of them have a key restriction regarding protocols and domains. Common requests cannot retrieve data from a different domain than the one hosting the application, and the protocol for the request must also be the same as the one used to retrieve the page.

Knowing of these limitations, our mind is focused to those AJAX requests that allow cross-protocol calls.

JSONP at the rescue

JSONP is an extension to the well known standard JSON, where the added "P" stands for "Padding". The padding is usually a JavaScript function call (might be any JavaScript code too) that is added by the server as a prefix to the data returned. These are two requests and responses from a server in both, JSON and JSONP formats:

JSON request:

        http://example.com/planets?json=true&planetid=4

JSON response:

        {
            "id" : 4,
            "name" : "Mars",
            "satellites" : 2
        }

JSONP request:

        http://example.com/planets?jsonp=true&planetid=4&jsonpcallback=callthisfunction

JSONP response:

        callthisfunction({            
            "id" : 4,
            "name" : "Mars",
            "satellites" : 2
        })     

As you can see, the only differences between both requests, is the "jsonpcallback" parameter, which is used as a function call to wrap the data in the response. This parameter can actually have any name, but in order to make it work with jQuery, it needs to end with the suffix "callback".

So, when a web page has a script tag with the previous request as the source, JSONP comes to life

        <script type="text/javascript" src="http://example.com/planets?jsonp=true&planetid=4&jsonpcallback=callthisfunction"></script>

Script tags can bring scripts from any domain, using any protocol. Using the previous script tags, not only brings the data from the server, but also calls

the callback function, allowing the client to run any desired code.

 

Putting all together

Now that we know the power of JSONP, we can use it to design our solution. On the client side, we will use a web part that uses jQuery to perform the request. On the server side, we will have an ASP.NET handler (.ashx) that logs the user in, or returns an error if the user or password supplied are invalid.

Let's begin with the server side. To publish the handler from SharePoint, we need the ashx file to look something like this:

    <%@ Assembly Name="Microsoft.Web.CommandUI, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
    <%@ Assembly Name="MyNamespace", Version=1.0.0.0, Culture=neutral, PublicKeyToken=a39c10d65c32c9c1, processorArchitecture=MSIL" %> 
    <%@ WebHandler Language="C#" Class="MyNamespace.MyHandler" %>
 

For the code file, we will use the SharePoint method "SPClaimsUtility.AuthenticateFormsUser" to perform the login. But also, we have to return a JSONP

response indicating whether that method succeeded or not. Let's see the easiest way of doing it:

    public partial class MyHandler : IHttpHandler
    {
        public bool IsReusable
        {
            get { throw new NotImplementedException(); }
        }
 
        public void ProcessRequest(HttpContext context)
        {
            context.Response.ContentType = "text/html";
            context.Response.Headers.Add("Access-Control-Allow-Origin", "*");
            // Parse the JSON callback
            string callback = context.Request["callback"] ?? string.Empty;
            callback = Regex.Replace(callback, @"[^a-zA-Z0-9\?]", "");
            string user = context.Request["User"];
            string password = context.Request["Password"];
            if (SPClaimsUtility.AuthenticateFormsUser(context.Request.Url, user, password))
            {
                context.Response.Write(callback + "({ \"result\" : true });");
            }
            else
            {
                context.Response.Write(callback + "({ \"result\" : false });");
            }
        }
    }

From this code, you can see how we parse the "callback" parameter, parsing the basic characters on it, and we use it to send the response to the client, which is an object containing only the "result" boolean property.

Another thing interesting in this code is the "Access-Control-Allow-Origin" header, which filters the domains that can request data from this server. By using the character "*", we are allowing any domain to request data from our server.

Now, let's see the client side. When a user enters his information in the "txtUser" and "txtPassword" fields, this script does the trick:

        function onLogin(evt, message) {
            var user = $("#txtUser").attr("value");
            var password = $("#txtPassword").attr("value");
            $.getJSON(
                https://example.com/_layouts/MyNamespace/MyHandler.ashx?callback=?,
                {
                    User: user,
                    Password: password
                },
                function (data) {
                    if (data.result) {
                        // Login succeeded, redirect to home page:
                        window.location = "https://example.com/default.aspx"; 
                    }
                    else {
                        // Login failed, show error message without leaving the page:
                        document.getElementById('lblLoginResult').innerHTML = "Wrong Userid or Password";
                    }
                }
            );
        }

 

To perform a JSONP request in jQuery, we just perform a normal JSON request. jQuery automatically detects the string "callback=?" in the url and

performs the request as JSONP. The function we supplied as the callback, is called with a JavaScript object, which is parsed by jQuery from the response

sent by the server.

We now have both sides working. We have built a login control that works on every browser, protects the users' security, and efficiently uses

HTTP/HTTPS protocols.

 

Error handling

Although some of the errors can be seen on the browser's error console, there is no way to handle JSONP errors in JavaScript. This is one of the main drawbacks of the protocol. When an error occurs, it just fails silently.

 

Security concerns

As we saw, JSONP is a powerful tool that makes possible some scenarios that would otherwise be unfeasible. However, it should be used with extreme care, especially with cross domain requests. Remember that the padding is nothing more than JavaScript code that will be run by the user's browser. This gives the external server, the possibility to send a malicious script to start an attack.

 

Conclusions

We have seen JSONP in action, resolving a specific problem in a very simple way. However, we also saw the drawbacks of this approach. Like any other tool, it is useful to keep in mind how powerful it is, the kind of problems it addresses, and the weak points it presents.

 

Thanks for reading!

Alfonso Cora

More Posts