2015-06-16 19 views
6

Tôi đang tìm cách thuận tiện hơn để làm việc với std::String trong các cuộc gọi winapi trong Rust.Cách thuận tiện hơn để làm việc với các chuỗi trong các cuộc gọi winapi

Sử dụng gỉ v 0.12.0-nigtly với WINAPI 0.1.22 và user32-sys 0.1.1

Bây giờ tôi đang sử dụng một cái gì đó như thế này:

use winapi; 
use user32; 

pub fn get_window_title(handle: i32) -> String { 
    let mut v: Vec<u16> = Vec::new(); 
    v.reserve(255); 
    let mut p = v.as_mut_ptr(); 
    let len = v.len(); 
    let cap = v.capacity(); 
    let mut read_len = 0; 
    unsafe { 
     mem::forget(v); 
     read_len = unsafe { user32::GetWindowTextW(handle as winapi::HWND, p, 255) }; 
     if read_len > 0 { 
      return String::from_utf16_lossy(Vec::from_raw_parts(p, read_len as usize, cap).as_slice()); 
     } else { 
      return "".to_string(); 
     } 
    } 
} 

Tôi nghĩ rằng, đó vector này phân bổ bộ nhớ dựa trên khá kỳ quái. Vì vậy, tôi đang tìm cách dễ dàng hơn để truyền LPCWSTR tới std::String

+0

Tôi không có câu trả lời (tôi không quen với Rust) nhưng tôi sẽ đoán rằng 'i32' là số nguyên 32 bit. 'HWND' (và trên thực tế, tất cả các xử lý) là con trỏ, do đó, điều này có thể phá vỡ trên Windows 64 bit; nếu không phải bây giờ, thì tại một thời điểm nào đó trong tương lai. Bạn nên sử dụng số nguyên có kích thước con trỏ nếu bạn không muốn hiển thị trực tiếp loại 'HWND'. – andlabs

+0

'i32' hoặc có thể' i64' ở đây được sử dụng để dễ dàng chuyển toàn bộ 'HWND' qua' Arc' và đồng bộ hóa chuỗi dễ dàng hơn. Nhưng đó không phải là vấn đề. – Cherrionella

+0

@DavidHeffernan: 'str' không có bảo đảm đột biến (im-), giống như' u8' không có bảo đảm, nó chỉ là một kiểu. [Nó hoàn toàn có thể có một '& mut str', không chỉ có nhiều công dụng cho nó] (https://github.com/rust-lang/rust/pull/26241). Loại 'Chuỗi' là [chắc chắn có thể thay đổi] (https://doc.rust-lang.org/nightly/collections/string/struct.String.html#method.push). –

Trả lời

3

Trong trường hợp của bạn, bạn luôn muốn tối đa 255 byte, vì vậy bạn có thể sử dụng mảng thay vì vectơ. Điều này làm giảm toàn bộ bản mẫu cho một cuộc gọi mem::uninitialized(), cuộc gọi as_mut_ptr() và thao tác cắt.

unsafe { 
    let mut v: [u16; 255] = mem::uninitialized(); 
    let read_len = user32::GetWindowTextW(
     handle as winapi::HWND, 
     v.as_mut_ptr(), 
     255, 
    ); 
    String::from_utf16_lossy(&v[0..read_len]) 
} 

Trong trường hợp bạn muốn sử dụng một Vec, có một cách dễ dàng hơn để tiêu diệt các vec và tái tạo nó. Bạn có thể ghi trực tiếp nội dung của Vec và để Rust xử lý mọi thứ khác.

let mut v: Vec<u16> = Vec::with_capacity(255); 
unsafe { 
    let read_len = user32::GetWindowTextW(
     handle as winapi::HWND, 
     v.as_mut_ptr(), 
     v.capacity(), 
    ); 
    v.set_len(read_len); // this is undefined behavior if read_len > v.capacity() 
    String::from_utf16_lossy(&v) 
} 

Là một phụ lưu ý, đó là thành ngữ trong Rust không sử dụng return trên báo cáo kết quả cuối cùng trong một hàm, nhưng chỉ đơn giản là để cho các biểu hiện đứng ở đó mà không có một dấu chấm phẩy. Trong mã ban đầu của bạn, trận chung kết nếu biểu hiện có thể được viết như

if read_len > 0 { 
    String::from_utf16_lossy(Vec::from_raw_parts(p, read_len as usize, cap).as_slice()) 
} else { 
    "".to_string() 
} 

nhưng tôi loại bỏ toàn bộ điều kiện từ các mẫu của tôi, vì nó là không cần thiết để xử lý 0 ký tự đọc khác so với n ký tự.

+0

Câu trả lời hay. Cảm ơn bạn rất nhiều. Tôi vừa hiểu lầm một số phần của tài liệu. Btw, mã của tôi đang tạo rò rỉ bộ nhớ trong phiên bản trình biên dịch 32 bit. Khi tôi nghĩ rằng nó có 'mem :: forget (v);' – Cherrionella

+0

Mã của bạn chỉ tạo ra một rò rỉ bộ nhớ nếu 'read_len' là' 0', bởi vì sau đó bạn không bao giờ lấy lại quyền kiểm soát của 'Vec'. –

Các vấn đề liên quan