Archive

Archive for February, 2012

Interop trouble in Powershell

February 27, 2012 Leave a comment

I was trying out the Visio 2010 automation SDK, using Powershell, the other day when I stumbled upon an unexpected problem. My code looked something like below. 

Add-Type -AssemblyName ‘Microsoft.Office.Interop.Visio, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c’
$app = new-object Microsoft.Office.Interop.Visio.ApplicationClass
$doc = $app.Documents.Open("C:\temp\test.vsd")
$page = $doc.Pages | select -first 1
$page.Shapes | select ID, Name, NameID, NameU, Text | Out-Host
$shape = $page.Shapes | select -first 1

The code starts off by loading the Visio interop assembly and then creates an application, loads a document, extracts the first page and the first shape on that page. So far, all was fine. However, then I wanted to call GluedShapes using:

$shape.GluedShapes(2, "", $null)

I got a strange error:

Exception calling "GluedShapes" with "3" argument(s): "Type mismatch. (Exception from HRESULT: 0x80020005 (DISP_E_TYPEMISMATCH))"
At line:2 char:19
+ $shape.GluedShapes <<<< (2, "", $null)
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : ComMethodTargetInvocation

I figured that something was rotten in the state of Powershell interop. A quick and dirty work around was to build a C# wrapper:

Add-Type -ReferencedAssemblies ‘Microsoft.Office.Interop.Visio, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c’ -TypeDefinition @"
using Microsoft.Office.Interop.Visio;
public class ShapeUtils
{
    public static System.Array GluedShapes(object shapeIn, VisGluedShapesFlags flags, string filter)
    {
        Microsoft.Office.Interop.Visio.IVShape x = (Microsoft.Office.Interop.Visio.IVShape)shapeIn;
        return x.GluedShapes(flags, filter, null);
    }
};
"@

and call it using:

[ShapeUtils]::GluedShapes($shape, 2, "")

Alright. That will keep things running. But what happened? The error is complaining about a type mismatch during dispatch. Why on earth is Powershell calling my object using COM dispatch? I started out in the pink ´n fluffy .NET world using the managed interrop assembly that should protect me against everything from the dark ´n scary COM world. Lets check some types:

$app, $doc, $page, $shape | % { $_.GetType() }

This yields:

IsPublic IsSerial Name BaseType
True False ApplicationClass System.__ComObject
True False DocumentClass System.__ComObject
True False __ComObject System.MarshalByRefObject
True False __ComObject System.MarshalByRefObject

$app and $doc looks alright, having proper .NET types but both $page and $shape are pure COM objects and thus will be called using COM dispatch. Having reached this far, I found some interesting links:

    Indeed, the last link confirms that this is a bug that has not been fixed in Powershell 2.0. So, that explains why COM dispatch is activated. The second link also notes that Powershell has trouble calling COM methods having optional arguments. Interestingly, that explains why the call to GluedShapes fails. It is not hard to confirm that this is a bug too; I fired up Visual studio and created an atl project and added a atl com object that implemented the following interface:

interface ITest : IDispatch {

    HRESULT PowershellTest([in]int dummy1, [in, optional]ITest* trouble);

};

 

    and indeed the following lines
    $x = New-Object -ComObject "TestLibrary.Test"
    $x.PowershellTest(0, $null)
    results in our now familiar

Exception calling "PowershellTest" with "2" argument(s): "Type mismatch. (Exception from HRESULT: 0x80020005 (DISP_E_TYPEMISMATCH))"
At line:1 char:13
+ $x.PowershellTest <<<< (0, $null)
+ CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : ComMethodTargetInvocation

    I wonder how these things work out in Powershell 3. Well, that is a task for another day.

    Categories: Uncategorized

    Playing with RavenDB

    February 12, 2012 Leave a comment

    I finally got some time to play with RavenDB. I decided to write a simple “todo list-ish” application. Since I wanted the application to be stand alone, I went for the embedded version. RavenDB is just a few clicks away using NuGet:

    image

    However, remember to switch to full .Net framework. This is required by the embedded version of RavenDB.

    full

    Now, we can create a database connection:

    NonAdminHttp.EnsureCanListenToWhenInNonAdminContext(8080);

    store = new EmbeddableDocumentStore { DataDirectory = "Data", UseEmbeddedHttpServer = true }.Initialize();

     

    A nice feature of RavenDB is that, even though we are using the embedded vesion, it is possible to inspect the database using a web browser. For this to work however, the UseEmbeddedHttpServer property must be set to true as above. The embedded server is available att http://localhost:8080 by default.

    image

    Oh, that is right. I needed to to one more thing; I added Raven.Studio.xap as a content file to my project.

    content

    The file can be found in $PROJECT\packages\RavenDB-Embedded.1.0.616\lib\net40.

    Alright, now we can start adding (or updating) data:

    private void SaveButton_Click(object sender, RoutedEventArgs e)

    {

        using (var session = store.OpenSession())

        {

            CurrentNote.HasUnsavedChanges = false;

            session.Store(CurrentNote);

            session.SaveChanges();

        }           

    }

    Search for data:

    private void SearchButton_Click(object sender, RoutedEventArgs e)

    {

        using (var session = store.OpenSession())

        {

            _searchResult.Clear();

            foreach (var note in

                session.Query<Note>().

                Where(IsMatch))

            {

                _searchResult.Add(note);

                note.TrackChanges();

            }

            FirePropertyChanged("SearchResult");

        }           

    }

     

    Delete data:

    private void buttonDelete_Click(object sender, RoutedEventArgs e)

    {

        if (CurrentNote != null)

        {

            using (var session = store.OpenSession())

            {

                var orig = session.Query<Note>().Where(n => n.Id == CurrentNote.Id).FirstOrDefault();

                if (orig != null)

                {

                    session.Delete<Note>(orig);

                    session.SaveChanges();

                }

            }

            _searchResult.Remove(CurrentNote);

            CurrentNote = null;

        }

    }

    The sample project is available here. RavenDB library is not included.

    Categories: Uncategorized