es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Comunicación IPC entre Node y .NET Core de plataforma cruzada utilizando descriptores de archivos / tuberías

Tengo una aplicación de terceros en Node que utiliza tuberías entre su proceso hijo y el proceso padre para la comunicación. Utiliza los descriptores de archivo subprocess.stdio[3] y subprocess.stdio[4], en lugar de la entrada y salida estándar. Documentación relevante: https://nodejs.org/api/child_process.html#subprocessstdio

Creé una aplicación de .NET Core que se lanzó como el proceso hijo de la aplicación Node. La comunicación funciona bien en Windows. Se basa en el siguiente código de respuesta de SO: SO answer:

internal class Comm : IDisposable
{
    [DllImport("MSVCRT.DLL", CallingConvention = CallingConvention.Cdecl)]
    static extern IntPtr _get_osfhandle(int fd);

    public readonly Stream Stream;

    public Comm(int fd)
    {
        var handle = _get_osfhandle(fd);
        if (handle == IntPtr.Zero || handle == (IntPtr)(-1) || handle == (IntPtr)(-2))
        {
            throw new ApplicationException("handle inválido");
        }

        var fileHandle = new SafeFileHandle(handle, true);
        Stream = new FileStream(fileHandle, FileAccess.ReadWrite);
    }

    public void Dispose()
    {
        Stream.Dispose();
    }       
}

Dado que este código se basa en _get_osfhandle de MSVCRT.DLL, ¿cómo puedo hacer que esta clase sea multiplataforma para que funcione en Windows, Linux y macOS? ¿Qué función debería importar en lugar de _get_osfhandle?

Tags:  , , , ,

Answer

  1. Avatar for davy.ai

    Para que esta clase sea compatible con varias plataformas, la función _get_osfhandle de la biblioteca MSVCRT.DLL debe ser reemplazada por funciones específicas de cada plataforma.

    En Linux, se puede utilizar la función dup para duplicar un descriptor de archivo, seguida de la función fcntl para recuperar las banderas del descriptor de archivo y establecer la bandera FD_CLOEXEC. El descriptor de archivo resultante se puede utilizar para crear un FileStream.

    En macOS, se puede utilizar la función fcntl con el comando F_DUPFD_CLOEXEC para duplicar un descriptor de archivo con la bandera FD_CLOEXEC establecida, seguida de la creación de un FileStream con el descriptor de archivo resultante.

    Aquí tienes una implementación de ejemplo que debería funcionar en Windows, Linux y macOS:

    internal class Comm : IDisposable
    {
        public readonly Stream Stream;
    
        public Comm(int fd)
        {
            var handle = GetHandle(fd);
            var fileHandle = new SafeFileHandle(handle, true);
            Stream = new FileStream(fileHandle, FileAccess.ReadWrite);
        }
    
        private static IntPtr GetHandle(int fd)
        {
            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            {
                return GetWindowsHandle(fd);
            }
            else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
            {
                return GetLinuxHandle(fd);
            }
            else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
            {
                return GetMacHandle(fd);
            }
            else
            {
                throw new PlatformNotSupportedException();
            }
        }
    
        private static IntPtr GetWindowsHandle(int fd)
        {
            return _get_osfhandle(fd);
        }
    
        private static IntPtr GetLinuxHandle(int fd)
        {
            int new_fd = dup(fd);
            if (new_fd == -1)
            {
                throw new SystemException("dup failed");
            }
    
            int flags = fcntl(new_fd, F_GETFD);
            if (flags == -1)
            {
                throw new SystemException("fcntl(F_GETFD) failed");
            }
    
            if (fcntl(new_fd, F_SETFD, flags | FD_CLOEXEC) == -1)
            {
                throw new SystemException("fcntl(F_SETFD) failed");
            }
    
            return (IntPtr)new_fd;
        }
    
        private static IntPtr GetMacHandle(int fd)
        {
            int new_fd = fcntl(fd, F_DUPFD_CLOEXEC, 0);
            if (new_fd == -1)
            {
                throw new SystemException("fcntl(F_DUPFD_CLOEXEC) failed");
            }
    
            return (IntPtr)new_fd;
        }
    
        public void Dispose()
        {
            Stream.Dispose();
        }
    
        // Definiciones de P/Invoke
        private const int FD_CLOEXEC = 1;
        private const int F_GETFD = 1;
        private const int F_SETFD = 2;
    
        [DllImport("MSVCRT.DLL", CallingConvention = CallingConvention.Cdecl)]
        private static extern IntPtr _get_osfhandle(int fd);
    
        [DllImport("c")]
        private static extern int dup(int oldfd);
    
        [DllImport("c")]
        private static extern int fcntl(int fd, int cmd);
    
        [DllImport("c")]
        private static extern int fcntl(int fd, int cmd, int arg);
    }
    

Comments are closed.