顯示具有 BCB 標籤的文章。 顯示所有文章
顯示具有 BCB 標籤的文章。 顯示所有文章

02 5月 2009

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

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

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

18 4月 2008

[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,將它刪除即可

28 2月 2008

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 );
}
}
範例檔下載連結

31 1月 2008

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

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

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可以用,比較方便。

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

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

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

27 9月 2007

[BCB] 無法new出新的TXMLDocument

BCB中有提供一個內建的物件TXMLDocument
它可以讀進一個xml檔,並建成一個完整的tree
讓user透過它對這個xml檔做操控
而這個物件可透過拖拉Tool bar 「Internet」中的XML圖檔至編輯器中

但是如果想新建的xml的文件一多該怎麼辦?
要read進100個xml檔 就該拉100個TXMLDocument至Form中嗎?!
那這樣就太可怕了....

 

一開始我以為可以自己寫一個TXMLDocument的pointer
再去new出一個TXMLDocument的物件出來就好了....
結果卻一直有問題...
一直出現不能create TXMLDocument的error!....
TXMLDocument * obj = new TXMLDocument("");
obj->LoadFromFile("user.xml");

後來找到這篇文章才知道
http://www.tinydust.net/prog/diary/2004/02/bcbtxmldocument.html
原來不能直接使用TXMLDocument
而要透過Delphi Interface的物件去create它才行
_di_IXMLDocument

_di_IXMLDocument doc = LoadXMLDocument("user.xml");

其他操作都跟TXMLDocument都一樣了