2

I am developing a plug-in for two 3rd party programs: A and B in C++ on Windows (7) and need a robust, relatively simple (and fast) way to communicate between the two programs.

The communication is one way: based on user interaction in program A I want my plug-in inside program A to send a signal that ends up calling a function inside my plug-in in program B.

The protocol is simple. This is the signature of the receiving function inside my plug-in in B:

struct XYZ { 
   double x, y, z;
}
void polyLineSelected(long id, std::vector<XYZ> & points);

How would you recommend to do this?

Andy
  • 3,251
  • 4
  • 32
  • 53
  • 1
    boost interprocess? One thing sure is that it's easy to use – emesx Sep 17 '13 at 18:03
  • 1
    How much data is there "per transaction"? (for example, how large is the `vector`? – Mats Petersson Sep 17 '13 at 18:04
  • Very small. Say max 20 points, often only 4. – Andy Sep 17 '13 at 18:06
  • 1
    A pipe or memory mapped file would seem reasonable. – WhozCraig Sep 17 '13 at 18:07
  • 1
    I would probably go with a named pipe then... – Mats Petersson Sep 17 '13 at 18:08
  • In which way this question isn't duplicate to this one: http://stackoverflow.com/q/372198/1025391 – moooeeeep Sep 17 '13 at 18:09
  • Have one application open a socket and listen for a connection; have the other open a socket to localhost and connect to it. Send data using standard BSD socket communications ... with the added benefit that if you ever needed to make this work in a distributed system, it is already written. – Zac Howland Sep 17 '13 at 18:24
  • @Zac There's nothing simple, robust, or fast about your solution. – IInspectable Sep 17 '13 at 18:29
  • @IInspectable: Simple: it uses a standard communication mechanism. Robust: It allows for expansion while still meeting the current need. Fast: connecting to a local machine (localhost) will create a named pipe (underneath) which will be just as fast (and in some cases faster - e.g. COPYDATA) than other proposed solutions. Not to mention that it is also much more portable (very little has to change if he were to suddenly port this to a POSIX system). – Zac Howland Sep 17 '13 at 19:42
  • @Zac Things don't become simple just because you call them standard. Your proposed solution is complex, once you implement it. You will not get the required functionality without creating at least one additional thread, both in the sender and receiver. It's not really robust either, unless you build robust synchronization around it. Now contrast that with `WM_COPYDATA`: The latter does not require multithreading and the synchronization is already there. Portability is completely irrelevant, the question is Windows-only. – IInspectable Sep 17 '13 at 20:46
  • @IInspectable Things are not un-simple (nor un-robust) just because you don't understand them. You do not need multiple threads to handle socket communication. Your solution assumes you know the `HWND` to the receiver. You have to get that from somewhere (which you completely ignore). Using sockets is no more complicated than that, and provides flexibility in the future. Your solution is equal in complexity (once you factor in the stuff you leave out), and is completely inflexible (making it hardly robust). And, the same synchronization issues apply to both. – Zac Howland Sep 17 '13 at 20:50
  • @Zac My solution requires to know the `HWND` of the receiver. Your 'solution' requires to know the port. You have to get that from somewhere (which you completely ignore). Now please, put your money where your mouth is, and offer a simple socket-based solution. – IInspectable Sep 18 '13 at 08:13
  • @IInspectable: I don't ignore the port issue - you (as the programmer) would set the port. The OS sets the value of the `HWND` - which is why you have to obtain it. And you want me to write yet another tutorial instead of linking one of the many that already exist on the net? (e.g. http://msdn.microsoft.com/en-us/library/ms740673%28VS.85%29.aspx, or http://stackoverflow.com/questions/2399377/examples-for-winsock). – Zac Howland Sep 18 '13 at 14:08
  • @Zac So, uhm... just to get this straight: You suggest to pick a fixed port? Sounds like a totally robust solution to me, no, really, I doubt anyone would listen on port 80, let's just use that. Now seriously, you claimed that your solution would be oh so simple. Why don't you just post it so that future visitors can benefit from your superior wisdom? Using winsock2 is cheating. This has got to be plain BSD sockets. – IInspectable Sep 18 '13 at 16:33
  • @IInspectable: And what is it exactly that you think common IPC programs do? Heaven forbid GChat/AIM/Y!Messenger, virtually every multiplayer video game on the planet (just to name a couple) pick a port above 5000 to use. – Zac Howland Sep 18 '13 at 17:09
  • @Zac I fail to see what sort of IPC a chat *client* would ever have a need for. This is not your *server*. Your rationale is flawed, you cannot simply pick a port to listen on localhost for connections. It fails the *"what if someone else did this"* test. In other words: You have to pick a port dynamically and communicate it to the sender. Now you need some sort of IPC for that. And you cannot use sockets. Looks like this is getting a tad bit complex here, and maybe we have to conclude, that the just-use-sockets-for-everything has stopped being cool in the 80s. – IInspectable Sep 18 '13 at 22:31

1 Answers1

0

By far the easiest way to implement one-way communication on Windows is to send a WM_COPYDATA message. It takes a COPYDATASTRUCT parameter to move arbitrary data from one application to another.

For your specific example an implementation of the sender would look like this:

// Declare symbolic constants to identify data
enum DataType {
    DataType_Points
};
// Declare struct to hold compound data
struct IPCData {
    long id;
    XYZ pts[];
};

// Allocate buffer
const size_t bufferSize = offsetof(IPCData, pts[points.size()]);
vector<char> buffer(bufferSize);
IPCData* pData = reinterpret_cast<IPCData*>(&buffer[0]);

// Fill the buffer
pData->id = 42;
copy(points.begin(), points.end(), &pData->pts[0]);

// Prepare COPYDDATASTRUCT
COPYDATASTRUCT cds = { 0 };
cds.dwData = DataType_Points;  // Can be used by the receiver to identify data
cds.cbData = bufferSize;
cds.lpData = pData;

// Send the data
SendMessage(hWndRecv, WM_COPYDATA,
            (WPARAM)hWndSender,
            (LPARAM)(LPVOID)&cds);
IInspectable
  • 46,945
  • 8
  • 85
  • 181
  • I am new to this method and just perused trough an example. Am I right in my understanding that the caller (A) uses the name of the process B to send it a message and that process B receives the message like any other event from the OS? – Andy Sep 17 '13 at 18:14
  • 1
    @Andy The caller needs to know the `HWND` of the receiver. The sample code at [Using Data Copy](http://msdn.microsoft.com/en-us/library/windows/desktop/ms649009.aspx) uses the target window's name to retrieve a handle. Either way your sender needs to retrieve the receiver's window handle. On the receiving side you handle `WM_COPYDATA` like any other message. – IInspectable Sep 17 '13 at 18:20
  • @Zac No, you don't ever want to use `PostMessage`. Read the [documentation](http://msdn.microsoft.com/en-us/library/windows/desktop/ms649011.aspx): *"While this message is being sent, the referenced data must not be changed by another thread of the sending process."* You want to block the sender (A) until the receiver (B) has finished handling the message. – IInspectable Sep 17 '13 at 21:08
  • Sorry, my point got cut off. If you use `SendMessage`, your thread in A will be blocked until B processes it. If B does not process it (or takes a long time to process it), A will remain deadlocked. If you are sending data back and forth using this mechanism, you can easily deadlock both programs. – Zac Howland Sep 17 '13 at 21:16
  • @Zac I don't see a deadlock scenario. [A thread can process inbound sent messages while waiting for an outgoing sent message to complete.](http://blogs.msdn.com/b/oldnewthing/archive/2004/06/08/150929.aspx) – IInspectable Sep 18 '13 at 17:43
  • 1
    B SendsMessage to A and waits for A to process the message before returning from the SendMessage call. If A does not process the message, B is "stuck" at the SendMessage call (yes, SendMessage has a message pump, but anything that was to happen after the SendMessage call is not going to get run). If A does the same, and B does not process the message, they are both stuck running their message loops and never completing the process they started. AKA: deadlock. – Zac Howland Sep 18 '13 at 17:56
  • @Zac If a message handler does not complete handling a message it is called a bug. There is no deadlock. I can't wait to see your proposed socket-based solution. It seems to be immune to all sorts of external factors, like bugs. Really, thrilled, cannot wait to see it. – IInspectable Sep 18 '13 at 18:04