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);
}
完整流程说明
- 网页链接点击 → 浏览器触发
openmstsc://IP:Port
。 - 浏览器调用注册好的协议→ 执行
open_mstsc.exe
,传入参数。 - 程序解析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)