Ответ 1
Позвольте разбить это на различные требования, необходимые вашему коду ржавчины:
- DLL должна выставить функцию с правильным именем
GetPacksChar
. Это связано с тем, что вы объявляете его с именемGetPacksChar
из С#, и имена должны совпадать. - Функция нуждается в правильном соглашении вызова, в данном случае
extern "C"
. Это связано с тем, что вы объявляете функцию какCallingConvention = CallingConvention.Cdecl
из С#, которая соответствует соглашению вызоваextern "C"
в Rust. - Функция нуждается в правильной сигнатуре, в этом случае принимает эквивалент Rust a
uint
и aPackChar**
и ничего не возвращает. Это соответствует сигнатуре функцииfn (u32, *mut *mut PackChar)
. - Объявление
PackChar
должно совпадать между С# и Rust. Я рассмотрю это ниже. - Функция должна воспроизводить поведение исходной функции C. Я рассмотрю это ниже.
Простейшая часть будет объявлять функцию в Rust:
#[no_mangle]
pub extern "C" fn GetPacksChar(length: u32, array_ptr: *mut *mut PackChar) {}
Далее нам нужно обратиться к PackChar
. Основываясь на том, как он используется в коде С#, похоже, что он должен быть объявлен:
#[repr(C)]
pub struct PackChar {
pub IntVal: i32,
pub buffer: *mut u8,
}
Прерывая это, #[repr(C)]
сообщает компилятору Rust организовать PackChar
в памяти так же, как компилятор C, что важно, поскольку вы сообщаете С#, что он вызывает C. IntVal
и buffer
оба используются с С# и исходной версией C. IntVal
объявлен как int
в версии C, поэтому мы используем i32
в версии Rust, а buffer
рассматривается как массив байтов в C, поэтому мы используем *mut u8
в Rust.
Обратите внимание, что определение PackChar
в С# должно соответствовать объявлению в C/Rust, поэтому:
public struct PackChar {
public int IntVal;
public char* buffer;
}
Теперь все, что осталось, - воспроизвести исходное поведение функции C в Rust:
#[no_mangle]
pub extern "C" fn GetPacksChar(len: u32, array_ptr: *const *mut PackChar) {
static DUMMY_STR: &'static [u8] = b"abcdefgHij\0";
// Allocate space for an array of `len` `PackChar` objects.
let bytes_to_alloc = len * mem::size_of::<PackChar>();
*array_ptr = CoTaskMemAlloc(bytes_to_alloc) as *mut PackChar;
// Convert the raw array of `PackChar` objects into a Rust slice and
// initialize each element of the array.
let mut array = slice::from_raw_parts(len as usize, *array_ptr);
for (index, pack_char) in array.iter_mut().enumerate() {
pack_char.IntVal = index;
pack_char.buffer = strdup(DUMMY_STR as ptr);
pack_char.buffer[DUMMY_STR.len() - 1] = b'0' + index % (126 - b'0');
}
}
Важные моменты из вышеперечисленного:
- Мы должны вручную включить нулевой завершающий символ (
\0
) вDUMMY_STR
, поскольку он должен быть строкой C. - Назовем
CoTaskMemAlloc()
иstrdup()
, которые являются обе C-функциями.strdup()
находится в libc crate, и вы, вероятно, можете найти в ole32-sys ящик. - Функция объявляется как
unsafe
, потому что нам нужно делать несколько небезопасных вещей, например, вызывать функции C и делатьstr::from_raw_parts()
.
Надеюсь, что это поможет!