How to create char s[] in rust for passing to a cdll

Dear community,

I am trying to write a simple program to use a function called GFunc from a Windows 32-Bit CDLL created by LabVIEW. The header file looks like this:

#include "extcode.h"
#pragma pack(push)
#pragma pack(1)

#ifdef __cplusplus
extern "C" {
#endif

/*!
 * GFunc
 */
void __cdecl GFunc(
    char csvResultPath[], char tdmsFilePath[], double value1, double value2);

MgErr __cdecl LVDLLStatus(char *errStr, int errStrLen, void *module);

void __cdecl SetExecuteVIsInPrivateExecutionSystem(Bool32 value);

#ifdef __cplusplus
} // extern "C"
#endif

#pragma pack(pop)

The code I have so far for testing looks like this:

extern crate libloading;

use libloading::{Library, Symbol};
use std::ffi::CStr;
use std::ffi::CString;
use std::os::raw::c_char;
use std::os::raw::c_double;


fn string_prep(in_path: &str) -> [i8; 261] {
    let pat = CString::new(in_path).expect("CString::new failed");
    let mut output_dir = [0; 261];
    let pat_bytes = pat.as_bytes_with_nul();
    output_dir
        .get_mut(..pat_bytes.len())
        .expect("pat is longer than output_dir buffer")
        .iter_mut()
        .zip(pat_bytes.iter())
        .for_each(|(dest, src)| *dest = *src as c_char);
    output_dir

}

fn main() {
    // Load the DLL
    let path = "../builds/3D Sensor- 1 Informationen - DLL -Test/3d-sensor-3info-dll/My DLL/SharedLib.dll";
    let lib = unsafe { Library::new(path).expect("Failed to load DLL") };

    // Define the function signature
    type AuswertungssoftwareFunc = extern "C" fn(
        c_char,   // char csvResultPath[]
        c_char,   // char tdmsFilePath[]
        c_double, // double value1
        c_double, // double value2
    );

    // Load the function from the DLL
    let func: Symbol<GFunc> = unsafe {
        lib.get(b"GFunc")
            .expect("Function not found")
    };

    // Prepare function inputs
    let csv_result_path = string_prep("./data/a.csv");
    let tdms_file_path = string_prep("./data/a.tdms");

    // Call the function with the prepared values
    func(
        csv_result_path,
        tdms_file_path,
        22000.0,
        3000.0,
    );

    // Unload the DLL
    drop(lib);
}

However after compiling the application with the correct targe i686-pc-windows-msvc, the dll crashes and shows an error message. I am assuming, that this is because I am not passing the function arguments char csvResultPath[], and char tdmsFilePath[] correctly. Any ideas on what the problem is and how I can fix it? Thanks in advance for any help or advices!

char foo[] arguments in C should be equivalent to char* foo arguments, so you should be passing a pointer but you're passing a single char

Thanks for the quick reply!

would changing c_char in the function call to *const i8 and adding .as_ptr() to the csv_result_path and tdms_file_path pass a correct pointer to the C DLL?

The function signature

void __cdecl GFunc(
    char csvResultPath[], char tdmsFilePath[], double value1, double value2);

corresponds to mutable pointers

csvResultPath: *mut std::ffi::c_char
tdmsFilePath: *mut std::ffi::c_char

Unless you're definitely absolutely sure that the the called function won't write into the provided pointer, I suggest not deviating from the provided signature and using *mut _. If you declare the function with *const _ parameter, then you will be able to pass &arg as that parameter. If the called function tries to write into the pointed-to buffer, you'll get Undefined Behaviour.

If the function really writes something into the pointer, make sure that your buffer has the expected format (probably should be null-terminated) and has sufficient size. It's possible that there is a constant specified somewhere, which denotes the expected buffer size for each parameter. Check the documentation.

Also note that the canonical way to use char in Rust is to use the std::ffi::c_char alias. It is guaranteed to be equal to char on all platforms. If your assumption that c_char == i8 is ever invalid, you'll get a proper compile error instead of a bug or UB.