ICodeFactory Labs

Workaround for removing broken VSTO bookmark object after manual removal from the document

by Janko 12. July 2010 23:14

Bookmarks are very useful in developing and working with documents. Besides content controls, they are the main tool for our Word add-in project and are used as placeholders to mark items or locations in the Word document. Although developers would like bookmarks to have more options and operations like content controls, they are just not as flexible. Some options that could be supported by VSTO 3 but, unfortunately are not, could make our lives as developers easier:

  • before delete and after add events,
  • locking bookmark to prevent deleting,
  • locking bookmark and its content,
  • deleting bookmark with its content…

All these options are supported for content controls.

 

This post is about a problem that exists when user deletes a bookmark from the document manually. There is no event triggered when this happens. Deleting bookmarks manually causes its Interop.Word.Bookmark object to be removed, but Tools.Word.Bookmark object remains in the documents controls collection. We can access this remaining object but access to its properties and methods will throw exception with message "Object has been deleted". In this state it can not even be removed from the document.Controls collection. So if user removes the bookmark manually, our application will be unaware of that and will break when trying to access its properties, methods or even to remove it.

 

Fortunately, there is a way to “repair” this broken Tools.Word.Bookmark object enough for us be able to remove it from the collection of document’s VSTO objects. Then we are able to recover our application from this unwanted user action. Every time we want to get any bookmarks we use the following method:

 

Public Function GetBookmarkIfExists(ByVal bookmarkName As String) _ 
                                    As Tools.Word.Bookmark
        Dim returningControl As Tools.Word.Bookmark = Nothing
       
        ' If bookmark exists in word document, get it:
        If Me.Controls.Contains(bookmarkName) Then
            returningControl = Me.Controls(bookmarkName)

            ' If user has deleted bookmark manually, VSTO object exists but interop
            ' not. To recover from this unwanted state it is needed to remove VSTO
            ' object too:
            If (Not (Globals.ThisDocument.Bookmarks.Exists(bookmarkName))) Then
                Globals.ThisDocument.Bookmarks.Add(bookmarkName, Me.Range(0, 0))

                ' Remove all bindings from the bookmark and delete it:
                returningControl.DataBindings.Clear()
                returningControl.Delete()
              
                returningControl = Nothing
            End If
        End If
        Return returningControl 
End Function 

The method is in the document class.

 

This method checks if the corresponding Interop object exists for the VSTO object. If not, the VSTO object it broken. By adding interop object with the same name, VSTO object will be “repaired”. Range of repaired bookmark will not be as it was before removal but it is deletable now. Deleting bookmark will not delete its content.

 

Of course this method needs to be adapted to your code and application.

Currently rated 1.5 by 147 people

  • Currently 1.530612/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags: , , ,

VSTO | .NET

Workaround for VSTO bug: Content control Exiting event handler

by Janko 21. May 2010 02:04

If you have ever worked on some Word add-in where you use content controls, you maybe have encountered problems for which you have no explanations. This post is about one of the known VSTO issues and offers a workaround for it.

 

We were working on Word add-in that uses content controls. Also we needed to process data on entering and exiting a content control.

 

Good thing is that VSTO offers events Entering and Exiting.

Bad thing is that Exiting event has a bug. If you are doing some "simple" process in Exiting event handler it could work fine, but you never know what will happen. Using this event could produce very strange problems, problems which look like they should not have any connections with the event.

 

Several strange problems have occurred for which we were unable to find a solution. So we created different POC projects to reproduce and isolate the bugs and posted them on the Microsoft forum. Shortly the issues were confirmed - one of them as a new VSTO bug (https://connect.microsoft.com/VisualStudio/feedback/details/556456/programatically-deselecting-a-content-control-results-in-a-com-exception?wa=wsignin1.0) and another got recognized as a notorious VSTO bug. After using proposed workaround for notorious bug, both bugs were resolved.

 

The notorious problem manifests itself in following way:

In the document there are two building block content controls:

  1.  POCControl1 and
  2.  POCControl2

On the document startup we are adding Entering and Exiting event handlers to these controls.

-On Entering the control TrackChanges is set to TRUE and old value is remembered.

-On Exiting all changes are confirmed and TrackChanges is returned to the previous version.

Problem occurs in following scenario:

  1. Select POCControl1
  2. Entering event occurs.
  3. Unselect POCControl1.
  4. Exiting event occurs.
  5. PROBLEM: on line
    Me.TrackRevisions = Me._oldTrackRevisionsStatus
    Entering event for POCControl1 occurs (which should not happen). After it finishes, Exiting event continues to run from the specified line.

Even stranger behavior occurs when selecting POCControl1 and then selecting POCControl2. Here is the whole POC code:

 

Public Class ThisDocument

    Private _oldTrackRevisionsStatus As Boolean

    Private Sub ThisDocument_Startup(ByVal sender As Object, _
                                     ByVal e As System.EventArgs) Handles Me.Startup
        AddHandler POCControl1.Entering, AddressOf TextItemSelected
        AddHandler POCControl2.Entering, AddressOf TextItemSelected
        AddHandler POCControl1.Exiting, AddressOf TextItemUnselected
        AddHandler POCControl2.Exiting, AddressOf TextItemUnselected

        POCControl1.Tag = "UPPER control"
        POCControl2.Tag = "LOWER control"
    End Sub

    Public Sub TextItemSelected(ByVal sender As BuildingBlockGalleryContentControl, _
                                ByVal e As ContentControlEnteringEventArgs)
        MsgBox("Select " & sender.Tag & " beggining")

        ' Set track changes:
        _oldTrackRevisionsStatus = Me.TrackRevisions
        Me.TrackRevisions = True

        MsgBox("Select " & sender.Tag & " ending")
    End Sub

    Public Sub TextItemUnselected(ByVal sender As BuildingBlockGalleryContentControl, _
                           ByVal cevent As Tools.Word.ContentControlExitingEventArgs)
        MsgBox("Unselect " & sender.Tag & " beggining")

        If sender.Range.Revisions.Count > 0 Then
            sender.Range.Revisions.AcceptAll()
        End If

        Me.TrackRevisions = Me._oldTrackRevisionsStatus

        MsgBox("Unselect " & sender.Tag & " ending")
    End Sub
End Class

 

Proposed solution:

Do not use Exiting event at all. For example, use Application.WindowSelectionChange event instead, which we did. This event is triggered on every selection change in the active document window, whenever it is manually or programmatically caused. In its handler we are tracking "exiting" from the control.

 

So we removed Exiting event handler and related code from our POC project and added WindowSelectionChange event handler:

 

Private _oldTrackRevisionsStatus As Boolean
    Private selectedControl As Tools.Word.BuildingBlockGalleryContentControl

    Private Sub ThisDocument_Startup(ByVal sender As Object, _
                                     ByVal e As System.EventArgs) Handles Me.Startup
        AddHandler POCControl1.Entering, AddressOf TextItemSelected
        AddHandler POCControl2.Entering, AddressOf TextItemSelected
        
        POCControl1.Tag = "UPPERControl"
        POCControl2.Tag = "LOWERControl"
    End Sub

    Public Sub TextItemSelected(ByVal sender As BuildingBlockGalleryContentControl, _
                                ByVal e As ContentControlEnteringEventArgs)
        MsgBox("Select " & sender.Tag & " beggining")

        selectedControl = sender

        ' Set track changes:
        _oldTrackRevisionsStatus = Me.TrackRevisions
        Me.TrackRevisions = True

        MsgBox("Select " & sender.Tag & " ending")
    End Sub

    
    Public Sub WindowSelectionChange_Handler(ByVal sel As Interop.Word.Selection) _
                                      Handles ThisApplication.WindowSelectionChange
        Dim control As Interop.Word.ContentControl = sel.Range.ParentContentControl
        If selectedControl IsNot Nothing _
         AndAlso (control Is Nothing OrElse control.Tag <> selectedControl.Tag) Then
            MsgBox("Unselect " & selectedControl.Tag & " beggining")

            If selectedControl.Range.Revisions.Count > 0 Then
                selectedControl.Range.Revisions.AcceptAll()
            End If

            Me.TrackRevisions = Me._oldTrackRevisionsStatus

            selectedControl = Nothing

            MsgBox("Unselect ending")
        End If
    End Sub

 

If you have issues with content controls and you are using Exiting event, it is possible that it is the root of your problems. Use WindowSelectionChange event and adapt its handler for your own code. It helped us and might potentially help you get rid of some annoying bugs and unwanted behavior.

Currently rated 1.6 by 55 people

  • Currently 1.58182/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags: , , ,

ICF.Labs | .NET | VSTO

Dynamically adding ASP.NET validators and ASP.NET ajax validation callout extenders using Javascript

by Lacio 13. May 2010 01:33

Have you ever encountered a problem where you generated some content dynamically without the help of the server and server side code, but still needed to perform some basic validation on those contents?

That is exactly what this post deals with – validating content which is taken from dynamically generated input fields and adding the validation callout extender to it. 

Since I did not find any suitable solutions online, I decided to do it myself. Turned out the solution is not so hard at all, and after a small analysis on the page source of pages with regular validation, I got an idea on how to do it.

The first thing to notice is that after the aspx page is parsed, the output of a validator control (of any type) is a SPAN tag. Sort of. The tag has some unconventional attributes, off course depending on the type of the validator used, but we will get to that later. Besides this, a reference to the span is kept in an array called Page_Validators, which is an array consisting of all the validators present on the page.

So let’s go step by step.

First create the span tag and place it in the DOM where you would place the validator control. Lets say we have an input field and we place a RequiredFieldValidator next to it, like in the code below:

 

               // Get the table:
                var tblEditItems = $get('tblEditItems');
                // Get the number of rows currently in the table:
                var rowsCount = tblEditItems.rows.length;
                // Insert a new row at the end of the table:
                var row = tblEditItems.insertRow(rowsCount);

                // Insert a table cell:
                var cellName = row.insertCell(0);
                // Create an input field for the name:
                var elName = document.createElement('input');
                elName.type = 'text';
                elName.size = 10;
                elName.id = 'txtProductName' + globalControlCounter;
                cellName.appendChild(elName);

                // Add the span that will represent the validotor
                // (in this case the required field validator):
                var elNameValidator = document.createElement('span');
                elNameValidator.style.color = "Red";
                elNameValidator.style.display = "none";
                elNameValidator.id = elName.id + "Validator";
                elNameValidator.controltovalidate = elName.id;
                elNameValidator.errormessage = "<b>Field is incorrect</b>
                                     <br /> <span>Name is a required field.</span>";
                elNameValidator.validationGroup = "EditItems";
                elNameValidator.initialvalue = "";
                elNameValidator.evaluationfunction =
                                              RequiredFieldValidatorEvaluateIsValid;

 

The next step is to add the validator to the array of existing validators:

 

                // Push the new validator inside the page validators array:
                Page_Validators.push(elNameValidator);

 

After this is done, we can add the validator callout extender control, that will add some nice pop out effects displaying the error message itself:

 

                // Now lets bind the validator callout that will popup
                // up when the field is not valid:
                $create(AjaxControlToolkit.ValidatorCalloutBehavior, {
                    "closeImageUrl": "/image/close.png",
                    "highlightCssClass": "highlight",
                    "id": elNameValidator.id + "ValidatorCalloutExtender",
                    "warningIconImageUrl": "../images/attention.png"
                }, null, null, $get(elNameValidator.id));

 

The last step that needs to be taken and is essential for this to work is to add some dummy validator and dummy validator callout extender to the page. This is needed, as otherwise the necessary javascript libraries will not be included on the page (either this, or including the libraries by hand, whichever solution you prefer).

 

Now, the triggering of the validators is up to you – I used triggering when the user clicks some submit action. Since the action does not cause a postback, I needed some client side validation check. If you have the same problem, you can check out this simple solution located on this post.

 

As you can see, it is really easy to create the validation and add the validator callout extenders, with just a bit of knowledge in javascript. I recommend experimenting with different types of validators, as they all attach different attributes to the DOM element. If you have any questions or suggestions, feel free to post them to the comments section, I will try my best to help.

Currently rated 1.5 by 92 people

  • Currently 1.532607/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags: , , , ,

.NET | ASP.NET | ICF.Labs

ASP.NET 3.5 ListView control - Is it perhaps too early?

by Lacio 3. July 2009 04:50

The ListView control was introduced with asp.net 3.5 as an alternative to the existing data bound controls. It offers much more possibilities than the other controls, plus it generates clean html code, that will display just the way you tell it to. It offers all the possibilities of the other controls combined - selection, sorting etc. Despite all of this sounding quite nicely, it still has some unresolved issues that might hinder its usage in the early stages.

 

Being accustomed to using drag&drop and free naming techniques, the first strange thing that popped to my mind the first time I used the control was the need to name the itemplaceholder exactly "itemPlaceholder". That was strange at first, but only later  have I realised that this was a clever method to transform any control running on client side into a potential place holder for the item templates defined by the ListView control. However, I still found it strange that the name has to be hard coded, otherwise the control won't work. Why couldn't they add a property that holds the name of the itemplaceholder, instead of explicitly being forced to name it like that.

 

After coping with this annoying issue all in the name of being able to use a great new control that is still better then any of the other ones - it enables selected item template - which beats the repeater - and it generates the html you tell it to generate - unlike the datalist control, which is really messed up here, I found another really strange issue. I am not sure if anyone has encountered this problem, I did some short googling on the issue, and haven't found any spot-on fixes. Either people are not using the control, or I have a very specific version of the .net framework and it just won't obey commands from my keyboard :).

 

The issue happens when raising the SelectedIndexChanged event - first of all, it requires the SelectedIndexChanging event to even work. If you don't specify the indexChanging event, the compiler will report an issue. If you manage to get it started however, you are about to hit another unpleasent surprise. If you expect it to run normally, it won't - when having the select command the index will always be lagging by one step - The selected index will always be the next to last item you selected. The way to fix this is by set the selected index in the method raised by the SelectedIndexChanging event. This way, when you get to the selectedIndexChanged event, you will have the correct index and it will show it selected on the page.

 

Doing some research has revealed that it is the least used control of them all. For me personally, once you learn how to tame it, it becomes a very powerfull tool to display data. I believe that with the coming of the new version of asp.net the control will mature a bit
and become a regular feature on sites worldwide.

Currently rated 1.9 by 21 people

  • Currently 1.857143/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags: , , ,

.NET | ICF.Labs | State of Mind

UAC Revealed ~ .elevation of rights from .NET as commonly misunderstood.

by Sergio 8. June 2009 09:19

Recently we had a project that involves impersonification of windows users.

Idea was to create a tool that will allow non admin domain user to start single executable that requires administrative rights without need to add domain user to administrators group.
So, we were planning to create command prompt tool that may process parameters, impersonate some other user (administrator) and starts another process with new user’s credentials.

We made a small proof of concept and it all worked well. Nice, I sad, let’s test it with Vista’s User Access Control. Whoops!

Newly started process, starts as admin user, but without elevated rights. That means user is administrator, but run with rights of regular user!

So we got “this process needs elevation” error in console window.

After some investigation on internet we found ProcessStartInfo.Verb property as point of interest in this case.
According to number of articles, you just need to set this property’s value to ‘runas’ and new process will be started with elevated rights. As if.
This approach would not help you in this case. Actually there is no solution for UAC!

We contacted Microsoft’s forums, and after number of posts and responses we discovered the awful truth.
There is no way to start another process from .net code, impersonate another user to execute that code and elevate rights for that user.
Actually there is no way to do it with UAC enabled. It is constraint of UAC itself. UAC will not allow you to do this.
Microsoft claims that this is security measure, but in my opinion it does not have too much sense since my code already knows administrator user’s credentials!
So, what kind of protection is this anyway?
Next thing that was really strange to me is why so many articles claim opposite?
(Take a look at: http://victorhurdugaci.com/using-uac-with-c-part-1/ for example.)

Problem is that there are some administrative users that are not controlled by UAC!
Built in local administrator user or build in domain administrator user are not constrained by UAC.

This means that you may start another process from your .net code, impersonate built in administrator user and new process will start elevated!
So this code snippet:

ProcessStartInfo processInfo = new ProcessStartInfo();
SecureString securePassword = new SecureString();
foreach (char ch in “testpass”)
{
securePassword.AppendChar(ch);
} // foreach
processInfo.UserName = “administrator”;
processInfo.Password = securePassword;
processInfo.Verb = “runas”;
processInfo.FileName = @”c:\windows\system32\cmd.exe”;
processInfo.UseShellExecute = false;
Process.Start(processInfo);

Will run cmd.exe elevated on Vista, because this user is not run as regular user, but as full rights administrator!
But, if you try to use any other member of administrators group as as new process user it will not work!

Unfortunately it has been an ‘show stopper’ for our project. Too bad.

By my first investigation it will  be possible by default on Windows 7! Now, that is interesting.

I wonder did we found a bug in UAC design or .NET implementation? Maybe. The World will know;-)

 

Currently rated 4.6 by 11 people

  • Currently 4.636363/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

.NET


Contact

Development and Sales
ICodeFactory d.o.o.
Trg Marije Trandafil 24/2
21000 Novi Sad
Serbia, Europe
Phone: +381 (0)21 41 77 08
info[at]icodefactory[dot]com

Headquarter
T.C. Bagljaš, Lok. 11
23000 Zrenjanin
Serbia, Europe

Working hours
Monday - Friday
8am - 4pm (GMT+1)