Use Named Pipes and Shared Memory for inter process communication with a child process or two
2014-01-14 18:55
483 查看
I wanted to inject some very low impact code that would run in any “parent” process, like Notepad or Excel or Visual Studio. I wanted to have some
User Interface for the data that my injected code gathered about the parent process, and that would work best in a different “child” process, preferably using WPF.
In the old days, I could call a COM server to handle the job. A DLL server would be in process, but it could be made out of process by making it a
COM+ application (see Blogs get 300 hits per hour: Visual FoxPro can count. and
Create multiple threads from within your application).
.Net Remoting
seemed to be a little heavyweight for Parent->Child process communication.
About 5 years ago, I wrote this:
Use Named Pipes to communicate between processes or machines, so I thought I’d use a combination of Named
Pipes and
Shared Memory. Luckily
.Net 3.5 added support for Named Pipes making the child process pretty simple.
Pipes could be used to send messages, and the lion’s share of data movement could be in the Shared memory.
Synchronization and lifetime management are a little tedious. We want the parent process to continue optionally if the child process terminates, but
we want the child to terminate automatically when the parent terminates for any reason. Similarly, the child process should terminate if the parent has gone.
This sample shows a parent process in C++ and 2 child processes in C# and VB. The parent spins off a thread to use to service incoming requests from
the children. Events are used to synchronize communication. A timer in each child process fires off requests to the parent.
I was using Visual Studio 2010: you can use VS 2008, but you’ll have to adjust for some of the new features I use, especially in the VB code.
Start Visual Studio. File->New->Project->C++ Win32 Project->Windows Application. In the wizard, Click Add common header files for ATL
Now hit F5 to build and see it execute: there’s a window and a couple menus.
Now add a second EXE project to your solution: choose File->Add->New Project->VB WPF Application.
(Repeat to add a 3rd project for C# !)
Fiddle with the Project->Properties->Compile->Build Output path so it builds into the same folder as the parent exe (for me, it was” ..\Debug\”)
Paste in the VB code below into MainWindow.Xaml.Vb
Somewhere inside the _tWinMain of your CPP project, add these 2 lines to instantiate a class that calls the WpfApplication as a child process, with
a shared memory size of 2048 (make sure to change the name of the EXE to match your VB and C# EXEs):
CreateChildProcess opCreateChildProcessCS(_T("NamedPipesCS.exe"),2048, 1);
CreateChildProcess opCreateChildProcessVB(_T("NamedPipesVB.exe"),2048, 2);
Paste the CPP code below before the _tWinMain.
F5 will show both processes launched. You can alt-tab between the 2: they behave like independent processes. Try terminating one of them.
If you uncomment the MsgBox, then hit F5, you can actually use VS to attach to a child process before it does too much. Try attaching to all 3!
See also:
Remove double spaces from pasted
code samples in blog
<C++ Code>
// CreateChildProcess : class in parent process to instantiate and communicate with a child process
// usage:
CreateChildProcess opCreateChildProcess(_T("WpfApplication1.exe"),2048);
class CreateChildProcess
{
HANDLE m_hChildProcess;// handle to the child process we create
HANDLE m_hNamedPipe; // handle to the named pipe the paren process creates
HANDLE m_hEvent;
HANDLE m_hThread; // thread in parent process to communicate with child
LPVOID m_pvMappedSection;
DWORD m_cbSharedMem;
public:
CreateChildProcess(TCHAR* szChildExeFileName,DWORD cbSharedMemSize,
int ChildNo )
{
m_cbSharedMem = cbSharedMemSize;
TCHAR szPipeName[1000];
TCHAR szEventName[1000];
swprintf_s(szPipeName, L"Pipe%d_%d", ChildNo, GetCurrentProcessId());
//make the names unique per child and per our (parent) process
swprintf_s(szEventName,L"Event%d_%d", ChildNo, GetCurrentProcessId());
//
SECURITY_ATTRIBUTES SecurityAttributes = {
sizeof( SECURITY_ATTRIBUTES ),
// nLength
NULL, // lpSecurityDescriptor. NULL = default for calling process
TRUE
// bInheritHandle
};
HANDLE hFileMapping = CreateFileMapping(
INVALID_HANDLE_VALUE, // backed by paging file
&SecurityAttributes,
PAGE_READWRITE,
0,
static_cast< DWORD >(m_cbSharedMem),
// Truncation in 64-bit
NULL);
m_pvMappedSection = MapViewOfFile( hFileMapping, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0 );
swprintf_s((TCHAR *)m_pvMappedSection, m_cbSharedMem,_T("here i am in shared mem %d"), 1);
CComBSTR bstrFullPipeName(L"\\\\.\\pipe\\");
bstrFullPipeName.Append(szPipeName); // the pipe name is "\\.\pipe\MyName"
m_hNamedPipe = CreateNamedPipe(
bstrFullPipeName,
PIPE_ACCESS_DUPLEX + FILE_FLAG_FIRST_PIPE_INSTANCE,
PIPE_TYPE_MESSAGE + PIPE_WAIT,
PIPE_UNLIMITED_INSTANCES,
100, //nOutBufferSize
100, //nInBufferSize
100, // nDefaultTimeout
0); // lpSecurityAttributes
m_hEvent = CreateEvent(
&SecurityAttributes,
FALSE, //bManualReset
FALSE, //bInitialState
szEventName); // name
STARTUPINFO StartupInfo = { sizeof(STARTUPINFO) };
PROCESS_INFORMATION ProcessInformation = {};
TCHAR achCommandLine[MAX_PATH*2];
// like WpfApplication1.exe 92 Event2648 Pipe2648 2048
swprintf_s( achCommandLine, _T("%s %d %s %s %d"),szChildExeFileName, hFileMapping, szPipeName, szEventName, m_cbSharedMem);
if( !CreateProcess(
NULL,
achCommandLine,
NULL,
NULL,
TRUE, // inherit handles
CREATE_DEFAULT_ERROR_MODE | NORMAL_PRIORITY_CLASS,
NULL,
NULL,
&StartupInfo,
&ProcessInformation))
{
OutputDebugString(L"failed to create process");
return;
}
::CloseHandle( ProcessInformation.hThread );// We don't need the thread handle
DWORD dwThreadId = 0;
m_hChildProcess = ProcessInformation.hProcess;
// Create the actual background thread in the parent process
m_hThread = CreateThread(NULL, // no security
0, // default stack size
CreateChildProcess::ThreadProc, // initial method
(void *)static_cast<CreateChildProcess *>(this),
// parameter to the thread proc
0, // run immediately
&dwThreadId);
//DWORD res = ::WaitForSingleObject( hThread, INFINITE );
return ;
}
static DWORD WINAPI ThreadProc(void *pvThreadObject)
{
CreateChildProcess *pCreateChildProcess = static_cast<CreateChildProcess *>(pvThreadObject);
BOOL fDone = false;
while (!fDone)
{
DWORD res = ::WaitForSingleObject( pCreateChildProcess ->m_hEvent, 2000 );
switch(res)
{
case WAIT_OBJECT_0:
// the event signalled: we got a request
{
DWORD nTotBytesAvail = 0;
PeekNamedPipe(pCreateChildProcess ->m_hNamedPipe,0,0,0,&nTotBytesAvail,0);
if (nTotBytesAvail != 0)
{
char data[1000];
DWORD nRead = 0;
//read the msg
if (ReadFile(pCreateChildProcess->m_hNamedPipe,data,nTotBytesAvail, &nRead,NULL))
{
data[nTotBytesAvail] = 0; //null terminate
CComBSTR bstrdat( data);
OutputDebugString(bstrdat);
OutputDebugString(L"\n");
if (_wcsnicmp(L"Quit",bstrdat,4)==0)
{
OutputDebugString(L"Child Proc sent Quittin' time msg\n");
fDone=true;
exit(0);
}
// write some stuff to shared mem
swprintf_s(
(wchar_t *)pCreateChildProcess->m_pvMappedSection,
pCreateChildProcess->m_cbSharedMem,
L"Written to shared mem %s", bstrdat);
// send a msg to the child
data[0]='C';
data[1]='+';
data[2]='+';
DWORD nBytesWritten= 0;
WriteFile( pCreateChildProcess->m_hNamedPipe, data, nTotBytesAvail,&nBytesWritten,0);
}
}
}
break;
case WAIT_TIMEOUT:
// test to see if child process still alive
if (WaitForSingleObject(pCreateChildProcess->m_hChildProcess,0) == WAIT_OBJECT_0)
// did the proc terminate?
{
fDone = true;
}
break;
default:
fDone=true;
}
}
// interprocess comm is over
CloseHandle(pCreateChildProcess->m_hChildProcess);
pCreateChildProcess->m_hChildProcess = INVALID_HANDLE_VALUE;
CloseHandle(pCreateChildProcess->m_hThread);
CloseHandle(pCreateChildProcess->m_hNamedPipe);
return 0;
}
};
</C++ Code>
<VB Code>
Imports System.IO.Pipes
Imports System.Runtime.InteropServices
Imports System.Windows.Threading
Imports System.Text
Module
NativeImports
Public Const FILE_MAP_READ
As Int32 = &H4
Public Const FILE_MAP_WRITE
As Int32 = &H2
Declare Function MapViewOfFile
Lib "kernel32" (ByVal hFileMappingObject
As IntPtr,
ByVal dwDesiredAccess As
UInt32, ByVal dwFileOffsetHigh
As UInt32,
ByVal dwFileOffsetLow As
UInt32, ByVal dwNumberOfBytesToMap
As UInt32)
As UInt32
End
Module
Class
MainWindow
Private WithEvents _timer
As New
DispatcherTimer With {.Interval =
TimeSpan.FromSeconds(2)}
Private _pipestream
As NamedPipeClientStream
Private _event As System.Threading.EventWaitHandle
Private _sharedMemAddr
As UInteger
Private _sharedMemSize
As Integer
Private _txtStatus
As New
TextBox With {
.AcceptsReturn = True,
.AcceptsTab = True,
.VerticalScrollBarVisibility = ScrollBarVisibility.Auto
}
Private Sub Window_Loaded(ByVal sender
As System.Object,
ByVal e As System.Windows.RoutedEventArgs)
Handles MyBase.Loaded
' MsgBox("attach a debugger", "VB")
Me.Title =
"VB Child Process"
Me.Content = _txtStatus
Dim args = System.Environment.GetCommandLineArgs
If args.Count < 4
Then
MessageBox.Show("No args")
Close()
Return
End If
Dim hFileMapping =
CInt(args(1))
_sharedMemAddr = MapViewOfFile(hFileMapping, FILE_MAP_READ
Or FILE_MAP_WRITE, 0, 0, 0)
Dim pipename = args(2)
_pipestream = New
NamedPipeClientStream(".", pipename,
PipeDirection.InOut,
PipeOptions.Asynchronous)
Dim eventname = args(3)
_event = System.Threading.ManualResetEvent.OpenExisting(eventname)
_sharedMemSize = CInt(args(4))
_pipestream.Connect()
UpdateStatus("start: shared mem " + ReadSharedMem())
_timer.Start()
End Sub
Shared _numticks
As Integer
Private _byteEncoding
As New
UTF8Encoding
Private _IsClosed =
False
Private _decoder = _byteEncoding.GetDecoder
Sub OnTimerTick()
Handles _timer.Tick
Try
If _pipestream
Is Nothing Or
Not _pipestream.IsConnected
Then
DoForceClose()
Return
End If
SendMsg("WPFClient msg" + _numticks.ToString +
" " +
DateTime.Now.ToLongTimeString)
_numticks += 1
Dim str = GetMsg()
UpdateStatus("Client got data: " + str)
Dim ss = ReadSharedMem()
UpdateStatus("shared mem: " + ss)
Catch ex As IO.IOException
' a named pipe IO op failed
DoForceClose()
Catch ex As
Exception
DoForceClose()
End Try
End Sub
Sub DoForceClose()
If Not _IsClosed
Then
_IsClosed = True
_timer = Nothing
_pipestream = Nothing
UpdateStatus("Disconnected")
Me.Close()
End If
End Sub
Sub SendMsg(ByVal szMsg
As String)
Dim barray = _byteEncoding.GetBytes(szMsg)
_pipestream.Write(barray, 0, barray.Length)
_event.Set()
End Sub
Function GetMsg()
As String
Dim str =
""
Dim barrRead(90)
As Byte
Dim charsRead(90)
As Char
Dim nBytesRead = _pipestream.Read(barrRead, 0, barrRead.Length)
Dim nChars = _decoder.GetChars(barrRead, 0, nBytesRead, charsRead, 0)
str = New String(charsRead, 0, nChars)
Return str
End Function
Function ReadSharedMem()
As String
Dim sResult =
""
For i = 0 To _sharedMemSize
Step 2 ' unicode
Dim aaddr = New
IntPtr(_sharedMemAddr + i)
Dim aByte =
Marshal.ReadByte(aaddr)
If aByte = 0 Then
Exit For
End If
sResult += Chr(aByte)
Marshal.WriteByte(aaddr, aByte + 1)
' demo: change the mem we just read: under a debugger you can see that shared mem was written
Next
Return sResult
End Function
Private Sub UpdateStatus(ByVal newStat
As String)
newStat = DateTime.Now.ToString +
" " + newStat + vbCrLf
Debug.Write(newStat)
Me._txtStatus.AppendText(newStat)
Me._txtStatus.ScrollToEnd()
End Sub
Sub On_Closed() Handles
Me.Closed
_timer = Nothing
If Not _IsClosed
Then
SendMsg("Quit")
End If
If _pipestream IsNot
Nothing Then
_pipestream.Dispose()
_pipestream = Nothing
End If
End Sub
End
Class
</VB Code>
<C# Code>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.IO.Pipes;
using System.Runtime.InteropServices;
namespace NamedPipesCS
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial
class MainWindow :
Window
{
[DllImport("kernel32.dll", SetLastError =
true)]
static extern
IntPtr MapViewOfFile(
IntPtr hFileMappingObject,
FileMapAccess dwDesiredAccess,
uint dwFileOffsetHigh,
uint dwFileOffsetLow,
uint dwNumberOfBytesToMap);
[Flags]
public enum
FileMapAccess :
uint
{
FileMapCopy = 0x0001,
FileMapWrite = 0x0002,
FileMapRead = 0x0004,
FileMapAllAccess = 0x001f,
fileMapExecute = 0x0020,
}
private
TextBox _txtStatus;
private
IntPtr _sharedMemAddr;
private
NamedPipeClientStream _pipestream;
private System.Threading.EventWaitHandle _event;
private int _sharedMemSize;
private System.Windows.Threading.DispatcherTimer _timer;
static int _numticks;
private
UTF8Encoding _byteEncoding = new
UTF8Encoding();
private bool _IsClosed;
private
Decoder _decoder;
public MainWindow()
{
//MessageBox.Show("attach a debugger","C#");
InitializeComponent();
_decoder = _byteEncoding.GetDecoder();
}
private void Window_Loaded(object sender,
RoutedEventArgs e)
{
_txtStatus = new
TextBox();
_txtStatus.AcceptsReturn = true;
_txtStatus.AcceptsTab = true;
_txtStatus.VerticalScrollBarVisibility =
ScrollBarVisibility.Auto;
this.Title =
"C# Child Process";
this.Content = _txtStatus;
var args =
Environment.GetCommandLineArgs();
if (args.Count() < 4)
{
MessageBox.Show("No args");
Close();
return;
}
var hFileMapping =
int.Parse(args[1]);
_sharedMemAddr = MapViewOfFile(new
IntPtr(hFileMapping), FileMapAccess.FileMapRead |
FileMapAccess.FileMapWrite, 0, 0, 0);
var pipeName = args[2];
_pipestream = new System.IO.Pipes.NamedPipeClientStream(".", pipeName, System.IO.Pipes.PipeDirection.InOut,
System.IO.Pipes.PipeOptions.Asynchronous);
var Eventname = args[3];
_event = System.Threading.ManualResetEvent.OpenExisting(Eventname);
_sharedMemSize = int.Parse(args[4]);
_pipestream.Connect();
UpdateStatus(string.Format("start: shared mem {0}" , ReadSharedMem()));
_timer = new System.Windows.Threading.DispatcherTimer();
_timer.Interval = TimeSpan.FromSeconds(2);
_timer.Tick += new
EventHandler(OnTimerTick);
_timer.Start();
this.Closed += new
EventHandler(on_Closed);
}
void OnTimerTick(Object o,
EventArgs e)
{
try
{
if (_pipestream ==
null || !_pipestream.IsConnected)
{
DoForceClose();
return;
}
SendMsg(String.Format("WPFClient msg {0} {1}" , _numticks,
DateTime.Now.ToLongTimeString()));
_numticks += 1;
var str = GetMsg();
UpdateStatus(String.Format("Client got data: {0}" , str));
var ss = ReadSharedMem();
UpdateStatus(String.Format("shared mem: {0}" , ss));
}
catch (Exception)
{
DoForceClose();
}
}
void DoForceClose()
{
if (!_IsClosed)
{
_IsClosed = true;
_timer = null;
_pipestream = null;
UpdateStatus("Diconnected");
Close();
}
}
void SendMsg(string szMsg)
{
var barray = _byteEncoding.GetBytes(szMsg);
_pipestream.Write(barray, 0, barray.Length);
_event.Set();
}
string GetMsg()
{
var str =
"";
var barrRead =new
Byte[90];
var charsRead = new
char[90];
var nByteRead = _pipestream.Read(barrRead, 0, barrRead.Length);
var nChars = _decoder.GetChars(barrRead, 0, nByteRead, charsRead, 0);
str = new string(charsRead, 0, nChars);
return str;
}
String ReadSharedMem()
{
var sResult =
"";
for (int i = 0; i < _sharedMemSize; i += 2)
{
var aaddr = _sharedMemAddr + i;
var aByte =
Marshal.ReadByte(aaddr);
if (aByte == 0)
{
break;
}
sResult += Convert.ToChar(aByte);
Marshal.WriteByte(aaddr,(byte)(aByte+(byte)1));// ' demo: change the mem we just read: under a debugger you can see that
shared mem was written
}
return sResult;
}
private void UpdateStatus(String newStat)
{
newStat =string.Format("{0} {1}\n",
DateTime.Now.ToString(),newStat);
System.Diagnostics.Debug.Write(newStat);
_txtStatus.AppendText(newStat);
_txtStatus.ScrollToEnd();
}
void on_Closed(Object o,
EventArgs e)
{
_timer = null;
if (!_IsClosed)
{
SendMsg("Quit");
}
if (_pipestream !=
null)
{
_pipestream.Dispose();
_pipestream = null;
}
}
}
}
</C# Code>
I wanted to inject some very low impact code that would run in any “parent” process, like Notepad or Excel or Visual Studio. I wanted to have some
User Interface for the data that my injected code gathered about the parent process, and that would work best in a different “child” process, preferably using WPF.
In the old days, I could call a COM server to handle the job. A DLL server would be in process, but it could be made out of process by making it a
COM+ application (see Blogs get 300 hits per hour: Visual FoxPro can count. and
Create multiple threads from within your application).
.Net Remoting
seemed to be a little heavyweight for Parent->Child process communication.
About 5 years ago, I wrote this:
Use Named Pipes to communicate between processes or machines, so I thought I’d use a combination of Named
Pipes and
Shared Memory. Luckily
.Net 3.5 added support for Named Pipes making the child process pretty simple.
Pipes could be used to send messages, and the lion’s share of data movement could be in the Shared memory.
Synchronization and lifetime management are a little tedious. We want the parent process to continue optionally if the child process terminates, but
we want the child to terminate automatically when the parent terminates for any reason. Similarly, the child process should terminate if the parent has gone.
This sample shows a parent process in C++ and 2 child processes in C# and VB. The parent spins off a thread to use to service incoming requests from
the children. Events are used to synchronize communication. A timer in each child process fires off requests to the parent.
I was using Visual Studio 2010: you can use VS 2008, but you’ll have to adjust for some of the new features I use, especially in the VB code.
Start Visual Studio. File->New->Project->C++ Win32 Project->Windows Application. In the wizard, Click Add common header files for ATL
Now hit F5 to build and see it execute: there’s a window and a couple menus.
Now add a second EXE project to your solution: choose File->Add->New Project->VB WPF Application.
(Repeat to add a 3rd project for C# !)
Fiddle with the Project->Properties->Compile->Build Output path so it builds into the same folder as the parent exe (for me, it was” ..\Debug\”)
Paste in the VB code below into MainWindow.Xaml.Vb
Somewhere inside the _tWinMain of your CPP project, add these 2 lines to instantiate a class that calls the WpfApplication as a child process, with
a shared memory size of 2048 (make sure to change the name of the EXE to match your VB and C# EXEs):
CreateChildProcess opCreateChildProcessCS(_T("NamedPipesCS.exe"),2048, 1);
CreateChildProcess opCreateChildProcessVB(_T("NamedPipesVB.exe"),2048, 2);
Paste the CPP code below before the _tWinMain.
F5 will show both processes launched. You can alt-tab between the 2: they behave like independent processes. Try terminating one of them.
If you uncomment the MsgBox, then hit F5, you can actually use VS to attach to a child process before it does too much. Try attaching to all 3!
See also:
Remove double spaces from pasted
code samples in blog
<C++ Code>
// CreateChildProcess : class in parent process to instantiate and communicate with a child process
// usage:
CreateChildProcess opCreateChildProcess(_T("WpfApplication1.exe"),2048);
class CreateChildProcess
{
HANDLE m_hChildProcess;// handle to the child process we create
HANDLE m_hNamedPipe; // handle to the named pipe the paren process creates
HANDLE m_hEvent;
HANDLE m_hThread; // thread in parent process to communicate with child
LPVOID m_pvMappedSection;
DWORD m_cbSharedMem;
public:
CreateChildProcess(TCHAR* szChildExeFileName,DWORD cbSharedMemSize,
int ChildNo )
{
m_cbSharedMem = cbSharedMemSize;
TCHAR szPipeName[1000];
TCHAR szEventName[1000];
swprintf_s(szPipeName, L"Pipe%d_%d", ChildNo, GetCurrentProcessId());
//make the names unique per child and per our (parent) process
swprintf_s(szEventName,L"Event%d_%d", ChildNo, GetCurrentProcessId());
//
SECURITY_ATTRIBUTES SecurityAttributes = {
sizeof( SECURITY_ATTRIBUTES ),
// nLength
NULL, // lpSecurityDescriptor. NULL = default for calling process
TRUE
// bInheritHandle
};
HANDLE hFileMapping = CreateFileMapping(
INVALID_HANDLE_VALUE, // backed by paging file
&SecurityAttributes,
PAGE_READWRITE,
0,
static_cast< DWORD >(m_cbSharedMem),
// Truncation in 64-bit
NULL);
m_pvMappedSection = MapViewOfFile( hFileMapping, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0 );
swprintf_s((TCHAR *)m_pvMappedSection, m_cbSharedMem,_T("here i am in shared mem %d"), 1);
CComBSTR bstrFullPipeName(L"\\\\.\\pipe\\");
bstrFullPipeName.Append(szPipeName); // the pipe name is "\\.\pipe\MyName"
m_hNamedPipe = CreateNamedPipe(
bstrFullPipeName,
PIPE_ACCESS_DUPLEX + FILE_FLAG_FIRST_PIPE_INSTANCE,
PIPE_TYPE_MESSAGE + PIPE_WAIT,
PIPE_UNLIMITED_INSTANCES,
100, //nOutBufferSize
100, //nInBufferSize
100, // nDefaultTimeout
0); // lpSecurityAttributes
m_hEvent = CreateEvent(
&SecurityAttributes,
FALSE, //bManualReset
FALSE, //bInitialState
szEventName); // name
STARTUPINFO StartupInfo = { sizeof(STARTUPINFO) };
PROCESS_INFORMATION ProcessInformation = {};
TCHAR achCommandLine[MAX_PATH*2];
// like WpfApplication1.exe 92 Event2648 Pipe2648 2048
swprintf_s( achCommandLine, _T("%s %d %s %s %d"),szChildExeFileName, hFileMapping, szPipeName, szEventName, m_cbSharedMem);
if( !CreateProcess(
NULL,
achCommandLine,
NULL,
NULL,
TRUE, // inherit handles
CREATE_DEFAULT_ERROR_MODE | NORMAL_PRIORITY_CLASS,
NULL,
NULL,
&StartupInfo,
&ProcessInformation))
{
OutputDebugString(L"failed to create process");
return;
}
::CloseHandle( ProcessInformation.hThread );// We don't need the thread handle
DWORD dwThreadId = 0;
m_hChildProcess = ProcessInformation.hProcess;
// Create the actual background thread in the parent process
m_hThread = CreateThread(NULL, // no security
0, // default stack size
CreateChildProcess::ThreadProc, // initial method
(void *)static_cast<CreateChildProcess *>(this),
// parameter to the thread proc
0, // run immediately
&dwThreadId);
//DWORD res = ::WaitForSingleObject( hThread, INFINITE );
return ;
}
static DWORD WINAPI ThreadProc(void *pvThreadObject)
{
CreateChildProcess *pCreateChildProcess = static_cast<CreateChildProcess *>(pvThreadObject);
BOOL fDone = false;
while (!fDone)
{
DWORD res = ::WaitForSingleObject( pCreateChildProcess ->m_hEvent, 2000 );
switch(res)
{
case WAIT_OBJECT_0:
// the event signalled: we got a request
{
DWORD nTotBytesAvail = 0;
PeekNamedPipe(pCreateChildProcess ->m_hNamedPipe,0,0,0,&nTotBytesAvail,0);
if (nTotBytesAvail != 0)
{
char data[1000];
DWORD nRead = 0;
//read the msg
if (ReadFile(pCreateChildProcess->m_hNamedPipe,data,nTotBytesAvail, &nRead,NULL))
{
data[nTotBytesAvail] = 0; //null terminate
CComBSTR bstrdat( data);
OutputDebugString(bstrdat);
OutputDebugString(L"\n");
if (_wcsnicmp(L"Quit",bstrdat,4)==0)
{
OutputDebugString(L"Child Proc sent Quittin' time msg\n");
fDone=true;
exit(0);
}
// write some stuff to shared mem
swprintf_s(
(wchar_t *)pCreateChildProcess->m_pvMappedSection,
pCreateChildProcess->m_cbSharedMem,
L"Written to shared mem %s", bstrdat);
// send a msg to the child
data[0]='C';
data[1]='+';
data[2]='+';
DWORD nBytesWritten= 0;
WriteFile( pCreateChildProcess->m_hNamedPipe, data, nTotBytesAvail,&nBytesWritten,0);
}
}
}
break;
case WAIT_TIMEOUT:
// test to see if child process still alive
if (WaitForSingleObject(pCreateChildProcess->m_hChildProcess,0) == WAIT_OBJECT_0)
// did the proc terminate?
{
fDone = true;
}
break;
default:
fDone=true;
}
}
// interprocess comm is over
CloseHandle(pCreateChildProcess->m_hChildProcess);
pCreateChildProcess->m_hChildProcess = INVALID_HANDLE_VALUE;
CloseHandle(pCreateChildProcess->m_hThread);
CloseHandle(pCreateChildProcess->m_hNamedPipe);
return 0;
}
};
</C++ Code>
<VB Code>
Imports System.IO.Pipes
Imports System.Runtime.InteropServices
Imports System.Windows.Threading
Imports System.Text
Module
NativeImports
Public Const FILE_MAP_READ
As Int32 = &H4
Public Const FILE_MAP_WRITE
As Int32 = &H2
Declare Function MapViewOfFile
Lib "kernel32" (ByVal hFileMappingObject
As IntPtr,
ByVal dwDesiredAccess As
UInt32, ByVal dwFileOffsetHigh
As UInt32,
ByVal dwFileOffsetLow As
UInt32, ByVal dwNumberOfBytesToMap
As UInt32)
As UInt32
End
Module
Class
MainWindow
Private WithEvents _timer
As New
DispatcherTimer With {.Interval =
TimeSpan.FromSeconds(2)}
Private _pipestream
As NamedPipeClientStream
Private _event As System.Threading.EventWaitHandle
Private _sharedMemAddr
As UInteger
Private _sharedMemSize
As Integer
Private _txtStatus
As New
TextBox With {
.AcceptsReturn = True,
.AcceptsTab = True,
.VerticalScrollBarVisibility = ScrollBarVisibility.Auto
}
Private Sub Window_Loaded(ByVal sender
As System.Object,
ByVal e As System.Windows.RoutedEventArgs)
Handles MyBase.Loaded
' MsgBox("attach a debugger", "VB")
Me.Title =
"VB Child Process"
Me.Content = _txtStatus
Dim args = System.Environment.GetCommandLineArgs
If args.Count < 4
Then
MessageBox.Show("No args")
Close()
Return
End If
Dim hFileMapping =
CInt(args(1))
_sharedMemAddr = MapViewOfFile(hFileMapping, FILE_MAP_READ
Or FILE_MAP_WRITE, 0, 0, 0)
Dim pipename = args(2)
_pipestream = New
NamedPipeClientStream(".", pipename,
PipeDirection.InOut,
PipeOptions.Asynchronous)
Dim eventname = args(3)
_event = System.Threading.ManualResetEvent.OpenExisting(eventname)
_sharedMemSize = CInt(args(4))
_pipestream.Connect()
UpdateStatus("start: shared mem " + ReadSharedMem())
_timer.Start()
End Sub
Shared _numticks
As Integer
Private _byteEncoding
As New
UTF8Encoding
Private _IsClosed =
False
Private _decoder = _byteEncoding.GetDecoder
Sub OnTimerTick()
Handles _timer.Tick
Try
If _pipestream
Is Nothing Or
Not _pipestream.IsConnected
Then
DoForceClose()
Return
End If
SendMsg("WPFClient msg" + _numticks.ToString +
" " +
DateTime.Now.ToLongTimeString)
_numticks += 1
Dim str = GetMsg()
UpdateStatus("Client got data: " + str)
Dim ss = ReadSharedMem()
UpdateStatus("shared mem: " + ss)
Catch ex As IO.IOException
' a named pipe IO op failed
DoForceClose()
Catch ex As
Exception
DoForceClose()
End Try
End Sub
Sub DoForceClose()
If Not _IsClosed
Then
_IsClosed = True
_timer = Nothing
_pipestream = Nothing
UpdateStatus("Disconnected")
Me.Close()
End If
End Sub
Sub SendMsg(ByVal szMsg
As String)
Dim barray = _byteEncoding.GetBytes(szMsg)
_pipestream.Write(barray, 0, barray.Length)
_event.Set()
End Sub
Function GetMsg()
As String
Dim str =
""
Dim barrRead(90)
As Byte
Dim charsRead(90)
As Char
Dim nBytesRead = _pipestream.Read(barrRead, 0, barrRead.Length)
Dim nChars = _decoder.GetChars(barrRead, 0, nBytesRead, charsRead, 0)
str = New String(charsRead, 0, nChars)
Return str
End Function
Function ReadSharedMem()
As String
Dim sResult =
""
For i = 0 To _sharedMemSize
Step 2 ' unicode
Dim aaddr = New
IntPtr(_sharedMemAddr + i)
Dim aByte =
Marshal.ReadByte(aaddr)
If aByte = 0 Then
Exit For
End If
sResult += Chr(aByte)
Marshal.WriteByte(aaddr, aByte + 1)
' demo: change the mem we just read: under a debugger you can see that shared mem was written
Next
Return sResult
End Function
Private Sub UpdateStatus(ByVal newStat
As String)
newStat = DateTime.Now.ToString +
" " + newStat + vbCrLf
Debug.Write(newStat)
Me._txtStatus.AppendText(newStat)
Me._txtStatus.ScrollToEnd()
End Sub
Sub On_Closed() Handles
Me.Closed
_timer = Nothing
If Not _IsClosed
Then
SendMsg("Quit")
End If
If _pipestream IsNot
Nothing Then
_pipestream.Dispose()
_pipestream = Nothing
End If
End Sub
End
Class
</VB Code>
<C# Code>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.IO.Pipes;
using System.Runtime.InteropServices;
namespace NamedPipesCS
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial
class MainWindow :
Window
{
[DllImport("kernel32.dll", SetLastError =
true)]
static extern
IntPtr MapViewOfFile(
IntPtr hFileMappingObject,
FileMapAccess dwDesiredAccess,
uint dwFileOffsetHigh,
uint dwFileOffsetLow,
uint dwNumberOfBytesToMap);
[Flags]
public enum
FileMapAccess :
uint
{
FileMapCopy = 0x0001,
FileMapWrite = 0x0002,
FileMapRead = 0x0004,
FileMapAllAccess = 0x001f,
fileMapExecute = 0x0020,
}
private
TextBox _txtStatus;
private
IntPtr _sharedMemAddr;
private
NamedPipeClientStream _pipestream;
private System.Threading.EventWaitHandle _event;
private int _sharedMemSize;
private System.Windows.Threading.DispatcherTimer _timer;
static int _numticks;
private
UTF8Encoding _byteEncoding = new
UTF8Encoding();
private bool _IsClosed;
private
Decoder _decoder;
public MainWindow()
{
//MessageBox.Show("attach a debugger","C#");
InitializeComponent();
_decoder = _byteEncoding.GetDecoder();
}
private void Window_Loaded(object sender,
RoutedEventArgs e)
{
_txtStatus = new
TextBox();
_txtStatus.AcceptsReturn = true;
_txtStatus.AcceptsTab = true;
_txtStatus.VerticalScrollBarVisibility =
ScrollBarVisibility.Auto;
this.Title =
"C# Child Process";
this.Content = _txtStatus;
var args =
Environment.GetCommandLineArgs();
if (args.Count() < 4)
{
MessageBox.Show("No args");
Close();
return;
}
var hFileMapping =
int.Parse(args[1]);
_sharedMemAddr = MapViewOfFile(new
IntPtr(hFileMapping), FileMapAccess.FileMapRead |
FileMapAccess.FileMapWrite, 0, 0, 0);
var pipeName = args[2];
_pipestream = new System.IO.Pipes.NamedPipeClientStream(".", pipeName, System.IO.Pipes.PipeDirection.InOut,
System.IO.Pipes.PipeOptions.Asynchronous);
var Eventname = args[3];
_event = System.Threading.ManualResetEvent.OpenExisting(Eventname);
_sharedMemSize = int.Parse(args[4]);
_pipestream.Connect();
UpdateStatus(string.Format("start: shared mem {0}" , ReadSharedMem()));
_timer = new System.Windows.Threading.DispatcherTimer();
_timer.Interval = TimeSpan.FromSeconds(2);
_timer.Tick += new
EventHandler(OnTimerTick);
_timer.Start();
this.Closed += new
EventHandler(on_Closed);
}
void OnTimerTick(Object o,
EventArgs e)
{
try
{
if (_pipestream ==
null || !_pipestream.IsConnected)
{
DoForceClose();
return;
}
SendMsg(String.Format("WPFClient msg {0} {1}" , _numticks,
DateTime.Now.ToLongTimeString()));
_numticks += 1;
var str = GetMsg();
UpdateStatus(String.Format("Client got data: {0}" , str));
var ss = ReadSharedMem();
UpdateStatus(String.Format("shared mem: {0}" , ss));
}
catch (Exception)
{
DoForceClose();
}
}
void DoForceClose()
{
if (!_IsClosed)
{
_IsClosed = true;
_timer = null;
_pipestream = null;
UpdateStatus("Diconnected");
Close();
}
}
void SendMsg(string szMsg)
{
var barray = _byteEncoding.GetBytes(szMsg);
_pipestream.Write(barray, 0, barray.Length);
_event.Set();
}
string GetMsg()
{
var str =
"";
var barrRead =new
Byte[90];
var charsRead = new
char[90];
var nByteRead = _pipestream.Read(barrRead, 0, barrRead.Length);
var nChars = _decoder.GetChars(barrRead, 0, nByteRead, charsRead, 0);
str = new string(charsRead, 0, nChars);
return str;
}
String ReadSharedMem()
{
var sResult =
"";
for (int i = 0; i < _sharedMemSize; i += 2)
{
var aaddr = _sharedMemAddr + i;
var aByte =
Marshal.ReadByte(aaddr);
if (aByte == 0)
{
break;
}
sResult += Convert.ToChar(aByte);
Marshal.WriteByte(aaddr,(byte)(aByte+(byte)1));// ' demo: change the mem we just read: under a debugger you can see that
shared mem was written
}
return sResult;
}
private void UpdateStatus(String newStat)
{
newStat =string.Format("{0} {1}\n",
DateTime.Now.ToString(),newStat);
System.Diagnostics.Debug.Write(newStat);
_txtStatus.AppendText(newStat);
_txtStatus.ScrollToEnd();
}
void on_Closed(Object o,
EventArgs e)
{
_timer = null;
if (!_IsClosed)
{
SendMsg("Quit");
}
if (_pipestream !=
null)
{
_pipestream.Dispose();
_pipestream = null;
}
}
}
}
</C# Code>
相关文章推荐
- Use Named Pipes and Shared Memory for inter process communication with a child process or two
- MS SQL错误:SQL Server failed with error code 0xc0000000 to spawn a thread to process a new login or connection. Check the SQL Server error log and the Windows event logs for information about possible related problems
- spring.data.jpa.query :Cannot use native queries with dynamic sorting and/or pagination in method
- Out of memory: Kill process scoreor sacrifice child
- Out of memory: Kill process xx or sacrifice child
- How to Enabling and Diabling VxDMP devices for use with Oracle ASM
- Database Patch Set Update Overlay Patches Required for Use with PSUs and Oracle E-Business Suite
- (FW: From MSDN)Creating a Child Process with Redirected Input and Output
- Shell script for logging cpu and memory usage of a Linux process
- Multi-processor having shared memory, private cache memories, and invalidate queues having valid bits and flush bits for serializing transactions
- Handle inter-process communication between PhantomJS and Golang processes via hixie-76 websockets
- Along with all the above benefits, you cannot overlook the space efficiency and performance gains in using DataFrames and Dataset APIs for two reasons.
- When to use volatile with shared CUDA Memory
- Out of memory: kill process 6184 (XXX) score 166 or a child Killed process 6184 (XXX)
- Three TFS cool things: a tool for TFS Integration with Project and two TFS guides
- about to fork child process, waiting until server is ready for connections. forked process: 2676 ERROR: child process failed, exited with error number 100
- Creating a Child Process with Redirected Input and Output
- An Analytical Model for a GPU Architecture with Memory-level and Thread-level Parallelism Awareness
- Name for parameter binding must not be null or empty! On JDKs < 8, you need to use @Param for named
- SurfaceFlinger/gralloc Out of Memory error when allocating buffer memory for use with virtual frame