1、创建vdagent对象并run运行
#ifdef __GNUC__
int main(int argc,char **argv)
#else
int APIENTRY _tWinMain(HINSTANCE instance, HINSTANCE prev_instance, LPTSTR cmd_line, int cmd_show)
#endif
{
VDAgent* vdagent = VDAgent::get();
vdagent->run();
delete vdagent;
return 0;
}
2、VDAgent单列创建
VDAgent* VDAgent::get()
{
if (!_singleton) {
_singleton = new VDAgent();
}
return _singleton;
}
3、构造函数处理
VDAgent::VDAgent()
: _hwnd (NULL)
, _hwnd_next_viewer (NULL)
, _add_clipboard_listener (NULL)
, _remove_clipboard_listener (NULL)
, _clipboard_owner (owner_none)
, _clipboard_tick (0)
, _input_time (0)
, _control_event (NULL)
, _stop_event (NULL)
, _in_msg (NULL)
, _in_msg_pos (0)
, _pending_input (false)
, _running (false)
, _session_is_locked (false)
, _desktop_switch (false)
, _display_setting (VD_AGENT_REGISTRY_KEY)
, _vio_serial (INVALID_HANDLE_VALUE)
, _read_pos (0)
, _write_pos (0)
, _logon_desktop (false)
, _display_setting_initialized (false)
, _max_clipboard (-1)
, _log (NULL)
{
TCHAR log_path[MAX_PATH];
TCHAR temp_path[MAX_PATH];
if (GetTempPath(MAX_PATH, temp_path)) {
swprintf_s(log_path, MAX_PATH, VD_AGENT_LOG_PATH, temp_path);
_log = VDLog::get(log_path);
}
ZeroMemory(&_read_overlapped, sizeof(_read_overlapped));
ZeroMemory(&_write_overlapped, sizeof(_write_overlapped));
ZeroMemory(_read_buf, sizeof(_read_buf));
_singleton = this;
}
std::queue<int> _control_queue; //控制队列
std::queue<VDIChunk*> _message_queue; //消息队列
4、启动vdagent
bool VDAgent::run()
{
DWORD session_id;
HANDLE event_thread;
WNDCLASS wcls;
close_vio_serial();
//获取会话ID
if (!ProcessIdToSessionId(GetCurrentProcessId(), &session_id)) {
vd_printf("ProcessIdToSessionId failed %lu", GetLastError());
return false;
}
vd_printf("***Agent started in session %lu***", session_id);
//版本日志
log_version();
//设置优先级
if (!SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS)) {
vd_printf("SetPriorityClass failed %lu", GetLastError());
}
//设置关闭
if (!SetProcessShutdownParameters(0x100, 0)) {
vd_printf("SetProcessShutdownParameters failed %lu", GetLastError());
}
//加载User32库,获取剪切板
HMODULE _user_lib = GetModuleHandle(L"User32");
if (!_user_lib) {
vd_printf("GetModuleHandle failed %lu", GetLastError());
return false;
}
_add_clipboard_listener = (PCLIPBOARD_OP)GetProcAddress(_user_lib, "AddClipboardFormatListener"); //获取剪切板
_remove_clipboard_listener = (PCLIPBOARD_OP)GetProcAddress(_user_lib, "RemoveClipboardFormatListener"); //移除剪切板
// do not use FormatListener APIs if not available
if (!_add_clipboard_listener || !_remove_clipboard_listener) {
_add_clipboard_listener = nullptr;
_remove_clipboard_listener = nullptr;
}
//创建事件
if (!_control_event)
_control_event = CreateEvent(NULL, FALSE, FALSE, NULL);
if (!_control_event) {
vd_printf("CreateEvent() failed: %lu", GetLastError());
return false;
}
ResetEvent(_control_event);
if (!_stop_event)
_stop_event = OpenEvent(SYNCHRONIZE, FALSE, VD_AGENT_STOP_EVENT);
//注册wcls
memset(&wcls, 0, sizeof(wcls));
wcls.lpfnWndProc = &VDAgent::wnd_proc;
wcls.lpszClassName = VD_AGENT_WINCLASS_NAME;
if (!RegisterClass(&wcls)) {
vd_printf("RegisterClass() failed: %lu", GetLastError());
return false;
}
_desktop_layout.reset(new DesktopLayout());
if (_desktop_layout->get_display_count() == 0) {
vd_printf("No QXL devices!");
}
//初始化串口读取句柄
if (!init_vio_serial()) {
return false;
}
if (!ReadFileEx(_vio_serial, _read_buf, sizeof(VDIChunk), &_read_overlapped, read_completion) &&
GetLastError() != ERROR_IO_PENDING) {
vd_printf("vio_serial read error %lu", GetLastError());
return false;
}
_running = true;
event_thread = CreateThread(NULL, 0, event_thread_proc, this, 0, NULL);
if (!event_thread) {
vd_printf("CreateThread() failed: %lu", GetLastError());
return false;
}
//发送能力集
send_announce_capabilities(true);
vd_printf("Connected to server");
//剪切板格式
_cb_format_drop_effect = RegisterClipboardFormat(CFSTR_PREFERREDDROPEFFECT);
//开始事件循环
while (_running) {
input_desktop_message_loop();
if (_clipboard_owner == owner_guest) {
set_clipboard_owner(owner_none);
}
}
//退出并终止线程
if (!QueueUserAPC(event_thread_stop_proc, event_thread, 0)) {
TerminateThread(event_thread, 0);
}
//销毁资源
WaitForSingleObject(event_thread, INFINITE);
CloseHandle(event_thread);
vd_printf("Agent stopped");
return true;
}
5、初始化串口句柄
#define VIOSERIAL_PORT_PATH L"\\\\.\\Global\\com.redhat.spice.0"
bool VDAgent::init_vio_serial()
{
_vio_serial = CreateFile(VIOSERIAL_PORT_PATH, GENERIC_READ | GENERIC_WRITE , 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
if (_vio_serial == INVALID_HANDLE_VALUE) {
vd_printf("Failed opening %ls, error %lu", VIOSERIAL_PORT_PATH, GetLastError());
return false;
}
return true;
}
6、完成读取后回调函数处理串口数据
VOID VDAgent::read_completion(DWORD err, DWORD bytes, LPOVERLAPPED overlapped)
{
VDAgent* a = _singleton;
VDIChunk* chunk = (VDIChunk*)a->_read_buf;
DWORD count;
if (err != 0 && err != ERROR_OPERATION_ABORTED && err != ERROR_NO_SYSTEM_RESOURCES) {
vd_printf("vio_serial read completion error %lu", err);
a->_running = false;
return;
}
a->_read_pos += bytes;
if (a->_read_pos < sizeof(VDIChunk)) {
count = sizeof(VDIChunk) - a->_read_pos;
} else if (a->_read_pos == sizeof(VDIChunk)) {
count = chunk->hdr.size;
if (count > sizeof(a->_read_buf) - a->_read_pos) {
vd_printf("chunk is too large, size %u port %u", chunk->hdr.size, chunk->hdr.port);
a->_running = false;
return;
}
} else if (a->_read_pos == sizeof(VDIChunk) + chunk->hdr.size){
a->handle_chunk(chunk);
count = sizeof(VDIChunk);
a->_read_pos = 0;
} else {
ASSERT(a->_read_pos < sizeof(VDIChunk) + chunk->hdr.size);
count = sizeof(VDIChunk) + chunk->hdr.size - a->_read_pos;
}
//继续读取
if (!ReadFileEx(a->_vio_serial, a->_read_buf + a->_read_pos, count, overlapped, read_completion) && GetLastError() != ERROR_IO_PENDING) {
vd_printf("vio_serial read error %lu", GetLastError());
a->_running = false;
}
}
7、解析消息
void VDAgent::handle_chunk(const VDIChunk* chunk)
{
//FIXME: currently assumes that multi-part msg arrives only from client port
if (_in_msg_pos == 0 || chunk->hdr.port == VDP_SERVER_PORT) {
// ignore the chunk if too short
if (chunk->hdr.size < sizeof(VDAgentMessage)) {
return;
}
//是否是VD_AGENT_PROTOCOL
VDAgentMessage* msg = (VDAgentMessage*)chunk->data;
if (msg->protocol != VD_AGENT_PROTOCOL) {
vd_printf("Invalid protocol %u", msg->protocol);
_running = false;
return;
}
//大小是否正常
uint32_t msg_size = sizeof(VDAgentMessage) + msg->size;
if (chunk->hdr.size == msg_size) {
//我们收到一条完整的消息,处理它
dispatch_message(msg, chunk->hdr.port);
return;
}
//无效的VDAgent消息
if (chunk->hdr.size >= msg_size) {
vd_printf("Invalid VDAgentMessage message");
_running = false;
return;
}
// 获取开始将所有块压缩到一个缓冲区中
_in_msg = (VDAgentMessage*)new uint8_t[msg_size];
memcpy(_in_msg, chunk->data, chunk->hdr.size);
_in_msg_pos = chunk->hdr.size;
return;
}
// 前一个块是部分消息,因此将此块附加到前一个组块
if (chunk->hdr.size > sizeof(VDAgentMessage) + _in_msg->size - _in_msg_pos) {
vd_printf("Invalid VDAgentMessage message");
_running = false;
return;
}
memcpy((uint8_t*)_in_msg + _in_msg_pos, chunk->data, chunk->hdr.size);
_in_msg_pos += chunk->hdr.size;
// 更新每个剪贴板块上的剪贴板标记以设置超时
if (_in_msg->type == VD_AGENT_CLIPBOARD && _clipboard_tick) {
_clipboard_tick = GetTickCount();
}
if (_in_msg_pos == sizeof(VDAgentMessage) + _in_msg->size) {
if (_in_msg->type == VD_AGENT_CLIPBOARD && !_clipboard_tick) {
vd_printf("Clipboard received but dropped due to timeout");
} else {
dispatch_message(_in_msg, 0);
}
cleanup_in_msg();
}
}
8、处理消息
void VDAgent::dispatch_message(VDAgentMessage* msg, uint32_t port)
{
bool res = true;
//检测消息的有效性
switch (agent_check_message(msg, msg->data, _final_caps.data(), _final_caps.size())) {
case AGENT_CHECK_WRONG_PROTOCOL_VERSION:
vd_printf("Invalid protocol %u", msg->protocol);
_running = false;
return;
case AGENT_CHECK_UNKNOWN_MESSAGE:
vd_printf("Unsupported message type %u size %u, ignoring", msg->type, msg->size);
return;
case AGENT_CHECK_INVALID_SIZE:
vd_printf("Unexpected msg size %u for message type %u", msg->size, msg->type);
_running = false;
return;
case AGENT_CHECK_TRUNCATED:
case AGENT_CHECK_INVALID_DATA:
vd_printf("Received malformed message, size %u type %u", msg->size, msg->type);
_running = false;
return;
case AGENT_CHECK_NO_ERROR:
break;
}
//获取消息类型并做相应的处理
switch (msg->type) {
case VD_AGENT_MOUSE_STATE: //鼠标状态消息
res = handle_mouse_event((VDAgentMouseState*)msg->data);
break;
case VD_AGENT_MONITORS_CONFIG: //监视器配置
res = handle_mon_config((VDAgentMonitorsConfig*)msg->data, port);
break;
case VD_AGENT_CLIPBOARD: //剪切板消息
handle_clipboard((VDAgentClipboard*)msg->data, msg->size - sizeof(VDAgentClipboard));
break;
case VD_AGENT_CLIPBOARD_GRAB: //捕获剪切板
handle_clipboard_grab((VDAgentClipboardGrab*)msg->data, msg->size);
break;
case VD_AGENT_CLIPBOARD_REQUEST: //请求剪切板
res = handle_clipboard_request((VDAgentClipboardRequest*)msg->data);
if (!res) {
VDAgentClipboard clipboard = {VD_AGENT_CLIPBOARD_NONE};
res = write_message(VD_AGENT_CLIPBOARD, sizeof(clipboard), &clipboard);
}
break;
case VD_AGENT_CLIPBOARD_RELEASE: //释放剪切板
handle_clipboard_release();
break;
case VD_AGENT_DISPLAY_CONFIG: //显示配置
res = handle_display_config((VDAgentDisplayConfig*)msg->data, port);
break;
case VD_AGENT_ANNOUNCE_CAPABILITIES: //声明能力
res = handle_announce_capabilities((VDAgentAnnounceCapabilities*)msg->data, msg->size);
break;
case VD_AGENT_FILE_XFER_START: { //文件传输开始
AgentFileXferStatusMessageFull status;
size_t status_size = sizeof(status.common);
if (_session_is_locked) {
VDAgentFileXferStartMessage *s = (VDAgentFileXferStartMessage *)msg->data;
status.common.id = s->id;
status.common.result = VD_AGENT_FILE_XFER_STATUS_SESSION_LOCKED;
vd_printf("Fail to start file-xfer %u due: Locked session", status.common.id);
agent_prepare_filexfer_status(&status, &status_size,
_client_caps.data(), _client_caps.size());
write_message(VD_AGENT_FILE_XFER_STATUS, status_size, &status);
} else if (_file_xfer.dispatch(msg, status, status_size)) {
agent_prepare_filexfer_status(&status, &status_size,
_client_caps.data(), _client_caps.size());
write_message(VD_AGENT_FILE_XFER_STATUS, status_size, &status);
}
break;
}
case VD_AGENT_FILE_XFER_STATUS: //文件传输状态
case VD_AGENT_FILE_XFER_DATA: { //文件传输内容
AgentFileXferStatusMessageFull status;
size_t status_size = sizeof(status.common);
if (_file_xfer.dispatch(msg, status, status_size)) {
agent_prepare_filexfer_status(&status, &status_size,
_client_caps.data(), _client_caps.size());
write_message(VD_AGENT_FILE_XFER_STATUS, sizeof(status), &status);
}
break;
}
case VD_AGENT_CLIENT_DISCONNECTED: //断开VDAgent
vd_printf("Client disconnected, resetting agent state");
cleanup_in_msg();
set_control_event(CONTROL_RESET);
break;
case VD_AGENT_MAX_CLIPBOARD: //设置剪切板最大容量
res = handle_max_clipboard((VDAgentMaxClipboard*)msg->data, msg->size);
break;
default:
vd_printf("Unsupported message type %u size %u", msg->type, msg->size);
}
if (!res) {
vd_printf("handling message type %u failed: %lu", msg->type, GetLastError());
_running = false;
}
}