【IDL】 IDL與C#混合程式設計技術

地理遥感生态网平台發表於2024-06-28

1 前言

C# (C Sharp)是微軟公司在2000年6月釋出的一種新的程式語言。C#與Java有很多的相似之處;包括了諸如單一繼承、介面、與Java幾乎同樣的語法,和編譯成中間程式碼再執行的過程。它又借鑑了Delphi的一個特點,與COM(元件物件模型)是直接整合的,而且它是微軟公司.NET windows網路框架的主角。

IDL則一直是應用程式開發和科學家進行視覺化與分析的首選語言,因為它功能強大,簡單易學,很少的幾行程式碼就能實現其他語言很難實現的功能,所以它是進行科學資料分析、視覺化表達和跨平臺應用開發的高效軟體和理想工具。作為第四代語法簡單、面向矩陣運算的計算機語言,IDL擁有豐富的分析工具包。同時支援遙感影像處理軟體ENVI的二次開發,使得利用IDL進行ENVI二次開發實現資料處理分析和視覺化程式變的非常容易。

2 C#呼叫IDL方式

C#可以透過COM元件的方式直接呼叫IDL進行開發。IDL提供了IDLDrawWidget和COM_IDL_CONNECT兩個元件,其中IDLDrawWidget元件是帶UI的可視元件,COM_IDL_CONNECT是不帶UI的功能元件,在實際使用的時候可以根據應用需求選取。

以IDLDrawWidget元件為例,該元件包含了多種功能方法(見表1),這些方法使得C#在呼叫的時候方便進行初始化、功能呼叫、引數傳遞和事件響應傳遞。

表1 IDLDrawWidget元件的方法

方法名稱

功能描述

CopyNamedArray

複製IDL下陣列到元件呼叫環境中的變數陣列

CopyWindow

將IDLDrawWidget元件顯示內容複製到Windows剪貼簿中

CreateDrawWidget

IDLDrawWidget控制元件初始化介面

DoExit

退出ActiveX控制元件並釋放IDL佔用的資源

ExecuteStr

執行IDL命令,相當於IDL的命令列功能

GetNamedData

獲取IDL中變數的值

InitIDL

IDL執行環境初始化(1:成功;0,失敗;-1元件未被許可;-2,IDL未安裝許可)

InitIDLEx

IDL執行環境初始化(可傳入引數)

Print

元件中顯示內容輸出到預設印表機

RegisterForEvents

元件是否傳遞程式事件(參考表17.2)

SetNamedArray

基於輸入的變數名和內容在IDL下建立陣列

SetNameData

基於輸入的變數名和內容在IDL下建立變數

SetOutputWnd

元件顯示內容輸出到指定視窗

VariableExists

判斷IDL下是否存在此變數

3 關鍵技術

以在Visual Studio 2008 C#下呼叫IDLDrawWidget元件為例,分析下呼叫該元件的關鍵技術。

(一) 元件初始化

與其他ActiveX元件一樣,在VisualStudio的工具箱元件上單擊滑鼠右鍵,彈出選單中選擇[選擇項],見圖1.

【IDL】 IDL與C#混合程式設計技術

圖1 載入元件

彈出的選擇工具箱項介面中點選TAB介面[COM元件],列表中找到“IDLDrawWidget Control3.0”並勾選(圖2)。若列表中不存在該元件,點選[瀏覽]查詢IDL安裝目錄下的子目錄“binbin.x86”中的“idldrawx3.ocx”檔案。

【IDL】 IDL與C#混合程式設計技術

圖2 COM列表

元件初始化前需要設定元件的IDL安裝目錄,本機的IDL安裝目錄可以透過查詢登錄檔選項的方式獲取,獲取IDL8.0安裝路徑的C#示例程式碼如下:

//讀取登錄檔獲取IDL8.0

RegistryKey rsg = null;

rsg = Registry.LocalMachine.OpenSubKey("SOFTWARE\ITT\IDL\8.0", true);

if (rsg.GetValue("InstallDir") != null) //讀取失敗返回null

{

    //初始化IDL80路徑

    axIDLDrawWidget1.IdlPath = Path.Combine(rsg.GetValue("InstallDir").ToString(), @"IDL80binbin.x86idl.dll");

}

int n;

  //初始化

  n = axIDLDrawWidget1.InitIDL((int)this.Handle);

  if (n == 0)

  {

     MessageBox.Show("IDL初始化失敗", "IDL初始化失敗,無法繼續!");

      return;

  }

(二) 功能呼叫

IDLDrawWidget元件支援呼叫IDL的原始碼檔案和sav檔案。其中ExecuteStr方法相當於IDL的命令列,而IDL可以使用點命令(見表2)在命令列下進行原始碼的編譯和功能呼叫。故,透過ExecuteStr方法可以輕鬆的呼叫IDL功能。

表2 點命令(DotCommand)

命 令

功 能

.COMPILE

編譯程式碼;

.CONTINUE

繼續執行程式碼;

.EDIT

在編輯器中開啟程式碼以便編輯;

.FULL_RESET_SESSION

編譯器完全重置(包括DLM等);

.GO

執行最近編譯過的主函式;

.OUT

執行當前程式直至返回;

.RESET_SESSION

編譯器重置,等同於點選工具欄的“重置”;

.RETURN

程式返回;

.RENEW

新建一個pro;

.RUN

編譯記憶體中的程式並執行主程式;

.SKIP

跳過程式段;

.STEP

執行1個或n個程式;

.STEPOVER

執行1個程式段,如果程式段中呼叫了其他函式則除錯進入函式;

.TRACE

程式異常時繼續執行。

使用點命令在命令列下進行原始碼編譯和執行的示例程式碼:

IDL>;編譯原始碼檔案,注意原始碼檔案路徑是字串,用’’或””。

IDL> .compile 'C:tempfirstIDL.pro'

% Compiled module: MYFUN.

% Compiled module: FIRSTIDL.

% Compiled module: TEST.

IDL>;呼叫原始碼中的pro執行

IDL> firstidl

abc

9

(三) 資料傳遞

IDLDrawWidget元件透過SetNamedArray、SetNameData等方法進行資料傳遞(表1),C#與IDL之間支援基本的資料型別變數和陣列傳遞(表3)。

表3 IDL與ActiveX下的通用的變數型別

IDL型別

ActiveX型別

IDL_TYPE_BYTE

UT_UI1 – unsigned char

IDL_TYPE_BYTE

VT_I1 - signed char

IDL_TYP_INT

VT_I2 - signed short

IDL_TYP_LONG

VT_I4 - signed long

IDL_TYP_FLOAT

VT_R4 - float

IDL_TYP_DOUBLE

VT_R8 - double

傳遞字串變數和陣列的示例程式碼如下:

//初始化定義變數

object objStr = "abc";

object objOri,objNow;

//定義變數

this.axIDLDrawWidget1.SetNamedData("var", objStr);

//編譯IDL功能程式碼並傳入單個變數

this.axIDLDrawWidget1.ExecuteStr(@".compile 'exchangevar.pro'");

this.axIDLDrawWidget1.ExecuteStr("exchangevar, var = var");

//將IDL中修改過的變數獲得並對話方塊顯示

objStr = this.axIDLDrawWidget1.GetNamedData("var");

//顯示IDL程式中更改後的值

MessageBox.Show("C#中的變數值為:"+objStr.ToString());

//定義陣列

int[,] dataarr = new int[3, 2] { { 6, 4 }, { 12, 9 }, { 18, 5 } };

//將陣列內容copy到IDL下的變數arr中

this.axIDLDrawWidget1.SetNamedArray("arr", dataarr, true);

//編譯IDL功能程式碼並傳入陣列

this.axIDLDrawWidget1.ExecuteStr(".compile 'exchangeArr.pro'");

this.axIDLDrawWidget1.ExecuteStr("exchangeArr,arr,oriArr= oriArr");

//透過CopyNameArray方法直接複製獲取IDL中的陣列

objOri = this.axIDLDrawWidget1.CopyNamedArray("oriarr");

//透過CopyNameArray方法直接複製獲取IDL中的陣列

objNow = this.axIDLDrawWidget1.CopyNamedArray("arr");

//彈出第一個元素的值           

MessageBox.Show("C#中的陣列值為:" + ((Array)objNow).GetValue(0, 0));

(四) 事件傳遞

IDLDrawWidget元件可以在C#或IDL下響應鍵盤和滑鼠事件。即透過C#主程式可以觸發元件的事件並由IDL事件響應程式進行響應。元件的事件響應處理方式與元件的RegisterForEvents值有關,各個值的含義見表4。

表4 RegisterForEvents對應功能描述

功能描述

0

停止傳遞所有事件

1

傳遞滑鼠移動事件

2

傳遞滑鼠按鍵點選事件

4

傳遞檢視捲軸事件

8

傳遞暴露事件

元件介面中新增滑鼠滾輪事件的示例程式碼如下:

public Form1()

{

    InitializeComponent();

    //增加滾輪滾動事件

    ((Control)this).MouseWheel += new MouseEventHandler(Form1_MouseWheel);

}




private void IDLDrawWidgetCreate()

{

    //指定事件由C#響應

    axIDLDrawWidget1.RegisterForEvents(3);

}

//滑鼠滾輪事件

void Form1_MouseWheel(object sender, MouseEventArgs e)

{

//轉換當前滑鼠點在元件上的位置

   y = axIDLDrawWidget1.Height - (e.Y - axIDLDrawWidget1.Location.Y);           

//呼叫IDL的滑鼠時間程式碼

   axIDLDrawWidget1.ExecuteStr("oImg.WheelEvents," + e.Delta.ToString() + ","+ (e.X - axIDLDrawWidget1.Location.X).ToString() + "," + y.ToString());

}

IDL中的響應該事件的程式碼如下:

;滑鼠滾輪時的事件

PRO ImgSHow::WheelEvents,wType,xPos,yPos

  COMPILE_OPT idl2

   ;獲取元件原始大小

  self.OWINDOW.GETPROPERTY, dimensions = winDims,graphics_tree = oView

  oView.GETPROPERTY, viewPlane_Rect = viewRect 

  ;判斷是放大還是縮小

  IF wType GT 0 THEN rate = 0.8 ELSE rate = 1.125

   ;計算放縮後的顯示區域大小

  oriDis =[xPos,yPos]*viewRect[2:3]/winDims

  viewRect[0:1]+=(1-rate)*oriDis

  viewRect[2:3]= viewRect[2:3]*rate

  ;更新顯示區域並重新渲染繪製

  oView.SETPROPERTY, viewPlane_Rect = viewRect

  self.OWINDOW.DRAW

END

類似的方式可以新增滑鼠拉框放大和縮小等功能,即透過C#與IDLDrawWidget元件構建了一個完整的影像顯示與基本處理程式,透過滑鼠可以對顯示影像進行放大、縮小和平移操作,並透過IDL實現了基本的影像處理、投影變換和模擬模擬功能。部分效果圖:

【IDL】 IDL與C#混合程式設計技術

圖3 靈活的操控

【IDL】 IDL與C#混合程式設計技術

圖4 影像顯示與影像處理

【IDL】 IDL與C#混合程式設計技術

圖5 投影變換功能

【IDL】 IDL與C#混合程式設計技術

圖6 衛星模擬模擬

4 結束語

透過IDLDrawWidget等元件提供的方法,C#可以方便靈活的整合IDL程式,輕鬆搭建視覺化分析與處理系統的框架,快速整合IDL的視覺化分析與處理功能。這樣充分發揮各語言的優勢,構建複雜的視覺化應用與分析的系統將會變得非常方便。

相關文章