• <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>
  • 用VC++開發WinPopup的增強版

    發表于:2007-07-04來源:作者:點擊數: 標簽:
    李佑民 在Internet Explorer 中,微軟帶有兩個很好的局域網通信工具:Chat 和NetMeeting,它們能使局域網中的用戶通過互發消息文本、電子白板,甚至語音和視頻圖像進行交流,但是它們都需要指定一個 服務器 才能正常工作。在通常由若干臺Windows 95/98 組成的
    李佑民

      在Internet Explorer 中,微軟帶有兩個很好的局域網通信工具:Chat 和NetMeeting,它們能使局域網中的用戶通過互發消息文本、電子白板,甚至語音和視頻圖像進行交流,但是它們都需要指定一個服務器才能正常工作。在通常由若干臺Windows 95/98 組成的對等網中,真正適用的消息傳送工具仍然是微軟通過網絡組件安裝的WinPopup.EXE,但微軟好像忘記了這個小程序,使它從最初發行到現在依然是老樣子,程序界面跟不上時代不說,每次只能發送38 個字節的消息文本,消息不能保存等不足使人感到十分遺憾。既然認為它不好,那我們就自己寫一個。就像VC ++中某個類的增強版都帶有Ex 后綴一樣,我們也決定將增強后的WinPopup.EXE 命名為WinPopup ?Ex.EXE,圖1 是完成后的WinPopupEx 的外觀。
    http://www.kjueaiud.com/uploads/2007/07/1_200707042009041.jpg (35427 字節)
      要在局域網中實現計算機之間的通信,可以采用的辦法很多,最容易想到的是針對某一個網絡協議進行編程,如TCP/IP、IPX/SPX 和NetBEUI,但是控制稍顯復雜,不易實現網絡廣播及只能針對某一個協議,顯得不夠靈活。微軟為我們提供了內部進程的通信(IPC)接口,如果按照ISO 的OSI 模型劃分,它工作在會話層,與它的下一層(傳輸層)采用何種協議無關。在IPC 接口中,MailLosts(郵槽)和NamedPipes(命名管道)都可以在服務器進程和客戶機進程之間進行通信,而且不論服務器進程和客戶機進程是駐留在同一臺機器,還是通過網絡聯系在一起,IPC 接口都能正確地將信息從一個進程傳送到另一個進程。而我們要做的就是在網絡中的每臺計算機上以它的“計算機名”建立一個郵槽或命名管道,其他計算機如果要發送信息給某臺計算機,它只需要像打開一個文件一樣(后面您將看到,的確是采用文件操作函數)打開以那臺計算機命名的郵槽或命名管道,然后像寫文件一樣將數據寫入,最后關閉它就完成了一次通信操作。

      郵槽和命名管道各有優缺點,命名管道是可靠的,在發送方不能確認接收方已接收到數據時,它會返回一個錯誤,但是它對網絡廣播操作就顯得力不從心;而郵槽則剛好相反,它可以將消息一次傳送給一組計算機,比如一個“工作組”或整個局域網,但它不能保證發送出去的數據一定就被接收方所接收??紤]到WinPopup 使用的是郵槽,為保證連續性,我們也決定采用MailLosts(郵槽)機制,至于通信的不可靠性,您在后面將看到,我們用一點手工代碼就可以彌補它。

      在這個增強版本中,我們要實現以下一些WinPopup 沒有的功能:

      * 消息可以自動保存, 根據您的選擇最多可以保存30 天;
      * 消息大小不再限制在38 字節, 每條消息最多可以達到400 字節;
      * 對單個計算機發出的消息, 可以要求接收方確認“已收到";
      * 可以廣播消息到局域網中的多個工作組;
      * 可將它縮小為系統狀態條圖標, 當有消息到達時, 它可以發出聲音或閃動圖標加以提醒;
      * 可定制的消息文本顯示字體和顏色;
      * 可選擇讓它開機自動運行;
      * 自動收集網絡信息, 您可以在“網絡鄰居”列表中選擇接收人, 而不是手工輸入它。
      本文不打算在這里將開發過程中的每一步細節都寫出來,而是只就一些重點問題進行說明,開發環境是Celeron 333、64M、Windows 98 和Visual C ++6.0。

      一、接收和發送消息

      WinPopupEx 的核心是消息的接收和發送,也就是對郵槽的處理。在程序開始運行時,它會調用函數:
    HANDLE CreateMailslot(
    LPCTSTR lpName, // 格式:
    "\\.\\MailSlot\\ 郵槽名”-本地郵槽
    DWORD nMaxMessageSize, 
    // 最大的消息文本長度,幫助文檔上說
      將該值設為0 則消息長度無限,實際上每次收發的消息長度不能超過424 字節

    DWORD lReadTimeout,   // 讀超時時間(毫秒)
    LPSECURITY_ATTRIBUTES
    lpSecurityAttributes // Windows 95/98
      的安全屬性應設置為NULL
           );
      建立兩個本地郵槽WinPopup 和WPAnswer,郵槽\\.\\MailSlot\\WinPopup 用于接收消息正文,而郵槽\\.\\MailSlot\\WPAnswer 則是為了彌補郵槽機制傳送消息的不可靠。當郵槽建立成功后,程序就在主線程之外新啟動一個工作線程,這個線程不停地檢查郵槽\\.\\MailSlot\\WinPopup,當郵槽不為空(有消息到達)時,它首先查看消息數據包中的發送方名字,如發送方名為B,則它向郵槽\\B\\MailSlot\\WPAnswer 發送一個極短的標志文本,以通知發送方自己已經收到它發來的消息,然后向主線程發送一條自定義消息,通知主線程有消息到達,主線程在該自定義消息處理函數中從郵槽\\.\\MailSlot\\WinPopup 里讀出消息正文并將它顯示給用戶。如果計算機A 要向計算機B 發送消息,它只需將消息正文按一定格式的數據包寫入郵槽\\B\\MailSlot\\WinPopup 中,然后在預定義的延遲時間后,檢查本地郵槽\\.\\MailSlot\\WPAnswer 是否有計算機B 返回的應答標志文本,就可知道接收方是否已收到消息。

      檢查郵槽中是否有消息到達使用函數:

    BOOL GetMailslotInfo(
    HANDLE hMailslot,     // 郵槽句柄
    LPDWORD lpMaxMessageSize,
    // 指向存放最大消息長度的變量的指針
    LPDWORD lpNextSize,   
    // 指向存放下一條消息長度的變量的指針
    LPDWORD lpMessageCount, 
    // 指向存放消息條數的變量的指針
    LPDWORD lpReadTimeout  
    // 讀超時時間(毫秒)
    );
      如果( *lpNextSize) != MAILSLOT_NO_MESSAGE,則說明有消息到達。

      從郵槽中讀取消息同從文件中讀取數據沒有區別:

    BOOL ReadFile(
    HANDLE hFile,    // 句柄(這里是郵槽)
    LPVOID lpBuffer,    // 接收數據的緩沖區指針
    DWORD nNumberOfBytesToRead, // 要讀取的字節數
    LPDWORD lpNumberOfBytesRead,
    // 指向存放已讀取字節數的變量的指針
    LPOVERLAPPED lpOverlapped  
    // 指向OVERLAPPD(重疊I/O) 結構的指針
           );
      寫入消息到郵槽遵循一般文件的建立、寫入和關閉三個步驟:

     建立:HANDLE CreateFile(
     LPCTSTR lpFileName,     
    // 文件名,通常是對方計算機的郵槽名,
    如:// "\\B\\MailSlot\\WinPopup"
     DWORD dwDesiredAclearcase/" target="_blank" >ccess,  
     // 存取模式,一般是:GENERIC_WRITE
     DWORD dwShareMode,    
     // 共享模式,一般是:FILE_SHARE_READ
    LPSECURITY_ATTRIBUTES lpSecurityAttributes,
     // Windows 95/98 的安全屬性應設置為NULL
     DWORD dwCreationDisposition, 
     // 如何建立,一般是:OPEN_EXISTING
     DWORD dwFlagsAndAttributes, 
    // 文件屬性,一般是:FILE_ATTRIBUTE_NORMAL
     HANDLE hTemplateFile  // 設置為NULL 即可
       );
     寫入:BOOL WriteFile(
     HANDLE hFile,     // 文件句柄
     LPCVOID lpBuffer,   // 要寫的數據緩沖區指針
     DWORD nNumberOfBytesToWrite, // 要寫入的字節數
     LPDWORD lpNumberOfBytesWritten,
     // 指向存放已寫入字節數的變量的指針
     LPOVERLAPPED lpOverlapped 
    // 指向OVERLAPPD(重疊I/O) 結構的指針
       );
     關閉:BOOL CloseHandle(
     HANDLE hObject     //   文件句柄
     );

      二、消息數據包格式

      消息正文的數據包格式為:
     {
     UINT    m_uMID;      // 唯一表示本消息的ID
     char    m_cNeedAnswer; // 是否需要應答
     char    m_cEntirNet;  // 是否廣播到“整個網絡"
     LPCTSTR    m_lpcsTo;
     // 接收人顯示姓名( 轉換“整個網絡" 為“*")
     LPCTSTR    m_lpcsMessage;    // 消息正文
     }
      應答消息包的格式為:
     {
     UINT    m_uMID;    // 表示要應答的消息的ID (UINT)
     LPCTSTR    m_lpcsTo;    // 應答接收人(LPCTSTR)
     }
      請注意上面的兩個數據包格式中都包含一個ID 值,原因比較有趣:就像我們前面說過的那樣,郵槽是工作在會話層,與下一層(傳輸層)采用何種協議無關。但是,下層的每種協議都是單獨與郵槽機制綁定在一起的,其結果就是當您通過郵槽發送數據時,對方計算機不只收到一條消息,而是若干條一樣的消息,數量是兩臺計算機安裝的通信協議數量的最小值,比如說計算機A 安裝有TCP/IP、IPX/SPX 和NetBEUI 三種協議,計算機B 安裝有TCP/IP 和NetBEUI 兩種協議,那么計算機A 向計算機B 通過郵槽發送消息,則計算機B 將會收到兩條一樣的消息。為了過濾掉多余的消息,我們給每條消息生成一個唯一的隨機數ID,接收消息時只保留其中一條,其余的簡單拋棄即可。

      三、界面

      我們一直認為系統托盤區是桌面上比較敏感的區域,只有那些對某個事件進行監視的應用才應該在系統托盤區放置圖標,否則只能使人反感。而WinPopupEx 正好符合這個條件,它將一直在后臺運行,當有消息到達時,我們不停地閃動圖標并通過系統音頻發出電話振鈴的聲音,以這種方式提醒用戶,直到程序被用戶手工切換到前臺,見圖2。既然在系統托盤區放置了圖標,那么系統任務條按鈕就不需要了,它被函數ShowWindow( SW_HIDE ) 隱藏了起來。
    http://www.kjueaiud.com/uploads/2007/07/1_200707042009042.jpg (12455 字節)
      程序的主窗口被分為上下兩部分,上面是一個ListCtrl,它的內容包括消息發送人、接收人、接收時間和消息正文的摘要;下面是一個RichEditCtrl,通過選擇上面列表中的項目,這里將會顯示該消息正文的詳細內容。這兩個子窗口的字體和顏色都是可以定制的。

      四、消息的保存

      原來的WinPopup 最不足的地方就是歷史消息不能保存下來,每次重新打開它都是一片空白。而我們通過網絡的交流一般都希望保存下來以后再看看。這個功能實現起來并不復雜,每次程序被關閉時,它都將所有的消息寫入處于同一目錄下的WinPopupEx.History 文件中,每次運行時也從這個文件中讀入,并將它填入程序對應的消息結構中即可。
    五、開機自動運行
      要讓一個程序開機自動運行并不是一個新技術,您只需往系統注冊表中新建一個鍵值就可以實現,即在“Software\\Microsoft\\windows\\CurrentVersion\\Run" 下新建一個鍵,鍵名為“WinPopupEx",值為您的WinPopupEx.EXE 所在的磁盤路徑。讓我們考慮另外一種情況:“如果關機時WinPopupEx 仍在運行,請在下次開機時自動運行它”。這就需要一點技巧,我們要注意兩條Windows 消息,一個是WM_QUERYENDSESSION,每當Windows 準備關閉時,它都會向所有運行的程序發送這條消息,通知系統準備關機,這時我們用一個BOOL 變量將這個信息保存起來,如:theApp.m_bShutDown = TRUE,并返回TRUE 同意關閉系統;另一個是WM_ENDSESSION,當Windows 從所有程序的WM_QUERYENDSESSION 處理結果那里都得到TRUE,它就將以TRUE 為參數再次廣播WM_ENDSESSION 消息,如果某個程序的WM_QUERYENDSESSION 處理返回FALSE,那么將以FALSE 為參數。在我們的WM_ENDSESSION 消息處理中,通過判斷那個參數就可以確定本次程序的退出是否是因為系統關機,這個信息被保留到WM_CLOSE 中處理,只有關機造成的退出才往系統注冊表中寫前面那個鍵值,這樣就達到了我們的目的?! ?/p>

    原文轉自:http://www.kjueaiud.com

    老湿亚洲永久精品ww47香蕉图片_日韩欧美中文字幕北美法律_国产AV永久无码天堂影院_久久婷婷综合色丁香五月

  • <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>