Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Corrode does not generate valid argument handling for Windows #140

Open
Marwes opened this issue Apr 11, 2017 · 3 comments
Open

Corrode does not generate valid argument handling for Windows #140

Marwes opened this issue Apr 11, 2017 · 3 comments

Comments

@Marwes
Copy link
Contributor

Marwes commented Apr 11, 2017

The main function which corrode generates uses ::std::os::unix::ffi::OsStringExt which does not exist on Windows. The easy way of doing this for Windows would probably be to just pass the UTF-8 encoded arguments to the c main functions which probably works in a lot of cases but will break silently in others. Doing this properly would involve linking against https://crates.io/crates/winapi and converting the OsString received into the current codepage which I believe will give the same behaviour as the C program http://stackoverflow.com/questions/5408730/what-is-the-encoding-of-argv (though it may obviously be lossy).

Potentially it could be useful to detect and generate a wmain function in the c source as well and call into that if it exists.

(I'd link directly to the code but unfortunately that is not possible in markdown files! It's in this section
https://github.com/jameysharp/corrode/blob/e0ad8a9f0bee57e8ad87faf7df1964dae7fe7eff/src/Language/Rust/Corrode/C.md#special-case-translation-for-main)

@Marwes Marwes changed the title Corrode does not generate argv for Windows Corrode does not generate valid argument handling for Windows Apr 11, 2017
@jameysharp
Copy link
Owner

True! I wasn't very happy with the amount of OS-specific stuff I did in translating main, but it was a start. (Handling the POSIX-permitted third argument, for the environment variables, is even worse. But maybe that isn't expected to work on Windows anyway?)

So I guess the way forward here is to guard the current definition of main with #[cfg(unix)], and provide the new version guarded with #[cfg(windows)], right?

I think maybe it's worth introducing a crate of helpers for Corrode-translated programs, as various people have suggested to me, just to place the main wrappers in it. I've resisted doing that for anything where I think doing so would make it harder to improve the generated Rust to be more idiomatic, but I don't think that applies here, and generating a pile of effectively constant code is pretty awkward. (Idea: the crate could be called "catalyst".)

I don't know anything about wmain. Do you have a link to documentation or a specification for such functions?

@Marwes
Copy link
Contributor Author

Marwes commented Apr 11, 2017

Handling the POSIX-permitted third argument, for the environment variables, is even worse. But maybe that isn't expected to work on Windows anyway

Were going to say that envp won't work on windows but it turns out that it actually exists there as well.

So I guess the way forward here is to guard the current definition of main with #[cfg(unix)], and provide the new version guarded with #[cfg(windows)], right?

Yep.

I don't know anything about wmain. Do you have a link to documentation or a specification for such functions?

Not really, it is basically main except it takes wchar_t** https://msdn.microsoft.com/en-us/library/bky3b5dh.aspx?f=255&MSPPError=-2147217396

@Marwes
Copy link
Contributor Author

Marwes commented Apr 16, 2017

This is what I have come up with so far which is to just convert the UTF-8 encoded args in rust into wide strings (UCS-2) and then back to the current codepage. If anything fails to convert it simply panics since I haven't been able to figure out what the behaviour should be for that case (and I don't want to spend much time on it either since it is a case where the program is likely broken anyway).

Question is, how should this be embedded in the generated corrode sources? Due to its size I'd rather not encode this in AST form as the unix variant. Adding a variant to the Item ast node as VerbatimItem String might work ? But then we still need a way to embed this string into the Haskell source which seems awkward since Haskell don't have raw strings or a way to include external files into the source file.

#[cfg(windows)]
extern crate winapi;
#[cfg(windows)]
extern crate kernel32;

#[cfg(windows)]
fn main() {
    use std::ptr;

    use winapi::c_int;
    use winapi::winnt::CHAR;
    use winapi::winnls::{CP_ACP, CP_UTF8};
    use kernel32::{WideCharToMultiByte, MultiByteToWideChar};
    unsafe {
        let mut args: Vec<_> = ::std::env::args()
            .map(|s| {
                let size = MultiByteToWideChar(CP_UTF8,
                                               0,
                                               s.as_ptr() as *const CHAR,
                                               s.len() as c_int,
                                               ptr::null_mut(),
                                               0);
                if size == 0 {
                    panic!("MultiByteToWideChar");
                }
                let mut w_string = Vec::with_capacity(size as usize);
                MultiByteToWideChar(CP_UTF8,
                                    0,
                                    s.as_ptr() as *const CHAR,
                                    s.len() as c_int,
                                    w_string.as_mut_ptr(),
                                    size);
                w_string.set_len(size as usize);

                let size2 = WideCharToMultiByte(CP_ACP,
                                                0,
                                                w_string.as_ptr(),
                                                w_string.len() as c_int,
                                                ptr::null_mut(),
                                                0,
                                                ptr::null(),
                                                ptr::null_mut());
                if size2 == 0 {
                    panic!("WideCharToMultiByte");
                }
                let mut codepage_string = Vec::with_capacity(size2 as usize);
                WideCharToMultiByte(CP_ACP,
                                    0,
                                    w_string.as_ptr(),
                                    w_string.len() as c_int,
                                    codepage_string.as_mut_ptr(),
                                    size2,
                                    ptr::null(),
                                    ptr::null_mut());
                codepage_string
            })
            .collect();

        let mut argv: Vec<_> = args.iter_mut().map(|arg| arg.as_mut_ptr()).collect();
        // _c_main(argv.len() as c_int, argv.as_mut_ptr());
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants