MapInfo Sürümünden Bağımsız Integrated Mapping Uygulamaları Geliştirme

C# ve MapInfo ile geliştirdiğimiz integrated mapping uygulamalarının MapInfo’nun herhangi bir sürümünde çalışabiliyor olması bazen önemli olabilir çünkü C# ile yazılan bir integrated mapping uygulamasında Early Binding kullanırsak bu uygulama, kullanıcılardan biri başka bir MapInfo sürümüne geçtiği anda çalışmayacaktır. Mesela yıllar önce C# ve MapInfo 7.0 ile bir integrated mapping uygulaması yaptınız ve bir gün ismini bile unuttuğunuz bir müşteriniz sizi arayarak

“-Bugün MapInfo yazılımlarımızı 10.5’a yükselttik ama ufak! bir problemimiz var: Abone programlarımızın (Integrated Mapping uygulaması kastediliyor) hiç biri çalışmıyor!” derse?

Yıllar sonra böyle bir problemle karşılaşmak bir geliştiricinin hiç ama hiç istemeyeceği bir durumdur çünkü proje yapan bir geliştirici yaptığı yazılımın her zaman tıkır tıkır çalışmasını ister öyle değil mi? Peki böyle bir durumda eski müşterimize

“-Kardeşim kim dedi sana MapInfo’ları upgrade et diye? Eski MapInfo’lar aslanlar gibi çalışırken niye durup dururken başına iş açıyorsun?”

mu diyeceğiz?

Tabii ki hayır. Burada sunulacak bir çözüm söz konusu integrated mapping uygulamasını MapInfo 10.5 ile tekrar derlemek ve müşteriye yeni bir kurulum CD si vermektir. Ama en iyisi böyle bir duruma hiç düşmemek tabi. Peki bu duruma düşmemek için ne yapmamız gerekiyor?

Buraya kadar biraz geyik yaptık, biraz da işin teknik tarafına bakalım:

Evet Early Binding metodu ile C# ta yazdığımız bir integrated mapping uygulaması diğer MapInfo sürümlerinde çalışmaz. Early Binding metodu ise integrated mapping de çoğu zaman kullandığımız metottur. Yani Visual Studio’da proje referanslarına MapInfo X.X OLE Automation Type Library ekleme ve daha sonra bu kütüphane içinde bulunan gerekli MapInfo sınıflarını doğrudan kullanma yöntemidir. Bu yöntemin “compile-time check” (diğer deyişle “strong type check”) ve intelli-sense desteği gibi çok önemli 2 avantajı var. Ama öyle bir dezavantajı var ki bu iki büyük avantajı nötrleyecek kadar kötü olabilir: Tahmin edileceği üzere MapInfo sürüm bağımlılığı!

Peki problem nedir, yani başka bir MapInfo sürümünde eski programımız çalışmaz derken neyi kastediyoruz? Integrated mapping uygulamamızı kullanan bir kullanıcı MapInfo’yu upgrade -veya downgrade- edince ne oluyor? Gerçekten kötü şeyler oluyor çünkü kodda tam da MapInfo’nun create edildiği satırda bir patlama oluyor ve MapInfo (dolayısıyla haritamız) formumuzda hiç gösterilemeden aşağıdaki gibi bir runtime hatası oluşuyor:

System.Runtime. InteropServices. COMException (0x80040154) :
Retrieving the COM class factory for component with CLSID {D66B3D9C-D465- 46B8-BFB4- F63F04FB203C} failed due to the following error: 80040154.

Bu hatanın oluştuğu satır:

mi = new MapInfoApplicationClass();

Hatanın sebebi şu: MapInfoApplicationClass sınıfına ait GUID (Globally Unique Identifier) değeri farklı MapInfo sürümlerinde farklı değerlere sahip. Örneğin yukarıdaki hatada geçen CLSID (GUID) değeri MapInfo 9.5 a aittir ve hata MapInfo 9.5 ta derlenen integrated mapping uygulamasının MapInfo 7.0 kurulu başka bir makinede çalıştırılması sonucu üretilmiştir. MapInfo 7.0’ın kurulu olduğu makinedeki COM registry sinde böyle bir GUID bulunmadığı için

mi = new MapInfoApplicationClass();

satırında MapInfo örneği oluşturulamıyor ve hata oluşuyor.

Peki çözüm nedir?

İlk akla gelen late binding, yani reflection. Ama reflection yönteminin de 2 önemli dezavantajı var: Compile time checking ve intelli-sense özelliklerinden mahrum kalarak bütün Do, Eval, RunMenuCommand vb. MapInfo metotları için oturup wrapper metotlar yazmak zorunda kalmak. Hiç pratik ve sevimli değil. Gerçi C# 4.0 ile gelen dynamic veri tipleri ile reflection eskiye göre daha kolay olacak gibi görünüyor ama en iyisi reflection dan başka bir yol bulmak.

Çözüm tüm MapInfo sürümlerinde aynı GUID değerine sahip olan ve MapInfoApplicationClass sınıfının implement ettiği bir interface olan DMapInfo yu kullanmak! Aşağıdaki kod bu yöntemle MapInfo’nun nasıl oluşturulacağını gösteriyor:

Type miType = Type.GetTypeFromProgID("MapInfo.Application" );
DMapInfo mi = (DMapInfo)Activator.CreateInstance(miType);

GetTypeFromProgID metodu verilen program identifier’ın (MapInfo.Application) ait olduğu Type ı döndürür. CreateInstance metodu ise Type ı verilen tipin (çok tip bir ifade oldu galiba 😉 yeni bir örneğini (instance) döndürür.

Buradaki önemli nokta şu:

Activator.CreateInstance(miType)

metodu bize return type olarak object döndürdüğü için MapInfo’nun Do, Eval vb. metotlarını kullanamayız. Ama dönen değeri DMapInfo ile cast edersek (yani DMapInfo tipine dönüştürürsek) artık elimizde gerçek bir MapInfo nesnesi olur. Bu yüzden

DMapInfo mi = (DMapInfo) Activator.CreateInstance(miType);

şeklinde bir casting yapıyoruz. Bu casting i yapabiliyoruz çünkü Activator.CreateIns tance(miType) metodu DMapInfo interface ini implement eden bir nesne döndürür. Artık bundan sonra

mi.Do("...") ;
mi.Eval("...");

kodları ile yolumuza devam edebiliriz.

Bu şekilde MapInfo nesnemizi oluşturduktan sonra early binding yönteminin avantajlarını kullanmakla beraber, aynı zamanda uygulamamıza MapInfo’nun sürüm bağımsızlığını da kazandırmış olduk. Yani sürüm bağımsızlığı sağlamak için reflection ile uğraşmaya gerek kalmamış oldu.

Sonuç olarak, bu metodu kullanarak farklı MapInfo sürümleri kullanan müşterilere dağıtım yapabilirsiniz. Ben bunu 2 taraflı test ettim, şöyle ki:

1) MapInfo 9.5 ile derlenmiş uygulamayı MapInfo 7.0 kurulu bir sanal makinede denedim ve çalıştı.

2) MapInfo 7.0 ile sanal makinede derlenmiş uygulamayı MapInfo 9.5 kurulu makinede denedim ve çalıştı.

Yani müşteri elindeki MapInfo’yu yükseltse de düşürse de her halükarda problem çıkmıyor.

Peki MapInfo’nun gelecek sürümlerinde de aynı garantiyi verebilir miyiz? Bu garantiyi sadece MapInfo verebilir. Çünkü belirttiğim gibi, söz konusu sürüm bağımsızlığının olması için yeni sürümlerdeki DMapInfo interface inin GUID değerinin eskilerle aynı olması gerekiyor ve muhtemelen de aynı olacaktır. Bizim emin olmak adına yapabileceğimiz şey de gayet basit: Yeni sürümdeki DMapInfo arayüzünün GUID değerinin eski sürümlerle aynı olduğunu görmek.

Advertisements
  1. Özlem
    28/10/2015 at 00:14

    Merhabalar, yukarıdaki sorunla bende karşılaştım ancak belirttiğiniz gibi değişiklikleri uygulamama rağmen miType değişkeni null değer döndürüyor ve program hata verip kapanıyor. Visual Studio’nun farklı bir sürümünü kullandığımdan olabilir mi yardımcı olursanız sevinirim.

    • 28/10/2015 at 10:15

      MapInfo’nun hangi sürümünü kullanıyorsunuz? Normal MapInfo mu, yoksa MapInfo Runtime mı? Aldığınız hata mesajını yazabilir misiniz?

  2. Özlem
    28/10/2015 at 11:26

    MapInfo 12.5, Visual Studio 2013 sürümünü kullanıyorum.
    Type miType = Type.GetTypeFromProgID(“MapInfo.Application” );
    DMapInfo mi = (DMapInfo)Activator.CreateInstance(miType); kodunu çalıştırdığımda da
    “An unhandled exception of type ‘System.ArgumentNullException” şeklinde de bir hata ile karşılaşıyorum.

  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: