30 10月 2007

BCB Indy的元件 IdTCPClient的OnWork有問題

BCB的Indy元件:TIdTCPClient
在使用時,出現了點問題。它的Event : OnWork,原本的意義應該要是Server端有資料傳進來時,TWorkMode的參數的值會是wmRead,當有資料要寫給Server時,wmRead的值會是wmWrite。但是,在使用時,當有資料要傳要寫時,這個Event都不會被觸發... 再拿它的sample code來看,它竟然是用Timer去判斷是否有資料要讀or寫... 這樣的話,用Event還有什麼意義,真搞不懂。

所以最後的替代方案,就是用以前的元件囉。TClientSocket,它有OnRead的Event可以用,比較方便。

15 10月 2007

[Linux] libtool: -e: command not found

最近弄嵌入式系統的作業,在compile i-boot-lite的時候,出現了這樣子的錯誤訊息
libtool: -e: command not found

解決方法:
export SED=sed

09 10月 2007

FreeBSD PHP無法啟動session

在FreeBSD的主機上,執行有session_start()的php網頁時,出現這樣的錯誤訊息...
Fatal error: Call to undefined function: session_start()
讓我很疑惑,我一直都以為安裝php時,預設會安裝session才對,檢查一下phpinfo();的結果,還真的找不到session的項目。後來google一下,找到了解決方法...
cd /usr/ports/www/phpx.x.x-session/
make
make install
make clean
apachectl restart

06 10月 2007

BCB Override WndProc的兩種方法

有時,為了攔截自訂的Message,所以必須Override WndProc,在這邊有兩種方法可以選擇
1.
class TForm1 : public TForm
{
virtual void __fastcall WndProc(TMessage&);
//....
};


2.
/// Unit1.h
class TForm1 : public TForm
{
void __fastcall MyWindowProc(TMessage&);
//...
};

/// Unit1.cpp
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
this->WindowProc = this->MyWindowProc;
//...
}
兩種方法的差別在於,法一在程式開始執行時,收到系統Message時,就會跑進WndProc了;法二卻會先跑完TForm1()的Constructor後,才會開始處理系統送來的Message。

05 10月 2007

PHP 清除session的方法

註冊session的兩種方法
1. $_SESSION['name'] = "falldog";
2. session_register( "name" );

使用第1種方法時,清除session的方法不應該用session_unregister('name');
而應該用unset(); => unset( $_SESSION['name'] );

BCB 更新Indy元件後 產生的問題

我前一陣子將BCB6的Indy更新至9.0版,結果最近寫程式時,用TClientSocket & TServerSocket的時候卻出現了compile error! 奇怪,之前更新完之後,用Indy的網路元件一點問題也沒有,怎麼現在用舊有的BCB網路元件卻給我出現錯誤訊息咧...
[C++ Error] winsock2.h(113): E2238 Multiple declaration for 'fd_set'
[C++ Error] winsock.h(55): E2344 Earlier declaration of 'fd_set'
[C++ Error] winsock2.h(116): E2146 Need an identifier to declare
[C++ Error] winsock2.h(157): E2238 Multiple declaration for 'timeval'
[C++ Error] winsock.h(98): E2344 Earlier declaration of 'timeval'
[C++ Error] winsock2.h(213): E2238 Multiple declaration for 'hostent'
....

後來google一下後,找到不少解法,但是只有一種解法能成功...
  • 將 Winsock.h 改名爲Winsock1.h
  • 將 Winsock2.h 拷貝一份並改名爲 WinSock.h
保證解决!!

不過這個方法有個後遺症...當之後再使用Indy9.0的網路元件時,當有hpp裡面#include <winsock2.h>時,就得將它改成#include <winsock.h>才行

04 10月 2007

BCB取得網路連線的名稱

因為Netlimit IP Switcher的需求,所以必須找出「網路連線」的名稱,就是控制台下的網路連線的名稱...比如"區域連線"、"ADSL"...等等的。

原本以為用MIB_IFROW裡頭的data即可,可惜並不是這麼單純...後來終於找到方法了,IP_ADAPTER_INFO裡面記錄了所有Adapter的資訊(可由GetAdaptersInfo取得),裡頭的變數AdapterName即是Adapter的GUID,還不是名稱喔!要取得名稱的話,必須進入Register中查詢後,才能得知。在Register中的路徑是:

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Network\{4D36E972-E325-11CE-BFC1-08002BE10318}

以下是我的簡單測試code
(1) 首先要拉兩個元件...ListBox & Button
(2) #include <Iphlpapi.h> 為了呼叫GetAdaptersInfo
(3) #include <vcl/registry.hpp> 為了查詢Registry

#include <Iphlpapi.h>
#include <vcl/registry.hpp>
//...

void __fastcall TForm1::Button1Click(TObject *Sender)
{
/*** 取得所有網路連線的名稱 ***/
//先透過GetAdaptersInfo取得Adapter的Name ... 此為GUID
//必須去Register取得真正的名稱
ULONG ulLen=4096;
BYTE *pbBuf=new BYTE[ulLen];
IP_ADAPTER_INFO * adp_info=NULL;

TRegistry * registry = new TRegistry();
registry->RootKey = HKEY_LOCAL_MACHINE;

if( ERROR_SUCCESS==GetAdaptersInfo((IP_ADAPTER_INFO*)pbBuf, &ulLen) )
{
adp_info = (IP_ADAPTER_INFO*)pbBuf;
do{
AnsiString key_path = "\\SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}\\"
+ AnsiString( adp_info->AdapterName )
+ "\\Connection";
if ( registry->OpenKeyReadOnly( key_path ) ){
if( registry->ValueExists("Name") ){
this->ListBox1->Items->Add( registry->ReadString("Name") );
}
}
registry->CloseKey();

adp_info = adp_info->Next;//point to next adapter!
}while(adp_info!=NULL);
}
delete pbBuf;
delete registry;
}

03 10月 2007

BCB TrayIcon元件 最小化至系統列 的基本應用三法

在BCB中,要將視窗程式要將程式最小化至系統匣中是很簡單的事,BCB提供了一個基本的元件提供使用...TrayIcon,只要用這個元件,就可以達到最小化至系統匣的目的了。TrayIcon的元件在Samples的分類底下。以下三種最小化的方法,供大家參考。

@ 按下縮下鍵就縮到系統列 @
拉入一個TrayIcon的元件, 將屬性Visible設true即可

@ 按關閉鍵時 縮入系統列 @
拉入一個TrayIcon的元件
Override WndProc

//Override的WndProc...
void __fastcall TForm1::WndProc(TMessage &Message){
if (Message.Msg == WM_CLOSE)
{
if (TrayIcon1->Visible == false)
{
TrayIcon1->Visible = true;
TrayIcon1->Minimize();
}
return;
}
WndProc(Message);
}

//再點選TrayIcon的EVENT Restore
void __fastcall TForm1::TrayIcon1Restore(TObject *Sender)
{
TrayIcon1->Visible = false;
}

@ 啟動時 就最小化在系統匣 @
1.在設計程式時,加入一個 TrayIcon 元件於主Form中,並將該元件屬性Visible設為true
2.在主 Form 的 OnActive 事件中寫入兩行程式碼:

Application->Minimize();
ShowWindow(Application->Handle, SW_HIDE);

Netlimit IP Switcher 1.12 release 發佈

增加了一些比較人性化的功能~

2007-09-16
1.12 Release
a. 增加功能: 程式開始執行後,取得目前IP
b. 增加功能: 最小化至系統匣
c. 增加功能: 記錄上次最後的設定
d. 增加功能: 下次啟動後 自動開始執行 (可放個捷徑在「程式集」中的「啟動」資料夾,開機後就會自動執行了)

下載頁面

Netlimit IP Switcher 1.11 release 發佈

有不少人反應希望能監控上傳&下載,因此,我多加了一點選擇上去了。至於選擇網路卡換IP的話,可能還要一段時間XD,因為程式不知從何下手... 過一段時間,我會將Code整理之後,放出來開放大家下載,畢竟我是Open Source的支持者:)

下載頁面


1.11 Release Note
a. 當"ip.config"中設定超過一組gateway & mask時,出現警告
b. "ip.config"中gateway可以不用設定
c. "ip.config"中的mask,gateway,ip不限大小寫
d. 使用者可以選擇監控上傳or下載的流量
e. 格式化"觀看目前流量"顯示number

使用限制:
(1) 只限於Windows環境
(2) 只能改您的網路連線中的「區域連線」的IP

Netlimit IP Switcher 1.1 release 發佈

Netlimit IP Switcher這是一個在Windows上可以限制流量、並自動更換IP的軟體,因為目前看來看去,都找不到有類似功能的軟體,只好自己寫一個囉,寫完成後還滿有成就感的,雖然只是個小程式XD~希望分享給有需要的人吧~

目前只提供偵測上傳的流量功能,如果有人需要下載的流量監控的話,我再增加吧。

Netlimit IP Switcher 1.1 release

軟體名稱:Netlimit IP Switcher

功能說明:
(1) 輸入固定流量,自動更換IP
(2) 手動更換IP
(3) 自訂每隔多久的時間檢查流量

使用方法:
(1) 下載完1.1.zip檔後,解壓打開執行Netlimit IP Switcher
(2) 執行第一次後 會產生ip.config
(3) 透過文字編輯器修改ip.config
內容說明:gateway->閘道, mask->子網路遮罩, ip->IP 位址
(4) ip.config範例 (先後順序無關)

mask 255.255.254.0
gateway 192.168.11.254
ip 192.168.10.123
ip 192.168.10.124
ip 192.168.10.125

(5) 重新執行Netlimit IP Switcher
(6) 輸入限流容量、時間,按下「開始」鍵,即可開始

使用限制:
(1) 限Windows環境下,目前測試過XP平台沒問題
(2) 此版本只提供「上傳」流量監控
(3) 只能改您的網路連線中的「區域連線」的IP
(4) ip.config中的mask & gateway只需(也只能)輸入一組,ip可輸入多組

修正Lifetype文章中有backslash '\'的問題

我已經被Lifetype中有backslash問題快煩死了...

在文章中要輸入backslash '\'時,實際上我在打文章時,必須輸入'\\'
如果輸入'\'的話,這個backslash就會被lifetype默默地吃掉....默默地...默默地...默默地...

如果文章中我輸入'\\'
顯示出來的結果是'\'
再重新編輯文章時,輸入區的文章卻變成顯示'\'而沒有顯示'\\'...
因此如果沒有再將'\'改成'\\'的話
這個backslash就會被lifetype默默地吃掉了...

以前想說自己找看看bug在哪,結果...打開Lifetype的source code來看,看得超痛苦的...
因為Lifetype將所有control頁面的流動都包在php中,因此要了解它其中的小變數,真得滿困難的...

不過不知為何,今天開竅了!
上Lifetype的網站API Document頁面看source code,感覺還滿簡單的
看到不知道的變數,都可以直接點選連結看更詳細的說明~
這樣子看就簡單多了,終於找到bug的地方了~

我目前的lifetype版本為lifetype1.2.1

修改的地方只有一個...
lifetype-1.2/class/view/admin/admineditpostview.class.php 裡面的第53

$this->setValue( "postText", str_replace('&', '&amp;', $this->_article->getText( false )));

改成

if( get_magic_quotes_gpc() )
$this->setValue( "postText", str_replace( '\\',"\\\\", str_replace('&', '&amp;', $this->_article->getText( false ))));
else
$this->setValue( "postText", str_replace('&', '&amp;', $this->_article->getText( false )));

在Windows上 修改本機端(local)的IP Address

由於最近架了個FTP,想在固定流量時,就更改IP,因此,東找西找,找到了可以修改IP的方法,在這邊供大家參考參考。

Windows提供了一個程式Netsh可以查看、修改IP, Gateway, Mask...等等的設定。因此我們就可以透過Netsh去修改本機端的IP Address。

Microsoft Netsh相關網頁

測試的方法可以在Cmd.exe(命令提示字元)下,輸入netsh,會出現...

netsh >
netsh > interface ip
netsh interface ip > dump

這樣子就可以查看目前介面卡(interface)的IP設定是如何了...

設定IP有兩種格式:(1) DHCP (2) Static
(1) DHCP是浮動IP的設定方法
(2) static是固定IP的設定方法

假設我們今天要將IP改為固定IP:100.1.1.1,則我們可以在Cmd.exe底下輸入

C:\> netsh interface ip   netsh interface ip > set address name="區域連線" source=static addr=100.1.1.1  mask=255.255.255.0 

或是比較簡易的輸入
netsh interface ip > set address "區域連線"  static  100.1.1.1  255.255.255.0 

*記得一定要輸入mask的值 否則會出現錯誤訊息

假設我們今天要將IP改為浮動IP,則我們可以在Cmd.exe底下輸入

C:\> netsh interface ip   netsh interface ip > set address "區域連線" dhcp

----

若是想寫程式直接執行,就可以修改IP的話,就將netsh後面那一串都當參數即可
在C/C++中的程式碼:

system("netsh interface ip set address \"區域連線\"  static  100.1.1.1  255.255.255.0");

在BCB中的程式碼:

AnsiString param = "interface ip set address name=\"區域連線\" source=static addr=100.1.1.1 mask=255.255.255.0";
ShellExecute( Handle, "open", "netsh", param.c_str(), NULL, SW_HIDE );

Skype API example in BCB

因為我未來的開發環境是在BCB上,因此在這邊寫了個簡單的BCB的小程式,可以與Skype連線,並送出最簡單的"PING" button。不過從最簡單的Win32 API轉至BCB時,遇到了不少難題,不過也一一克服了,在下面會詳細地說明。

在這邊的步驟可以對照Win32 API simple example這篇文章,流程是差不多的,只是實作的方法不一樣而已。

我的code在這邊…http://falldog7.googlepages.com/Skype_BCB.rar

首先遇到的問題就是...指定處理Message的Function,不能由BCB定義的BEGIN_MESSAGE_MAP和END_MESSAGE_MAP來處理。因為BCB背後所處理的方式是#define ...,所以這些Message的Message ID必須是compile前就定義好的"常數"(Ex: WM_USR+n),而我們要處理的Skype Message是在程式開始執行後,透過RegisterWindowMessage()所獲得的動態Messag ID,所以當你用BEGIN_MESSAGE_MAP來定義處理uiGlobal_MsgID_SkypeControlAPIAttach,會出現這種錯誤:
E2313 Constant expression required
因此,要定義這種由RegisterWindowMessage()取得的MessageID的處理function,可以做WndProc()的Override(覆寫)
在class TForm1中定義:

virtual void __fastcall WndProc(TMessage&);


WndProc()的Defination...
//WndProc() Overloading
void __fastcall TForm1::WndProc(TMessage & msg){
TForm::WndProc(msg);//用基本的WndProc函数讓Windows其他程式去處理~
//....
}

不過這種作法要小心,因為你可能把其他的Message都給吃下來,記得要呼叫base WndProc()去處理其他的Message

第二個比較需要注意的問題就是當接收到WM_COPYDATA的時候,要return 1; 而WndProc()一定要定義成void,所以一開始讓我很困擾,讓我不知所措,想說把void改成UINT,結果連compile都不會過了... 後來才知道,原來void WndProc(TMessage & msg);其中的參數TMessage可以設定return的值。所以只要當我們接收到WM_COPYDATA時,將msg的return值設為1即可,這樣Skype就不會Disconnect了!

if(msg.Msg==WM_COPYDATA){
// ...
msg.Result = 1;
}

Skype API simple example

原本的Skype API附有一個很基本的windows example,只要看這個範例應該就能了解Skype API的運作方式了,不過它多了不少沒必要的code,如果想了解大致上的架構與訊息傳遞的話,其實也不用太多的code,因此我在這邊重新改寫了一個有最最最基本功能簡單視窗程式。

http://falldog7.googlepages.com/Skype_simple_example_window.cpp

這隻程式,用純Win32 API寫的,只是單純的可以開始控制Skype,然後出現視窗,在視窗內點左鍵,就會送出"PING"這個指令給Skype,正常來說,Skype會回傳"PONG"的訊息回來,這樣的結果就是正確的!!!

Skype API基本流程(請對照著我寫好的範例程式):

1. 首先必須先透過RegisterWindowMessage定義一個Message ID,並存起來

UINT uiGlobal_MsgID_SkypeControlAPIAttach = RegisterWindowMessage("SkypeControlAPIAttach");
UINT uiGlobal_MsgID_SkypeControlAPIDiscover = RegisterWindowMessage("SkypeControlAPIDiscover");

2. 透過SendMessage() 將uiGlobal_MsgID_SkypeControlAPIDiscover廣播出去

SendMessage( HWND_BROADCAST, uiGlobal_MsgID_SkypeControlAPIDiscover, (WPARAM)hwnd, 0);

3. 接著就可以在WndProc()裡接收uiGlobal_MsgID_SkypeControlAPIAttach的訊息,若是LPARAM的值為程式開頭enum中定義的SKYPECONTROLAPI_ATTACH_SUCCESS,則表示有偵測到Skype。此時要將WPARAM記錄起來,因為它就是Skype Window Handle

hGlobal_SkypeAPIWindowHandle = (HWND)wParam;

4. 接著Skype就會不斷地送WM_COPYDATA的訊息過來,當我們收到後可以將訊息中的結果Show到視窗上。另外,很重要的是…在收到WM_COPYDATA後,務必要return 1;否則Skype會主動Disconnect!!!

5. 要控制Skype的話,就可以透過SendMessage() 將WM_COPYDATA的訊息,送給hGlobal_SkypeAPIWindowHandle,要傳輸的指令寫入COPYDATASTRUCT中即可。

執行結果頁面:

1.首先執行程式後,會出現一個Skype的詢問視窗,問使用者是否要開放這個試圖操控Skype的程式,當然要選Yes啦~不然怎麼Run....=_=

2. 接著可以看到一堆Skype送過來的訊息產生的訊息視窗。

3. 接著就可以看到主視窗了

4. 在視窗上面點左鍵

5. Skype回傳訊息

如果以上步驟都完成,而你也都了解其中的意義的話,那Skype API應該就算是入門完畢了:)

談談Skype API

Skype最近真是愈來愈火熱了,隨著網路電話的興起、Skype手機的出現、Wi-Max標準的制定,再再都表示,Skype日後真是無可限量啊!!! 連我的論文也跟Skype有關係了~因為Skype大方公開API的關係,讓更多人可以開發與Skype相關的軟體,我想對Skype也是有所助益啊。

Skype API雖然推出一段時間,但是我在Google上找到的中文資料卻是少之又少, 因此,我只能上去Skype API的官網看著原文的資料...(喔~殺了我吧~英文是我的罩門...) 等到略有了解後,才上到論壇去找更多人討論的資料,因為我以後的開發環境是BCB,但是論壇上的人大都是在VC上開發較為多數=_=,怎麼這麼不順利啊我...還好我集給智慧與勇氣於一身,PO了一篇文章指出我的問題,幸好有BCB的高手幫了我一個大忙~現在我已經寫出最最最簡單的程式了,哈哈哈。


學習Skype API的可去之處:


Skype API 的架構與概念:
Skype API 簡單的說,就是透過Windows Message去控制正在執行的Skype程式,也就是說不執行Skype的話,就不能使用 Skype API 了。一開始我還以為是可以直接Call它的library咧,結果並非我所想的那樣。

....如果想更深入了解 Skype API 的程式, 請繼續參考站上的其他文章~

Skype API 專欄報導

Linux下的重新命名(rename) & 新增檔案(new file)的指令

重新命名的指令mv
其實在Linux底下沒有特別處理rename的直接指令,不過可以透過mv這個指令,得到rename的效果。mv原本是用來處理「移動檔案」到不同的目錄底下,因此可以透過mv將原檔案移至原目錄底下新的檔名,就是rename的效果。
Ex: 將a.out改名稱為b.out
% mv a.out b.out

新增檔案的指令touch
Ex: 新增檔案a.txt
% touch a.txt
touch原本的意義是將檔案的建立時間更新至目前的時間,因為常常會有檔案的時間不一致,導致一些問題。比如Makefile更改時間跑到了未來,導致執行make時,會出現一些Warnning,可以透過touch Makefile來避免這個Warnning出現

MySQL++使用教學

MySQL++是一套提供C++程式可以對MySQL的Database做存取的Library。雖然MySQL原本就有提供mysql.h供人使用,但我認為MySQL++對學習C++的人而言,語法非常的簡易好用,整個用法感覺上跟PHP很像,如果學過PHP & C++的人,應該很快就能上手了~

MySQL++的相關網站:
首頁
Documentation
Simple Example

MySQL++執行環境:
‧GCC 3.x and 4.x on recent x86 Linuxes,
‧GCC 4.x from Xcode 2.x on Mac OS X 10.4
‧Visual C++ 2005 on 32-bit Windows.

而我的安裝環境是Linux的Ubuntu以下就針對Ubuntu的用者說明安裝過程
安裝MySQL++ in Ubuntu:

% sudo apt-get install libmysql++-dev  
% sudo apt-get install mysql-client-5.0

很簡單吧....不知不覺的,安裝結束了 XD,記得重開機一下,這樣等等compile程式時才抓得到library

Compile程式所需要相關MySQL++的參數:

% g++ [code-file-name] -I/usr/include/mysql++ -I/usr/include/mysql  \
-lmysqlpp -lmysqlclient

  • -I/usr/include/mysql
    • 因為MySQL++的程式都是建立在MySQL提供的mysql.h上,所以必須指名include mysql.h的路徑,這個路徑就因機器而異了,你可以locate mysql.h看看,我所寫的是Ubuntu預設的路徑
  • -I/usr/include/mysql++
    • 這個就是MySQL++的header file存放的路徑。
  • -lmysqlpp -lmysqlclient
    • 在linking time時,compiler會使用到這兩個系統註冊的library檔


MySQL++的基本類別(class)說明:
這些類別都是在mysqlpp的namespace底下,如果要使用的話,必須在程式開始就宣告using namespace mysqlpp,或是在程式中每個類別前都加上mysqlpp::Connection、mysqlpp::Query、等等。

  • Connection -- 可以與DB連線
  • Query -- 可對與DB連線的Connection下指令
  • Result -- 儲存Query執行後的結果值
  • Row -- 儲存Result中每一筆的內容


以下有個我參考官網附的Example修改的code,加上一點中文註解,希望有興趣的人可以更快學會MySQL++

#include <mysql++.h>
#include <iostream>

//這樣子之後的code裡的Connection, Query, Result, Row...
// 前面就不用加mysqlpp::
using namespace mysqlpp;
using namespace std;

int main(int argc, char *argv[])
{
/*** 連結到input輸入的argument的DB ***/
// Input argument format
// ./a.out <DB-Name> <DB-Host-IP> <Name> <Password>
Connection con(false);
if ( !con.connect( argv[1], argv[2], argv[3], argv[4] ) ){
cerr<<"connect DB error..."<<endl;
return -1;
}

/*** 從DB撈資料出來 ***/
Query query = con.query();// create一個Query
// 可以對con做讀取or寫入
query << "SELECT item FROM mydb";//輸入要query的command
Result res = query.store();//執行query,並把結果存至Result res

/*** 秀出所有的result set ***/
cout << "We have : " << endl;
if (res) {//如果res有抓到東西的話....

Row row;//row可儲存每一列的資料
Row::size_type i;
for (i = 0; row = res.at(i); ++i) {
cout << 't' << row.at(0) << endl;
}
}
else {
//秀出Query產生的Error
cerr << "Failed to get item list: "
<< query.error() << endl;
return -1;
}

return 0;
}

身為一個Programmer必須注意的事 --- i++ & ++i 效率是一樣的嗎!?

i++ & ++i 寫起來不一樣、意思不一樣,沒想到連效率也不一樣!!! 最近研讀侯捷大師翻譯的「Exception C++」讀到其中這一段,其實還滿令我驚訝的,因為我本人平時寫code的習慣,就是習慣性的將for loop最後的++寫成i++,沒想到i++比++i 還沒效率....

這個是平常寫for loop的習慣... 如果MAX_SIZE 一大,所造成的效率問題,就會比較明顯了~

for( int i=0 ; i < MAX_SIZE ; i++ )
{ .... }

為什麼說++i 比 i++還要有效率呢? 就字面上的意義而言:
‧++i => 先將i+1,再使用 i 的值~
.i++ => 先使用i的值,再將 i 的值+1

重點就是在先加或是後加的問題~先加再用,則compiler背後的運作,就是將 i 的值+1然後再使用 i。而如果是先用再加呢,compiler勢必先create出一個temp(暫時性的變數)存放著i的值,然後才將 i 的值+1,實際上使用的卻是temp的值。所以實作 i++所必須付出的代價,就是多create出一個temp的變數,以及temp變數的constructor。

在這邊我實作了一個名為INT的class,它的功能就跟一般的int型別是一樣的~而其中的operator ++很明顯地就表達出先加與後加所必須付出的代價...

#include<iostream>
#include<list>

using namespace std;

class INT{
public:
INT():_value(0){}
INT(int a):_value(a){}
INT(const INT & i):_value(i._value){}//copy constructor

//為了cout所實作出來的operator
friend ostream & operator <<(ostream & out, const INT & i){
out << i._value;
return out;
}

//i++
INT operator ++(int t){
INT temp(_value);//!!! 必須create出一個temp的變數!!!
this->_value++;
return temp;
}

//++i
INT operator ++(){
this->_value++;
return *this;
}

void operator =(int i){
_value=i;
}

private:
int _value;
};

int main(int argc, char**argv){
INT i;
cout << "i==1" <<endl;
i=1;
cout << "++i == " << ++i << endl;

i=1;
cout << "i++ == " << i++ << endl;
return 0;
}

談談Ubuntu的apt-get remove

之前因為想要自己手動compile安裝apache, php, mysql,所以想說先將原本用apt-get install安裝的apache, php, mysql先移除,然後再上網將它們的source code抓下來編譯安裝

% apt-get remove apache2
% apt-get remove php5
% apt-get remove mysql

結果手動compile安裝好後,重新開機,結果開機後自動執行的apache竟然是原本apt-get install所安裝的路徑,整個覺得很怪,不是刪除了嗎...怎麼還能跑能跳的....=__="

原來,apt-get remove <package-name>它並不會將檔案刪除,他只是將dpkg中的清單裡的套件刪了而已,並不會將它的程式、config、檔案…等刪除,於是我加上了--purge這個參數,說明是說可以將相關檔案刪除。不過實際上,跟remove是一樣的道理,只是將dpkg中的套件名稱刪除而已。

因此,如果要將套件的全部檔案刪除的話,就必須用dpkg -P <package-name> (注意...是大寫P....)才行,但是這個指令不會自動一次將所以相關套件一次刪除,只會刪除指定的套件,因此,若是想要刪除乾淨的話,就要一個一個下指令刪除才行。

比如現在要完全刪除apache2以及相關套件,先用dpkg -l 看看我們裝了哪些跟apache2相關的套件

% dpkg -l | grep apache2
ii apache2
ii apache2-mpm-prefork
ii apache2-utils
ii apache2.2-common
ii libapache2-mod-php5

因此現在必須一個個刪除這些相關套件

% dpkg -P apache2 
% dpkg -P apach2-utils
....

只是要注意有些套件是存在相依性,A depend on B,所以要先將A刪除後才能刪B。
這樣子執行完後,apache2才算是完整的被刪除了。

[Linux] 無法啟用or關閉Apache

今天看網頁怪怪的,很慢才出現,所以想說重新啟動Apache看看,可能會好一點,結果竟然出現錯誤,著實嚇了我一大跳...
% /etc/init.d/apache2 restart
(13)Permission denied: make_sock: could not bind to address [::]:80

後來才發現,原來是忘了用sudo了....Orz...要有root的權限才能啟用or關閉apache,發現原因後,覺得還滿白痴的=_="""
% sudo /etc/init.d/apache2 restart

將Lifetype Server從Windows搬移至Linux

由於本站之前在windows上執行,有時候會造成Apache莫名奇妙地失靈,所以才想將server移至Linux Ubuntu上執行。這樣子所吃的資源可能也會比較少一點。

以下紀錄了移機的過程,主要遇到的問題有...編碼以及轉簡潔網址的問題。

[事前準備工作]

  1. 將Windows的資料備份出來
    • 將apache底下的lifetype整個資料夾壓縮備份起來(用zip壓縮,傳至Linux上再用unzip解壓縮)
    • 進入phpMyAdmin後,將lifetype資料庫輸出至檔案中lifetype.sql(請選擇「使用完整新增指令」)
  2. 在Ubunte上安裝Apache、Mysql、PHP(在這邊不多加著墨)
    • sudo apt-get install apache2
    • sudo apt-get install mysql
    • sudo apt-get install php
    • sudo apt-get install php-mysql
  3. 將原本Windows的lifetype程式碼,傳至apache2預設資料夾/var/www/
    • unzip lifetype.zip
  4. 上網下載phpMyAdmin,用來管理mysql資料庫

[重建Database]

  1. 利用phpMyAdmin建立一個lifetype資料庫,校對要調成utf8_unicode_ci
  2. 在Windows匯出的lifetype.sql檔前面加上
    • SET NAMES utf8;
      SET CHARACTER_SET_CLIENT=utf8;
      SET CHARACTER_SET_RESULTS=utf8;
  3. 在cmd line下指令,匯入lifetype.sql檔
    • mysql [lifetype DB name] -u[Name] -p[Password] < lifetype.sql \
      --default-character-set=utf8
    • # 因為mysql的預設編碼為latin1,因為需設定正確的character-set才能匯入
    • # 或是直接更改mysql預設的編碼方式為utf8就更乾脆了,可修改mysql的設定檔my.cnf
    • # [client]
      # default-character-set=utf8
      # [mysqld]
      # default-character-set=utf8
      # default-collation=utf8_general_ci
  4. ---- Error Message ----
    都按照上面的步驟做的話,應該就不會有所謂的亂碼出現才對。一開始我沒就直接匯入,也不疑有他,結果就犯了全天下男人都會犯的錯…ERROR 1064 (42000) at line 330: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'xxxxxx' 主要的原因就是因為編碼問題,因為lifetype.sql中挾雜著鼎鼎大名的「許功蓋…」含有跳脫字元「」,所以就視為字串已結束,就出現錯了。
    不過我後來都照上面的做了,卻還是不斷出現同樣的錯誤訊息纏著我,讓我陷入人生中的低潮,後來才發現,原來是因為在Windows匯出lifetype.sql檔案,沒有選擇「使用完整新增指令」,所以才會不斷地出錯,當我又重新試了一次之後,終於成功了!!!讓我流下了感動的眼淚,並且大喝了一聲X,響徹雲霄!!!

[建置Lifetype網站]

  1. 把原本在Windows上的lifetype資料夾複製到/var/www後,建議是把wizard.php抓回來,重新建置一遍,這樣子會比較簡單一點,否則要自己動手修改一些東西。
    • 修改/lifetype/config/config.properties.php內的db_host為
      $config['db_host'] = localhost
    • tmpgallery這兩個資料夾權限改為777
      % cd /var/www/lifetype
      % chmod 777 tmp
      % chmod 777 gallery
    • 為了安全性起見,不將gallery底下的資料夾權限全開,因此必須將它的Owner改為apache的名字,我的主機上apache的名稱為www-data
      % chown -R www-data gallery
    • 看gallery底下有幾個資料夾就改幾個資料夾的權限,因為這些都是blog user上傳檔案的資料夾,因為本站只有一個user,blog_user_id為1,所以就只有一個資料夾「1」
      % cd gallery
      % chmod 755 1
      再將1資料夾底下的previews&previews-med的權限改成755
      % cd 1
      % chmod 755 previews
      % chmod 755 previews-med
  2. 輸入網站的網址,試試看是否正常運作。照理來說,到目前為止應該都是正常的。
    % /etc/init.d/apache2 start
    如果出現Error Message: apache2: Could not determine the server's fully qualified domain name, using 127.0.1.1 for ServerName
    在/etc/apache2/apache2.conf中,加入ServerName localhost即可
  3. 但是,要進入admin.php的後台管理系統卻會失敗,因為原本網站的IP跟新網站的IP不一樣,而原本的DB中紀錄的base_url導致網頁中的連結都不正常了。
    @解決方法:利用phpMyAdmin進入lifetype的DB,進入lt_config這個Table。修改其中的兩項參數:
    base_url
    blog_does_not_exist_url
    將這兩個url參數中的網址改成新的IP就行了!
  4. 網站中有使用authImage外掛,可能會發現在產生驗證圖檔時,出現錯誤訊息「You don't have GD support compiled in, we cannot create an authimage. Please activate GD Support.」。這是因為你的php5沒有使用GD module,因為只要將GD module安裝起來即可,以下為Ubuntu的做法
    % apt-get install php5-gd
    % /etc/init.d/apache2 restart

[使用簡潔網址]

  1. 有使用簡潔網址的人,因為Ubuntu預設沒有掛載mod_rewrite,所以要先將module rewrite掛進來,因為apache在安裝時,其實已經裝載好,只是尚未load而已。而load的語法已寫好放在/etc/apache2/mods-available中,只要做個link放至mods-enabled,apache就會自動去load了
    % sudo ln -s /etc/apache2/mods-available/rewrite.load \
    /etc/apache2/mods-enabled/rewrite.load
    或是
    %
    sudo a2enmod rewrite
  2. 修改/etc/apache2/sites-enabled/000-default
    先找到
    <Directory /var/www>
    ...
    </Directory>
    將其中的AllowOverride後的None改為All
    Options後面要有FollowSymLinks

    然後重新啟動Apache
    /etc/init.d/apache2 restart
  3. 順利的話,應該就一切正常了:)
    可惜的是,我一點也不順利....Orz,我開網頁時,一直遇到error 500,打開apache的error.log檔來看時,顯示的錯誤是:「/var/www/lifetype/.htaccess: </Files> without matching <Files> section 」我一直不知道是哪裡的問題,千萬百試之後,才發現,我的.htaccess檔的編碼是utf8,後來把它改成ansi編碼就OK了~我猜可能是從windows複製過來linux後的才變這樣。