diff --git a/xpfe/bootstrap/nsNativeAppSupportOS2.cpp b/xpfe/bootstrap/nsNativeAppSupportOS2.cpp index 7d8dd63bd1c..7e47f6a3f53 100644 --- a/xpfe/bootstrap/nsNativeAppSupportOS2.cpp +++ b/xpfe/bootstrap/nsNativeAppSupportOS2.cpp @@ -21,27 +21,127 @@ * Bill Law law@netscape.com * IBM Corp. */ + +// For server mode systray icon. +#include "nsIStringBundle.h" + #include "nsNativeAppSupportBase.h" -#include "nsNativeAppSupportOS2.h" #include "nsString.h" #include "nsICmdLineService.h" #include "nsCOMPtr.h" #include "nsXPIDLString.h" #include "nsIComponentManager.h" #include "nsIServiceManager.h" +#include "nsICmdLineHandler.h" #include "nsIDOMWindow.h" #include "nsISupportsPrimitives.h" #include "nsIWindowWatcher.h" +#include "nsIDOMWindowInternal.h" +#include "nsIScriptGlobalObject.h" +#include "nsIDocShell.h" +#include "nsIBaseWindow.h" +#include "nsIWidget.h" +#include "nsIAppShellService.h" +#include "nsIProfileInternal.h" +#include "rdf.h" + +// These are needed to load a URL in a browser window. +#include "nsIDOMLocation.h" +#include "nsIJSContextStack.h" +#include "nsIWindowMediator.h" + #define INCL_PM #define INCL_GPI #define INCL_DOS +#define INCL_DOSERRORS #include -//#include #include #include #include #include +#include "nsNativeAppSupportOS2.h" + +#define TURBO_QUIT 1 + + +// getting from nsAppRunner. Use to help track down arguments. +extern char ** __argv; +extern int __argc; + + +typedef ULONG DWORD; +typedef PBYTE LPBYTE; +typedef HDATA HDDEDATA; +#define CP_WINANSI 0 // When 0 is specified for codepage on these + // dde functions, it will use the codepage + // that is associated with the current thread. + // CP_WINANSI in win32 means that the non + // unicode version of DdeCreateStringHandle + // was used. + +// Adding to support DDE +#define ID_MESSAGEWINDOW 0xDEAF +/* trying to keep this like Window's COPYDATASTRUCT, but a compiler error is + * forcing me to add chBuff so that we can append the data to the end of the + * structure + */ +typedef struct _COPYDATASTRUCT +{ + ULONG dwData; + ULONG cbData; + PVOID lpData; + CHAR chBuff; +}COPYDATASTRUCT, *PCOPYDATASTRUCT; +#define WM_COPYDATA (WM_USER + 60) + +char szCommandLine[2*CCHMAXPATH]; + + +static HWND hwndForDOMWindow( nsISupports * ); + +static +nsresult +GetMostRecentWindow(const PRUnichar* aType, nsIDOMWindowInternal** aWindow) { + nsresult rv; + nsCOMPtr med( do_GetService( NS_RDF_DATASOURCE_CONTRACTID_PREFIX "window-mediator", &rv ) ); + if ( NS_FAILED( rv ) ) + return rv; + + if ( med ) + return med->GetMostRecentWindow( aType, aWindow ); + + return NS_ERROR_FAILURE; +} + +static +void +activateWindow( nsIDOMWindowInternal *win ) { + // Try to get native window handle. + HWND hwnd = hwndForDOMWindow( win ); + if ( hwnd ) { + + /* if we are looking at a client window, then we really probably want + * the frame so that we can manipulate THAT + */ + LONG id = WinQueryWindowUShort( hwnd, QWS_ID ); + if( id == FID_CLIENT ) + { + hwnd = WinQueryWindow( hwnd, QW_PARENT ); + } + + // Restore the window in case it is minimized. + // Use the OS call, if possible. + WinSetWindowPos( hwnd, 0L, 0L, 0L, 0L, 0L, + SWP_SHOW | SWP_RESTORE | SWP_ACTIVATE ); + } else { + // Use internal method. + win->Focus(); + } +} + + + // Define this macro to 1 to get DDE debugging output. #define MOZ_DEBUG_DDE 0 @@ -104,60 +204,58 @@ public: MRESULT EXPENTRY DialogProc( HWND dlg, ULONG msg, MPARAM mp1, MPARAM mp2 ); void _Optlink ThreadProc (void *splashScreen); -#ifndef XP_OS2 // Simple Win32 mutex wrapper. struct Mutex { Mutex( const char *name ) : mName( name ), mHandle( 0 ), mState( -1 ) { - mHandle = CreateMutex( 0, FALSE, mName.get() ); - #if MOZ_DEBUG_DDE + DosCreateMutexSem( mName.get(), &mHandle, 0, FALSE ); +#if MOZ_DEBUG_DDE printf( "CreateMutex error = 0x%08X\n", (int)GetLastError() ); - #endif +#endif } ~Mutex() { if ( mHandle ) { // Make sure we release it if we own it. Unlock(); - BOOL rc = CloseHandle( mHandle ); - #if MOZ_DEBUG_DDE - if ( !rc ) { + APIRET rc = DosCloseMutexSem( mHandle ); +#if MOZ_DEBUG_DDE + if ( rc != NO_ERROR ) { printf( "CloseHandle error = 0x%08X\n", (int)GetLastError() ); } - #endif +#endif } } BOOL Lock( DWORD timeout ) { if ( mHandle ) { - #if MOZ_DEBUG_DDE +#if MOZ_DEBUG_DDE printf( "Waiting (%d msec) for DDE mutex...\n", (int)timeout ); - #endif - mState = WaitForSingleObject( mHandle, timeout ); - #if MOZ_DEBUG_DDE +#endif + mState = DosRequestMutexSem( mHandle, timeout ); +#if MOZ_DEBUG_DDE printf( "...wait complete, result = 0x%08X\n", (int)mState ); - #endif - return mState == WAIT_OBJECT_0; +#endif + return mState == NO_ERROR; } else { return FALSE; } } void Unlock() { - if ( mHandle && mState == WAIT_OBJECT_0 ) { - #if MOZ_DEBUG_DDE + if ( mHandle && mState == NO_ERROR ) { +#if MOZ_DEBUG_DDE printf( "Releasing DDE mutex\n" ); - #endif - ReleaseMutex( mHandle ); +#endif + DosReleaseMutexSem( mHandle ); mState = -1; } } private: nsCString mName; - int mHandle; + HMTX mHandle; DWORD mState; }; -#endif /* DDE Notes * @@ -223,35 +321,86 @@ private: * they fail harmlessly. Refinements on this point will be made as * details emerge (and time permits). */ + +/* Update 2001 March + * + * A significant DDE bug in Windows is causing Mozilla to get wedged at + * startup. This is detailed in Bugzill bug 53952 + * (http://bugzilla.mozilla.org/show_bug.cgi?id=53952). + * + * To resolve this, we are using a new strategy: + * o Use a "message window" to detect that Mozilla is already running and + * to pass requests from a second instance back to the first; + * o Run only as a "DDE server" (not as DDE client); this avoids the + * problematic call to DDEConnect(). + * + * We still use the mutex semaphore to protect the code that detects + * whether Mozilla is already running. + */ + class nsNativeAppSupportOS2 : public nsNativeAppSupportBase { -#ifndef XP_OS2 public: // Overrides of base implementation. NS_IMETHOD Start( PRBool *aResult ); NS_IMETHOD Stop( PRBool *aResult ); NS_IMETHOD Quit(); + NS_IMETHOD StartServerMode(); + NS_IMETHOD SetIsServerMode( PRBool isServerMode ); + + // The "old" Start method (renamed). + NS_IMETHOD StartDDE(); // Utility function to handle a Win32-specific command line // option: "-console", which dynamically creates a Windows // console. - static void CheckConsole(); + void CheckConsole(); private: - static HDDEDATA CALLBACK HandleDDENotification( UINT uType, - UINT uFmt, - HCONV hconv, - HSZ hsz1, - HSZ hsz2, - HDDEDATA hdata, - ULONG dwData1, - ULONG dwData2 ); - static void HandleRequest( LPBYTE request ); + static HDDEDATA APIENTRY HandleDDENotification( ULONG idInst, + USHORT uType, + USHORT uFmt, + HCONV hconv, + HSZ hsz1, + HSZ hsz2, + HDDEDATA hdata, + ULONG dwData1, + ULONG dwData2 ); + static void HandleRequest( LPBYTE request, PRBool newWindow = PR_TRUE ); + static nsCString ParseDDEArg( HSZ args, int index ); + static void ActivateLastWindow(); + static HDDEDATA CreateDDEData( DWORD value ); + static PRBool InitTopicStrings(); + static int FindTopic( HSZ topic ); static nsresult GetCmdLineArgs( LPBYTE request, nsICmdLineService **aResult ); + static nsresult EnsureProfile(nsICmdLineService* args); static nsresult OpenWindow( const char *urlstr, const char *args ); + static nsresult OpenBrowserWindow( const char *args, PRBool newWindow = PR_TRUE ); + static nsresult ReParent( nsISupports *window, HWND newParent ); + static nsresult GetStartupURL(nsICmdLineService *args, nsCString& taskURL); + static void SetupSysTrayIcon(); + static void RemoveSysTrayIcon(); + + static int mConversations; - static HSZ mApplication, mTopic; + enum { + topicOpenURL, + topicActivate, + topicCancelProgress, + topicVersion, + topicRegisterViewer, + topicUnRegisterViewer, + // Note: Insert new values above this line!!!!! + topicCount // Count of the number of real topics + }; +#ifdef DOWENEED + static NOTIFYICONDATA mIconData; + static HMENU mTrayIconMenu; +#endif /* DOWENEED */ + + static HSZ mApplication, mTopics[ topicCount ]; static DWORD mInstance; -#endif + static char *mAppName; + friend struct MessageWindow; }; // nsNativeAppSupportOS2 nsSplashScreenOS2::nsSplashScreenOS2() @@ -276,6 +425,24 @@ nsSplashScreenOS2::Show() { NS_IMETHODIMP nsSplashScreenOS2::Hide() { if ( mDlg ) { +#ifdef DOWENEED + // Fix for bugs: + // http://bugzilla.mozilla.org/show_bug.cgi?id=26581 + // http://bugzilla.mozilla.org/show_bug.cgi?id=65974 + // http://bugzilla.mozilla.org/show_bug.cgi?id=29172 + // http://bugzilla.mozilla.org/show_bug.cgi?id=45805 + // As the splash-screen is in a seperate thread, Windows considers + // this the "foreground" thread. When our main windows on the main + // thread are activated, they are treated like windows from a different + // application, so Windows 2000 and 98 both leave the window in the background. + // Therefore, we post a message to the splash-screen thread that includes + // the hwnd of the window we want moved to the foreground. This thread + // can then successfully bring the top-level window to the foreground. + nsCOMPtr topLevel; + GetMostRecentWindow(nsnull, getter_AddRefs( topLevel ) ); + HWND hWndTopLevel = topLevel ? hwndForDOMWindow(topLevel) : 0; +#endif /* DOWENEED */ + // Dismiss the dialog. WinPostMsg(mDlg, WM_CLOSE, 0, 0); // Release custom bitmap (if there is one). @@ -374,9 +541,26 @@ MRESULT EXPENTRY DialogProc( HWND dlg, ULONG msg, MPARAM mp1, MPARAM mp2 ) { WinEndPaint( hps ); return (MRESULT)TRUE; } +#ifdef DOWENEED + else if (msg == WM_CLOSE) { + // Before killing ourself, set the top-level current. + // See comments in nsSplashScreenWin::Hide() above. + HWND topLevel = (HWND)mp2; + if (topLevel) + ::SetForegroundWindow(topLevel); + // Destroy the dialog + ::EndDialog(dlg, 0); + // Release custom bitmap (if there is one). + HBITMAP bitmap = (HBITMAP)wp; + if ( bitmap ) { + ::DeleteObject( bitmap ); + } + } +#endif /* DOWENEED */ return WinDefDlgProc (dlg, msg, mp1, mp2); } + void nsSplashScreenOS2::SetDialog( HWND dlg ) { // Save dialog handle. mDlg = dlg; @@ -399,13 +583,13 @@ void _Optlink ThreadProc(void *splashScreen) { // _endthread(); } -#ifndef XP_OS2 void nsNativeAppSupportOS2::CheckConsole() { for ( int i = 1; i < __argc; i++ ) { if ( strcmp( "-console", __argv[i] ) == 0 || strcmp( "/console", __argv[i] ) == 0 ) { +#ifdef DOWENEED // Users wants to make sure we have a console. // Try to allocate one. BOOL rc = ::AllocConsole(); @@ -452,20 +636,35 @@ nsNativeAppSupportOS2::CheckConsole() { } // Don't bother doing this more than once. break; +#endif /* DOWENEED */ + } else if ( strcmp( "-turbo", __argv[i] ) == 0 + || + strcmp( "/turbo", __argv[i] ) == 0 + || + strcmp( "-server", __argv[i] ) == 0 + || + strcmp( "/server", __argv[i] ) == 0 ) { + // Start in server mode (and suppress splash screen). + SetIsServerMode( PR_TRUE ); + __argv[i] = "-nosplash"; // Bit of a hack, but it works! + // Ignore other args. + break; } } return; } -#endif // Create and return an instance of class nsNativeAppSupportOS2. nsresult NS_CreateNativeAppSupport( nsINativeAppSupport **aResult ) { if ( aResult ) { - *aResult = new nsNativeAppSupportOS2; - if ( *aResult ) { - NS_ADDREF( *aResult ); + nsNativeAppSupportOS2 *pNative = new nsNativeAppSupportOS2; + if ( pNative ) { + *aResult = pNative; + NS_ADDREF( *aResult ); + // Check for dynamic console creation request. + pNative->CheckConsole(); } else { return NS_ERROR_OUT_OF_MEMORY; } @@ -473,10 +672,6 @@ NS_CreateNativeAppSupport( nsINativeAppSupport **aResult ) { return NS_ERROR_NULL_POINTER; } -#ifndef XP_OS2 - // Check for dynamic console creation request. - nsNativeAppSupportOS2::CheckConsole(); -#endif return NS_OK; } @@ -513,24 +708,376 @@ NS_CreateSplashScreen( nsISplashScreen **aResult ) { return NS_OK; } -#ifndef XP_OS2 // Constants -#define MOZ_DDE_APPLICATION "Mozilla" -#define MOZ_DDE_TOPIC "WWW_OpenURL" -#define MOZ_DDE_MUTEX_NAME "MozillaDDEMutex" +#define MOZ_DDE_APPLICATION "Mozilla" +#ifdef XP_OS2 +/* os/2 named semaphores must begin with "\\SEM32\\" to be valid */ +#define MOZ_MUTEX_PREPEND "\\SEM32\\" +#endif /* XP_OS2 */ +#define MOZ_STARTUP_MUTEX_NAME "StartupMutex" #define MOZ_DDE_START_TIMEOUT 30000 #define MOZ_DDE_STOP_TIMEOUT 15000 #define MOZ_DDE_EXEC_TIMEOUT 15000 +// The array entries must match the enum ordering! +const char * const topicNames[] = { "WWW_OpenURL", + "WWW_Activate", + "WWW_CancelProgress", + "WWW_Version", + "WWW_RegisterViewer", + "WWW_UnRegisterViewer" }; + // Static member definitions. int nsNativeAppSupportOS2::mConversations = 0; HSZ nsNativeAppSupportOS2::mApplication = 0; -HSZ nsNativeAppSupportOS2::mTopic = 0; +HSZ nsNativeAppSupportOS2::mTopics[nsNativeAppSupportOS2::topicCount] = { 0 }; DWORD nsNativeAppSupportOS2::mInstance = 0; -// Try to initiate DDE conversation. If that succeeds, pass -// request to server process. Otherwise, register application -// and topic (i.e., become server process). + +// Added this as pmddeml has no api like this +int DdeCmpStringHandles( HSZ hsz1, HSZ hsz2 ) +{ + char chhsz1[CCHMAXPATH], chhsz2[CCHMAXPATH]; + int rc = -1; + + /* I am assuming that the strings will never be more that CCHMAXPATH in + * length. Safe bet (for now). To find true length, call WinDdeQueryString + * and pass in (hsz, NULL, 0L, 0) and it will return iLength. Passing 0 + * for codepage will use the codepage of the current thread. + */ + WinDdeQueryString( hsz1, chhsz1, sizeof( chhsz1 ), 0 ); + WinDdeQueryString( hsz2, chhsz2, sizeof( chhsz2 ),0 ); + + rc = stricmp( chhsz1, chhsz2 ); + + return(rc); +} + + +char *GetCommandLine() +{ + /* This function is meant to be like the Window's GetCommandLine() function. + * The value returned by pPIB->pib_pchcmd is of the format: + * \0. So the first string is the + * .exe and the second string is what followed on the command line. Dorky, + * but I guess it'll have to do. + */ + PTIB pTIB = NULL; + PPIB pPIB = NULL; + APIRET rc = NO_ERROR; + char *pchParam = NULL; + + rc = DosGetInfoBlocks( &pTIB, &pPIB ); + if( rc == NO_ERROR ) + { + INT iLen = 0; + char *pchTemp = NULL; + pchParam = pPIB->pib_pchcmd; + strcpy( szCommandLine, pchParam ); + iLen = strlen( pchParam ); + + /* szCommandLine[iLen] is '\0', so see what's next. Probably be another + * '\0', a space or start of the arguments + */ + pchTemp = &(pchParam[iLen+1]); + + /* At this point, szCommandLine holds just the program name. Now we + * go for the parameters. If our next value is a space, ignore it + * and go for the next character + */ + if( *pchTemp ) + { + szCommandLine[iLen] = ' '; + iLen++; + if( *pchTemp == ' ' ) + { + pchTemp++; + } + strcpy( &(szCommandLine[iLen]), pchTemp ); + } + + } + + return( szCommandLine ); +} + +typedef struct _DDEMLFN +{ + PFN *fn; + ULONG ord; +} DDEMLFN, *PDDEMLFN; + +DDEMLFN ddemlfnTable[] = +{ + { (PFN *)&WinDdeAbandonTransaction ,100 }, + { (PFN *)&WinDdeAccessData ,101 }, + { (PFN *)&WinDdeAddData ,102 }, + { (PFN *)&WinDdeSubmitTransaction ,103 }, + { (PFN *)&WinDdeCompareStringHandles ,104 }, + { (PFN *)&WinDdeConnect ,105 }, + { (PFN *)&WinDdeConnectList ,106 }, + { (PFN *)&WinDdeCreateDataHandle ,107 }, + { (PFN *)&WinDdeCreateStringHandle ,108 }, + { (PFN *)&WinDdeDisconnect ,109 }, + { (PFN *)&WinDdeDisconnectList ,110 }, + { (PFN *)&WinDdeEnableCallback ,111 }, + { (PFN *)&WinDdeFreeDataHandle ,112 }, + { (PFN *)&WinDdeFreeStringHandle ,113 }, + { (PFN *)&WinDdeGetData ,114 }, + { (PFN *)&WinDdeInitialize ,116 }, + { (PFN *)&WinDdeKeepStringHandle ,117 }, + { (PFN *)&WinDdeNameService ,118 }, + { (PFN *)&WinDdePostAdvise ,119 }, + { (PFN *)&WinDdeQueryConvInfo ,120 }, + { (PFN *)&WinDdeQueryNextServer ,121 }, + { (PFN *)&WinDdeQueryString ,122 }, + { (PFN *)&WinDdeReconnect ,123 }, + { (PFN *)&WinDdeSetUserHandle ,124 }, + { (PFN *)&WinDdeUninitialize ,126 }, + { (PFN *)NULL , 0 } +}; + +/* SetupOS2ddeml makes sure that we can get pointers to all of the DDEML + * functions in demlfnTable using entrypoints. If we can't find one of them + * or can't load pmddeml.dll, then returns FALSE + */ +BOOL SetupOS2ddeml() +{ + BOOL bRC = FALSE; + HMODULE hmodDDEML = NULLHANDLE; + APIRET rc = NO_ERROR; + ULONG ulVersion = 0; + + rc = DosLoadModule( NULL, 0, "PMDDEML", &hmodDDEML ); + if( rc == NO_ERROR ) + { + int i=0; + /* all of this had better work. Get ready to be a success! */ + bRC = TRUE; + for( i; ddemlfnTable[i].ord != 0; i++ ) + { + rc = DosQueryProcAddr( hmodDDEML, ddemlfnTable[i].ord, NULL, + ddemlfnTable[i].fn ); + if( rc != NO_ERROR ) + { + /* we're horked. Return rc = horked */ + bRC = FALSE; + break; + } + } + } /* load PMDDEML */ + + return( bRC ); +} + +#ifdef DOWENEED +NOTIFYICONDATA nsNativeAppSupportOS2::mIconData = { sizeof(NOTIFYICONDATA), + 0, + 1, + NIF_ICON | NIF_MESSAGE | NIF_TIP, + WM_USER, + 0, + 0 }; +HMENU nsNativeAppSupportOS2::mTrayIconMenu = 0; +#endif /* DOWENEED */ + + +// Message window encapsulation. +struct MessageWindow { + // ctor/dtor are simplistic + MessageWindow() { + // Try to find window. + // XXX need to improve this to use check the class + mHandle = WinWindowFromID( HWND_OBJECT, ID_MESSAGEWINDOW ); + } + + // Act like an HWND. + operator HWND() { + return mHandle; + } + + // Class name: appName + "MessageWindow" + static const char *className() { + static char classNameBuffer[128]; + static char *mClassName = 0; + if ( !mClassName ) { + sprintf( classNameBuffer, + "%s%s", + nsNativeAppSupportOS2::mAppName, + "MessageWindow" ); + mClassName = classNameBuffer; + } + return mClassName; + } + + // Create: Register class and create window. + NS_IMETHOD Create() { + + // Register the window class. + NS_ENSURE_TRUE( WinRegisterClass( 0, className(), + (PFNWP)&MessageWindow::WindowProc, + 0L, 0 ), + NS_ERROR_FAILURE ); + + /* Giving a size but offscreen so that won't be seen, even if all + * goes wrong. Giving a size so that can be seen and understood by + * tools + */ + const char * pszClassName = className(); + mHandle = WinCreateWindow( HWND_OBJECT, + pszClassName, + NULL, // name + WS_DISABLED, // style + 0,0, // x, y + 0,0, // cx,cy + HWND_DESKTOP,// owner + HWND_BOTTOM, // hwndbehind + ID_MESSAGEWINDOW, // id + NULL, // pCtlData + NULL ); // pres params + +#if MOZ_DEBUG_DDE + printf( "Message window = 0x%08X\n", (int)mHandle ); +#endif + + return NS_OK; + } + + // SendRequest: Pass string via WM_COPYDATA to message window. + NS_IMETHOD SendRequest( const char *cmd ) { +#ifdef XP_OS2 + /* Nothing like WM_COPYDATA in OS/2, where the OS allows pointers to be + * passed to a different process and automatically accessible by that + * process. So we have to create shared mem on our side and then the + * process that gets the WM_COPYDATA message has to do a + * DosGetSharedMem on this pointer to be able to access the data + */ + + COPYDATASTRUCT *pcds; + APIRET rc = NO_ERROR; + PVOID pvData = NULL; + ULONG ulSize = sizeof(COPYDATASTRUCT)+strlen(cmd)+1; + rc = DosAllocSharedMem( &pvData, NULL, ulSize, + (PAG_COMMIT|PAG_READ|PAG_WRITE|OBJ_GETTABLE) ); + + if( rc != NO_ERROR ) + { + /* don't even try doing anything else. Windows doesn't worry about + * errors so I guess that we shouldn't either + */ + return NS_OK; + } + + memset( pvData, '\0', ulSize ); + pcds = (COPYDATASTRUCT *)(pvData); + pcds->dwData = 0; + pcds->cbData = strlen(cmd)+1; + /* put the data in the buffer space immediately after the + * COPYDATASTRUCT + */ + pcds->lpData = &(pcds->chBuff); + if( cmd ) + { + strcpy( (char *)pcds->lpData, cmd ); + } + WinSendMsg( mHandle, WM_COPYDATA, 0, (MPARAM)pcds ); + DosFreeMem( pvData ); +#else + COPYDATASTRUCT cds = { 0, strlen( cmd ) + 1, (void*)cmd }; + ::SendMessage( mHandle, WM_COPYDATA, 0, (LPARAM)&cds ); +#endif /* XP_OS2 */ + + return NS_OK; + } + + // Window proc. + static MRESULT EXPENTRY WindowProc( HWND msgWindow, ULONG msg, MPARAM wp, + MPARAM lp ) + { + MRESULT rc = (MRESULT)TRUE; + + if ( msg == WM_COPYDATA ) { + // This is an incoming request. + COPYDATASTRUCT *cds = (COPYDATASTRUCT*)lp; +#ifdef XP_OS2 + DosGetSharedMem( (PVOID)cds, PAG_READ|PAG_WRITE ); +#endif /* XP_OS2 */ +#if MOZ_DEBUG_DDE + printf( "Incoming request: %s\n", (const char*)cds->lpData ); +#endif + (void)nsNativeAppSupportOS2::HandleRequest( (LPBYTE)cds->lpData ); + } +#ifdef DOWENEED + else if ( msg == WM_USER ) { + if ( lp == WM_RBUTTONUP ) { + // Show menu with Exit disabled/enabled appropriately. + nsCOMPtr win; + GetMostRecentWindow( 0, getter_AddRefs( win ) ); + ::EnableMenuItem( nsNativeAppSupportWin::mTrayIconMenu, 1, win ? MF_GRAYED : MF_ENABLED ); + POINT pt; + GetCursorPos( &pt ); + + SetForegroundWindow(msgWindow); + int selectedItem = ::TrackPopupMenu( nsNativeAppSupportWin::mTrayIconMenu, + TPM_NONOTIFY | TPM_RETURNCMD | + TPM_RIGHTBUTTON, + pt.x, + pt.y, + 0, + msgWindow, + 0 ); + + switch (selectedItem) { + case TURBO_QUIT: + (void)nsNativeAppSupportWin::HandleRequest( (LPBYTE)"mozilla -kill" ); + break; + } + PostMessage(msgWindow, WM_NULL, 0, 0); + } else if ( lp == WM_LBUTTONDBLCLK ) { + // Activate window or open a new one. + nsCOMPtr win; + GetMostRecentWindow( 0, getter_AddRefs( win ) ); + if ( win ) { + activateWindow( win ); + } else { + // This will do the default thing and open a new window. + (void)nsNativeAppSupportWin::HandleRequest( (LPBYTE)"mozilla" ); + } + } + } +#endif /* DOWENEED */ + +#ifdef XP_OS2 + /* We have to return a FALSE from WM_CREATE or this window will never + * get off of the ground + */ + else if ( msg == WM_CREATE ) { + rc = (MRESULT)FALSE; + } +#endif /* XP_OS2 */ + + return rc; +} + +private: + HWND mHandle; +}; // struct MessageWindow + +static char nameBuffer[128] = { 0 }; +char *nsNativeAppSupportOS2::mAppName = nameBuffer; + +/* Start: Tries to find the "message window" to determine if it + * exists. If so, then Mozilla is already running. In that + * case, we use the handle to the "message" window and send + * a request corresponding to this process's command line + * options. + * + * If not, then this is the first instance of Mozilla. In + * that case, we create and set up the message window. + * + * The checking for existance of the message window must + * be protected by use of a mutex semaphore. + */ NS_IMETHODIMP nsNativeAppSupportOS2::Start( PRBool *aResult ) { NS_ENSURE_ARG( aResult ); @@ -539,106 +1086,144 @@ nsNativeAppSupportOS2::Start( PRBool *aResult ) { nsresult rv = NS_ERROR_FAILURE; *aResult = PR_FALSE; - // Grab mutex before doing DdeInitialize! This is - // important (see comment above). + // Grab mutex first. int retval; UINT id = ID_DDE_APPLICATION_NAME; char nameBuf[ 128 ]; - retval = LoadString( (HINSTANCE) NULL, id, (LPTSTR) nameBuf, sizeof(nameBuf) ); - if ( retval != 0 ) { - Mutex ddeLock = Mutex( MOZ_DDE_MUTEX_NAME ); - if ( ddeLock.Lock( MOZ_DDE_START_TIMEOUT ) ) { - // Initialize DDE. - UINT rc = DdeInitialize( &mInstance, - nsNativeAppSupportOS2::HandleDDENotification, - APPCLASS_STANDARD, - 0 ); - if ( rc == DMLERR_NO_ERROR ) { - mApplication = DdeCreateStringHandle( mInstance, - nameBuf, - CP_WINANSI ); - mTopic = DdeCreateStringHandle( mInstance, - MOZ_DDE_TOPIC, - CP_WINANSI ); - if ( mApplication && mTopic ) { - // Everything OK so far, try to connect to previusly - // started Mozilla. - HCONV hconv = DdeConnect( mInstance, mApplication, mTopic, 0 ); - - if ( hconv ) { - // We're the client... - // Get command line to pass to server. - LPTSTR cmd = GetCommandLine(); - #if MOZ_DEBUG_DDE - printf( "Acting as DDE client, cmd=%s\n", cmd ); - #endif - rc = (UINT)DdeClientTransaction( (LPBYTE)cmd, - strlen( cmd ) + 1, - hconv, - 0, - 0, - XTYP_EXECUTE, - MOZ_DDE_EXEC_TIMEOUT, - 0 ); - if ( rc ) { - // Inform caller that request was issued. - rv = NS_OK; - } else { - // Something went wrong. Not much we can do, though... - #if MOZ_DEBUG_DDE - printf( "DdeClientTransaction failed, error = 0x%08X\n", - (int)DdeGetLastError( mInstance ) ); - #endif - } - } else { - // We're going to be the server... - #if MOZ_DEBUG_DDE - printf( "Setting up DDE server...\n" ); - #endif - - // Next step is to register a DDE service. - rc = (UINT)DdeNameService( mInstance, mApplication, 0, DNS_REGISTER ); - - if ( rc ) { - #if MOZ_DEBUG_DDE - printf( "...DDE server started\n" ); - #endif - // Tell app to do its thing. - *aResult = PR_TRUE; - rv = NS_OK; - } else { - #if MOZ_DEBUG_DDE - printf( "DdeNameService failed, error = 0x%08X\n", - (int)DdeGetLastError( mInstance ) ); - #endif - } - } - } else { - #if MOZ_DEBUG_DDE - printf( "DdeCreateStringHandle failed, error = 0x%08X\n", - (int)DdeGetLastError( mInstance ) ); - #endif - } - } else { - #if MOZ_DEBUG_DDE - printf( "DdeInitialize failed, error = 0x%08X\n", (int)rc ); - #endif - } - - // Release mutex. - ddeLock.Unlock(); - } - } - - // Clean up. The only case in which we need to preserve DDE stuff - // is if we're going to be acting as server. - if ( !*aResult ) { - Quit(); + retval = WinLoadString( NULLHANDLE, NULLHANDLE, id, sizeof(nameBuffer), nameBuffer ); + if ( retval == 0 ) { + // No app name; just keep running. + *aResult = PR_TRUE; + return NS_OK; } + // Build mutex name from app name. + char mutexName[128]; + sprintf( mutexName, "%s%s%s", MOZ_MUTEX_PREPEND, nameBuffer, MOZ_STARTUP_MUTEX_NAME ); + Mutex startupLock = Mutex( mutexName ); + + NS_ENSURE_TRUE( startupLock.Lock( MOZ_DDE_START_TIMEOUT ), NS_ERROR_FAILURE ); + +#ifdef XP_OS2 + /* We need to have a message queue to do the MessageWindow stuff (for both + * Create() and SendRequest()). If we don't have one, make it now. + * If we are going to end up going away right after ::Start() returns, + * then make sure to clean up the message queue. + */ + MQINFO mqinfo; + HAB hab; + HMQ hmqCurrent = WinQueryQueueInfo( HMQ_CURRENT, &mqinfo, + sizeof( MQINFO ) ); + if( !hmqCurrent ) + { + hab = WinInitialize( 0 ); + hmqCurrent = WinCreateMsgQueue( hab, 0 ); + } + +#endif /* XP_OS */ + + // Search for existing message window. + MessageWindow msgWindow; + if ( (HWND)msgWindow ) { + // We are a client process. Pass request to message window. + char *cmd = GetCommandLine(); + rv = msgWindow.SendRequest( cmd ); + } else { + // We will be server. + rv = msgWindow.Create(); + if ( NS_SUCCEEDED( rv ) ) { + // Start up DDE server. + this->StartDDE(); + // Tell caller to spin message loop. + *aResult = PR_TRUE; + } + } + + startupLock.Unlock(); + +#ifdef XP_OS2 + if( *aResult == PR_FALSE ) + { + /* This process isn't going to continue much longer. Make sure that we + * clean up the message queue + */ + if( hmqCurrent ) + { + WinDestroyMsgQueue( hmqCurrent ); + } + if( hab ) + { + WinTerminate( hab ); + } + } +#endif /* XP_OS2 */ + return rv; } +PRBool +nsNativeAppSupportOS2::InitTopicStrings() { + for ( int i = 0; i < topicCount; i++ ) { + if ( !( mTopics[ i ] = WinDdeCreateStringHandle( (PSZ)topicNames[ i ], CP_WINANSI ) ) ) { + return PR_FALSE; + } + } + return PR_TRUE; +} + +int +nsNativeAppSupportOS2::FindTopic( HSZ topic ) { + for ( int i = 0; i < topicCount; i++ ) { + if ( DdeCmpStringHandles( topic, mTopics[i] ) == 0 ) { + return i; + } + } + return -1; +} + +// Start DDE server. +// +// This used to be the Start() method when we were using DDE as the +// primary IPC mechanism between secondary Mozilla processes and the +// initial "server" process. +// +// Now, it simply initializes the DDE server. The caller must check +// that this process is to be the server, and, must acquire the DDE +// startup mutex semaphore prior to calling this routine. See ::Start(), +// above. +NS_IMETHODIMP +nsNativeAppSupportOS2::StartDDE() { + NS_ENSURE_TRUE( mInstance == 0, NS_ERROR_NOT_INITIALIZED ); + + BOOL bDdemlReady = SetupOS2ddeml(); + UINT rc = DDEERR_NOT_INITIALIZED; + if( bDdemlReady == TRUE ) + { + rc = (UINT) WinDdeInitialize( + &mInstance, + nsNativeAppSupportOS2::HandleDDENotification, + APPCLASS_STANDARD, + 0 ); + } + + // Initialize DDE. + NS_ENSURE_TRUE( DDEERR_NO_ERROR == rc, NS_ERROR_FAILURE ); + + + // Allocate DDE strings. + NS_ENSURE_TRUE( ( mApplication = WinDdeCreateStringHandle( mAppName, CP_WINANSI ) ) && InitTopicStrings(), + NS_ERROR_FAILURE ); + + // Next step is to register a DDE service. + NS_ENSURE_TRUE( WinDdeNameService( mInstance, mApplication, 0, DNS_REGISTER ), NS_ERROR_FAILURE ); + +#if MOZ_DEBUG_DDE + printf( "DDE server started\n" ); +#endif + + return NS_OK; +} + // If no DDE conversations are pending, terminate DDE. NS_IMETHODIMP nsNativeAppSupportOS2::Stop( PRBool *aResult ) { @@ -648,7 +1233,7 @@ nsNativeAppSupportOS2::Stop( PRBool *aResult ) { nsresult rv = NS_OK; *aResult = PR_TRUE; - Mutex ddeLock( MOZ_DDE_MUTEX_NAME ); + Mutex ddeLock( MOZ_STARTUP_MUTEX_NAME ); if ( ddeLock.Lock( MOZ_DDE_STOP_TIMEOUT ) ) { if ( mConversations == 0 ) { @@ -659,6 +1244,12 @@ nsNativeAppSupportOS2::Stop( PRBool *aResult ) { ddeLock.Unlock(); } + else { + // No DDE application name specified, but that's OK. Just + // forge ahead. + *aResult = PR_TRUE; + } + return rv; } @@ -667,30 +1258,31 @@ nsNativeAppSupportOS2::Stop( PRBool *aResult ) { NS_IMETHODIMP nsNativeAppSupportOS2::Quit() { if ( mInstance ) { + // Unregister application name. + WinDdeNameService( mInstance, mApplication, 0, DNS_UNREGISTER ); // Clean up strings. if ( mApplication ) { - DdeFreeStringHandle( mInstance, mApplication ); + WinDdeFreeStringHandle( mApplication ); mApplication = 0; } - if ( mTopic ) { - DdeFreeStringHandle( mInstance, mTopic ); - mTopic = 0; + for ( int i = 0; i < topicCount; i++ ) { + if ( mTopics[i] ) { + WinDdeFreeStringHandle( mTopics[i] ); + mTopics[i] = 0; + } } - DdeUninitialize( mInstance ); + WinDdeUninitialize( mInstance ); mInstance = 0; } - return NS_OK; } -#endif PRBool NS_CanRun() { return PR_TRUE; } -#ifndef XP_OS2 #if MOZ_DEBUG_DDE // Macro to generate case statement for a given XTYP value. #define XTYP_CASE(t) \ @@ -723,10 +1315,10 @@ static nsCString uTypeDesc( UINT uType ) { static nsCString hszValue( DWORD instance, HSZ hsz ) { // Extract string from HSZ. nsCString result("["); - DWORD len = DdeQueryString( instance, hsz, NULL, NULL, CP_WINANSI ); + DWORD len = WinDdeQueryString( hsz, NULL, NULL, CP_WINANSI ); if ( len ) { char buffer[ 256 ]; - DdeQueryString( instance, hsz, buffer, sizeof buffer, CP_WINANSI ); + WinDdeQueryString( hsz, buffer, sizeof buffer, CP_WINANSI ); result += buffer; } result += "]"; @@ -744,9 +1336,10 @@ static nsCString hszValue( DWORD, HSZ ) { #endif -HDDEDATA CALLBACK -nsNativeAppSupportOS2::HandleDDENotification( UINT uType, // transaction type - UINT uFmt, // clipboard data format +HDDEDATA APIENTRY +nsNativeAppSupportOS2::HandleDDENotification( ULONG idInst, // DDEML instance + USHORT uType, // transaction type + USHORT uFmt, // clipboard data format HCONV hconv, // handle to the conversation HSZ hsz1, // handle to a string HSZ hsz2, // handle to a string @@ -754,7 +1347,7 @@ nsNativeAppSupportOS2::HandleDDENotification( UINT uType, // transaction t ULONG dwData1, // transaction-specific data ULONG dwData2 ) { // transaction-specific data - #if MOZ_DEBUG_DDE +#if MOZ_DEBUG_DDE printf( "DDE: uType =%s\n", uTypeDesc( uType ).get() ); printf( " uFmt =%u\n", (unsigned)uFmt ); printf( " hconv =%08x\n", (int)hconv ); @@ -763,37 +1356,154 @@ nsNativeAppSupportOS2::HandleDDENotification( UINT uType, // transaction t printf( " hdata =%08x\n", (int)hdata ); printf( " dwData1=%08x\n", (int)dwData1 ); printf( " dwData2=%08x\n", (int)dwData2 ); - #endif +#endif HDDEDATA result = 0; if ( uType & XCLASS_BOOL ) { switch ( uType ) { - case XTYP_CONNECT: - case XTYP_CONNECT_CONFIRM: - // Make sure its for our service/topic. - if ( DdeCmpStringHandles( hsz1, mTopic ) == 0 - && - DdeCmpStringHandles( hsz2, mApplication ) == 0 ) { - // We support this connection. + case XTYP_CONNECT: + // Make sure its for our service/topic. + if ( FindTopic( hsz1 ) != -1 ) { + // We support this connection. + result = (HDDEDATA)1; + } + break; + case XTYP_CONNECT_CONFIRM: + // We don't care about the conversation handle, at this point. result = (HDDEDATA)1; - } + break; } } else if ( uType & XCLASS_DATA ) { + if ( uType == XTYP_REQUEST ) { + switch ( FindTopic( hsz1 ) ) { + case topicOpenURL: { + // Open a given URL... + nsCString url = ParseDDEArg( hsz2, 0 ); + // Make it look like command line args. + url.Insert( "mozilla -url ", 0 ); +#if MOZ_DEBUG_DDE + printf( "Handling dde XTYP_REQUEST request: [%s]...\n", url.get() ); +#endif + // Now handle it. + HandleRequest( LPBYTE( url.get() ), PR_FALSE ); + // Return pseudo window ID. + result = CreateDDEData( 1 ); + break; + } + case topicActivate: { + // Activate a Nav window... + nsCString windowID = ParseDDEArg( hsz2, 0 ); + // 4294967295 is decimal for 0xFFFFFFFF which is also a + // correct value to do that Activate last window stuff + if ( windowID.Equals( "-1" ) || + windowID.Equals( "4294967295" ) ) { + // We only support activating the most recent window (or a new one). + ActivateLastWindow(); + // Return pseudo window ID. + result = CreateDDEData( 1 ); + } + break; + } + case topicVersion: { + // Return version. We're restarting at 1.0! + DWORD version = 1 << 16; // "1.0" + result = CreateDDEData( version ); + break; + } + case topicRegisterViewer: { + // Register new viewer (not implemented). + result = CreateDDEData( PR_FALSE ); + break; + } + case topicUnRegisterViewer: { + // Unregister new viewer (not implemented). + result = CreateDDEData( PR_FALSE ); + break; + } + default: + break; + } + } else if ( uType & XTYP_POKE ) { + switch ( FindTopic( hsz1 ) ) { + case topicCancelProgress: { + // "Handle" progress cancel (actually, pretty much ignored). + result = (HDDEDATA)DDE_FACK; + break; + } + default: + break; + } + } } else if ( uType & XCLASS_FLAGS ) { if ( uType == XTYP_EXECUTE ) { // Prove that we received the request. DWORD bytes; - LPBYTE request = DdeAccessData( hdata, &bytes ); - #if MOZ_DEBUG_DDE + LPBYTE request = (LPBYTE)WinDdeAccessData( hdata, &bytes ); +#if MOZ_DEBUG_DDE printf( "Handling dde request: [%s]...\n", (char*)request ); - #endif +#endif HandleRequest( request ); result = (HDDEDATA)DDE_FACK; } else { - result = (HDDEDATA)DDE_FNOTPROCESSED; + result = (HDDEDATA)DDE_NOTPROCESSED; } } else if ( uType & XCLASS_NOTIFICATION ) { } +#if MOZ_DEBUG_DDE + printf( "DDE result=%d (0x%08X)\n", (int)result, (int)result ); +#endif + return result; +} + +// Utility to parse out argument from a DDE item string. +nsCString nsNativeAppSupportOS2::ParseDDEArg( HSZ args, int index ) { + nsCString result; + DWORD argLen = WinDdeQueryString( args, NULL, NULL, CP_WINANSI ); + if ( argLen ) { + nsCString temp; + // Ensure result's buffer is sufficiently big. + temp.SetLength( argLen + 1 ); + // Now get the string contents. + WinDdeQueryString( args, (char*)temp.get(), temp.Length(), CP_WINANSI ); + // Parse out the given arg. We assume there's no commas within quoted strings + // (which may not be accurate, but makes life so much easier). + const char *p = temp.get(); + // Skip index commas. + PRInt32 offset = index ? temp.FindChar( ',', PR_FALSE, 0, index ) : 0; + if ( offset != -1 ) { + // Desired argument starts there and ends at next comma. + PRInt32 end = temp.FindChar( ',', PR_FALSE, offset+1 ); + if ( end == -1 ) { + // Rest of string. + end = temp.Length(); + } + // Extract result. + result.Assign( temp.get() + offset, end - offset ); + } + + } + return result; +} + +void nsNativeAppSupportOS2::ActivateLastWindow() { + nsCOMPtr navWin; + GetMostRecentWindow( NS_LITERAL_STRING("navigator:browser").get(), getter_AddRefs( navWin ) ); + if ( navWin ) { + // Activate that window. + activateWindow( navWin ); + } else { + // Need to create a Navigator window, then. + OpenBrowserWindow( "about:blank" ); + } +} + +HDDEDATA nsNativeAppSupportOS2::CreateDDEData( DWORD value ) { + HDDEDATA result = WinDdeCreateDataHandle( (LPBYTE)&value, + sizeof value, + 0, + mApplication, + CF_TEXT, + 0 ); return result; } @@ -803,48 +1513,92 @@ nsNativeAppSupportOS2::HandleDDENotification( UINT uType, // transaction t // arguments. This replicates code elsewhere, to some extent, // unfortunately (if you can fix that, please do). void -nsNativeAppSupportOS2::HandleRequest( LPBYTE request ) { +nsNativeAppSupportOS2::HandleRequest( LPBYTE request, PRBool newWindow ) { // Parse command line. - nsCOMPtr args; - nsresult rv = GetCmdLineArgs( request, getter_AddRefs( args ) ); - if ( NS_SUCCEEDED( rv ) ) { - nsXPIDLCString arg; - if (NS_SUCCEEDED(args->GetURLToLoad(getter_Copies(arg) ) ) && - (const char*)arg ) { - // Launch browser. - #if MOZ_DEBUG_DDE - printf( "Launching browser on url [%s]...\n", (const char*)arg ); - #endif - (void)OpenWindow( "chrome://navigator/content/", arg ); - } - else if (NS_SUCCEEDED(args->GetCmdLineValue("-chrome", getter_Copies(arg))) && - (const char*)arg ) { - // Launch chrome. - #if MOZ_DEBUG_DDE - printf( "Launching chrome url [%s]...\n", (const char*)arg ); - #endif - (void)OpenWindow( arg, "" ); - } - else if (NS_SUCCEEDED(args->GetCmdLineValue("-edit", getter_Copies(arg))) && - (const char*)arg ) { - // Launch composer. - #if MOZ_DEBUG_DDE - printf( "Launching editor on url [%s]...\n", arg ); - #endif - (void)OpenWindow( "chrome://editor/content/", arg ); - } else if ( NS_SUCCEEDED( args->GetCmdLineValue( "-mail", getter_Copies(arg))) && - (const char*)arg ) { - // Launch composer. - #if MOZ_DEBUG_DDE - printf( "Launching mail...\n" ); - #endif - (void)OpenWindow( "chrome://messenger/content/", "" ); - } else { - #if MOZ_DEBUG_DDE - printf( "Unknown request [%s]\n", (char*) request ); - #endif - } + nsCOMPtr args; + nsresult rv; + + rv = GetCmdLineArgs( request, getter_AddRefs( args ) ); + if (NS_FAILED(rv)) return; + + // first see if there is a url + nsXPIDLCString arg; + rv = args->GetURLToLoad(getter_Copies(arg)); + if (NS_SUCCEEDED(rv) && (const char*)arg ) { + // Launch browser. +#if MOZ_DEBUG_DDE + printf( "Launching browser on url [%s]...\n", (const char*)arg ); +#endif + if (NS_SUCCEEDED(EnsureProfile(args))) + (void)OpenBrowserWindow( arg ); + return; + } + + + // ok, let's try the -chrome argument + rv = args->GetCmdLineValue("-chrome", getter_Copies(arg)); + if (NS_SUCCEEDED(rv) && (const char*)arg ) { + // Launch chrome. +#if MOZ_DEBUG_DDE + printf( "Launching chrome url [%s]...\n", (const char*)arg ); +#endif + if (NS_SUCCEEDED(EnsureProfile(args))) + (void)OpenWindow( arg, "" ); + return; + } + + // try using the command line service to get the url + nsCString taskURL; + rv = GetStartupURL(args, taskURL); + if (NS_SUCCEEDED(rv) && NS_SUCCEEDED(EnsureProfile(args))) { + (void)OpenWindow(taskURL, ""); + return; + } + + // try for the "-kill" argument, to shut down the server + rv = args->GetCmdLineValue( "-kill", getter_Copies(arg)); + if ( NS_SUCCEEDED(rv) && (const char*)arg ) { + // Turn off server mode. + nsCOMPtr appShell = + do_GetService( "@mozilla.org/appshell/appShellService;1", &rv); + if (NS_FAILED(rv)) return; + + nsCOMPtr native; + rv = appShell->GetNativeAppSupport( getter_AddRefs( native )); + if (NS_SUCCEEDED(rv)) { + native->SetIsServerMode( PR_FALSE ); + // This closes app if there are no top-level windows. + appShell->UnregisterTopLevelWindow( 0 ); + } + + return; + } + + + // ok, no idea what the param is. +#if MOZ_DEBUG_DDE + printf( "Unknown request [%s]\n", (char*) request ); +#endif + // if all else fails, open a browser window + const char * const contractID = + "@mozilla.org/commandlinehandler/general-startup;1?type=browser"; + nsCOMPtr handler = do_GetService(contractID, &rv); + if (NS_FAILED(rv)) return; + + rv = EnsureProfile(args); + if (NS_FAILED(rv)) return; + + nsXPIDLString defaultArgs; + rv = handler->GetDefaultArgs(getter_Copies(defaultArgs)); + if (NS_FAILED(rv) || !defaultArgs) return; + + if (defaultArgs) { + nsCAutoString url; + url.AssignWithConversion( defaultArgs ); + OpenBrowserWindow((const char*)url); + } else { + OpenBrowserWindow("about:blank"); } } @@ -862,7 +1616,7 @@ nsNativeAppSupportOS2::GetCmdLineArgs( LPBYTE request, nsICmdLineService **aResu int between, quoted, bSlashCount; int argc; char *p; - nsCString arg; + nsCAutoString arg; // We loop if we've not finished the second pass through. while ( 1 ) { // Initialize if required. @@ -990,9 +1744,9 @@ nsNativeAppSupportOS2::GetCmdLineArgs( LPBYTE request, nsICmdLineService **aResu NS_GET_IID( nsICmdLineService ), (void**)aResult ); if ( NS_FAILED( rv ) || NS_FAILED( ( rv = (*aResult)->Initialize( argc, argv ) ) ) ) { - #if MOZ_DEBUG_DDE - printf( "Error creating command line service = 0x%08X\n", (int)rv ); - #endif +#if MOZ_DEBUG_DDE + printf( "Error creating command line service = 0x%08X (argc=%d, argv=0x%08X)\n", (int)rv, (int)argc, (void*)argv ); +#endif } // Cleanup. @@ -1004,12 +1758,49 @@ nsNativeAppSupportOS2::GetCmdLineArgs( LPBYTE request, nsICmdLineService **aResu return rv; } +// Check the needsProfileUI attribute of the native app and, +// if so, do it. Once this suceeds, set the attribute to false +// so we can only do this once. +nsresult +nsNativeAppSupportOS2::EnsureProfile(nsICmdLineService* args) +{ + nsresult rv = NS_OK; + nsCOMPtr appShell = do_GetService( "@mozilla.org/appshell/appShellService;1", &rv ); + if (NS_FAILED(rv)) return rv; + nsCOMPtr nativeApp; + rv = appShell->GetNativeAppSupport(getter_AddRefs(nativeApp)); + if (NS_FAILED(rv)) return rv; + PRBool needsProfileUI; + rv = nativeApp->GetNeedsProfileUI(&needsProfileUI); + if (NS_FAILED(rv)) return rv; + + nsCOMPtr profileMgr(do_GetService(NS_PROFILE_CONTRACTID, &rv)); + if (NS_FAILED(rv)) return rv; + + if (needsProfileUI) { + // We need profile UI because we started in + // server mode and could not show it then. + rv = profileMgr->StartupWithArgs(args, PR_TRUE); + if (NS_FAILED(rv)) return rv; + nativeApp->SetNeedsProfileUI(PR_FALSE); + } + else { + // Even if not started in server mode, ensure + // that we have a profile. We can hit this case + // if somebody double-clicks the app twice. + PRBool haveProfile = PR_FALSE; + (void)profileMgr->IsCurrentProfileAvailable(&haveProfile); + if (!haveProfile) return NS_ERROR_FAILURE; + } + return NS_OK; +} + nsresult nsNativeAppSupportOS2::OpenWindow( const char*urlstr, const char *args ) { nsresult rv = NS_ERROR_FAILURE; - nsCOMPtr wwatch(do_GetService("@mozilla/embedcomp/window-watcher;1")); + nsCOMPtr wwatch(do_GetService("@mozilla.org/embedcomp/window-watcher;1")); nsCOMPtr sarg(do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID)); if (sarg) sarg->SetData(args); @@ -1018,11 +1809,351 @@ nsNativeAppSupportOS2::OpenWindow( const char*urlstr, const char *args ) { nsCOMPtr newWindow; rv = wwatch->OpenWindow(0, urlstr, "_blank", "chrome,dialog=no,all", sarg, getter_AddRefs(newWindow)); -#ifdef MOZ_DEBUG_DDE +#if MOZ_DEBUG_DDE } else { printf("Get WindowWatcher (or create string) failed\n"); #endif } return rv; } -#endif + +static char procPropertyName[] = "MozillaProcProperty"; + +#ifdef DOWENEED +// Subclass procedure used to filter out WM_SETFOCUS messages while reparenting. +static LRESULT CALLBACK focusFilterProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) { + if ( uMsg == WM_SETFOCUS ) { + // Don't let Mozilla's window procedure see this. + return 0; + } else { + // Pass on all other messages to Mozilla's window proc. + HANDLE oldProc = ::GetProp( hwnd, procPropertyName ); + if ( oldProc ) { + return ::CallWindowProc( (WNDPROC)oldProc, hwnd, uMsg, wParam, lParam ); + } else { + // Last resort is the default window proc. + return ::DefWindowProc( hwnd, uMsg, wParam, lParam ); + } + } +} +#endif /* DOWENEED */ + +HWND hwndForDOMWindow( nsISupports *window ) { + nsCOMPtr ppScriptGlobalObj( do_QueryInterface(window) ); + if ( !ppScriptGlobalObj ) { + return 0; + } + nsCOMPtr ppDocShell; + ppScriptGlobalObj->GetDocShell( getter_AddRefs( ppDocShell ) ); + if ( !ppDocShell ) { + return 0; + } + nsCOMPtr ppBaseWindow( do_QueryInterface( ppDocShell ) ); + if ( !ppBaseWindow ) { + return 0; + } + + nsCOMPtr ppWidget; + ppBaseWindow->GetMainWidget( getter_AddRefs( ppWidget ) ); + + return (HWND)( ppWidget->GetNativeData( NS_NATIVE_WIDGET ) ); +} + +nsresult +nsNativeAppSupportOS2::ReParent( nsISupports *window, HWND newParent ) { + HWND hMainFrame = hwndForDOMWindow( window ); + if ( !hMainFrame ) { + return NS_ERROR_FAILURE; + } + +#ifdef DOWENEED + // Filter out WM_SETFOCUS messages while reparenting to + // other than the desktop. + // + // For some reason, Windows generates one and it causes + // our focus/activation code to assert. + LONG oldProc = 0; + if ( newParent ) { + // Subclass the window. + oldProc = ::SetWindowLong( hMainFrame, + GWL_WNDPROC, + (LONG)(WNDPROC)focusFilterProc ); + + // Store old procedure in window so it is available within + // focusFilterProc. + ::SetProp( hMainFrame, procPropertyName, (HANDLE)oldProc ); + } +#endif /* DOWENEED */ + + // Reset the parent. + WinSetParent( hMainFrame, newParent, FALSE ); + +#ifdef DOWENEED + // Restore old procedure. + if ( newParent ) { + ::SetWindowLong( hMainFrame, GWL_WNDPROC, oldProc ); + ::RemoveProp( hMainFrame, procPropertyName ); + } +#endif /* DOWENEED */ + + return NS_OK; +} + +static const char sJSStackContractID[] = "@mozilla.org/js/xpc/ContextStack;1"; + +class SafeJSContext { +public: + SafeJSContext(); + ~SafeJSContext(); + + nsresult Push(); + JSContext *get() { return mContext; } + +protected: + nsCOMPtr mService; + JSContext *mContext; +}; + +SafeJSContext::SafeJSContext() : mContext(nsnull) { +} + +SafeJSContext::~SafeJSContext() { + JSContext *cx; + nsresult rv; + + if(mContext) { + rv = mService->Pop(&cx); + NS_ASSERTION(NS_SUCCEEDED(rv) && cx == mContext, "JSContext push/pop mismatch"); + } +} + +nsresult SafeJSContext::Push() { + nsresult rv; + + if (mContext) // only once + return NS_ERROR_FAILURE; + + mService = do_GetService(sJSStackContractID); + if(mService) { + rv = mService->GetSafeJSContext(&mContext); + if (NS_SUCCEEDED(rv) && mContext) { + rv = mService->Push(mContext); + if (NS_FAILED(rv)) + mContext = 0; + } + } + return mContext ? NS_OK : NS_ERROR_FAILURE; +} + + +nsresult +nsNativeAppSupportOS2::OpenBrowserWindow( const char *args, PRBool newWindow ) { + nsresult rv = NS_OK; + // Open the argument URL in the most recently used Navigator window. + // If there is no Nav window, open a new one. + + // Get most recently used Nav window. + nsCOMPtr navWin; + GetMostRecentWindow( NS_LITERAL_STRING( "navigator:browser" ).get(), getter_AddRefs( navWin ) ); + + // This isn't really a loop. We just use "break" statements to fall + // out to the OpenWindow call when things go awry. + do { + // If caller requires a new window, then don't use an existing one. + if ( newWindow ) { + break; + } + if ( !navWin ) { + // Have to open a new one. + break; + } + // Get content window. + nsCOMPtr content; + navWin->GetContent( getter_AddRefs( content ) ); + if ( !content ) { + break; + } + // Convert that to internal interface. + nsCOMPtr internalContent( do_QueryInterface( content ) ); + if ( !internalContent ) { + break; + } + // Get location. + nsCOMPtr location; + internalContent->GetLocation( getter_AddRefs( location ) ); + if ( !location ) { + break; + } + // Set up environment. + SafeJSContext context; + if ( NS_FAILED( context.Push() ) ) { + break; + } + // Set href. + nsAutoString url; url.AssignWithConversion( args ); + if ( NS_FAILED( location->SetHref( url ) ) ) { + break; + } + // Finally, if we get here, we're done. + return NS_OK; + } while ( PR_FALSE ); + + // Last resort is to open a brand new window. + return OpenWindow( "chrome://navigator/content", args ); +} + +#ifdef DOWENEED +// Utility function that sets up system tray icon. +void +nsNativeAppSupportWin::SetupSysTrayIcon() { + // Messages go to the hidden window. + mIconData.hWnd = (HWND)MessageWindow(); + + // Icon is our default application icon. + mIconData.hIcon = ::LoadIcon( ::GetModuleHandle( NULL ), IDI_APPLICATION ), + + // Tooltip is the brand short name. + mIconData.szTip[0] = 0; + nsCOMPtr svc( do_GetService( NS_STRINGBUNDLE_CONTRACTID ) ); + if ( svc ) { + nsCOMPtr bundle1; + svc->CreateBundle( "chrome://global/locale/brand.properties", getter_AddRefs( bundle1 ) ); + if ( bundle1 ) { + nsXPIDLString tooltip; + bundle1->GetStringFromName( NS_LITERAL_STRING( "brandShortName" ).get(), + getter_Copies( tooltip ) ); + // (damned strings...) + nsAutoString autoTip( tooltip ); + nsAutoCString tip( autoTip ); + ::strncpy( mIconData.szTip, (const char*)tip, sizeof mIconData.szTip - 1 ); + } + // Build menu. + nsCOMPtr bundle2; + svc->CreateBundle( "chrome://communicator/locale/profile/profileManager.properties", + getter_AddRefs( bundle2 ) ); + nsAutoString exitText; + if ( bundle2 ) { + nsXPIDLString text; + bundle2->GetStringFromName( NS_LITERAL_STRING( "exitButton" ).get(), + getter_Copies( text ) ); + exitText = (const PRUnichar*)text; + } + if ( exitText.IsEmpty() ) { + // Fall back to this. + exitText = NS_LITERAL_STRING( "Exit" ); + } + + // Create menu and add item. + mTrayIconMenu = ::CreatePopupMenu(); + ::AppendMenuW( mTrayIconMenu, MF_STRING, TURBO_QUIT, exitText.get() ); + + } + + // Add the tray icon. + ::Shell_NotifyIcon( NIM_ADD, &mIconData ); +} + +// Utility function to remove the system tray icon. +void +nsNativeAppSupportWin::RemoveSysTrayIcon() { + // Remove the tray icon. + ::Shell_NotifyIcon( NIM_DELETE, &mIconData ); + // Delete the menu. + ::DestroyMenu( mTrayIconMenu ); +} +#endif /* DOWENEED */ + + + +// This opens a special browser window for purposes of priming the pump for +// server mode (getting stuff into the caching, loading .dlls, etc.). The +// window will have these attributes: +// - Load about:blank (no home page) +// - No toolbar (so there's no sidebar panels loaded, either) +// - Pass magic arg to cause window to close in onload handler. +NS_IMETHODIMP +nsNativeAppSupportOS2::StartServerMode() { +#ifdef DOWENEED + // Turn on system tray icon. + SetupSysTrayIcon(); +#endif /* DOWENEED */ + + // Create some of the objects we'll need. + nsCOMPtr ww(do_GetService("@mozilla.org/embedcomp/window-watcher;1")); + nsCOMPtr arg1(do_CreateInstance(NS_SUPPORTS_WSTRING_CONTRACTID)); + nsCOMPtr arg2(do_CreateInstance(NS_SUPPORTS_WSTRING_CONTRACTID)); + if ( !ww || !arg1 || !arg2 ) { + return NS_OK; + } + + // Create the array for the arguments. + nsCOMPtr argArray; + NS_NewISupportsArray( getter_AddRefs( argArray ) ); + if ( !argArray ) { + return NS_OK; + } + + // arg1 is the url to load. + // arg2 is the string that tells navigator.js to auto-close. + arg1->SetData( NS_LITERAL_STRING( "about:blank" ).get() ); + arg2->SetData( NS_LITERAL_STRING( "turbo=yes" ).get() ); + + // Put args into array. + if ( NS_FAILED( argArray->AppendElement( arg1 ) ) || + NS_FAILED( argArray->AppendElement( arg2 ) ) ) { + return NS_OK; + } + + // Now open the window. + nsCOMPtr newWindow; + ww->OpenWindow( 0, + "chrome://navigator/content", + "_blank", + "chrome,dialog=no,toolbar=no", + argArray, + getter_AddRefs( newWindow ) ); + + if ( !newWindow ) { + return NS_OK; + } + + // Hide this window by re-parenting it (to ensure it doesn't appear). + ReParent( newWindow, (HWND)MessageWindow() ); + + return NS_OK; +} + +NS_IMETHODIMP +nsNativeAppSupportOS2::SetIsServerMode( PRBool isServerMode ) { + // If it is being turned off, remove systray icon. + if ( mServerMode && !isServerMode ) { +#ifdef DOWENEED + RemoveSysTrayIcon(); +#endif /* DOWENEED */ + } + return nsNativeAppSupportBase::SetIsServerMode( isServerMode ); +} + + +// go through the command line arguments, and try to load a handler +// for one, and when we do, get the chrome URL for its task +nsresult +nsNativeAppSupportOS2::GetStartupURL(nsICmdLineService *args, nsCString& taskURL) +{ + nsresult rv; + + nsCOMPtr handler; + + // see if there is a handler + rv = args->GetHandlerForParam(nsnull, getter_AddRefs(handler)); + if (NS_FAILED(rv)) return rv; + + // ok, from here on out, failures really are fatal + nsXPIDLCString url; + rv = handler->GetChromeUrlForTask(getter_Copies(url)); + if (NS_FAILED(rv)) return rv; + + taskURL.Assign(url.get()); + return NS_OK; +} + diff --git a/xpfe/bootstrap/nsNativeAppSupportOS2.h b/xpfe/bootstrap/nsNativeAppSupportOS2.h index cd90a7bc0a1..6a16d5ea328 100644 --- a/xpfe/bootstrap/nsNativeAppSupportOS2.h +++ b/xpfe/bootstrap/nsNativeAppSupportOS2.h @@ -20,6 +20,7 @@ * Contributor(s): * Bill Law law@netscape.com * IBM Corp. + * Achim Hasenmueller */ // Splash screen dialog ID. @@ -31,3 +32,295 @@ // DDE application name #define ID_DDE_APPLICATION_NAME 102 +/* + * OS/2 DDEML library headers + * For more information, please refer to the Windows documentation + */ + +#ifndef _H_DDEML +#define _H_DDEML + +/* all structures must be byte aligned */ +#pragma pack(1) + +/* custom type definitions */ +typedef LHANDLE HCONV; +typedef LHANDLE HCONVLIST; +typedef LHANDLE HDATA; +typedef LHANDLE HDDEINST; +typedef LHANDLE HSZ; +typedef HCONV *PHCONV; +typedef HCONVLIST *PHCONVLIST; +typedef HDATA *PHDATA; +typedef HDDEINST *PHDDEINST; +typedef HSZ *PHSZ; +typedef HDATA (APIENTRY FNDDECB)(ULONG, USHORT, USHORT, HCONV, HSZ, HSZ, HDATA, ULONG, ULONG); +typedef FNDDECB *PFNDDECB; + +/* constant definitions */ +#define XCLASS_BOOL 0x1000 +#define XCLASS_DATA 0x2000 +#define XCLASS_FLAGS 0x4000 +#define XCLASS_NOTIFICATION 0x8000 +#define XTYPF_NOBLOCK 0x0002 +#define XTYPF_NODATA 0x0004 +#define XTYPF_ACKREQ 0x0008 +#define XTYP_ERROR (0x0000 | XCLASS_NOTIFICATION | XTYPF_NOBLOCK) +#define XTYP_ADVDATA (0x0010 | XCLASS_FLAGS) +#define XTYP_ADVREQ (0x0020 | XCLASS_DATA | XTYPF_NOBLOCK) +#define XTYP_ADVSTART (0x0030 | XCLASS_BOOL) +#define XTYP_ADVSTOP (0x0040 | XCLASS_NOTIFICATION) +#define XTYP_EXECUTE (0x0050 | XCLASS_FLAGS) +#define XTYP_CONNECT (0x0060 | XCLASS_BOOL | XTYPF_NOBLOCK) +#define XTYP_CONNECT_CONFIRM (0x0070 | XCLASS_NOTIFICATION | XTYPF_NOBLOCK) +#define XTYP_XACT_COMPLETE (0x0080 | XCLASS_NOTIFICATION) +#define XTYP_POKE (0x0090 | XCLASS_FLAGS) +#define XTYP_REGISTER (0x00A0 | XCLASS_NOTIFICATION | XTYPF_NOBLOCK) +#define XTYP_REQUEST (0x00B0 | XCLASS_DATA) +#define XTYP_DISCONNECT (0x00C0 | XCLASS_NOTIFICATION | XTYPF_NOBLOCK) +#define XTYP_UNREGISTER (0x00D0 | XCLASS_NOTIFICATION | XTYPF_NOBLOCK) +#define XTYP_WILDCONNECT (0x00E0 | XCLASS_DATA | XTYPF_NOBLOCK) +#define XTYP_MONITOR (0x00F0 | XCLASS_NOTIFICATION | XTYPF_NOBLOCK) +#define ST_CONNECTED ((USHORT)0x0001) +#define ST_ADVISE ((USHORT)0x0002) +#define ST_ISLOCAL ((USHORT)0x0004) +#define ST_BLOCKED ((USHORT)0x0008) +#define ST_CLIENT ((USHORT)0x0010) +#define ST_TERMINATED ((USHORT)0x0020) +#define ST_INLIST ((USHORT)0x0040) +#define ST_BLOCKNEXT ((USHORT)0x0080) +#define ST_ISSELF ((USHORT)0x0100) +#define XST_NULL 0 +#define XST_INCOMPLETE 1 +#define XST_CONNECTED 2 +#define XST_INITSENT 3 +#define XST_INITACKRCVD 4 +#define XST_REQSENT 5 +#define XST_DATARCVD 6 +#define XST_POKESENT 7 +#define XST_POKEACKRCVD 8 +#define XST_EXECSENT 9 +#define XST_EXECACKRCVD 10 +#define XST_ADVSENT 11 +#define XST_UNADVSENT 12 +#define XST_ADVACKRCVD 13 +#define XST_UNADVACKRCVD 14 +#define XST_ADVDATASENT 15 +#define XST_ADVDATAACKRCVD 16 +#define MF_HSZ ((ULONG)0x01000000L) +#define MF_SENDMSGS ((ULONG)0x02000000L) +#define MF_POSTMSGS ((ULONG)0x04000000L) +#define MF_CALLBACKS ((ULONG)0x08000000L) +#define MF_ERRORS ((ULONG)0x10000000L) +#define MF_LINKS ((ULONG)0x20000000L) +#define MF_CONV ((ULONG)0x40000000L) +#define CBF_FAIL_SELFCONNECTIONS ((ULONG)0x00001000L) +#define CBF_FAIL_CONNECTIONS ((ULONG)0x00002000L) +#define CBF_FAIL_ADVISES ((ULONG)0x00004000L) +#define CBF_FAIL_EXECUTES ((ULONG)0x00008000L) +#define CBF_FAIL_POKES ((ULONG)0x00010000L) +#define CBF_FAIL_REQUESTS ((ULONG)0x00020000L) +#define CBF_FAIL_ALLSVRXACTIONS ((ULONG)0x0003f000L) +#define CBF_SKIP_CONNECT_CONFIRMS ((ULONG)0x00040000L) +#define CBF_SKIP_REGISTRATIONS ((ULONG)0x00080000L) +#define CBF_SKIP_UNREGISTRATIONS ((ULONG)0x00100000L) +#define CBF_SKIP_DISCONNECTS ((ULONG)0x00200000L) +#define CBF_SKIP_ALLNOTIFICATIONS ((ULONG)0x003c0000L) +#define CBF_MASK ((ULONG)0x00FFF000L) +#define APPF_CLIENTONLY ((ULONG)0x00000010L) +#define APPF_FILTERINITS ((ULONG)0x00000020L) +#define APPF_MASK ((ULONG)0x00000FF0L) +#define APPCLASS_STANDARD ((ULONG)0x00000000L) +#define APPCLASS_MONITOR ((ULONG)0x00000001L) +#define APPCLASS_MASK ((ULONG)0x0000000FL) +#define CBR_BLOCK ((ULONG)-1L) +#define QID_SYNC ((ULONG)-1L) +#define TIMEOUT_ASYNC ((ULONG)-1L) +#define CADV_LATEACK 0xFFFF +#define MH_CREATE ((USHORT)0x0001) +#define MH_KEEP ((USHORT)0x0002) +#define MH_DELETE ((USHORT)0x0003) +#define MH_CLEANUP ((USHORT)0x0004) +#define MH_SYS_CREATE ((USHORT)0x0005) +#define MH_SYS_KEEP ((USHORT)0x0006) +#define MH_SYS_DELETE ((USHORT)0x0007) +#define DDEERR_NO_ERROR 0 +#define DDEERR_FIRST 0x7000 +#define DDEERR_BUSY 0x7001 +#define DDEERR_INVALID_USAGE 0x7002 +#define DDEERR_INVALID_PARAMETER 0x7003 +#define DDEERR_MEMORY_ERROR 0x7004 +#define DDEERR_NO_CONV_ESTABLISHED 0x7005 +#define DDEERR_NO_MEMORY 0x7006 +#define DDEERR_NO_MSG_QUEUE 0x7007 +#define DDEERR_NOT_INITIALIZED 0x7008 +#define DDEERR_NOT_PROCESSED 0x7009 +#define DDEERR_POSTMSG_FAILED 0x700a +#define DDEERR_REENTRANCY 0x700b +#define DDEERR_SERVER_DIED 0x700c +#define DDEERR_SYSTEM_ERROR 0x700d +#define DDEERR_TIMEOUT_ADVACK 0x700e +#define DDEERR_TIMEOUT_EXECACK 0x700f +#define DDEERR_TIMEOUT_POKEACK 0x7010 +#define DDEERR_TIMEOUT_DATAACK 0x7011 +#define DDEERR_TIMEOUT_UNADVACK 0x7012 +#define DDEERR_UNKNOWN_QUEUE_ID 0x7013 +#define DDEERR_LAST 0x70FF +#define CSH_CASESENSITIVE ((ULONG)0x00000001L) +#define CSH_ERROR 0L +#define CSH_EQ 1L +#define CSH_LT 2L +#define CSH_GT 3L +#define HDATA_APPOWNED ((ULONG)DDEPM_NOFREE) +#define EC_ENABLEALL ((USHORT) 0x0000) +#define EC_ENABLEONE ST_BLOCKNEXT +#define EC_DISABLE ST_BLOCKED +#define EC_QUERYWAITING ((USHORT)0x0002) +#define DNS_REGISTER ((ULONG)0x00000001L) +#define DNS_UNREGISTER ((ULONG)0x00000002L) +#define DNS_FILTERON ((ULONG)0x00000004L) +#define DNS_FILTEROFF ((ULONG)0x00000008L) + +/* structure definitions */ +typedef struct +{ + ULONG cb; + ULONG hUser; + HCONV hConvPartner; + HSZ hszServicePartner; + HSZ hszServiceRequest; + HSZ hszTopic; + HSZ hszItem; + USHORT usFormat; + USHORT fsType; + USHORT fsStatus; + USHORT usState; + ULONG ulLastError; + HCONVLIST hConvList; + CONVCONTEXT ConvCtxt; + HWND hwnd; + HWND hwndPartner; +} CONVINFO, *PCONVINFO; + +typedef struct +{ + HSZ hszService; + HSZ hszTopic; +} HSZPAIR, *PHSZPAIR; + +typedef struct +{ + ULONG cb; + ULONG ulTime; + ULONG hTask; + ULONG ulRet; + USHORT fsType; + USHORT usFormat; + HCONV hConv; + HSZ hsz1; + HSZ hsz2; + HDATA hData; + ULONG ulData1; + ULONG ulData2; + CONVCONTEXT ConvCtxt; + ULONG cbData; + BYTE abData[32]; +} MONCBSTRUCT, *PMONCBSTRUCT; + +typedef struct +{ + ULONG cb; + BOOL fConnect; + ULONG ulTime; + ULONG hTaskPartner; + HSZ hszService; + HSZ hszTopic; + HCONV hConvClient; + HCONV hConvServer; +} MONCONVSTRUCT, *PMONCONVSTRUCT; + +typedef struct +{ + ULONG cb; + ULONG ulLastError; + ULONG ulTime; + ULONG hTask; +} MONERRSTRUCT, *PMONERRSTRUCT; + +typedef struct +{ + ULONG cb; + USHORT fsAction; + ULONG ulTime; + HSZ hsz; + ULONG hTask; + ULONG ulReserved; + CHAR szString[1]; +} MONHSZSTRUCT, *PMONHSZSTRUCT; + +typedef struct +{ + ULONG cb; + ULONG ulTime; + ULONG hTask; + HSZ hszService; + HSZ hszTopic; + HSZ hszItem; + HCONV hConvServer; + HCONV hConvClient; + BOOL fServer; + BOOL fEstablished; + USHORT fsStatus; + USHORT usFormat; +} MONLINKSTRUCT, *PMONLINKSTRUCT; + +typedef struct +{ + ULONG cb; + HWND hwndTo; + ULONG ulTime; + ULONG hTask; + ULONG idMsg; + MPARAM mp1; + MPARAM mp2; + USHORT fsStatus; + USHORT usFormat; + USHORT offszString1; + USHORT offszString2; + ULONG cbData; + BYTE abData[32]; +} MONMSGSTRUCT, *PMONMSGSTRUCT; + + +/* API definitions */ +BOOL (* APIENTRY WinDdeAbandonTransaction)(HDDEINST, HCONV, ULONG); +PVOID (* APIENTRY WinDdeAccessData)(HDATA, PULONG); +HDATA (* APIENTRY WinDdeAddData)(HDATA, PVOID, ULONG, ULONG); +ULONG (* APIENTRY WinDdeCompareStringHandles)(HSZ, HSZ, ULONG); +HCONV (* APIENTRY WinDdeConnect)(HDDEINST, HSZ, HSZ, PCONVCONTEXT); +HCONVLIST (* APIENTRY WinDdeConnectList)(HDDEINST, HSZ, HSZ, HCONVLIST, PCONVCONTEXT); +HDATA (* APIENTRY WinDdeCreateDataHandle)(PVOID, ULONG, ULONG, HSZ, USHORT, ULONG); +HSZ (* APIENTRY WinDdeCreateStringHandle)(PSZ, ULONG); +BOOL (* APIENTRY WinDdeDisconnect)(HCONV); +BOOL (* APIENTRY WinDdeDisconnectList)(HCONVLIST); +BOOL (* APIENTRY WinDdeEnableCallback)(HDDEINST, HCONV, ULONG); +BOOL (* APIENTRY WinDdeFreeDataHandle)(HDATA); +BOOL (* APIENTRY WinDdeFreeStringHandle)(HSZ); +ULONG (* APIENTRY WinDdeGetData)(HDATA, PVOID, ULONG, ULONG); +ULONG (* APIENTRY WinDdeInitialize)(PHDDEINST, PFNDDECB, ULONG, ULONG); +BOOL (* APIENTRY WinDdeKeepStringHandle)(HSZ); +HDATA (* APIENTRY WinDdeNameService)(HDDEINST, HSZ, HSZ, ULONG); +BOOL (* APIENTRY WinDdePostAdvise)(HDDEINST, HSZ, HSZ); +ULONG (* APIENTRY WinDdeQueryConvInfo)(HCONV, ULONG, PCONVINFO); +HCONV (* APIENTRY WinDdeQueryNextServer)(HCONVLIST, HCONV); +ULONG (* APIENTRY WinDdeQueryString)(HSZ, PSZ, ULONG, ULONG); +HCONV (* APIENTRY WinDdeReconnect)(HCONV); +BOOL (* APIENTRY WinDdeSetUserHandle)(HCONV, ULONG, ULONG); +HDATA (* APIENTRY WinDdeSubmitTransaction)(PVOID, ULONG, HCONV, HSZ, USHORT, USHORT, ULONG, PULONG); +BOOL (* APIENTRY WinDdeUninitialize)(HDDEINST); + +/* restore structure packing */ +#pragma pack() + +#endif /* _H_DDEML */ +