Search code examples
windowsffiponyponylang

PonyLang Windows CreateProcess FFI


I've been trying to call Window's CreateProcessA from Pony Language's FFI.

I created both a C and a PonyLang example. The C example works great:

#include <windows.h>
#include <stdio.h>
#include <tchar.h>

void wmain(void) {
    STARTUPINFO info={0};
    PROCESS_INFORMATION processInfo={0};

    CreateProcessA("calc.exe", 0, 0, 0, 0, 0, 0, 0, &info, &processInfo);

    if (status == 0)
        printf("%d",GetLastError()); // never hits

}

I put calc.exe in the current directory. This works flawlessly on Windows. However, my PonyLang implementation keeps on returning a non zero GetLastError:

use "lib:kernel32"

primitive _ProcessAttributes
primitive _ThreadAttributes
primitive _Inherit
primitive _Creation
primitive _Environment
primitive _CurrentDir
primitive _StartupInfo
primitive _ProcessInfo

primitive _HandleIn
primitive _HandleOut
primitive _HandleErr

primitive _Thread
primitive _Process

struct StartupInfo 
    var cb:I32 = 0
    var lpReserved:Pointer[U8] tag= "".cstring()
    var lpDesktop:Pointer[U8] tag= "".cstring()
    var lpTitle:Pointer[U8] tag= "".cstring()
    var dwX:I32 = 0
    var dwY:I32 = 0
    var dwXSize:I32=0
    var dwYSize:I32=0
    var dwXCountChars:I32=0
    var dwYCountChars:I32=0
    var dwFillAttribute:I32=0
    var dwFlags:I32=0
    var wShowWindow:I16=0
    var cbReserved2:I16=0
    var lpReserved2:Pointer[U8] tag="".cstring()
    var hStdInput:Pointer[_HandleIn] = Pointer[_HandleIn]
    var hStdOutput:Pointer[_HandleOut]= Pointer[_HandleOut]
    var hStdError:Pointer[_HandleErr]= Pointer[_HandleErr]

struct ProcessInfo
    var hProcess:Pointer[_Process] = Pointer[_Process]
    var hThread:Pointer[_Thread] = Pointer[_Thread]
    var dwProcessId:I32 = 0
    var dwThreadId:I32 = 0


//var si:StartupInfo = StartupInfo

actor Main
  new create(env: Env) =>
    var si: StartupInfo = StartupInfo
    var pi: ProcessInfo = ProcessInfo
    var inherit:I8 = 0
    var creation:I32 = 0
    var one:I32 = 0
    var two:I32 = 0
    var three:I32 = 0
    var four:I32 = 0
    var z:I32 = 0

    var p = @CreateProcessA[I8]("calc.exe",
    z,
    one,
    two,
    inherit,
    creation,
    three,
    four,
    addressof si,
    addressof pi)

    if p == 0 then
        var err = @GetLastError[I32]() // hits this every time.
        env.out.print("Last Error: " + err.string())
    end

So the above code compiles for PonyLang, but GetLastError most of the time returns 2. Sometimes GetLastError returns 123. Other times it returns 998? It all seems odd that the error code is different sometimes. Those codes all mean that there is some issue with file access?

Calc.exe is in the current directory (same directory as the c example).

Also not only is the Error code different but calc.exe is executed(runs fine) in the C version but not in the PonyLang version. This leads me to believe something is off with my PonyLang ffi setup.

Does anyone know what may be wrong?


Solution

  • The problem is with your use of addressof. When you create a struct object, e.g. with var si = StartupInfo, the underlying type is a pointer to the structure (i.e structs in Pony don't have value semantics). Then when you call CreateProcessA with addressof, you're actually passing a pointer to pointer to the function.

    If your C function expects a pointer to a structure, you can simply pass the Pony object without addressof when doing the FFI call.