Its pretty much only sockets you could use, or mmap some shared memory and acces it from a NIF or port driver. But shared memory is not very good for fault isolation.
If its possible to start your external program as port this would bring you the advantage of automatic restarts when the owner process is supervised.
One other possibility is using a C-Node or in your case maybe a C# Node, I.e. speaking Erlangs distribution protocol from programs in different languages.