Windows 2000 內建 Messenger 服務提供區域網路即時訊息傳遞。本文示範以 C++Builder 開發類似功能的客戶端,涵蓋多執行緒防死結、網路使用者列舉、Unicode 轉換、不規則視窗、自動啟動與系統托盤整合等關鍵技術。
| 技術 | 用途 | 關鍵點 |
|---|---|---|
| 多執行緒 | 收發訊息、網路列舉 | 主執行緒專司 UI,背景執行緒處理封包,Synchronize 更新介面 |
| NetServerEnum / NetUserEnum | 列舉線上電腦/使用者 | 需連結 Netapi32.lib,處理分頁與緩衝區 |
| Unicode 轉換 | AnsiString ↔ Unicode | WideString、MultiByteToWideChar、WideCharToMultiByte |
| 不規則視窗 | 自訂外觀 | SetWindowRgn + 區域建立 |
| 自動啟動 | 登錄 Run 鍵 | HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run |
| 系統托盤 | 常駐背景、右鍵選單 | Shell_NotifyIcon (NIM_ADD/MODIFY/DELETE) |
TListView:線上使用者清單(詳細檢視)TTreeView:網域/工作群組階層TMemo:訊息輸入區TCheckBox:匿名發送、要求回執TButton:發送鈕TStatusBar:狀態列TMainMenu、TPopupMenu、TrayIconvoid __fastcall TFormMain::ButtonSendClick(TObject* Sender) {
AnsiString Message = Memo1->Text.Trim();
if (Message.IsEmpty()) { ShowMessage("請輸入訊息內容"); return; }
AnsiString UserName, FromName;
if (CheckBoxAnony->Checked) FromName = EditAnony->Text;
else FromName = "";
TListItem* Item;
if (!ListView1->SelCount) {
// 從 TreeView 選取
if (!TreeView1->Selected) { ShowMessage("請選擇接收對象"); return; }
UserName = TreeView1->Selected->Text;
if (TreeView1->Selected->Level == 1) UserName += "*"; // 使用者節點
new NetMessageSend(false, UserName, FromName, Message, CheckBoxGetRet->Checked);
return;
}
// 從 ListView 多選
Item = ListView1->Selected;
UserName = Item->Caption;
if (TreeView1->Selected->Level == 0) UserName += "*"; // 電腦節點
while (UserName[1] == '\\') UserName = UserName.SubString(2, UserName.Length() - 1);
new NetMessageSend(false, UserName, FromName, Message, CheckBoxGetRet->Checked);
for (int i = 1; i < ListView1->SelCount; ++i) {
Item = ListView1->GetNextItem(Item, sdAll, TItemStates() << isSelected);
UserName = Item->Caption;
if (TreeView1->Selected->Level == 0) UserName += "*";
while (UserName[1] == '\\') UserName = UserName.SubString(2, UserName.Length() - 1);
new NetMessageSend(false, UserName, FromName, Message, CheckBoxGetRet->Checked);
}
}
class NetEnumThread : public TThread {
protected:
void __fastcall Execute() {
// 1. 列舉網域/工作群組
// NetServerEnum(NULL, 100, ... SV_TYPE_DOMAIN_ENUM ...)
// 2. 對每個網域列舉伺服器
// NetServerEnum(domain, 100, ... SV_TYPE_SERVER ...)
// 3. Synchronize 更新 TreeView/ListView
Synchronize(&UpdateUI);
}
};
// 建立圖示資料
NOTIFYICONDATA nid = {0};
nid.cbSize = sizeof(nid);
nid.hWnd = Handle;
nid.uID = 1;
nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
nid.uCallbackMessage = WM_USER + 100; // 自訂訊息
nid.hIcon = Application->Icon->Handle;
StrCopy(nid.szTip, "My Messenger");
Shell_NotifyIcon(NIM_ADD, &nid);
// 訊息處理
BEGIN_MESSAGE_MAP
MESSAGE_HANDLER(WM_USER + 100, TMessage, OnTrayIcon)
END_MESSAGE_MAP(TForm)
void __fastcall TFormMain::OnTrayIcon(TMessage& Msg) {
if (Msg.LParam == WM_RBUTTONUP) {
POINT pt; GetCursorPos(&pt);
PopupMenu1->Popup(pt.x, pt.y);
} else if (Msg.LParam == WM_LBUTTONDBLCLK) {
Show(); WindowState = wsNormal;
}
}
void RegisterAutoStart(bool enable) {
TRegistry* reg = new TRegistry();
reg->RootKey = HKEY_CURRENT_USER;
if (reg->OpenKey("\\Software\\Microsoft\\Windows\\CurrentVersion\\Run", true)) {
if (enable)
reg->WriteString("MyMessenger", Application->ExeName);
else
reg->DeleteValue("MyMessenger");
}
delete reg;
}
class NetMessageSend : public TThread {
AnsiString FUser, FFrom, FMsg;
bool FNeedReceipt;
public:
__fastcall NetMessageSend(bool CreateSuspended,
AnsiString User, AnsiString From,
AnsiString Msg, bool NeedReceipt)
: TThread(CreateSuspended), FUser(User), FFrom(From),
FMsg(Msg), FNeedReceipt(NeedReceipt) {
FreeOnTerminate = true;
}
protected:
void __fastcall Execute() {
// 1. 開啟 MAILSLOT 或 Named Pipe
// 2. 組封包 (Unicode 轉換)
// 3. WriteFile 發送
// 4. 若需回執,ReadFile 等待回應
// 5. Synchronize 更新 UI 狀態
}
};
WideString 或 std::wstring,避免 ANSI 頁面碼問題Synchronize 或 TThread::Queue (非阻塞)GetLastError()、記錄日誌、使用者友善提示此專案展示了 C++Builder 開發系統級網路應用的完整技術棧。雖然 Messenger 協定已過時,但多執行緒架構、網路列舉、系統整合(托盤、自動啟動、不規則視窗)等技術在現代 Windows 桌面開發中依然適用。