Back to index

lightning-sunbird  0.9+nobinonly
ipcConnectionWin.cpp
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is Mozilla IPC.
00015  *
00016  * The Initial Developer of the Original Code is
00017  * Netscape Communications Corporation.
00018  * Portions created by the Initial Developer are Copyright (C) 2002
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *   Darin Fisher <darin@meer.net>
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #include <windows.h>
00039 
00040 #include "prprf.h"
00041 #include "prmon.h"
00042 #include "prthread.h"
00043 #include "plevent.h"
00044 
00045 #include "nsIServiceManager.h"
00046 #include "nsIEventQueue.h"
00047 #include "nsIEventQueueService.h"
00048 #include "nsAutoLock.h"
00049 
00050 #include "ipcConfig.h"
00051 #include "ipcLog.h"
00052 #include "ipcConnection.h"
00053 #include "ipcm.h"
00054 
00055 
00056 //-----------------------------------------------------------------------------
00057 // NOTE: this code does not need to link with anything but NSPR.  that is by
00058 //       design, so it can be easily reused in other projects that want to 
00059 //       talk with mozilla's IPC daemon, but don't want to depend on xpcom.
00060 //       we depend at most on some xpcom header files, but no xpcom runtime
00061 //       symbols are used.
00062 //-----------------------------------------------------------------------------
00063 
00064 
00065 //-----------------------------------------------------------------------------
00066 // windows message thread
00067 //-----------------------------------------------------------------------------
00068 
00069 #define IPC_WM_SENDMSG    (WM_USER + 0x1)
00070 #define IPC_WM_CALLBACK   (WM_USER + 0x2)
00071 #define IPC_WM_SHUTDOWN   (WM_USER + 0x3)
00072 
00073 static nsresult       ipcThreadStatus = NS_OK;
00074 static PRThread      *ipcThread = NULL;
00075 static PRMonitor     *ipcMonitor = NULL;
00076 static HWND           ipcDaemonHwnd = NULL;
00077 static HWND           ipcLocalHwnd = NULL;
00078 static PRBool         ipcShutdown = PR_FALSE; // not accessed on message thread!!
00079 
00080 //-----------------------------------------------------------------------------
00081 // window proc
00082 //-----------------------------------------------------------------------------
00083 
00084 static LRESULT CALLBACK
00085 ipcThreadWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
00086 {
00087     LOG(("got message [msg=%x wparam=%x lparam=%x]\n", uMsg, wParam, lParam));
00088 
00089     if (uMsg == WM_COPYDATA) {
00090         COPYDATASTRUCT *cd = (COPYDATASTRUCT *) lParam;
00091         if (cd && cd->lpData) {
00092             ipcMessage *msg = new ipcMessage();
00093             PRUint32 bytesRead;
00094             PRBool complete;
00095             PRStatus rv = msg->ReadFrom((const char *) cd->lpData, cd->cbData,
00096                                         &bytesRead, &complete);
00097             if (rv == PR_SUCCESS && complete)
00098                 IPC_OnMessageAvailable(msg); // takes ownership of msg
00099             else {
00100                 LOG(("  unable to deliver message [complete=%u]\n", complete));
00101                 delete msg;
00102             }
00103         }
00104         return TRUE;
00105     }
00106 
00107     if (uMsg == IPC_WM_SENDMSG) {
00108         ipcMessage *msg = (ipcMessage *) lParam;
00109         if (msg) {
00110             LOG(("  sending message...\n"));
00111             COPYDATASTRUCT cd;
00112             cd.dwData = GetCurrentProcessId();
00113             cd.cbData = (DWORD) msg->MsgLen();
00114             cd.lpData = (PVOID) msg->MsgBuf();
00115             SendMessageA(ipcDaemonHwnd, WM_COPYDATA, (WPARAM) hWnd, (LPARAM) &cd);
00116             LOG(("  done.\n"));
00117             delete msg;
00118         }
00119         return 0;
00120     }
00121 
00122     if (uMsg == IPC_WM_CALLBACK) {
00123         ipcCallbackFunc func = (ipcCallbackFunc) wParam;
00124         void *arg = (void *) lParam;
00125         (func)(arg);
00126         return 0;
00127     }
00128 
00129     if (uMsg == IPC_WM_SHUTDOWN) {
00130         IPC_OnConnectionEnd(NS_OK);
00131         PostQuitMessage(0);
00132         return 0;
00133     }
00134 
00135     return DefWindowProc(hWnd, uMsg, wParam, lParam);
00136 }
00137 
00138 //-----------------------------------------------------------------------------
00139 // ipc thread functions
00140 //-----------------------------------------------------------------------------
00141 
00142 static void
00143 ipcThreadFunc(void *arg)
00144 {
00145     LOG(("entering message thread\n"));
00146 
00147     DWORD pid = GetCurrentProcessId();
00148 
00149     WNDCLASS wc;
00150     memset(&wc, 0, sizeof(wc));
00151     wc.lpfnWndProc = ipcThreadWindowProc;
00152     wc.lpszClassName = IPC_CLIENT_WINDOW_CLASS;
00153     RegisterClass(&wc);
00154 
00155     char wName[sizeof(IPC_CLIENT_WINDOW_NAME_PREFIX) + 20];
00156     PR_snprintf(wName, sizeof(wName), "%s%u", IPC_CLIENT_WINDOW_NAME_PREFIX, pid);
00157 
00158     ipcLocalHwnd = CreateWindow(IPC_CLIENT_WINDOW_CLASS, wName,
00159                                 0, 0, 0, 10, 10, NULL, NULL, NULL, NULL);
00160 
00161     {
00162         nsAutoMonitor mon(ipcMonitor);
00163         if (!ipcLocalHwnd)
00164             ipcThreadStatus = NS_ERROR_FAILURE;
00165         mon.Notify();
00166     }
00167 
00168     if (ipcLocalHwnd) {
00169         MSG msg;
00170         while (GetMessage(&msg, ipcLocalHwnd, 0, 0))
00171             DispatchMessage(&msg);
00172 
00173         ipcShutdown = PR_TRUE; // assuming atomic memory write
00174 
00175         DestroyWindow(ipcLocalHwnd);
00176         ipcLocalHwnd = NULL;
00177     }
00178 
00179     LOG(("exiting message thread\n"));
00180     return;
00181 }
00182 
00183 static PRStatus
00184 ipcThreadInit()
00185 {
00186     if (ipcThread)
00187         return PR_FAILURE;
00188 
00189     ipcShutdown = PR_FALSE;
00190 
00191     ipcMonitor = PR_NewMonitor();
00192     if (!ipcMonitor)
00193         return PR_FAILURE;
00194 
00195     // spawn message thread
00196     ipcThread = PR_CreateThread(PR_USER_THREAD, ipcThreadFunc, NULL,
00197                                 PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
00198                                 PR_JOINABLE_THREAD, 0);
00199     if (!ipcThread) {
00200         NS_WARNING("thread creation failed");
00201         PR_DestroyMonitor(ipcMonitor);
00202         ipcMonitor = NULL;
00203         return PR_FAILURE;
00204     }
00205 
00206     // wait for hidden window to be created
00207     {
00208         nsAutoMonitor mon(ipcMonitor);
00209         while (!ipcLocalHwnd && NS_SUCCEEDED(ipcThreadStatus))
00210             mon.Wait();
00211     }
00212 
00213     if (NS_FAILED(ipcThreadStatus)) {
00214         NS_WARNING("message thread failed");
00215         return PR_FAILURE;
00216     }
00217 
00218     return PR_SUCCESS;
00219 }
00220 
00221 static PRStatus
00222 ipcThreadShutdown()
00223 {
00224     if (PR_AtomicSet(&ipcShutdown, PR_TRUE) == PR_FALSE) {
00225         LOG(("posting IPC_WM_SHUTDOWN message\n"));
00226         PostMessage(ipcLocalHwnd, IPC_WM_SHUTDOWN, 0, 0);
00227     }
00228 
00229     LOG(("joining w/ message thread...\n"));
00230     PR_JoinThread(ipcThread);
00231     ipcThread = NULL;
00232 
00233     //
00234     // ok, now the message thread is dead
00235     //
00236 
00237     PR_DestroyMonitor(ipcMonitor);
00238     ipcMonitor = NULL;
00239 
00240     return PR_SUCCESS;
00241 }
00242 
00243 //-----------------------------------------------------------------------------
00244 // windows specific IPC connection impl
00245 //-----------------------------------------------------------------------------
00246 
00247 nsresult
00248 IPC_Disconnect()
00249 {
00250     LOG(("IPC_Disconnect\n"));
00251 
00252     //XXX mHaveConnection = PR_FALSE;
00253     
00254     if (!ipcDaemonHwnd)
00255         return NS_ERROR_NOT_INITIALIZED;
00256 
00257     if (ipcThread)
00258         ipcThreadShutdown();
00259 
00260     // clear our reference to the daemon's HWND.
00261     ipcDaemonHwnd = NULL;
00262     return NS_OK;
00263 }
00264 
00265 nsresult
00266 IPC_Connect(const char *daemonPath)
00267 {
00268     LOG(("IPC_Connect\n"));
00269 
00270     NS_ENSURE_TRUE(ipcDaemonHwnd == NULL, NS_ERROR_ALREADY_INITIALIZED);
00271     nsresult rv;
00272 
00273     ipcDaemonHwnd = FindWindow(IPC_WINDOW_CLASS, IPC_WINDOW_NAME);
00274     if (!ipcDaemonHwnd) {
00275         LOG(("  daemon does not appear to be running\n"));
00276         //
00277         // daemon does not exist; spawn daemon...
00278         //
00279         rv = IPC_SpawnDaemon(daemonPath);
00280         if (NS_FAILED(rv))
00281             return rv;
00282 
00283         ipcDaemonHwnd = FindWindow(IPC_WINDOW_CLASS, IPC_WINDOW_NAME);
00284         if (!ipcDaemonHwnd)
00285             return NS_ERROR_FAILURE;
00286     }
00287 
00288     // 
00289     // delay creation of the message thread until we know the daemon exists.
00290     //
00291     if (!ipcThread && ipcThreadInit() != PR_SUCCESS) {
00292         ipcDaemonHwnd = NULL;
00293         return NS_ERROR_FAILURE;
00294     }
00295 
00296     return NS_OK;
00297 }
00298 
00299 nsresult
00300 IPC_SendMsg(ipcMessage *msg)
00301 {
00302     LOG(("IPC_SendMsg\n"));
00303 
00304     if (ipcShutdown) {
00305         LOG(("unable to send message b/c message thread is shutdown\n"));
00306         goto loser;
00307     }
00308     if (!PostMessage(ipcLocalHwnd, IPC_WM_SENDMSG, 0, (LPARAM) msg)) {
00309         LOG(("  PostMessage failed w/ error = %u\n", GetLastError()));
00310         goto loser;
00311     }
00312     return NS_OK;
00313 loser:
00314     delete msg;
00315     return NS_ERROR_FAILURE;
00316 }
00317 
00318 nsresult
00319 IPC_DoCallback(ipcCallbackFunc func, void *arg)
00320 {
00321     LOG(("IPC_DoCallback\n"));
00322 
00323     if (ipcShutdown) {
00324         LOG(("unable to send message b/c message thread is shutdown\n"));
00325         return NS_ERROR_FAILURE;
00326     }
00327     if (!PostMessage(ipcLocalHwnd, IPC_WM_CALLBACK, (WPARAM) func, (LPARAM) arg)) {
00328         LOG(("  PostMessage failed w/ error = %u\n", GetLastError()));
00329         return NS_ERROR_FAILURE;
00330     }
00331     return NS_OK;
00332 }