use crate::dbgeng::{breakpoint::DebugBreakpoint, client::DebugClassFlags, *};
use std::time::Duration;
use windows::{
Win32::Foundation::FARPROC,
core::{PCSTR, PCWSTR, PSTR, PWSTR},
};
use windows_core::{BOOL, GUID};
use windy::{ACPStr, ACPString, WStr, WString};
use windy_macros::{acpstr, wstr};
impl_debug_interface!(
DebugControl,
DebugControlRef,
IDebugControl7,
IDebugControl
);
#[derive(Debug, Copy, Clone)]
pub enum BreakpointParametersLookup<'a> {
Ids(&'a [u32]),
IndexRange { start: u32, count: u32 },
}
#[repr(u32)]
#[derive(Debug, Copy, Clone)]
pub enum DebugSysVerStrFlags {
ServicePack = DEBUG_SYSVERSTR_SERVICE_PACK,
Build = DEBUG_SYSVERSTR_BUILD,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct TimeoutQuery(u32);
const INFINITE: u32 = u32::MAX;
const MAX_FINITE_TIMEOUT: u32 = INFINITE - 1;
impl TimeoutQuery {
pub const INFINITE: Self = Self(INFINITE);
pub const fn is_infinite(self) -> bool { self.0 == INFINITE }
pub const fn as_milliseconds(self) -> Option<u32> {
if self.is_infinite() {
None
} else {
Some(self.0)
}
}
fn finite_milliseconds(milliseconds: u64) -> Self {
Self(milliseconds.min(MAX_FINITE_TIMEOUT as u64) as u32)
}
pub fn from_hours(hours: u32) -> Self {
Self::finite_milliseconds(hours as u64 * 60 * 60 * 1000)
}
pub fn from_minutes(minutes: u32) -> Self {
Self::finite_milliseconds(minutes as u64 * 60 * 1000)
}
pub fn from_secs(secs: u32) -> Self {
Self::finite_milliseconds(secs as u64 * 1000)
}
pub fn from_milliseconds(milliseconds: u32) -> Self {
Self::finite_milliseconds(milliseconds as u64)
}
pub fn try_from_duration(duration: Duration) -> WinResult<Self> {
let milliseconds = duration.as_millis();
if milliseconds >= INFINITE as u128 {
return Err(windows::Win32::Foundation::E_INVALIDARG.into());
}
Ok(Self(milliseconds as u32))
}
}
impl From<u32> for TimeoutQuery {
fn from(value: u32) -> Self { Self(value) }
}
impl From<TimeoutQuery> for u32 {
fn from(value: TimeoutQuery) -> Self { value.0 }
}
impl From<Duration> for TimeoutQuery {
fn from(value: Duration) -> Self {
Self(value.as_millis().min(MAX_FINITE_TIMEOUT as u128) as u32)
}
}
pub const DEBUG_EXEC_FLAGS_NONBLOCK: u32 = 1;
bitflags::bitflags! {
pub struct DebugExecFlags: u32 {
const NonBlock = DEBUG_EXEC_FLAGS_NONBLOCK;
}
}
#[repr(transparent)]
#[derive(Debug, Eq, PartialEq)]
pub struct DebugExtensionHandle(u64);
impl DebugExtensionHandle {
pub unsafe fn from_raw(handle: u64) -> Self { Self(handle) }
pub fn as_raw(&self) -> u64 { self.0 }
}
impl DebugControl {
pub fn get_interrupt(&self) -> WinResult<bool> {
unsafe { hr!(vcall!(self, GetInterrupt)) }
}
pub fn set_interrupt(&self, flags: DebugInterruptFlag) -> WinResult<()> {
unsafe { self.0.SetInterrupt(flags.into()) }
}
pub fn get_interrupt_timeout(&self) -> WinResult<u32> {
unsafe { self.0.GetInterruptTimeout() }
}
pub fn set_interrupt_timeout(&self, seconds: u32) -> WinResult<()> {
unsafe { self.0.SetInterruptTimeout(seconds) }
}
pub fn get_log_file(&self) -> WinResult<(ACPString, bool)> {
let mut append: BOOL = BOOL(0);
unsafe {
let log_file = astring_with_capacity(64, |v, s| {
vcall!(
self,
GetLogFile,
pa!(v),
v.len().try_into().unwrap(),
s,
&mut append,
)
})?;
Ok((log_file, append.as_bool()))
}
}
pub fn open_log_file(
&self,
file: impl AsRef<ACPStr>,
append: bool,
) -> WinResult<()> {
unsafe { self.0.OpenLogFile(pca!(file), append) }
}
pub fn close_log_file(&self) -> WinResult<()> {
unsafe { self.0.CloseLogFile() }
}
pub fn get_log_mask(&self) -> WinResult<DebugOutputFlags> {
unsafe { Ok(DebugOutputFlags::from_bits_retain(self.0.GetLogMask()?)) }
}
pub fn set_log_mask(&self, mask: DebugOutputFlags) -> WinResult<()> {
unsafe { self.0.SetLogMask(mask.bits()) }
}
pub fn input(&self) -> WinResult<ACPString> {
unsafe {
astring_with_capacity(64, |v, s| {
vcall!(self, Input, pa!(v), v.len().try_into().unwrap(), s)
})
}
}
pub fn return_input(&self, buffer: impl AsRef<ACPStr>) -> WinResult<()> {
unsafe { self.0.ReturnInput(pca!(buffer)) }
}
pub fn output(
&self,
mask: DebugOutputFlags,
s: impl AsRef<ACPStr>,
) -> WinResult<()> {
type Func =
unsafe extern "C" fn(*mut c_void, u32, PCSTR, PCSTR) -> HRESULT;
let vt = Interface::vtable(&self.0);
unsafe {
let f: Func = std::mem::transmute(vt.Output);
f(
self.0.as_raw(),
mask.bits(),
PCSTR(acpstr!("%s").as_u8_ptr()),
pca!(s),
)
.ok()
}
}
pub fn controlled_output(
&self,
output_control: DebugOutctlFlags,
mask: DebugOutputFlags,
s: impl AsRef<ACPStr>,
) -> WinResult<()> {
type Func = unsafe extern "C" fn(
*mut c_void,
u32,
u32,
PCSTR,
PCSTR,
) -> HRESULT;
let vt = Interface::vtable(&self.0);
unsafe {
let f: Func = std::mem::transmute(vt.ControlledOutput);
f(
self.0.as_raw(),
output_control.bits(),
mask.bits(),
PCSTR(acpstr!("%s").as_u8_ptr()),
pca!(s),
)
.ok()
}
}
pub fn get_prompt_text(&self) -> WinResult<ACPString> {
unsafe {
astring_with_capacity(128, |v, s| {
vcall!(
self,
GetPromptText,
pa!(v),
v.len().try_into().unwrap(),
s
)
})
}
}
pub fn output_version_information(
&self,
output_control: DebugOutctlFlags,
) -> WinResult<()> {
unsafe { self.0.OutputVersionInformation(output_control.bits()) }
}
pub fn get_notify_event_handle(&self) -> WinResult<EventHandle> {
unsafe { Ok(EventHandle(self.0.GetNotifyEventHandle()?)) }
}
pub fn set_notify_event_handle(
&self,
handle: EventHandle,
) -> WinResult<()> {
unsafe { self.0.SetNotifyEventHandle(handle.0) }
}
pub fn assemble(
&self,
offset: DebuggeeOffset,
instr: impl AsRef<ACPStr>,
) -> WinResult<DebuggeeOffset> {
unsafe { self.0.Assemble(offset, pca!(instr)) }
}
pub fn disassemble(
&self,
offset: DebuggeeOffset,
flags: DebugDisasmFlags,
) -> WinResult<(ACPString, DebuggeeOffset)> {
let mut end_offset = 0;
let res = unsafe {
astring_with_capacity(128, |v, s| {
vcall!(
self,
Disassemble,
offset,
flags.bits(),
pa!(v),
v.len().try_into().unwrap(),
s,
&mut end_offset,
)
})?
};
Ok((res, end_offset))
}
pub fn get_disassemble_effective_offset(&self) -> WinResult<u64> {
unsafe { self.0.GetDisassembleEffectiveOffset() }
}
pub fn get_near_instruction(
&self,
offset: DebuggeeOffset,
delta: i32,
) -> WinResult<DebuggeeOffset> {
unsafe { self.0.GetNearInstruction(offset, delta) }
}
pub fn get_stack_trace(
&self,
frame_offset: Option<DebuggeeOffset>,
stack_offset: Option<DebuggeeOffset>,
instruction_offset: Option<DebuggeeOffset>,
frames_size: u32,
) -> WinResult<Vec<DebugStackFrame>> {
unsafe {
let mut frames_filled = 0;
let mut frames = Vec::with_capacity(frames_size as usize);
vcall!(
self,
GetStackTrace,
frame_offset.unwrap_or(0),
stack_offset.unwrap_or(0),
instruction_offset.unwrap_or(0),
frames.as_mut_ptr() as *mut _,
frames_size,
&mut frames_filled
)
.ok()?;
let frames_filled = frames_filled as usize;
assert!(
frames_filled <= frames.capacity(),
"DbgEng returned more stack frames than requested"
);
frames.set_len(frames_filled);
Ok(frames)
}
}
pub fn get_return_offset(&self) -> WinResult<DebuggeeOffset> {
unsafe { self.0.GetReturnOffset() }
}
pub fn get_debuggee_type(&self) -> WinResult<DebuggeeType> {
let mut class = 0;
let mut qualifier = 0;
unsafe {
self.0.GetDebuggeeType(&mut class, &mut qualifier)?;
}
Ok(DebuggeeType::from_raw(class, qualifier))
}
pub fn get_actual_processor_type(&self) -> WinResult<ProcessorType> {
unsafe { Ok(ProcessorType::from(self.0.GetActualProcessorType()?)) }
}
pub fn get_executing_processor_type(&self) -> WinResult<ProcessorType> {
unsafe { Ok(ProcessorType::from(self.0.GetExecutingProcessorType()?)) }
}
pub fn get_number_possible_executing_processor_types(
&self,
) -> WinResult<u32> {
unsafe { self.0.GetNumberPossibleExecutingProcessorTypes() }
}
pub fn get_possible_executing_processor_types(
&self,
start: u32,
count: u32,
) -> WinResult<Vec<ProcessorType>> {
let mut v = Vec::with_capacity(count as usize);
unsafe {
vcall!(
self,
GetPossibleExecutingProcessorTypes,
start,
count,
v.as_mut_ptr()
)
.ok()?;
v.set_len(count as usize);
}
Ok(v.iter().map(|x| ProcessorType::from(*x)).collect())
}
pub fn get_number_processors(&self) -> WinResult<u32> {
unsafe { self.0.GetNumberProcessors() }
}
pub fn get_system_version(
&self,
) -> WinResult<(u32, u32, u32, ACPString, u32, ACPString)> {
let mut service_pack_string_size = 0;
let mut build_string_size = 0;
let mut service_pack_number = 0;
let mut platform_id = 0;
let mut major = 0;
let mut minor = 0;
unsafe {
hr!(vcall!(
self,
GetSystemVersion,
&mut platform_id,
&mut major,
&mut minor,
PSTR(std::ptr::null_mut()),
0,
&mut service_pack_string_size,
&mut service_pack_number,
PSTR(std::ptr::null_mut()),
0,
&mut build_string_size,
))?;
let mut service_pack_string =
Vec::with_capacity(service_pack_string_size as usize);
let mut build_string =
Vec::with_capacity(build_string_size as usize);
let mut service_pack_string_size2 = 0;
let mut build_string_size2 = 0;
assert!(hr!(vcall!(
self,
GetSystemVersion,
&mut platform_id,
&mut major,
&mut minor,
PSTR(service_pack_string.as_mut_ptr()),
service_pack_string_size,
&mut service_pack_string_size2,
&mut service_pack_number,
PSTR(build_string.as_mut_ptr()),
build_string_size,
&mut build_string_size2,
))?);
assert_eq!(service_pack_string_size, service_pack_string_size2);
assert_eq!(build_string_size, build_string_size2);
service_pack_string.set_len(service_pack_string_size as usize);
build_string.set_len(build_string_size as usize);
Ok((
platform_id,
major,
minor,
ACPString::from_vec_with_nul_unchecked(service_pack_string),
service_pack_number,
ACPString::from_vec_with_nul_unchecked(build_string),
))
}
}
pub fn get_page_size(&self) -> WinResult<u32> {
unsafe { self.0.GetPageSize() }
}
pub fn is_pointer_64bit(&self) -> WinResult<bool> {
unsafe { hr!(vcall!(self, IsPointer64Bit)) }
}
pub fn read_bug_check_data(&self) -> WinResult<(u32, u64, u64, u64, u64)> {
let mut code = 0;
let mut arg1 = 0;
let mut arg2 = 0;
let mut arg3 = 0;
let mut arg4 = 0;
unsafe {
self.0.ReadBugCheckData(
&mut code, &mut arg1, &mut arg2, &mut arg3, &mut arg4,
)?;
Ok((code, arg1, arg2, arg3, arg4))
}
}
pub fn get_number_supported_processor_types(&self) -> WinResult<u32> {
unsafe { self.0.GetNumberSupportedProcessorTypes() }
}
pub fn get_supported_processor_types(
&self,
start: u32,
count: u32,
) -> WinResult<Vec<ProcessorType>> {
let mut v = Vec::with_capacity(count as usize);
unsafe {
vcall!(
self,
GetSupportedProcessorTypes,
start,
count,
v.as_mut_ptr()
)
.ok()?;
v.set_len(count as usize);
}
Ok(v.iter().map(|x| ProcessorType::from(*x)).collect())
}
pub fn get_processor_type_names(
&self,
r#type: ProcessorType,
) -> WinResult<(ACPString, ACPString)> {
let mut full_name_size = 0;
let mut abbrev_name_size = 0;
unsafe {
hr!(vcall!(
self,
GetProcessorTypeNames,
r#type as u32,
PSTR(std::ptr::null_mut()),
0,
&mut full_name_size,
PSTR(std::ptr::null_mut()),
0,
&mut abbrev_name_size,
))?;
let mut full_name = Vec::with_capacity(full_name_size as usize);
let mut abbrev_name = Vec::with_capacity(abbrev_name_size as usize);
let mut full_name_size2 = 0;
let mut abbrev_name_size2 = 0;
assert!(hr!(vcall!(
self,
GetProcessorTypeNames,
r#type as u32,
PSTR(full_name.as_mut_ptr()),
full_name_size,
&mut full_name_size2,
PSTR(abbrev_name.as_mut_ptr()),
abbrev_name_size,
&mut abbrev_name_size2,
))?);
assert_eq!(full_name_size, full_name_size2);
assert_eq!(abbrev_name_size, abbrev_name_size2);
full_name.set_len(full_name_size as usize);
abbrev_name.set_len(abbrev_name_size as usize);
Ok((
ACPString::from_vec_with_nul_unchecked(full_name),
ACPString::from_vec_with_nul_unchecked(abbrev_name),
))
}
}
pub fn get_effective_processor_type(&self) -> WinResult<ProcessorType> {
unsafe { Ok(self.0.GetEffectiveProcessorType()?.into()) }
}
pub fn set_effective_processor_type(
&self,
r#type: ProcessorType,
) -> WinResult<()> {
unsafe { self.0.SetEffectiveProcessorType(r#type.into()) }
}
pub fn get_execution_status(&self) -> WinResult<DebugStatus> {
unsafe {
Ok(DebugStatus::try_from(self.0.GetExecutionStatus()?)
.expect("Invalid execution status"))
}
}
pub fn set_execution_status(
&self,
status: DebugExecutionStatus,
) -> WinResult<()> {
unsafe { self.0.SetExecutionStatus(status as u32) }
}
pub fn get_code_level(&self) -> WinResult<CodeLevel> {
unsafe {
Ok(self
.0
.GetCodeLevel()?
.try_into()
.expect("invalid CodeLevel"))
}
}
pub fn set_code_level(&self, code_level: CodeLevel) -> WinResult<()> {
unsafe { self.0.SetCodeLevel(code_level as u32) }
}
pub fn get_engine_options(&self) -> WinResult<EngineOptions> {
unsafe {
Ok(EngineOptions::from_bits_retain(self.0.GetEngineOptions()?))
}
}
pub fn add_engine_options(&self, options: EngineOptions) -> WinResult<()> {
unsafe {
self.0.AddEngineOptions(options.bits())?;
Ok(())
}
}
pub fn remove_engine_options(
&self,
options: EngineOptions,
) -> WinResult<()> {
unsafe { self.0.RemoveEngineOptions(options.bits()) }
}
pub fn set_engine_options(&self, options: EngineOptions) -> WinResult<()> {
unsafe { self.0.SetEngineOptions(options.bits()) }
}
pub fn get_system_error_control(&self) -> WinResult<(u32, u32)> {
let mut output_level = 0;
let mut break_level = 0;
unsafe {
self.0
.GetSystemErrorControl(&mut output_level, &mut break_level)?;
}
Ok((output_level, break_level))
}
pub fn set_system_error_control(
&self,
output_level: u32,
break_level: u32,
) -> WinResult<()> {
unsafe { self.0.SetSystemErrorControl(output_level, break_level) }
}
pub fn get_text_macro(&self, slot: u32) -> WinResult<ACPString> {
unsafe {
astring_with_capacity(64, |v, s| {
vcall!(
self,
GetTextMacro,
slot,
pa!(v),
v.len().try_into().unwrap(),
s
)
})
}
}
pub fn set_text_macro(
&self,
slot: u32,
r#macro: impl AsRef<ACPStr>,
) -> WinResult<()> {
unsafe { self.0.SetTextMacro(slot, pca!(r#macro)) }
}
pub fn get_radix(&self) -> WinResult<DebugRadix> {
unsafe {
Ok(DebugRadix::try_from(self.0.GetRadix()?)
.expect("DbgEng returned an invalid radix"))
}
}
pub fn set_radix(&self, radix: DebugRadix) -> WinResult<()> {
unsafe { self.0.SetRadix(radix as u32) }
}
pub fn evaluate(
&self,
expression: impl AsRef<ACPStr>,
desired_type: DebugValueType,
) -> WinResult<DebugValue> {
let mut ret = DebugValue::default();
unsafe {
self.0.Evaluate(
pca!(expression),
desired_type as u32,
ret.as_mut_ptr(),
None,
)?;
Ok(ret)
}
}
pub fn coerce_value(
&self,
value: &DebugValue,
out_type: DebugValueType,
) -> WinResult<DebugValue> {
let mut ret = DebugValue::default();
unsafe {
self.0.CoerceValue(
value.as_ptr(),
out_type as u32,
ret.as_mut_ptr(),
)?;
Ok(ret)
}
}
pub fn execute(
&self,
output_control: DebugOutctlFlags,
command: impl AsRef<ACPStr>,
flags: DebugExecutionFlags,
) -> WinResult<()> {
unsafe {
self.0
.Execute(output_control.bits(), pca!(command), flags.bits())
}
}
pub fn execute_command_file(
&self,
output_control: DebugOutctlFlags,
command_file: impl AsRef<ACPStr>,
flags: DebugExecutionFlags,
) -> WinResult<()> {
unsafe {
self.0.ExecuteCommandFile(
output_control.bits(),
pca!(command_file),
flags.bits(),
)
}
}
pub fn get_number_breakpoints(&self) -> WinResult<u32> {
unsafe { self.0.GetNumberBreakpoints() }
}
pub unsafe fn get_breakpoint_by_index(
&self,
index: u32,
) -> WinResult<DebugBreakpoint> {
unsafe {
DebugBreakpoint::from_interface(
&self.0.GetBreakpointByIndex(index)?,
)
}
}
pub unsafe fn get_breakpoint_by_id(
&self,
id: impl Into<BreakpointId>,
) -> WinResult<DebugBreakpoint> {
unsafe {
DebugBreakpoint::from_interface(
&self.0.GetBreakpointById(id.into().0)?,
)
}
}
pub fn get_breakpoint_parameters(
&self,
e: BreakpointParametersLookup,
) -> WinResult<(Vec<DebugBreakpointParameters>, bool)> {
unsafe {
let (v, is_all) = match e {
BreakpointParametersLookup::Ids(ids) => {
let mut v =
vec![DebugBreakpointParameters::default(); ids.len()];
let is_all = hr!(vcall!(
self,
GetBreakpointParameters,
ids.len().try_into()?,
ids.as_ptr(),
0,
v.as_mut_ptr() as *mut _,
))?;
(v, is_all)
}
BreakpointParametersLookup::IndexRange { start, count } => {
let mut v = vec![
DebugBreakpointParameters::default();
count as usize
];
let is_all = hr!(vcall!(
self,
GetBreakpointParameters,
count,
std::ptr::null_mut(),
start,
v.as_mut_ptr() as *mut _,
))?;
(v, is_all)
}
};
Ok((v, is_all))
}
}
pub unsafe fn add_breakpoint(
&self,
ty: DebugBreakpointTypes,
desired_id: Option<BreakpointId>,
) -> WinResult<DebugBreakpoint> {
unsafe {
DebugBreakpoint::from_interface(&self.0.AddBreakpoint(
ty.into(),
desired_id.map_or(DEBUG_ANY_ID, |x| x.0),
)?)
}
}
pub fn remove_breakpoint(&self, handle: DebugBreakpoint) -> WinResult<()> {
unsafe {
self.0
.RemoveBreakpoint(IDebugBreakpoint::from_raw_borrowed(
&handle.as_raw().as_raw(),
))?;
std::mem::forget(handle);
Ok(())
}
}
pub unsafe fn add_extension(
&self,
path: impl AsRef<ACPStr>,
) -> WinResult<DebugExtensionHandle> {
unsafe { Ok(DebugExtensionHandle(self.0.AddExtension(pca!(path), 0)?)) }
}
pub unsafe fn remove_extension(
&self,
handle: DebugExtensionHandle,
) -> WinResult<()> {
unsafe { self.0.RemoveExtension(handle.0) }
}
pub fn get_extension_by_path(
&self,
path: impl AsRef<ACPStr>,
) -> WinResult<DebugExtensionHandle> {
unsafe {
Ok(DebugExtensionHandle(self.0.GetExtensionByPath(pca!(path))?))
}
}
pub unsafe fn call_extension(
&self,
handle: &DebugExtensionHandle,
function: impl AsRef<ACPStr>,
arguments: impl AsRef<ACPStr>,
) -> WinResult<()> {
unsafe {
self.0
.CallExtension(handle.0, pca!(function), pca!(arguments))
}
}
pub fn get_extension_function(
&self,
handle: &DebugExtensionHandle,
func_name: impl AsRef<ACPStr>,
) -> WinResult<FARPROC> {
unsafe { self.0.GetExtensionFunction(handle.0, pca!(func_name)) }
}
pub fn get_windbg_extension_apis64(
&self,
) -> WinResult<WindbgExtensionApis64> {
unsafe {
let mut x = WindbgExtensionApis64::default();
self.0
.GetWindbgExtensionApis64(&mut x as *mut _ as *mut _)?;
Ok(x)
}
}
pub fn get_number_event_filters(&self) -> WinResult<(u32, u32, u32)> {
let mut specific_events = 0;
let mut specific_exceptions = 0;
let mut arbitrary_exceptions = 0;
unsafe {
self.0.GetNumberEventFilters(
&mut specific_events,
&mut specific_exceptions,
&mut arbitrary_exceptions,
)?;
Ok((specific_events, specific_exceptions, arbitrary_exceptions))
}
}
pub fn get_event_filter_text(
&self,
index: EventFilterIndex,
) -> WinResult<ACPString> {
unsafe {
astring_with_capacity(64, |v, s| {
vcall!(
self,
GetEventFilterText,
index.into(),
pa!(v),
v.len().try_into().unwrap(),
s
)
})
}
}
pub fn get_event_filter_command(
&self,
index: EventFilterIndex,
) -> WinResult<ACPString> {
unsafe {
astring_with_capacity(64, |v, s| {
vcall!(
self,
GetEventFilterCommand,
index.into(),
pa!(v),
v.len().try_into().unwrap(),
s
)
})
}
}
pub fn set_event_filter_command(
&self,
index: EventFilterIndex,
command: impl AsRef<ACPStr>,
) -> WinResult<()> {
unsafe { self.0.SetEventFilterCommand(index.into(), pca!(command)) }
}
pub fn wait_for_event(
&self,
timeout: impl Into<TimeoutQuery>,
) -> WinResult<bool> {
unsafe { hr!(vcall!(self, WaitForEvent, 0, timeout.into().into())) }
}
pub fn _get_debuggee_class(&self) -> WinResult<DebugClassFlags> {
let mut class = 0;
unsafe {
self.0.GetDebuggeeType(&mut class, std::ptr::null_mut())?;
Ok(DebugClassFlags::try_from(class).expect("Invalid class"))
}
}
pub fn _get_platform_id(&self) -> WinResult<u32> {
let mut platform_id = 0;
unsafe {
self.0.GetSystemVersion(
&mut platform_id,
std::ptr::null_mut(),
std::ptr::null_mut(),
None,
None,
std::ptr::null_mut(),
None,
None,
)?;
}
Ok(platform_id)
}
pub fn _get_major(&self) -> WinResult<u32> {
let mut major = 0;
unsafe {
self.0.GetSystemVersion(
std::ptr::null_mut(),
&mut major,
std::ptr::null_mut(),
None,
None,
std::ptr::null_mut(),
None,
None,
)?;
}
Ok(major)
}
pub fn _get_minor(&self) -> WinResult<u32> {
let mut minor = 0;
unsafe {
self.0.GetSystemVersion(
std::ptr::null_mut(),
std::ptr::null_mut(),
&mut minor,
None,
None,
std::ptr::null_mut(),
None,
None,
)?;
}
Ok(minor)
}
pub fn _get_service_pack_number(&self) -> WinResult<u32> {
let mut service_pack_number = 0;
unsafe {
self.0.GetSystemVersion(
std::ptr::null_mut(),
std::ptr::null_mut(),
std::ptr::null_mut(),
None,
None,
&mut service_pack_number,
None,
None,
)?;
}
Ok(service_pack_number)
}
}
impl DebugControl {
pub fn get_current_time_date(&self) -> WinResult<Duration> {
unsafe { Ok(Duration::from_secs(self.0.GetCurrentTimeDate()? as u64)) }
}
pub fn get_current_system_up_time(&self) -> WinResult<Duration> {
unsafe {
Ok(Duration::from_secs(self.0.GetCurrentSystemUpTime()? as u64))
}
}
pub fn get_number_text_replacements(&self) -> WinResult<u32> {
unsafe { self.0.GetNumberTextReplacements() }
}
pub fn remove_text_replacements(&self) -> WinResult<()> {
unsafe { self.0.RemoveTextReplacements() }
}
}
impl DebugControl {
pub fn get_assembly_options(&self) -> WinResult<DebugAsmOptFlags> {
unsafe {
Ok(DebugAsmOptFlags::from_bits_retain(
self.0.GetAssemblyOptions()?,
))
}
}
pub fn add_assembly_options(
&self,
options: DebugAsmOptFlags,
) -> WinResult<()> {
unsafe { self.0.AddAssemblyOptions(options.bits()) }
}
pub fn remove_assembly_options(
&self,
options: DebugAsmOptFlags,
) -> WinResult<()> {
unsafe { self.0.RemoveAssemblyOptions(options.bits()) }
}
pub fn set_assembly_options(
&self,
options: DebugAsmOptFlags,
) -> WinResult<()> {
unsafe { self.0.SetAssemblyOptions(options.bits()) }
}
pub fn get_expression_syntax(&self) -> WinResult<DebugExprOptions> {
unsafe {
Ok(DebugExprOptions::try_from(self.0.GetExpressionSyntax()?)
.expect("Invalid expression syntax"))
}
}
pub fn set_expression_syntax(
&self,
flags: DebugExprOptions,
) -> WinResult<()> {
unsafe { self.0.SetExpressionSyntax(flags as u32) }
}
pub fn set_expression_syntax_by_name(
&self,
abbrev_name: impl AsRef<ACPStr>,
) -> WinResult<()> {
unsafe { self.0.SetExpressionSyntaxByName(pca!(abbrev_name)) }
}
pub fn get_number_expression_syntaxes(&self) -> WinResult<u32> {
unsafe { self.0.GetNumberExpressionSyntaxes() }
}
pub fn get_expression_syntax_names(
&self,
index: u32,
) -> WinResult<(ACPString, ACPString)> {
let mut full_name = Vec::with_capacity(32);
let mut abbrev_name = Vec::with_capacity(32);
let mut full_name_size1 = 0;
let mut abbrev_name_size1 = 0;
unsafe {
let hr = hr!(vcall!(
self,
GetExpressionSyntaxNames,
index,
PSTR(full_name.as_mut_ptr()),
32,
&mut full_name_size1,
PSTR(abbrev_name.as_mut_ptr()),
32,
&mut abbrev_name_size1,
))?;
if hr {
assert!(full_name_size1 as usize <= full_name.capacity());
assert!(abbrev_name_size1 as usize <= abbrev_name.capacity());
full_name.set_len(full_name_size1 as usize);
abbrev_name.set_len(abbrev_name_size1 as usize);
return Ok((
ACPString::from_vec_with_nul_unchecked(full_name),
ACPString::from_vec_with_nul_unchecked(abbrev_name),
));
}
let mut full_name = Vec::with_capacity(full_name_size1 as usize);
let mut abbrev_name =
Vec::with_capacity(abbrev_name_size1 as usize);
let mut full_name_size2 = 0;
let mut abbrev_name_size2 = 0;
let hr = hr!(vcall!(
self,
GetExpressionSyntaxNames,
index,
PSTR(full_name.as_mut_ptr()),
full_name_size1,
&mut full_name_size2,
PSTR(abbrev_name.as_mut_ptr()),
abbrev_name_size1,
&mut abbrev_name_size2,
))?;
assert_eq!(full_name_size1, full_name_size2);
assert_eq!(abbrev_name_size1, abbrev_name_size2);
if hr {
full_name.set_len(full_name_size2 as usize);
abbrev_name.set_len(abbrev_name_size2 as usize);
Ok((
ACPString::from_vec_with_nul_unchecked(full_name),
ACPString::from_vec_with_nul_unchecked(abbrev_name),
))
} else {
Err(S_FALSE.into())
}
}
}
pub fn get_number_events(&self) -> WinResult<u32> {
unsafe { self.0.GetNumberEvents() }
}
}
impl DebugControl {
pub fn get_log_file_wide(&self) -> WinResult<(WString, bool)> {
let mut append: BOOL = BOOL(0);
unsafe {
let log_file = wstring_with_capacity(64, |v, s| {
vcall!(
self,
GetLogFileWide,
pw!(v),
v.len().try_into().unwrap(),
s,
&mut append,
)
})?;
Ok((log_file, append.as_bool()))
}
}
pub fn open_log_file_wide(
&self,
file: impl AsRef<WStr>,
append: bool,
) -> WinResult<()> {
unsafe { self.0.OpenLogFileWide(pcw!(file), append) }
}
pub fn input_wide(&self) -> WinResult<WString> {
unsafe {
wstring_with_capacity(64, |v, s| {
vcall!(self, InputWide, pw!(v), v.len().try_into().unwrap(), s)
})
}
}
pub fn return_input_wide(&self, buffer: impl AsRef<WStr>) -> WinResult<()> {
unsafe { self.0.ReturnInputWide(pcw!(buffer)) }
}
pub fn output_wide(
&self,
mask: DebugOutputFlags,
s: impl AsRef<WStr>,
) -> WinResult<()> {
type Func =
unsafe extern "C" fn(*mut c_void, u32, PCWSTR, PCWSTR) -> HRESULT;
let vt = Interface::vtable(&self.0);
unsafe {
let f: Func = std::mem::transmute(vt.OutputWide);
f(
self.0.as_raw(),
mask.bits(),
PCWSTR(wstr!("%s").as_ptr()),
pcw!(s),
)
.ok()
}
}
pub fn controlled_output_wide(
&self,
output_control: DebugOutctlFlags,
mask: DebugOutputFlags,
s: impl AsRef<WStr>,
) -> WinResult<()> {
type Func = unsafe extern "C" fn(
*mut c_void,
u32,
u32,
PCWSTR,
PCWSTR,
) -> HRESULT;
let vt = Interface::vtable(&self.0);
unsafe {
let f: Func = std::mem::transmute(vt.ControlledOutputWide);
f(
self.0.as_raw(),
output_control.bits(),
mask.bits(),
PCWSTR(wstr!("%s").as_ptr()),
pcw!(s),
)
.ok()
}
}
pub fn get_prompt_text_wide(&self) -> WinResult<WString> {
unsafe {
wstring_with_capacity(128, |v, s| {
vcall!(
self,
GetPromptTextWide,
pw!(v),
v.len().try_into().unwrap(),
s
)
})
}
}
pub fn assemble_wide(
&self,
offset: DebuggeeOffset,
instr: impl AsRef<WStr>,
) -> WinResult<DebuggeeOffset> {
unsafe { self.0.AssembleWide(offset, pcw!(instr)) }
}
pub fn disassemble_wide(
&self,
offset: DebuggeeOffset,
flags: DebugDisasmFlags,
) -> WinResult<(WString, DebuggeeOffset)> {
let mut end_offset = 0;
let res = unsafe {
wstring_with_capacity(128, |v, s| {
vcall!(
self,
DisassembleWide,
offset,
flags.bits(),
pw!(v),
v.len().try_into().unwrap(),
s,
&mut end_offset,
)
})?
};
Ok((res, end_offset))
}
pub fn get_processor_type_names_wide(
&self,
r#type: ProcessorType,
) -> WinResult<(WString, WString)> {
let mut full_name_size = 0;
let mut abbrev_name_size = 0;
unsafe {
hr!(vcall!(
self,
GetProcessorTypeNamesWide,
r#type as u32,
PWSTR(std::ptr::null_mut()),
0,
&mut full_name_size,
PWSTR(std::ptr::null_mut()),
0,
&mut abbrev_name_size,
))?;
let mut full_name = Vec::with_capacity(full_name_size as usize);
let mut abbrev_name = Vec::with_capacity(abbrev_name_size as usize);
let mut full_name_size2 = 0;
let mut abbrev_name_size2 = 0;
assert!(hr!(vcall!(
self,
GetProcessorTypeNamesWide,
r#type as u32,
PWSTR(full_name.as_mut_ptr()),
full_name_size,
&mut full_name_size2,
PWSTR(abbrev_name.as_mut_ptr()),
abbrev_name_size,
&mut abbrev_name_size2,
))?);
assert_eq!(full_name_size, full_name_size2);
assert_eq!(abbrev_name_size, abbrev_name_size2);
full_name.set_len(full_name_size as usize);
abbrev_name.set_len(abbrev_name_size as usize);
Ok((
WString::from_vec_with_nul_unchecked(full_name),
WString::from_vec_with_nul_unchecked(abbrev_name),
))
}
}
pub fn get_text_macro_wide(&self, slot: u32) -> WinResult<WString> {
unsafe {
wstring_with_capacity(64, |v, s| {
vcall!(
self,
GetTextMacroWide,
slot,
pw!(v),
v.len().try_into().unwrap(),
s
)
})
}
}
pub fn set_text_macro_wide(
&self,
slot: u32,
r#macro: impl AsRef<WStr>,
) -> WinResult<()> {
unsafe { self.0.SetTextMacroWide(slot, pcw!(r#macro)) }
}
pub fn evaluate_wide(
&self,
expression: impl AsRef<WStr>,
desired_type: DebugValueType,
) -> WinResult<DebugValue> {
let mut ret = DebugValue::default();
unsafe {
self.0.EvaluateWide(
pcw!(expression),
desired_type as u32,
ret.as_mut_ptr(),
None,
)?;
Ok(ret)
}
}
pub fn execute_wide(
&self,
output_control: DebugOutctlFlags,
command: impl AsRef<WStr>,
flags: DebugExecutionFlags,
) -> WinResult<()> {
unsafe {
self.0.ExecuteWide(
output_control.bits(),
pcw!(command),
flags.bits(),
)
}
}
pub fn execute_command_file_wide(
&self,
output_control: DebugOutctlFlags,
command_file: impl AsRef<WStr>,
flags: DebugExecutionFlags,
) -> WinResult<()> {
unsafe {
self.0.ExecuteCommandFileWide(
output_control.bits(),
pcw!(command_file),
flags.bits(),
)
}
}
pub unsafe fn add_extension_wide(
&self,
path: impl AsRef<WStr>,
) -> WinResult<DebugExtensionHandle> {
unsafe {
Ok(DebugExtensionHandle(
self.0.AddExtensionWide(pcw!(path), 0)?,
))
}
}
pub fn get_extension_by_path_wide(
&self,
path: impl AsRef<WStr>,
) -> WinResult<DebugExtensionHandle> {
unsafe {
Ok(DebugExtensionHandle(
self.0.GetExtensionByPathWide(pcw!(path))?,
))
}
}
pub unsafe fn call_extension_wide(
&self,
handle: &DebugExtensionHandle,
function: impl AsRef<WStr>,
arguments: impl AsRef<WStr>,
) -> WinResult<()> {
unsafe {
self.0
.CallExtensionWide(handle.0, pcw!(function), pcw!(arguments))
}
}
pub fn get_extension_function_wide(
&self,
handle: &DebugExtensionHandle,
func_name: impl AsRef<WStr>,
) -> WinResult<FARPROC> {
unsafe { self.0.GetExtensionFunctionWide(handle.0, pcw!(func_name)) }
}
pub fn get_event_filter_text_wide(
&self,
index: EventFilterIndex,
) -> WinResult<WString> {
unsafe {
wstring_with_capacity(64, |v, s| {
vcall!(
self,
GetEventFilterTextWide,
index.into(),
pw!(v),
v.len().try_into().unwrap(),
s
)
})
}
}
pub fn get_event_filter_command_wide(
&self,
index: EventFilterIndex,
) -> WinResult<WString> {
unsafe {
wstring_with_capacity(64, |v, s| {
vcall!(
self,
GetEventFilterCommandWide,
index.into(),
pw!(v),
v.len().try_into().unwrap(),
s
)
})
}
}
pub fn set_event_filter_command_wide(
&self,
index: EventFilterIndex,
command: impl AsRef<WStr>,
) -> WinResult<()> {
unsafe {
self.0
.SetEventFilterCommandWide(index.into(), pcw!(command))
}
}
pub fn set_expression_syntax_by_name_wide(
&self,
abbrev_name: impl AsRef<WStr>,
) -> WinResult<()> {
unsafe { self.0.SetExpressionSyntaxByNameWide(pcw!(abbrev_name)) }
}
pub fn get_expression_syntax_names_wide(
&self,
index: u32,
) -> WinResult<(WString, WString)> {
let mut full_name = Vec::with_capacity(32);
let mut abbrev_name = Vec::with_capacity(32);
let mut full_name_size1 = 0;
let mut abbrev_name_size1 = 0;
unsafe {
let hr = hr!(vcall!(
self,
GetExpressionSyntaxNamesWide,
index,
PWSTR(full_name.as_mut_ptr()),
32,
&mut full_name_size1,
PWSTR(abbrev_name.as_mut_ptr()),
32,
&mut abbrev_name_size1,
))?;
if hr {
assert!(full_name_size1 as usize <= full_name.capacity());
assert!(abbrev_name_size1 as usize <= abbrev_name.capacity());
full_name.set_len(full_name_size1 as usize);
abbrev_name.set_len(abbrev_name_size1 as usize);
return Ok((
WString::from_vec_with_nul_unchecked(full_name),
WString::from_vec_with_nul_unchecked(abbrev_name),
));
}
let mut full_name = Vec::with_capacity(full_name_size1 as usize);
let mut abbrev_name =
Vec::with_capacity(abbrev_name_size1 as usize);
let mut full_name_size2 = 0;
let mut abbrev_name_size2 = 0;
let hr = hr!(vcall!(
self,
GetExpressionSyntaxNamesWide,
index,
PWSTR(full_name.as_mut_ptr()),
full_name_size1,
&mut full_name_size2,
PWSTR(abbrev_name.as_mut_ptr()),
abbrev_name_size1,
&mut abbrev_name_size2,
))?;
assert_eq!(full_name_size1, full_name_size2);
assert_eq!(abbrev_name_size1, abbrev_name_size2);
if hr {
full_name.set_len(full_name_size2 as usize);
abbrev_name.set_len(abbrev_name_size2 as usize);
Ok((
WString::from_vec_with_nul_unchecked(full_name),
WString::from_vec_with_nul_unchecked(abbrev_name),
))
} else {
Err(S_FALSE.into())
}
}
}
pub fn get_system_version_values(
&self,
) -> WinResult<(u32, u32, u32, u32, u32)> {
let mut platform_id = 0;
let mut win32_major = 0;
let mut win32_minor = 0;
let mut kd_major = 0;
let mut kd_minor = 0;
unsafe {
self.0.GetSystemVersionValues(
&mut platform_id,
&mut win32_major,
&mut win32_minor,
Some(&mut kd_major),
Some(&mut kd_minor),
)?;
Ok((platform_id, win32_major, win32_minor, kd_major, kd_minor))
}
}
pub fn get_system_version_string(
&self,
which: DebugSysVerStrFlags,
) -> WinResult<ACPString> {
unsafe {
astring_with_capacity(64, |v, s| {
vcall!(
self,
GetSystemVersionString,
which as u32,
pa!(v),
v.len().try_into().unwrap(),
s
)
})
}
}
pub fn get_system_version_string_wide(
&self,
which: DebugSysVerStrFlags,
) -> WinResult<WString> {
unsafe {
wstring_with_capacity(64, |v, s| {
vcall!(
self,
GetSystemVersionStringWide,
which as u32,
pw!(v),
v.len().try_into().unwrap(),
s
)
})
}
}
}
impl DebugControl {
pub fn get_stack_trace_ex(
&self,
frame_offset: Option<DebuggeeOffset>,
stack_offset: Option<DebuggeeOffset>,
instruction_offset: Option<DebuggeeOffset>,
frames_size: u32,
) -> WinResult<Vec<DebugStackFrameEx>> {
unsafe {
let mut frames_filled = 0;
let mut frames = Vec::with_capacity(frames_size as usize);
vcall!(
self,
GetStackTraceEx,
frame_offset.unwrap_or(0),
stack_offset.unwrap_or(0),
instruction_offset.unwrap_or(0),
frames.as_mut_ptr() as *mut _,
frames_size,
&mut frames_filled
)
.ok()?;
let frames_filled = frames_filled as usize;
assert!(
frames_filled <= frames.capacity(),
"DbgEng returned more stack frames than requested"
);
frames.set_len(frames_filled);
Ok(frames)
}
}
pub unsafe fn get_breakpoint_by_guid(
&self,
guid: &GUID,
) -> WinResult<DebugBreakpoint> {
unsafe {
DebugBreakpoint::from_interface(&self.0.GetBreakpointByGuid(guid)?)
}
}
}
impl DebugControl {
pub fn get_execution_status_ex(&self) -> WinResult<DebugStatus> {
unsafe { Ok(self.0.GetExecutionStatusEx()?.try_into()?) }
}
pub fn get_synchronization_status(&self) -> WinResult<(u32, u32)> {
let mut sends_attempted = 0;
let mut seconds_since_last_response = 0;
unsafe {
self.0.GetSynchronizationStatus(
&mut sends_attempted,
&mut seconds_since_last_response,
)?;
}
Ok((sends_attempted, seconds_since_last_response))
}
}
impl DebugControl {
pub fn get_debuggee_type2(
&self,
flags: DebugExecFlags,
) -> WinResult<DebuggeeType> {
let mut class = 0;
let mut qualifier = 0;
unsafe {
self.0.GetDebuggeeType2(
flags.bits(),
&mut class,
&mut qualifier,
)?;
}
Ok(DebuggeeType::from_raw(class, qualifier))
}
}