[vbox-dev] COM API ProcessCreate and GuestProcess.Write

Ivo Smits Ivo at UFO-Net.nl
Sat Jun 1 21:18:47 GMT 2013


After hours of experimenting (why!?!?!?), I think the problem is 
somewhere in the IDispatch implementation. The Interop assembly 
generated by VS2008 appears to use te IDispatch interface to call 
VirtualBox functions. I think the Interop assembly expects an array of 
unsigned bytes on the .Net side, while VirtualBox uses signed bytes...

So, after some crashes of both my own code and the VirtualBox VM, I came 
up with the attached C# code:
- Standard output works, although it is ugly and unreliable with the 
timed wait
- Standard input hangs the VM process. Be careful with this, you may 
have to kill the running VM! If VBox hangs here, you may still shut down 
the guest OS via remote desktop, and VBox will get stuck in the Stopping 
state. Bug report?

Ivo

Op 31-5-2013 13:05, Magnus Madsen schreef:
> LeeEll <info at ...> writes:
>
>> I have change the code so i execute ipconfig directly (not via cmd.exe)
>> but i can't get the output from it.
>>
>> I don't understand why this happens. The output type for Read method is
>> System.Array.
>>
>> I really need help with this!!!!!!!!
>>
> Hi LeeEll,
> Are you still having issues with this problem?
>
> I've been working on utilizing the VirtualBox interface through .NET,
> and was not able to find much information on it.
>
> Please let me know if I should elaborate, but I can let you know that it
> is,
> to my knowledge, simply not possible to directly access those functions in
> the
> VirtualBox API that return binary data as an array from .NET.
> I am not entirely sure why, but the SafeArray type returned cannot be
> marshalled correctly.
>
> What I ended up doing was writing a simple wrapper in unmanaged C++ to
> create
> a new "normal" SafeArray of bytes and returning that to my .NET application.
>
> Using the wrapper I was able to successfully return output data from a
> process
> I've created from the host. I haven't tried sending input to a process, but
> looking at IProcess::write it requires the same kind of parameter:
>
> IProcess::write(
> [in] unsigned long handle,
> [in] unsigned long flags,
> [in] octet data[],
> [in] unsigned long timeoutMS)
>
> Therefore it is probably also not possible to send data to a process
> without
> using a wrapper.
> Let me know if you want the source for the wrapper; it's quite simple.
>
> Kind regards,
> Magnus
>
>
> _______________________________________________
> vbox-dev mailing list
> vbox-dev at virtualbox.org
> https://www.virtualbox.org/mailman/listinfo/vbox-dev
>

-------------- next part --------------
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using VirtualBox;

namespace ConsoleApplication1 {
	[Guid("DFA39A36-5D43-4840-A025-67EA956B3111")]
	[TypeLibType(4160)]
	[InterfaceType(ComInterfaceType.InterfaceIsDual)]
	interface IProcessA {
		Array Arguments { get; }
		Array Environment { get; }
		string ExecutablePath { get; }
		int ExitCode { get; }
		string Name { get; }
		uint PID { get; }
		ProcessStatus Status { get; }
		ProcessWaitResult WaitFor(uint aWaitFor, uint aTimeoutMS);
		ProcessWaitResult WaitForArray(Array aWaitFor, uint aTimeoutMS);
		[return: MarshalAs(UnmanagedType.SafeArray, SafeArraySubType=VarEnum.VT_I1)]
		Array Read(uint aHandle, uint aToRead, uint aTimeoutMS);
		uint Write(uint aHandle, uint aFlags, [MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_I1)] Array aData, uint aTimeoutMS);
		uint WriteArray(uint aHandle, Array aFlags, Array aData, uint aTimeoutMS);
		void Terminate();
	}

	class Program {
		private static SByte ByteToSByteConverter(Byte b) { return (SByte)b; }
		unsafe static void Main(string[] args) {
			VirtualBoxClient vboxclient = new VirtualBoxClient();
			IMachine machine = vboxclient.VirtualBox.FindMachine("x");
			Session session = vboxclient.Session;
			try {
				machine.LockMachine(session, LockType.LockType_Shared);
				IGuestSession guestsession = session.Console.Guest.CreateSession("x", "x", "", "DotNet Test");
				try {
					//Call "cmd.exe /c echo hi" which will print "hi" to stdout
					IGuestProcess process = guestsession.ProcessCreate("cmd.exe", new String[] { "/c", "echo", "hi" }, new String[0], new ProcessCreateFlag[] { ProcessCreateFlag.ProcessCreateFlag_WaitForStdOut }, 0);
					//Call "cmd.exe", which should accept commands via stdin...
					//IGuestProcess process = guestsession.ProcessCreate("cmd.exe", new String[] { }, new String[0], new ProcessCreateFlag[] { ProcessCreateFlag.ProcessCreateFlag_WaitForStdOut }, 0);

					//Cast process handle to correct interface
					IProcessA processa = (IProcessA)process;
					//Wait for process to start
					process.WaitForArray(new ProcessWaitForFlag[] { ProcessWaitForFlag.ProcessWaitForFlag_Start }, 0);

					//Provide input via stdin
					//Warning! Using this may hang your VM process!!!
					//Byte[] bytesin = Encoding.UTF8.GetBytes("echo hi\r\nexit\r\n");
					//processa.Write(0, (uint)ProcessInputFlag.ProcessInputFlag_EndOfFile, Array.ConvertAll<Byte, SByte>(bytesin, ByteToSByteConverter), 0);

					//Wait for process to end
					process.WaitForArray(new ProcessWaitForFlag[] { ProcessWaitForFlag.ProcessWaitForFlag_Terminate }, 500);

					//Read output from stdout
					Array bytes = processa.Read(1, 64 * 1024, 0);
					Console.WriteLine(bytes.Length);
					Console.WriteLine(Encoding.UTF8.GetString((Byte[])bytes));
				} finally {
					guestsession.Close();
				}
			} finally {
				if (session.State == SessionState.SessionState_Locked) session.UnlockMachine();
			}
			Console.ReadLine();
		}
	}
}


More information about the vbox-dev mailing list