SaguiItay

My blog has moved!

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

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: , ,

Thursday, September 6, 2007

Populating a TreeView with Outlook folders

In this post I will demonstrate how to populate a TreeView with the different Outlook folders. We’ll be using the Outlook Object Model (OOM) to retrieve the folder and some basic information about them. The code itself is well documented, so I’ll not explain it.
private void GetFolders(MAPIFolder currentFolder)
{
    TreeNodeCollection nodes = null;
    if (currentFolder == null)
    {
        // If current folder is null, we’ll be adding the root folders to
        // the TreeView itself
        nodes = treeView1.Nodes;
    }
    else
    {
        // otherwise, find th node that represent the MAPIFolder we’re about
        // to expand
        TreeNode parentNode = folderToNode[currentFolder] as TreeNode;
        if (parentNode == null)
            throw new ArgumentException("currentFolder");
        nodes = parentNode.Nodes;
    }

    // If this folder was already expanded, there’s nothing left for us to do
    if (currentFolder != null && folderExpanded[currentFolder] != null)
        return;
    else if (currentFolder != null)
    {
        // Otherwise, clear the children nodes of the node we’re about to expand
        // This is done since we might have placed a Dummy node there - see later
        if (nodes.Count > 0)
            nodes.Clear();
        // Mark folder as expanded
        folderExpanded[currentFolder] = true;
    }
    
    // Retrieve the root/sub folders
    Folders folders = (currentFolder == null ? nameSpace.Folders : currentFolder.Folders);
    foreach (MAPIFolder folder in folders)
    {
        // If this folder already has a node, we’re done with it
        if (folderToNode[folder] != null)
            continue;
        // Generate a node for the current subfolder
        TreeNode newNode = GenerateNode(folder);
        // Add folder to hashes and to it’s parent node
        nodeToFolder[newNode] = folder;
        folderToNode[folder] = newNode;
        nodes.Add(newNode);
    }
}

private TreeNode GenerateNode(MAPIFolder folder)
{
    // Create a new node with the correct name
    TreeNode newNode = new TreeNode(folder.Name);
    // Check if and how the number of items should be displayed
    switch (folder.ShowItemCount)
    {
        case OlShowItemCount.olNoItemCount:
            // Don’t display the number of items
            break;
        case OlShowItemCount.olShowTotalItemCount:
            // Display the total number of items
            if (folder.Items.Count > 0)
                newNode.Text += " (" + folder.Items.Count + ")";
            break ;
        case OlShowItemCount.olShowUnreadItemCount:
            // Display only the number of unread items
            if (folder.UnReadItemCount > 0)
                newNode.Text += " (" + folder.UnReadItemCount + ")";
            break;
    }

    // If folder has any subfolders, create a dummy node beneath it. This is
    // used so that the node will have the nice plus (+) sign next to it
    // so the user will know there are subfolders. This Dummy node will be
    // removed once the folder is actually expanded
    if (folder.Folders.Count > 0)
        newNode.Nodes.Add(new TreeNode(DummyNode));

    return newNode;
}

private const string DummyNode = "{1194595F-CAF7-474b-8972-CF143F097243}";
private Hashtable nodeToFolder = new Hashtable();
private Hashtable folderToNode = new Hashtable();
private Hashtable folderExpanded = new Hashtable();

Labels: , , , , , ,

Updating GUI from different threads

Even the smallest thing, when thought upfront, can help you go a long way. Whenever you create an application that is more that two forms thrown together, and that does a little bit more than just saying “Hello world”, you’ll encounter a point where you’ll want to use threads. So if you’re going to reach that point somewhere along the way, why not plan and prepare for that in advance? Working with threads by itself is not the really topic of this post. However, changing the GUI from different threads is. Windows applications in .Net request that changes in Forms be made in the thread that owns the form. Managing that on your own can be a real pain. Thanksfully, the .Net team thought about us, and allowed us a simple way to handle this:
public void UpdateForm()
{
    if (this.InvokeRequired())
    {
        this.Invoke(new MethodInvoker(UpdateForm));
        return;
    }
    // Update your form as usual here...
}
The more attentive readers will raise the point, that this example works only because UpdateForm() returns void and accepts no argument. Sadly this is true. For any other case we’ll have to write a delegate, and send the Invoke() method an instance of that delegate, just as the following example shows:
private delegate bool UpdateFormMethod(int argument);

public bool UpdateForm(int argument)
{
    if (this.InvokeRequired)
    {
        return (bool)this.Invoke(new UpdateFormMethod(UpdateForm),
            new object[] {argument});
    }
    // Update your form as usual here...
    return true;
}

Labels: , ,

Transparent Click-thru windows

It is often usefull to provide the user with information on the screen, without interfering his work. Usually, you would just create a window with a somewhat lower opacity, allowing the user to see what’s beneath your window. However, this window will still receive all the normal windows events, and mostly, the user’s mouse clicks. To prevent this, all you have to do is override the window’s CreateParams accessor, and add to the ExStyle property the WS_EX_TRANSPARENT value. Let’s have a look:
protected override CreateParams CreateParams
{
    get
    {
        CreateParams cp = base.CreateParams;
        cp.ExStyle = 0×00000020;
        return cp;
    }
}

Labels: , ,

Filtering Win32 messages of a form

I've recently came across a nice post on C# Frequently Asked Questions blog. In the post, they've shown a way to filter out, or (pre)process Win32 messages of a form. Let's have a look at the code, then we’ll go through it.
public class MyMessageFilter : IMessageFilter
{
    public bool PreFilterMessage(ref Message m)
    {
        // Intercept the left mouse button down message
        if (m.Msg == 513)
        {
            Console.WriteLine("WM_LBUTTONDOWN is: " + m.Msg);
            return true;
        }
        return false;
    }
}
And in the form itself:
public class mainForm : System.Windows.Forms.Form
{
    private MyMessageFilter msgFliter = new MyMessageFilter();

    public mainForm()
    {
        // Register message filter
        Application.AddMessageFilter(msgFliter);
    }
}
As you can see, we've started by creating a class that implements the IMessageFilter interface. Then we've added an instance of that class to the form, and in the constructor registered it. We can use the Application.RemoveMessageFilter() method to stop filtering messages using the specified filter. All the filter class does is handle the WM_LBUTTONDOWN message. By returning true, we specify that we've handled the message, and there's no need of any further handling of it - meaning that no event will be sent. This technique can be used either as a way to handle specific messages, or just ignore certain messages. Other nice things to try is to catch the message only a certain amount of times, or to "hide" some of the messages when certain requirements are met.

Labels: , ,

Unbound items in a bound ComboBox

You are once again invited to read, reply and vote on my new article at CodeProject. The article demonstrates how to add unbound items to a bound ComboBox. The code uses a wrapper that implements the IList interface, and supply the ComboBox both the extra items, that are kept at the beginning of the drop-down list, and the normal items, taken from the DataSource. The article can be found here: http://www.codeproject.com/useritems/UnboundItemsComboBox.asp

Labels: , , ,

Fading form while moving

The different between a nice application and an exellent one, is many time only in the small details. How much did the developers really put into it? How much they thought about every detail? The following code is nothing more than an eye-candy, but we all know that it’s those simple things, that take seconds to write that make the difference. Let’s write 2 methods to increase and decrease the opacity of a window:

private void DecreaseOpacity()
{
    while (this.Opacity > 0.2)
    {
        this.Opacity -= 0.02;
        double lastOpacity = this.Opacity;
        Thread.Sleep(10);
        if (this.Opacity != lastOpacity)
            break;
    }
}
private void IncreaseOpacity()
{
    while (1 > this.Opacity)
    {
        this.Opacity += 0.04;
        Thread.Sleep(10);
    }
}

Now, we want the form do fade out when the user moves it, and fade back in when he releases the form. For this, we’ll catch the WM_ENTERSIZEMOVE and WM_EXITSIZEMOVE messages of the window, and call the above methods in a different thread:

protected override void WndProc(ref Message m)
{
    switch(m.Msg)
    {
        case (int)0×231: //WM_ENTERSIZEMOVE
            Thread t = new Thread(new ThreadStart(DecreaseOpacity));
            t.Start();
            break;
        case (int)0×232: //WM_EXITSIZEMOVE
            Thread t2 = new Thread(new ThreadStart(IncreaseOpacity));
            t2.Start();
            break;
    }
    base.WndProc(ref m);
}

Labels: , ,

Writing an IsApplicationRunning method

You are welcome to read and vote for my new article at CodeProject: Writing a Win32 method to find if an application is running The article explains how to write a method to find out if a certain application is running in the backgroud, using the Process32First and Process32Next functions.

Labels: , ,

Working with MAPI Items

While the Outlook objects expose most of the properties of the items they represent, sometime they miss the exact field that you need. In this post I'll show you the very basics in retrieving information from the MAPI object that your CDO object represent. Assuming that you have an Outlook mail item, you need to convert it to a MAPI object. First well need to have a MAPI.Session object:
MAPI.Session session = new MAPI.SessionClass();
session.Logon(null, null, false, false, null, true, Missing.Value);
And now to the actual conversion, which is basically a request for the MAPI object, according to its EntryID:
MAPI.Message message = (MAPI.Message)session.GetMessage(item.EntryID, Missing.Value);
Now that we have our MAPI.Message, well get all its properties (called fields), then well fish out of them the specific required field. After that, you can just get the fields value, and type:
MAPI.Fields fields = message.Fields as MAPI.Fields;
MAPI.Field field = fields.get_Item(MAPI.CdoPropTags.CdoPR_SUBJECT, Missing.Value) as MAPI.Field;
Console.WriteLine(field.Type.ToString());
Console.WriteLine(field.Value.ToString());
In the above example we've retrieved the items subject. Nothing fancy here, since we could have just as easily get that from the Outlook object. However, if you'll have a closer look at the MAPI.CdoPropTags enumeration, you'll be able to find many more fields that are not exposed. You can see the values of the enum and the meanings of the different type in a file called MAPITags.h

Labels: , , ,

The Interlocked class

Working with threads and counters can because a really pain if you do not take care. Fortunetly for us, .Net supplies us with a couple of methods that can make our life a little bit easier. The class Interlocked, which can be found in System.Thread allows you to increment, decrement, and set the value of an int or long as an atomic action, thus preventing race conditions where multiple threads update the same counter at the same time. The first methods are of course, Increment() and Decrement(), both accepting an int or long variable and update it as requested. The next method is the Exchange(), which allow you to set the value of either an int, a long or object variable, by passing it the variable by reference, and the next requested value. Another interesting method in the CompareExchange(). This method checks if the value of the variable is as expected, and only then update it. The methos returns the current value of the variable, whether it was updated or not, allowing you to redo you calculations if the value changed while you were calculating the new value. Below is an example take from MSDN:
public int AddToTotal(int addend)
{
    int initialValue, computedValue;
    do
    {
        initialValue = totalValue;
        computedValue = initialValue + addend;
    }
    while (initialValue != Interlocked.CompareExchange(ref totalValue, computedValue, initialValue));
    return computedValue;
}

Labels: ,

Wednesday, September 5, 2007

Prevent changing the state of a CheckBox

Preventing an item in a CheckedListBox or a ListView with CheckBoxed is a really basic requirement when dealing with GUI. The simple and elegant code is for some reason, quite under-sampled. Assuming that you know which item you want to prevent the user from changing, all you have to do is register on the ItemCheck event off either the CheckedListBox or the ListView, and write the following event handler:
private void checkedListBox1_ItemCheck(object sender, System.Windows.Forms.ItemCheckEventArgs e)
{
    if (e.Index == 0)
        e.NewValue = e.CurrentValue;
}
One could add grayed-out coloring to the item, to visually notify the user that the item is disable, and of course, obviously you could keep a list of all indexes that are to be disabled this way. If you take a look at my previous post, and expand it to a CheckedListBox, you can just as easily keep the Enabled state of the item in the obect itself, and use that in the ItemCheck event.

Labels: , ,

Tuesday, September 4, 2007

Using ComboBox with objects

In most of the cases where you need to use a ComboBox, you need it only so the user will be able to choose between a couple of string values. In the majority of the cases, you’ll have a list of objects that you’ll want the user to choose from, and you’ll have to do something similar to this:
Hashtable idToItem = new Hashtable();
foreach (Item item in listOfItems)
{
    int index = comboBox.Add(item.DisplayName);
    idToItem[index] = item;
}
We can now use the SelectedIndex field of the ComboBox, in conjuction with the Hashtable to retrieve the selected item. While the above code works perfectly, it does consume more memory, and takes more time than needed. And while you might say that both the time and memory wasted are negligible, try to use 10,000 items to a ComboBox, that go enjoy a cup of coffee while you wait for things to move around… There exist a simple and elegant way of doing the same thing. The DisplayMember field of the ComboBox is mostly known in relation to binding the control to a data source. We are going to use it for something similar - our data sources will be the items themselves:
comboBox.DisplayMember = "DisplayName";
foreach (Item item in listOfItems)
{
    comboBox.Add(item);
}
Now we can just use the SelectedItem field of the ComboBox, casting it to the correct type.

Labels: , ,