Initial commit: hello-agent — headless RustDesk-protocol-compatible Windows agent
build-windows / build-hello-agent-x64 (push) Successful in 5m41s
build-windows / build-hello-agent-x64 (push) Successful in 5m41s
A single-binary, Flutter-free remote-support agent that speaks the stock
RustDesk wire protocol. Designed for one-line MDM deployment against a
self-hosted rustdesk-server: a supporter using the unmodified rustdesk.exe
client connects, the controlled-side user gets a native Win32 approval
prompt, click Yes / No.
CLI surface
hello-agent.exe --install # register + start service
hello-agent.exe --uninstall # stop, delete, clean up
hello-agent.exe --config <BLOB> # admin-UI deploy string
hello-agent.exe --install --config <BLOB> # MDM one-liner
--config accepts both forms emitted by the rustdesk-server admin UI: the
reversed-base64 deploy string and the host=,key=,api=,relay= filename
form. Decoded via the upstream custom_server module, persisted via
hbb_common::config::Config::set_option.
Architecture
--service runs as a Session 0 LocalSystem service. It polls
WTSGetActiveConsoleSessionId and (re)spawns hello-agent.exe --server
into the active console session via librustdesk::platform::run_as_user,
handling the Session 0 → user-session token impersonation.
--server is the worker. It boots three concurrent components:
1. cm_popup: an IPC listener on the rustdesk `_cm` named pipe
2. librustdesk::start_server(true, false): the upstream protocol
stack — rendezvous mediator, NAT punch, IPC server, screen
capture, login validation, hbbs_http heartbeat / sysinfo sync
3. (implicit) ApproveMode::Click is pinned in config, so every
incoming connection routes through cm_popup
The popup mechanism reuses an existing upstream contract without any
patches to the protocol code: when a peer connects with no password,
Connection::start in the upstream code calls try_start_cm_ipc, which
ipc::connect-s the `_cm` pipe before falling back to spawning a Flutter
CM child. Since cm_popup is up first, step 1 succeeds; we read the
Data::Login{authorized:false} frame, show MessageBoxTimeoutW (Yes/No,
60s, top-most, system-modal), and reply Data::Authorize or Data::Close.
Source tree
src/main.rs CLI dispatcher + run_server() composition
src/cli.rs hand-rolled argv parser + unit tests
src/service.rs windows-service install/uninstall/dispatcher
src/config_import.rs --config blob decoding + persistence
src/cm_popup.rs _cm IPC listener + Win32 approval dialog
Vendoring
The upstream RustDesk crate is vendored under vendor/rustdesk/ — full
workspace including libs/{hbb_common, scrap, enigo, clipboard,
virtual_display, remote_printer}. This makes the build self-contained
(no submodules, no sibling-repo checkout in CI) and gives us freedom to
fork in a different direction later. Excluded from the vendor: .git,
target/, flutter/, appimage/, flatpak/, fastlane/, docs/, examples/,
ci/, build.py, Dockerfile, upstream README/CLAUDE/AGENTS/GEMINI.
One local divergence vs. upstream: vendor/rustdesk/src/lib.rs flips
`mod custom_server` → `pub mod custom_server` so config_import.rs can
call get_custom_server_from_string without going through the
ui_interface shim. Documented in README.md → "Re-syncing the vendored
copy".
CI
.gitea/workflows/build-windows.yml builds on a self-hosted Windows
runner with Rust 1.75, LLVM 15.0.6 (libclang for bindgen via libvpx-sys),
and a vcpkg cache. The vendored vcpkg.json drives x64-windows-static
deps. The workflow stages the resulting hello-agent.exe into
SignOutput\, reports authenticode signing status (warns on unsigned),
and uploads as artifact. ~15 min full build, faster on incremental.
Out of scope for this commit: Linux/macOS builds, code signing, MSI
packaging, coexistence with stock rustdesk on the same box (currently
shares the RustDesk APP_NAME and config dir).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,191 @@
|
||||
#[cfg(windows)]
|
||||
pub mod win10;
|
||||
use hbb_common::ResultType;
|
||||
#[cfg(windows)]
|
||||
use hbb_common::{bail, lazy_static};
|
||||
#[cfg(windows)]
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[cfg(windows)]
|
||||
use std::sync::Mutex;
|
||||
|
||||
#[cfg(windows)]
|
||||
lazy_static::lazy_static! {
|
||||
// If device is uninstalled though "Device Manager" Window.
|
||||
// RustDesk is unable to handle device any more...
|
||||
static ref H_SW_DEVICE: Mutex<u64> = Mutex::new(0);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[cfg(windows)]
|
||||
pub fn get_driver_install_path() -> &'static str {
|
||||
win10::DRIVER_INSTALL_PATH
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn download_driver() -> ResultType<()> {
|
||||
#[cfg(windows)]
|
||||
let _download_url = win10::DRIVER_DOWNLOAD_URL;
|
||||
#[cfg(target_os = "linux")]
|
||||
let _download_url = "";
|
||||
|
||||
// process download and report progress
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn get_driver_install_abs_path() -> ResultType<PathBuf> {
|
||||
let install_path = win10::DRIVER_INSTALL_PATH;
|
||||
let exe_file = std::env::current_exe()?;
|
||||
let abs_path = match exe_file.parent() {
|
||||
Some(cur_dir) => cur_dir.join(install_path),
|
||||
None => bail!(
|
||||
"Invalid exe parent for {}",
|
||||
exe_file.to_string_lossy().as_ref()
|
||||
),
|
||||
};
|
||||
if !abs_path.exists() {
|
||||
bail!("{} not exists", install_path)
|
||||
}
|
||||
Ok(abs_path)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn install_update_driver(_reboot_required: &mut bool) -> ResultType<()> {
|
||||
#[cfg(windows)]
|
||||
unsafe {
|
||||
{
|
||||
// Device must be created before install driver.
|
||||
// https://github.com/fufesou/RustDeskIddDriver/issues/1
|
||||
if let Err(e) = create_device() {
|
||||
bail!("{}", e);
|
||||
}
|
||||
|
||||
let abs_path = get_driver_install_abs_path()?;
|
||||
let full_install_path: Vec<u16> = abs_path
|
||||
.to_string_lossy()
|
||||
.as_ref()
|
||||
.encode_utf16()
|
||||
.chain(Some(0).into_iter())
|
||||
.collect();
|
||||
|
||||
let mut reboot_required_tmp = win10::idd::FALSE;
|
||||
if win10::idd::InstallUpdate(full_install_path.as_ptr() as _, &mut reboot_required_tmp)
|
||||
== win10::idd::FALSE
|
||||
{
|
||||
bail!("{}", win10::get_last_msg()?);
|
||||
}
|
||||
*_reboot_required = reboot_required_tmp == win10::idd::TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn uninstall_driver(_reboot_required: &mut bool) -> ResultType<()> {
|
||||
#[cfg(windows)]
|
||||
unsafe {
|
||||
{
|
||||
let abs_path = get_driver_install_abs_path()?;
|
||||
let full_install_path: Vec<u16> = abs_path
|
||||
.to_string_lossy()
|
||||
.as_ref()
|
||||
.encode_utf16()
|
||||
.chain(Some(0).into_iter())
|
||||
.collect();
|
||||
|
||||
let mut reboot_required_tmp = win10::idd::FALSE;
|
||||
if win10::idd::Uninstall(full_install_path.as_ptr() as _, &mut reboot_required_tmp)
|
||||
== win10::idd::FALSE
|
||||
{
|
||||
bail!("{}", win10::get_last_msg()?);
|
||||
}
|
||||
*_reboot_required = reboot_required_tmp == win10::idd::TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn is_device_created() -> bool {
|
||||
#[cfg(windows)]
|
||||
return *H_SW_DEVICE.lock().unwrap() != 0;
|
||||
#[cfg(not(windows))]
|
||||
return false;
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn create_device() -> ResultType<()> {
|
||||
if is_device_created() {
|
||||
return Ok(());
|
||||
}
|
||||
#[cfg(windows)]
|
||||
unsafe {
|
||||
let mut lock_device = H_SW_DEVICE.lock().unwrap();
|
||||
let mut h_sw_device = *lock_device as win10::idd::HSWDEVICE;
|
||||
if win10::idd::DeviceCreate(&mut h_sw_device) == win10::idd::FALSE {
|
||||
bail!("{}", win10::get_last_msg()?);
|
||||
} else {
|
||||
*lock_device = h_sw_device as u64;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn close_device() {
|
||||
#[cfg(windows)]
|
||||
unsafe {
|
||||
win10::idd::DeviceClose(*H_SW_DEVICE.lock().unwrap() as win10::idd::HSWDEVICE);
|
||||
*H_SW_DEVICE.lock().unwrap() = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn plug_in_monitor(_monitor_index: u32, _edid: u32, _retries: u32) -> ResultType<()> {
|
||||
#[cfg(windows)]
|
||||
unsafe {
|
||||
if win10::idd::MonitorPlugIn(_monitor_index as _, _edid as _, _retries as _)
|
||||
== win10::idd::FALSE
|
||||
{
|
||||
bail!("{}", win10::get_last_msg()?);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn plug_out_monitor(_monitor_index: u32) -> ResultType<()> {
|
||||
#[cfg(windows)]
|
||||
unsafe {
|
||||
if win10::idd::MonitorPlugOut(_monitor_index) == win10::idd::FALSE {
|
||||
bail!("{}", win10::get_last_msg()?);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
type PMonitorMode = win10::idd::PMonitorMode;
|
||||
#[cfg(not(windows))]
|
||||
type PMonitorMode = *mut std::ffi::c_void;
|
||||
|
||||
#[no_mangle]
|
||||
pub fn update_monitor_modes(
|
||||
_monitor_index: u32,
|
||||
_mode_count: u32,
|
||||
_modes: PMonitorMode,
|
||||
) -> ResultType<()> {
|
||||
#[cfg(windows)]
|
||||
unsafe {
|
||||
if win10::idd::FALSE
|
||||
== win10::idd::MonitorModesUpdate(_monitor_index as _, _mode_count as _, _modes)
|
||||
{
|
||||
bail!("{}", win10::get_last_msg()?);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,161 @@
|
||||
#pragma once
|
||||
#include <Windows.h>
|
||||
#include <tchar.h>
|
||||
#include <swdevice.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Install or Update RustDeskIddDriver.
|
||||
*
|
||||
* @param fullInfPath [in] Full path of the driver inf file.
|
||||
* @param rebootRequired [out] Indicates whether a restart is required.
|
||||
*
|
||||
* @return TRUE/FALSE. If FALSE returned, error message can be retrieved by GetLastMsg()
|
||||
*
|
||||
* @see GetLastMsg#GetLastMsg
|
||||
*/
|
||||
BOOL InstallUpdate(LPCTSTR fullInfPath, PBOOL rebootRequired);
|
||||
|
||||
/**
|
||||
* @brief Uninstall RustDeskIddDriver.
|
||||
*
|
||||
* @param fullInfPath [in] Full path of the driver inf file.
|
||||
* @param rebootRequired [out] Indicates whether a restart is required.
|
||||
*
|
||||
* @return TRUE/FALSE. If FALSE returned, error message can be retrieved by GetLastMsg()
|
||||
*
|
||||
* @see GetLastMsg#GetLastMsg
|
||||
*/
|
||||
BOOL Uninstall(LPCTSTR fullInfPath, PBOOL rebootRequired);
|
||||
|
||||
/**
|
||||
* @brief Check if RustDeskIddDriver device is created before.
|
||||
* The driver device(adapter) should be single instance.
|
||||
*
|
||||
* @param created [out] Indicates whether the device is created before.
|
||||
*
|
||||
* @return TRUE/FALSE. If FALSE returned, error message can be retrieved by GetLastMsg()
|
||||
*
|
||||
* @see GetLastMsg#GetLastMsg
|
||||
*
|
||||
*/
|
||||
BOOL IsDeviceCreated(PBOOL created);
|
||||
|
||||
/**
|
||||
* @brief Create device.
|
||||
* Only one device should be created.
|
||||
* If device is installed earlier, this function returns FALSE.
|
||||
*
|
||||
* @param hSwDevice [out] Handler of software device, used by DeviceCreate(). Should be **NULL**.
|
||||
*
|
||||
* @return TRUE/FALSE. If FALSE returned, error message can be retrieved by GetLastMsg()
|
||||
*
|
||||
* @see GetLastMsg#GetLastMsg
|
||||
*
|
||||
*/
|
||||
BOOL DeviceCreate(PHSWDEVICE hSwDevice);
|
||||
|
||||
/**
|
||||
* @brief Create device and set the lifetime.
|
||||
* Only one device should be created.
|
||||
* If device is installed earlier, this function returns FALSE.
|
||||
*
|
||||
* @param lifetime [in] The lifetime to set after creating the device. NULL means do not set the lifetime.
|
||||
* https://learn.microsoft.com/en-us/windows/win32/api/swdevice/nf-swdevice-swdevicesetlifetime
|
||||
* @param hSwDevice [out] Handler of software device, used by DeviceCreate(). Should be **NULL**.
|
||||
*
|
||||
* @return TRUE/FALSE. If FALSE returned, error message can be retrieved by GetLastMsg()
|
||||
*
|
||||
* @see GetLastMsg#GetLastMsg
|
||||
*
|
||||
*/
|
||||
BOOL DeviceCreateWithLifetime(SW_DEVICE_LIFETIME * lifetime, PHSWDEVICE hSwDevice);
|
||||
|
||||
/**
|
||||
* @brief Close device.
|
||||
*
|
||||
* @param hSwDevice Handler of software device, used by SwDeviceClose().
|
||||
* If hSwDevice is INVALID_HANDLE_VALUE or NULL, try find and close the device.
|
||||
*
|
||||
*/
|
||||
VOID DeviceClose(HSWDEVICE hSwDevice);
|
||||
|
||||
/**
|
||||
* @brief Plug in monitor.
|
||||
*
|
||||
* @param index [in] Monitor index, should be 0, 1, 2.
|
||||
* @param edid [in] Monitor edid.
|
||||
* 0 Modified EDID from Dell S2719DGF
|
||||
* 1 Modified EDID from Lenovo Y27fA
|
||||
* @param retries [in] Retry times. Retry 1 time / sec. 25~30 seconds may be good choices.
|
||||
* -1 is invalid.
|
||||
* 0 means doing once and no retries.
|
||||
* 1 means doing once and retry one time...
|
||||
*
|
||||
* @return TRUE/FALSE. If FALSE returned, error message can be retrieved by GetLastMsg()
|
||||
*
|
||||
* @see GetLastMsg#GetLastMsg
|
||||
*
|
||||
* @remark Plug in monitor may fail if device is created in a very short time.
|
||||
* System need some time to prepare the device.
|
||||
*
|
||||
*/
|
||||
BOOL MonitorPlugIn(UINT index, UINT edid, INT retries);
|
||||
|
||||
/**
|
||||
* @brief Plug out monitor.
|
||||
*
|
||||
* @param index [in] Monitor index, should be 0, 1, 2.
|
||||
*
|
||||
* @return TRUE/FALSE. If FALSE returned, error message can be retrieved by GetLastMsg()
|
||||
*
|
||||
* @see GetLastMsg#GetLastMsg
|
||||
*
|
||||
*/
|
||||
BOOL MonitorPlugOut(UINT index);
|
||||
|
||||
typedef struct _MonitorMode {
|
||||
DWORD width;
|
||||
DWORD height;
|
||||
// Sync affects frequency.
|
||||
DWORD sync;
|
||||
} MonitorMode, *PMonitorMode;
|
||||
|
||||
/**
|
||||
* @brief Update monitor mode.
|
||||
*
|
||||
* @param index [in] Monitor index, should be 0, 1, 2.
|
||||
* @param modeCount [in] Monitor mode count.
|
||||
* @param MonitorMode [in] Monitor mode data.
|
||||
*
|
||||
* @return TRUE/FALSE. If FALSE returned, error message can be retrieved by GetLastMsg()
|
||||
*
|
||||
* @see GetLastMsg#GetLastMsg
|
||||
*
|
||||
*/
|
||||
BOOL MonitorModesUpdate(UINT index, UINT modeCount, PMonitorMode modes);
|
||||
|
||||
/**
|
||||
* @brief Get last error message.
|
||||
*
|
||||
* @return Message string. The string is at most 1024 bytes.
|
||||
*
|
||||
*/
|
||||
const char* GetLastMsg();
|
||||
|
||||
/**
|
||||
* @brief Set if print error message when debug.
|
||||
*
|
||||
* @param b [in] TRUE to enable printing message.
|
||||
*
|
||||
* @remark For now, no need to read environment variable to check if should print.
|
||||
*
|
||||
*/
|
||||
VOID SetPrintErrMsg(BOOL b);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
|
||||
#include <minwindef.h>
|
||||
#include <winioctl.h>
|
||||
#include <guiddef.h>
|
||||
|
||||
#define IOCTL_CHANGER_IDD_PLUG_IN CTL_CODE(IOCTL_CHANGER_BASE, \
|
||||
0x1001, \
|
||||
METHOD_BUFFERED, \
|
||||
FILE_READ_ACCESS | FILE_WRITE_ACCESS)
|
||||
#define IOCTL_CHANGER_IDD_PLUG_OUT CTL_CODE(IOCTL_CHANGER_BASE, \
|
||||
0x1002, \
|
||||
METHOD_BUFFERED, \
|
||||
FILE_READ_ACCESS | FILE_WRITE_ACCESS)
|
||||
#define IOCTL_CHANGER_IDD_UPDATE_MONITOR_MODE CTL_CODE(IOCTL_CHANGER_BASE, \
|
||||
0x1003, \
|
||||
METHOD_BUFFERED, \
|
||||
FILE_READ_ACCESS | FILE_WRITE_ACCESS)
|
||||
|
||||
|
||||
#define STATUS_ERROR_ADAPTER_NOT_INIT (3 << 30) + 11
|
||||
//#define STATUS_ERROR_IO_CTL_GET_INPUT (3 << 30) + 21
|
||||
//#define STATUS_ERROR_IO_CTL_GET_OUTPUT (3 << 30) + 22
|
||||
#define STATUS_ERROR_MONITOR_EXISTS (3 << 30) + 51
|
||||
#define STATUS_ERROR_MONITOR_NOT_EXISTS (3 << 30) + 52
|
||||
#define STATUS_ERROR_MONITOR_INVALID_PARAM (3 << 30) + 53
|
||||
#define STATUS_ERROR_MONITOR_OOM (3 << 30) + 54
|
||||
|
||||
#define MONITOR_EDID_MOD_DELL_S2719DGF 0
|
||||
#define MONITOR_EDID_MOD_LENOVO_Y27fA 1
|
||||
|
||||
typedef struct _CtlPlugIn {
|
||||
UINT ConnectorIndex;
|
||||
UINT MonitorEDID;
|
||||
GUID ContainerId;
|
||||
} CtlPlugIn, *PCtlPlugIn;
|
||||
|
||||
typedef struct _CtlPlugOut {
|
||||
UINT ConnectorIndex;
|
||||
} CtlPlugOut, *PCtlPlugOut;
|
||||
|
||||
typedef struct _CtlMonitorModes {
|
||||
UINT ConnectorIndex;
|
||||
UINT ModeCount;
|
||||
struct {
|
||||
DWORD Width;
|
||||
DWORD Height;
|
||||
DWORD Sync;
|
||||
} Modes[1];
|
||||
} CtlMonitorModes, *PCtlMonitorModes;
|
||||
|
||||
|
||||
#define SYMBOLIC_LINK_NAME L"\\Device\\RustDeskIddDriver"
|
||||
|
||||
@@ -0,0 +1,215 @@
|
||||
#![allow(dead_code)]
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(unused_variables)]
|
||||
#![allow(non_snake_case)]
|
||||
#![allow(deref_nullptr)]
|
||||
|
||||
pub type size_t = ::std::os::raw::c_ulonglong;
|
||||
pub type __vcrt_bool = bool;
|
||||
pub type wchar_t = ::std::os::raw::c_ushort;
|
||||
|
||||
pub type POINTER_64_INT = ::std::os::raw::c_ulonglong;
|
||||
pub type INT8 = ::std::os::raw::c_schar;
|
||||
pub type PINT8 = *mut ::std::os::raw::c_schar;
|
||||
pub type INT16 = ::std::os::raw::c_short;
|
||||
pub type PINT16 = *mut ::std::os::raw::c_short;
|
||||
pub type INT32 = ::std::os::raw::c_int;
|
||||
pub type PINT32 = *mut ::std::os::raw::c_int;
|
||||
pub type INT64 = ::std::os::raw::c_longlong;
|
||||
pub type PINT64 = *mut ::std::os::raw::c_longlong;
|
||||
pub type UINT8 = ::std::os::raw::c_uchar;
|
||||
pub type PUINT8 = *mut ::std::os::raw::c_uchar;
|
||||
pub type UINT16 = ::std::os::raw::c_ushort;
|
||||
pub type PUINT16 = *mut ::std::os::raw::c_ushort;
|
||||
pub type UINT32 = ::std::os::raw::c_uint;
|
||||
pub type PUINT32 = *mut ::std::os::raw::c_uint;
|
||||
pub type UINT64 = ::std::os::raw::c_ulonglong;
|
||||
pub type PUINT64 = *mut ::std::os::raw::c_ulonglong;
|
||||
pub type LONG32 = ::std::os::raw::c_int;
|
||||
pub type PLONG32 = *mut ::std::os::raw::c_int;
|
||||
pub type ULONG32 = ::std::os::raw::c_uint;
|
||||
pub type PULONG32 = *mut ::std::os::raw::c_uint;
|
||||
pub type DWORD32 = ::std::os::raw::c_uint;
|
||||
pub type PDWORD32 = *mut ::std::os::raw::c_uint;
|
||||
pub type INT_PTR = ::std::os::raw::c_longlong;
|
||||
pub type PINT_PTR = *mut ::std::os::raw::c_longlong;
|
||||
pub type UINT_PTR = ::std::os::raw::c_ulonglong;
|
||||
pub type PUINT_PTR = *mut ::std::os::raw::c_ulonglong;
|
||||
pub type LONG_PTR = ::std::os::raw::c_longlong;
|
||||
pub type PLONG_PTR = *mut ::std::os::raw::c_longlong;
|
||||
pub type ULONG_PTR = ::std::os::raw::c_ulonglong;
|
||||
pub type PULONG_PTR = *mut ::std::os::raw::c_ulonglong;
|
||||
pub type SHANDLE_PTR = ::std::os::raw::c_longlong;
|
||||
pub type HANDLE_PTR = ::std::os::raw::c_ulonglong;
|
||||
pub type UHALF_PTR = ::std::os::raw::c_uint;
|
||||
pub type PUHALF_PTR = *mut ::std::os::raw::c_uint;
|
||||
pub type HALF_PTR = ::std::os::raw::c_int;
|
||||
pub type PHALF_PTR = *mut ::std::os::raw::c_int;
|
||||
pub type SIZE_T = ULONG_PTR;
|
||||
pub type PSIZE_T = *mut ULONG_PTR;
|
||||
pub type SSIZE_T = LONG_PTR;
|
||||
pub type PSSIZE_T = *mut LONG_PTR;
|
||||
pub type DWORD_PTR = ULONG_PTR;
|
||||
pub type PDWORD_PTR = *mut ULONG_PTR;
|
||||
pub type LONG64 = ::std::os::raw::c_longlong;
|
||||
pub type PLONG64 = *mut ::std::os::raw::c_longlong;
|
||||
pub type ULONG64 = ::std::os::raw::c_ulonglong;
|
||||
pub type PULONG64 = *mut ::std::os::raw::c_ulonglong;
|
||||
pub type DWORD64 = ::std::os::raw::c_ulonglong;
|
||||
pub type PDWORD64 = *mut ::std::os::raw::c_ulonglong;
|
||||
pub type KAFFINITY = ULONG_PTR;
|
||||
pub type PKAFFINITY = *mut KAFFINITY;
|
||||
pub type PVOID = *mut ::std::os::raw::c_void;
|
||||
pub type CHAR = ::std::os::raw::c_char;
|
||||
pub type SHORT = ::std::os::raw::c_short;
|
||||
pub type LONG = ::std::os::raw::c_long;
|
||||
pub type WCHAR = wchar_t;
|
||||
pub type PWCHAR = *mut WCHAR;
|
||||
pub type LPWCH = *mut WCHAR;
|
||||
pub type PWCH = *mut WCHAR;
|
||||
pub type LPCWCH = *const WCHAR;
|
||||
pub type PCWCH = *const WCHAR;
|
||||
pub type NWPSTR = *mut WCHAR;
|
||||
pub type LPWSTR = *mut WCHAR;
|
||||
pub type PWSTR = *mut WCHAR;
|
||||
pub type PZPWSTR = *mut PWSTR;
|
||||
pub type PCZPWSTR = *const PWSTR;
|
||||
pub type LPUWSTR = *mut WCHAR;
|
||||
pub type PUWSTR = *mut WCHAR;
|
||||
pub type LPCWSTR = *const WCHAR;
|
||||
pub type PCWSTR = *const WCHAR;
|
||||
pub type PZPCWSTR = *mut PCWSTR;
|
||||
pub type PCZPCWSTR = *const PCWSTR;
|
||||
pub type LPCUWSTR = *const WCHAR;
|
||||
pub type PCUWSTR = *const WCHAR;
|
||||
pub type PZZWSTR = *mut WCHAR;
|
||||
pub type PCZZWSTR = *const WCHAR;
|
||||
pub type PUZZWSTR = *mut WCHAR;
|
||||
pub type PCUZZWSTR = *const WCHAR;
|
||||
pub type PNZWCH = *mut WCHAR;
|
||||
pub type PCNZWCH = *const WCHAR;
|
||||
pub type PUNZWCH = *mut WCHAR;
|
||||
pub type PCUNZWCH = *const WCHAR;
|
||||
pub type PCHAR = *mut CHAR;
|
||||
pub type LPCH = *mut CHAR;
|
||||
pub type PCH = *mut CHAR;
|
||||
pub type LPCCH = *const CHAR;
|
||||
pub type PCCH = *const CHAR;
|
||||
pub type NPSTR = *mut CHAR;
|
||||
pub type LPSTR = *mut CHAR;
|
||||
pub type PSTR = *mut CHAR;
|
||||
pub type PZPSTR = *mut PSTR;
|
||||
pub type PCZPSTR = *const PSTR;
|
||||
pub type LPCSTR = *const CHAR;
|
||||
pub type PCSTR = *const CHAR;
|
||||
pub type PZPCSTR = *mut PCSTR;
|
||||
pub type PCZPCSTR = *const PCSTR;
|
||||
pub type PZZSTR = *mut CHAR;
|
||||
pub type PCZZSTR = *const CHAR;
|
||||
pub type PNZCH = *mut CHAR;
|
||||
pub type PCNZCH = *const CHAR;
|
||||
pub type TCHAR = ::std::os::raw::c_char;
|
||||
pub type PTCHAR = *mut ::std::os::raw::c_char;
|
||||
pub type TBYTE = ::std::os::raw::c_uchar;
|
||||
pub type PTBYTE = *mut ::std::os::raw::c_uchar;
|
||||
pub type LPTCH = LPCH;
|
||||
pub type PTCH = LPCH;
|
||||
pub type LPCTCH = LPCCH;
|
||||
pub type PCTCH = LPCCH;
|
||||
pub type PTSTR = LPSTR;
|
||||
pub type LPTSTR = LPSTR;
|
||||
pub type PUTSTR = LPSTR;
|
||||
pub type LPUTSTR = LPSTR;
|
||||
pub type PCTSTR = LPCSTR;
|
||||
pub type LPCTSTR = LPCSTR;
|
||||
pub type PCUTSTR = LPCSTR;
|
||||
pub type LPCUTSTR = LPCSTR;
|
||||
pub type PZZTSTR = PZZSTR;
|
||||
pub type PUZZTSTR = PZZSTR;
|
||||
pub type PCZZTSTR = PCZZSTR;
|
||||
pub type PCUZZTSTR = PCZZSTR;
|
||||
pub type PZPTSTR = PZPSTR;
|
||||
pub type PNZTCH = PNZCH;
|
||||
pub type PUNZTCH = PNZCH;
|
||||
pub type PCNZTCH = PCNZCH;
|
||||
pub type PCUNZTCH = PCNZCH;
|
||||
pub type PSHORT = *mut SHORT;
|
||||
pub type PLONG = *mut LONG;
|
||||
pub type ULONG = ::std::os::raw::c_ulong;
|
||||
pub type PULONG = *mut ULONG;
|
||||
pub type USHORT = ::std::os::raw::c_ushort;
|
||||
pub type PUSHORT = *mut USHORT;
|
||||
pub type UCHAR = ::std::os::raw::c_uchar;
|
||||
pub type PUCHAR = *mut UCHAR;
|
||||
pub type PSZ = *mut ::std::os::raw::c_char;
|
||||
pub type DWORD = ::std::os::raw::c_ulong;
|
||||
pub type BOOL = ::std::os::raw::c_int;
|
||||
pub type BYTE = ::std::os::raw::c_uchar;
|
||||
pub type WORD = ::std::os::raw::c_ushort;
|
||||
pub type FLOAT = f32;
|
||||
pub type PFLOAT = *mut FLOAT;
|
||||
pub type PBOOL = *mut BOOL;
|
||||
pub type LPBOOL = *mut BOOL;
|
||||
pub type PBYTE = *mut BYTE;
|
||||
pub type LPBYTE = *mut BYTE;
|
||||
pub type PINT = *mut ::std::os::raw::c_int;
|
||||
pub type LPINT = *mut ::std::os::raw::c_int;
|
||||
pub type PWORD = *mut WORD;
|
||||
pub type LPWORD = *mut WORD;
|
||||
pub type LPLONG = *mut ::std::os::raw::c_long;
|
||||
pub type PDWORD = *mut DWORD;
|
||||
pub type LPDWORD = *mut DWORD;
|
||||
pub type LPVOID = *mut ::std::os::raw::c_void;
|
||||
pub type LPCVOID = *const ::std::os::raw::c_void;
|
||||
pub type INT = ::std::os::raw::c_int;
|
||||
pub type UINT = ::std::os::raw::c_uint;
|
||||
pub type PUINT = *mut ::std::os::raw::c_uint;
|
||||
pub type va_list = *mut ::std::os::raw::c_char;
|
||||
|
||||
pub const TRUE: ::std::os::raw::c_int = 1;
|
||||
pub const FALSE: ::std::os::raw::c_int = 0;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct _MonitorMode {
|
||||
pub width: DWORD,
|
||||
pub height: DWORD,
|
||||
pub sync: DWORD,
|
||||
}
|
||||
pub type MonitorMode = _MonitorMode;
|
||||
pub type PMonitorMode = *mut MonitorMode;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct HSWDEVICE__ {
|
||||
pub unused: ::std::os::raw::c_int,
|
||||
}
|
||||
pub type HSWDEVICE = *mut HSWDEVICE__;
|
||||
pub type PHSWDEVICE = *mut HSWDEVICE;
|
||||
|
||||
#[link(name = "Newdev")]
|
||||
extern "C" {
|
||||
pub fn InstallUpdate(fullInfPath: LPCTSTR, rebootRequired: PBOOL) -> BOOL;
|
||||
}
|
||||
|
||||
#[link(name = "Setupapi")]
|
||||
extern "C" {
|
||||
pub fn IsDeviceCreated(created: PBOOL) -> BOOL;
|
||||
}
|
||||
|
||||
#[link(name = "Swdevice")]
|
||||
#[link(name = "OneCoreUAP")]
|
||||
extern "C" {
|
||||
pub fn DeviceCreate(hSwDevice: PHSWDEVICE) -> BOOL;
|
||||
pub fn DeviceClose(hSwDevice: HSWDEVICE);
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
pub fn Uninstall(fullInfPath: LPCTSTR, rebootRequired: PBOOL) -> BOOL;
|
||||
pub fn MonitorPlugIn(index: UINT, edid: UINT, retries: INT) -> BOOL;
|
||||
pub fn MonitorPlugOut(index: UINT) -> BOOL;
|
||||
pub fn MonitorModesUpdate(index: UINT, modeCount: UINT, modes: PMonitorMode) -> BOOL;
|
||||
|
||||
pub fn GetLastMsg() -> PCHAR;
|
||||
pub fn SetPrintErrMsg(b: BOOL);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
pub mod idd;
|
||||
use std::ffi::CStr;
|
||||
|
||||
pub const DRIVER_INSTALL_PATH: &str = "RustDeskIddDriver/RustDeskIddDriver.inf";
|
||||
pub const DRIVER_DOWNLOAD_URL: &str = "";
|
||||
|
||||
pub unsafe fn get_last_msg() -> Result<&'static str, std::str::Utf8Error> {
|
||||
CStr::from_ptr(idd::GetLastMsg()).to_str()
|
||||
}
|
||||
Reference in New Issue
Block a user