2007年5月30日 星期三

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">
<html xmlns="http://www.w3.org/1999/xhtml">
<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 離線瀏覽新功能
Google Reader 離線瀏覽新功能
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 的應用。




2007年5月27日 星期日

使用 DLL 和 Interface 進行可擴充系統設計
MSN SpaceGoogle DocGoogle Blog
Chui-Wen Chiu(Arick)
2007.05.27 建立

測試環境
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

2007年5月20日 星期日

(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)

2007年5月16日 星期三

Google 個人首頁又升級了

MSN SpaceGoogle DocGoogle Blog啪啦資訊科技
Chui-Wen Chiu(Arick)
2007.05.17 建立

今天打開瀏覽器發現 Google 個人首頁又有新東西,就是 Goolgle 服務的快捷選單,可是這個功能目前似乎只出現在 IE 瀏覽英文語系的 Goolgle 個人首頁,下面是我的 Goolgle 個人首頁截圖:

[IE - Google 英文個人首頁]



[IE - Google 中文個人首頁]

[Firefox - Google 英文個人首頁]

雖然這個功能可以透過 Google Toolbar 取代,但並不是每個人都會裝 Google Toolbar,如果將個人首頁放在 Firefox 的 SideBar 那就更加實用了。另外,透過該選單也可知道 Google 又新增哪些可以用的服務,希望 Google 儘快在每一種環境實作出來。每天用 Google 都有不同的驚喜,真是一間特別的公司~

VC++ 2005 執行時自動重新編譯

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

最近在BCB與VC兩頭撰寫程式,發現 Borland C++ Builder 在測試程式的時候,只要按下 F9 執行時,如果程式有更動時,會自動重新編譯後再執行,相當的便利。但是 VC++ 2005 怎麼每次都要手動點選重新編譯才會執行新版的程式,有時寫到頭昏時都會不知道目前執行的到底是不是最新的,真是頗為困擾。今天終於發現怎麼讓 VC 也有類似的功能,只要簡單的更改一下設定即可,選取"工具 | 選項 " 如下圖:
只要將"執行時(如果專案已經過期)"更改為"永遠建置",就可以像 BCB 一樣執行即自動重新編譯。

2007年5月10日 星期四

(AS3)利用位元運算加速運算效率
MSN SpaceGoogle DocGoogle Blog啪啦資訊科技
Chui-Wen Chiu(Arick)
2007.05.11 建立

位元運算在 C 語言相當常見,這種寫法的優勢在於運算非常的有效率,但缺點是可讀性不高和寫法上有些許限制,因此,如果程式有執行效能瓶頸,可透過位元算算來提高運算效能,[1] 提供一些 AS3 在位元運算上的範例和校能改善幅度。以下針對[1] 的內容整理如下:

位元運算加速技巧
1. 如果乘上一個 2 的倍數數值,可以改用左移運算(Left Shift) 加速 300%
x = x * 2;
x
= x * 64;

//改為:
x
= x << 1; // 2 = 21
x
= x << 6; // 64 = 26
2. 如果除上一個 2 的倍數數值,可以改用右移運算加速 350%
x = x / 2;
x
= x / 64;

//改為: x = x >> 1;// 2 = 21
x
= x >> 6;// 64 = 26
3. 數值轉整數加速 10%
x = int(1.232)

//改為: x = 1.232 >> 0;
4. 交換兩個數值(swap),使用 XOR 可以加速 20%
var t:int = a;
a
= b;
b
= t;

//equals:
a
^= b;
b
^= a;
a
^= b;
5. 正負號轉換,可以加入 300%
i = -i;

//改為
i
= ~i + 1; // NOT 寫法

//或
i
= (i ^ -1) + 1; // XOR 寫法

6. 取餘數,如果除數為 2 的倍數,可利用 AND 運算加速 600%
x = 131 % 4;

//equals:
x
= 131 & (4 - 1);

7. 利用 AND 運算檢查整數是否為 2 的倍數,可以加速 600%
isEven = (i % 2) == 0;

//equals:
isEven
= (i & 1) == 0;
8. 加速 Math.abs 600% 的寫法1,寫法2 又比寫法1加速 20%
//寫法1
i
= x < 0 ? -x : x;
//寫法2
i = (x ^ (x >> 31)) - (x >> 31);
9. 比較兩數值相乘之後是否擁有相同的符號,加速 35%
eqSign = a * b > 0;

//equals:
eqSign
= a ^ b > 0;

其他位元運算技巧
1. RGB 色彩分離
var 24bitColor:uint = 0xff00cc;

var r:uint = 24bitColor >> 16;
var g:uint = 24bitColor >> 8 & 0xFF;
var b:uint = 24bitColor & 0xFF;

2. RGB 色彩合併
var r:uint = 0xff;
var g:uint = 0x00;
var b:uint = 0xcc;

var 24bitColor:uint = r << 16 | g << 8 | b;

雖然上述的數據相當誘人,不過,還是建議效能關鍵處再使用上述的方式,否則後續維護上是一個問題。

參考資料:
[1] Bitwise gems - fast integer math
[2] Bitwise Operations in C

2007年5月3日 星期四

[C#] 利用 WMI 完成 Windows 資料夾分享

MSN SpaceGoogle DocGoogle Blog啪啦資訊科技
Chui-Wen Chiu(Arick)
2007.05.03 建立

[1] 提出利用 WMI 快速完成特定資料夾分享的方式,整個概念的核心程式碼如下:

using System;
using System.Management;

namespace ConsoleApplication1 {
class Program {

// 測試片段,將 c:perl 目錄設定為共用
static void Main(string[] args) {
QshareFolder("c:\Perl", "Test Share", "This is a Test Share");
}

// WMI 建立 Windows 資料夾分享

private static void QshareFolder(string FolderPath, string ShareName, string Description) {
try {
// Create a ManagementClass object
ManagementClass managementClass = new ManagementClass("Win32_Share");

// Create ManagementBaseObjects for in and out parameters
ManagementBaseObject inParams = managementClass.GetMethodParameters("Create");
ManagementBaseObject outParams;

// Set the input parameters
inParams["Description"] = Description;
inParams["Name"] = ShareName;
inParams["Path"] = FolderPath;

inParams["Type"] = 0x0; // Disk Drive

//Another Type:
// DISK_DRIVE = 0x0
// PRINT_QUEUE = 0x1
// DEVICE = 0x2
// IPC = 0x3
// DISK_DRIVE_ADMIN = 0x80000000
// PRINT_QUEUE_ADMIN = 0x80000001
// DEVICE_ADMIN = 0x80000002
// IPC_ADMIN = 0x8000003
// inParams["MaximumAllowed"] = int maxConnectionsNum;
// Invoke the method on the ManagementClass object
outParams = managementClass.InvokeMethod("Create", inParams, null);

// Check to see if the method invocation was successful
if ((uint)(outParams.Properties["ReturnValue"].Value) != 0) {
throw new Exception("Unable to share directory.");
}

} catch (Exception ex) {
Console.WriteLine(ex.Message);
}
}
}
}



執行結果:


參考資料:
[1] How to Share Windows Folders Using C#

2007年5月2日 星期三

Prototype 1.5.1 正式推出了
MSN SpaceGoogle DocGoogle Blog啪啦資訊科技
Chui-Wen Chiu(Arick)
2007.05.02 建立

Prototype 1.5.1 於 5/1 正式推出,除了錯誤修正之外,還多出了下面幾項新功能:
1. CSS 3.0 完整支援
2. 完整支援 JSON 編解碼
3. Element#get/setStyle 指令改善跨瀏覽器的相容性與效能
4. 新的 Form#request 指令,可將序列化的 Form 資料透過 HTTPRequest 送到 Server
5. String 新增了給個新指令:blank, empty, endsWith, escapeHTML, evalJSON, include, startsWith, times, toJSON, unfilterJSON

檔案下載:
1. Prototype 1.5.1

參考資料:
[1] Prototype 官方網站
[2] http://ajaxian.com/archives/prototype-151-released
[3] Prototype API 參考



Javascript 與 CSS 合併成單一檔案下載的小技巧
MSN SpaceGoogle DocGoogle Blog啪啦資訊科技
Chui-Wen Chiu(Arick)
2007.05.02 建立

[1] 提出一種利用瀏覽器解析 CSS 和 Javascript 內容的行為模式,將 CSS 與 Javascript 內容合併在當一檔案中的小技巧,他的原理在於 CSS 解析器(Parser) 會忽略 <!--,而 Javascript 解析器會將 <!-- 解譯成 //,所以,如果內容是
<!-- /*
function t(){
alert("hello");
}
<!-- */
<!-- body { background-color: Aqua; }

對於 CSS 解析器來說,等同於下面的內容
/*
function t(){
alert("hello");
}
*/
body { background-color: Aqua; }

對於 Javascript 來說,內容則等同於下面:

// /*
function t(){

alert("hello");

}
// */
// body { background-color: Aqua; }


因此,如果我們將上述混和CSS與Javascript的內容存成 test.jscss,並在 HTML 分別使用 <script> 和 <style> 引用
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title> new document </title>
<link type="text/css" rel="stylesheet" href="test.jscss" />
<script type="text/javascript" language="javascript" src="test.jscss"></script>
</head>

<body onload = 't();'>
fdasfdsafdsa
</body>
</html>

則神奇的效果出現了
[IE6]


[Firefox 2]


該技術在[1] 描述 IE6、IE7和 Firefox 2.0 上測試通過,其他瀏覽器可能也可以運作。這樣的技術並非正規作法,而且該技術並未完整的測試過,是否有例外情況並未得知,且這些多餘的 <!-- 是否會造成維護上得問題,都是值得考慮,所以該技巧在實務仍需經過一段時間的驗證,也許未來在某種應用場合可以使用,多知道一些未嘗不好。

參考資料:
[1] Combine CSS with JS and make it into a single download!

2007年5月1日 星期二

從 C# 使用 PHP 函數庫
MSN SpaceGoogle DocGoogle Blog啪啦資訊科技
Chui-Wen Chiu(Arick)
2007.05.01 建立

測試環境:
[1] Windows XP Pro
[2] Visual Studio 2004
[3] Phalanger 2.0

基本概念是透過 Phalanger 將 PHP 程式碼編譯成 .NET IL 類別庫。如此就可以使用 .NET 上的其他語言使用該類別庫。以下示範這個過程:
1. 編譯 PHP 程式碼

Library.php
<?php
include "ClassC.php";

function f()
{
echo "Hello!n";
}

echo "Library initialized: Now, you can use ".
"classes and functions declared here.n";
?>
ClassC.php
<?php
class C
{
public $array = array(1,2,3);

function __construct($data)
{
$this->data = $data;
}
}
?>

編譯 PHP 程式碼
phpc /target:dll /out:ClassLibrary.dll Library.php ClassC.php
如下圖:

圖1、PHP 編譯結果

2. 編譯 C# 程式碼
Program.cs
using PHP.Core; // 加入 PHP 語言核心參考
using System;

namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
ScriptContext context = ScriptContext.CurrentContext;// 建立 Script 執行環境

context.Output = Console.Out; // 將 PHP 輸出重導至 Console 輸出

// 引入 ClassLibrary.dll 中的 Library.php
Type library_representative = typeof(ClassLibrary);
context.IncludeScript("Library.php", library_representative); // 印出圖3的 (1)

// 呼叫 PHP 的函數
context.Call("f"); // 印出圖 3 的 (2)

// 建立 C 物件,並使用 array("a" => 1, "b" => 2) 來初始化
object c = context.NewObject("C", PhpArray.Keyed("a", 1, "b", 2));
PhpVariable.Dump(c); // 將物件傾印出來,也就是 PHP 的 var_dump 指令,印出圖 3 的 (3)
}
}
}


圖2、C# 編譯結果


圖3、C# 程式執行結果

補充:
1. 編譯成 ClassLibrary.dll 後的 php 程式碼不需要一同佈署及能用於 .NET 程式中。

參考資料:
[1] Using PHP library from C#



搜尋此網誌