Latest wisdom? CreateProcess Stdin Stdout, etc

S

Starbuck

I understand from postings by Paul Tobey and others that:
1) With CF2 we cannot use System.Diagnostics.Process to get a handle
to stdin and stdout.
2) OpenNETCF SDF no longer supports a Process class.
3) We need to use GetStdioPathW, SetStdPathW, CreateProcess, etc

I'm writing new code in C# 2.0 / VS2005 for CF2, where the goal is to
start a console .exe application and completely control the flow
to/from stdin/stdout. (And of course manage stderr if possible.) The
child process should run in a second thread to avoid deadlock issues
and to facilitate brutal process termination if required.

There are so many code segments available on this topic but each one
has problems - like failure to properly declare functions like
CreateProcess, or maybe incorrect usage of in/out values to that
function. I'd like to define once and for all exactly what is required
to accomplish this task, and I'll be happy to publish the entire
solution as open source when it's working. If publishing threatens the
IP of someone who generously provides information here (a constant
issue I face) then please let me know how we can work this out so that
the information can be made available to those who can use it without
impeding your ability to make a living.

While I'm coding in C#, I can easily translate VB.NET or just
reference a VB.NET DLL for the process interaction. I've seen some
C++ code on this but nothing that I've been able to translate to
functional C#. So C# is preferred, but since I'm trying to learn how
it all works, any language providing working functionality of various
aspects of this will be welcome.

Where am I getting stuck? There are subtle differences in the way
people are setting the SECURITY_ATTRIBUTES struct and STARTUPINFO
(which personally I prefer to refer to as STARTUP_INFO but the
standard seems to be without the underscore). For example, some
people create a memory pointer for STARTUPINFO and others use
IntPtr.Zero for that value when calling CreatePipe. Some people use
SetHandleInformation to make sure a pipe isn't inherited, some don't.

Following the "teach a man to fish..." saying, I'm not looking for
"fish" and I'm quite willing to learn how things work, but doing this
entirely on my own is taking way too long. I need help, I'd like to
rely on colleagues here to guide the process, and if anyone can help,
I'd like to contribute this aspect of my good fortune back to the
community.

So before I start asking code questions in this thread, my first
questions are these:

1) Is this any easier in CF 3.5 or VS 2008? I'm still coding for
PPC2003 at the moment but I can set a minimum supported WinCE level if
required.

2) Is a complete solution for this already available somewhere? Even
an internal project that someone can provide (oops, asking for a fish)
that I can rip up (and learn in the process) and publish as a basic
skeleton?

3) Is there a better approach to learning how to create this specific
environment? A book that goes through this in detail?

Thank you very much!!
(Email welcome, disposable address will be changed as spam becomes a
problem)
 
C

Chris Tacke, eMVP

I recently had a question from a colleague about stdin/stdout as well, so
let's look at how we can solve this.

First, I'm looking at the docs for CreateProcess for the desktop:
http://msdn.microsoft.com/en-us/library/ms682425.aspx

I'd say that the lpStartupInfo parameter is exactly how you adjust the
stdin/stdout streams here.

Now look at the CreateProcess docs for CE:
http://msdn.microsoft.com/en-us/library/ms885182.aspx

"psiStartInfo: [in] Not supported; set to NULL"

That tells me that OpenNETCF, when we did have a ProcessInfo class, never
supported this. It also explains why the CF doesn't support it. The OS
itself doesn't support it. That also tells me that it's highly unlikely
that anyone else has solved and at published the solution.

So where does that leave us? For now completely stuck. That said, I'mnever
one to give up quickly, so I'll do a little reasearch under the Platform
Builder source code as soon as my other PC boots up and post back here. I
already have one potential idea that may work, but until I look at the OS
source, I'm not going to say yea or nay.


--

Chris Tacke, Embedded MVP
OpenNETCF Consulting
Giving back to the embedded community
http://community.OpenNETCF.com
 
P

Paul G. Tobey [eMVP]

Starbuck said:
I understand from postings by Paul Tobey and others that:
1) With CF2 we cannot use System.Diagnostics.Process to get a handle
to stdin and stdout.
2) OpenNETCF SDF no longer supports a Process class.
3) We need to use GetStdioPathW, SetStdPathW, CreateProcess, etc


1. You could always P/Invoke OpenProcess. All you need for that is the
process ID, which you have via Process.Id. It's dumb that they don't expose
Handle, as it's obviously in there, but what can you do?

Where am I getting stuck? There are subtle differences in the way
people are setting the SECURITY_ATTRIBUTES struct and STARTUPINFO
(which personally I prefer to refer to as STARTUP_INFO but the
standard seems to be without the underscore). For example, some
people create a memory pointer for STARTUPINFO and others use
IntPtr.Zero for that value when calling CreatePipe. Some people use
SetHandleInformation to make sure a pipe isn't inherited, some don't.

How SECURITY_ATTRIBUTES is declared makes zero difference. Windows CE
doesn't support setting it to anything other than NULL in C, so... The same
is true of STARTUPINFO. Must be NULL. This declaration should be fine for
any Windows CE program:

[DllImport("coredll.dll", SetLastError=true)]
public extern static bool CreateProcess(string pszImageName, string
pszCmdLine,
IntPtr psaProcess, IntPtr psaThread, int fInheritHandles, int
fdwCreate,
IntPtr pvEnvironment, IntPtr pszCurDir, IntPtr psiStartInfo,
ProcessInfo pi);

You must set psaProcess, psaThread, pvEnvironment, and psiStartInfo all to
IntPtr.Zero. You must also set fInheritHandles to false.
So before I start asking code questions in this thread, my first
questions are these:

1) Is this any easier in CF 3.5 or VS 2008? I'm still coding for
PPC2003 at the moment but I can set a minimum supported WinCE level if
required.

I'd say no, not really. Handle still isn't exposed.
2) Is a complete solution for this already available somewhere? Even
an internal project that someone can provide (oops, asking for a fish)
that I can rip up (and learn in the process) and publish as a basic
skeleton?

I'm not quite sure what you mean by a solution. For the whole thing,
including drivers for standard in/out/error? The source for OpenNETCF SDF
1.x should have the correct declarations for everything you need for a
suitable Process class of your own.
3) Is there a better approach to learning how to create this specific
environment? A book that goes through this in detail?

No, it's too complex. There's a sample of sorts, that comes with Platform
Builder/Windows CE itself. The Telnet server sample that you can build into
your OS acts as a parent application for the MS-DOS shell, capturing its
stdin, stdout, and stderr and sending them over the network to the Telnet
client. That should tell you what functions your driver (yes, you have to
write a driver, in C/C++), must have and generally shows how to call
SetStdioPathW(). I guess that, since you're determined, you could download
the evaluation version of Platform Builder for CE5 from
www.microsoft.com/downloads and look at the code. After you install it,
you'll find the Telnet sample in
\WINCE500\PUBLIC\SERVERS\SDK\SAMPLES\TELNETD. The readme, unlike most of
the readme files in the Windows CE source code, is actually pretty good.
You'll still have to figure out what gets compiled into a driver and what
gets compiled into an application/service because Windows CE builds
everything to libraries and then decides what to do with those in a separate
step, but you'll have some source, at least.

Paul T.
 
C

Chris Tacke, eMVP

Further investigation leads me to think that wile possible, this would be a
real challenge to implement.

The first path I went dow was to look at freopen (which is actually exported
from coredll as _wfreopen at ordinal 1201). The problem here is that you
have to redirect it to a file *name*. If we had the option to redirect to a
file handle, we'd be golden, but we're not. A kluge here would be to send
it to a file, then use a memory-mapped file to the same location from your
process to watch it, but that's an ugly hack and you'd get no events on
incoming data and would have to resort to polling. Wouldn't do jack for you
for stdin.

The next path I looked at was SetStdioPathW (which we already have wrapped
in the SDF under OpenNETCF.WindowsCE.DeviceManagement.SetStdioPath). Here
it wants a device driver path (like TEL1: for telnet, as Paul recommended).
The problem there is that we don't want a driver to handle it, we want our
app to do it. The workaround there is to write a custom driver that will
act as a "shim" and handle inbound and outbound data and you'd simple create
a StreamInterfaceDriver child class to handle comms to it, then wrap that
into your custom ProcessInfo implementation.

It would certainly work, but it's a hell of a lot of work (I'd guess it
would take me a solid week to put together a fairly solid native driver with
events, the managed code to sink those events and handle data marshaling,
and the new Process class that used it). The other issue is then you have
to deploy this driver for it all to work, and driver deployment isn't quite
like an app. It has to get deployed, registered and started, and if the
device has security enabled, then it gets real muddy, real fast.

So my prognosis is this: Is it possible? Certainly. Would I do it just for
fun? Maybe, but unlikely. First, I don't have a free week to do it, but
more importantly I don't see it as a technical challenge to myself and I
wouldn't learn a lot from the exercise. I can already see all of the steps
to it in my mind, so it's not much of a puzzle and therefore it's tough for
me to get excited or interested in doing it. Add to that the fact that I
just don't think there are that many people who would ever use or need it,
so I'd basically be doing the work for maybe half a dozen people (and that's
a big maybe).

I'd actually say this would be a fantastic senior thesis project for a
software student, as you would learn a *lot* from doing it.

Maybe you could convince Paul to do it, since I'm certain he can see the
end-to-end path for it too, but I'd bet he's got similar feelings bout the
cost versus utility and fun factor.
--

Chris Tacke, Embedded MVP
OpenNETCF Consulting
Giving back to the embedded community
http://community.OpenNETCF.com


Chris Tacke said:
I recently had a question from a colleague about stdin/stdout as well, so
let's look at how we can solve this.

First, I'm looking at the docs for CreateProcess for the desktop:
http://msdn.microsoft.com/en-us/library/ms682425.aspx

I'd say that the lpStartupInfo parameter is exactly how you adjust the
stdin/stdout streams here.

Now look at the CreateProcess docs for CE:
http://msdn.microsoft.com/en-us/library/ms885182.aspx

"psiStartInfo: [in] Not supported; set to NULL"

That tells me that OpenNETCF, when we did have a ProcessInfo class, never
supported this. It also explains why the CF doesn't support it. The OS
itself doesn't support it. That also tells me that it's highly unlikely
that anyone else has solved and at published the solution.

So where does that leave us? For now completely stuck. That said,
I'mnever one to give up quickly, so I'll do a little reasearch under the
Platform Builder source code as soon as my other PC boots up and post back
here. I already have one potential idea that may work, but until I look
at the OS source, I'm not going to say yea or nay.


--

Chris Tacke, Embedded MVP
OpenNETCF Consulting
Giving back to the embedded community
http://community.OpenNETCF.com




Starbuck said:
I understand from postings by Paul Tobey and others that:
1) With CF2 we cannot use System.Diagnostics.Process to get a handle
to stdin and stdout.
2) OpenNETCF SDF no longer supports a Process class.
3) We need to use GetStdioPathW, SetStdPathW, CreateProcess, etc

I'm writing new code in C# 2.0 / VS2005 for CF2, where the goal is to
start a console .exe application and completely control the flow
to/from stdin/stdout. (And of course manage stderr if possible.) The
child process should run in a second thread to avoid deadlock issues
and to facilitate brutal process termination if required.

There are so many code segments available on this topic but each one
has problems - like failure to properly declare functions like
CreateProcess, or maybe incorrect usage of in/out values to that
function. I'd like to define once and for all exactly what is required
to accomplish this task, and I'll be happy to publish the entire
solution as open source when it's working. If publishing threatens the
IP of someone who generously provides information here (a constant
issue I face) then please let me know how we can work this out so that
the information can be made available to those who can use it without
impeding your ability to make a living.

While I'm coding in C#, I can easily translate VB.NET or just
reference a VB.NET DLL for the process interaction. I've seen some
C++ code on this but nothing that I've been able to translate to
functional C#. So C# is preferred, but since I'm trying to learn how
it all works, any language providing working functionality of various
aspects of this will be welcome.

Where am I getting stuck? There are subtle differences in the way
people are setting the SECURITY_ATTRIBUTES struct and STARTUPINFO
(which personally I prefer to refer to as STARTUP_INFO but the
standard seems to be without the underscore). For example, some
people create a memory pointer for STARTUPINFO and others use
IntPtr.Zero for that value when calling CreatePipe. Some people use
SetHandleInformation to make sure a pipe isn't inherited, some don't.

Following the "teach a man to fish..." saying, I'm not looking for
"fish" and I'm quite willing to learn how things work, but doing this
entirely on my own is taking way too long. I need help, I'd like to
rely on colleagues here to guide the process, and if anyone can help,
I'd like to contribute this aspect of my good fortune back to the
community.

So before I start asking code questions in this thread, my first
questions are these:

1) Is this any easier in CF 3.5 or VS 2008? I'm still coding for
PPC2003 at the moment but I can set a minimum supported WinCE level if
required.

2) Is a complete solution for this already available somewhere? Even
an internal project that someone can provide (oops, asking for a fish)
that I can rip up (and learn in the process) and publish as a basic
skeleton?

3) Is there a better approach to learning how to create this specific
environment? A book that goes through this in detail?

Thank you very much!!
(Email welcome, disposable address will be changed as spam becomes a
problem)
 
P

Paul G. Tobey [eMVP]

BTW, here's a little process class that I threw together in 30 minutes or
so:

public class Process2
{
private ProcessInfo pi;

public IntPtr Handle
{
get
{
return pi.hProcess;
}
}
public int Id
{
get
{
return pi.dwProcessID;
}
}
public IntPtr ThreadHandle
{
get
{
return pi.hThread;
}
}
public int ThreadId
{
get
{
return pi.dwThreadID;
}
}

private Process2(string program, string arguments)
{
if (!CreateProcess(program, arguments, IntPtr.Zero, IntPtr.Zero, 0,
0, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, out pi))
{
}
}

~Process2()
{
// Close the process and thread handles, so we don't leak them.
if (pi.hThread != IntPtr.Zero)
{
CloseHandle(pi.hThread);
pi.hThread = IntPtr.Zero;
}
if (pi.hProcess != IntPtr.Zero)
{
CloseHandle(pi.hProcess);
pi.hProcess = IntPtr.Zero;
}
}

private class ProcessInfo
{
public IntPtr hProcess = IntPtr.Zero;
public IntPtr hThread = IntPtr.Zero;
public int dwProcessID = 0;
public int dwThreadID = 0;
}

// To start another process with different standard I/O settings, call
// SetStdioPathW to set each one for the current process, then call
// Process.Start to start the new process. It will use the same settings
// for standard I/O devices. You can then set the ones for the current
// process back to what they were before.
public static Process2 Start(string program, string arguments)
{
return new Process2(program, arguments);
}

public void Close()
{
// Release handles, but don't actually do anything to the process.
if (pi.hThread != IntPtr.Zero)
{
CloseHandle(pi.hThread);
pi.hThread = IntPtr.Zero;
}
if (pi.hProcess != IntPtr.Zero)
{
CloseHandle(pi.hProcess);
pi.hProcess = IntPtr.Zero;
}
}

public enum Stdio { stdin = 0, stdout = 1, stderr = 2 };

[DllImport("coredll.dll", SetLastError = true)]
private extern static bool CreateProcess(string pszImageName,
string pszCmdLine, IntPtr psaProcess, IntPtr psaThread,
int fInheritHandles, int fdwCreate,IntPtr pvEnvironment,
IntPtr pszCurDir, IntPtr psiStartInfo, out ProcessInfo pi);

[DllImport("coredll.dll", SetLastError = true)]
public extern static bool SetStdioPathW(Stdio id, string path);

public static bool GetStdioPathW(Stdio id, out string path)
{
byte[] b = new byte[20]; // Enough for 10 Unicode characters.
int blen = b.Length / 2; // Unicode. Could be done much
more-readably.
bool ret = GetStdioPathW_CE(id, b, ref blen);
// Create string. Note that, if there's no device associated with
id,
// you'll get an empty string back.
if (ret)
{
path = System.Text.ASCIIEncoding.Unicode.GetString(b, 0,
b.Length);
}
else
{
path = "";
}
return ret;
}

[DllImport("coredll.dll", EntryPoint="GetStdioPathW", SetLastError =
true)]
private extern static bool GetStdioPathW_CE(Stdio id, byte[] path, ref
int pathlen);

[DllImport("coredll.dll", SetLastError = true)]
private extern static bool CloseHandle(IntPtr h);
}

Obviously, it doesn't begin to be a full class, but I think that the
declarations for CreateProcess, SetStdioPathW, and GetStdioPathW should be
workable. You'd call it something like this:

res = Process2.GetStdioPathW(Process2.Stdio.stdin, out stdin);
res = Process2.SetStdioPathW(Process2.Stdio.stdin, "COM1:");
Process2 proc = Process2.Start("myprog.exe", "param1 param2 param3");
res = Process2.SetStdioPathW(Process2.Stdio.stdin, stdin);

That should start myprog.exe with the indicated parameters and with stdin
set to COM1:.

Paul T.

"Paul G. Tobey [eMVP]" <p space tobey no spam AT no instrument no spam DOT
com> wrote in message news:[email protected]...
Starbuck said:
I understand from postings by Paul Tobey and others that:
1) With CF2 we cannot use System.Diagnostics.Process to get a handle
to stdin and stdout.
2) OpenNETCF SDF no longer supports a Process class.
3) We need to use GetStdioPathW, SetStdPathW, CreateProcess, etc


1. You could always P/Invoke OpenProcess. All you need for that is the
process ID, which you have via Process.Id. It's dumb that they don't
expose Handle, as it's obviously in there, but what can you do?

Where am I getting stuck? There are subtle differences in the way
people are setting the SECURITY_ATTRIBUTES struct and STARTUPINFO
(which personally I prefer to refer to as STARTUP_INFO but the
standard seems to be without the underscore). For example, some
people create a memory pointer for STARTUPINFO and others use
IntPtr.Zero for that value when calling CreatePipe. Some people use
SetHandleInformation to make sure a pipe isn't inherited, some don't.

How SECURITY_ATTRIBUTES is declared makes zero difference. Windows CE
doesn't support setting it to anything other than NULL in C, so... The
same is true of STARTUPINFO. Must be NULL. This declaration should be
fine for any Windows CE program:

[DllImport("coredll.dll", SetLastError=true)]
public extern static bool CreateProcess(string pszImageName, string
pszCmdLine,
IntPtr psaProcess, IntPtr psaThread, int fInheritHandles, int
fdwCreate,
IntPtr pvEnvironment, IntPtr pszCurDir, IntPtr psiStartInfo,
ProcessInfo pi);

You must set psaProcess, psaThread, pvEnvironment, and psiStartInfo all to
IntPtr.Zero. You must also set fInheritHandles to false.
So before I start asking code questions in this thread, my first
questions are these:

1) Is this any easier in CF 3.5 or VS 2008? I'm still coding for
PPC2003 at the moment but I can set a minimum supported WinCE level if
required.

I'd say no, not really. Handle still isn't exposed.
2) Is a complete solution for this already available somewhere? Even
an internal project that someone can provide (oops, asking for a fish)
that I can rip up (and learn in the process) and publish as a basic
skeleton?

I'm not quite sure what you mean by a solution. For the whole thing,
including drivers for standard in/out/error? The source for OpenNETCF SDF
1.x should have the correct declarations for everything you need for a
suitable Process class of your own.
3) Is there a better approach to learning how to create this specific
environment? A book that goes through this in detail?

No, it's too complex. There's a sample of sorts, that comes with Platform
Builder/Windows CE itself. The Telnet server sample that you can build
into your OS acts as a parent application for the MS-DOS shell, capturing
its stdin, stdout, and stderr and sending them over the network to the
Telnet client. That should tell you what functions your driver (yes, you
have to write a driver, in C/C++), must have and generally shows how to
call SetStdioPathW(). I guess that, since you're determined, you could
download the evaluation version of Platform Builder for CE5 from
www.microsoft.com/downloads and look at the code. After you install it,
you'll find the Telnet sample in
\WINCE500\PUBLIC\SERVERS\SDK\SAMPLES\TELNETD. The readme, unlike most of
the readme files in the Windows CE source code, is actually pretty good.
You'll still have to figure out what gets compiled into a driver and what
gets compiled into an application/service because Windows CE builds
everything to libraries and then decides what to do with those in a
separate step, but you'll have some source, at least.

Paul T.
 
C

Chris Tacke, eMVP

The problem here is that it doesn't give the other end that he's after - the
access to stdin and stdout as a Stream. However it's really an interesting
idea to "hijack" a COM port and use it for this redirection. It certainly
would eliminate the need for deploying a custom driver (though it would
consume a serial port if you didn't) and the entire solution could be done
in managed code. That at least makes the problem more entertaining.


--

Chris Tacke, Embedded MVP
OpenNETCF Consulting
Giving back to the embedded community
http://community.OpenNETCF.com

"Paul G. Tobey [eMVP]" <p space tobey no spam AT no instrument no spam DOT
com> wrote in message news:[email protected]...
BTW, here's a little process class that I threw together in 30 minutes or
so:

public class Process2
{
private ProcessInfo pi;

public IntPtr Handle
{
get
{
return pi.hProcess;
}
}
public int Id
{
get
{
return pi.dwProcessID;
}
}
public IntPtr ThreadHandle
{
get
{
return pi.hThread;
}
}
public int ThreadId
{
get
{
return pi.dwThreadID;
}
}

private Process2(string program, string arguments)
{
if (!CreateProcess(program, arguments, IntPtr.Zero, IntPtr.Zero, 0,
0, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, out pi))
{
}
}

~Process2()
{
// Close the process and thread handles, so we don't leak them.
if (pi.hThread != IntPtr.Zero)
{
CloseHandle(pi.hThread);
pi.hThread = IntPtr.Zero;
}
if (pi.hProcess != IntPtr.Zero)
{
CloseHandle(pi.hProcess);
pi.hProcess = IntPtr.Zero;
}
}

private class ProcessInfo
{
public IntPtr hProcess = IntPtr.Zero;
public IntPtr hThread = IntPtr.Zero;
public int dwProcessID = 0;
public int dwThreadID = 0;
}

// To start another process with different standard I/O settings, call
// SetStdioPathW to set each one for the current process, then call
// Process.Start to start the new process. It will use the same
settings
// for standard I/O devices. You can then set the ones for the current
// process back to what they were before.
public static Process2 Start(string program, string arguments)
{
return new Process2(program, arguments);
}

public void Close()
{
// Release handles, but don't actually do anything to the process.
if (pi.hThread != IntPtr.Zero)
{
CloseHandle(pi.hThread);
pi.hThread = IntPtr.Zero;
}
if (pi.hProcess != IntPtr.Zero)
{
CloseHandle(pi.hProcess);
pi.hProcess = IntPtr.Zero;
}
}

public enum Stdio { stdin = 0, stdout = 1, stderr = 2 };

[DllImport("coredll.dll", SetLastError = true)]
private extern static bool CreateProcess(string pszImageName,
string pszCmdLine, IntPtr psaProcess, IntPtr psaThread,
int fInheritHandles, int fdwCreate,IntPtr pvEnvironment,
IntPtr pszCurDir, IntPtr psiStartInfo, out ProcessInfo pi);

[DllImport("coredll.dll", SetLastError = true)]
public extern static bool SetStdioPathW(Stdio id, string path);

public static bool GetStdioPathW(Stdio id, out string path)
{
byte[] b = new byte[20]; // Enough for 10 Unicode characters.
int blen = b.Length / 2; // Unicode. Could be done much
more-readably.
bool ret = GetStdioPathW_CE(id, b, ref blen);
// Create string. Note that, if there's no device associated with
id,
// you'll get an empty string back.
if (ret)
{
path = System.Text.ASCIIEncoding.Unicode.GetString(b, 0,
b.Length);
}
else
{
path = "";
}
return ret;
}

[DllImport("coredll.dll", EntryPoint="GetStdioPathW", SetLastError =
true)]
private extern static bool GetStdioPathW_CE(Stdio id, byte[] path, ref
int pathlen);

[DllImport("coredll.dll", SetLastError = true)]
private extern static bool CloseHandle(IntPtr h);
}

Obviously, it doesn't begin to be a full class, but I think that the
declarations for CreateProcess, SetStdioPathW, and GetStdioPathW should be
workable. You'd call it something like this:

res = Process2.GetStdioPathW(Process2.Stdio.stdin, out stdin);
res = Process2.SetStdioPathW(Process2.Stdio.stdin, "COM1:");
Process2 proc = Process2.Start("myprog.exe", "param1 param2 param3");
res = Process2.SetStdioPathW(Process2.Stdio.stdin, stdin);

That should start myprog.exe with the indicated parameters and with stdin
set to COM1:.

Paul T.

"Paul G. Tobey [eMVP]" <p space tobey no spam AT no instrument no spam DOT
com> wrote in message news:[email protected]...
Starbuck said:
I understand from postings by Paul Tobey and others that:
1) With CF2 we cannot use System.Diagnostics.Process to get a handle
to stdin and stdout.
2) OpenNETCF SDF no longer supports a Process class.
3) We need to use GetStdioPathW, SetStdPathW, CreateProcess, etc


1. You could always P/Invoke OpenProcess. All you need for that is the
process ID, which you have via Process.Id. It's dumb that they don't
expose Handle, as it's obviously in there, but what can you do?

Where am I getting stuck? There are subtle differences in the way
people are setting the SECURITY_ATTRIBUTES struct and STARTUPINFO
(which personally I prefer to refer to as STARTUP_INFO but the
standard seems to be without the underscore). For example, some
people create a memory pointer for STARTUPINFO and others use
IntPtr.Zero for that value when calling CreatePipe. Some people use
SetHandleInformation to make sure a pipe isn't inherited, some don't.

How SECURITY_ATTRIBUTES is declared makes zero difference. Windows CE
doesn't support setting it to anything other than NULL in C, so... The
same is true of STARTUPINFO. Must be NULL. This declaration should be
fine for any Windows CE program:

[DllImport("coredll.dll", SetLastError=true)]
public extern static bool CreateProcess(string pszImageName, string
pszCmdLine,
IntPtr psaProcess, IntPtr psaThread, int fInheritHandles, int
fdwCreate,
IntPtr pvEnvironment, IntPtr pszCurDir, IntPtr psiStartInfo,
ProcessInfo pi);

You must set psaProcess, psaThread, pvEnvironment, and psiStartInfo all
to IntPtr.Zero. You must also set fInheritHandles to false.
So before I start asking code questions in this thread, my first
questions are these:

1) Is this any easier in CF 3.5 or VS 2008? I'm still coding for
PPC2003 at the moment but I can set a minimum supported WinCE level if
required.

I'd say no, not really. Handle still isn't exposed.
2) Is a complete solution for this already available somewhere? Even
an internal project that someone can provide (oops, asking for a fish)
that I can rip up (and learn in the process) and publish as a basic
skeleton?

I'm not quite sure what you mean by a solution. For the whole thing,
including drivers for standard in/out/error? The source for OpenNETCF
SDF 1.x should have the correct declarations for everything you need for
a suitable Process class of your own.
3) Is there a better approach to learning how to create this specific
environment? A book that goes through this in detail?

No, it's too complex. There's a sample of sorts, that comes with Platform
Builder/Windows CE itself. The Telnet server sample that you can build
into your OS acts as a parent application for the MS-DOS shell, capturing
its stdin, stdout, and stderr and sending them over the network to the
Telnet client. That should tell you what functions your driver (yes, you
have to write a driver, in C/C++), must have and generally shows how to
call SetStdioPathW(). I guess that, since you're determined, you could
download the evaluation version of Platform Builder for CE5 from
www.microsoft.com/downloads and look at the code. After you install it,
you'll find the Telnet sample in
\WINCE500\PUBLIC\SERVERS\SDK\SAMPLES\TELNETD. The readme, unlike most of
the readme files in the Windows CE source code, is actually pretty good.
You'll still have to figure out what gets compiled into a driver and what
gets compiled into an application/service because Windows CE builds
everything to libraries and then decides what to do with those in a
separate step, but you'll have some source, at least.

Paul T.
 
S

Starbuck

Gentlemen - I need to take some time to absorb everything so far but
will have a response within a few days.

I'm glad I'm not asking about something too-too painfully obvious. :)

Paul, I recognize your approach from your prior postings on the topic
and I've slapped together a lot of code (with a pale resemblence to
the code you provided, no coincidence I guess if that's the approach)
in an attempt to implement your advice. The thing that always gets me
is the part about creating a driver, ActiveDevice(), etc, which may
venture outside of managed code. Again, I'll keep trying to do my
part.

Chris, thank you as well. I've been a frequent visitor to your web
site for the last several days. :) I posted a note to "Announcing
the CAB Installer SDK" and saw your follow-up regarding the pricing
model.

I highly encourage anyone who's doing CF development to check out
Chris' blog - and if you're deploying WinCE projects, do your best to
support his efforts to provide free and low-cost/personally-priced
tools for fellow developers. It's only by actually supporting one
another (equitably) that we get to survive to ask and answer questions
another day.

Regards,
-s-
 
S

Starbuck

Paul G. Tobey said:
There's a sample of sorts, that comes with Platform
Builder/Windows CE itself. The Telnet server sample that you can build into
your OS acts as a parent application for the MS-DOS shell, capturing its
stdin, stdout, and stderr and sending them over the network to the Telnet
client. That should tell you what functions your driver (yes, you have to
write a driver, in C/C++), must have and generally shows how to call
SetStdioPathW(). I guess that, since you're determined, you could download
the evaluation version of Platform Builder for CE5 from
www.microsoft.com/downloads and look at the code. After you install it,
you'll find the Telnet sample in
\WINCE500\PUBLIC\SERVERS\SDK\SAMPLES\TELNETD. The readme, unlike most of
the readme files in the Windows CE source code, is actually pretty good.
You'll still have to figure out what gets compiled into a driver and what
gets compiled into an application/service because Windows CE builds
everything to libraries and then decides what to do with those in a separate
step, but you'll have some source, at least.


Paul, based on your recommendation I've had a look at that code. The
Microsoft site is surprisingly not helpful at all about linking to
related resources for this sort of development and it took me a Long
time to finally find the right download. When I finally found the
Platform Builder, I was just about to load an old embedded C++ 4.0 DVD
from my MSDN package. Also the \PUBLIC directory isn't created on a
minimal installation - you need the full CE OS and emulators (total of
about 2.5GB of disk) before it coughs up all of the sample code.

Yes, it does a setup for stdin/stdout/stderr to "TELX:" as a wrapper
around cmd.exe. Most of that telnetd code deals with connectivity,
RFC-compliant negotiation, etc. This shell interface part that we're
talking about is a very small piece of that solution - but it was good
to see the approach we're talking about used in real code.

At one point I was thinking about using that code as a wrapper around
my exe, and working my client code to telnet into localhost for the
comms. Not only would that be non-performant, but the more I look at
it the less production-ready that code seems. And again, my forte'
isn't with C++ so I'm crossing a major comfort threshold. I'm looking
for a fishing pole, not fish, but with that pole I'd be catching
something I couldn't digest. :)

Frankly, the C# code that we've been knocking around is already well
on the way to solving the problem. I'll see if I can do the same sort
of interface with a "TELX:" / "COM1:" type device.

In the back of my mind I'm worried that anything I do for a PPC2003
device, for example, won't apply to CE4.2, CE5, CE6, CF.NET3.5, or any
of the many supported CPU types. The more I look at CE development
with an eye for creating a general purpose utility, the more
discouraged I get. Like targeting XP then Vista, or 32 bit then 64,
(and yes I remember when we had to support 16 bit apps too) it seems
each target platform needs to be approached one at a time on its own
merits, with each functional device being a success rather than
inability to target every device being seen as a failure. If the goal
is to support everything I foresee a separate VS solution for every
permutation and a lot of chaos and frustration.

Could be wrong...
Thanks again,
-s-
 
C

Christopher Fairbairn [MVP]

Hi,

Starbuck said:
At one point I was thinking about using that code as a wrapper around
my exe, and working my client code to telnet into localhost for the
comms. Not only would that be non-performant, but the more I look at
it the less production-ready that code seems. And again, my forte'
isn't with C++ so I'm crossing a major comfort threshold.

I may have missed the start of this converstation (did it have it's origins
within another thread?). Would you be willing to take a step back for a
second?

As you have discovered in this thread redirecting console stdin/stdout can
be a tricky business if you need to progamatically control what is feed into
STDIN etc. As Chris Tacke suggested a possible technique would be to write a
stream driver to satisify the needs of having a valid file name (creating
your own named pipe implementation in a sense).

What is the console based application you want to wrap? Is it something you
have written yourself or is it provided by a third party?

Could there be an alternative API or way in which you could achieve what you
want progamatically?

If you have source code to the console application it could possibly be less
work to rework it into a DLL which could be accessed in C# via Platform
Invoke functionality that it would be to write a stream device driver
(especially if C/C++ isn't your strong point).

Hope this helps,
Christopher Fairbairn
 
C

Chris Tacke, MVP

Excellent points. I was just assuming that he wanted to do something like
have a native app call "printf" and have that end up in a stream in their C#
app. I'm assuming they have no control over the native source, so using
another transport is not an option. That is a big assumption, that if not
true changes this eqution greatly.


--

Chris Tacke, Embedded MVP
OpenNETCF Consulting
Giving back to the embedded community
http://community.OpenNETCF.com
 
S

Starbuck

Chris Tacke said:
Excellent points. I was just assuming that he wanted to do something like
have a native app call "printf" and have that end up in a stream in their C#
app. I'm assuming they have no control over the native source, so using
another transport is not an option. That is a big assumption, that if not
true changes this eqution greatly.

I'll clarify the project below to make sure we're all on the same
page. It'll be a bonus if the answer is "oh, is that all you want to
do? just do this...".

Christopher F, you can get a recap on the thread here:
http://tinyurl.com/8r8pln
Thank you for your interest.

To summarize the challenge:

The target is an EXE that has no GUI interface. It comes in two
flavors:
- There is a server edition which is accessed via a command line that
puts a user into a command-line sub-shell. Users can also telnet into
a socket where they are put into the same shell. This edition is most
often run as a background service.
- There is a WinCE edition which is generally accessed from an icon,
but the interface is still the command-line sub-shell. This edition
does not have the telnet server component.

So the traditional IO is:

telnet client <> server <> executable
or
cmd.exe <> executable

If you are familiar with the telnet server code in the Platform
Builder, you can see how this is a perfect fit for that sample.

I want to use managed code to access the process, substituting for the
"server" component in the traditional IO example above, or wrapping or
interfacing with cmd.exe so that all stdin comes from my code and all
stdout flows back. I can deal with a synchronous process to start but
will probably try to make the data flow more event driven and async in
a v2.

The further we go into this the more I realize that I need to get more
information about IO from streams, named pipes, etc. Paul Tobey
mentions devices occasionally and the telnet server uses "TELX:". If
I can used managed code to map stdin and stdout for a process to
"MYIO:" and then use that same device as a named pipe from my
front-end code, then I think we have a solution.

Also, the more I look at this the more I'm thinking that I shouldn't
be trying to wrap a process directly, but that the goal should be to
wrap only cmd.exe. If I can create an alternate "UI" (streams/pipes,
file IO, whatever) around cmd.exe, and reliably broker all
stdin/stdout from that one executable, then it doesn't matter what
process is further invoked from there - and this eliminates the whole
"what app is this?" problem.

All of that said, I do have a good relationship with the company that
provides the target component. Minor modifications to the WinCE
executable are possible if it will help the cause here without
introducing instabilities. I wouldn't ask them to replace their own
front-end with file IO, for example, unless that is "the" solution,
but all suggestions are welcome.

Thoughts?

Thanks as always!
-s-
 
C

Chris Tacke, eMVP

I'd vote to ask them for a front end that is stream or queue based,
depending on how you interact with it. Anything else is a lot of work for
you.


--

Chris Tacke, Embedded MVP
OpenNETCF Consulting
Giving back to the embedded community
http://community.OpenNETCF.com
 
S

Starbuck

Chris Tacke said:
I'd vote to ask them for a front end that is stream or queue based,
depending on how you interact with it. Anything else is a lot of work for
you.

Closing thread...

I didn't mind putting some effort into this, in fact I welcomed it.
However, given that this doesn't seem resolvable without a LOT of
work, I did have to come up with another solution.

The child product is a non-relational DBMS which allows for embedded
stored procedures that are capable of File IO. For the PDA platform
it doesn't support the same network connectivity or development APIs
available for server platforms. So I wrote an asynchronous
handshaking protocol using files to move data. My client writes a
request, the "child" environment picks it up, processes, writes a
response, and my client reads in the response from the file system.
It's not an elegant solution but it's much easier to code than trying
to pipe directly into stdin/stdout.

On one hand I'm disappointed that we couldn't do this but on the
other, I was seeking a solution and I don't care what direction that
took. Unfortunately my solution probably won't translate to anyone
else's project, except for the idea that sometimes we need to think
outside of the box.

Chris - I'm hoping my colleagues will ask for solution deployment
based on this effort and give me an opportunity to look at and pay you
for your CAB Installer SDK. Yes, most developers are cheap. In my
case I don't mind paying for tools that allow me to make more money -
my problem is that I can't find enough people who actually want mobile
solutions. I must be looking under all the wrong rocks. :)

Best
-s-
 
P

Paul G. Tobey [eMVP]

For piping, it seems like a Point-to-Point message queue (or two, one for
each communication direction), would be easier than handling picking up the
fact that the file is there, handling multiple file names, etc. It's a
pretty easy-to-use cross-process data transfer structure (wrapped by
OpenNETCF's SDF, too, if you need that on one or both ends).

Paul T.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top