轉自:http://blog.sina.com.cn/s/blog_764b1e9d0100w06w.html
C# (C Sharp)是微軟公司在2000年6月發佈的一種新的編程語言。C#與Java有很多的相似之處,包括了諸如單一繼承、界面、與Java幾乎同樣的語法,和編譯成中間代碼再運行的過程。它又借鑑了Delphi的一個特點,與COM(組件對象模型)是直接集成的,而且它是微軟公司.NET windows網絡框架的主角。
IDL則一直是應用程序開發和科學家進行可視化與分析的首選語言。因爲它功能強大,簡單易學,很少的幾行代碼就能實現其他語言很難實現的功能,所以它是進行科學數據分析、可視化表達和跨平臺應用開發的高效軟件和理想工具。作爲第四代語法簡單、面向矩陣運算的計算機語言,IDL擁有豐富的分析工具包。同時支持遙感圖像處理軟件ENVI的二次開發,使得利用IDL進行ENVI二次開發實現數據處理分析和可視化程序變得非常容易。
1 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運行環境初始化(可傳入參數) |
|
組件中顯示內容輸出到默認打印機 |
RegisterForEvents |
組件是否傳遞程序事件(參考表17.2) |
SetNamedArray |
基於輸入的變量名和內容在IDL下創建數組 |
SetNameData |
基於輸入的變量名和內容在IDL下創建變量 |
SetOutputWnd |
組件顯示內容輸出到指定窗口 |
VariableExists |
判斷IDL下是否存在此變量 |
2 關鍵技術
以在Visual Studio 2008 C#下調用IDLDrawWidget組件爲例,分析下調用該組件的關鍵技術。
(一) 組件初始化
與其他ActiveX組件一樣,在VisualStudio的工具箱組件上單擊鼠標右鍵,彈出菜單中選擇[選擇項],見圖1。
圖1 加載組件
彈出的選擇工具箱項界面中點擊TAB界面[COM組件],列表中找到“IDLDrawWidget Control3.0”並勾選(圖2)。若列表中不存在該組件,點擊[瀏覽]查找IDL安裝目錄下的子目錄“bin\bin.x86”中的“idldrawx3.ocx”文件。
圖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(), @"IDL80\bin\bin.x86\idl.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:\temp\firstIDL.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 |
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實現了基本的圖像處理、投影變換和仿真模擬功能。部分效果圖:
圖3 靈活的操控
圖4 圖像顯示與圖像處理
3 結束語
通過IDLDrawWidget等組件提供的方法,C#可以方便靈活地集成IDL程序,輕鬆搭建可視化分析與處理系統的框架,快速集成IDL的可視化分析與處理功能。這樣充分發揮各語言的優勢,構建複雜的可視化應用與分析的系統將會變得非常方便。