Database Hot Loader 二部曲:Using VSTO and Lua

接續前篇文章的簡短介紹,本文將開始使用 VSTO 與 Lua 語言實作 DHL 系統。首先,定義 DHL 系統的開發環境以及執行環境:

Database Hot Loader 系統

  • 程式開發環境:Visual Studio 2005 Team Suite
  • 使用者執行環境:Office 2003

為了使 Office 2003 達成轉換資料格式的任務,必須仰賴 VSTO 所提供的擴充功能。Visual Studio Tools for Office (簡稱為 VSTO)的作用,簡單來說就是讓程式設計者能夠在 Office 應用程式中,使用 .NET 撰寫附加的擴充元件,使一般的文件檔案,也能夠具備客製化的特殊功能。另外,微軟 .NET 平台的優勢,就是能夠使用 .NET 家族中的任何一種語言來撰寫程式,在此我選擇 C# 做為開發 DHL 系統的語言。

只要使用 VSTO,幾乎就能夠達成一切 Winform 可以做到的事情。在活頁簿上放個按鈕元件?沒問題;另外產生一個全新的 Winform?小意思;在 Excel 的主選單上增加自訂項目?一片蛋糕。總是覺得 Office 應用程式某些地方用得不夠順手嗎?有了 VSTO 以後,就可以幫助我們達成自己動手撰寫擴充功能的願望。

在前篇文章中,曾特別提到需要使用 Visual Studio 2005 的 Team Suite 版本,原因在於 DHL 系統必須使用 VSTO 的「文件層級」(document-level) 擴充程式,而在 Visual Studio 的眾多版本中,只有 Team Suite 版本能夠開發文件層級的擴充程式,一般程式設計者常用的 Professional 版本則只能夠開發 VSTO 的「應用程式層級」(application-level) 擴充程式。關於文件層級與應用程式層級的介紹,請參考「Visual Studio Tools for Office 」中文線上文件裡的詳細說明。

只要安裝了 Team Suite 版本之後,就可以在 Visual Studio 2005 的「新增專案」選項中,「Visual C#」的「Office」頁面裡,看到以下幾種專案範本:

  • Excel 活頁簿
  • Word 文件
  • Excel 範本
  • Word 範本
  • Outlook 增益集

首先,選擇建立「Excel 活頁簿」專案類型,Visual Studio 會自動產生出 ThisWorkbook.cs、Sheet1.cs、Sheet2.cs、Sheet3.cs 四個原始碼檔案。然後就可以開始在 ThisWorkbook.cs 裡,撰寫資料格式的轉換程序:

// 匯出指定的工作表
public void ExportSheet(Excel.Worksheet ws)
{
    // 以工作表名稱為檔名,創建一個文字格式的檔案
    StreamWriter sw = File.CreateText(String.Format("{0}/{1}{2}", GetCurrentPath(), ws.Name, EXPORT_FILE_EXT));

    sw.WriteLine("{0}{1}", ws.Name, TOKEN_EQUAL);
    sw.WriteLine(TOKEN_LEFT_BRACKET);

    int currentRow = 1;
    int currentColumn = 1;
    string fieldName = GetRangeString(ws, currentRow, currentColumn);
    while (!String.IsNullOrEmpty(fieldName)) {
        // 寫入欄位名稱
        sw.WriteLine("{0}{1}{2}", TOKEN_INDENT, fieldName, TOKEN_EQUAL);
        sw.WriteLine("{0}{1}", TOKEN_INDENT, TOKEN_LEFT_BRACKET);

        ++currentRow;
        string id = GetRangeString(ws, currentRow, 1);
        while (!String.IsNullOrEmpty(id)) {
            string value = GetRangeString(ws, currentRow, currentColumn);

            // 寫入每筆資料
            sw.WriteLine("{0}{1}{2}{3}{4}{5}{6}{7}{8}",
                TOKEN_DOUBLE_INDENT, TOKEN_LEFT_SQ_BRACKET, id, TOKEN_RIGHT_SQ_BRACKET, TOKEN_EQUAL,
                TOKEN_DOUBLE_QUOTE, value, TOKEN_DOUBLE_QUOTE, TOKEN_DOT);

            ++currentRow;

            id = GetRangeString(ws, currentRow, 1);
        }

        // 寫入右括弧與逗號
        sw.WriteLine("{0}{1}{2}", TOKEN_INDENT, TOKEN_RIGHT_BRACKET, TOKEN_DOT);
        sw.WriteLine("");

        currentRow = 1;
        ++currentColumn;
        fieldName = GetRangeString(ws, currentRow, currentColumn);
    }

    // 寫入最終的右括弧
    sw.WriteLine(TOKEN_RIGHT_BRACKET);

    // 關閉檔案
    sw.Flush();
    sw.Close();
}

ExportSheet() 函式的作用,就是抓取 Excel 儲存格中的資料,並且格式化為合法的 Lua 原始碼語法寫入檔案。利用 .NET 所提供的基底函式庫功能,僅需不到 100 行的程式碼,就能夠相當輕易地寫出轉換程序。接著開啟 Visual Studio 工具箱介面,在專案的 Excel 頁面裡放上一個 Button 元件,然後在 Button 的 Click 事件函式中,把活頁簿裡所有的工作表 (worksheet) 傳入 ExportSheet() 函式中處理。完成之後,Excel 文件的使用者,只需按下文件上的按鈕,就能夠立即將活頁簿中所有的資料輸出為合法的 Lua 檔案。

在轉換資料格式時,會以活頁簿裡各張工作表的名稱,做為檔案名稱個別輸出成 .lua 檔案,同時也做為 Lua 程式碼中最上層的 table 結構。接著把每張工作表中的每個欄位名稱,格式化為第二層的 table 結構,最後再將該欄位每一筆資料輸出成 table 的內容物。輸出完成的 Lua 資料格式非常單純易懂,第一層是資料表的名稱,第二層是欄位的名稱,最後則將每一筆資料以 id 對應數值的方式,嵌在 table 最內層。以 triangle 工作表為例:

triangle = 
{
    id = 
    {
        [1] = "1",
        [2] = "2",
        [3] = "3",
    },

    vertex1 = 
    {
        [1] = "0",
        [2] = "1",
        [3] = "0",
    },

    vertex2 = 
    {
        [1] = "-1",
        [2] = "-1",
        [3] = "0",
    },

    vertex3 = 
    {
        [1] = "1",
        [2] = "-1",
        [3] = "0",
    },

    color1 = 
    {
        [1] = "1",
        [2] = "0",
        [3] = "0",
    },

    color2 = 
    {

        [1] = "0",
        [2] = "1",
        [3] = "0",
    },

    color3 = 
    {
        [1] = "0",
        [2] = "0",
        [3] = "1",
    },

    translate = 
    {
        [1] = "-1.5",
        [2] = "0",
        [3] = "-6",
    },

    rotate = 
    {
        [1] = "0",
        [2] = "1",
        [3] = "0",
    },

}

專案建置完成後,會產生出一個 Excel 文件檔案以及一個 DLL 檔案。但是如果你馬上把這兩個檔案複製到電腦中的其他目錄下,或者傳給其他人使用,在開啟 Excel 檔案後應該會看到這樣的錯誤訊息:「找不到或無法載入自訂組件」。這是因為 Office 對於 .NET 擴充元件,有比較嚴格的安全性規範,只要沒有經過安全性設定允許的檔案目錄,就無法順利載入擴充元件的 .dll 檔案,所以必須再經過一道安全性變更的手續。

舉例來說,如果把專案建置出來的 ExcelToLuaExporter.xls 與 ExcelToLuaExporter.dll 放在 D:\test 資料夾中,就要在程式集選單中,開啟 Microsoft .NET Framework SDK v2.0 的「SDK 命令提示字元」,然後輸入 DLL 檔案所在的完整路徑:

caspol.exe -u -ag All_Code -url "D:\test\ExcelToLuaExporter.dll" FullTrust

出現確認提示訊息後,按下 y 鍵確認即可:

執行的作業將會變更安全性原則。
您確定要執行這項作業嗎? (yes/no)

為了簡化這道安全性變更的步驟,在文章最後的執行檔下載檔案中,我提供了一個 PermissionConsole.exe 程式,只要直接點擊執行,然後按下 y 鍵確認,就會執行以上程序而無須手動輸入。

除此之外,要在沒有安裝 Visual Studio Team Suite 的一般使用者電腦上執行 VSTO 程式,必須要先行安裝 VSTO Runtime 與其他相關的項目。VSTO 有許多不同的版本存在,而我使用的是 Visual Studio 2005 能夠運作的 VSTO 2005 Second Edition 版本:

只要安裝完上列三個項目後,關於 VSTO 的部分,就大功告成了!

比較熟悉 Excel 操作的讀者可能會想問:「為什麼不直接用 VBA 撰寫資料轉換的程式,而選擇使用比較複雜的 VSTO?」答案很簡單,因為我不會,所以我選擇自己最熟悉的 C# 語言做為開發工具。理論上使用 VBScript 也能撰寫出相同的功能並且得到同樣的結果,只要能夠將 Excel 文件的資料轉換為合法的 Lua 檔案格式即可。

處理完 VSTO 之後,相對來說 Lua 的部分則單純許多。在範例程式中,我創建了一個 LuaDatabaseManager 類別,用來處理所有相關於 Lua 資料庫的功能。首先,使用 luaL_dofile() 函式將由 Excel 匯出的檔案載入執行,接著使用 GetData() 函式,傳入資料表名稱、欄位名稱、資料 ID 以及回傳值的參考之後,就可以使用 Lua 的 C API 取得指定 table 中的資料:

void LuaDatabaseManager::GetData(std::string& sheetName, std::string& fieldName, unsigned int id, std::string& value)
{
	int oldTop = lua_gettop(m_DBState);

	lua_getglobal(m_DBState, sheetName.c_str());
	lua_pushstring(m_DBState, fieldName.c_str());
	lua_gettable(m_DBState, LUA_GETTABLE_INDEX);
	lua_pushnumber(m_DBState, id);
	lua_gettable(m_DBState, LUA_GETTABLE_INDEX);
	value = luaL_checkstring(m_DBState, lua_gettop(m_DBState));

	lua_settop(m_DBState, oldTop);
}

在範例程式中,將 LuaDatabaseManager.exe、ExcelToLuaExporter.xls 與 ExcelToLuaExporter.dll 以及 triangle.lua 這四個檔案放在同目錄下,開啟 ExcelToLuaExporter.xls 並且執行 LuaDatabaseManager.exe。接著任意改變 triangle 工作表中的資料後,按下 Excel 上的「匯出全部資料表」按鈕,再於 LuaDatabaseManager.exe 裡按下 x 鍵即可重新載入資料!

搞定了繁瑣的 VSTO 轉換程序與簡單的 Lua 檔案讀取機制後,下一篇文章將利用 Observer 設計模式,實作 DHL 系統在遊戲程式中的應用範例。

執行檔下載:DatabaseHotLoader_Ep2_Release.zip(需要先行安裝 vcredist_x86.exe(下載次數: 1615 )
原始碼下載:DatabaseHotLoader_Ep2_Source.zip (下載次數: 1634 )

8 Replies to “Database Hot Loader 二部曲:Using VSTO and Lua”

  1. @gino:
    沒有,我們都是使用 Open Source 軟體達成專案所需要的各種功能。不過我也對於 Team Foundation Server 非常好奇,不知道是否能夠提供專案管理者與開發者具有價值的功能項目?

  2. VS2008 Professional版似乎就可以開發VSTO..因為看到Office相關的專案可以建立
    看到你用Team Suit, 以為你們有裝Team Foundation Server ..
    會想用TFS 主要是因為想嘗試看看TFS的版本控制的功能…感覺起來SourceSafe快被放棄了
    不知道你們是用什麼版本控制軟體, Subversion嗎?
    拍謝, 一直問提外話…

    不過透過你介紹的VSTO, 我會研究看看怎麼改變我們目前的作法:)

  3. @gino:
    我還沒有使用過 VS 2008,不過根據 Wikipedia 上的說明:「With Visual Studio 2008, VSTO 3.0 is no longer available separately but is integrated with full functionality into Professional and Team System IDEs. VSTO version 3.0 and its runtime only work with Office 2007.」應該沒錯,但是就只能在 Office 2007 上使用囉。

    SourceSafe 一點也不 Safe 啊。 XD
    我用過 CVS 和 SVN,現在我們是使用 SVN 做版本控制,功能比 CVS 更強大更好用,到目前為止的使用經驗都還滿不錯的。基本上,版本控制的需求交給 SVN 處理應該就很足夠了,我比較好奇的是 TFS 中和專案管理有關的功能,不知道實行起來的效果如何。

    關於 DHL 系統,精彩的還在後頭。 :P

  4. 我們用VSS倒是沒遇到什麼錯誤, 遇到最大的問題是VSS跨Internet非常慢
    所以在尋找新的版本控制工具..

    除了TFS外, SVN也是我們選項之一(目前我們有用SVN管理game asset)
    試用了AnkhSVN 2.0 感覺還不錯..對copy-modify-merge的流程不太習慣
    以為像之前試用的外星人腦袋(AlienBrain)一樣在check in的時候做merge動作.
    有時候覺得copy-modify-merge比較適合一些能力經驗差距不大且細心的人工作.

  5. @gino:
    根據 Wikipedia 上對 VSS 的評論,建議只使用在 5 人左右的小型專案上(還真小 XD),而且最好是建置在 LAN 的網路環境中;如果是比較大型的專案,還是改用 TFS 或其他版本控制軟體吧。MSDN 裡有篇說明 TFS 與 VSS 功能差異的文章,可以參考看看。

    我們目前使用的是 TortoiseSVN,不過 AnkhSVN 看起來也滿不錯的。想要轉換專案的版本控制工具,主要考量的還是使用習慣上的問題,只要能夠建立起操作流程的使用習慣,之後就會變成工作中的自然反應囉~

  6. 個人覺得,理想地還是在遊戲編輯器提供 GUI 給 Game Designer、Level Designer 修改數值,Real-time WYSIWYG。利用 Lua 可以 Reflection 的特性,應該可以簡單地去做一個通用的編輯器。(我也會慢慢嘗試,不過真的很慢…… ^_^)

    對於 Excel,一直覺得最好用的情況是 Outsource。例如做 Localization,一列一組字串,每欄是不同的 Locale。那麼一般的翻譯公司都應該懂得編輯。(雖然這只是我的想法,從未實際試過。)

    另外我許多時(包括在大學做實驗或現在的遊戲開發上)會用程式輸出 csv (comma separated values)格式,給 Excel 讀入。這種格式無論是生成或 Parsing 都非常簡單,不需借助其他程式庫,所以尤其適合 ad hoc 場合。

    P.S. 小兒上月出生,英文名字改了叫 Lua,寫在出生証明書上了…… 現在看見 Lua 這三個字母便會有不同聯想。

  7. @Milo:
    恭喜喜獲麟兒~ ^_^
    太讚啦!真是非常佩服你的巧思,英文名取做 Lua 實在是絕妙無比啊! XD

    如果能做到 Real-Time Editor 自然是再好不過的事情了。CryEngine Sandbox 已經示範了在 Game Editor 中使用 Lua 的可行性,可見得 Lua 的確很適合做為遊戲資料與呈現結果的動態接口。期待你之後的實作心得文章。

    在做 Localization 時,我們的確是用 Excel 列出字串的方式交給翻譯者,不論是資料的匯入匯出與各種操作都相當容易。我們最主要會選擇使用 Excel 的原因,還是在於它是一般企畫設計者最熟悉且易於上手的工具。

    順道一提,如果電腦裡沒有安裝 VS 2005 的話,要執行範例程式前必須先安裝 vcredist_x86.exe。另外,之前的執行檔中忘記附上 lua5.1.dll,現在已經更新囉。

Leave a Reply