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++一樣分享出來。

相關連結:

修改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讓它置中了。
參考連結

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

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 ,將原本的檔案覆蓋過去即可

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);
}

[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=...,就不用每次上傳時都手動輸入了。相關的參數請參考

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

很酷的Google App Engine

最近Google又推出一項新服務 - Google App Engine

  主旨是讓大家可以在Google上建立一個網頁伺服器的服務網站,不過提供的語法不是一般熟悉的PHP、ASP、JSP…等等。而是新時代語言:Python
  還提供資料庫可以使用,但是也不是一般人熟悉的MySQL,而是Google自己開發的新型態資料庫GQL - DataStore,這個資料庫不同於以往的關聯式資料庫,而是用到了Google引以自豪的BitTable的技術,不像複雜的關聯式資料庫為了符合第x正規化,而產生一個以上的Table,在這個DataStore裡,完全不怕你的Table太大,因此你可以將資料全部寫入同一個Table處理,我想這是一個對資料庫全新的詮釋,新接觸者可能要花一段時間了解才行。....不過這個免費的服務在撈資料時,有限制一次最多只能撈出1000筆data。
  除此之外還有提供一些Google其他的API,包括GMail、Google Account、Picasa…等等的服務可以使用。

  我已經開始看了一些文件、API,改寫了一下我的MoneyDog,目前看來,要弄起這個網站應該是沒啥太大的問題。之後有空再慢慢把心得貼上來。

Windows上建構wxWidgets的編譯環境

如何在Windows上,建構wxWidgets的編譯環境呢?由於wxWidgets只是個Library,所以必須要安裝其他的IDE+Compiler才能寫出wxWidgets的程式,或者也是可以直接用文字編輯器寫code,然後在Comand Line底下compile也是可以(如果你夠勤勞的話...)。否則,裝一個IDE決對是一個事半功倍的事情。
wxWidgets是跨平台的Library,所以我就決定找一個優秀的跨平台IDE:Code::Blocks。這樣在任何平台都可以用熟悉的環境寫wxWidgets的程式,多麼令人蘇胡的一件事啊~ 話不多說,馬上裝來用用看!

wxWidgets簡介

  最近有點無聊,想說找找跨平台的Language來玩看看,找了一些資料,也拜讀了PCMan大大的文章(Qt, Gtk+, wxWidgets的比較),感覺wxWidgets在跨平台的表現上很猛的樣子,除了可以在不同的平台上重新編譯外,還可以結合不同的Compiler得到該平台上最佳化的結果(Ex: Windows上可用Visual C++、Borland C++ Builder compile)。
  由於wxWidgets它底層的部分在不同的平台上是重新寫過的,所以跨平台的表現就比其他的Language來得強,也不像Java需要利用JVM來執行(馬的JVM...愈來愈肥)。但是也由於這個因素,在wxWidgets中,並非所有的元件都是跨平台的,有些元件只允許在某平台上才可執行(詳見此)。
  而wxWidgets的中文資料真是有點少得可憐…Google一下wxWidgets,繁體中文的結果竟然不到7000個=_=,看來在台灣真的是不太流行。

wxWidgets簡介
  首先要先了解wxWidgets的定位是什麼,wxWidgets是一個Open Source的C++ GUI Library,所以你可以選擇自己喜歡的開發平台、及該平台上的IDE compiler撰寫wxWidgets的程式。
  如果你不想用C++也沒關係,wxWidgets也提供其他binding wxWidgets的Language,比如Python(wxPython)、Perl(wxPerl)、Java(wx4j)…等等的Language來撰寫。

● wxWidgets支援的平台與相對應的wx Library

  • Windows : wxMSW
  • Linux : wxGTK
  • Mac : wxMac

  由於wxWidgets只是個GUI的Library,所以必須透過其他的IDE來撰寫程式,GUI Library最被迫切需要的就是用快速拖拉的方式(RAD),拉出使用者所見的圖形介面。雖然說直接在文字介面下coding也是可以,但是透過所見即所得的拖拉方式,總是比較輕鬆比較直觀。

● wxWidgets的RAD工具( Rapid Application Development )
  • wxFormBuilder : 免費,跨平台,但是只提供拖拉視窗並產生code,並不提供compile環境(純RAD),而且目前尚未提供將元件拖拉到Form上任何一點的位置,一定要擺在Layout(wxSizer)裡面才行
  • wxSmith : Code::Blocks內建的wxWidgets RAD工具,使用結果覺得不是挺穩定的
  • wxDev-C++ : 以Delphi開發的免費軟體,只能在Windows上執行,RAD+IDE
  • wxGlade : 以wxPython撰寫而成,RAD+IDE
  • 其他...

wxWidgets支援各平台上,在相對應IDE上compile
  • Cross-platform :
    • Code::Blocks
    • Eclipse
  • Windows :
    • Visual C++
    • Borland C++ Builder
    • Dev-C++
  • Linux :
    • Anjuta
    • KDevelop
  • Mac :
    • Xcode

這邊提供一些wxWidgets的連結參考:

[Linux] 基本的Timer介紹

Linux提供了兩種基本的Timer機制可以使用:

  • alarm
  • setitimer

● alarm
#include <unistd.h>
unsigned int alarm(unsigned int seconds);
這是一個簡單的設定Timer介面。當呼叫了alarm( n )後,等待n秒後,就會觸發一次的SIGALRM的signal,所以必須要在呼叫alarm前,先設好SIGALRM的handler function才行。而當乎呼alarm(0)時,則表示停止當前的timer處理,不要發出SIGALRM的signal。

Return value : 返回上一次呼叫alarm的剩餘秒數。若未設定alarm,則返回0。

Example : 第一次等待1秒後觸發Timer,之後每隔2秒觸發一次。
#include <iostream>
#include <unistd.h>
#include <signal.h>

using namespace std;

void my_alarm_handler(int a)
{
cerr<<"my_alarm_handler"<<endl;
alarm(2);//重新設定
}

int main()
{
signal( SIGALRM, my_alarm_handler );
alarm(1);

while(1){}

return 0;
}

--------------------------------------------------------------------------------------
● setitimer
#include <sys/time.h>
#define ITIMER_REAL 0
#define ITIMER_VIRTUAL 1
#define ITIMER_PROF 2

int getitimer(int which, struct itimerval *value);

int setitimer(int which, const struct itimerval *value,
struct itimerval *ovalue);
setitimer與getitimer提供了三種類別的Timer使用:
  • ITIMER_REAL : 以系統真實的時間來計算,觸發時會送出SIGALRM
  • ITIMER_VIRTUAL : 只計算process真正在執行的時間(在User Mode的處理),觸發時會送出SIGVTALRM
  • ITIMER_PROF : 計算該process在User Mode與Kernel Mode的處理時間,觸發時送出SIGPROF
透過第一個參數which指定要使用哪一種Timer (ITIMER_REAL, ITIMER_VIRTUAL, ITIMER_PROF )。setitimer是用來設定該種Timer的觸發時間為多少。getitimer則是取得上一次Timer設定的時間。設定的內容是一個系統內建的struct itimerval:
struct itimerval {
   struct timeval it_interval; /* next value : 下一次觸發所需的時間*/
   struct timeval it_value; /* current value : 目前距離觸發時間點 剩餘的時間*/
};
struct timeval {
   long tv_sec; /* seconds */
   long tv_usec; /* microseconds */
};

setitimer由第二個參數value設定觸發的時間。第三個參數ovalue用來取得上一次 setitimer設定的itimerval值(此參數可以為NULL)。值得注意的是,根據itimerval裡變數的意義,當it_interval設定為0時,Timer只會觸發一次。而it_value設定為0時,代表Timer結束。

Return value : 如果成功則return 0,失敗則return -1。

Example : 第一次等待1秒後觸發Timer,之後每隔2秒觸發一次。
#include <iostream>
#include <sys/time.h>
#include <signal.h>

using namespace std;

void my_alarm_handler(int a)
{
cerr<<"test "<<endl;
}


int main(){
struct itimerval t;
t.it_interval.tv_usec = 0;
t.it_interval.tv_sec = 2;
t.it_value.tv_usec = 0;
t.it_value.tv_sec = 1;

if( setitimer( ITIMER_REAL, &t, NULL) < 0 ){
cerr<<"settimer error."<<endl;
return -1;
}
signal( SIGALRM, my_alarm_handler );

while(1){
sleep(2);
}
return 0;
}

● 根據以上,可知Linux內建的Timer還是有點簡陋,而且setitimer同一時間只能處理3個Timer,如果應用程式需要多個Timer的話,這個Linux內建的Timer可能就不敷需求了!

[BCB] 升級indy9的元件後 compile後link產生的問題

在程式compile後 會有Error出現:
 [Linker Fatal Error] Fatal: Unable to open file 'INDY.BPI'
因為安裝了INDY9.0元件以後,indy.bpi變成了indy60.bpi

兩種方法可以解決:

  • 修改預設值,改一遍即可
    Project->Option->Packages裡面的Runtime packages,找到indy刪除就可以了(左下角的Default要打勾)
  • 如果之前已經存在的專案
    可用文字編輯器打開*.bpr,搜尋裡面出現的indy.bpi,將它刪除即可

Skype4Com in BCB 的一些範例

Skype4Com在BCB上的應用,其實官網上的資料給的並不多,只是有Delphi的範例可以看一下(註:我不會寫Delphi啊~~~~~XD) 還好在論壇上找到一些些的資料,然後再靠自己的一些摸索後,終於抓到一些訣竅了。以下是一個簡單測試一些Skype4Com的Code,有興趣的可以試試看。

比較需要注意的是,由於Skype4Com下載下來的壓縮檔裡面的說明檔Skype4Com.chm,裡面所說到的Class name, Function name都不能直接用,因為Skype4Com.dll已經透過BCB封裝過了,所以必須按照BCB編過後的方式去處理才行,封裝過後的檔案在$(BCB)/Imports/裡,有四個檔案,包括SKYPE4COMLib_OCXSKYPE4COMLib_TLB的.cpp與.h檔。以下的範例有提到,請參考。

#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;
// TButton *Button1;
// TButton *Button2;
// TButton *Button3;
// 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() );

// 設置處理Skype Event的function
mSkype->OnCallStatus = OnCallStatus;
mSkype->OnMessageStatus = OnMessageStatus;
}


// @target :
// Create 一個Chat Object
// 並傳送訊息給 Friend "falldog7_test"
void __fastcall TForm1::Button1Click(TObject *Sender)
{
IChatPtr chat;
chat = mSkype->CreateChatWith( L"falldog7_test" );
//開啟對話視窗
chat->OpenWindow();
//傳送訊息過去給falldog7_test
chat->SendMessage( L"Fuck You!" );
chat->Disband();
}

// @target :
// 撥電話給 好友 : "falldog7_test"
void __fastcall TForm1::Button2Click(TObject *Sender)
{
ICallPtr call = mSkype->PlaceCall(L"falldog7_test",L"",L"",L"");
}

// @target :
// 取出所有正在通話的 Call ,並將其所有的OutputDevice導至(1) Soundcard (2) File
void __fastcall TForm1::Button3Click(TObject *Sender)
{
wstring path = L"D:\\WorkRoom\\project-中文也行\\test.wav";
ICallCollectionPtr call_list = mSkype->ActiveCalls;
for( int i=1; i<=call_list->Count ; i++ ){
call_list->get_Item(i)->set_OutputDevice( callIoDeviceTypeSoundcard, L"default" );
call_list->get_Item(i)->set_OutputDevice( callIoDeviceTypeFile, const_cast<wchar_t*>(path.c_str()) );
this->Memo1->Lines->Add( "OutputDevice direct success!" );
}
}

// Skype4Com的Event
// 在Skype4Com.chm中定義的 Class : _SkypeEvents 裡面
// HRESULT CallStatus ([in] ICall *pCall,[in] TCallStatus Status)
// This event is caused by a change in call status.
// 經由BCB處理過後的header檔,定義在 $(BCB)/Imports/SKYPE4COMLib_OCX.h 88行
//
// @TCallStatus 的定義在Skype4Com.chm 與 SKYPE4COMLib_TLB.h中都找得到
// @target : 當有人Call我時(Ringing) 就直接接起此通Call
void __fastcall TForm1::OnCallStatus( TObject *Sender, ICall* _call/*[in]*/, TCallStatus Status/*[in]*/)
{
if( Status == clsRinging ){
// ShowMessage("A Call is Rining");
_call->Answer();
}
}

// Skype4Com的Event
// 在Skype4Com.chm中定義的 Class : _SkypeEvents 裡面
// HRESULT MessageStatus ([in] IChatMessage *pMessage,[in] TChatMessageStatus Status)
// This event is caused by a change in chat message status.
// 經由BCB處理過後的header檔,定義在 $(BCB)/Imports/SKYPE4COMLib_OCX.h 93行
//
// @TChatMessageStatus 的定義在Skype4Com.chm 與 SKYPE4COMLib_TLB.h中都找得到
// @target : 當有人傳訊息給我時,就在Memo1上印出訊息
void __fastcall TForm1::OnMessageStatus( TObject *Sender, IChatMessage* pMessage/*[in]*/, TChatMessageStatus Status/*[in]*/)
{
this->Memo1->Lines->Add( AnsiString("From : ")+ pMessage->FromHandle );

if( Status == cmsReceived ){
this->Memo1->Lines->Add( AnsiString("Msg : ")+ pMessage->Body );
}
}
範例檔下載連結

Skype4Com in BCB

Skype4Com在BCB中實現有兩種方法

  1. 一種是廣為人知的透過Variant去控制Com的物件
  2. 另一種是使用BCB強大的功能,將Skype4Com.dll解析後,直接使用其中的class
以下將各給一個例子,去取得「好友清單」的列表,再把它列到Memo元件中。


法1 : 透過Variant去控制Com的物件
// Prepare Setp :
// 1. Add a Memo to the Form1
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
Memo1->Clear();

//Create a Skype Com object
Variant oSkype;
oSkype = Variant::CreateObject("Skype4COM.Skype");
// test if Skype is running, if not, start it;
if( !oSkype.OlePropertyGet("Client").OlePropertyGet("IsRunning") )
oSkype.OlePropertyGet("Client").OleFunction("Start");

// Connect to Skype via API
oSkype.OleFunction("Attach");

// Get all firends
Variant friends = oSkype.OlePropertyGet("Friends");
int count = friends.OlePropertyGet( "Count" );
Memo1->Lines->Add( "User Count : "+IntToStr(count) );

// Add to Memo
for( int i=1 ; i<=count ; i++ )//start index from 1 !!!
Memo1->Lines->Add( friends.OlePropertyGet("Item", i).OlePropertyGet("Handle") );
}
//---------------------------------------------------------------------------



法2 :

  1. 下載Skype4Com Releas,解壓縮出Skype4Com.dll
  2. 打開BCB,點選Component -> Import ActiveX Control -> Add Skype4Com.dll -> Install
  3. 至元件列表中,點選「ActiveX」,看看有沒有出現「Skype」這個Icon,有的話,就是成功了
  4. 拉一個Skype元件下來,即可使用TSkype這個class了
  5. 切記:Skype4Com.dll的位置不能改變,否則BCB會出錯
// Prepare step :
// 1. Add a Skype to Form
// 2. Add a Memo to Form
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
Skype = new TSkype(Owner);
Skype->Attach(5,false);

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

// Get the count of Friends
int count = Skype->Friends->get_Count();
Memo1->Lines->Add( IntToStr(count) );

// Add Friend list to Memo
for( int i=1 ; i<count ; i++ )// start index from 1 !!!
Memo1->Lines->Add( Skype->Friends->get_Item(i)->get_Handle() );

}
//---------------------------------------------------------------------------
延伸閱讀:

Skype API 的另一種應用 - Skype4Com

  Skype4Com - 意思就是Skype for Com,是將Skype API包成一個Com的Object,讓任何支援Com/ActiveX元件的語言所使用(如:Visual Studio, Delphi, BCB, VB, PHP, ... ),但是只限於Windows平台上。

  之前的文章介紹過的Skype API,是透過Windows Message去存取Skype的行為。但是這樣子的處理下,有些行為變得比較不直觀,比如,我想得到所有的友人清單,在自己撰寫的一個Function中,下Command : 「Search Friends」,卻必須在另一個Function : WndProc()中才能得到Skype回應的Message,這樣子的寫法會讓程式變得很支離破碎。
  而使用Skype4Com就沒有這樣子的困擾了,當你Create出一個Skype的Com物件後,就可以直接存取它裡面的一些變數值。所以就程式的觀點而言,Skype4Com是比較優的!!!


以下的網址可參考:

  1. Skype官網上 Skype4Com的說明
  2. Skype4Com的元件說明
  3. Skype4Com的Example (包含VB, C++, C#, Delphi)
  4. Skype4Com 1.0.28.2 Release下載 (包含Skype4Com.dll, Skype4Com Windows說明檔)

延伸閱讀:

Netlimit IP Switcher 1.21 release 發佈

Netlimit IP Switcher
1.21 Release Note

-1. 自動更換IP程式
-2. 可設定流量上限
-3. 可選擇網卡更換IP
-4. 多國語言
-5. 下次執行時 可設定自動開始


Netlimit IP Switcher
1.21 Change Log

- Fixed : 修正無法正常切換IP的問題

下載連結

Netlimit IP Switcher 1.2 release 發佈

Netlimit IP Switcher
1.2 Release Note

-1. Automate Switcher your IP.
-2. Set the net limit.
-3. You can choice the interface adapter to change IP.
-4. Multi-Language.
-5. You can set it auto-run when you start it next time.

-------------------------------------
Netlimit IP Switcher
1.2 Change Log

- Add : 使用者可選擇處理哪一張網路介面卡.
- Add : 支援多國語言(中文、英文) ... testing...

下載連結

[Lex] flex如何結合c++的code

我在之前的文章介紹過Lex的基本用法了,flex是用來處理Token Analysis的好工具,原本的.l檔在flex compile過後,會變成lex.yy.c,再用gcc compile後,就可以成為執行檔了。所以一般在寫入.l檔時,都是使用c的library。不過有時候想使用c++的std library時,也是有方法可以達成的!以下介紹兩個方法供參考。

1. 偷吃步 簡易法
 在.l檔直接include c++ std library,用flex產生lex.yy.c後,再用g++去compile即可。

2. c++達人 完全物件法
 flex提供了一個Lex的c++版,.l檔的寫法完全class物件化,因此用了這種方法,並不能與之前的寫法相容喔。
 使用方法有兩種:
 a. 在.l檔裡,檔案開頭加上%option c++
 b. flex指令加上-+
 而flex compile過後的檔案是lex.yy.cc,而非lex.yy.c。而且會幫你自動include FlexLexer.h檔。檔案裡頭宣告了兩個class:FlexLexer和yyFlexLexer,FlexLexer是一個interface,是給yyFlexLexer繼承的,原本用到的lex c function,全部都被包進class yyFlexLexer裡,所以,在使用時,必須先宣告一個yyFlexLexer物件來使用,原本c的yyin不見了,所以要指定input、output的來源必須透過yyFlexLexer的constructor來指定(預設是stdin & stdout),下面會給個範例可以讀檔當input。

 將c++版的class yyFlexLexer裡面的function與原本的c版的lex做簡單的比較:
 const char * YYText()
   回傳目前match的token字串,與c版的yytext相同。
 int YYLeng()
   回傳目前match的token字串長度,與c版的yyleng相同。
 int lineno() const
   回傳目前parser到的是文件中的第幾行(若要使用,記得寫入%option yylineno)

以下有個簡單完整的例子,是用c++版的Flex寫的,可以parser出文件中的number, name, string:

%option noyywrap
%{
using std::cout;
int mylineno = 0;
%}

string \"[^\n"]+\"
ws [ \t]+
alpha [A-Za-z]
dig [0-9]
name ({alpha}|{dig}|\$)({alpha}|{dig}|[_.\-/$])*
num1 [-+]?{dig}+\.?([eE][-+]?{dig}+)?
num2 [-+]?{dig}*\.{dig}+([eE][-+]?{dig}+)?
number {num1}|{num2}

%%


{ws} ;/* skip blanks and tabs */
{number} cout << "number " << YYText() << '\n';
\n mylineno++;
{name} cout << "name " << YYText() << '\n';
{string} cout << "string " << YYText() << '\n';


%%
#include <fstream>
int main( int argc, char **argv )
{
std::ifstream input;
FlexLexer* lexer;

++argv, --argc;
//--- File input ---//
if ( argc > 0 ){
input.open(argv[0]);
lexer = new yyFlexLexer( &input, &std::cout );
}
//--- Stdin ---//
else{
lexer = new yyFlexLexer;
}

while( lexer->yylex() != 0 );
return 0;
}

compile的指令:
flex -+ xxx.l
g++ lex.yy.cc -o myoutput

參考文章:FreeBSD flex

[Linux] 搜尋資料夾底下 檔案內部文字

find "path name" -name "file name" -exec grep -H "search content" {} \;

find原本是用「檔案名稱」來搜尋在哪些地方有這些檔案,將這些路徑結果餵給grep後,就可以拿來搜尋檔案內部的文字片段。

Ex:
find ./kernel -name "*.c" -exec grep -H "main" {} \;
以上這段指令,是要搜尋./kernel底下所有的.c檔,內容含有"main"的地方。

find的參數:
  -name 要搜尋哪些檔名
  -exec utility name [argument...{} \;
    搜尋出的檔名 交給哪個執行檔(utility name)處理
    {} 會被find搜尋後的檔名路徑名稱所取代
    \; 代表exec的參數到此為止

grep的參數:
  -H 列出搜尋到的檔案名稱路徑