SaguiItay

My blog has moved!

You should be automatically redirected in 4 seconds. If not, visit:
http://itaysagui.wordpress.com
and update your bookmarks.

Wednesday, March 2, 2011

Windsor Factories and Obfuscation

I love Windsor - it's simplicity yet versatility amazes me every time I use it.

In my last project, I've used it, along with the TypedFactoryFacility in order to "late-resolve" some objects. The resolved object required some parameters to be passed to the CTOR - nothing too complex - you just create an annonymous type with properties that match the names of the parameters, and Windsor handles the matching and passes the values. Piece of cake.

Everything worked perfectly fine for a while. That is, until we started to test our release setups. Our testing team started to complain that the UI is not updated when they click the various buttons.
After some digging, I figured that objects are not being created in the Business Logic layer that uses Windsor, which are then reflected in the UI.

It took me almost a day to figure things out - in our release setups, our code is obfuscated. The anonymous type mentioned earlier is compiler-generated, and then obfuscated. Since it's a private class, it's properties are renamed, which result in Windsor not being able to match properties to parameters.

Once that was clear, the solution was simple - I replaced the anonymous type with a regular class, and marked it as Serializable, which notifies the obfuscation not to touch it.

After that, things went back to work - the class was not obfuscated, which means that the properties where not renamed. That in turn meant that Windsor was able to match properties to parameters, and the factory was able to resolve the objects.

Labels: , , ,

Monday, February 28, 2011

IRibbonExtensibility needs to be ComVisible

When developing an Outlook add-in using VSTO 4, if you are implementing the IRibbonExtensibility interface (for customizing a Ribbon, for example) you might encounter cases where your Ribbon or menu items don't appear in Outlook's UI.
In those cases, you should check the following things first:
  1. Your XML is valid - invalid Ribbon XML, such as placing the node inside the node are common mistakes, which will prevent your controls from appearing
  2. Your class should be marks as ComVisible:
    [ComVisible(true)]
    public class Ribbon : IRibbonExtensibility
    {
       ...
    }

Labels: , , ,

Tuesday, February 8, 2011

TFS: Undo another user's check-out files

In order to undo files that are checked-out by another user, ou can use the TF.EXE command-line tool:

 
tf undo [/workspace:workspacename[;workspaceowner]] [/recursive] itemspec [/noprompt] [/login:username,[password]] [/collection:TeamProjectCollectionUrl]

For example:
tf undo /workspace:MyWorkspace;DOMAIN\Itay /recursive *.* /collection:TeamProject

 
You can find the TF.EXE tool in the following locations:
  • For TFS 2008: C:\Program Files (x86)\Microsoft Visual Studio 9.0\Common7\IDE\TF.exe
  • For TFS 2010: C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\TF.exe

Labels: ,

Friday, June 25, 2010

Microsoft Translator - Quick Start

After adding a Service Reference to the following URL http://api.microsofttranslator.com/v2/Soap.svc, You can easily translate text by following the sample below:

LanguageService service = new LanguageServiceClient();

string appId = ""; // Application ID required for using the Bing Web Service


// Get all available languages for translation
var langs = service.GetLanguagesForTranslate(appId);
foreach (var lang in langs)
    Console.WriteLine("Language: " + lang);

// We request up-to 5 french translations for the english work 'number'
var result = service.GetTranslations(appId, "number", "en", "fr", 5, null);
foreach (var tranlationMatch in result.Translations)
{
    // TranslatedText: The translated text
    Console.WriteLine("TranslatedText: " + tranlationMatch.TranslatedText);
    // MatchDegree: The system matches input sentences against the store, including inexact matches.
    // MatchDegree indicates how closely the input text matches the original text found in the store.
    // The value returned ranges from 0 to 100, where 0 is no similarity and 100 is an exact case sensitive match.
    Console.WriteLine("MatchDegree: " + tranlationMatch.MatchDegree);
    // Rating: Indicates the authority of the person making the quality decision. Machine Translation
    // results will have a rating of 5. Anonymously provided translations will generally have a rating of 1 to
    // 4, authoritatively provided translations will generally have a rating of 6 to 10.
    Console.WriteLine("Rating: " + tranlationMatch.Rating);
}

Labels: , ,

Thursday, January 29, 2009

WordprocessingML: Part 2

In the first part of this serie, we've created a simple WordprocessingML document, and added some basic content to it. It is now time to add some formatting, to make that content presentable.

In this part we'll focus on the two basic methods to modify the formating of text:

  • Runs properties
  • Paragraph properties
More advanced methods, such as styles, will be covered later on.

Runs properties

Going back to the sample from part 1, let's take a look at a text run:

writer.WriteStartElement("r", wordmlNamespace);
writer.WriteStartElement("t", wordmlNamespace);
writer.WriteValue("Hello world");
writer.WriteEndElement(); // t
writer.WriteEndElement(); // r

After the start of the run ("r") element, let's add a "run properties" element:

writer.WriteStartElement("rPr", wordmlNamespace);
// formatting goes here
writer.WriteEndElement(); // rPr

Now we are free to define the format of the run. Attributes, such as bold, italic, underline are quite easy to define:

writer.WriteElementString("b", wordmlNamespace, "");
writer.WriteElementString("i", wordmlNamespace, "");
writer.WriteElementString("u", wordmlNamespace, "");

More complex attributes, like the font size, color, or superscript/subscript are only slightly more interesting:

writer.WriteStartElement("sz", wordmlNamespace);
writer.WriteAttributeString("val", wordmlNamespace, "8");
writer.WriteEndElement(); // sz

writer.WriteStartElement("color", wordmlNamespace);
writer.WriteAttributeString("val", wordmlNamespace, Colors.Red.ToArgb().ToString("X8").Substring(2));
writer.WriteEndElement(); // color

writer.WriteStartElement("vertAlign", wordmlNamespace);
writer.WriteAttributeString("val", wordmlNamespace, "superscript"); // subcript
writer.WriteEndElement(); // vertAlign

As you can see, defining the basic settings of the text is simple work.

Paragraph properties

In a very similar way, we can define properties at the paragraph level:

writer.WriteStartElement("pPr", wordmlNamespace);
// formatting goes here
writer.WriteEndElement(); // pPr

Alignment of the paragraph is defined using the "jc" (can anyone explain the JC name?!) element:

writer.WriteStartElement("jc", wordmlNamespace);
writer.WriteAttributeString("val", wordmlNamespace, "right");
writer.WriteEndElement(); // jc

And indentation is just as easy (Note: All units in the fields are in TWIPS (1/20 of a point). There are 72 points to an inch and 20 TWIPS to a point, and therefore there are 72 * 20 TWIPS to an inch):

writer.WriteStartElement("ind", wordmlNamespace);
writer.WriteAttributeString("firstLine", wordmlNamespace, "720");
writer.WriteAttributeString("hanging", wordmlNamespace, "1440");
writer.WriteEndElement(); // ind

and lines spacing:

writer.WriteStartElement("spacing", wordmlNamespace);
writer.WriteAttributeString("line", wordmlNamespace, "120");
writer.WriteAttributeString("after", wordmlNamespace, "240");
writer.WriteAttributeString("before", wordmlNamespace, "360");
writer.WriteEndElement(); // spacing

That's it - basic formatting of text is quite simple, yet very powerful. There are a lot more options to control the formatting of textual components.

Labels: , , ,

Wednesday, January 21, 2009

WordprocessingML: Part 1

Let's start with a public notice: Most of the OpenXML/WordprocessingML samples I've found word with TextWriters, and just write XML strings into the writer. Me, being the funny guy I am, prefer to work with XmlWriters. This makes sure I make no structure mistakes in the XML itself, allows me to generate well-formatted XML (useful during development), and just seems more "natural" to me.

Ok. Now that we got that out of the way, let's start with the very basics - creating an empty Docx file:

Creating a WordprocessingDocument object:

Nothing can be more simple than this. Just call the static method "Create" of the type WordprocessingDocument, provide a filename or stream, and select the type of document you want to create. There are several types of documents, defined in the WordprocessingDocumentType enumeration. For more details on this, just go to http://msdn.microsoft.com/en-us/library/documentformat.openxml.wordprocessingdocumenttype.aspx

Here's the snippet:

using (WordprocessingDocument wpd = WordprocessingDocument.Create(filename, WordprocessingDocumentType.Document))
{
}

Notice how I used the "using" directive - WordprocessingDocument implements the IDisposable interface...

Adding the main part:

Each document consist of multiple parts, the "main" part being the document content itself. other types (which I'll cover in future entries) include styles, numbering, properties and settings. There's nothing exciting in this part - we just ask our document to create a "main" part for itself, and we keep a reference to that part.

MainDocumentPart mainPart = wpd.AddMainDocumentPart();

Getting an XML writer:

Each part implements a "GetStream" method, so this is mostly boiler plate code:

XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
settings.IndentChars = "\t";
settings.Encoding = _new UTF8Encoding();

using (Stream stream = mainPart.GetStream())
using (XmlWriter xmlWr = XmlWriter.Create(stream, settings))
{
}

Adding content:

I'm not going to go too deeply in this section - you can find various samples explaining the full structure of content in documents. For now, let's just say that text goes into paragraphs. Paragraphs consist of runs, and runs contain text.

string wordmlNamespace = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
writer.WriteStartElement("p", wordmlNamespace);
writer.WriteStartElement("r", wordmlNamespace);
writer.WriteStartElement("t", wordmlNamespace);
writer.WriteValue("Hello word"); // NOT A TYPO! :)
writer.WriteEndElement(); // t
writer.WriteEndElement(); // r
writer.WriteEndElement(); // p

That's it - you're free to add content to your document as you see fit. Just don't forget to call the "Close" method of the WordprocessingDocument instance, in order to save the file.

Labels: , , ,

Friday, January 16, 2009

WordprocessingML to the rescue

I've recently found myself in need to create documents out of various sources (other documents, information from ECM system, and so on). I've tried several approaches, including:
  • Word Object Model
  • HTML generation
  • RTF generation
  • Aspose.Words
  • OpenXML

My first try was with the Microsoft Word Object Model, but encountered several bumpers: The complexity of the object model, the requirement of having Microsoft Word installed on the client machine, the non-fluent code - all of those made the whole experience something I'd rather forget.

Next, I tried generating HTML and RTF documents. HTML proved quite simple, but was a bit limited for my requirements, and generating a single-file HTML (MHT) would have required much too manual work to my liking. RTF, with it's specification proved just nasty, for lack of a better word.

At first I was relunctant to use a third part component, such as Aspose.Words. The component provide quite easy to learn, and quite powerful, but was lacking some of the keep requirements (such as formatting tables, embedding objects, etc), and therefore I had to drop it. I'm still planning to use it as a format-converting component, as it allow to easily convert between HTML, PDF, DOC, DOCX and so on, while retaining a high level of fidelity.

Lastly, I tackled the OpenXML SDK. To tell you the truth, I wasn't too happy to go there in the beginning - the SDK seems simple enough, be requires A LOT of manual work with XML - not very user friendly or code-efficient. However, to my surprise, the Markup Language Reference was quite easy to use; the OpenXML format is EXTREMELY powerful (allowing me to do even more than I planned).

Although I am still experiencing some problems in generating numbered paragraphs and such, after a single day of playing with it, I find myself quite comfortable with the OpenXML format, and confident it will suite my needs.

Some useful resources for getting started with OpenXML:

As I continue my research and work, I'll try posting some code samples and guides.

Labels: , , , ,

Thursday, January 8, 2009

Retrieving Documentum repeating values

Documentum provides the functionality of "repeating" properties - properties that have more than one value. Retrieving those values is a simple matter of getting the number of values for that property, and then request each one of the values.

Here's a small utility method:

private static object[] GetRepeatingValue(IDfSysObject dfObj, string attributeName)
{
    int valuesCount = dfObj.getValueCount(attributeName);
    object[] values = new object[valuesCount];

    IDfValue val = null;
    for (int index = 0; index < valuesCount; index++)
    {
        try
        {
            val = dfObj.getRepeatingValue(attributeName, index);
            values[index] = val.asString();
        }
        finally
        {
            NAR(val);
            val = null;
        }
    }
    return values;
}

Labels: , ,

Version comments for a Documentum object

Retrieving the version comments of a Documentum SysObject is an easy task:

private static string GetVersionsComment(IDfSysObject dfObj)
{
    StringBuilder sb = new StringBuilder();

    if (dfObj.getVersionLabelCount() > 0)
    {
        for (int i = 0; i < dfObj.getVersionLabelCount(); i++)
        {
            string versionLabel = dfObj.getVersionLabel(i);
            sb.AppendLine(versionLabel);
        }
        sb.AppendLine(dfObj.getLogEntry());
    }
    return sb.ToString().Trim();
}

Labels: , ,

Displaying properties of a Documentum object

When working with Documentum TypedObjects, you almost always need to retrieve their properties. Below is a method to print those properties to the Console. Notice, that this example uses the getAllRepeatingStrings() method - a useful method for displaying values to the user, but not very useful if you need to process and work with the actual values.
public static void DisplayItem(IDfTypedObject obj)
{
    if (obj == null)
        return;
    Console.WriteLine("-------------------------------------------");
    int attrCount = obj.getAttrCount();
    for (int i = 0; i <>
    {
        IDfAttr attr = null;
        try
        {
            attr = obj.getAttr(i);
            string attrName = attr.getName();
            Console.Write(attrName + ": ");
            if (!obj.hasAttr(attrName) obj.isNull(attrName))
            {
                Console.WriteLine("NULL");
                continue;
            }
            Console.WriteLine(obj.getAllRepeatingStrings(attrName, "; "));
        }
        finally
        {
            NAR(attr);
            attr = null;
        }
    }
}

Labels: , ,

Tuesday, January 6, 2009

Quality Doesn’t Just Happen

Judy McKay writes a very interesting article, Quality Doesn’t Just Happen about project management process, and how to place Quality front-and-center of a project lifecycle.
A quality-focused team produces a better project in a shorter amount of time, every time, but you have to have the right people to make it happen. We won't need the heroes to ride in at the end to save the project if it's never in distress. A well-planned project with a quality focus won't be in crisis. There may still be trade-off decisions, which is why we use risk-based testing to be sure we mitigate the highest risk first, but these can be informed decisions with measurable consequences.

As I gain more experience, both as a developer, and a team leader, I try to learn from past mistakes, pickup good practice and processes. However, I still had to smile to myself and feel uncomfortable in my seat while reading some of this article. This is a good thing - it means I'm still learning!

Labels: ,

The Visitor design patern

I've always been a huge advocat for design patterns in the past, but up until recently, I didn't get a chance to actually implement the Visitor design pattern.

This changed last week, when one of my colleagues asked me to implement a small tool, that handles one of our standard XML files. The tool was simple enough - scan the XML, and create dummy files based on information found the the XML. Nothing too fancy - just a small console application.

This triggered some light-bulb in my head - we've been manipulating those XMLs outside of our product for some time now. Mostly just for tests, or special clients requests. Instead of creating a one-time utility, I could create something much more useful - an infrastructure that will allow me to very quickly create any manipulation tool I'd like.

In comes the Visitor design pattern. I've very quickly created a Visitor abstract class, threw in a few classes that represent the various objects that are described in the XML, and voila - we're pretty much done.

The Visitor design pattern provides the infrastructure to "visit" an hierarchy of items, and "notify" the visitor when each item is handled (in my case, there's a Start-Children-End cycle). Users are now able to implement their own visitors, and handle the various "visits", by just overriding the virtual methods of the abstract Visitor.

From this point on, creating the requested tool took around 10 minutes - override the correct methods, retrieve the required information, and generate the dummy file.

More complex visitiors where just as easy to implement - removing content from the XML, adding new content, and even just running some statistics on the items in the XML.

For more details on design patterns, I strongly suggest reading AT LEAST on of the following books:

Design Patterns: Elements of Reusable Object-Oriented Software (Addison-Wesley Professional Computing Series)
or
Head First Design Patterns

Labels: , ,

Friday, February 1, 2008

User friendly file size

Based on a utility method found in the Microsoft.SharePoint.Utilities.SPUtility class, here's a method to get a user-friendly text for a file size:
public static string FormatSize(long cbSize)
{
    double num;
    if (cbSize <= 1024L)
    {
        if (cbSize <= 0L)
        {
            return string.Format("{0} KB", "0");
        }
        return string.Format("< 1 KB");
    }
    if (cbSize <= 1048576L)
    {
        num = Math.Round((double)(Convert.ToDouble(cbSize) / 1024.0), 1);
        return string.Format("{0} KB", num);
    }
    if (cbSize <= 1073741824L)
    {
        num = Math.Round((double)(Convert.ToDouble(cbSize) / 1048576.0), 1);
        return string.Format("{0} MB", num);
    }

    if (cbSize <= 1099511627776L)
    {
        num = Math.Round((double)(Convert.ToDouble(cbSize) / 1073741824.0), 1);
        return string.Format("{0} GB", num);
    }

    num = Math.Round((double)(Convert.ToDouble(cbSize) / 1099511627776.0), 1);
    return string.Format("{0} TB", num);
}

Labels: ,

Tuesday, January 29, 2008

MIME Types and File Extensions

Whenever working with content of files, it is often useful to have a way to find the MIME type based on a file extension, or the other way around - finding the file extension from a MIME type. Below are 2 useful methods for such requirements:
public static string GetExtensionFromMime(string mimeType)
{
    try
    {
        RegistryKey key = Registry.ClassesRoot.OpenSubKey(@"Mime\Database\Content Type\" + mimeType);
        if (key == null)
            return null;

        string str = key.GetValue("Extension") as string;
        if (string.IsNullOrEmpty(str))
            return string.Empty;
        
        return str;
    }
    catch
    {
        return string.Empty;
    }
}

public static string GetMimeFromExtension(string ext)
{
    if (!ext.StartsWith("."))
        ext = "." + ext;
    RegistryKey key = Registry.ClassesRoot.OpenSubKey(ext);
    if (key == null)
        return null;

    return key.GetValue("Content Type") as string;
}

Labels: ,

Monday, October 1, 2007

SqlCE doesn't support TRUNCATE TABLE

Not long ago, I modified a piece of code to use SqlCE as a data store. The older code was using OleDb to access an MS Access file. As part of the modifications, our team did a general overview of much of the data access code. We did many changes, more than I can even number. Here is a partial list: Review of table keys, indices and restrictions. Usage of IDbCommand instead of SQL string statements. Removed legacy object model. Replaced consecutive DELETE and INSERT statements with UPDATE. and so on. A few days ago, I noticed some delay in the data layer. This was nothing new - we process a huge amount of data - but since the many improvements, what used to be fast might now appear slow compared to the optimized code. After some search I've come up to a method that clears a many tables in the database. Something along the lines of:
using (IDbCommand cmd = DAL.GetClearTableCommand(tablename))
{
    // cmd.CommandText == "DELETE FROM " + tablename
    cmd.ExecuteNonQuery();
}
This of course, is nothing fancy or special. My thought was, instead of performing a DELETE statement, why not use the TRUNCATE TABLE statement, which is faster and more efficient. To my surprise, changing the GetClearTableCommand() method to return a TRUNCATE TABLE statement results in an parsing exception. After some research, I've found that the TRUNCATE TABLE statement is not supported/implemented in SqlCE. Much to my disappointment, I will have to leave the DELETE statement in place, until I find a faster solution.

Labels: , , , ,

Saturday, September 29, 2007

Allowing timeout on long-running operations - possible bug - Miscellaneous Debris

Avner Kashtan writes in his blog about an interesting problem, and solution, on how to run a long-running operation, with a timeout. The solution involves, obviously, running the code in a different thread. The possible bug is an unhandled exception in the different thread, which might kill the whole application process.

The suggested solution is to catch the exception in the running thread, and "passing the exception backward". This is possible thanks to the use of an anonymous delegate. Although this is indeed, as mentioned in the post, an ugly solution, I find myself wondering what's the penalty of doing such a thing. Is the performance degraded so much? Is it such a horrible OOP crime? Allowing timeout on long-running operations - possible bug - Miscellaneous Debris

Labels: ,

Friday, September 14, 2007

Connecting to Documentum using .Net

I've spoke in an earlier post about working with Documentum in .Net. In this post, I'll show you how to connect to a DocBase, and get the list of cabinets. The following code will connect to a DocBase. It assumes that you have a Username, Password and DocBase variables declared that contain valid information:

// Get a client object 
DfClientX _clientx = new DfClientX(); 
IDfClient _client = _clientx.getLocalClient(); 

if (_client == null) 
    throw new Exception("Failed creating Documentum client"); 

// Retrieve the client's version 
Console.WriteLine("Using DFC version '{0}'", _clientx.getDFCVersion()); 

// Create an object with the credentials of the user
IDfLoginInfo _loginInfoObj = _clientx.getLoginInfo(); 
_loginInfoObj.setUser(Username); 
_loginInfoObj.setPassword(Password); 

// Create a new session to the requested DocBase 
IDfSession _session = _client.newSession(DocBase, _loginInfoObj); 
if (_session == null && !_session.isConnected()) 
{ 
    Console.WriteLine("Failed conecting to Documentum"); 
    if (_session != null) 
    { 
        Console.WriteLine("DFC Messages:\r\n{0}", _session.getMessage(1)); 
    } 
    return; 
} 
Console.WriteLine("Using server version '{0}'", _session.getServerVersion()); 

Now, once we're connected to the Documentum DocBase, we'll list all the cabinets:

IDfQuery query = _clientx.getQuery();
// Quering the "dm_cabinet" table returns only items of dm_cabinet type
query.setDQL("SELECT r_object_id, object_name, title FROM dm_cabinet");

// Query the session for the cabinets
IDfCollection col = query.execute(_session, (int)DFCLib.tagDfQueryTypes.IDfQuery_DF_READ_QUERY);

// Loop through all the items in the collection
while (col.next())
{
    // Get the current item from the collection
    IDfTypedObject typedObj = col.getTypedObject();
    // Print the item's name
    Console.WriteLine("Cabinet name: {0}", typedObj.getString("object_name"))
}
col.Close();

One of the most important thing to remember, is that you have to close the IDfCollection. Each session has a very limited number of collections it can have open at the same time. If you need more collections, I would suggest just caching the items inside a .Net collection for later use.

Labels: , , ,

Wednesday, September 12, 2007

Retrieving extended permissions in Documentum with .Net

Following version 5 of the Documentum Content Server, security entities can have extended permissions on items. Those extended permissions include: Execute Procedure, Change Location, Change State, Change Permission and Change Ownership In order to retrieve those permissions by code, it is required to manually check for those permissions. Assuming that you have the object ID of an item, here's the .Net code in order to know if the user has those extended permissions:
IDfId itemIdObj = null;
IDfSysObject itemSysObj = null;
IDfACL aclObj = null;
string itemId = null;
try
{
    // Get the Id object of the item
    itemIdObj = _clientx.getId(itemId);
    // Get the item itself
    itemSysObj = (IDfSysObject)_session.getObject(itemIdObj);
    // Get the ACL of the item
    aclObj = itemSysObj.getACL();
    // Get extended permissions for entity i. This code should be run for each entity
    int xperms = aclObj.getAccessorXPermit(i);
    if ((xperms & 1) == 1)
    {
        // User has the "Execute Procedure"
    }
    if ((xperms & 2) == 2)
    {
        // User has the "ChangeLocation"
    }
    if ((xperms & 32768) == 32768)
    {
        // User has the "Change State"
    }
    if ((xperms & 65536) == 65536)
    {
        // User has the "Change Permission"
    }
    if ((xperms & 131072) == 131072)
    {
        // User has the "Change Ownership"
    }
}
catch (Exception ex)
{
    // Log exception
}

Labels: , , ,

Monday, September 10, 2007

Retrieving a list of available Documentum DocBases

While adding support for EMC Documentum to the Tzunami Deployer, our SharePoint migration tool, I needed to allow the user to enter the name of a DocBase to connect to. I wanted a interface that is a bit more that just a TextBox where the user can enter the DocBase name. I ended up using a ComboBox, and added a "Refresh" button, similar to the one used in the Server Explorer of Visual Studio. When the user press the "Refresh" button, the ComboBox gets populated by the list of known DocBases. Bellow is the code in the event handler of the button:

comboBoxDocBase.Items.Clear();
try
{
    IDfClientX clientx = new DfClientX();
    IDfClient client = clientx.getLocalClient();
    IDfDocbaseMap docbaseMap = client.getDocbaseMap();

    int docbaseCount = myMap.getDocbaseCount();
    for (int i = 0; i < docbaseCount; i++)
    {
        comboBoxDocBase.Items.Add(docbaseMap.getDocbaseName(i));
    }
}
catch (Exception ex)
{
    // Log the exception and show the user a warning
}

This allowed me to easily allow average users to just select from a list of available servers, and advanced users can just enter the name of the DocBase.

Labels: , , ,

Updating GUI from different threads - Part 2

As part of my work at Tzunami Inc., I'm working on migrating our product Deployer - a SharePoint migration tool - from .Net 1.1 to .Net 2.0 and Visual Studio 2005. One of the first things we've encountered was the managed debugging assistants (MDA). I'll talk about the MDAs in a later entry, but for now, let's just say that those are exceptions that are thrown only in debug mode, and that assist you in finding bugs and problematic points in your code that are otherwise hard to locate. The first MDA that we've encountered was the "Cross-thread operation not valid". What this means, is that you're trying to update the GUI from a thread other than the thread that created the GUI control. You can read about how to solve this issue in .Net 1.1 in one of my previous entries. However, .Net 2.0 allows you to resolve this in a much cleaner way. Bellow is the way we decided to handle this:
private void InvokeGuiDelegate(GUIDelegate d)
{
    if (InvokeRequired)
        BeginInvoke(d);
    else
        d();
}
Now we can use the above method everywhere we need to update the GUI, using an annonymous method:
InvokeGuiDelegate( delegate() { numericUDTimeout.Value = value; });

Labels: , ,