Feeds:
文章
迴響

Archive for 2007 年 05 月

Google Gears 測試

MSN SpaceGoogle DocGoogle Blog
Chui-Wen Chiu(Arick)
2007.05.31 建立

簡介
Google Gears 採用 BSD 授權的開放原始碼專案, 主要是讓開發人員建立離線瀏覽的網路應用程式。目前支援的作業系統和瀏覽器如下:

  • Apple Mac OS X (10.2 or higher)
    • Firefox 1.5 or higher
  • Linux
    • Firefox 1.5 or higher
  • Microsoft Windows (XP or higher)
    • Firefox 1.5 or higher
    • Internet Explorer 6 or higher

提供的主要功能有:

  • 用戶端伺服器:快取和提供應用程式資源(HTML, JavaScript, images, etc.)
  • 資料庫:提供瀏覽器上的資料儲存和讀取
  • Thread Pool:讓應用程式可在背景執行運算,使操作上有有更好的回應

使用 Google Gears
這部份我依循官方的教學文件走一遍,首先系統必須先安裝 Google Gears,然後下載下面檔案

編輯 manifest 檔案
用文字編輯器打開 tutorial_manifest.json,首先 version 填寫上你的應用程式版本描述,如:"version": "version 1.0"。接著設定可以離線瀏覽的檔案清單,
  "entries": [
      { "url": "go_offline.html"},
      { "url": "go_offline.js"},
      { "url": "gears_init.js"},
      { "url": "resources/logo.gif" },
      { "url": "http://www.example.com/index.html" }
    ]
範例的內容為
{
  "betaManifestVersion": 1,
  "version": "version 1.0",
  "entries": [
      { "url": "go_offline.html"},
      { "url": "go_offline.js"},
      { "url": "gears_init.js"}
    ]
}

主程式源碼剖析

go_offline.html – UI

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;
<html xmlns="http://www.w3.org/1999/xhtml"&gt;
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<script type="text/javascript" src="gears_init.js"></script> <!– Google Gears 初始化 –>
<script type="text/javascript" src="go_offline.js"></script>

<title>Enable Offline Usage</title>
<style type="text/css">
<!–
.style1 {
    color: #003399;
    font-style: italic;
    font-weight: bold;
    font-size: large;
}
.style3 {
    color: #009933;
    font-weight: bold;
    font-style: italic;
}
–>
</style>
</head>

<body onload="init()">
<h2>Getting offline-enabled documents with Google Gears </h2>
<p>&nbsp;</p>
<div>
  <p class="style1">Status Message: <span id="textOut" class="style3"></span></p>
</div>

<p><strong>Q:</strong> I want to see these documents when I’m not online! What must I do?<br />
<strong>A:</strong> <a href="http://gears.google.com/">Install Google Gears</a> on your computer and then click &quot;Capture&quot; to store the documents to your computer. You can then access the URLs without a network connection. </p>
<p>
  <button onclick="createStore()" > Capture </button>
 </p>

<p><strong>Q:</strong> I want to remove my offline access to these documents. What must I do?<br />
<strong>A: </strong>Click &quot;Erase&quot; below to removed the &quot;captured&quot; documents from your computer.  The documents will no longer be available without a network connection. </p>
<p>
  <button onclick="removeStore()" > Erase </button>
</p>
</body>
</html>

gears_init.js – Google Gears 初始化程式

(function() {
// 檢查是否已經定義 Google Gear
  // We are already defined. Hooray!
  if (window.google && google.gears) {
    return;
  }

// 依據不同的瀏覽器,採用不同方式產生 GearFactory
  var factory = null; 
  // Firefox
  if (typeof GearsFactory != ‘undefined’) {
    factory = new GearsFactory();
  } else {
    // IE
    try {
      factory = new ActiveXObject(‘Gears.Factory’);
    } catch (e) {
      // Safari
      if (navigator.mimeTypes["application/x-googlegears"]) {
        factory = document.createElement("object");
        factory.style.display = "none";
        factory.width = 0;
        factory.height = 0;
        factory.type = "application/x-googlegears";
        document.documentElement.appendChild(factory);
      }
    }
  }

  // *Do not* define any objects if Gears is not installed. This mimics the
  // behavior of Gears defining the objects in the future.
  if (!factory) {
    return;
  }

// 保存 Google Gear
  // Now set up the objects, being careful not to overwrite anything.
  if (!window.google) {
    window.google = {};
  }

  if (!google.gears) {
    google.gears = {factory: factory};
  }
})();

go_offline.js – 程式邏輯

// 定義 Managed Store 的名稱,這個名稱可用於 createManagedStore, removeManagedStore 和 openManagedStore 三個 API
var STORE_NAME = "my_offline_docset";

// 保存資源列表的 manifest 檔案名稱
var MANIFEST_FILENAME = "tutorial_manifest.json";

// Local Server
var localServer;

// Managed Store
var store;

/**
 * 網頁內容載入完成後執行此程序
 *
 */
function init() {
  // 檢查 Google Gears 是否初始化
  if (!window.google || !google.gears) {
    textOut("NOTE:  You must install Google Gears first.");
  } else {
    // 建立 Local Server
    localServer = google.gears.factory.create("beta.localserver","1.0");

    // 建立儲存空間
    store = localServer.createManagedStore(STORE_NAME);

    textOut("Yeay, Google Gears is already installed.");
  }
}

/**
 * 資源保存在 Managed Resource
 *
 */
function createStore() {
  if (!window.google || !google.gears) {
    alert("You must install Google Gears first.");
    return;
  }

  // 指定 manifest 檔案
  store.manifestUrl = MANIFEST_FILENAME;

  // 檢查資料是否過期,如果過期,下載新的資料
  store.checkForUpdate();

  // 等待資料保存
  var timerId = window.setInterval(function() {
    // 假如 currentVersion 屬性有值表示 manifest 中的檔案已經都下載完成。
    if (store.currentVersion) {
      window.clearInterval(timerId);
      textOut("The documents are now available offline.n" +
              "With your browser offline, load the document at " +
              "its normal online URL to see the locally stored " +
                    "version. The version stored is: " +
              store.currentVersion);
    // 假如下載資源失敗
    } else if (store.updateStatus == 3) {
      textOut("Error: " + store.lastErrorMessage);
    }
  }, 500); 
}

/**
 * 移除在 Managed Store 中的資源
 *
 */
function removeStore() {
  if (!window.google || !google.gears) {
    alert("You must install Google Gears first.");
    return;
  }

  localServer.removeManagedStore(STORE_NAME);
  textOut("Done. The local store has been removed." +
          "You will now see online versions of the documents.");
}

/**
 * 顯示文字
 *
 */
function textOut(s) {
 var elm = document.getElementById("textOut");
  while (elm.firstChild) {
    elm.removeChild(elm.firstChild);
  }
  elm.appendChild(document.createTextNode(s));
}

測試
1. 上傳上述四個檔案到 HTTP Server
2. 用瀏覽器觀看 go_offline.html,如:http://192.168.10.120:2527/GoogleGears/go_offline.html

3. 點選 Capture 進行網頁保存

4. 將網頁資源移除

如果你點選 "Capture" 將資料保存在 Managed Store 中,此時網路連線切斷,並將 IE 的 Cache 清空,則重新整理該頁面,則仍然能夠看到頁面,如果此時你點選"Erase" 將資源從 Managed Store 移除,再重新瀏覽頁面會發現找不到網頁。

參考資料
[1] RSS Bling goes Offline with Google Gears
[2] Audible Ajax Episode 21: Dojo Offline on Google Gears
[3] Google Gears
[4] Google Gears – Offline Functionality for Web Apps
[5] Dojo Offline
[6] Google Reader 離線瀏覽新功能

廣告

Read Full Post »

MSN SpaceGoogle DocGoogle Blog
Chui-Wen Chiu(Arick)
2007.05.31 建立

今天使用 Google Reader 偶然發現右上角多出了一個 Offline 連結,如下圖:

點選後會出現功能說明:

簡單的說就是安裝 Google Gears 之後才可以使用離線瀏覽功能。下面是 Google Gears 官方的頁面

取得 GoogleGearsSetup.exe 安裝之後獲得下面的訊息:

重新啟動瀏覽器再進入 Google Reader 會詢問你是否讓 Google 將資料存放在你的電腦中,如下的圖示:

接著原來的 Offline 連結也變成一個圖示

點選一下該圖示就會將最新的2000比資料儲存在你的電腦中,如此離線情況下就可以直接使用這些已經下載的資料

下載完成後,就可以再離線情況下使用 Google Reader,下圖是我切斷網路後使用 Google Reader 的截圖:

離線瀏覽的達成主要是透過 Google Gears,該軟體以 Dojo Offline 為基礎,所以應該也是透過 Proxy Server 的方式來達成,下一篇再來整理 Google Gears 的應用。

Read Full Post »

// AS3

var a:Array = new Array("a", "b", "c", "d", "e");
function shuffle(a,b):Number {
var num : Number = Math.round(Math.random()*2)-1;
return num;

}

var b:Array = a.sort(shuffle);
trace(b);

 
 
ps. 上述方法適用於 Javascript,如下:
var a = new Array("a", "b", "c", "d", "e");
function shuffle(a,b) {
 var num = Math.round(Math.random()*2)-1;
 return num;
}
var b = a.sort(shuffle);
alert(b);
 
參考資料:

Read Full Post »

測試環境
1. Windows XP Pro
2. Visual Studio 2005

簡介
本文主要是整理[1]所述的可擴充系統,系統的基礎是建構在 DLL 和 Interface 上,因為程式在執行階段必須動態載入外部檔案,才能達到可擴充系統的要求,[1]利用 Win32 API 的 LoadLibraryGetProcAddress 來完成這個要求,雖然透過上述兩個 API 可以載入 DLL 並取用內部的函數,可是主程式並不清楚這些外部 DLL 提供哪些可用的函數,所以,這個系統的另一個基石就是既定的 Interface,也就是說,DLL 要能夠符合系統的需要,必須實作定義的 Interface,所以,載入 DLL 之後,只要呼要這些既定介面即可。[1] 先以一個簡單的秀圖程式來說明可擴充系統的概念。接著再針對問題進行。

秀圖程式
這個秀圖程式支援外部 DLL 擴充不同的圖檔格式,為了滿足這樣的需求,首先我們定義圖檔解析的 interface 如下:

class IImageParser{
public:
/**
* 解析檔案並回傳 HBITMAP
*
* @param fname 檔案名稱
*/
virtual HBITMAP ParseFile( const char *fname )=0;
/**
* 檢查是否支援指定的格式
*
* @param type 副檔名,如:.bmp, .jpg  
*/
virtual bool SupportsType( const char *type ) const=0;
};

實作解析 BMP 的 Parser 如下:

class CBMPParser: public IImageParser
{
public:
virtual HBITMAP ParseFile( const char *fname );
virtual bool SupportsType( const char *type ) const;

private:
HBITMAP CreateBitmap( int width, int height, void **data );
};

static CBMPParser g_BMPParser;
// 外部取得 BMP 的 IImageParser 介面
extern "C" __declspec(dllexport) IImageParser *GetParser( void ){
return &g_BMPParser;
}

類別圖如下:

主程式載入 Plugin 的程式片段

void LoadPlugins( void )
{
char path[_MAX_PATH];
GetModuleFileName(NULL,path,_MAX_PATH);
*PathFindFileName(path)=0;
char spec[_MAX_PATH];
sprintf(spec,"%s*.imp",path);
WIN32_FIND_DATA data;
// find all DLLs
HANDLE h=FindFirstFile(spec,&data);
if (h!=INVALID_HANDLE_VALUE) {
do {
char fname[_MAX_PATH];
sprintf(fname,"%s%s",path,data.cFileName);
HMODULE hModule=LoadLibrary(fname);
if (hModule) {
// get the parser pointer and add it to the parsers list
TGetParser getparser=(TGetParser)GetProcAddress(hModule,"GetParser");
if (getparser)
AddParser(getparser());
}
} while (FindNextFile(h,&data));
FindClose(h);
}
}

const int MAX_PARSERS=100;
IImageParser *g_Parsers[MAX_PARSERS];
int g_NumParsers=0;
void AddParser( IImageParser *parser )
{
g_Parsers[g_NumParsers++]=parser;
}

載入圖形的片段

void LoadImage( const char *fname )
{
// find the parser for that type and load the image
char ext[_MAX_EXT];
_splitpath(fname,NULL,NULL,NULL,ext);
for (int i=0;i<g_NumParsers;i++)
// 檢查 Parser 是否支援指定的副檔名
if (g_Parsers[i]->SupportsType(ext)) {
// 取得 HBITMAP
g_Bitmap=g_Parsers[i]->ParseFile(fname);
break;
}
}

LRESULT CALLBACK WindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
// ... 略

if (uMsg==WM_PAINT) {
// paints the window
// if a bitmap is loaded - draw the bitmap
// otherwise draw a text prompt
PAINTSTRUCT ps;
HDC hdc=BeginPaint(hwnd,&ps);
if (g_Bitmap) {
HDC hsrc=CreateCompatibleDC(hdc);
SelectObject(hsrc,g_Bitmap);
BITMAP bmp;
GetObject(g_Bitmap,sizeof(bmp),&bmp);
BitBlt(hdc,0,0,bmp.bmWidth,bmp.bmHeight,hsrc,0,0,SRCCOPY);
DeleteDC(hsrc);
// ... 略
}

上述已經是一個可擴充架構的雛型,開發人員只要在 DLL 中實作 IImageParser 介面,即可在不重新編譯程式的情況下,擴充可顯示的圖形格式。

但是上述的架構還有兩點可改善的地方,
1. 每一個實作 IImageParser 介面的類別會有重複的程式碼
2. 主程式無法公開全域性的資料和函數給外部 DLL 使用

[1] 將主程式和 interface 程式分割到獨立的 DLL,DLL 中如果要公開給外部使用,則加上 __declspec(dllexport),如果要使用外部的資料或函數,則加上 __declspec(dllimport)。因此這個新的 DLL 定義如下:

Host.h

#ifndef _HOST_H
#define _HOST_H

#ifdef COMPILE_HOST
#define HOSTAPI __declspec(dllexport) // when the host is compiling
#else
#define HOSTAPI __declspec(dllimport) // when the plugins are compiling
#endif

void AddParser( class CImageParser *parser );

HOSTAPI int RunHost( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow );

#endif

Host.cpp

const int MAX_PARSERS=100;
CImageParser *g_Parsers[MAX_PARSERS];
int g_NumParsers=0;

void AddParser( CImageParser *parser )
{
    g_Parsers[g_NumParsers++]=parser; // (5)
}

void LoadPlugins( void )
{
    char path[_MAX_PATH];
    GetModuleFileName(NULL, path, _MAX_PATH);
    *PathFindFileName(path)=0; // ?

    char spec[_MAX_PATH];
    sprintf(spec,"%s*.imp",path);

    WIN32_FIND_DATA data;
    // find all DLLs
    HANDLE h=FindFirstFile(spec,&data);
    if (h!=INVALID_HANDLE_VALUE) {
        do {
            char fname[_MAX_PATH];
            sprintf(fname,"%s%s",path,data.cFileName);
            LoadLibrary(fname); // (2)
        } while (FindNextFile(h,&data));
        FindClose(h);
    }
}

int RunHost( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
    // … 略

    LoadPlugins(); // (1)

    // … 略
}

ImageParser.h

class CImageParser
{
public:
    HOSTAPI CImageParser( void ); // adds the parser to the parsers list
    virtual HBITMAP ParseFile( const char *fname )=0; // parses the image file and reads it into a HBITMAP
    virtual bool SupportsType( const char *type ) const=0; // returns true if the file type is supported

protected:
    HOSTAPI HBITMAP CreateBitmap( int width, int height, void **data );
};

ImageParser.cpp

#include <windows.h>
#include "Host.h"
#include "ImageParser.h"

CImageParser::CImageParser( void )
{
    AddParser(this); // (4)
}

// Creates a bitmap with the specified size
HBITMAP CImageParser::CreateBitmap( int width, int height, void **data )
{
    BITMAPINFO bmi={sizeof(BITMAPINFOHEADER),width,height,1,24,0,0,0,0,0,0};
    return CreateDIBSection(NULL,&bmi,DIB_RGB_COLORS,data,0,0);
}

BMPParser.cpp

#include <windows.h>
#include <stdio.h>
#include "..HostHost.h"
#include "..HostImageParser.h"

// CBMPParser inherits from CImageParser
class CBMPParser: public CImageParser
{
public:
    virtual HBITMAP ParseFile( const char *fname );
    virtual bool SupportsType( const char *type ) const;
};

// Parses a BMP file
HBITMAP CBMPParser::ParseFile( const char *fname )
{
    // … 略
    HBITMAP bitmap=CreateBitmap(width,height,&data);
    // … 略
}

static CBMPParser g_BMPParser; // (3)

修改後的類別圖如下:

上面巧妙的安排,產生有趣的結果,由於 Host.h/.cpp 和 ImageParser.h/.cpp 被封裝在獨立的中介 DLL 中,且產生 Import LIB,主程式和擴充 DLL 都會連接這個Import  LIB 檔,使得主程式和擴充 DLL 都可以使用中介 DLL 的資料,如:主程式可以使用 RunHost,擴充 DLL 可以使用 CImageParser 中的建構子和 CreateBitmap 成員。

因此,當主程式執行 RunHost() 啟動程式時,會先執行(1)呼叫 LoadPlugins(),然後 (2) 將所有外部的 DLL 載入到 Process 中,接著每一個被載入到 Process 中的 DLL,因為內部都宣告了一個靜態物件(3),所以 DLL 每一個 ImageParser(如:BmpParser) 物件將被建立,由於每一個 Parser 都以 ImageParser 為基礎類別,所以 ImageParser 的建構子會被喚起(4),接著將物件指標透過全域函數 AddParser 加入陣列中。

以上的動作是不是非常的精巧呢?透過上述的作法,我們可以將原先介面上重複程式法封裝在基礎類別 CImageParser 中,也透過 Import Lib ,動態將每一個 CImageParser 加入到陣列中,而不需要透過 GetProcAddress 。

上述作法的主程式進入點直接呼叫 RunHost 之後就沒事了,所以,分成兩部份沒有什麼太大的意義,但是,擴充 DLL 必須透過 Import Lib 將 CImageParser 動態加入 陣列,這個問題點在於 Import Lib 的產生,在 VC6 /IMPLIB 只能用於 DLL,可是 Visual Studio 2003/2005 可以讓 EXE 產生 Impoer Lib,只需如下圖設定

如此,主程式就省去 Host.lib 連接。但是因為編譯器不會將未使用函數編譯到目的程式碼中,因此,必須透過定義 DEF 檔來描述 EXE 公開給外部使用的函數,Linker 會保留 DEF 所定義的函數列表,即使這些函數從未被使用。VS2005 如下圖設定

DEF 檔案的定義類似

EXPORTS
    // the C++ decorated name for the CImageParser constructor
??0CImageParser@@QAE@XZ

// the C++ decorated name for CImageParser::CreateBitmap
?CreateBitmap@CImageParser@@IAEPAUHBITMAP__@@HHPAPAX@Z

你可以手動編輯,或是透過[1]提供的 defmaker 來產生,這部份並非本文重點,有興趣者自行參考[1]。

結語
透過上述的整理,我們從最基本透過 LoadLibrary 和 GetProcAddress 完成可擴充系統設計,接著透過 Import LIB 省略 GetProcAddress 的呼叫,最後在透過 DEF 自訂公開的函數介面,使得 Import Lib 從原來的 DLL 變成由 EXE 產生,經過這一系列的整理,應該對於以 interface 為基礎的可擴充系統設計有某些程度的概念。

參考資料
[1] Ivo Beltchev, "Plugin System – an alternative to GetProcAddress and interfaces"
[2] Plug-In framework using DLLs by Mohit Khanna
[3]
ATL COM Based Addin / Plugin Framework With Dynamic Toolbars and Menus by thomas_tom99
[4] LoadLibrary
[5]
GetProcAddress

Read Full Post »

[1] 提供一個類別,將 LoadLibrary 常用動作封裝在 C++ 類別中,透過這個類別動態載入 DLL 之後,可以避免忘記呼叫 FreeLibrary 的問題。
下面是一個簡單的使用範例:

// 引入 LibraryMrg 類別標頭檔
#include "LibraryMgr.h"


int _tmain(int argc, _TCHAR* argv[]){
// 載入指定的 DLL
LibraryMgr_t lm( _T("C:\MyProg\MyLib.dll") );

typedef int (*Test_t)(void);
Test_t TestFn;
// 取得 DLL 中的函數指標
TestFn = (Test_t) lm.GetProcAddress( _T("Test"
) );

if(TestFn){
// 執行 DLL 中的函數
TestFn();
}

return 0;
}

 
參考資料:

Read Full Post »

Read Full Post »

(C#)MP3 轉 EXE 的方法

MSN SpaceGoogle DocGoogle Blog
Chui-Wen Chiu(Arick)
2007.05.21 建立

測試環境
1. Windows XP Pro SP2
2. Visual Studio 2005

原理
[1]提供的方法是動態編譯產生播放 MP3 的程式。這個程式樣板存放在 Resource 中,當產生 MP3 的 EXE 檔案時,從資源檔中萃取出該樣板,並將 MP3 內嵌到 EXE 檔的 Resource 中,當 EXE 執行時,再從 Resource 中取出 MP3 來播放。

程式碼剖析

產生 EXE 的程式片段

// 指定存放 EXE 的檔名和目錄
SaveFileDialog sv = new SaveFileDialog();
sv.RestoreDirectory = true;
sv.OverwritePrompt = true;
sv.Filter = "exe file (*.exe)|*.exe";
sv.DefaultExt = "exe";
if (sv.ShowDialog() == DialogResult.OK)
{
    // 1. 建立 C# 程式碼產生和編譯的物件
    Microsoft.CSharp.CSharpCodeProvider pr = new Microsoft.CSharp.CSharpCodeProvider();
    CompilerParameters cp = new CompilerParameters();
    string pathtoicon="";
    if (File.Exists(Application.StartupPath + "\icon.ico"))
    {
        pathtoicon= Application.StartupPath + "\icon.ico";
    }
    if (skinRadioButton2.Checked)
    {
        pathtoicon = this.pictureBox1.ImageLocation;
    }

    // 2. 設定編譯器參數
    // 2.1 叫用編譯器時,要使用之選擇性的其他命令列引數字串
    // 產生 windows 的 EXE 檔,並使用指定 icon 檔
    cp.CompilerOptions = "/target:winexe" + " " + "/win32icon:" + """ + pathtoicon + """;
    // 2.2 產生可執行檔
    cp.GenerateExecutable = true;
    // 2.3 不包含除錯資訊
    cp.IncludeDebugInformation = false;
    // 2.4 內嵌 MP3 資源
    cp.EmbeddedResources.Add(this.textBox1.Text);
    // 2.5 輸出的 EXE 檔名
    cp.OutputAssembly = sv.FileName;
    // 2.6 不在記憶體中產生輸出
    cp.GenerateInMemory = false;
    // 2.7 EXE 參照的 Assembly
    cp.ReferencedAssemblies.Add("System.dll");
    cp.ReferencedAssemblies.Add("System.Data.dll");
    cp.ReferencedAssemblies.Add("System.Deployment.dll");
    cp.ReferencedAssemblies.Add("System.Drawing.dll");
    cp.ReferencedAssemblies.Add("System.Windows.Forms.dll");
    cp.ReferencedAssemblies.Add("System.Xml.dll");
    // 2.8 不將警告當做錯誤
    cp.TreatWarningsAsErrors = false;

    // 3. 編譯 *.cs 產生 *.exe
    CompilerResults cr = pr.CompileAssemblyFromFile(cp, Environment.GetEnvironmentVariable("TEMP") + "\it.cs");
    if (cr.Errors.Count>0)
    {
        MessageBox.Show("There was an error while converting the file","Error",MessageBoxButtons.OK,MessageBoxIcon.Error);
    }
}

播放 MP3 的程式樣板核心片段

// 隱藏程式 UI
this.Hide();
this.ShowInTaskbar = false;

// 從 Resource 取出 mp3
string[] a = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceNames();
Stream theResource = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream(a[0]);
BinaryReader br = new BinaryReader(theResource);
FileStream fs = new FileStream(Environment.GetEnvironmentVariable("TEMP")+"\it.mp3", FileMode.Create);
BinaryWriter bw = new BinaryWriter(fs);
byte[] bt = new byte[theResource.Length];
theResource.Read(bt,0, bt.Length);
bw.Write(bt);
br.Close();
bw.Close();

// 播放 mp3
MP3Player pl = new MP3Player();
try
{
    pl.Open(Environment.GetEnvironmentVariable("TEMP") + "\it.mp3");
    pl.Play();
    while (pl.CurrentPosition != pl.AudioLength)
    { }
    Application.Exit();
}
catch (Exception ex)
{ }
Application.Exit();

MP3Player 在 [1] 中是使用 Win32 API 的 mciSendString() 來實作,這部份並非本文的重心,故有興趣者自行參考[1]的原始碼或 MSDN 說明。

結語
由於 .NET 提供將 C# 產生 EXE 的類別,且又支援 Reflection 因此可以很容易達到任意資源轉換成 EXE 檔的方法,透過上述說明,你應該知道如何自行擴充將圖檔或其他檔案轉成 EXE 。

參考資料
[1] http://www.codeproject.com/useritems/mp3toexe.asp
[2] CSharpCodeProvider Class (Microsoft.CSharp)
[3] CompilerParameters Class (System.CodeDom.Compiler)

Read Full Post »

Older Posts »