IPC Functionality 


When injecting a DLL into other processes, your DLL usually needs to contact your base application somehow. This can be quite a difficult task, because you have to communicate over process boundaries here. The easiest way to do such IPC (inter process communication) is to use the message APIs like FindWindow + PostMessage. Unfortunately messages may not work inside of non interactive service/system processes. So we need different methods to build up a reliable IPC connection. Since this is a quite basic problem which will touch most madCodeHook users, I've decided to add some nice and easy to use IPC functions to madCodeHook.

Let's say your application injects a DLL into all processes system wide. Now the DLL(duplicate)s loaded into all the different processes want to contact your application and send some information. Maybe the DLLs even want to get an answer from the application. This all can be realized easily by using madCodeHook's IPC functionality. Before injecting the DLL, the application should create an IPC queue, giving in a unique IPC name and an appropriate IPC message handling function. This function will later be called for each incoming message.

CAUTION: When using the default parameters each IPC message is handled by a seperate thread, as a result your handler will be called in the context of a different thread each time. This is has some advantages and some disadvantages. One problem is that when using multiple threads Windows' time scheduler will do whatever it likes. This may result in that the order of the incoming messages is slightly different to how they were sent. If that is a problem for you, please specifiy a maximum thread count of "1". This will make sure that the order of the messages stays the same.

  // This is how you get notified about incoming ipc messages.
  // You have to write a function which fits to this type definition
  // and then you enter it into "CreateIpcQueue".
  TIpcCallback = procedure (name       : pchar;
                            messageBuf : pointer;
                            messageLen : dword;
                            answerBuf  : pointer;
                            answerLen  : dword;
                            context    : pointer); stdcall;

// Create an ipc queue.
// Please choose a unique ipc name to avoid conflicts with other programs.
// Only one ipc queue with the same name can be open at the same time.
// So if 2 programs try to create the same ipc queue, the second call will fail.
// You can specify how many threads may be created to handle incoming messages.
// If the order of the messages is crucial for you, set "maxThreadCount" to "1".
// In its current implementation "maxThreadCount" only supports "1" or unlimited.
// The parameter "maxQueueLen" is not yet implemented at all.
// The "context" is a simple pointer value that is forwarded to your callback.
function CreateIpcQueue (ipc            : pchar;
                         callback       : TIpcCallback;
                         context        : pointer = nil;
                         maxThreadCount : dword   = 16;
                         maxQueueLen    : dword   = $1000) : bool; stdcall;

// destroy the ipc queue again
// when the queue owning process quits, the ipc queue is automatically deleted
// only the queue owning process can destroy the queue
function DestroyIpcQueue (ipc: pchar) : bool; stdcall;

// Example:
procedure GetMsgFromDLL(name       : pchar;
                        messageBuf : pointer; messageLen : dword;
                        answerBuf  : pointer; answerLen  : dword;
                        context    : pointer); stdcall;
  // this code has to be thread safe!
  boolean(answerBuf^) := MessageBox(0, pchar(messageBuf), 'question', MB_YESNO) = ID_YES;

  CreateIpcQueue('JustAnExample', GetMsgFromDLL);

In order to send an IPC message to the application, the DLL just needs to call "SendIpcMessage". If no answer is needed, only the first 3 parameters need to be filled. If the message sender wants to get a reply, it can specify a wait timeout value. Also it can specify whether the current thread shall handle window messages while waiting for the IPC message answer.

If you call SendIpcMessage in the initialization (DLL_PROCESS_ATTACH) of a hook dll, then please set "handleMessages" to false. Because otherwise your hook dll might eat DDE messages. Besides, during hook dll initialization it doesn't make much sense to handle messages while waiting, anyway.

function SendIpcMessage(ipc            : pchar;
                        messageBuf     : pointer;
                        messageLen     : dword;
                        answerBuf      : pointer = nil;
                        answerLen      : dword   = 0;
                        answerTimeOut  : dword   = INFINITE;
                        handleMessages : bool    = true    ) : bool; stdcall;

// Example:
function AskApplication(question: string) : boolean;
  result := false;
  SendIpcMessage('JustAnExample', pchar(question), Length(question), @result, sizeOf(result));