Channels ▼
RSS

.NET

Using Named Pipes to Connect a GUI to a Console App in Windows


Software developers usually provide a rich graphical user interface to easily run an application in a desktop environment. However, power users sometimes prefer a console application that typically provides access to the fullest range of application options via a command line interface and may be composed into a pipeline of UI-less processing steps. What if an application needs to be available both as a console application for power users and as a graphically rich application for typical users? This article presents techniques for accomplishing this using synchronous and asynchronous named pipes to control a UI-less console application from a separate process.

The Need

I am a research engineer at a consulting firm that performs forensic analysis of software source code. Law firms provide us with source code from companies involved in intellectual property disputes, and we use proprietary techniques and software tools to analyze the codebases and determine whether there is evidence of code plagiarism.

It is critical that our tools perform a thorough analysis so we can state our results with confidence. Our algorithms can take hours to perform a complete analysis even when running on high-end workstations.

A few months ago, my boss asked me to enhance one of our code analysis tools. He wanted a console application that would perform the analysis and a separate Windows application that would provide a graphical user interface to the console application.

The tool I was asked to enhance had already been implemented as two programs:

  • The Code Analyzer, a console application written in C# and .NET, and
  • The GUI Application, a Microsoft Windows application written in C++ and MFC.

A user could run the Code Analyzer from a command window, passing a source code file's path on the command line, to analyze the file with our proprietary algorithms. Alternatively, a user could analyze all the files in a directory tree by running the GUI application. The GUI app would recursively walk the tree and call the Code Analyzer, passing each file's path in the command line. This design had a problem: If there were thousands of files to analyze, the GUI Application would launch the Code Analyzer thousands of times, once per file. Clearly, this was not very efficient!

My first task was to move the directory traversal logic into the Code Analyzer and have the GUI application launch the Code Analyzer just once. Easy enough. My more interesting challenges were to have the Code Analyzer periodically inform the GUI Application of its progress so the latter could update a progress bar, and provide a "Stop" button so a user could stop a long-running job — Figure 1 shows the final result. Clearly, the GUI app and the Code Analyzer would have to communicate with each other via an interprocess communication (IPC) mechanism. But which IPC technology should they use?

Figure 1: Screen shot of running sample program.

Choosing an IPC Technology

Over the years, Microsoft has introduced a variety of interprocess communication technologies, each having distinct advantages and disadvantages. Table 1 lists the main IPC technology choices available on the Windows platform today.

[Click image to view at full size]
Table 1: Interprocess Communication Technologies in Windows.

When selecting an IPC technology it is important to carefully consider your application's IPC requirements. My requirements were straightforward: I needed a technology that would allow a Windows application written in C++ to communicate with a .NET-based console application written in C#. This ruled out antiquated technologies like DDE, which .NET does not support. This also ruled out sophisticated technologies, such as Windows Sockets or WCF, which would have been overkill. I narrowed the choice down to anonymous pipes, named pipes, and memory-mapped files. Any of these would have worked, but I decided on named pipes for their richer synchronization support and to allow for the possibility of remotely executing the Code Analyzer over our LAN.

Pipes are a mature technology. They can be traced back to the UNIX operating system, which had unnamed pipes since 1972 and rudimentary named pipes by 1977 (Ritchie). Microsoft introduced named pipes in LAN Manager in the 1980s (Fisher), continued to support them in Windows NT and Windows 2000, and added security features to them in subsequent versions of Windows.

Named pipes are a core IPC technology built into the Windows client and server operating systems. They are exposed to application developers through both Windows APIs and .NET classes. When two processes want to communicate through a named pipe, one process, the pipe server, creates the pipe, and another process, the pipe client, connects to the pipe using its name. The two processes can then communicate by writing and reading messages from/to the pipe using APIs similar to those for file I/O. Conceptually, it's all very straightforward. In practice, getting all the synchronization logic in place is a bit more challenging, as we will explore in the next sections.

Using a Synchronous Named Pipe for Progress Reporting

Named pipes come in two varieties: synchronous and asynchronous. The difference has to do with how applications synchronize reading and writing. An unmanaged Windows application can read from a synchronous pipe by calling the ReadFile API, whereas a .NET application calls the .NET NamedPipeClientStream class's Read method. Both calls block until another thread writes to or closes the pipe. By contrast, when code calls the ReadFile API on an asynchronous pipe the call returns immediately regardless of whether a message is in the pipe.

I decided to send progress report messages from the Code Analyzer using a synchronous named pipe because it would be easier to ensure that at most one message was in the pipe. This synchronous design was cleaner than handling a queue of messages in the pipe, which would have had edge cases such as the queue growing to the pipe's buffer size. The Code Analyzer sends a progress message each time it finishes processing some fraction, say 1%, of its files, after the GUI Application has read the previous message. Figure 2 shows a UML-style sequence diagram of the solution I implemented. There are two processes, the GUI Application and the Code Analyzer, and among them three threads.

Figure 2: Sequence diagram of processes and thread interactions when user clicks Start button.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Comments:

dblake950
2013-02-28T22:55:13

Doh, sorry about that. I've fixed the "Code is available" link on the second page so you can download the zip file containing the code and notes now.


Permalink
ubm_techweb_disqus_sso_-2f225f5e4cfa63e5dd2b7d3009a8c783
2012-12-04T21:50:08

Try googling an example for Named Pipes. Check this one out from Microsoft
http://msdn.microsoft.com/en-u...

Goodluck

Firas S Assaad


Permalink
RamonFHerreraG
2012-08-20T15:52:13

This application is EXACTLY what I need! The bad news is that I cannot find the source code anywhere. :-(

Does anybody know how to get in touch with Jim Zamiska, the author??

Giga-Thanks!


Permalink
ubm_techweb_disqus_sso_-71fe90849f198c1debe4b67f14243c3d
2011-12-02T03:03:21

Hi Jim,

There's no question that COM has its limitations. It's not a one-size-fits-all technology. However, given the alternatives (C++/CLI, P/Invoke, sockets or named pipes), I've found it presents the fewest headaches.

You would typically mark the .NET classes as supporting both threading models. That way, the client application can choose whatever's appropriate. The .NET code should assume that it can be called from multiple threads and use appropriate synchronization mechanisms.

In a MFC GUI application, you would typically create the object in a single threaded apartment on your main thread. The only real caveat is that if you share the object pointer with worker threads in that app, you must remember to marshall the calls.

How should the COM library report progress to the UI?

The easiest way is to use a callback interface. You define the interface in the class library and import it into the C++ app. The MFC application would implement the interface and pass an instance of it to the managed object by some means (a Subscribe method or COM connection point for example).

Also, how should the UI notify the COM object when the user pushes the Stop button?

Just define a Stop() method on one of your interfaces. It doesn't matter if the client created the object in a STA or MTA. On the .NET side, you don't care. If the .NET code has created worker threads, it will have to use appropriate signaling and synchronization methods to shut them down.

What changes would have to be made to a COM-based solution to support distributed processing?

To be honest, I haven't looked at DCOM in many years, and I certainly haven't tried it with .NET COM servers. In a distributed scenario, you'd probably be better off creating a WCF interface for the .NET code. Create a small .NET proxy library to call the service and interface with the C++ app.

What if you wanted the UI and analysis engine to run in separate processes on the same machine for better crash recovery?

Once again, this is outside my experience. I've only tried the in-process scenario. It probably wouldn't be hard to create an out-of-process version, however.

One final note. If you do use this technique, I highly recommend doing it with registration free COM. This greatly simplifies deployment.


Permalink
ubm_techweb_disqus_sso_-7edaca1ed008e6575a093937942b8b80
2011-11-28T22:58:32

Dear Pruderman000,

Thank you for suggesting COM as an alternative to named pipes. This is an interesting idea and a good choice for many applications that need to share code. COM of course supports calls from both .NET and native C++ code.

However I am wondering if you could say more about the threading architecture you would recommend when using COM for an application like the one I describe. I assume there would be at least two threads: the UI thread where the GUI C++ or .NET client application runs, and a worker thread where the COM library runs.

How should the COM library report progress to the UI? For example, the COM object could generate progress events and the .NET client could consume them as described by the MSDN article "Handling
Events Raised by a COM Source” ( msdn.microsoft.com/en-us/libra... ). The .NET delegate receiving events would presumably execute on the worker thread and, for thread safety, invoke progress bar updates on the UI thread. Should an unmanaged C++ client use the same mechanism to receive progress notifications?

Also, how should the UI notify the COM object when the user pushes the Stop button? I would expect the COM library to be thread-safe so the UI thread could notify it to stop analyzing files on the worker thread. COM supports STA, MTA and free threading models – which is best for this type of application?

Finally, distributing processing across a LAN in a named pipe architecture should be possible by simply specifying a pipe name of the form “\\ServerName\pipe\PipeName” when creating the pipe, as described by the MSDN article “Pipe
Names” ( msdn.microsoft.com/en-us/libra... ). What changes would have to be made to a COM-based solution to support distributed processing? What if you wanted the UI and analysis engine to run in separate processes on the same machine for better crash recovery?

COM is a good solution for many applications, but the developer will still need to write code that handles concurrency requirements and, in some cases, named pipes provide a degree of flexibility that is preferable.

Thanks,
- Jim


Permalink
ubm_techweb_disqus_sso_-a0e9774e9b781685ffa591e4d217001a
2011-11-16T20:32:23

Would you use a similar implementation strategy in Linux?


Permalink
ubm_techweb_disqus_sso_-4c2cbc3186b383ea2625680a6b645870
2011-11-16T19:35:32

A nice write-up, but the example is a little stretched. The GUI application like the one you have on the screenshot can be done in .NET within 30 minutes.

Having done that your could just call your .NET code analysis directly from the GUI app, avoiding the threading (mostly) and IPC altogether.


Permalink
ubm_techweb_disqus_sso_-71fe90849f198c1debe4b67f14243c3d
2011-11-16T18:45:39

COM is your friend. In my experience, the easiest way to share code between the managed and native worlds is through COM. An alternative approach to that advocated here would be to move the bulk of the analyzer logic into a class library.

The console app would become a very thin bootstrap that created objects from the library. The library would mark its public interfaces as COMVisible. This would allow the C++ GUI app to create those objects and use them the same way.


Permalink

Video