windows的vdagent启动分析

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;
    }
}

以下是对提供的参考资料的总结,按照要求结构化多个要点分条输出: 4G/5G无线网络优化与网规案例分析: NSA站点下终端掉4G问题:部分用户反馈NSA终端频繁掉4G,主要因终端主动发起SCGfail导致。分析显示,在信号较好的环境下,终端可能因节能、过热保护等原因主动释放连接。解决方案建议终端侧进行分析处理,尝试关闭节电开关等。 RSSI算法识别天馈遮挡:通过计算RSSI平均值及差值识别天馈遮挡,差值大于3dB则认定有遮挡。不同设备分组规则不同,如64T和32T。此方法可有效帮助现场人员识别因环境变化引起的网络问题。 5G 160M组网小区CA不生效:某5G站点开启100M+60M CA功能后,测试发现UE无法正常使用CA功能。问题原因在于CA频点集标识配置错误,修正后测试正常。 5G网络优化与策略: CCE映射方式优化:针对诺基亚站点覆盖农村区域,通过优化CCE资源映射方式(交织、非交织),提升RRC连接建立成功率和无线接通率。非交织方式相比交织方式有显著提升。 5G AAU两扇区组网:与三扇区组网相比,AAU两扇区组网在RSRP、SINR、下载速率和上传速率上表现不同,需根据具体场景选择适合的组网方式。 5G语音解决方案:包括沿用4G语音解决方案、EPS Fallback方案和VoNR方案。不同方案适用于不同的5G组网策略,如NSA和SA,并影响语音连续性和网络覆盖。 4G网络优化与资源利用: 4G室分设备利旧:面对4G网络投资压减与资源需求矛盾,提出利旧多维度调优策略,包括资源整合、统筹调配既有资源,以满足新增需求和提质增效。 宏站RRU设备1托N射灯:针对5G深度覆盖需求,研究使用宏站AAU结合1托N射灯方案,快速便捷地开通5G站点,提升深度覆盖能力。 基站与流程管理: 爱立信LTE基站邻区添加流程:未提供具体内容,但通常涉及邻区规划、参数配置、测试验证等步骤,以确保基站间顺畅切换和覆盖连续性。 网络规划与策略: 新高铁跨海大桥覆盖方案试点:虽未提供详细内容,但可推测涉及高铁跨海大桥区域的4G/5G网络覆盖规划,需考虑信号穿透、移动性管理、网络容量等因素。 总结: 提供的参考资料涵盖了4G/5G无线网络优化、网规案例分析、网络优化策略、资源利用、基站管理等多个方面。 通过具体案例分析,展示了无线网络优化中的常见问题及解决方案,如NSA终端掉4G、RSSI识别天馈遮挡、CA不生效等。 强调了5G网络优化与策略的重要性,包括CCE映射方式优化、5G语音解决方案、AAU扇区组网选择等。 提出了4G网络优化与资源利用的策略,如室分设备利旧、宏站RRU设备1托N射灯等。 基站与流程管理方面,提到了爱立信LTE基站邻区添加流程,但未给出具体细节。 新高铁跨海大桥覆盖方案试点展示了特殊场景下的网络规划需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值