02 5月 2009

C++ Builder / Delphi 深度歷險 電子書下載

上次貼出侯捷大師的「深入淺出MFC」電子書下載連結,獲得不少好評,心胸寬大的作者不只侯捷一人,還有陳昇瑋大師的著作C++ Builder 深度歷險、Delphi 深度歷險這兩本書也有免費的電子書可以下載,有興趣的人可以下載來閱讀。(感謝Beavis大大的推薦)

31 1月 2009

初探Skype4Py

  在Skype API裡,除了之前介紹的Windows Message、Skype4Com的方法之外,還有一個Skype4Py可以使用。

  Skype4Py是用Python的語法,將Skype API包裝起來,提供一層high-level的介面供使用者使用,用法跟Skype4Com很類似,而且它還有一個特點,就是跨平台!寫出一個Skype4Py的Python程式,可以在Wiindows、Linux和Mac等不同的OS上執行。而這也是Python的特點之一。

  在我看過Skype4Py的source code後,原來它只是將不同平台上的Skype API底層的做法包裝起來,所以實際上它執行的方法,在Windows上,還是透過Windows Message傳遞的。有興趣的人可以參考看看。

安裝方法:
  1. 安裝Python 2.4以上的版本
  2. 安裝Skype
  3. 安裝Skype4Py

  就是這麼簡單,在安裝Skype4Py上,也提供了多種選則,在Windows上可以安裝exe執行檔,或是下載source code自行下指令安裝。

  在安裝完成後,就可以寫個小小的簡單測試程式:
import Skype4Py

# Create Skype instance
skype = Skype4Py.Skype()

# Connect Skype object to Skype client
skype.Attach()

print 'Your full name:', skype.CurrentUser.FullName
print 'Your contacts:'
for user in skype.Friends:
print ' ', user.FullName
  寫法相當的簡潔,這樣子就可以取得好友清單了。Skype4Py的另一項特點,就是它的線上Document格式長得很像JavaDoc的格式,個人覺得在使用上是頗為方便。

相關網頁:

29 1月 2009

Skype API - DTMF介紹

  在一般的電話對談中,如果我們不小心按到了電話上的按鍵(0~9, #, *),此時會響起一個嗶嗶聲,這個聲響就是以DTMF的方式編碼傳送,每個按鍵都有各自獨有的頻率,所以照理來說,只要對方可以解析傳來的DTMF頻率,即可知道傳來的按鍵為何,可以讓對方做進一步的處理…(如,撥打分機號碼)

  而在Skype的AP中,也有著類似的介面供使用者在通話中,按下畫面上虛擬的按鍵,即會發出DTMF Tone了。而 Skype API 也有提供解析DTMF code的方法,很是方便。

  Skype API 提供了一個Command供使用者送出/接收DTMF code,「SET CALL DTMF」,在Skype4Com裡,也提供了一個Event,當收到DTMF code時,就會觸發「CallDtmfReceived」Event,不過在使用上還是有些限制…
  1. 使用者只能接收到對方送來的DTMF code。當自己送出DTMF code時,並不會觸發CallDtmfReceived Event。
  2. 在Skype4Com的Document中提到,「This event can be received with P2P calls」,所以似乎只限定在P2P的通話上( Skype AP<->Skype AP ),不支援手機<->Skype AP

範例程式:
範例程式下載連結

#include <vcl.h>
#include <iostream>
#pragma hdrstop

#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma link "SKYPE4COMLib_OCX"
#pragma resource "*.dfm"
TForm1 *Form1;
using namespace std;

// 這個程式是用來測試Skype4Com的一些功能
// (1) 要執行這個程式 首先要將$(BCB)/Imports/加入 Include Path
// #include "SKYPE4COMLib_OCX.h"
// (2) 需要新增以下的元件:
// TListBox *ListBox1;
// TMemo *Memo1;
// (3) 需要額外宣告變數:
// TSkype * mSkype;

//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
mSkype = new TSkype(Owner);
mSkype->Attach(5,false);

// test if Skype is running, if not, start it;
if( !mSkype->Client->IsRunning )
mSkype->Client->Start(false,false);
// 1st parameter - start Skype in minimized state
// 2nd parameter - show splash screen

// Get the count of Friends
int count = mSkype->Friends->get_Count();

// Add Friend list to Memo
for( int i=1 ; i<count ; i++ )// start index from 1 !!!
this->ListBox1->Items->Add( mSkype->Friends->get_Item(i)->get_Handle() );

// bind the Skype Event.
mSkype->OnCallDtmfReceived = OnCallDtmfReceived;
}

// Skype4Com的Event
// 在Skype4Com.chm中定義的 Class : _SkypeEvents 裡面
// HRESULT CallDtmfReceived ([in] ICall *pCall,[in] BSTR Code)
// This event is caused by a call DTMF event.
// 經由BCB處理過後的header檔,定義在 $(BCB)/Imports/SKYPE4COMLib_OCX.h 100行
//
// @target : when receive the DTMF code, print on Memo.
void __fastcall TForm1::OnCallDtmfReceived( TObject *Sender, ICall* pCall/*[in]*/, BSTR code/*[in]*/)
{
this->Memo1->Lines->Add( "OnCallDtmfReceived" );
if( pCall!=NULL ){
this->Memo1->Lines->Add( AnsiString("DTMF Code : ")+WideString(code) );
}
}

補充:
在coding時的測試要透過兩個不同的Skype AP,一般的情況下都得在兩台安裝Skype AP的電腦上測試,建議可以用官網提供的Skype Launcher這個小程式,就能在一台電腦上執行多個Skype AP了。

相關連結:

17 1月 2009

[Python] 實作出 class中的 virtual function

  在Python的世界裡,Class並沒有pure virtual function的概念,所以也沒有所謂的virtual class,但是實際上Python提供了一套機制去模擬virtual function的概念,雖然不像C++嚴謹到無法使用virtual class去宣告出一個實體物件,但是至少在使用此function時,可以丟出一個Error Message出來。

  Python提供的方法就是,你可以在pure virtual function裡,丟一個Error message NotImplementedError出來,夠簡單了吧… 其實自己隨便丟一個也可以達到相同的目的=_=,只是Python提供了個error type,讓我們用起來更有感覺。不過,沒魚蝦也好啦~

以下是範例程式碼…

class virtualClass:
def __init__(self):
pass
def virtualMethod(self): #virtual function.
raise NotImplementedError( "virtualMethod is virutal! Must be overwrited." )

class subClass( virtualClass ):
def __init__(self):
virtualClass.__init__(self)
pass
def virtualMethod(self): #overwrite the virtual function.
print 'subClass! have overwirted the virtualMethod() success.'

v = virtualClass()
v.virtualMethod()

s = subClass()
s.virtualMethod()

侯捷大師 - 深入淺出MFC 2/e 電子書下載

最近想看看MFC的書,也不知道什麼書寫得比較好,就搜尋一下別人推薦的,結果就找到這個免費的電子書,而且是全書的章節都有,侯捷大師真是佛心來著的(大心)。真的是太屌了,我只能這麼說,一個作者可以完全開放電子書供人下載,胸襟之大,可以見得。

有興趣的人也可以下載來看看,是繁體中文版的喔~

直接下載PDF檔連結:

04 1月 2009

做中學 - Skype API Tracer

第一次接觸Skype API的人,看完上一篇介紹Skype API架構的文章後,可能還是霧煞煞的,不太清楚在講什麼東東,沒關係,在這邊我們可以藉由一個Skype官網提供的 Tool - Skype API Tracer 的幫忙,來更加了解Skype API到底在搞什麼 、怎麼搞的!?

首先,必須先到 Skype Download 頁面下載 Skype API Tracer 這個 Tool。

接著,開啟你電腦上的Skype,並啟動 Skype API Tracer,操作介面如下:

操作介面非常的簡潔明瞭,這個Tool就是透過Skype API去操控原本的Skype主程式,比如,在指令輸入欄位輸入「PING」後,就會回傳「PONG」。

也可以輸入指令,取得目前登入Skype帳號的好友清單,輸入「SEARCH FRIENDS」,接著就會回傳「USERS echo123, xxx1, xxx2, xxx3, ....」回傳好友清單中,每個好友的帳號的串列,並以分號「,」為間隔。

當然除了取得帳號相關的資訊外,也可以針對Skype的視窗處理的API,如「MINIMIZE 」,輸入後,Skype主程式的視窗就會自動最小化。輸入「OPEN DIALPAD」後會出現Skype的撥號介面。

當然也可以透過指令,讓Skype撥電話給某位使用者,比如我想打電話給我的朋友SummerDog,就輸入「Call SummerDog」即可,Skype會自動幫你處理接下來撥號的動作。

所以,說穿了,Skype API也不過是個文字遊戲,我們丟給它一些文字指令,它可以選擇吃不吃這些指令,不吃就吐個錯誤訊息給你,吃的下去,就幫你處理一些事情,或是回傳一些有用的訊息給你。
而這些文字訊息都必須透過Windows Message去傳遞,而且必須注意的是,文字訊息的內容,必須用UTF-8的編碼格式才行,不然文字訊息一有中文的內容,而Programmer又不將中文encoding成UTF-8的編碼(在程式裡,預設編碼應該是Big5),Skype會完全看不懂這訊息,就不會理你了。

以上介紹的這些API指令,都可以在官網找到說明與簡易教學,在這邊列出給大家參考一下:

可以用的API指令還滿多的,有興趣的人,可以先使用Skype API Tracer先與Skype溝通一下後,再試著寫入程式裡面,會更有效率~

以上介紹的API指令,都是屬於Windows Message傳遞去控制Skype,其實還有另一個方法,可以用Function的概念去處理Skype API的架構 - Skype4Com,有興趣的人可以參考一下拙作

26 12月 2008

Python IDE - HAP (Python Remote Debugger)

由於公司專案開發使用的程式語言是Python,所以不能像以前一樣只用Notepad++打天下了。進公司後才知道Python有 HAP (全名是Python Remote Debugger)這個IDE的開發工具,下面是它執行的介面圖。很普通,但是基本應該有的功能都有。

HAP - Python Remote Debugger具備下列幾項優點
  1. 可以儲存Python專案。
  2. 可以設中斷點除錯,一般的除錯功能都有,包括Wath、Call Stack、Local視窗,真的還滿方便的。
  3. 有類似Noteped++的書籤功能,可以讓改code更方便。
  4. 支援Tab、Shift Tab,調整code的range。
  5. 支援選取多行程式,自動註解的功能。
  6. 自動解析py檔的Class架構,列出檔案中所有的function list。
但是也是有幾個缺點啦:
  1. 最新版本是2005.08的版本,已經很久沒有再更新了…
  2. Only for Windows.
  3. 預設關閉「顯示Line Number」的功能,沒有Line Number在旁邊,真的很麻煩@_@
  4. 程式碼的顏色沒有Template可以選擇,我個人是比較習慣黑底的介面,雖然自己改成黑底的介面,但是由於它是將設定存在Register中,所以不能像Notepad++一樣分享出來。

相關連結:

25 12月 2008

修改CSS的Style讓版面置中

最近又改了新的Blogger Template,感覺滿漂亮的(至少比上一個好多了XD),果然還是拿現成的比較方便~ 只是拿了現成的後,總會發現一些不如人意的地方,還有一些小缺點,像現在這個Template原本來寬螢幕上,會整個偏左邊,所以就找了一下如何修改CSS Style,讓樣版置中。

主要分兩種:
1. 彈性寬度 置中
會隨著螢幕的寬度變大,而讓版面變大
#container {
margin: 0 auto;/*上下邊界為零、左右邊界則由瀏覽器自動調節(auto)*/
width: 80%;
}


2. 固定寬度 置中
不會隨著螢幕寬度改變大小
/*方法一*/
#container {
position: relative;
margin: 0 auto;
width: 760px;
}

/*方法二*/
#container {
position: relative;
left: 50%;
margin-left: -380px; /* 760 除以 -2 */
width: 760px;
}

所以我只要找到Template的版面的div的id,我就可以修改它的Styles讓它置中了。
參考連結

18 11月 2008

Netlimit IP Switcher SVN Host

之前就將Netlimit IP Switcher的Release程式放在 SourceForge.net 上面,但是Source Code都是我自行在電腦上面開發,所有的版本控制都是人工的…(汗...)。但是最近進公司終於學會SVN的用法了,所以就導到這個Project來玩玩。

SVN果然是個好物,在做大型程式開發時,沒有了它,程式應該是黑白的(...)。雖然我的程式不大,但是還是可以使用它。

相關連結:

下載最新的SVN code:
svn co https://limitipswitcher.svn.sourceforge.net/svnroot/limitipswitcher/trunk

24 10月 2008

Notepad++ dark style!

原本Notepad++畫面都是白底的,看起來是既簡單又乾淨,但是身為一個Programmer,整天看著白底的螢幕,多多少少還是有點傷眼睛,所以我在google大神的指引下找到了一個別人改過的Notepad++ Style,在我手上我又修改了一下key word的顏色,勉強還看得下去。 有興趣的人可以下載去改一下。

這是我的Dark Style Notepad++執行畫面

Notepad++ Dark Style

修改步驟:
  1. 下載Style檔
  2. 按Windows「開始」列表,點選「執行」
  3. 輸入「%APPDATA%\Notepad++
  4. 將剛下載的Style檔改名為 stylers.xml ,將原本的檔案覆蓋過去即可

17 7月 2008

Skype4Com in BCB範例 - 將Call In的使用者加入Conference中

  在Skype中,當使用者目前有一個通話正在進行,而其他使用者想要加入此通話,則變成為三方通話(即Skype中的多方通話功能),此多方通話稱為一個Conference。使用Skype4Com的功能,只要將新Call In進來的電話,呼叫Join( ConferenceId )的Function,傳入目前正在進行中的Call的ConferenceId,即可達到此功能。

以下有範例程式檔,供參考:
Unit1.h
#ifndef Unit1H
#define Unit1H
//---------------------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include "SKYPE4COMLib_OCX.h"
#include <OleCtrls.hpp>
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published: // IDE-managed Components
TButton *Button1;
void __fastcall Button1Click(TObject *Sender);
private: // User declarations
public: // User declarations
long mConferenceId;
TSkype * mSkype;

__fastcall TForm1(TComponent* Owner);
void __fastcall OnCallStatus( TObject *Sender, ICall* pCall/*[in]*/, TCallStatus Status/*[in]*/);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif



Unit1.cpp
#include <vcl.h>
#include <iostream>
#pragma hdrstop

#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma link "SKYPE4COMLib_OCX"
#pragma resource "*.dfm"
TForm1 *Form1;

using namespace std;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
mSkype = new TSkype(Owner);
mSkype->Attach(5,false);

// test if Skype is running, if not, start it;
if( !mSkype->Client->IsRunning )
mSkype->Client->Start(false,true);
// 1st parameter - start Skype in minimized state
// 2nd parameter - show splash screen

//註冊Skype4Com的CallStatus Event
mSkype->OnCallStatus = OnCallStatus;
}

// Call 一位使用者
void __fastcall TForm1::Button1Click(TObject *Sender)
{
ICallPtr call = mSkype->PlaceCall(L"falldog7_test",L"",L"",L"");
}

// 當有使用者Call In進來,則自動接起電話
// 第一位使用者打入,記錄ConferenceId
// 其後打入的使用者,加入此Conference
void __fastcall TForm1::OnCallStatus( TObject *Sender, ICall* _call/*[in]*/, TCallStatus Status/*[in]*/)
{
if( Status == clsRinging ){
if( mConferenceId > 0 ){
_call->Join( mConferenceId );
_call->Answer();
}
else{
_call->Answer();
mConferenceId = _call->ConferenceId;
}
}
}

Skype4Com in BCB範例 - Call其他使用者

在Skype4Com裡,如果要Call Out給其他使用者,有兩種方法可以做到。

1. 使用Skype4Com的Function:PlaceCall
  這個方法是Skype4Com現成的Function,但是同一時間Call Out的人數上限只有4人...
void __fastcall TForm1::Button1Click(TObject *Sender)
{
ICallPtr call = mSkype->PlaceCall( L"falldog7_test", L"falldog7_test2", L"", L"" );
}


2. 使用Skype4Com的Command模式
  這個方法,就好像是使用Windows Message傳送Command的方式一樣,將要傳給Skype的指令儲存在一字串裡,再將此字串傳送給Skype4Com即可。這個方法就沒有人數的上限了!
  因此在這個方法裡,可以將使用者的名稱接在Call指令的後面即可。詳細的官網說明。使用Skype4Com提供的Command[ID][Command][Reply][Block][Timeout],取得一個ICommand的物件,再將此Command送給Skype4Com即可。
  • ID:Command的ID,可以自行指定
  • Command:Command的內容,必須為wchar_t字串
  • Reply:預期回傳的字串,預設為""
  • Block:是否等待Skype完成此動作
  • Timeout:Block的Timeout時間,單位為ms(豪秒),預設為30000
void __fastcall TForm1::Button2Click(TObject *Sender)
{
WideString call_user_list = L"Call falldog7_test, falldog7_test2";
ICommandPtr cmd = mSkype->Command[1][ call_user_list.c_bstr() ][L""][true][5000];
mSkype->SendCommand(cmd);
}

16 7月 2008

[Google App Engine] 介紹 - 建置開發環境

目前Google App Engine(以下簡稱GAE) 1.1版,支援Python 2.5版,而GAE的SDK開發環境,只要是可以執行Python 2.5的平台,都可以,所以無論Windows、Linux或是Mac都可以開發GAE。

1. 安裝GAE SDK

  所以建置開發環境的第一步就是在你的電腦上安裝Python 2.5(以上),然後就可以安裝GAE SDK
  安裝完成後,有兩個python檔案先介紹一下,詳細的用法之後再一一說明:
  • dev_appserv.py :在本機端測試的python執行檔
  • appcfg.py :上傳至GAE Server的python執行檔

2. 本機端測試
  由於GAE是提供虛擬伺服器的服務,所以它的測試環境與一般的Apache、Tomcat類似,可以將你寫好的GAE Code執行起來,然後使用者可以透過瀏覽器輸入http://localhost:8080 (預設是8080的Port,可以更改),去測試你寫好的程式。

2.1 建立Project檔案
  可以在GAE的安裝目錄底下新建一個新資料夾Ex: Test。此Test資料夾內,必須至少有兩個檔案:(1)app.yaml (2)myapp.py。其中,app.yaml是你的Project的config檔(檔名固定),myapp.py就是背後處理網頁的python檔(檔名自訂)。

2.2 app.yaml簡介
  每個Project資料夾中,必須要有一個app.yaml檔,這是一個Project config檔,用來指定Project在GAE上註冊的名稱,目前的版本,還有最重要的URL mapping。比如下面這個範例,是安裝GAE SDK時,裡面附的範例檔 - GuestBook,這是它的app.yaml檔的內容:
application: guestbook
version: 1
runtime: python
api_version: 1

handlers:
- url: .*
script: guestbook.py
  application放的就是Project檔的名稱(上傳至GAE時,就依此名稱存放,因此必須是有註冊成功的名稱才行),version就是目前的開發版本,允許的版本號的範圍為^(?!-)[a-z\d\-]{1,100}$,有興趣的可以試試不一樣的版本號:)
handlers放的就是URL的mapping,會根據url的內容,指定給一個python script檔去執行。而這個guestbook的範例,就是將所有的url都交給guestbook.py這個檔案執行。此url的撰寫方式,支援正規表示法
  所有你的GAE Project底下的URL,第一關卡,就是經過這個app.yaml決定誰去處理。所以你的GAE Project裡的Image、CSS、Javascript檔的URL link,都會經過此app.yaml的處理。因為這些檔案都是直接上傳給瀏覽器即可,不需要額外的處理,所以可以用static_dir這個handler去指定它。如:
handlers:
- url: /images
static_dir: images
- url: /js
static_dir: js
- url: /.*
script: myapp.py
  根據上面這個app.yaml的設定,當url為/images/.*時,會直接從images這個資料夾找到相同名稱的檔案上傳。同樣的,/js/.*也是一樣的做法。而其他所有的url,全會交由myapp.py處理。
  必須注意到的是,url mapping會由上而下找到第一個對應到的url rule,所以/.*這個rule必須擺在最下面,否則所有的url都會符合此rule,就不會找其他的rule了。
  其他更多的app.yaml說明,請參考官網

2.3 本機端執行Project
  當完成上面的步驟後,建好自己的Project,資料夾內至少包括(1)myapp.py (2)app.yaml,就可以執行起來試看看了。在Console Mode底下,到GAE的安裝目錄底下,輸入下面的指令即可(Windows的使用者要先將*.py指定給Python執行)
dev_appserver.py myapp/

  GAE安裝好後,有一個範例檔在:$(GAE)/demos/guestbook。執行的指令為:
dev_appserver.py demos/guestbook

  執行後,即可在瀏覽器中輸入http://localhost:8080,看看Project執行後的成果了。
修改程式碼時,並不需要重新啟動Server,只需要網頁重新整理即可,很方便。

dev_appserver.py有一些參數可以用,僅列出常用的,其餘請參考
 --help
  印出說明訊息
 --port=...
  指定localhost的port,預設為8080
 --clear_datastore
  清除之前的Datastore的data


3 上傳Project至GAE Server
  在Console Mode底下,至GAE安裝目錄,執行appcfg.py即可上傳。
appcfg.py [options] update <app-directory>
  輸入的<app-directory>必須要包含app.yaml,上傳的檔案會根據app.yaml的設定去搜尋需要上傳的檔案。且會根據app.yaml中設定的application名稱,上傳至你註冊的GAE application。上傳時,會詢問你的email和密碼,也可以在[options]裡面,指定--email=...,就不用每次上傳時都手動輸入了。相關的參數請參考

04 7月 2008

Google App Engine 1.1.0 Gql Query Date Property 的Bug

最近在玩Google App Engine時,玩沒多久就發現一個Bug... 應該是Bug沒錯吧=_=

當Gql Query的filter條件加入DateProperty時,就會出錯。解決方法,就是不要用DateProperty,改用DatetimeProperty就行了。

出現的Error Message:
BadValueError: Unsupported type for property : <type 'datetime.date'>

以下是會出錯的範例Code
class TestDB(db.Model):
author = db.UserProperty()
content = db.StringProperty()
date = db.DateProperty()

class MainPage(webapp.RequestHandler):
def get(self):
query = db.GqlQuery("SELECT * FROM TestDB WHERE date>:1", datetime.datetime(2008,7,4) )#Correct!
query = db.GqlQuery("SELECT * FROM TestDB WHERE date>:1", datetime.date(2008,7,4) )#Error!
query.fetch(10)

[Python] datetime取得當月份天數

Python並沒有提供datetime中month的天數(一個月,最多幾天),所以必須要用算的把它算出來,是有點麻煩。以下是我個人的作法,如果有比較好的作法,歡迎交流:)

import datetime
def DaysOfMonth( dt ): #dt的type是datetime or date皆可
#先加一個月,避免邊界值問題。
if dt.day < 15 :
dt = dt+datetime.timedelta(31)
else:
dt = dt+datetime.timedelta(20)
dt = dt+datetime.timedelta(days=(-dt.day))#再扣掉天數
return dt.day

dt = datetime.datetime(2008,7,4)
print DaysOfMonth(dt)
# output>> 31

dt = datetime.date(2008,7,4)
print DaysOfMonth(dt)
# output>> 31