Follow us on Twitter

Using Late Binding to call MapPoint from C#

There are a number of articles on the MapForums website about using MapPoint from a variety of different languages. Most if not all of these articles use early binding to reference the MapPoint COM objects, because this is usually the easiest way of working with COM and it tends to be safer due to compiler checking. Early binding is also slightly faster due to all the references being hard-coded. However there are times when late binding might be more appropriate. This article shows you how to call MapPoint from C# using late binding. Late binding is also known as “dynamic invocation”.

What is late binding?

Early and late binding refer to how the code refers to the MapPoint COM objects. Early binding is performed at compile time and is essentially ‘hard-coded’. E.g.

string sVersion = myApp.Version;

The compiler knows how myApp is defined, and can check the “Version” property exists. To do this, you must have a type library file for the COM objects being called.

However, you might not have this type library, or you might want to support multiple type libraries. In this case, you have to write code which checks the interface of the COM object, and makes the call at run time. The above line becomes:

string sVersion = (string) myApp.GetType().InvokeMember( "Version",
                            BindingFlags.GetProperty, null, myApp, null);

This is obviously a lot longer and more complicated! However, note that the property name is defined as string. This string could be changed at run time. The return type could also be changed at runtime without forcing an inappropriate cast to occur.

So why would you want to use late binding, if it is so much work? Here are some possible reasons:

  • A type library might not be available at compile time
  • Easier to support multiple versions – eg. You can check the version number before invoking a new method. Early binding would fail if you compiled with a type library that used a new method but you called an earlier version of the interface.
  • Useful for generic programs that can interface to a wide range of object types (eg. debuggers).

Sample code that uses late binding

So let’s see an example! This tongue-in-cheek example plans a road route through London for Gordon Brown’s eventual trip to Buckingham Palace when he dissolves Parliament (Footnote: When this was originally written, Gordon Brown’s poll ratings were still fairly high).

To use late binding, you will need to add namespace references for System.Reflection and System.Runtime.InteropServices:

// You will need these references
using System.Reflection;
using System.Runtime.InteropServices;

No MapPoint object definitions are available, so our MapPoint objects are all defined as generic objects:

private object mp_app;
private object mp_map;

Parameters are passed to MapPoint methods using arrays of objects. This sample only needs to pass one or two parameters at a time, so define these next. Note that these arrays can be re-used – we do not have to define new arrays for each and every method call.

            // Re-usable arrays used to pass parameters
            object[] Param1 = new object[1];
            object[] Param2 = new object[2];

Let’s create a MapPoint instance. In order to create an object instance, we need a type. We obtain the type from a COM name by using Type.GetTypeFromProgID:

            // Start MapPoint Europe
            Type objClassType;
            objClassType = Type.GetTypeFromProgID("MapPoint.Application.EU");
            mp_app = Activator.CreateInstance(objClassType);

mp_app now contains a reference to the main MapPoint Application object. Properties and methods are invoked using the InvokeMember() method on the object’s type. A BindingFlags value specifies the operation to invoke. We can read properties like so:

            // Read a property to find the Map object
            mp_map = mp_app.GetType().InvokeMember("ActiveMap",
                             BindingFlags.GetProperty, null, mp_app, null);

Similarly, we can write properties:

            // Set a property to make MapPoint visible
            Param1[0] = true;
            mp_app.GetType().InvokeMember("Visible",
                             BindingFlags.SetProperty, null, mp_app, Param1 );

A parameter has to be passed when writing a property. We do this by setting a one element array to the value that we need to pass. More parameters could be passed (eg. to a method) by passing a longer array.

Next we find two locations:

            // Find the first location:  10 Downing Street
            Param1[0] = "10 Downing Street, London";
            object fr =  mp_map.GetType().InvokeMember("FindPlaceResults", BindingFlags.InvokeMethod, null, mp_map, Param1);
            int resultsQuality = (int)fr.GetType().InvokeMember("ResultsQuality", BindingFlags.GetProperty, null, fr, null);

            // Assume results are good, for this sample and fetch the first result
            Param1[0] = 1;
            object locPM = fr.GetType().InvokeMember("Item", BindingFlags.GetProperty, null, fr, Param1);

            // Find the second location: Buckingham Palace
            Param1[0] = "Buckingham Palace";
            fr =  mp_map.GetType().InvokeMember("FindPlaceResults", BindingFlags.InvokeMethod, null, mp_map, Param1);
            resultsQuality = (int)fr.GetType().InvokeMember("ResultsQuality", BindingFlags.GetProperty, null, fr, null);

            // Assume the first result is good
            Param1[0] = 1;
            object locER = fr.GetType().InvokeMember("Item", BindingFlags.GetProperty, null, fr, Param1);

In both cases I have assumed that the first result is good. In a real application, you would check resultsQuality before reading the results collection (object fr).

Next we fetch a reference to the Route object and the Waypoints collection:

            // Fetch the route object, and the waypoints collection
            object oRoute = mp_map.GetType().InvokeMember("ActiveRoute", BindingFlags.GetProperty, null, mp_map, null);

            object wps = oRoute.GetType().InvokeMember("Waypoints", BindingFlags.GetProperty, null, oRoute, null);

In order to set the waypoints, we must pass two parameters to the waypoints object. This is performed using a two element array instead of the previous one element array. As an aside, it should be noted that all option parameters should be set. This is a good general rule for both early and late binding calls to COM from .NET.

            // Add the locations as waypoints
            // Note that we're passing two parameters at a time
            Param2[0] = locPM;
            Param2[1] = "Gordon Brown";
            wps.GetType().InvokeMember("Add", BindingFlags.InvokeMethod, null, wps, Param2);
            Param2[0] = locER;
            Param2[1] = "The Queen";
            wps.GetType().InvokeMember("Add", BindingFlags.InvokeMethod, null, wps, Param2);

Next we calculate the route, and fetch any results that we might require:

            // Calculate the route
            oRoute.GetType().InvokeMember("Calculate", BindingFlags.InvokeMethod, null, oRoute, null);

            // We can fetch results if we wish to
            double lfDistance = (double)oRoute.GetType().InvokeMember("Distance", BindingFlags.GetProperty, null, oRoute, null);

This is what MapPoint now looks like:

MapPoint screenshot of the route created using late binding

C# and .NET do a good job of garbage collection and cleaning up after COM. All you need to do is to set the object references to null, or to let them pass out of scope. You should still call the Application.Quit method to finally close MapPoint:

            Param1[0] = true;
            mp_map.GetType().InvokeMember("Saved", BindingFlags.SetProperty, null, mp_map, Param1 );
            mp_app.GetType().InvokeMember("Quit",BindingFlags.InvokeMethod, null, mp_app, null);
            mp_map = null;
            mp_app = null;

And that is it! Late binding is more complicated, and it lacks the safety net of compiler errors; but it can be very useful if you need to support multiple versions of MapPoint in .NET.

Leave a Reply

 

 

 

You can use these HTML tags

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>