gpt4 book ai didi

winapi - 从 2 个 winapi 调用返回不一致的错误

转载 作者:行者123 更新时间:2023-11-29 07:59:53 29 4
gpt4 key购买 nike

这是我的源代码:

extern crate user32;
extern crate kernel32;

use std::io::prelude::*;
use std::net::TcpStream;
use std::ptr;

fn main() {
let mut message = get_data();
loop {
if message != get_data() {
let mut client = TcpStream::connect("127.0.0.1:8080").unwrap();
message = get_data();
println!("{}", message);
let _ = client.write(message.as_bytes());
println!("Sent!");
}
}
}

fn get_data() -> String {
let value: String;
unsafe {
let open = user32::OpenClipboard(ptr::null_mut());
if open == 0 {
println!("{}", kernel32::GetLastError());
user32::CloseClipboard();
return "NULL".to_string()
}
let var = user32::GetClipboardData(13 as u32);
if var.is_null() {
println!("{}", kernel32::GetLastError());
user32::CloseClipboard();
return "NULL".to_string()
}
let data = kernel32::GlobalLock(var) as *mut u16;
let len = rust_strlen16(data);
let raws = std::slice::from_raw_parts(data, len);
value = String::from_utf16_lossy(raws);
kernel32::GlobalUnlock(var);
user32::CloseClipboard();
}
value
}

#[inline(always)] // used from clipboard-win, not mine
unsafe fn rust_strlen16(buff_p: *mut u16) -> usize {
let mut i: isize = 0;
while *buff_p.offset(i) != 0 {
i += 1;
}
return i as usize
}

当我在 main 的第 1 行初始化 message 时,一切都很好,但是当我开始复制文本时,事情开始变得有点奇怪。

get_data() 中的 OpenClipboard 函数,我从 GetLastError 得到的错误要么是 6(ERROR_INVALID_HANDLE) 或 5 (ERROR_ACCESS_DENIED),原因不明。

GetClipboardData 函数中,我始终会从 GetLastError 中获取错误代码 1418 (ERROR_CLIPBOARD_NOT_OPEN)。您可能会认为我会因为从 OpenClipboard 获取错误代码 6 或 5 而得到此错误,但是它不会打印 1418,因为它不能在函数中走那么远,对吧?我可能是错的,但这就是我的看法。

您还应该注意的是,在循环中比较 message 时会出现这些错误,但是 if 语句仍然继续并且消息被分配了实际复制的数据,这里是一些示例输出。

1418  // this is when message and get_data() are being checked
println!("{}", result); // this is the actual copied data which is message
Sent! // sent!
5
NULL
Sent!
println!("Got Connection");
Sent!

最佳答案

这是我对正在发生的事情的猜测:系统正在强行关闭您打开的剪贴板句柄。

据我所知,剪贴板是真正的全局共享资源,甚至是跨进程的。我无法追溯 OpenClipboard 函数出现的时间有多远,但根据 MSDN,它至少可以在 Windows 2000 上运行。如果它真的起源于 Windows 98/95 或更早版本,我不会感到惊讶。剪贴板是一个古老的概念!

但是,出现了一个概念性问题:如果一个程序打开剪贴板而忘记关闭它怎么办?然后你的整个系统将被阻止使用剪贴板,这将是非常糟糕的。我相信(没有证据)操作系统会跟踪您打开剪贴板的次数,并决定在您占用剪贴板时将其取回。

当您运行您的程序时,您可以看到它固定在 100% 的 CPU 上,因为您构建了一个大部分时间都打开剪贴板的繁忙循环。这足以触发简单的滥用启发式算法。添加 (::std::thread::sleep_ms(100)) 到循环的末尾似乎让一切都表现得更好。

但是,您仍然偶尔会收到错误代码 5 (ERROR_ACCESS_DENIED)。同样,我认为这是由剪贴板的全局共享性质引起的。某些其他 进程出于某种原因打开了剪贴板,您不能拥有它。正确的做法是处理无法始终立即访问资源的问题。

进行这些更改似乎可以使您的程序正常运行,但还有更多工作要做:

extern crate user32;
extern crate kernel32;

use std::ptr;
use std::marker::PhantomData;

fn main() {
let mut sequence_val = None;

loop {
let clip = match Clipboard::open() {
Err(WinApiError(ERROR_ACCESS_DENIED)) |
Err(WinApiError(ERROR_CLIPBOARD_NOT_OPEN)) => {
println!("Someone else is using the clipboard; continuing");
continue;
},
Err(e) => panic!("Unknown error while opening the clipboard: {:?}", e),
Ok(c) => c,
};

let next_sequence_val = Some(clip.sequence_number());
if sequence_val != next_sequence_val {
println!("Clipboard advanced from {:?} to {:?}", sequence_val, next_sequence_val);
sequence_val = next_sequence_val;

let all_formats: Result<Vec<_>, _> = clip.formats().collect();
println!("Available formats: {:?}", all_formats);

let message = clip.get_text().expect("Cannot read from clipboard");

match message {
Some(message) => println!("Got clipboard text: {}", message),
None => println!("Clipboard did not contain textual data"),
}
}

::std::thread::sleep_ms(250);
}
}

#[derive(Debug, Copy, Clone)]
struct WinApiError(u32);

const ERROR_ACCESS_DENIED: u32 = 0x5;
const ERROR_CLIPBOARD_NOT_OPEN: u32 = 0x58A;

impl WinApiError {
fn from_global() -> Result<(), WinApiError> {
let val = unsafe { kernel32::GetLastError() };
if val != 0 {
Err(WinApiError(val))
} else {
Ok(())
}
}
}

// PhantomData is used here to prevent creating the struct with
// `Clipboard`.
struct Clipboard {
marker: PhantomData<()>,
}

const CF_UNICODETEXT: u32 = 13;

impl Clipboard {
fn open() -> Result<Clipboard, WinApiError> {
unsafe {
user32::OpenClipboard(ptr::null_mut());
try!(WinApiError::from_global());
Ok(Clipboard { marker: PhantomData })
}
}

fn formats(&self) -> ClipboardFormats {
ClipboardFormats { clip: PhantomData, fmt: 0 }
}

fn sequence_number(&self) -> u32 {
unsafe { user32::GetClipboardSequenceNumber() }
}

fn get_text(&self) -> Result<Option<String>, WinApiError> {
for fmt in self.formats() {
let fmt = try!(fmt);
if fmt == CF_UNICODETEXT {
unsafe {
let var = user32::GetClipboardData(CF_UNICODETEXT);
try!(WinApiError::from_global());

// I don't believe this lock is actually
// needed. In searching around, it only seems to
// be used when you call `GlobalAlloc` and then
// set the clipboard with that data.

// If it's needed, consider making another RAII
// type to handle automatically unlocking.
let data = kernel32::GlobalLock(var) as *mut u16;
try!(WinApiError::from_global());

let len = rust_strlen16(data);
let raws = std::slice::from_raw_parts(data, len);
let value = String::from_utf16_lossy(raws);

kernel32::GlobalUnlock(var);
try!(WinApiError::from_global());

return Ok(Some(value));
}
}
}

Ok(None)
}
}

impl Drop for Clipboard {
fn drop(&mut self) {
unsafe {
// Ignore failure to close as we can't really do anything
// about it
user32::CloseClipboard();
let _ = WinApiError::from_global();

}
}
}

unsafe fn rust_strlen16(buff_p: *mut u16) -> usize {
let mut i = 0;
while *buff_p.offset(i) != 0 { i += 1 }
i as usize
}

struct ClipboardFormats<'a>{
// PhantomData is used here to prevent outliving the opened
// clipboard
clip: PhantomData<&'a Clipboard>,
fmt: u32,
}

impl<'a> Iterator for ClipboardFormats<'a> {
type Item = Result<u32, WinApiError>;

fn next(&mut self) -> Option<Self::Item> {
let next_fmt = unsafe { user32::EnumClipboardFormats(self.fmt) };
if next_fmt == 0 {
match WinApiError::from_global() {
Ok(_) => None,
Err(e) => Some(Err(e)),
}
} else {
self.fmt = next_fmt;
Some(Ok(next_fmt))
}
}
}

也就是说,我创建了一个便利类型 WinApiError,它会自动检查 GetLastError 并将其绑定(bind)到 Rust 标准 Result 类型中。这与 try! 一起广泛使用。

我引入了一个 Clipboard 结构来拥有一个类型来实现 Drop。这确保了我们将始终在使用完剪贴板后将其关闭。您甚至可能想要添加一个 close 方法,如果您想提前关闭它,它会变得非常明显和容易:

fn close(self) {
// Automatically dropped as it goes out of scope.
}

为了稍微提高性能,我使用了 GetClipboardSequenceNumber,它返回剪贴板的整数时间戳。这允许进行更轻量级的检查以了解剪贴板是否已更改,而不是每次都创建一个 String。它还会阻止您使用神奇的 "NULL" 字符串。您可能仍想跟踪最后一个字符串,以了解用户是否再次复制了相同的文本。

从剪贴板中检索数据时,您应该遍历所有可用格式并在您关心的第一个上采取行动。虽然您只关心 UCS-2 格式,但我在尝试调试时添加了它,所以我想我会保留它。

有比 sleep 更好的解决方案,我还没有实现。 AddClipboardFormatListener可以在剪贴板更改时通知消息循环。这可能是正确的解决方案,但确实需要您创建一个窗口(即使它仅用于消息)。

关于winapi - 从 2 个 winapi 调用返回不一致的错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34572755/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com