매개 - c# string parameter



Rust에서 C#으로 구조체의 배열을 반환하는 방법 (1)

C / C ++와 C #을 상호 운용하는 대안을 찾으려고 노력하면서 녹을 배우고 있습니다.

아래의 C 코드와 같이 녹 코드를 작성하는 방법은 무엇입니까? 지금까지 마샬링 옵션이없는 내 녹 코드입니다.

pub struct PackChar { id: u32, val_str: String, }

#[no_mangle]
pub extern fn get_packs_char(size: u32) -> Vec<PackChar> {

    let mut out_vec = Vec::new();

    for i in 0 .. size {
        let int_0 = '0' as u32;
        let last_char_val = int_0 + i % (126 - int_0);
        let last_char = char::from_u32(last_char_val).unwrap();
        let buffer = format!("abcdefgHi{}", last_char);

        let pack_char = PackChar {
            id: i,
            val_str: buffer,
        };

        out_vec.push(pack_char);
    }

    out_vec
}

위 코드는 다음과 같이 상호 운용 할 수있는 다음 C 코드를 재현하려고 시도합니다.

void GetPacksChar(int size, PackChar** DpArrPnt)
{
    int TmpStrSize = 10;
    *DpArrPnt = (PackChar*)CoTaskMemAlloc( size * sizeof(PackChar));
    PackChar* CurPackPnt = *DpArrPnt;
    char dummyString[]= "abcdefgHij";
    for (int i = 0; i < size; i++,CurPackPnt++)
    {
        dummyString[TmpStrSize-1] = '0' + i % (126 - '0');
        CurPackPnt->IntVal = i;
        CurPackPnt->buffer = strdup(dummyString);
    }
}

이 C 코드는 다음과 같이 C #에서 DLL 가져 오기를 통해 액세스 할 수 있습니다.

[Dllimport("DllPath", CallingConvention = CallingConvention.Cdecl)]
public static extern void GetPacksChar(uint length, PackChar** ArrayStructs)

PackChar* MyPacksChar;
GetPacksChar(10, &MyPacksChar);
PackChar* CurrentPack = MyPacksChar;
var contLst = new List<PackChar>();
for (uint i = 0; i < ArrL; i++, CurrentPack++)
    contlist.Add(new PackChar() {
        IntVal = CurrentPack->IntVal, buffer = contLst->buffer
    });

https://src-bin.com


Answer #1

Rust 코드가 충족시켜야하는 다양한 요구 사항을 설명합니다.

  1. DLL은 올바른 이름의 GetPacksChar 함수를 노출해야합니다. 이것은 C #에서 GetPacksChar 라는 이름으로 선언하고 이름이 일치해야하기 때문입니다.
  2. 이 함수는 올바른 호출 규칙 ( extern "C" 합니다. 이는 Rust의 extern "C" 호출 규칙과 일치하는 C #에서 CallingConvention = CallingConvention.Cdecl 과 같은 함수를 선언하기 때문입니다.
  3. 함수에는 올바른 서명이 필요합니다.이 경우에는 Rust를 uintPackChar** 와 동일하게 가져오고 아무 것도 반환하지 않습니다. 이것은 함수의 서명 fn (u32, *mut *mut PackChar) 와 일치합니다.
  4. PackChar 의 선언은 C #과 Rust 사이에서 일치해야합니다. 나는 이것을 아래에서 살펴볼 것이다.
  5. 이 함수는 원래 C 함수의 동작을 복제해야합니다. 나는 이것을 아래에서 살펴볼 것이다.

가장 쉬운 부분은 Rust에서 함수를 선언하는 것입니다.

#[no_mangle]
pub extern "C" fn GetPacksChar(length: u32, array_ptr: *mut *mut PackChar) {}

다음으로 PackChar 를 처리해야합니다. C # 코드에서 사용 된 방법에 따라 선언해야하는 것처럼 보입니다.

#[repr(C)]
pub struct PackChar {
    pub IntVal: i32,
    pub buffer: *mut u8,
}

이것을 깨고, #[repr(C)] 는 Rust 컴파일러에게 C 컴파일러와 동일한 방식으로 PackChar 를 배열하도록 지시합니다. C #에서 C로 호출한다는 사실을 IntVal 때문에 중요합니다. IntValbuffer 는 모두에서 사용됩니다. C # 및 원래 C 버전. IntVal 은 C 버전에서 int 로 선언되므로 Rust 버전에서 i32 를 사용하고 buffer 는 C에서 바이트 배열로 처리되므로 Rust에서 *mut u8 을 사용합니다.

C #의 PackChar 정의는 C / Rust의 선언과 일치해야합니다. 따라서 :

public struct PackChar {
    public int IntVal;
    public char* buffer;
}

이제 남은 것은 Rust의 C 함수의 원래 동작을 재현하는 것입니다.

#[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');
    }
}

위의 중요한 사항 :

  • null 종결 문자 ( \0 )는 C 문자열이기 때문에 DUMMY_STR 에 수동으로 포함시켜야합니다.
  • 우리는 CoTaskMemAlloc()strdup() 두 C 함수를 호출합니다. strdup()libc 상자 에 있으며, 아마도 ole32-sys 크레이트 에서 찾을 수 있습니다.
  • 이 함수는 C 함수를 호출하고 str::from_raw_parts() 와 같이 여러 가지 안전하지 않은 작업을해야하기 때문에 unsafe 것으로 선언됩니다.

희망이 도움이됩니다!





dynamic-arrays