2012年9月17日

使用 COM 方式實現 Excel 資料匯入匯出的功能

對使用者來說,在 Excel 整理大量的資料還是比較方便又快速的,
傳統 TIPTOP 的匯入 Excel 資料的方式都是需要先轉為文字檔,
利用分隔符號來區分資料的欄位,但是這樣已是古代的作業方式了。

通常應用程式開發工具可以使用 COM 的方式是跟 Excel Application 進行通訊,
GDC 是 Clinet 端的軟體,所以可以使用 COM 的方式來讀取 Excel 的資料,
參考 4js 提供的 Genero 使用說明,找到 ui,interface.frontcall() 的使用方式。

建立 COM
1:CALL ui.interface.frontcall("WinCOM","CreateInstance",[program],[handle]
  A. program - 系統中註冊的 COM 名稱
  B. handle - 回傳狀態 -1 表示有錯誤,此值在 API 可以使用

使用指定的方法
2:CALL ui.interface.frontcall("WinCOM","CallMethod",[handle,method,arg1,...],[result])
  A.handle - 使用宣告的 handle 值。
  B. method - 函數的名稱。
  C. arg1 - 傳送給方法用的參數
  D. result - 回傳狀態 -1 表示有錯誤,0 表示沒有錯誤

讀取屬性值
3.CALL ui.interface.frontcall("WinCOM","GetProperty",[Handle,member],[result]
  A. handle - 使用宣告的 handle 值
  B. member - 取得屬性的
  C. result - 回傳狀態 -1 表示有錯誤,0 表示沒有錯誤

設定屬性值
4.CALL ui.interface.frontcall("WinCOM","SetProperty",[handle,member,value],[result]
  A. handle - 使用宣告的 handle 值
  B. member - 設定屬性的名稱
  C. value - 屬性的值。
  D. result - 回傳狀態 -1 表示有錯誤,0 表示沒有錯誤

錯誤訊息內容
5.CALL ui.interface.frontcall("WinCOM","GetError",[],[result]
  A. result - 錯誤的說明,如果沒有錯誤就為 Null

關閉 COM
6.CALL ui.interface.frontcall("WinCOM","ReleaseInstance",[handle],[result])
  A. handle - 使用宣告的 handle 值
  B. result - 回傳狀態 -1 表示有錯誤,0 表示沒有錯誤

以下就為 4GL 程式 Excel 資料匯入的範例:
DEFINE l_excelapp INTEGER,
               l_excelwb INTEGER,
               l_result INTEGER,
               l_str STRING,
               l_filename STRING,
               l_target STRING,
               l_range STRING

DEFINE l_i LIKE type_file.num5,
               l_j LIKE type_file.num5,
               l_column LIKE type_file.chr10
LET l_excelapp = -1
LET l_excelwb = -1

# 選擇檔案的位置和檔案名稱
LET l_filename = cl_browse_file()
# 建立 Excel Application 的 COM
CALL ui.interface.frontcall("WinCOM","CreateInstance",["Excel.Application"],[l_excelapp])
# 開啟所選擇的 Excel 檔案
CALL ui.interface.frontcall("WinCOM","CallMethod",[l_excelapp,"WorkBooks.Open",l_filename],[l_excelwb])
# 設定 Excel 要顯示
CALL ui.interface.frontcall("WinCOM","SetProperty",[l_excelapp,"Visible",true],[l_result])
# 讀取 Excel 的行數
CALL ui.interface.frontcall("WinCOM","GetProperty",[l_excelwb,'activesheet.UsedRange.Rows.Count'],[l_result])

FOR l_i=1 TO l_result
   LET l_column = l_i
   # 所要讀取的欄位,A 列第 n 行
   LET l_range = 'activesheet.Range("A',l_column,'").Value'
   # 讀取欄位的值
   CALL ui.interface.frontcall("WinCOM","GetProperty",[l_excelwb,l_range],[l_str])
   LET g_bmd[l_i].bmd01 = l_str
   # 所要讀取的欄位,B 列第 n 行
   LET l_range = 'activesheet.Range("B',l_column,'").Value'
   # 讀取欄位的值
   CALL ui.interface.frontcall("WinCOM","GetProperty",[l_excelwb,l_range],[l_str])
   LET g_bmd[l_i].bmd02 = l_str
END FOR

# 關閉 Excel 的 檔案和 COM
CALL ui.interface.frontcall("WinCOM","CallMethod",[l_excelapp,"WorkBooks.Close"],[l_excelwb])
CALL ui.interface.frontcall("WinCOM","ReleaseInstance",[l_excelapp],[l_result])