windows 平台如何点击网页上的url ,会打开远程桌面连接服务器

你可以使用自定义协议方案(Protocol Scheme)实现网页上点击URL后自动启动远程桌面连接(mstsc),参考你提供的C++代码思路,如下实现:

第一步:注册自定义协议

使用类似openmstsc://协议。

注册示例 (reg 文件形式)
复制代码
Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\openmstsc]
@="URL:openmstsc Protocol"
"URL Protocol"=""

[HKEY_CLASSES_ROOT\openmstsc\shell\open\command]
@="\"C:\\your-path\\open_mstsc.exe\" \"%1\""

或通过你的C++代码自动完成注册(代码里已经包含该功能)。


第二步:网页中调用协议URL

网页端代码(简单HTML):

复制代码
<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <title>远程桌面连接示例</title>
</head>
<body>
    <a href="openmstsc://192.168.1.100:3389">连接远程桌面 192.168.1.100</a>
</body>
</html>

注意:

  • 点击此链接时,浏览器会提示用户是否允许调用该协议(首次使用时会询问),确认即可。

第三步:你的C++程序实现要点(已提供,以下强调注意点)

你的C++程序中关键实现点(你代码中已经包含了):

  • 解析传入的URL,提取IP:端口
  • 使用ShellExecuteExW调用mstsc.exe并传入/v:IP:Port参数。

示例(摘录):

复制代码
void OpenWithMstsc(const std::wstring& serverIP) {
    if (serverIP.empty()) return;

    std::wstring mstscArgs = L"/v:" + serverIP;

    SHELLEXECUTEINFOW sei = { sizeof(sei) };
    sei.lpFile = L"mstsc.exe";
    sei.lpParameters = mstscArgs.c_str();
    sei.nShow = SW_SHOWNORMAL;
    sei.fMask = SEE_MASK_NOASYNC;

    ShellExecuteExW(&sei);
}

完整流程说明

  1. 网页链接点击 → 浏览器触发openmstsc://IP:Port
  2. 浏览器调用注册好的协议→ 执行open_mstsc.exe,传入参数。
  3. 程序解析URL → 调用mstsc.exe→ 远程桌面客户端打开。

这样即可实现点击网页上的链接自动打开远程桌面连接的功能。

复制代码
// open_mstsc.cpp
// C++ 重写版本:实现与 C# 相同功能,包括注册自定义协议、解析 URL,并通过 WPS 打开文件,且不弹出控制台窗口。
//语言功能 "结构化绑定" 需要编译器标志 "/std:c++17"
//链接器-》系统-》子系统-》窗口模式
#include <windows.h>
#include <shlwapi.h>
#include <iostream>
#include <string>
#include <urlmon.h>
#include <shellapi.h>
#include <algorithm> // for std::transform
#pragma comment(lib, "Shlwapi.lib")
#pragma comment(lib, "urlmon.lib")
#include <cctype>
const std::wstring PROTOCOL_NAME = L"openMstsc";
#include <windows.h>
#include <shellapi.h>

bool IsRunAsAdmin() {
    BOOL isAdmin = FALSE;
    PSID adminGroup;
    SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;

    if (AllocateAndInitializeSid(&NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS,
        0, 0, 0, 0, 0, 0, &adminGroup)) {
        CheckTokenMembership(NULL, adminGroup, &isAdmin);
        FreeSid(adminGroup);
    }
    return isAdmin;
}

void RelaunchAsAdmin() {
    wchar_t exePath[MAX_PATH];
    GetModuleFileNameW(NULL, exePath, MAX_PATH);

    SHELLEXECUTEINFOW sei = { sizeof(sei) };
    sei.lpVerb = L"runas"; // 以管理员权限运行
    sei.lpFile = exePath;
    sei.nShow = SW_SHOWNORMAL;
    sei.fMask = SEE_MASK_NOASYNC;

    if (ShellExecuteExW(&sei)) {
        ExitProcess(0); // 关闭当前进程
    }
}
// 替代 HttpUtility.UrlDecode 的简单实现
// 使用 UrlUnescapeW API 进行解码
static std::wstring UrlDecode(const std::wstring& encoded) {
    if (encoded.empty()) return L"";

    // 预留缓冲区存放解码后的结果
    const size_t BUFFER_SIZE = 4096;
    wchar_t buffer[BUFFER_SIZE];
    wcsncpy_s(buffer, encoded.c_str(), BUFFER_SIZE);

    DWORD dwSize = (DWORD)BUFFER_SIZE;
    HRESULT hr = UrlUnescapeW(buffer, NULL, &dwSize, URL_UNESCAPE_INPLACE);
    if (SUCCEEDED(hr)) {
        return std::wstring(buffer);
    }
    else {
        // 如果解码失败,可以根据需求返回空字符串或原始值
        return L"";
    }
}

 



static std::wstring ProcessServerIP(const std::wstring& inputUrl, const std::wstring& protocolName)
{
    // 1) 构造 "{protocol}://" 的小写形式
    std::wstring lowerProtocol = protocolName;
    std::transform(lowerProtocol.begin(), lowerProtocol.end(), lowerProtocol.begin(), ::towlower);
    std::wstring protocolPrefix = lowerProtocol + L"://";

    // 2) 转换 inputUrl 为小写进行查找
    std::wstring lowerInput = inputUrl;
    std::transform(lowerInput.begin(), lowerInput.end(), lowerInput.begin(), ::towlower);

    // 3) 移除 "protocol://"
    std::wstring url;
    size_t pos = lowerInput.find(protocolPrefix);
    if (pos != std::wstring::npos)
    {
        url = inputUrl.substr(pos + protocolPrefix.size());
    }
    else
    {
        url = inputUrl;  // 原始 URL
    }

    // 4) URL 解码(假设 UrlDecode 是可用函数)
    url = UrlDecode(url);

    // 5) 去除路径部分,仅保留 "host:port"
    size_t pathPos = url.find(L'/');
    if (pathPos != std::wstring::npos)
    {
        url = url.substr(0, pathPos);
    }

    return url;
}


//-----------------------------------------------------------
// 下面是原有的函数声明与实现
//-----------------------------------------------------------

void RegisterUrlScheme(const std::wstring& protocol, const std::wstring& exePath);
std::wstring GetRegisteredPath(const std::wstring& protocol);

void OpenWithMstsc(const std::wstring& fileUrl);
std::pair<std::wstring, std::wstring> GetWpsLauncherPath();
void EnsureUrlScheme(const std::wstring& protocol);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
//int main(){

    if (!IsRunAsAdmin()) {
        RelaunchAsAdmin(); // 如果不是管理员权限,则重新以管理员权限运行
        return 0;
    }
    EnsureUrlScheme(PROTOCOL_NAME);

    int argc;
    LPWSTR* argv = CommandLineToArgvW(GetCommandLineW(), &argc);
    if (argv == NULL) return 0;

    if (argc < 2) {
        //MessageBoxW(NULL, L"⚠️ 未检测到 URL 参数。", L"提示", MB_OK | MB_ICONWARNING);
        return 0;
    }
     

    // 调用新版 ProcessUrl
    std::wstring url = ProcessServerIP(argv[1], PROTOCOL_NAME);



    if (PathIsURLW(url.c_str())) {
        OpenWithMstsc(url);
    }
    else {
 
    }

    LocalFree(argv);
    return 0;
}

void EnsureUrlScheme(const std::wstring& protocol) {
    wchar_t exePath[MAX_PATH];
    GetModuleFileNameW(NULL, exePath, MAX_PATH);
    std::wstring registeredPath = GetRegisteredPath(protocol);

    if (registeredPath.empty() || !_wcsicmp(registeredPath.c_str(), exePath) == 0) {
        RegisterUrlScheme(protocol, exePath);
    }
}

std::wstring GetRegisteredPath(const std::wstring& protocol) {
    HKEY hKey;
    std::wstring regPath = L"";
    std::wstring keyPath = protocol + L"\\shell\\open\\command";
    if (RegOpenKeyExW(HKEY_CLASSES_ROOT, keyPath.c_str(), 0, KEY_READ, &hKey) == ERROR_SUCCESS) {
        wchar_t value[MAX_PATH];
        DWORD value_length = sizeof(value);
        if (RegQueryValueExW(hKey, NULL, NULL, NULL, (LPBYTE)value, &value_length) == ERROR_SUCCESS) {
            std::wstring commandLine(value);
            size_t firstQuoteEnd = commandLine.find(L'"', 1);
            if (firstQuoteEnd != std::wstring::npos) {
                regPath = commandLine.substr(1, firstQuoteEnd - 1);
            }
        }
        RegCloseKey(hKey);
    }
    return regPath;
}

void RegisterUrlScheme(const std::wstring& protocol, const std::wstring& exePath) {
    HKEY hKey;
    std::wstring keyPath = protocol;
    if (RegCreateKeyExW(HKEY_CLASSES_ROOT, keyPath.c_str(), 0, NULL, 0, KEY_WRITE, NULL, &hKey, NULL) == ERROR_SUCCESS) {
        RegSetValueExW(hKey, NULL, 0, REG_SZ, (const BYTE*)(L"URL:" + protocol + L" Protocol").c_str(),
            (DWORD)((protocol.size() + 10) * sizeof(wchar_t)));
        RegSetValueExW(hKey, L"URL Protocol", 0, REG_SZ, (const BYTE*)L"", sizeof(wchar_t));
        HKEY hCommandKey;
        if (RegCreateKeyExW(hKey, L"shell\\open\\command", 0, NULL, 0, KEY_WRITE, NULL, &hCommandKey, NULL) == ERROR_SUCCESS) {
            std::wstring command = L"\"" + exePath + L"\" \"%1\"";
            RegSetValueExW(hCommandKey, NULL, 0, REG_SZ, (const BYTE*)command.c_str(), (DWORD)(command.size() * sizeof(wchar_t)));
            RegCloseKey(hCommandKey);
        }
        RegCloseKey(hKey);
        MessageBoxW(NULL, L"远程组件注册成功。", L"成功", MB_OK | MB_ICONINFORMATION);
    }
}
 
void OpenWithMstsc(const std::wstring& serverIP) {
    if (serverIP.empty()) {
        // MessageBoxW(NULL, L"❌ 服务器 IP 不能为空。", L"错误", MB_OK | MB_ICONERROR);
        return;
    }

    // 远程桌面连接的完整命令行参数
    std::wstring mstscArgs = L"/v:" + serverIP;

    SHELLEXECUTEINFOW sei = { sizeof(sei) };
    sei.lpFile = L"mstsc.exe";  // 远程桌面客户端
    sei.lpParameters = mstscArgs.c_str();
    sei.nShow = SW_SHOWNORMAL;
    sei.fMask = SEE_MASK_NOASYNC;

    if (!ShellExecuteExW(&sei)) {
        // MessageBoxW(NULL, L"❌ 无法启动远程桌面连接。", L"错误", MB_OK | MB_ICONERROR);
    }
}

注意上面代码要以管理员权限运行才能正确写入注册表,否则失败

下面将继续实现一下unbuntu上如何实现类似方法

复制代码
sudo apt install freerdp2-x11  
xfreerdp /v:10.10.10.11:33389 /u:username /p:passwrod /cert-ignore /dynamic-resolution or( /w:1440 /h:900)
相关推荐
qq_3129201122 分钟前
Nginx限流与防爬虫与安全配置方案
运维·爬虫·nginx·安全
GanGuaGua28 分钟前
Linux系统:线程的互斥和安全
linux·运维·服务器·c语言·c++·安全
lsnm37 分钟前
【LINUX网络】IP——网络层
linux·服务器·网络·c++·网络协议·tcp/ip
全糖去冰吃不了苦38 分钟前
ELK 集群部署实战
运维·jenkins
不掰手腕1 小时前
在UnionTech OS Server 20 (统信UOS服务器版) 上离线安装PostgreSQL (pgsql) 数据库
linux·数据库·postgresql
Lynnxiaowen1 小时前
今天继续昨天的正则表达式进行学习
linux·运维·学习·正则表达式·云计算·bash
努力学习的小廉1 小时前
深入了解linux系统—— POSIX信号量
linux·运维·服务器
刘一说2 小时前
CentOS部署ELK Stack完整指南
linux·elk·centos
从零开始的ops生活2 小时前
【Day 50 】Linux-nginx反向代理与负载均衡
linux·nginx
IT成长日记2 小时前
【Linux基础】Linux系统配置IP详解:从入门到精通
linux·运维·tcp/ip·ip地址配置