□みの開発日記 本文へジャンプ
HOME
2010年02月24日(水)
C#のサービスとC++アプリケーションのプログラム間通信
  1.  開発環境 
    1. OS:Windows 7 Ultimate
    2. IDE:Visual Studio 2008 SP1

  2.  概 要 
    デスクトップアプリケーション(C++)の起動時、サービスプログラム(C#)に起動したことを通知。
    終了時、同様に終了したことを通知する。
    プログラム間通信には名前付きパイプを使用する。

  3.  サービスプログラム(C#) 
    1. System.Timers.Timer をツールボックスからドラッグ。プロパティ・イベントに以下の内容で設定。
      						- プロパティ -
      						Name=timer
      						Interval=300
      						- イベント -
      						Elapsed=timer_Elapsed
      						
    2. サービスの開始イベント - OnStart()
      						protected override void OnStart( string[] args )
      						{
      							timer.Start();
      						}
      						
    3. サービスの停止イベント - OnStop()
      						protected override void OnStop()
      						{
      							timer.Stop();
      						}
      						
    4. タイマーイベント - timer_Elapsed
      						private void timer_Elapsed( object sender, ElapsedEventArgs e )
      						{
      							char [] readBuff = new char[100];
      							NamedPipeServerStream pipeStream = null;
      							StreamReader reader = null;
      							StreamWriter writer = null;
      
      							// タイマーの中断
      							timer.Stop();
      
      							try {
      								// アクセス権限の取得
      								PipeSecurity pipeSecurity = GetPipeSecurity();
      
      								// 名前付きパイプのオープン
      								pipeStream = GetPipeStream( pipeSecurity );
      
      								// 接続を待機する
      								pipeStream.WaitForConnection();
      
      								// 受信データの読込
      								reader = new StreamReader( pipeStream );
      								reader.Read( readBuff, 0, 8 );
      								string readString =
      										new string( readBuff, 0, BUFF_SIZE );
      									・
      									・ (ここに処理を記述する)
      									・
      								// 送信データの書込
      								writer = new StreamWriter( pipeStream );
      								writer.Write( "OK" );
      								writer.Flush();
      							} catch( Exception ex ) {
      									・
      									・
      							} finally {
      								if( pipeStream != null ) {
      										pipeStream.Dispose();
      										pipeStream = null;
      								}
      								// タイマーの再開
      								timer.Start();
      							}
      						}
      						
    5. アクセス権限の取得 - GetPipeSecurity()
      WellKnownSidType.WorldSid とすることでどのユーザーからもアクセス 可能とする。
      ※この AddAccessRule をしておかないと、呼出側は、ERROR_ACCESS_DENIED (5) でエラーになる。
      						private PipeSecurity GetPipeSecurity()
      						{
      							PipeSecurity pipeSecurity = null;
      							using(	NamedPipeServerStream pipeStream = new NamedPipeServerStream(
      											"MyPipe", PipeDirection.InOut, 1, 
      											PipeTransmissionMode.Message, PipeOptions.None ) )
      							{
      								// PipeSecurity オブジェクトを取得
      								pipeSecurity = pipeStream.GetAccessControl();
      							}
      							SecurityIdentifier worldSid = new SecurityIdentifier( 
      															WellKnownSidType.WorldSid, null );
      							// アクセス規則を、アクセス制御リスト (DACL) に追加
      							pipeSecurity.AddAccessRule(new PipeAccessRule( worldSid,
      											PipeAccessRights.ReadWrite, AccessControlType.Allow ) );
      
      							return pipeSecurity;
      						}
      						
    6. 名前付きパイプのオープン - GetPipeStream()
      						private NamedPipeServerStream GetPipeStream(PipeSecurity pipeSecurity)
      						{
      							return new NamedPipeServerStream( "MyPipe", PipeDirection.InOut, 1,
      										PipeTransmissionMode.Message, PipeOptions.None, 256, 256,
      										pipeSecurity );
      						}
      						
      ※PipeSecurity を指定するコンストラクタには、in/out のバッファサイズまで 指定しなければならなかったのでとりあえず、ともに 256 としておいた。

  4.  デスクトップアプリケーション(C++) 

    1. 名前付きパイプでの送受信
      							int DoCallNamedPipe(char* pszSendBuff, int nSendSize)
      							{
      								SetLastError( NO_ERROR );
      								char	szReadBuff[10];
      								DWORD	dwRead;
      								ZeroMemory( szReadBuff, sizeof(szReadBuff) );
      								CallNamedPipe( _T("\\\\.\\pipe\\MyPipe"), pszSendBuff, nSendSize,
      													&szReadBuff, sizeof(szReadBuff), &dwRead, 300 );
      								return GetLastError();
      							}
      							
      ※C#のパイプ名との差異に注意!!

    2. WM_CREATE
      							DoCallNamedPipe( "START", strlen( "START" ) );
      							
    3. WM_DESTROY
      							DoCallNamedPipe( "END", strlen( "END" ) );