Search:

Calling MapPoint from C# with Late Binding

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'. Eg.

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:

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 written, Gordon Brown's popularity ratings made then-President George Bush look like a popular President).

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

// You 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.

Return to the Main MapPoint Articles Page.