原文轉自:http://www.3800hk.com/news/w45/56263.html
前置知識:Borland C++ Builder 6.0基本使用方法
Socket:本文主要介紹了遠程屏幕監視軟件EagleEye的開發與設計過程。逐一介紹了比較正規的專業程序開發步驟:需求分析、功能設計與實現、軟件測試運行。在功能設計中用程序流程圖展現了程序執行的具體過程。在功能實現中詳細的展現了系統的各個功能模塊、所需的VCL類和自定義的相關類,給出了實現相應的功能的函數及代碼,文章的最後還給出了本軟件的主要功能源代碼。我們推出這樣的文章是想讓大家真正瞭解到專業程序開發的流程和詳細的步驟分析,或許這個文章就能讓無數新手邁進專業程序開發的第一步呢?!
帶你邁上專業軟件開發第一步:遠程屏幕監視軟件的設計與實現
通過21天的編寫,遠程輔助類工具軟件EagleEye 基本編寫完畢,能夠完成多窗口多種方式(可以選擇截圖或實時監視的方式)同時監視多臺遠程計算機,可以實現對本地計算機上顯示的遠程屏幕圖像的實行存儲,可以將圖像拷貝到剪貼板,並且用戶可以設定監視時間、監視方式,可以選擇程序的運行方式(可以是正常模式,也可以使程序隱藏於後臺運行)。完全實現了我最初的設想。心情不錯,於是急忙拿出來和廣大的黑防讀者們一起共享,共同富裕嘛!
開發環境和功能分析
遠程屏幕監視作爲我自主編寫的遠程控制軟件的一個模塊,我將其獨立分割出來發布測試,軟件要求能實現分屏同時監視遠程主機。大體的開發環境是:
操作系統:Windows XP SP1
開發所用計算機配置:512M內存+P42.4G CPU+800MHZ系統總線
環境:Borland C++ Builder 6.0+SUIStyle控件
至於功能需求分析,我們一點一點看,這或許是專業開發人員和普通自學程序員之間最大的差距,一起來學習、探討一下吧!
1.爲實現數據的網絡傳輸,軟件採用C/S(客戶端/服務器)數據傳輸模式。從遵循計算機道德的角度出發,在該設計過程中,不打算編寫具有木馬特徵的獨立的服務端,而將軟件集客戶端(Client)和服務端(Server)於一體。軟件基本定位是:遠程輔助類工具軟件。
Socket:咳……其實你可以花5分鐘時間將客戶端(Client)和服務端(Server)分離,並加入自啓動的功能,如果夠狠的話,再加入關聯文件的功能,這不是一個截屏木馬?
2.服務端(Server)實現:採用C++ Builder的Socket Server控件來實現服務端的數據傳輸功能。主要功能:監聽本地計算機的指定端口,截獲本機屏幕信息,拷貝屏幕區域到自定義的位圖變量。
Socket:爲了達到提高傳輸效率,服務端根據在連接過程中所獲得的轉換參數將位圖轉換成JPGE格式後存入緩存區,在與遠程客戶端連接的前提下,將緩存區數據流發送至客戶端(Client)。
3.客戶端(Client)實現:利用Winsock API函數來定義用於本軟件網絡數據傳輸的Sockets。功能實現:向遠程主機服務端(Server)提出連接申請,並在此過程中將JPGE轉換參數發送給服務端(Server)。在獲得連接的前提下,不斷接受遠程服務端(Server)所發送至的包含遠程主機屏幕信息的JPGE的數據流,並實時將所接受的圖像在本軟件的客戶區域(Client Region)上重繪,從而實現在本地計算機實時監視遠程計算機屏幕的目的。
詳細功能設計
上面大體分析了功能和需要,下一步一起來看看如何進行詳細的功能設計。
1.客戶端(Client)
(1)客戶端與服務端之間網絡暢通狀況的檢測。編寫Ping模塊,此模塊發送一個ICMP echo request(ICMP協議回顯請求)至目標主機,如果獲得回顯,則向目標主機發送連接請求。
(2)客戶端(Client)與服務端(Server)之間的數據傳輸。利用Winsock API函數來定義用於本軟件數據傳輸的Sockets。具體過程爲:連接遠程主機->返回有效SOCKET(使用Connect_Server())->向SOCKET寫字符串(使用Write_Socket())->向遠程主機的指定端口發送字符串提供轉換參數(使用SendMsg())->動態分配端口,並與SOCKET綁定->返回該SOCKET(使用BindSocket())->向遠程主機的指定端口發送請求(使用SendStream())->從遠程主機的指定端口接收數據流(使用RecvStream())。
(3)圖像重繪:使用Image控件將從遠程主機發送到的JPGE圖像顯示。
2.服務端(Server)
(1)服務端(Server)功能的實現。使用C++ Builder的Socket Server控件編寫軟件的服務端(Server)。監聽本地計算機指定端口,接受由客戶端發送至的相關參數,將參數傳遞給屏幕圖像截取模塊。
(2)屏幕圖像截取與傳輸步驟:讀取取得桌面的矩形區域範圍GetWindowRect()->創建內存設備描述表從而定義位圖變量GetDC()->拷貝屏幕的指定區域到位圖BitBlt();->創建JPEG圖象將位圖轉化爲JPEG格式->保存JPEG圖象信息至內存數據流Assign(),SavetoStream()->將圖象信息數據流通過Sockets發送至客戶端(由SendStream()實現)。
程序流程圖
數據結構與算法
在此對實現主要功能的類和方法做出說明,對由IDE所生成的與可視化控件相關的方法在此不予詳細說明。同時爲配合新手學習,代碼後附詳細功能註釋。
1.所用的VCL中現成的類
TBitmap(位圖對象)類和TJPEGImage類。TBitmap(位圖)是VCL中抽象圖形類Graphics的一個對象,它可以用於在內存中創建和處理圖像,也可以存儲圖像數據流。TBitmap包裝了VCL中的位圖操作,其屬性有Palette、Height、Width和TransparentColor等;TJPEGImage封裝了用於處理JPGE格式數據的Graphic類,它可以對以JPGE格式壓縮的圖像數據進行創建和讀取。其屬性有:Height、Palette、Performance、PixelFormat、Scale、Width等。
2.繼承於VCL中的類
(1)線程類TRecvStreamThread,繼承於TThread類:
class TRecvStreamThread : public TThrea
{
private:
protected:
void __fastcall Execute(); //
public:
__fastcall TRecvStreamThread(bool CreateSuspended);//接受數據的線程
bool __fastcall LoadImage(TImage *Image1);//顯示圖像
TImage *RemoteScreen; // 顯示圖象的對象指針
TStatusBar *StatusBar; // 顯示狀態信息的對象指針
AnsiString RemoteAddress; // 遠程主機IP
int CL, CQ; // 色深和圖象品質
};
(2)主窗口類TMainForm,繼承於TForm類:
class TMainForm : public TForm
{
__published:
TMainMenu *MainMenu1;
TToolButton *ToolButton14;
……
……//可視化控件相關聲明
void __fastcall HelpAbout1Execute(TObject *Sender);
void __fastcall FileExit1Execute(TObject *Sender);
void __fastcall ServerSocket1ClientRead(TObject *Sender,
TCustomWinSocket *Socket);
void __fastcall ServerSocket1ClientError(TObject *Sender,
TCustomWinSocket *Socket, TErrorEvent ErrorEvent,
int &ErrorCode);
void __fastcall FormCreate(TObject *Sender);
void __fastcall FileSaveAsItemClick(TObject *Sender);
void __fastcall HideForm(TObject *Sender);
……//處理可視化控件的相關方法
private:
void __fastcall CreateMDIChild(const AnsiString sAddress, int CL, int CQ);
public:
virtual __fastcall TMainForm(TComponent *Owner);
};
(3)子窗口類TMDIChild:
class TMDIChild : public TForm
{
__published:
……//可視化控件相關聲明
void __fastcall FormClose(TObject *Sender, TCloseAction &Action);
void __fastcall Image1DblClick(TObject *Sender);
void __fastcall N3Click(TObject *Sender);
void __fastcall N2Click(TObject *Sender);
……//處理可視化控件的相關方法
private:
public:
virtual __fastcall TMDIChild(TComponent *Owner);
bool __fastcall LoadImage(void); // 載入圖象
AnsiString RemoteAddress; // 遠程主機IP
int CL, CQ; // 色深和圖象品質
};
3.繼承類中的相關方法:
(1)CaptureImage()——捕獲當前屏幕並保存到Imagestream中。具體代碼如下:
void CaptureImage(int options, int level, int cq, TMemoryStream* imgstream)
{
LONG width,height;
RECT capRect;
HDC DesktopDC;
switch (options) {
case CM_ENTIRESCREEN: // 捕獲整個屏幕
// 取得桌面的矩形區域範圍
GetWindowRect(GetDesktopWindow(),&capRect);
break;
case CM_ACTIVEWINDOW: // 捕獲當前窗口
HWND ForegWin;
ForegWin = GetForegroundWindow(); // 取得當前窗口句柄
if (!ForegWin)
ForegWin = GetDesktopWindow();
GetWindowRect(ForegWin,&capRect); // 取得當前窗口的矩形區域範圍
break;
}
DesktopDC = GetDC(GetDesktopWindow()); // 創建內存設備描述表
width = capRect.right - capRect.left;
height = capRect.bottom - capRect.top;
Graphics::TBitmap *bBitmap; // 定義位圖變量
try {
bBitmap = new Graphics::TBitmap(); // 創建位圖
bBitmap->Width=width;
bBitmap->Height=height;
if ((level>0)&&(level<8))
bBitmap->PixelFormat = TPixelFormat(level); // 設定色深
// 拷貝屏幕的指定區域到位圖
BitBlt(bBitmap->Canvas->Handle,0,0,width,height,DesktopDC,
capRect.left,capRect.top,SRCCOPY);
if (cq>=0) {
TJPEGImage *jpeg;
try {
jpeg = new TJPEGImage; // 創建JPEG圖象
jpeg->CompressionQuality = cq; // 設定圖象品質
jpeg->Assign(bBitmap); // 將位圖轉化爲JPEG格式
jpeg->SaveToStream(imgstream); // 保存JPEG圖象信息
}
__finally {
delete jpeg; // 釋放資源
}
}
else {
bBitmap->SaveToStream(imgstream); // 保存位圖信息
}
}
__finally {
delete bBitmap; // 釋放資源
}
}
(2)ServerSocket1ClientRead()——捕獲併發送自己的屏幕圖象。具體代碼如下:
Void __fastcall TMainForm::ServerSocket1ClientRead(TObject *Sender,TCustomWinSocket *Socket)
{
AnsiString sRecvString = Socket->ReceiveText(); // 保存接收到的字符串
AnsiString sRemoteAddress = Socket->RemoteAddress; // 保存對方IP
int CL,CQ;
u_short port;
// 將接收到的字符串分解爲接收端口、色深、品質3個參數
int pos = sRecvString.Pos("/n");
// 接收端口
port = u_short(StrToIntDef(sRecvString.SubString(1,pos-1),0));
sRecvString = sRecvString.SubString(pos+1,sRecvString.Length()-pos);
pos = sRecvString.Pos("/n");
// 色深
CL = StrToIntDef(sRecvString.SubString(1,pos-1),0);
sRecvString = sRecvString.SubString(pos+1,sRecvString.Length()-pos);
pos = sRecvString.Pos("/n");
// 品質
CQ = StrToIntDef(sRecvString.SubString(1,pos-1),0);
if (port) {
TMemoryStream *ImageStream; // 定義數據流
try {
ImageStream = new TMemoryStream; // 分配內存
// 捕獲當前屏幕並保存到ImageStream中
CaptureImage(CM_ENTIRESCREEN, CL, CQ, ImageStream);
// 發送ImageStream到接收端口
if (!SendStream(sRemoteAddress, port, ImageStream))
MessageBox(0,"發送數據流失敗","EagleEye",MB_ICONERROR);
}
__finally {
delete ImageStream; // 釋放資源
}
}
}
(3)LoadImage ()——接受服務端屏幕數據並在本地計算機上重繪圖像。具體代碼如下:
bool __fastcall TRecvStreamThread::LoadImage(TImage *Image1)
{
CPingReply reply;
CPing PingHost;
bool bRtn = false; // 函數返回值初始爲FALSE
StatusBar->SimpleText = "正在連接主機...";
// 先PING主機,檢測網絡是否暢通
bool rtn = PingHost.Ping(RemoteAddress.c_str(),reply,64,4000,32);
if (rtn) {
u_short RecvPort=0;
TMemoryStream *Stream;
try{
for(int i=1;i<=(ConfigForm->times);i++) //進入循環,不斷獲得遠程桌面圖像
{ // 定義一個數據流並分配內存
Stream = new TMemoryStream;
TJPEGImage *jpeg; // 定義JPEG圖象
try{
jpeg = new TJPEGImage; // 分配內存
int RecvSocket = BindSocket(&RecvPort); //動態分配接收端口
if (RecvSocket)
{ // 將接收端口和色深、圖象品質合成一條命令,參數之間以'/n'分隔
AnsiString Msg = IntToStr(RecvPort) + "/n" +IntToStr(CL) + "/n" +IntToStr(CQ) + "/n";
Application->ProcessMessages(); // 處理系統消息
// 向遠程主機發送命令
if (SendMsg(RemoteAddress,LISTENPORT,Msg))
{ // 開始接收圖象到數據流中
if (RecvStream(RecvSocket,Stream))
{
StatusBar->SimpleText = "正在接收數據...";
// 從數據流中載入圖象
jpeg->LoadFromStream(Stream); // 顯示圖象
Image1->Picture->Bitmap->Assign(jpeg); //MessageBeep(MB_OK);
// 發出提示聲音,返回值爲TRUE,表示成功
bRtn = true; }
else
MessageBox(0,"接收數據流失敗","EagleEye",MB_ICONERROR); }
else
MessageBox(0,("無法與主機'"+ RemoteAddress +"'建立連接").c_str(),"EagleEye",MB_ICONERROR); }
else
MessageBox(0,"分配端口失敗,無法繼續接收數據","EagleEye",MB_ICONERROR);}
__finally {
delete jpeg; // 釋放資源
}
RecvPort+=1;
}
}
__finally {
delete Stream; // 釋放資源
}
}
else
MessageBox(0,("主機'"+RemoteAddress+"'沒有響應").c_str(),"EagleEye",MB_ICONERROR);
return bRtn;
}
大體流程就是這樣,中間因爲篇幅的問題省略掉了Ping模塊的編寫,不過已經提供了全部的程序代碼,大家可以自己看看。
後期程序測試
軟件運行界面如圖1所示:
圖1
軟件運行平臺:Windows 98/2000/XP/2003
軟件所實現功能:多窗口兩種方式(可以選擇截圖或實時監視的方式)同時監
視多臺遠程計算機;用戶可以設定監視時間、監視方式;用戶可以實現對本地計算機上顯示的遠程屏幕圖像的存儲,可以將圖像拷貝到剪貼板;用戶可以選擇程序的運行方式,可以是正常模式,也可以使程序隱藏於後臺運行。
軟件運行最低配置:由於條件限制沒能在更低配置的機器測試,所以最低運行配置數據不準確。軟件在P4賽揚1.6G,128M內存,133MHZ系統總線,操作系統爲Windows 98的計算機上運行良好。
軟件在100.0Mbps的局域網中對遠程主機的實時監視圖像傳輸速率可以234幀/min;在帶寬爲512kb/s的ADSL寬帶用戶的計算機上可以達到196幀/min;對帶寬爲56kb/s的撥號上網用戶無法實現實時監視功能,只能使用截圖方式對其監視。
結語
經過二十一天的設計和開發,遠程屏幕監視軟件EagleEye基本開發完畢。其功能基本符合需求,但由於設計時間太短,該軟件還有許多不盡如人意的地方,比如出錯處理不夠周全,圖像傳輸效率不是很高等一些方面問題,這些都有待進一步改善。
通過這軟件設計,鞏固了編程開發工具Borland C++ Builder 6.0的使用技能,它使用面向對象的開發技術,能夠輕鬆開發出功能強大的應用程序。使用與Borland C++ Builder 6.0相配套的第三方面控件能夠快速高效地製作美觀的用戶界面。使用其自帶的相關控件可以快速、隨意地製作出用戶需要的各種形式的程序模塊。
最後感謝《黒客防線》給我這次投稿的機會。在此,向貴社的廣大讀者朋友們、編輯們表示最衷心的感謝。行文倉促,如有不足錯誤之處,請批評指教:Email:[email protected]
Socket:如果軟件在你的計算機無法編譯,原因是你計算機上的C++Builder沒有安裝SUIpackL控件或控件的搜索路徑與我的計算機的路徑不同。安裝後控件後修改EagleEye工程代碼中的視圖資源文件的路徑爲你計算機上SUIpack的搜索路徑即可。