遠程共享顯示及聲音的實現
在局域網內共享調制解調器以及共享打印機都是我們非常熟悉的,而對于顯示器和聲卡的共享一般比較陌生。
當你在進行教學、演示或展示時,也許你希望主控電腦上的畫面同時也出現在其它電腦上;當你在跟蹤調試程序時,你也許夢想過兩臺顯示器能夠同步,以便觀看源代碼時不破壞運行程序畫面;還有,出于管理的目的,或許你需要遠程監控其它電腦的運行狀況,等等。上述所有情況都是遠程共享顯示的例子。
共享聲卡的需求也不少:首先這可以降低硬件投資,讓沒有裝或者沒法裝聲卡的電腦(如某些筆記本電腦)也有了“喉舌”;其次,即使每臺機器都有聲卡也不無用武之地:至少你的 mp3背景音樂與你的英語有聲軟件不再經常發生沖突了;還有,共享聲卡使得“遠程有聲通知”成為可能,其作用類似于立即型聲音郵件,在日常工作中有廣泛的應用。
共享顯示器或聲卡的硬件(一般在擴展槽內插一塊卡)在市場上不難找到。但擴充本文的示例程序即可以用軟件輕松實現這些功能。
一、共享顯示
我們通過一個完整的示例程序(VB 6.0)來說明。在例子中,被共享端(即服務器端)的任務是:自動捕獲本機的當前顯示畫面,并將之傳給共享端(即客戶端)。
1. 服務器端程序(frmServer.frm)
這里有三點需要重點說明:一是自動捕獲畫面問題。為了模擬“捕獲屏幕鍵”被按下的動作,程序里使用了 API調用 keybd_event。雖然VB的SendKeys語句也有類似功能,但它不如 keybd_event穩定和可靠。二是畫面粘貼和壓縮存儲問題。為了能把已經被捕獲到系統剪貼板中的圖像粘貼下來并存儲到文件,程序里使用了 ImgEdit控件。該控件強大的功能遠非PictureBox控件或 Image控件可比。ImgEdit 不僅支持多種壓縮圖像格式(如 JPG、TIFF等),而且它對畫面進行操縱和編輯的功能也非常強大(如圖像旋轉、縮放、嵌入等)。ImgEdit 還能對剪貼板進行Copy、Cut、Paste等操作。使用 Ctrl+T 或菜單(工程->部件)來添加 Windows標準的 ImgEdit控件(參見圖一)。三是文件傳輸問題。用 ImgEdit存儲的壓縮文件一般只有 40 KB左右,使用 Winsock控件可以一次傳輸出去。但由于接收方的 Winsock控件一般是4K至8K調用一次 DataArrival子程,故程序使用主動分塊進行傳輸,接收方確認后再發下一塊。

服務器端源程序如下:
'====================== frmServer.frm
| Option Explicit Const FileName = "C:sys1.tmp", BlockSize = 3072 ' 傳送包大小 Private Declare Sub keybd_event Lib "user32" _ (ByVal bVk As Byte, ByVal bScan As Byte, _ ByVal dwFlags As Long, ByVal dwExtraInfo As Long) |
| Private Sub Form_Load() tcpServer.LocalPort = 1001 ' 設置監聽端口號 tcpServer.Listen ' 開始監聽 End Sub |
| Private Sub tcpServer_ConnectionRequest(ByVal requestID As Long) If tcpServer.State <> sckClosed Then tcpServer.Close tcpServer.Accept requestID tcpServer.SendData "SH" ' 成功連接后,發送“握手”信息 End Sub |
| Private Sub tcpServer_DataArrival(ByVal bytesTotal As Long) Static FileID As Integer, Cur_Pos As Long, FileLen As Long Dim strData As String, j Dim Buf() As Byte ' 定義一個可變大小的數組,用于傳送二進制圖像包 tcpServer.GetData strData Select Case strData Case "Close" ' 接到“Disconnect”命令后,關閉當前連接,并繼續監聽 tcpServer.Close ImgEdit1.ClearDisplay tcpServer.LocalPort = 1001 tcpServer.Listen Case "Save Picture" Call keybd_event(vbKeySnapshot, 1, 0, 0) ' 模擬按鍵操作 j = DoEvents() If Dir$(FileName) <> "" Then Kill FileName If ImgEdit1.IsClipboardDataAvailable Then ' 當剪貼板上有數據時 ImgEdit1.ClearDisplay ImgEdit1.DisplayBlankImage Screen.Width / _ Screen.TwipsPerPixelX, Screen.Height / _ Screen.TwipsPerPixelY, , , 6 ImgEdit1.ClipboardPaste ' 從剪貼板粘貼圖像 ImgEdit1.BurnInAnnotations 0, 2 ImgEdit1.SaveAs FileName, 1, 6, 6, 256 ' 另存圖像。參數說明如下: ' “FileName”:文件名 ' 參數“1”:TIFF 型文件; ' 第一個“6”:RGB24類型; ' 第二個“6”:JPEG壓縮類型 ' 參數“256”:最大壓縮比 Clipboard.Clear tcpServer.SendData "PS" ' 發送“圖像文件就緒”信息 End If Case "Get Picture" If Dir$(FileName) <> "" Then FileID = FreeFile Open FileName For Binary As #FileID ' 打開文件并發送第一塊數據 FileLen = LOF(FileID) ReDim Buf(1 To BlockSize) As Byte Get #FileID, , Buf tcpServer.SendData Buf Cur_Pos = BlockSize End If Case "Next Block" If Cur_Pos = FileLen Then tcpServer.SendData "EF" ' 文件傳送完畢后,發送“完成”信息 Close FileID Exit Sub End If j = Cur_Pos + BlockSize If j > FileLen Then j = FileLen - Cur_Pos Else j = BlockSize End If ReDim Buf(1 To j) As Byte ' 動態確定數組大小 Get #FileID, , Buf tcpServer.SendData Buf ' 發送后續包 Cur_Pos = Cur_Pos + j End Select End Sub |
2. 客戶端程序(frmClient.frm)
在窗體上建六個控件:一個名為 tcpClient的 Winsock控件用于通訊;一個名為txtIP 的 TextBox控件用于填寫服務器的IP地址;一個名為ImgEdit1的 ImgEdit控件用于顯示服務器傳來的圖像;三個CommandButton控件( cmdConnect、cmdGet_Pic和cmdDisconnect) 分別用于執行連接、取回圖像和斷開連接(見圖二)。
客戶端源代碼如下:
'====================== frmClient.frm
| Option Explicit Const FileName = "C:sys1.tmp" Private Sub cmdConnect_Click() If tcpClient.State <> sckClosed Then tcpClient.Close tcpClient.RemoteHost = txtIP.Text tcpClient.RemotePort = 1001 tcpClient.Connect ' 進行連接 End Sub |
| Private Sub cmdDisconnect_Click() tcpClient.SendData "Close" ' 斷開連接 cmdConnect.Enabled = True cmdGet_Pic.Enabled = False cmdDisconnect.Enabled = False End Sub |
| Private Sub cmdGet_Pic_Click() tcpClient.SendData "Save Picture" ' 請求圖像返回 frmClient.MousePointer = 11 End Sub |
| Private Sub Form_Resize() ' 使 ImgEdit1 的大小隨窗體的變化而變化 ImgEdit1.Height = frmClient.Height - 825 ImgEdit1.Width = frmClient.Width - 225 End Sub |
| Private Sub tcpClient_DataArrival(ByVal bytesTotal As Long) Static FileID As Integer, FileLen As Long Dim Buf() As Byte Dim j As Integer ReDim Buf(bytesTotal) As Byte ' 根據到達數據的字節數確定接收數組的大小 tcpClient.GetData Buf ' 收到連接完成的“握手”信息 If bytesTotal = 2 And Chr(Buf(0)) = "S" And Chr(Buf(1)) = "H" Then cmdConnect.Enabled = False cmdGet_Pic.Enabled = True cmdDisconnect.Enabled = True Exit Sub End If ' 收到圖像就緒的信息 If bytesTotal = 2 And Chr(Buf(0)) = "P" And Chr(Buf(1)) = "S" Then If Dir$(FileName) <> "" Then Kill FileName FileID = FreeFile Open FileName For Binary As #FileID ' 打開文件,準備存儲圖像 FileLen = 0 tcpClient.SendData "Get Picture" Exit Sub End If ' 收到圖像發送完畢的信息 If bytesTotal = 2 And Chr(Buf(0)) = "E" And Chr(Buf(1)) = "F" Then Close #FileID ' 關閉文件 j = DoEvents() ImgEdit1.Image = FileName ImgEdit1.Display ' 顯示收到的圖像 ImgEdit1.BurnInAnnotations 0, 2 frmClient.MousePointer = 0 Exit Sub End If ' 收到一塊二進制圖像信息 Put #FileID, , Buf ' 將當前數據塊存盤 tcpClient.SendData "Next Block" ' 申請下一塊 FileLen = FileLen + bytesTotal frmClient.Caption = "TCP Client " + Trim(Str(FileLen)) + _ " Bytes Received." ' 顯示當前收到的字節數 End Sub |
客戶端成功共享服務器端顯示畫面后的外觀如圖三所示。
二、共享聲音
共享聲音與共享顯示的思想是一致的,只是這時是客戶端向服務器端發送聲音文件,以便共享服務器的聲卡。服務器端應使用微軟的多媒體控件(MMControl) 進行聲音播放(使用 Ctrl+T 或菜單“工程->部件”來添加)。用該控件播放聲音不僅是簡單的,而且功能強大。
由于關鍵模塊與共享顯示一致,故此處略去源代碼。