最近在學習Python遇到了一個問題
如果加入#!/usr/bin/env python則在執行的時候就不需要打python xxx.py,直接打xxx.py即可
我遇到的問題是在Window上coding並且在Linux上執行時,出現:No such file or directory訊息困擾了我許久,本來還以為是python版本的相容性問題,結果是被無形的字符影響。原來在Window編輯完後在該行的尾段會自動加入CR LF,在Linux則是認定為LF所以#!/usr/bin/env python CR LF 在Linux會認定為#!/usr/bin/env python CR 而不是#!/usr/bin/env python,所以當然出現:No such file or directory的訊息囉!
如何看到 CR LF 這些字符?
UltraEdit、Notepad++都可以很容易地看到及轉換
我舉Notepad++來當例子
如何看:
檢視 -> 特殊字元 -> 顯示所有字元
如何轉換:
編輯 -> 檔案格式轉換 -> 轉換成UNIX格式
2014年8月21日 星期四
2012年4月27日 星期五
2010年6月3日 星期四
[轉文]處理文字檔內多了個^M
文字檔內多了個^M(是dos檔的關係)要怎麼移除呢?
tr -d \r <> newfile
Ex:
#!/usr/local/bin/bash
if [ -d ./ ]; then
for file in ./*.list ; do
tr -d \r < $file > $file.new && mv $file.new $file
done
fi
PS:如果不行就用這個sed -i “s/\r//” FILENAME
tr -d \r <> newfile
Ex:
#!/usr/local/bin/bash
if [ -d ./ ]; then
for file in ./*.list ; do
tr -d \r < $file > $file.new && mv $file.new $file
done
fi
PS:如果不行就用這個sed -i “s/\r//” FILENAME
2010年5月11日 星期二
DEL功能應用
刪除一個或數個檔案。
DEL [/P] [/F] [/S] [/Q] [/A[[:]attributes]] names
ERASE [/P] [/F] [/S] [/Q] [/A[[:]attributes]] names
names 指定一個或多個檔案或者目錄清單。
萬用字元可用來刪除多個檔案。
如果指定了一個目錄,該目錄中的所
有檔案都會被刪除。
/P 刪除每一個檔案之前提示確認。
/F 強製刪除只讀檔案。
/S 刪除所有次目錄中的指定的檔案。
/Q 安靜模式。刪除全局萬用字元時,不要求確認
/A 根據屬性選擇要刪除的檔案
屬性 R 只讀檔案 S 系統檔案
H 隱藏檔案 A 存檔檔案
I 無內容索引檔案 L 重分析點
- 表示“否”的前綴
如果命令擴展被啟用,DEL 和 ERASE 更改如下:
/S 開關的顯示句法會顛倒,即只顯示已經刪除的檔案,而不顯示找不到的檔案。
如果遇到無法刪除的檔案可以使用以下的範例來存成*.bat檔
再將無法刪除的檔案拖移至該bat檔上即可刪除
===============================
DEL F A Q %1
RD S Q %1
===============================
DEL [/P] [/F] [/S] [/Q] [/A[[:]attributes]] names
ERASE [/P] [/F] [/S] [/Q] [/A[[:]attributes]] names
names 指定一個或多個檔案或者目錄清單。
萬用字元可用來刪除多個檔案。
如果指定了一個目錄,該目錄中的所
有檔案都會被刪除。
/P 刪除每一個檔案之前提示確認。
/F 強製刪除只讀檔案。
/S 刪除所有次目錄中的指定的檔案。
/Q 安靜模式。刪除全局萬用字元時,不要求確認
/A 根據屬性選擇要刪除的檔案
屬性 R 只讀檔案 S 系統檔案
H 隱藏檔案 A 存檔檔案
I 無內容索引檔案 L 重分析點
- 表示“否”的前綴
如果命令擴展被啟用,DEL 和 ERASE 更改如下:
/S 開關的顯示句法會顛倒,即只顯示已經刪除的檔案,而不顯示找不到的檔案。
如果遇到無法刪除的檔案可以使用以下的範例來存成*.bat檔
再將無法刪除的檔案拖移至該bat檔上即可刪除
===============================
DEL F A Q %1
RD S Q %1
===============================
2010年2月24日 星期三
顯示出系統錯誤訊息
// 若有任何的錯誤, 你可以呼叫下面這個 functin
// 幫你組合出錯誤訊息
void ShowError()
{
LPVOID lpMsgBuf;
/* 中文訊息
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
FORMAT_MESSAGE_FROM_SYSTEM
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), //Default language
(LPTSTR) &lpMsgBuf,0,NULL );
*/
// 英文訊息 (比較方便到 Yahoo 查別人如何解決)
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
FORMAT_MESSAGE_FROM_SYSTEM
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
GetLastError(),
MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), // Default language
(LPTSTR) &lpMsgBuf,0,NULL );
MessageBox( NULL, (LPCTSTR)lpMsgBuf, "Error", MB_OK
MB_ICONINFORMATION );
LocalFree( lpMsgBuf );
}
// 幫你組合出錯誤訊息
void ShowError()
{
LPVOID lpMsgBuf;
/* 中文訊息
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
FORMAT_MESSAGE_FROM_SYSTEM
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), //Default language
(LPTSTR) &lpMsgBuf,0,NULL );
*/
// 英文訊息 (比較方便到 Yahoo 查別人如何解決)
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
FORMAT_MESSAGE_FROM_SYSTEM
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
GetLastError(),
MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), // Default language
(LPTSTR) &lpMsgBuf,0,NULL );
MessageBox( NULL, (LPCTSTR)lpMsgBuf, "Error", MB_OK
MB_ICONINFORMATION );
LocalFree( lpMsgBuf );
}
[轉載]VC/ Vista / Run as administrator 權限
引用 http://www.codeproject.com/KB/vista-security/VistaElevator.aspx?df=100&forumid=368223&exp=0&select=1909847
擷取 Starting an elevated process段 使用ShellExecuteinfo 中的run as 讓系統判斷目前是否需要出現Run as administrator 提示dialog.
BOOLRunElevated( HWND hwnd,
LPCTSTR pszPath,
LPCTSTR pszParameters = NULL,
LPCTSTR pszDirectory = NULL )
{
SHELLEXECUTEINFO shex;
memset( &shex, 0, sizeof( shex) );
shex.cbSize = sizeof( SHELLEXECUTEINFO );
shex.fMask = 0;
shex.hwnd = hwnd;
shex.lpVerb = _T("runas");
shex.lpFile = pszPath;
shex.lpParameters = pszParameters;
shex.lpDirectory = pszDirectory;
shex.nShow = SW_NORMAL;
return ::ShellExecuteEx( &shex );
}
[Search keywords]
[Vista 權限 執行]
[安裝 install regsvr32 COM]
[registry 存取]
[Administrator system account privilege]
擷取 Starting an elevated process段 使用ShellExecuteinfo 中的run as 讓系統判斷目前是否需要出現Run as administrator 提示dialog.
BOOLRunElevated( HWND hwnd,
LPCTSTR pszPath,
LPCTSTR pszParameters = NULL,
LPCTSTR pszDirectory = NULL )
{
SHELLEXECUTEINFO shex;
memset( &shex, 0, sizeof( shex) );
shex.cbSize = sizeof( SHELLEXECUTEINFO );
shex.fMask = 0;
shex.hwnd = hwnd;
shex.lpVerb = _T("runas");
shex.lpFile = pszPath;
shex.lpParameters = pszParameters;
shex.lpDirectory = pszDirectory;
shex.nShow = SW_NORMAL;
return ::ShellExecuteEx( &shex );
}
[Search keywords]
[Vista 權限 執行]
[安裝 install regsvr32 COM]
[registry 存取]
[Administrator system account privilege]
[轉載]Run Delphi Program Elevated under Win Vista & Win 7
最近因為工作上的需要開始接觸 Delphi,這個跟 BCB 很像,但是使用 Pascal 為語言的 IDE,不過這不是重點。
重點是,相較於 XP 在 Vista 和 7 底下多了一個新的系統安全機制 UAC ( User Account Control )。
雖然他的立足點是為了系統的安全性的考量,但是這對於程式設計上卻也是個困擾 :(
假設,你寫了一隻程式,做了一些只有 Administrator 權限才能進行的動作,例如: 修改 Registry、安裝 Driver;
在沒有點右鍵,點選 "Run as Administrator" 的情況下,你的程式可能就莫名其妙的 FAIL 了
而這個原因就在於 UAC 這個機制,他封鎖了所有一般 User 權限外所能進行的動作
那就點右鍵就好啦! 但是如果不能點右鍵呢? 總不能要求使用者每次都去點右鍵選 "Run as Administrator" 吧
很不巧...我要寫的程式連點右鍵的機會都沒有,所以要想辦法讓 UAC 自動提示 User 切換到 Administrator 的權限做事
在網路上找到了下面這個方法 - 嵌入 Manifest
1. 建立 Manifest - Autorun.exe.manifest
基本上照著附圖打就行了,但是記得把 name="Autorun" 中的 Autorun 換成你程式的名字
重點是,相較於 XP 在 Vista 和 7 底下多了一個新的系統安全機制 UAC ( User Account Control )。
雖然他的立足點是為了系統的安全性的考量,但是這對於程式設計上卻也是個困擾 :(
假設,你寫了一隻程式,做了一些只有 Administrator 權限才能進行的動作,例如: 修改 Registry、安裝 Driver;
在沒有點右鍵,點選 "Run as Administrator" 的情況下,你的程式可能就莫名其妙的 FAIL 了
而這個原因就在於 UAC 這個機制,他封鎖了所有一般 User 權限外所能進行的動作
那就點右鍵就好啦! 但是如果不能點右鍵呢? 總不能要求使用者每次都去點右鍵選 "Run as Administrator" 吧
很不巧...我要寫的程式連點右鍵的機會都沒有,所以要想辦法讓 UAC 自動提示 User 切換到 Administrator 的權限做事
在網路上找到了下面這個方法 - 嵌入 Manifest
1. 建立 Manifest - Autorun.exe.manifest
基本上照著附圖打就行了,但是記得把 name="Autorun" 中的 Autorun 換成你程式的名字

2. 建立 RC 檔 - Autorun.exe.rc
內容照抄下面的,檔名換成你剛剛建的 manifest
1 24 Autorun.exe.manifest
#ps 1 資源編號
24 資源類型為RTMAINIFEST
3. Compile RES
在 command line 下進行 compile,指令如下:
brcc32 Autorun.exe.rc -foAutorun.res
4. Include RES file
在你的 pas 檔中把 RES 檔 include 進來
{$R Autorun.res}
5. Rebuild Program
最後重新 build 一下你的程式
如果你的程式在 Vista 跟 7 中有出現一個盾牌的符號,這樣就算大功告成了。
2010年2月8日 星期一
如何在本機使用apt-get install xxx
1.建立一個資料夾並將xxx軟體的*.deb放入該資料夾
#mkdir –p /root/deb/xxx/
#cp –a *.deb /root/deb/xxx/
2./root#dpkg-scanpackages deb /dev/null | gzip > deb/Packages.gz
3.vim /.etc/apt/source.list只加上deb file:/root/ deb/
4.apt-get update
5.apt-get install xxx
你可以使用同樣的方法產生一個源碼庫,但請記住你需要將.orig.tar.gz檔案、.dsc檔案和.diff.gz檔案包含在目錄中,同時必須產生Source.gz檔案而不是Packages.gz檔案。
所使用的命令也不相同,要使用dpkg-scansources,命令如下所示:
/root#dpkg-scansources deb | gzip > deb/Sources.gz
#mkdir –p /root/deb/xxx/
#cp –a *.deb /root/deb/xxx/
2./root#dpkg-scanpackages deb /dev/null | gzip > deb/Packages.gz
3.vim /.etc/apt/source.list只加上deb file:/root/ deb/
4.apt-get update
5.apt-get install xxx
你可以使用同樣的方法產生一個源碼庫,但請記住你需要將.orig.tar.gz檔案、.dsc檔案和.diff.gz檔案包含在目錄中,同時必須產生Source.gz檔案而不是Packages.gz檔案。
所使用的命令也不相同,要使用dpkg-scansources,命令如下所示:
/root#dpkg-scansources deb | gzip > deb/Sources.gz
2010年1月14日 星期四
[轉載]extern(外部變數)&static(靜態外部變數)的差異
以extern為例:
#include#include
using namespace std;
void test1();//測試用函數一
void test2();//測試用函數二
int main(void) //內部
{
extern int a;
for(int i=1;i<=3;i++)
{
cout<<"==========NO."<cout<<"a未進入函數的值="<test1(); //放入函數且不改變
test2(); //改變a的數值
}
system("pause");
return 0;
}
int a=2; //記得要宣告a的變數型態 int
//================ 函數一 =================
void test1() //外部
{
cout<<"函數一:a="<}
//================ 函數二 ==================
void test2() //外部
{
a++;
cout<<"函數二:a+1="<}
========================================
輸出結果為:
==========NO.1==========
a未進入函數的值=2
函數一:a=2
函數二:a+1=3
==========NO.2==========
a未進入函數的值=3
函數一:a=3
函數二:a+1=4
==========NO.3==========
a未進入函數的值=4
函數一:a=4
函數二:a+1=5
========================================
從輸出結果可以看出a的值只要再其中一個地方被改變值不管是不是在程式內被改變還是在函數內被改變
a的值永遠是共用的(被外部&內部共用)
以static為例:
#include
#include
using namespace std;
void test1();//測試用函數一
void test2();//測試用函數二
int main(void)
{
static int a;
for(int i=1;i<=3;i++)
{
cout<<"==========NO."<cout<<"a未進入函數的值="<test1(); //放入函數且不改變
test2(); //改變a的數值
}
system("pause");
return 0;
}
int a=2; //記得要宣告a的變數型態 int
//========== 函數一 ==========
void test1()
{
cout<<"函數一:a="<}
//==========函數二 ==========
void test2()
{
a++;
cout<<"函數二:a+1="<}
========================================
輸出結果為:
==========NO.1==========
a未進入函數的值=0
函數一:a=2
函數二:a+1=3
==========NO.2==========
a未進入函數的值=0
函數一:a=3
函數二:a+1=4
==========NO.3==========
a未進入函數的值=0
函數一:a=4
函數二:a+1=5
========================================
內部的a和外部的a並沒有共用 函數和函數之間共用一個a
#include
using namespace std;
void test1();//測試用函數一
void test2();//測試用函數二
int main(void) //內部
{
extern int a;
for(int i=1;i<=3;i++)
{
cout<<"==========NO."<cout<<"a未進入函數的值="<test1(); //放入函數且不改變
test2(); //改變a的數值
}
system("pause");
return 0;
}
int a=2; //記得要宣告a的變數型態 int
//================ 函數一 =================
void test1() //外部
{
cout<<"函數一:a="<}
//================ 函數二 ==================
void test2() //外部
{
a++;
cout<<"函數二:a+1="<}
========================================
輸出結果為:
==========NO.1==========
a未進入函數的值=2
函數一:a=2
函數二:a+1=3
==========NO.2==========
a未進入函數的值=3
函數一:a=3
函數二:a+1=4
==========NO.3==========
a未進入函數的值=4
函數一:a=4
函數二:a+1=5
========================================
從輸出結果可以看出a的值只要再其中一個地方被改變值不管是不是在程式內被改變還是在函數內被改變
a的值永遠是共用的(被外部&內部共用)
以static為例:
#include
#include
using namespace std;
void test1();//測試用函數一
void test2();//測試用函數二
int main(void)
{
static int a;
for(int i=1;i<=3;i++)
{
cout<<"==========NO."<cout<<"a未進入函數的值="<test1(); //放入函數且不改變
test2(); //改變a的數值
}
system("pause");
return 0;
}
int a=2; //記得要宣告a的變數型態 int
//========== 函數一 ==========
void test1()
{
cout<<"函數一:a="<}
//==========函數二 ==========
void test2()
{
a++;
cout<<"函數二:a+1="<}
========================================
輸出結果為:
==========NO.1==========
a未進入函數的值=0
函數一:a=2
函數二:a+1=3
==========NO.2==========
a未進入函數的值=0
函數一:a=3
函數二:a+1=4
==========NO.3==========
a未進入函數的值=0
函數一:a=4
函數二:a+1=5
========================================
內部的a和外部的a並沒有共用 函數和函數之間共用一個a
[轉載]理解 typedef 與 #define 的差異
話說,因為看不懂前人寫的程式碼,上網查了一下說明.....
也才真正地瞭解 typedef 與 #define 的不同....真是不求甚解丫.....Orz
為了加強印象,增加這個網誌分類....
一來加強理解;二來也算是註記分享
=================================================
為了節省閱讀時間,如果看得懂下面的範例....
基本上,再看下去是沒意義的,因為接下來也只是解釋而已....
Ex 1.
typedef int Alias1[10];
typedef void (*Alias2)(void);
看懂了嗎!?看不懂沒關係,再花點時間繼續看下去囉....
讓我們先來看下面的例子....
typedef int Alias1
main() {
Alias1 PI;
PI = 3.14; }
我們可以把 typedef int Alias1 改寫成 #define Alias1 int
坊間書籍,常以這樣的方式來解釋 typedef
確實,用這樣的方式說明,兩者是相通的....
但是,這樣的說明並無法說明兩者的差異性甚至還造成了混淆
把 #define 的思維套用在 typedef(像我就是.....T^T)
相信有許多人跟我一樣會把 int 與 Alias1 拆開來看....
* int 是一組;Alias1是一組 *
* typedef int Alias1[10];
也就是有了這樣的思維,便無法理解 Ex 1. 而誤以為....
int 要替換成 Alias1[10];void 置換成 (*p)(void)
但是,這樣的敘述似乎又不是合法的,因此陷入迷惑中....
實際上,我們必需把 typedef 視為一個整體的敘述....
在Ex 1. 中的第一個小例子是說明 Alias1代表一個,具有連續10個 int 型態的資料別名....
也就是說,我們可以把Alias1視為一個一維陣列、大小是10個 int,這樣的資料型態用Alias1去稱呼它
在我們的程式碼中,如果我們常常需要使用到這樣的資料型態
我們就可以使用 typedef 去宣告,這麼一個資料型態別名,可以幫助我們去理解程式碼....
同樣的,在Ex 1.中的第二個小例子,也是相同意義只是這個資料型態是一項函式
最後,舉個例子來說明加深彼此的理解吧....
Ex 2.
#include "stdafx.h"
typedef typedef int Alias1[10];
main() {
int A1[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
Alias1 A2;
for(int i = 0; i <>
using namespace std;
typedef void (* Alias2)(void);
void functionA(void) { cout << "This class A" << endl; }
void functionB(void) { cout << "This class B" << endl; }
int main(int argc, char* argv[]) {
Alias2 pfunction;
pfunction = functionA;
pfunction();
pfunction = functionB;
pfunction();
return 0; }
也才真正地瞭解 typedef 與 #define 的不同....真是不求甚解丫.....Orz
為了加強印象,增加這個網誌分類....
一來加強理解;二來也算是註記分享
=================================================
為了節省閱讀時間,如果看得懂下面的範例....
基本上,再看下去是沒意義的,因為接下來也只是解釋而已....
Ex 1.
typedef int Alias1[10];
typedef void (*Alias2)(void);
看懂了嗎!?看不懂沒關係,再花點時間繼續看下去囉....
讓我們先來看下面的例子....
typedef int Alias1
main() {
Alias1 PI;
PI = 3.14; }
我們可以把 typedef int Alias1 改寫成 #define Alias1 int
坊間書籍,常以這樣的方式來解釋 typedef
確實,用這樣的方式說明,兩者是相通的....
但是,這樣的說明並無法說明兩者的差異性甚至還造成了混淆
把 #define 的思維套用在 typedef(像我就是.....T^T)
相信有許多人跟我一樣會把 int 與 Alias1 拆開來看....
* int 是一組;Alias1是一組 *
* typedef int Alias1[10];
也就是有了這樣的思維,便無法理解 Ex 1. 而誤以為....
int 要替換成 Alias1[10];void 置換成 (*p)(void)
但是,這樣的敘述似乎又不是合法的,因此陷入迷惑中....
實際上,我們必需把 typedef 視為一個整體的敘述....
在Ex 1. 中的第一個小例子是說明 Alias1代表一個,具有連續10個 int 型態的資料別名....
也就是說,我們可以把Alias1視為一個一維陣列、大小是10個 int,這樣的資料型態用Alias1去稱呼它
在我們的程式碼中,如果我們常常需要使用到這樣的資料型態
我們就可以使用 typedef 去宣告,這麼一個資料型態別名,可以幫助我們去理解程式碼....
同樣的,在Ex 1.中的第二個小例子,也是相同意義只是這個資料型態是一項函式
最後,舉個例子來說明加深彼此的理解吧....
Ex 2.
#include "stdafx.h"
typedef typedef int Alias1[10];
main() {
int A1[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
Alias1 A2;
for(int i = 0; i <>
using namespace std;
typedef void (* Alias2)(void);
void functionA(void) { cout << "This class A" << endl; }
void functionB(void) { cout << "This class B" << endl; }
int main(int argc, char* argv[]) {
Alias2 pfunction;
pfunction = functionA;
pfunction();
pfunction = functionB;
pfunction();
return 0; }
2009年12月4日 星期五
如何開發Android 程式
安裝Android SDK
http://ithelp.ithome.com.tw/question/10011929
如何下載與安裝Eclipse
http://ithelp.ithome.com.tw/question/10011996
深入淺出Android程式設計(9)-如何安裝Eclipse外掛(ADT)
http://ithelp.ithome.com.tw/question/10012087
深入淺出Android程式設計(12)-如何在Eclipse上開發Android應用程式(上)
http://ithelp.ithome.com.tw/question/10012318
深入淺出Android程式設計(14)-如何在Eclipse上開發Android應用程式(下)
http://ithelp.ithome.com.tw/question/10012466
http://ithelp.ithome.com.tw/question/10011929
如何下載與安裝Eclipse
http://ithelp.ithome.com.tw/question/10011996
深入淺出Android程式設計(9)-如何安裝Eclipse外掛(ADT)
http://ithelp.ithome.com.tw/question/10012087
深入淺出Android程式設計(12)-如何在Eclipse上開發Android應用程式(上)
http://ithelp.ithome.com.tw/question/10012318
深入淺出Android程式設計(14)-如何在Eclipse上開發Android應用程式(下)
http://ithelp.ithome.com.tw/question/10012466
[轉貼]淺談 EC/KBD 與OEM Application key
目前市面上的EC 大多內含Keyboard Interface, 一般本身也把 60/64 port 那邊做成一個8042 相容的keyboard controller, 支援標準與OEM command, 而在OS 下, 一般程式是就如同一般keyboard input, 的對待.在驅動程式面 Windows 下會由i8042prt.sys 的驅動程式驅動它的上層在掛一個kbdclass.sys 支援, 而PS/2的Mouse則是由 i8042prt.sys 掛 mouclass.sys 支援, 在i8042prt.sys 會負責抓取Scancode 往kbdcalss.sys 發送, 在kbdclass.sys 中會轉成windows本身支援的OS virtual key, 在這過程中, Windows 2000 與XP還提供了一個 Scancode mapper, 可以透過Registry [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layout] 下定義Scancode 須要remap 的key. (ref. Archive: Scan Code Mapper for Windows)
但是目前Notebook 上還有許多 Hotkey. 如控制Brightness, Audio Volume, Device ON/OFF 等.
這在實做上可以分為幾個不同層面
在早期OS上, 如DOS/Win3.1 的機器上, 事情簡單許多, 就通常 EC本身自己控制對應的硬體界面即可, 如Brightness 可以由EC本身的PWM interface 輸出, Audio Volume 可以用GPIO/I2C 去控制 digital resistor or amplifier. 因為OS本身不管理這些介面, 所以也就不與理會
在現代ACPI award 的OS下, 這些介面漸漸納入OS 控制, 早期如 Brightness 從XP 由driver support 到現在ACPI 3.0 由ACPI正式support, 這時就不是單純由EC實做就可以! 而Audio Volume早期的AC97 codec 可以由EC 提供 GPIO直接去 Up/Down, 然後AC97本身的Driver會去維護. 進入HD Audio後, 目前已知Realtek已經不支援這樣的function. 轉而建議透過OS支援的Multimedia key 去實作
而Device Power On/Off control 的機制, 則還沒被 ACPI/OS 納入規範中, 因此也還是可以簡單的透過EC 使用GPIO 去ON/OFF, 但是此時出現新的問題, 如何提供指示(indicator)告訴使用者, 正在處理該項事情, 以防止不小心誤按時, 使用者摸不著頭緒, 為啥Device突然被關或是打開.
這時我所知的有兩種選擇
1.使用EC透過ACPI Device 與 Q event等方式, 在OS下需要implement 一個對應的ACPI Driver 去 接收這樣的event並使用一個長駐的應用程式去處理對應的事件
2. 透過 OEM Applcation key, MSFT 有提供一個 scancode.doc 的文件, 裡面提到 win+numberic(0 ~ 9) 是保留給OEM 使用的, 此時EC可以將一些特殊的function 透過發出特定的 application key 來傳遞給OS, 而透過長駐的application 去hook keyboard event 來獲知該function.
這兩種的方式各有優缺點.
第一種方式缺點是必須寫一個ACPI driver去處理
而第二種優點是不需要寫driver. 缺點是使用者可能會不知道需要安裝長駐程式!
因此一般通常是採用1.的方法.在裝driver的時候, 順便把長駐程式掛進系統!
另外Application 收到訊息後除了可以顯示資訊也可以透過其他方法另外作一些其他特殊功能! 不過正規做法還是在ACPI Device 透過對應的method 是比較好的!可以把ACPI 當作一個 abstract layer 那麼以此發展的程式, 可以快速沿用許多平台!縱使有許多不同的硬體設計! 例如目前在我使用的Thinkpad X60 可以找到許多已經定義的ACPI driver method 有的還是透過 SW SMI 去處理, 可以suppoer 最多3顆電池等等..
但是目前Notebook 上還有許多 Hotkey. 如控制Brightness, Audio Volume, Device ON/OFF 等.
這在實做上可以分為幾個不同層面
在早期OS上, 如DOS/Win3.1 的機器上, 事情簡單許多, 就通常 EC本身自己控制對應的硬體界面即可, 如Brightness 可以由EC本身的PWM interface 輸出, Audio Volume 可以用GPIO/I2C 去控制 digital resistor or amplifier. 因為OS本身不管理這些介面, 所以也就不與理會
在現代ACPI award 的OS下, 這些介面漸漸納入OS 控制, 早期如 Brightness 從XP 由driver support 到現在ACPI 3.0 由ACPI正式support, 這時就不是單純由EC實做就可以! 而Audio Volume早期的AC97 codec 可以由EC 提供 GPIO直接去 Up/Down, 然後AC97本身的Driver會去維護. 進入HD Audio後, 目前已知Realtek已經不支援這樣的function. 轉而建議透過OS支援的Multimedia key 去實作
而Device Power On/Off control 的機制, 則還沒被 ACPI/OS 納入規範中, 因此也還是可以簡單的透過EC 使用GPIO 去ON/OFF, 但是此時出現新的問題, 如何提供指示(indicator)告訴使用者, 正在處理該項事情, 以防止不小心誤按時, 使用者摸不著頭緒, 為啥Device突然被關或是打開.
這時我所知的有兩種選擇
1.使用EC透過ACPI Device 與 Q event等方式, 在OS下需要implement 一個對應的ACPI Driver 去 接收這樣的event並使用一個長駐的應用程式去處理對應的事件
2. 透過 OEM Applcation key, MSFT 有提供一個 scancode.doc 的文件, 裡面提到 win+numberic(0 ~ 9) 是保留給OEM 使用的, 此時EC可以將一些特殊的function 透過發出特定的 application key 來傳遞給OS, 而透過長駐的application 去hook keyboard event 來獲知該function.
這兩種的方式各有優缺點.
第一種方式缺點是必須寫一個ACPI driver去處理
而第二種優點是不需要寫driver. 缺點是使用者可能會不知道需要安裝長駐程式!
因此一般通常是採用1.的方法.在裝driver的時候, 順便把長駐程式掛進系統!
另外Application 收到訊息後除了可以顯示資訊也可以透過其他方法另外作一些其他特殊功能! 不過正規做法還是在ACPI Device 透過對應的method 是比較好的!可以把ACPI 當作一個 abstract layer 那麼以此發展的程式, 可以快速沿用許多平台!縱使有許多不同的硬體設計! 例如目前在我使用的Thinkpad X60 可以找到許多已經定義的ACPI driver method 有的還是透過 SW SMI 去處理, 可以suppoer 最多3顆電池等等..
2009年10月8日 星期四
2009年9月21日 星期一
EWMH and ICCCM全名
EWMH = Extended Window Manager Hints
http://standards.freedesktop.org/wm-spec/wm-spec-1.3.html
ICCCM = Inter-Client Communication Conventions Manual
http://tronche.com/gui/x/icccm/
http://standards.freedesktop.org/wm-spec/wm-spec-1.3.html
ICCCM = Inter-Client Communication Conventions Manual
http://tronche.com/gui/x/icccm/
2009年9月10日 星期四
目前套件及部署技術
分散式系統
理想的分散式環境包含:開發、系統整合測試及生產環境三種環境。開發人員使用開發環境進行模組及整合測試。為了初步確保測試的順利進行,他們檢查源程式碼存放庫 (如 Visual SourceSafe) 中的程式碼。目前實際處理的範例包含一檢查唯讀權限的組建小組,並按需要的方式編譯二進位程式碼。組建小組繼續將分散式程式碼部署到分散式測試伺服器,即「系統整合測試」環境。為了成功的完成測試,組建小組將分散式程式碼部署到生產環境,該環境對外部操作開放。
圖 1 理想化分散式環境的示意圖
在此分散式系統中,作為部署策略的一部份,此文件期待推薦部署程序的封裝問題。封裝分散式程式碼為以一致性及控制性方式管理部署的中心。已封裝程式碼的好處包含藉由記錄顯示部署及安裝內容,執行版本控制。理想的分散式系統包含分散式網路管理系統伺服器,如 SMS 或 Application Center 2000 Server,它可能會促進已封裝 MSI 容器的部署及安裝。
圖 2 理想化分散式環境的封裝
「Microsoft 系統管理伺服器 (SMS)」可用於部署及初始化 MSI 套件的遠端部署。但自動化部署及使用 Microsoft Application Center 2000 Server 安裝這些套件可帶來更大的益處。Application Center 2000 Server 將 Windows Installer 特定的用於遠端軟體發佈及安裝。利用為開發及遠端安裝設計的預先組建 MSI 套件,Application Center 2000 Server 可以獨立及排定為基礎,用於自動化 Web 伺服器的拆卸及安裝。
部署處理程序
封裝處理程序開始於來源控制存放庫,並依序到達名單建立 (由一組自我描述部署指示組成的 XML 例項),讀取該名單的封裝工具,然後產生部署及安裝就緒的 MSI 套件。
圖 3 封裝程序概觀
封裝工具建立一Shell MSI 套件,消耗 (讀取) 名單,開始以與安裝指示相關的資訊填入 MSI 套件。該工具然後將奪取所有名單中列出的檔案,並將其精簡為將裝入 MSI 套件的壓縮 CAB 檔。該名單亦將包含於此 MSI 套件中用於安裝後指示。
圖 4 使用封裝工具建立 MSI 套件。
圖 5 封裝程序
開發人員使用開發環境測試其程式碼,然後將其來源存放至來源程式碼存放庫區域。BCM 編譯二進位程式碼,並將位元封裝至 MSI 套件。然後這些套件部署至各自的系統整合測試 (SIT) 及名單中定義之檔案結構中的生產環境。
圖 6 利用 MSI 部署的理想化環境
將所有檔案部署至其適當的目的地,並且執行了所有 Windows Installer 自身操作之後,將會安裝「安裝後 Windows Installer 自訂操作 (WICA)」元件並執行非自身 Windows Installer 操作。這些非自身操作可包含 IIS 虛擬 Web 目錄安裝程式、 NT 4.0 Server、SQL 指令檔及查詢執行方式的 MTS 套件登錄、及指令行執行。
Windows 2000 系統之 Windows Installer 本身擁有 COM+ 套件登錄。為 NT 4.0 伺服器設計之 MTS 套件的 WICA 安裝程式執行方式亦可以嚴密地執行在 Windows 2000 上。
圖 7 自動化公布的安裝處理程序
角色
開發人員
開發人員根據名單和/或 BCM 的執行方式指示,以結構格式化提供關於如何組建產品,如 DLL 的指示。開發人員將協助 BCM 產生關於 MTS/COM+ 元件、資料庫指令檔建立、及任何成功安裝所必需的名單資訊。此外,開發人員提供任何依存檔案如 .OCX、.INI、及部署產品所需之登錄資料項目和協力廠商 (如果存在) 元件。
組建設定管理員
BCM 對封裝、部署、及自動化 MSI 套件建立至關重要。此非開發人員形式可使開發人員輕鬆地進行程式碼開發。此人瞭解應用程式如何完整地工作,及各部份結合的位置。一般職責包含編譯碼、確認名單及封裝及部署應用程式。此人將特別負責:
名單 (XML 檔案) 的建立及維護。(可使用文字或 XML 編輯器手動地的建立名單,或程式化的使用來源控制的 API 自動化建立。)
定義組建元件的群組 (Windows Installer 元件)
組建程序
部署/安裝程序
遷移程序
專案領域
專案領域定義及條件對所有專案都至關重要。本文建議使用 Visual Basic 及 Windows Installer 組建自訂封裝工具。無論工具組的種類為何,部署策略應保持一致。
領域
領域定義為:
建議的工具組是指此後提及的 WinDNA 封裝程式。相關的文件提供將用於產生分散應用程式之 Windows Installer 套件的程序及一組可多次使用的元件。這些將包含提供 MTS/COM+ 的 DLL、Windows Installer 的 IIS 及 SQL 自訂操作功能、說明程式 WSH 指令檔、名單架構及架構存取程式碼、及描述該工具組使用方法的文件。
領域包含:
MSI 套件的自動化建立
名單的 XML-架構。
工具 (WinDNA 封裝程式)
該工具的結構將幫助 Windows Installer API 讀取名單及 XML 檔案表單中的資料,並建立安裝就緒的 MSI 套件。MSI 套件將由包含文字檔案 (例如,ASP、XML、HTML) 的 Windows Installer 元件、DLL、EXE、MTS/COM+ DLL、支援檔案、及該名單本身組成。工具組或部署裝備及相關的文件提供將用於產生應用程式之 Windows Installer 套件的程序及一組可多次使用的元件及指令檔。
資源
BCM 將負責產生及維護名單,即 XML 檔案,它是來源控制存放庫 (如 Visual SourceSafe) 與 WinDNA 封裝程式之間的媒體。此人亦將執行來源控制中來源程式碼的唯讀檢驗、編譯二進位碼、對部署工具開放二進位碼及支援檔案、維護名單、並使用 WinDNA 套件建立 MSI 套件。
Patches/Hotfixes/Includes
MSI 套件可包含單一 Windows Installer 元件;因此,將產生更流行的 MSI 套件取代現存的目標元件。BCM 可能選擇性的包含合併模組 (將屬於 MSI 套件) 中的二進位碼,如最新的 ADO 位元。
名單
名單作為 Windows Installer 技術的摘要的中間階層。BCM 需要瞭解 Windows Installer 的工作方法、或 MSI 資料庫的建立方法。BCM 幾乎無需瞭解 Windows DNA 應用程式對成功部署的要求。
建立名單有兩種建議方法。第一種為藉由程式化的提高來源控制 (例如,Visual SourceSafe) 物件模型及 Microsoft XML DOM,自動化建立例項。此方為兩種方法中較可靠的一種,但它需要初始程式投資。第二種方法為手動建立名單—開發人員逐段建立名單,每個人負責建立各自的區域。BCM 可將獨立例項編譯為一個檔案。
名單的建立對其相關的 XML 架構必須正確的。判定 XML 架構可為與之相關的程序,特別是由於名單負責兩個區域:套件建立及安裝後指示。名單 XML 及其使用方式詳見本文末尾的附錄章節。範例的使用方式資訊與 WinDNA 封裝程式緊密相關。
結構
該結構包含 Custom Action DLL 即「Windows Installer 自訂操作 (WICA)」處理非 Windows Installer 自身的工作。已對 MSI 套件建立套用了最佳方法。設計該結構以允許工具靈活的使用在任何 Windows DNA 應用程式的部署及安裝。
該結構亦允許支援二進位的安裝,如 MDAC (ADO) 元件的最新版本,以合併模式包含於 MSI 套件中。
套件工具非常依賴名單提供安裝 Windows DNA 應用程式的大量規則。
套件工具
封裝工具讀取名單以取得用於封裝 MSI 套件所需的指示組。封裝程序包含所有列出的檔案並將其精簡到 CAB 壓縮檔案中,此檔案實體性的儲存於 MSI 套件中。
MSI 套件為一包含檔案部署資訊,如其大小及位置,的 SQL 型資料庫。該套件亦包含下列資訊:登錄設定、ODBC 設定、NT 服務安裝程式、Windows Installer 自訂操作元件及帶有安裝後資訊的名單。
MSI 套件可與其他 MSI 套件相結合,即為合併模組。合併模組可由「組合」功能設定加以靈活擴充。例如,包含最新的「使用中資料物件 (ADO)」的 MDAC 2.5,它可自行封裝,然後併入需要 ADO 2.5 的 Windows DNA 應用程式中。
圖 8 顯示該結構的構成。由 Windows Installer API 組成的工具將讀取該名單,並相應地建立安裝 MSI 套件。它亦將該名單及安裝後「Windows Installer 自訂操作」元件併入該套件。安裝程式 MSI 套件可能透過 Windows Installer 轉型,結合合併模組或其他 MSI 套件。
圖 8 架構撰寫
圖 9 顯示如何撰寫安裝 MSI 套件。如果它結合「MSI/合併模組」套件,將造成如下列圖表中顯示的轉型。合併模組為可與其他 MSI 套件結合的 MSI 套件。
圖 9 安裝 MSI 套件,包含合併模組
圖 10 顯示轉型的 MSI 套件。
圖 10 安裝 MSI 套件結合 MSI/合併模組套件的轉型結果
Windows Installer 自訂操作 (WICA)
安裝後 COM 元件 DLL,WICA 讀取名單並依存於其安裝、執行工作 (非 Windows Installer 自身的) 的內容及類型。這些工作包含:
MTS 登入/取消登入自訂執行 DLL 建立套件並根據名單的內容將元件填入其中。如果出現移除旗標,自訂操作 DLL 在執行有效性檢查後移除套件。Windows Installer 自身支援 Windows 2000 系統的 COM+ 封裝,但不支援 NT 4.0 Server 的 COM+ 封裝。
IIS 安裝/移除。自訂操作 DLL 根據名單的內容設定虛擬目錄。如果出現移除旗標,自訂操作 DLL 在執行有效性檢查後移除虛擬目錄。
資料庫安裝程式。自訂操作 DLL 執行名單所含的 SQL 指令。它亦尋找名單中的復原指示。名單亦包含復原 SQL 指令。
指令行執行。自訂操作元件執行指令行操作,如 Windows Scripting Host 及批次檔。
有效性檢查。移除套件或虛擬目錄前,自訂操作 DLL 檢查目標套件或虛擬目錄是否為空。如果如此,則已移除該套件或虛擬目錄。
摘要
WinDNA 封裝程式為綜合的封裝解決方案,它提供 MSI 套件形式之分散設計應用程式的端對端覆蓋。MSI 套件為 SQL 類型資料庫,Windows Installer 服務使用它安裝應用程式。充分發揮 Windows Installer 技術,Windows DNA 應用程式將具有與桌面應用程式 (如同 Microsoft Office 2000) 同樣方便的安裝。
XML 例項是指包含用於封裝、部署、及安裝後指導之自身說明資料集的名單。它亦在 WinDNA 封裝程式與來源控制機制間作為額外階層。最後,它允許負責組建程序的人員 BCM,將注意集中於 Windows DNA 應用程式封裝程序,而非建立 MSI 套件必需的技術機制。
安裝後元件包含於 WinDNA 封裝程式,即為「Windows Installer 自訂操作 (WICA)」。WICA 負責 Windows Installer 自身不執行的操作—安裝後步驟—。這些步驟包含 Windows NT 4.0 Server 上 MTS 套件的登錄,它亦可用於 Windows 2000 (儘管 Windows Installer 自身支援 COM+ 套件建立) 的 COM+ 套件。安裝後的步驟額外地包含 IIS 虛擬目錄的安裝程式及其存取權限,及建立/銷毀/修改資料庫的 SQL 查詢及指令檔執行方式,因為大多數 Windows DNA 應用程式由資料庫驅動。WICA 亦將執行指令行操作,如 Windows Scripting Host 或批次檔。WICA 需要其名單以取得用於安裝後指示的資訊。
在分散式網路管理系統,如 Microsoft Systems Manager Server (SMS) 或 Microsoft Application Center 2000 Server 的協助下,MSI 套件部署及安裝可在遠端以排定且自動化方式完成。然而,WinDNA 封裝程式亦可遠端藉由擴充其功能安裝 MSI 套件,此過程藉由程式化結合「Windows 管理設備 (WMI)」物件模型來完成。
理想的分散式環境包含:開發、系統整合測試及生產環境三種環境。開發人員使用開發環境進行模組及整合測試。為了初步確保測試的順利進行,他們檢查源程式碼存放庫 (如 Visual SourceSafe) 中的程式碼。目前實際處理的範例包含一檢查唯讀權限的組建小組,並按需要的方式編譯二進位程式碼。組建小組繼續將分散式程式碼部署到分散式測試伺服器,即「系統整合測試」環境。為了成功的完成測試,組建小組將分散式程式碼部署到生產環境,該環境對外部操作開放。
圖 1 理想化分散式環境的示意圖
在此分散式系統中,作為部署策略的一部份,此文件期待推薦部署程序的封裝問題。封裝分散式程式碼為以一致性及控制性方式管理部署的中心。已封裝程式碼的好處包含藉由記錄顯示部署及安裝內容,執行版本控制。理想的分散式系統包含分散式網路管理系統伺服器,如 SMS 或 Application Center 2000 Server,它可能會促進已封裝 MSI 容器的部署及安裝。
圖 2 理想化分散式環境的封裝
「Microsoft 系統管理伺服器 (SMS)」可用於部署及初始化 MSI 套件的遠端部署。但自動化部署及使用 Microsoft Application Center 2000 Server 安裝這些套件可帶來更大的益處。Application Center 2000 Server 將 Windows Installer 特定的用於遠端軟體發佈及安裝。利用為開發及遠端安裝設計的預先組建 MSI 套件,Application Center 2000 Server 可以獨立及排定為基礎,用於自動化 Web 伺服器的拆卸及安裝。
部署處理程序
封裝處理程序開始於來源控制存放庫,並依序到達名單建立 (由一組自我描述部署指示組成的 XML 例項),讀取該名單的封裝工具,然後產生部署及安裝就緒的 MSI 套件。
圖 3 封裝程序概觀
封裝工具建立一Shell MSI 套件,消耗 (讀取) 名單,開始以與安裝指示相關的資訊填入 MSI 套件。該工具然後將奪取所有名單中列出的檔案,並將其精簡為將裝入 MSI 套件的壓縮 CAB 檔。該名單亦將包含於此 MSI 套件中用於安裝後指示。
圖 4 使用封裝工具建立 MSI 套件。
開發人員使用開發環境測試其程式碼,然後將其來源存放至來源程式碼存放庫區域。BCM 編譯二進位程式碼,並將位元封裝至 MSI 套件。然後這些套件部署至各自的系統整合測試 (SIT) 及名單中定義之檔案結構中的生產環境。
圖 6 利用 MSI 部署的理想化環境
將所有檔案部署至其適當的目的地,並且執行了所有 Windows Installer 自身操作之後,將會安裝「安裝後 Windows Installer 自訂操作 (WICA)」元件並執行非自身 Windows Installer 操作。這些非自身操作可包含 IIS 虛擬 Web 目錄安裝程式、 NT 4.0 Server、SQL 指令檔及查詢執行方式的 MTS 套件登錄、及指令行執行。
Windows 2000 系統之 Windows Installer 本身擁有 COM+ 套件登錄。為 NT 4.0 伺服器設計之 MTS 套件的 WICA 安裝程式執行方式亦可以嚴密地執行在 Windows 2000 上。
圖 7 自動化公布的安裝處理程序
角色
開發人員
開發人員根據名單和/或 BCM 的執行方式指示,以結構格式化提供關於如何組建產品,如 DLL 的指示。開發人員將協助 BCM 產生關於 MTS/COM+ 元件、資料庫指令檔建立、及任何成功安裝所必需的名單資訊。此外,開發人員提供任何依存檔案如 .OCX、.INI、及部署產品所需之登錄資料項目和協力廠商 (如果存在) 元件。
組建設定管理員
BCM 對封裝、部署、及自動化 MSI 套件建立至關重要。此非開發人員形式可使開發人員輕鬆地進行程式碼開發。此人瞭解應用程式如何完整地工作,及各部份結合的位置。一般職責包含編譯碼、確認名單及封裝及部署應用程式。此人將特別負責:
名單 (XML 檔案) 的建立及維護。(可使用文字或 XML 編輯器手動地的建立名單,或程式化的使用來源控制的 API 自動化建立。)
定義組建元件的群組 (Windows Installer 元件)
組建程序
部署/安裝程序
遷移程序
專案領域
專案領域定義及條件對所有專案都至關重要。本文建議使用 Visual Basic 及 Windows Installer 組建自訂封裝工具。無論工具組的種類為何,部署策略應保持一致。
領域
領域定義為:
建議的工具組是指此後提及的 WinDNA 封裝程式。相關的文件提供將用於產生分散應用程式之 Windows Installer 套件的程序及一組可多次使用的元件。這些將包含提供 MTS/COM+ 的 DLL、Windows Installer 的 IIS 及 SQL 自訂操作功能、說明程式 WSH 指令檔、名單架構及架構存取程式碼、及描述該工具組使用方法的文件。
領域包含:
MSI 套件的自動化建立
名單的 XML-架構。
工具 (WinDNA 封裝程式)
該工具的結構將幫助 Windows Installer API 讀取名單及 XML 檔案表單中的資料,並建立安裝就緒的 MSI 套件。MSI 套件將由包含文字檔案 (例如,ASP、XML、HTML) 的 Windows Installer 元件、DLL、EXE、MTS/COM+ DLL、支援檔案、及該名單本身組成。工具組或部署裝備及相關的文件提供將用於產生應用程式之 Windows Installer 套件的程序及一組可多次使用的元件及指令檔。
資源
BCM 將負責產生及維護名單,即 XML 檔案,它是來源控制存放庫 (如 Visual SourceSafe) 與 WinDNA 封裝程式之間的媒體。此人亦將執行來源控制中來源程式碼的唯讀檢驗、編譯二進位碼、對部署工具開放二進位碼及支援檔案、維護名單、並使用 WinDNA 套件建立 MSI 套件。
Patches/Hotfixes/Includes
MSI 套件可包含單一 Windows Installer 元件;因此,將產生更流行的 MSI 套件取代現存的目標元件。BCM 可能選擇性的包含合併模組 (將屬於 MSI 套件) 中的二進位碼,如最新的 ADO 位元。
名單
名單作為 Windows Installer 技術的摘要的中間階層。BCM 需要瞭解 Windows Installer 的工作方法、或 MSI 資料庫的建立方法。BCM 幾乎無需瞭解 Windows DNA 應用程式對成功部署的要求。
建立名單有兩種建議方法。第一種為藉由程式化的提高來源控制 (例如,Visual SourceSafe) 物件模型及 Microsoft XML DOM,自動化建立例項。此方為兩種方法中較可靠的一種,但它需要初始程式投資。第二種方法為手動建立名單—開發人員逐段建立名單,每個人負責建立各自的區域。BCM 可將獨立例項編譯為一個檔案。
名單的建立對其相關的 XML 架構必須正確的。判定 XML 架構可為與之相關的程序,特別是由於名單負責兩個區域:套件建立及安裝後指示。名單 XML 及其使用方式詳見本文末尾的附錄章節。範例的使用方式資訊與 WinDNA 封裝程式緊密相關。
結構
該結構包含 Custom Action DLL 即「Windows Installer 自訂操作 (WICA)」處理非 Windows Installer 自身的工作。已對 MSI 套件建立套用了最佳方法。設計該結構以允許工具靈活的使用在任何 Windows DNA 應用程式的部署及安裝。
該結構亦允許支援二進位的安裝,如 MDAC (ADO) 元件的最新版本,以合併模式包含於 MSI 套件中。
套件工具非常依賴名單提供安裝 Windows DNA 應用程式的大量規則。
套件工具
封裝工具讀取名單以取得用於封裝 MSI 套件所需的指示組。封裝程序包含所有列出的檔案並將其精簡到 CAB 壓縮檔案中,此檔案實體性的儲存於 MSI 套件中。
MSI 套件為一包含檔案部署資訊,如其大小及位置,的 SQL 型資料庫。該套件亦包含下列資訊:登錄設定、ODBC 設定、NT 服務安裝程式、Windows Installer 自訂操作元件及帶有安裝後資訊的名單。
MSI 套件可與其他 MSI 套件相結合,即為合併模組。合併模組可由「組合」功能設定加以靈活擴充。例如,包含最新的「使用中資料物件 (ADO)」的 MDAC 2.5,它可自行封裝,然後併入需要 ADO 2.5 的 Windows DNA 應用程式中。
圖 8 顯示該結構的構成。由 Windows Installer API 組成的工具將讀取該名單,並相應地建立安裝 MSI 套件。它亦將該名單及安裝後「Windows Installer 自訂操作」元件併入該套件。安裝程式 MSI 套件可能透過 Windows Installer 轉型,結合合併模組或其他 MSI 套件。
圖 9 顯示如何撰寫安裝 MSI 套件。如果它結合「MSI/合併模組」套件,將造成如下列圖表中顯示的轉型。合併模組為可與其他 MSI 套件結合的 MSI 套件。
圖 10 顯示轉型的 MSI 套件。
Windows Installer 自訂操作 (WICA)
安裝後 COM 元件 DLL,WICA 讀取名單並依存於其安裝、執行工作 (非 Windows Installer 自身的) 的內容及類型。這些工作包含:
MTS 登入/取消登入自訂執行 DLL 建立套件並根據名單的內容將元件填入其中。如果出現移除旗標,自訂操作 DLL 在執行有效性檢查後移除套件。Windows Installer 自身支援 Windows 2000 系統的 COM+ 封裝,但不支援 NT 4.0 Server 的 COM+ 封裝。
IIS 安裝/移除。自訂操作 DLL 根據名單的內容設定虛擬目錄。如果出現移除旗標,自訂操作 DLL 在執行有效性檢查後移除虛擬目錄。
資料庫安裝程式。自訂操作 DLL 執行名單所含的 SQL 指令。它亦尋找名單中的復原指示。名單亦包含復原 SQL 指令。
指令行執行。自訂操作元件執行指令行操作,如 Windows Scripting Host 及批次檔。
有效性檢查。移除套件或虛擬目錄前,自訂操作 DLL 檢查目標套件或虛擬目錄是否為空。如果如此,則已移除該套件或虛擬目錄。
摘要
WinDNA 封裝程式為綜合的封裝解決方案,它提供 MSI 套件形式之分散設計應用程式的端對端覆蓋。MSI 套件為 SQL 類型資料庫,Windows Installer 服務使用它安裝應用程式。充分發揮 Windows Installer 技術,Windows DNA 應用程式將具有與桌面應用程式 (如同 Microsoft Office 2000) 同樣方便的安裝。
XML 例項是指包含用於封裝、部署、及安裝後指導之自身說明資料集的名單。它亦在 WinDNA 封裝程式與來源控制機制間作為額外階層。最後,它允許負責組建程序的人員 BCM,將注意集中於 Windows DNA 應用程式封裝程序,而非建立 MSI 套件必需的技術機制。
安裝後元件包含於 WinDNA 封裝程式,即為「Windows Installer 自訂操作 (WICA)」。WICA 負責 Windows Installer 自身不執行的操作—安裝後步驟—。這些步驟包含 Windows NT 4.0 Server 上 MTS 套件的登錄,它亦可用於 Windows 2000 (儘管 Windows Installer 自身支援 COM+ 套件建立) 的 COM+ 套件。安裝後的步驟額外地包含 IIS 虛擬目錄的安裝程式及其存取權限,及建立/銷毀/修改資料庫的 SQL 查詢及指令檔執行方式,因為大多數 Windows DNA 應用程式由資料庫驅動。WICA 亦將執行指令行操作,如 Windows Scripting Host 或批次檔。WICA 需要其名單以取得用於安裝後指示的資訊。
在分散式網路管理系統,如 Microsoft Systems Manager Server (SMS) 或 Microsoft Application Center 2000 Server 的協助下,MSI 套件部署及安裝可在遠端以排定且自動化方式完成。然而,WinDNA 封裝程式亦可遠端藉由擴充其功能安裝 MSI 套件,此過程藉由程式化結合「Windows 管理設備 (WMI)」物件模型來完成。
2009年3月5日 星期四
[轉貼]VI Editor使用方法

vi 總共有三大模式:
1. Command mode (Normal mode)
預設模式,任何按鍵操作皆具備命令意義
2.Insert mode
進入資料編輯/新增模式
3.Last line mode (Command-line command)
功能指定項目,提供檔案開啟、存檔、字串替換等功能
------------------------------------------------------------
當一進去 vi 時候,即是在第一種模式下(Command mode),這種模式具有指令功能:
dd == 刪除一行
dnd == n 代表數字,即可刪除游標往下 n 行
v == Virtual mode 可以反白所要區塊
y == yank 模式,即可將反白的區塊複製
p == paste 模式,再執行 y 後,即可在游標處貼上該資料
/ == 左下角可以輸入欲搜尋字串(pattern)
: == 進入到第三種模式 (Last Line Mode)
------------------------------------------------------------
而可以藉由下列指令進入第二種模式(Insert mode):
i,a,o or I,A,O
此時左下角會有 INSERT 字樣出現,代表已經進入第二種模式
也就是可以開始編輯資料,在編輯完資料後,必須先回到第一種模式(按 Esc)
再到第三種模式(Last Line mode)(按 :)以便對檔案進行儲存動作。
------------------------------------------------------------
當回到第一種模式(Command Line)後,可以借由打入 :(冒號)
進入到第三種模式(Last Line),即可進行檔案存取操作。
:e filename :開啟編輯檔案
:r filename :讀取指定檔案檔案內容合併到目前畫面
:w filename :寫入儲存檔案
:q :離開結束程式
:x :同 wq 功能
:wq :儲存檔案然後結束程式
:e! filename :強迫開啟編輯另外檔案,放棄目前編輯的檔案
:w! filename :強迫寫入儲存檔案
:q! :強迫離開結束程式 (就算是檔案異動了尚未儲存)
:wq! :強迫儲存檔案然後結束程式
:%s/foo/bar/gc :用 bar 取代所有 foo 字樣
=== 環境設定 ===
:set :查閱目前功能設定與支援狀態
:set all :查閱所有功能設定支援狀態
:set (no)number :設定行號顯示
:set (no)wrap :設定是否自動斷行顯示
:set (no)backup :設定是否儲存備份檔案
:set (no)autoindent :設定是否自動縮排
:set (no)incsearch :設定是否遞增式尋找字串
:set (no)ignorecase :設定忽略大小寫尋找
:set (no)hlsearch :設定支援尋找結果高亮度顯示
:set (no)paste :設定目前屬於剪貼模式
:set tabstop=n :設定 tab 跳位按鍵顯示的 n 字元寬度
------------------------------------------------------------
2008年12月5日 星期五
防止重複執行程式
//防止重複執行程式
//方法一
HANDLE h = CreateMutex(NULL, TRUE, L"xxxxx");
if((h == NULL) || (GetLastError() == ERROR_ALREADY_EXISTS)) {
MessageBox(NULL,L"系統中已經有重複的程式正在執行",L"Error",MB_OK);
return 0;
}
//方法二
HWND h = FindWindow(NULL, L"xxxxx");
if(h != NULL) {
MessageBox(NULL,L"系統中已經有重複的程式正在執行",L"Error",MB_OK);
return 0;
}
//方法三
HANDLE h = CreateEvent(NULL, FALSE, FALSE, L"xxxxx");
if((h == NULL) || (GetLastError() == ERROR_ALREADY_EXISTS)) {
MessageBox(NULL,L"系統中已經有重複的程式正在執行",L"Error",MB_OK);
return 0;
}
//方法一
HANDLE h = CreateMutex(NULL, TRUE, L"xxxxx");
if((h == NULL) || (GetLastError() == ERROR_ALREADY_EXISTS)) {
MessageBox(NULL,L"系統中已經有重複的程式正在執行",L"Error",MB_OK);
return 0;
}
//方法二
HWND h = FindWindow(NULL, L"xxxxx");
if(h != NULL) {
MessageBox(NULL,L"系統中已經有重複的程式正在執行",L"Error",MB_OK);
return 0;
}
//方法三
HANDLE h = CreateEvent(NULL, FALSE, FALSE, L"xxxxx");
if((h == NULL) || (GetLastError() == ERROR_ALREADY_EXISTS)) {
MessageBox(NULL,L"系統中已經有重複的程式正在執行",L"Error",MB_OK);
return 0;
}
2008年12月2日 星期二
[轉貼]模擬滑鼠及鍵盤事件
由虛擬碼而知當
key=1表示您按下了滑鼠的左鍵
key=2表示您按下了滑鼠的右鍵依此類推...
而
keybd_event();是模擬鍵盤的動作(按下某個鍵)
mouse_event();是模擬滑鼠的動作
==================================
如何用程式觸發滑鼠按鍵的事件(如何模擬滑鼠的按鍵)
模擬滑鼠按左鍵:
mouse_event(MOUSEEVENTF_LEFTDOWN,0,0,0,0);// click down
mouse_event(MOUSEEVENTF_LEFTUP,0,0,0,0);// click down
模擬滑鼠按Double Click:
mouse_event(MOUSEEVENTF_LEFTDOWN,0,0,0,0);// click down
mouse_event(MOUSEEVENTF_LEFTUP,0,0,0,0);// click down
mouse_event(MOUSEEVENTF_LEFTDOWN,0,0,0,0);// click down
mouse_event(MOUSEEVENTF_LEFTUP,0,0,0,0);// click down
模擬滑鼠按右鍵:
mouse_event(MOUSEEVENTF_RIGHTDOWN,0,0,0,0);// click down
mouse_event(MOUSEEVENTF_RIGHTUP,0,0,0,0);// click down
==================================
如何用程式觸發鍵盤按鍵的事件(模擬鍵盤按鍵)
對Form1觸發Tab按鍵按下事件:
PostMessage(form1.Handle, WM_KEYDOWN, VK_TAB, 0);
對Edit1觸發Enter按鍵按下事件:
PostMessage(edit1.Handle, WM_KEYDOWN, VK_RETURN, 0);
對Edit1觸發Enter按鍵放開事件:
PostMessage(edit1.Handle, WM_KEYUP, VK_F1, 0);
對Edit1觸發 1 按鍵按下事件:
PostMessage(edit1.Handle, WM_KEYDOWN, 49, 0);
對Edit1觸發 A 按鍵按下事件:
PostMessage(edit1.Handle, WM_KEYDOWN, 65, 0);
==================================
key=1表示您按下了滑鼠的左鍵
key=2表示您按下了滑鼠的右鍵依此類推...
而
keybd_event();是模擬鍵盤的動作(按下某個鍵)
mouse_event();是模擬滑鼠的動作
==================================
如何用程式觸發滑鼠按鍵的事件(如何模擬滑鼠的按鍵)
模擬滑鼠按左鍵:
mouse_event(MOUSEEVENTF_LEFTDOWN,0,0,0,0);// click down
mouse_event(MOUSEEVENTF_LEFTUP,0,0,0,0);// click down
模擬滑鼠按Double Click:
mouse_event(MOUSEEVENTF_LEFTDOWN,0,0,0,0);// click down
mouse_event(MOUSEEVENTF_LEFTUP,0,0,0,0);// click down
mouse_event(MOUSEEVENTF_LEFTDOWN,0,0,0,0);// click down
mouse_event(MOUSEEVENTF_LEFTUP,0,0,0,0);// click down
模擬滑鼠按右鍵:
mouse_event(MOUSEEVENTF_RIGHTDOWN,0,0,0,0);// click down
mouse_event(MOUSEEVENTF_RIGHTUP,0,0,0,0);// click down
==================================
如何用程式觸發鍵盤按鍵的事件(模擬鍵盤按鍵)
對Form1觸發Tab按鍵按下事件:
PostMessage(form1.Handle, WM_KEYDOWN, VK_TAB, 0);
對Edit1觸發Enter按鍵按下事件:
PostMessage(edit1.Handle, WM_KEYDOWN, VK_RETURN, 0);
對Edit1觸發Enter按鍵放開事件:
PostMessage(edit1.Handle, WM_KEYUP, VK_F1, 0);
對Edit1觸發 1 按鍵按下事件:
PostMessage(edit1.Handle, WM_KEYDOWN, 49, 0);
對Edit1觸發 A 按鍵按下事件:
PostMessage(edit1.Handle, WM_KEYDOWN, 65, 0);
==================================
訂閱:
文章 (Atom)