#include "pch.h"
#include "framework.h"
#include "Client.h"
#include <winsock2.h>
#include <string>
#include <windows.h>
#include <commctrl.h>
#include <ws2tcpip.h>
#include <ctime>
#include <sstream>
#include <iostream>
#pragma comment(lib, "ws2_32")
// 定义资源标识符(如果没有使用资源文件)
#ifndef STATUSCLASSNAME
#define STATUSCLASSNAME "msctls_statusbar32"
#endif
#ifndef SBARS_SIZEGRIP
#define SBARS_SIZEGRIP 0x0004
#endif
#define IDC_EDIT_SEND 1001
#define IDC_EDIT_RECEIVE 1002
#define IDC_BUTTON_SEND 1003
#define IDC_CONNECT 1004
#define MAX_LOADSTRING 100
// 全局变量
HINSTANCE hInst; // 当前实例
WCHAR szTitle[MAX_LOADSTRING]; // 标题栏文本
WCHAR szWindowClass[MAX_LOADSTRING]; // 主窗口类名
HWND hStatusBar; // 状态栏句柄
HWND hEditSend; // 消息输入框句柄
HWND hEditReceive; // 消息显示框柄
SOCKET clientSocket; // 客户端套接字
struct sockaddr_in serverAddr;
bool isConnected = false; // 连接状态
// 函数声明
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
int WINAPI WinMain( _In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine,_In_ int nCmdShow );
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
DWORD WINAPI ReceiveThread(LPVOID lpParam);
void UpdateStatusBar(const std::string& status);
void ConnectToServer(HWND hWnd);
void SendMessageToServer(const std::string& message);
std::string GetCurrentTimeString(const std::string& message);
// 全局互斥锁
CRITICAL_SECTION mutex;
// 获取当前时间并格式化为字符串,接受message参数
std::string GetCurrentTimeString(const std::string& message) {
char buffer[9];
time_t now = time(0);
tm ltm;
localtime_s(<m, &now);
strftime(buffer, sizeof(buffer), "%H:%M:%S", <m);
std::string timeStamp = std::string(buffer);
std::stringstream ss;
ss << "[" << timeStamp << "] " << message;
return ss.str();
}
DWORD WINAPI ReceiveThread(LPVOID lpParam) {
char buffer[1024];
int recvLen;
HWND hWnd = *(static_cast<HWND*>(lpParam));
while (isConnected) {
// 加强窗口句柄有效性检查
DWORD dwStyle = GetWindowLong(hEditReceive, GWL_STYLE);
DWORD dwExStyle = GetWindowLong(hEditReceive, GWL_EXSTYLE);
if (!IsWindow(hEditReceive) || !IsWindowVisible(hEditReceive) || !IsWindowEnabled(hEditReceive) || GetParent(hEditReceive) != hWnd || (dwStyle & ES_READONLY) != ES_READONLY || (dwExStyle & WS_EX_CLIENTEDGE) != WS_EX_CLIENTEDGE) {
MessageBox(NULL, L"hEditReceive is in an invalid state", L"Error", MB_OK | MB_ICONERROR);
// 输出更多窗口状态信息到调试控制台
std::cout << "hEditReceive state: style=" << dwStyle << ", exStyle=" << dwExStyle << std::endl;
return 0;
}
if (!IsWindow(hWnd) || !IsWindowVisible(hWnd) || !IsWindowEnabled(hWnd)) {
MessageBox(NULL, L"hWnd is invalid", L"Error", MB_OK | MB_ICONERROR);
return 0;
}
// 接收消息(加锁)
EnterCriticalSection(&mutex);
recvLen = recv(clientSocket, buffer, sizeof(buffer) - 1, 0);
LeaveCriticalSection(&mutex);
if (recvLen > 0) {
buffer[recvLen] = '\0';
std::string message(buffer);
std::string formattedMessage = GetCurrentTimeString(message);
std::wstring wideFormattedMessage(formattedMessage.begin(), formattedMessage.end());
// 获取编辑框当前的内容长度(加锁)
EnterCriticalSection(&mutex);
int len = GetWindowTextLengthW(hEditReceive);
if (len == -1) { // 处理获取长度失败的情况
int error = GetLastError();
std::wstring errorMsg = L"获取编辑框文本长度失败,错误码: " + std::to_wstring(error);
MessageBox(hWnd, errorMsg.c_str(), L"错误", MB_OK | MB_ICONERROR);
LeaveCriticalSection(&mutex);
continue;
}
LeaveCriticalSection(&mutex);
// 获取编辑框当前的内容(加锁)
EnterCriticalSection(&mutex);
std::wstring wideMessage(len + 1 + wideFormattedMessage.length(), L'\0');
if (GetWindowTextW(hEditReceive, &wideMessage[0], len + 1) == 0) { // 处理获取文本失败的情况
int error = GetLastError();
std::wstring errorMsg = L"获取编辑框文本失败,错误码: " + std::to_wstring(error);
MessageBox(hWnd, errorMsg.c_str(), L"错误", MB_OK | MB_ICONERROR);
LeaveCriticalSection(&mutex);
continue;
}
LeaveCriticalSection(&mutex);
wideMessage += wideFormattedMessage + L"\n";
// 设置编辑框的内容(加锁)
EnterCriticalSection(&mutex);
if (SetWindowTextW(hEditReceive, wideMessage.c_str()) == 0) { // 处理设置文本失败的情况
int error = GetLastError();
std::wstring errorMsg = L"设置编辑框文本失败,错误码: " + std::to_wstring(error);
MessageBox(hWnd, errorMsg.c_str(), L"错误", MB_OK | MB_ICONERROR);
}
LeaveCriticalSection(&mutex);
}
else if (recvLen == 0) {
// 服务器关闭连接
isConnected = false;
UpdateStatusBar("服务器已关闭连接");
MessageBox(hWnd, L"服务器已关闭连接", L"提示", MB_OK | MB_ICONINFORMATION);
// 关闭套接字(加锁)
EnterCriticalSection(&mutex);
closesocket(clientSocket);
LeaveCriticalSection(&mutex);
break;
}
else {
// 接收消息出错
int errorCode = WSAGetLastError();
std::wstring errorText = L"接收消息出错,错误码: " + std::to_wstring(errorCode);
MessageBox(hWnd, errorText.c_str(), L"错误", MB_OK | MB_ICONERROR);
if (errorCode == WSAECONNRESET) {
// 如果连接被重置,尝试重新连接服务器
isConnected = false;
// 关闭套接字(加锁)
EnterCriticalSection(&mutex);
closesocket(clientSocket);
LeaveCriticalSection(&mutex);
ConnectToServer(hWnd);
}
else {
isConnected = false;
// 关闭套接字(加锁)
EnterCriticalSection(&mutex);
closesocket(clientSocket);
LeaveCriticalSection(&mutex);
}
break;
}
}
return 0;
}
// 更新状态栏显示
void UpdateStatusBar(const std::string& status) {
int statusBarParts[] = { 0, 100 };
std::wstring wideStatus(status.begin(), status.end());
SendMessage(hStatusBar, SB_SETPARTS, sizeof(statusBarParts) / sizeof(int), (LPARAM)statusBarParts);
if (SendMessage(hStatusBar, SB_SETTEXT, 0, (LPARAM)wideStatus.c_str()) == 0) { // 处理设置状态栏文本失败的情况
int error = GetLastError();
std::wstring errorMsg = L"设置状态栏文本失败,错误码: " + std::to_wstring(error);
MessageBox(NULL, errorMsg.c_str(), L"错误", MB_OK | MB_ICONERROR);
}
}
// 连接到服务器
void ConnectToServer(HWND hWnd) {
WSADATA wsaData;
SOCKET serverSocket;
struct sockaddr_in serverAddr;
int result;
int retryCount = 3; // 设置重试次数
// 初始化Winsock
result = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (result != 0) {
MessageBox(NULL, L"WSAStartup failed.", L"Error", MB_OK);
return;
}
// 创建套接字(加锁)
EnterCriticalSection(&mutex);
serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (serverSocket == INVALID_SOCKET) {
MessageBox(NULL, L"Error at socket(): ", L"Socket Initialization Failed", MB_OK);
WSACleanup();
LeaveCriticalSection(&mutex);
return;
}
std::cout << "Socket created successfully. Socket descriptor: " << serverSocket << std::endl; // 输出套接字描述符
// 设置服务器地址
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(8888); // 端口号
inet_pton(AF_INET, "127.0.0.1", &serverAddr.sin_addr); // 服务器IP
// 显示连接等待提示框
MessageBox(NULL, L"正在连接服务器,请稍候...", L"连接中", MB_OK | MB_ICONINFORMATION);
// 连接到服务器(重试机制)
while (retryCount > 0) {
result = connect(serverSocket, (struct sockaddr*)&serverAddr, sizeof(serverAddr));
if (result == SOCKET_ERROR) {
int errorCode = WSAGetLastError();
if (errorCode == WSAECONNREFUSED && retryCount > 1) {
// 如果连接被拒绝,等待一段时间后重试
Sleep(1000);
retryCount--;
continue;
}
MessageBox(NULL, L"Unable to connect to server.", L"Connection Error", MB_OK);
closesocket(serverSocket);
WSACleanup();
LeaveCriticalSection(&mutex);
return;
}
break;
}
std::cout << "Connected to server successfully." << std::endl; // 输出连接成功信息
clientSocket = serverSocket; // 保存套接字
isConnected = true; // 更新连接状态
UpdateStatusBar("Connected to server");
// 启动接收线程(加锁)
std::cout << "Thread parameter address (hWnd): " << std::hex << (void*)hWnd << std::endl;
HANDLE hThread = CreateThread(NULL, 0, ReceiveThread, (LPVOID)hWnd, 0, NULL);
if (hThread == NULL) {
MessageBox(NULL, L"Unable to create receive thread.", L"Thread Creation Failed", MB_OK);
closesocket(clientSocket);
WSACleanup();
isConnected = false;
LeaveCriticalSection(&mutex);
return;
}
CloseHandle(hThread);
// 解锁
LeaveCriticalSection(&mutex);
}
// 向服务器发送消息
void SendMessageToServer(const std::string& message) {
if (isConnected) {
int sendResult = send(clientSocket, message.c_str(), (int)message.length(), 0);
if (sendResult == SOCKET_ERROR) {
int errorCode = WSAGetLastError();
std::wstring errorText = L"Send failed with error code: " + std::to_wstring(errorCode);
MessageBox(NULL, errorText.c_str(), L"Send Error", MB_OK | MB_ICONERROR);
isConnected = false; // 发送失败,更新连接状态
// 关闭套接字(加锁)
EnterCriticalSection(&mutex);
closesocket(clientSocket);
LeaveCriticalSection(&mutex);
}
else {
// 发送成功,可以在状态栏显示发送成功的信息
UpdateStatusBar("Message sent successfully");
}
}
else {
MessageBox(NULL, L"Not connected to the server.", L"Send Error", MB_OK | MB_ICONWARNING);
}
}
// 注册窗口类
ATOM MyRegisterClass(HINSTANCE hInstance) {
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_CLIENT));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_CLIENT);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassExW(&wcex);
}
// 保存实例句柄并创建主窗口
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) {
hInst = hInstance;
HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, 500, 300, nullptr, nullptr, hInstance, nullptr);
if (!hWnd) {
return FALSE;
}
hStatusBar = CreateWindow(STATUSCLASSNAME, nullptr, WS_CHILD | WS_VISIBLE | SBARS_SIZEGRIP,
0, 0, 0, 0, hWnd, (HMENU)IDC_CONNECT, hInst, nullptr);
hEditSend = CreateWindowEx(WS_EX_CLIENTEDGE, L"EDIT", nullptr, WS_CHILD | WS_VISIBLE | WS_BORDER | ES_MULTILINE | ES_AUTOVSCROLL,
10, 10, 200, 50, hWnd, (HMENU)IDC_EDIT_SEND, hInst, nullptr);
hEditReceive = CreateWindowEx(WS_EX_CLIENTEDGE, L"EDIT", nullptr, WS_CHILD | WS_VISIBLE | WS_BORDER | ES_MULTILINE | ES_READONLY | ES_AUTOVSCROLL,
10, 70, 200, 180, hWnd, (HMENU)IDC_EDIT_RECEIVE, hInst, nullptr);
CreateWindowW(L"BUTTON", L"发送", WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON,
10, 260, 80, 20, hWnd, (HMENU)IDC_BUTTON_SEND, hInst, nullptr);
CreateWindowW(L"BUTTON", L"连接", WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON,
100, 260, 80, 20, hWnd, (HMENU)IDC_CONNECT, hInst, nullptr);
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
// 应用程序的入口点,负责初始化应用程序、处理消息循环和程序退出。
// hInstance: 当前实例句柄。
// hPrevInstance: 前一个实例句柄(在32位Windows中通常为NULL)。
// szCmdLine: 命令行参数字符串。
// iCmdShow: 窗口的显示状态。
// 返回值: 程序退出时的状态码。
int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nCmdShow) {
// 初始化全局字符串
LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadStringW(hInstance, IDC_CLIENT, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
// 初始化互斥锁
InitializeCriticalSection(&mutex);
// 执行应用程序初始化
if (!InitInstance(hInstance, nCmdShow)) {
return FALSE;
}
HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_CLIENT));
MSG msg;
// 主消息循环
while (GetMessage(&msg, nullptr, 0, 0)) {
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
// 销毁互斥锁
DeleteCriticalSection(&mutex);
return (int)msg.wParam;
}
// About对话框回调函数
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) {
switch (message) {
case WM_INITDIALOG:
return (INT_PTR)TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) {
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}
// WndProc函数定义
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
switch (message) {
case WM_COMMAND: {
int wmId = LOWORD(wParam);
// 分析菜单选择
switch (wmId) {
case IDC_CONNECT:
if (isConnected) {
// 断开连接逻辑
// 关闭套接字(加锁)
EnterCriticalSection(&mutex);
closesocket(clientSocket);
LeaveCriticalSection(&mutex);
isConnected = false;
UpdateStatusBar("未连接");
}
else {
ConnectToServer(hWnd);
}
break;
case IDC_BUTTON_SEND: {
if (isConnected) {
int len = GetWindowTextLengthW(hEditSend);
if (len == 0) {
MessageBox(hWnd, L"请输入消息内容", L"提示", MB_OK | MB_ICONINFORMATION);
break;
}
std::wstring wideMessage(len + 1, L'\0');
GetWindowTextW(hEditSend, const_cast<LPWSTR>(wideMessage.data()), len + 1);
std::string message(wideMessage.begin(), wideMessage.end());
SendMessageToServer(message);
}
else {
MessageBox(hWnd, L"请先连接服务器", L"提示", MB_OK | MB_ICONINFORMATION);
}
}
break;
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// TODO: 在此处添加使用hdc的任何绘图代码...
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY: {
if (isConnected) {
// 关闭套接字(加锁)
EnterCriticalSection(&mutex);
closesocket(clientSocket);
LeaveCriticalSection(&mutex);
}
// 释放状态栏资源
DestroyWindow(hStatusBar);
// 释放消息输入框资源
DestroyWindow(hEditSend);
// 释放消息显示框资源
DestroyWindow(hEditReceive);
// 如果接收线程正在运行,等待其结束
if (isConnected) {
HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, *((DWORD*)&hWnd));
if (hThread != NULL) {
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
}
}
// 销毁互斥锁
DeleteCriticalSection(&mutex);
// 发送退出消息
PostQuitMessage(0);
break;
}
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
在DWORD WINAPI ReceiveThread(LPVOID lpParam)函数代码HWND hWnd = *(static_cast<HWND*>(lpParam));处出现0x00007FF715BE9E11 处(位于 Client.exe 中)引发的异常: 0xC0000005: 读取位置 0x0000000000090812 时发生访问冲突。请问,如何修改