Ejemplo n.º 1
0
OSStatus AEMSendMessageThreadSafe(
    AppleEvent *            eventPtr,
    AppleEvent *            replyPtr,
	AESendMode              sendMode,
    long                    timeOutInTicks
)
    // See comment in header.
{
    OSStatus        err = noErr;
    mach_port_t     replyPort;
    assert(eventPtr != NULL);
    assert(replyPtr != NULL);
    
	if (sendMode && kAEWaitReply) {
		replyPort = MACH_PORT_NULL;
		
		// Set up the reply port if necessary.
		
		err = BindReplyMachPortToThread(&replyPort);
		if ( (err == noErr) && (replyPort != MACH_PORT_NULL) ) {
			err = AEPutAttributePtr(eventPtr, keyReplyPortAttr, typeMachPort, &replyPort, sizeof(replyPort));
		}
    }
	
    // Call through to AESendMessage.
    
    if (err == noErr) {
        err = AESendMessage(eventPtr, replyPtr, sendMode, timeOutInTicks);
    }
    
    return err;
}
Ejemplo n.º 2
0
OSStatus SendAppleEventToSystemProcess(AEEventID eventToSendID)
{
  AEAddressDesc targetDesc;
  static const  ProcessSerialNumber kPSNOfSystemProcess = {0, kSystemProcess };
  AppleEvent    eventReply  = {typeNull, NULL};
  AppleEvent    eventToSend = {typeNull, NULL};

  OSStatus status = AECreateDesc(typeProcessSerialNumber,
    &kPSNOfSystemProcess, sizeof(kPSNOfSystemProcess), &targetDesc);

  if (status != noErr)
    return status;

  status = AECreateAppleEvent(kCoreEventClass, eventToSendID,
    &targetDesc, kAutoGenerateReturnID, kAnyTransactionID, &eventToSend);
  AEDisposeDesc(&targetDesc);

  if (status != noErr)
    return status;

  status = AESendMessage(&eventToSend, &eventReply, kAENormalPriority, kAEDefaultTimeout);
  AEDisposeDesc(&eventToSend);

  if (status != noErr)
    return status;

  AEDisposeDesc(&eventReply);

  return status;
}
Ejemplo n.º 3
0
static void we_are_dead(void) {
    AttachErrorCode(quit_event,noErr);
    /* Send the reply (I hope) */
    AESendMessage(quit_event,NULL, kAENoReply, kAEDefaultTimeout);
    AEDisposeDesc(quit_event);
    /* fall off the end of the world and die */
 fprintf( logfile, " event succeded.\n"); fflush( logfile );
}
Ejemplo n.º 4
0
bool document_is_open(CFStringRef docNameCF, ProcessSerialNumber *psn)
{
	char          docNameData[255];
	char          *docName = &docNameData[0];
	OSStatus      status;
	AppleEvent    ae, reply;
	AEBuildError  buildError;
	char          returnValue;
	char          *eventDescriptor;
	
	/* Apple Events only support MacRoman, I think */
	if (!CFStringGetCString(docNameCF, docName, 255, kCFStringEncodingMacRoman))
		give_up("Document name could not be encoded as MacRoman, or too long");
	
	if (docName[0] == '/')
		eventDescriptor =
			"'----':obj{form:enum('test'),want:type('docu'),seld:cmpd{relo:=,"
			"'obj1':obj{form:prop, want:type('prop'), seld:type('ppth'), from:exmn()},"
			"'obj2':TEXT(@)},from:null()}";
	else
		eventDescriptor =
			"'----':obj{form:enum('name'),want:type('docu'),seld:TEXT(@),from:null()}";

	status = AEBuildAppleEvent(kAECoreSuite, kAEDoObjectsExist,
		typeProcessSerialNumber, psn, sizeof(*psn),
		kAutoGenerateReturnID, kAnyTransactionID, &ae,
		&buildError, eventDescriptor, docName);
	if (status != noErr) {
		fprintf(stderr, "check_open: AEBuildAppleEvent failed: error %d at pos %lu\n",
			buildError.fError, buildError.fErrorPos);
		fprintf(stderr, "(See http://developer.apple.com/technotes/tn/tn2045.html#errstable)\n");
		exit(255);
	}
	
	status = AESendMessage(&ae, &reply,	kAEWaitReply, kAEDefaultTimeout);
	if (status != noErr)
		die("AESend failed");
	
	status = AEGetParamPtr (&reply, keyDirectObject, typeBoolean, 
		NULL, &returnValue, 1, NULL);
	if (status != noErr)
		/* Presumably this is because the reply is an error message */
		give_up("Application appears not to understand request");
	
	return (returnValue != 0);
	
    /* We don't need to bother disposing of things, because we're about to finish */
}
Ejemplo n.º 5
0
static PyObject *AEDesc_AESendMach(AEDescObject *_self, PyObject *_args)
{
	PyObject *_res = NULL;
	OSErr _err;
	AppleEvent reply;
	AESendMode sendMode;
	AESendPriority sendPriority;
	long timeOutInTicks;
	mach_port_t replyPort;
#ifndef AESend
	PyMac_PRECHECK(AESend);
#endif
	if (!PyArg_ParseTuple(_args, "lhl",
						  &sendMode,
						  &sendPriority,
						  &timeOutInTicks))
		return NULL;
	if (sendMode == kAEWaitReply) {
		if (mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &replyPort) != KERN_SUCCESS) {
			PyErr_SetString(PyExc_SystemError, "Failed to allocate Mach port!");
			return NULL;
		}
		_err = AEPutAttributePtr(&_self->ob_itself,
								 keyReplyPortAttr,
								 typeMachPort,
								 &replyPort,
								 sizeof(replyPort));
		if (_err != noErr) goto cleanup;
	}
	_err = AESendMessage(&_self->ob_itself,
						 &reply,
						 sendMode,
						 timeOutInTicks);
	if (_err != noErr) goto cleanup;
	_res = Py_BuildValue("O&",
						 AEDesc_New, &reply);
	if (sendMode == kAEWaitReply) {
		mach_port_destroy(mach_task_self(), replyPort);
	}
	return _res;
cleanup:
	if (sendMode == kAEWaitReply) {
		mach_port_destroy(mach_task_self(), replyPort);
	}
	return PyMac_Error(_err);
}
Ejemplo n.º 6
0
static PyObject *AEDesc_AESendMessage(AEDescObject *_self, PyObject *_args)
{
	PyObject *_res = NULL;
	OSErr _err;
	AppleEvent reply;
	AESendMode sendMode;
	long timeOutInTicks;
#ifndef AESendMessage
	PyMac_PRECHECK(AESendMessage);
#endif
	if (!PyArg_ParseTuple(_args, "ll",
						  &sendMode,
						  &timeOutInTicks))
		return NULL;
	_err = AESendMessage(&_self->ob_itself,
						 &reply,
						 sendMode,
						 timeOutInTicks);
	if (_err != noErr) return PyMac_Error(_err);
	_res = Py_BuildValue("O&",
						 AEDesc_New, &reply);
	return _res;
}
static OSStatus SendAppleEvent(const AEDesc *event, AEDesc *reply)
	// This is the bottleneck routine we use for sending Apple events.
	// It has a number of neato features.
	// 
	// o It use the "AEMach.h" routine AESendMessage because that allows 
	//   us to do an RPC without having to field UI events while waiting 
	//   for the reply.  Yay for Mac OS X!
	//
	// o It automatically extracts the error from the reply.
	//
	// o It allows you to enable printing of events and their replies 
	//   for debugging purposes.
{
	static const long kAETimeoutTicks = 5 * 60;
	OSStatus 	err;
	OSErr		replyErr;
	DescType	junkType;
	Size		junkSize;

	// Normally I don't declare function prototypes in local scope, 
	// but I made this exception because I don't want anyone except 
	// for this routine calling GDBPrintAEDesc.  This routine takes 
	// care to only link with the routine when debugging is enabled; 
	// everyone else might not be so careful.
	
	#if LOGIN_ITEMS_AE_PRINT_DESC

		extern void GDBPrintAEDesc(const AEDesc *desc);
			// This is private system function used to print a 
			// textual representation of an AEDesc to stderr.  
			// It's very handy when debugging, and is meant only 
			// for that purpose.  It's only available to Mach-O 
			// clients.  We use it when debugging *only*.

	#endif

	assert(event != NULL);
	assert(reply != NULL);

	#if LOGIN_ITEMS_AE_PRINT_DESC
		GDBPrintAEDesc(event);
	#endif

	err = AESendMessage(event, reply, kAEWaitReply, kAETimeoutTicks);

	#if LOGIN_ITEMS_AE_PRINT_DESC
		GDBPrintAEDesc(reply);
	#endif

	// Extract any error from the Apple event handler via the 
	// keyErrorNumber parameter of the reply.
	
	if ( (err == noErr) && (reply->descriptorType != typeNull) ) {
		err = AEGetParamPtr(
			reply, 
			keyErrorNumber, 
			typeShortInteger, 
			&junkType,
			&replyErr, 
			sizeof(replyErr), 
			&junkSize
		);
		
		if (err == errAEDescNotFound ) {
			err = noErr;
		} else {
			err = replyErr;
		}
	}
	
	return err;
}
wchar_t *sendpraatW (void *display, const wchar_t *programName, long timeOut, const wchar_t *text) {
    wchar_t nativeProgramName [100];
#if xwin
    char *home, pidFileName [256], messageFileName [256];
    FILE *pidFile;
    long pid, wid = 0;
#elif win
    wchar_t homeDirectory [256], messageFileName [256], windowName [256];
    HWND window;
    (void) display;
    (void) timeOut;
#elif mac
    AEDesc programDescriptor;
    AppleEvent event, reply;
    OSErr err;
    UInt32 signature;
    (void) display;
#endif

    /*
     * Clean up from an earlier call.
     */
    errorMessageW [0] = '\0';

    /*
     * Handle case differences.
     */
    wcscpy (nativeProgramName, programName);
#if xwin
    nativeProgramName [0] = tolower (nativeProgramName [0]);
#else
    nativeProgramName[0] = toupper (nativeProgramName [0]);
#endif

    /*
     * If the text is going to be sent in a file, create its name.
     * The file is going to be written into the preferences directory of the receiving program.
     * On X Window, the name will be something like /home/jane/.praat-dir/message.
     * On Windows, the name will be something like C:\Documents and Settings\Jane\Praat\Message.txt,
     * or C:\Windows\Praat\Message.txt on older systems.
     * On Macintosh, the text is NOT going to be sent in a file.
     */
#if xwin
    if ((home = getenv ("HOME")) == NULL) {
        swprintf (errorMessageW, 1000, L"HOME environment variable not set.");
        return errorMessageW;
    }
    sprintf (messageFileName, "%s/.%ls-dir/message", home, programName);
#elif win
    if (GetEnvironmentVariableW (L"USERPROFILE", homeDirectory, 255)) {
        ;   /* Ready. */
    } else if (GetEnvironmentVariableW (L"HOMEDRIVE", homeDirectory, 255)) {
        GetEnvironmentVariableW (L"HOMEPATH", homeDirectory + wcslen (homeDirectory), 255);
    } else {
        GetWindowsDirectoryW (homeDirectory, 255);
    }
    swprintf (messageFileName, 256, L"%ls\\%ls\\Message.txt", homeDirectory, programName);
#endif

    /*
     * Write the message file (Unix and Windows only).
     */
#if xwin
    FILE *messageFile;
    if ((messageFile = fopen (messageFileName, "w")) == NULL) {
        swprintf (errorMessageW, 1000, L"Cannot create message file \"%s\" "
                  L"(no privilege to write to directory, or disk full).\n", messageFileName);
        return errorMessageW;
    }
    if (timeOut)
        fwprintf (messageFile, L"#%ld\n", getpid ());   /* Write own process ID for callback. */
    fwprintf (messageFile, L"\ufeff%ls", text);
    fclose (messageFile);
#elif win
    {   /* 20090401 [email protected] added braces to please visual studio */
        FILE *messageFile;
        if ((messageFile = _wfopen (messageFileName, L"w")) == NULL) {
            swprintf (errorMessageW, 1000, L"Cannot create message file \"%ls\" "
                      L"(no privilege to write to directory, or disk full).\n", messageFileName);
            return errorMessageW;
        }
        fwprintf (messageFile, L"\ufeff%ls", text);
        fclose (messageFile);
    } /* 20090401 [email protected] added braces to please visual studio */
#endif

    /*
     * Where shall we send the message?
     */
#if xwin
    /*
     * Get the process ID and the window ID of a running Praat-shell program.
     */
    sprintf (pidFileName, "%s/.%ls-dir/pid", home, programName);
    if ((pidFile = fopen (pidFileName, "r")) == NULL) {
        swprintf (errorMessageW, 1000, L"Program %ls not running (or a version older than 3.6).", programName);
        return errorMessageW;
    }
    if (fscanf (pidFile, "%ld%ld", & pid, & wid) < 1) {
        fclose (pidFile);
        swprintf (errorMessageW, 1000, L"Program %ls not running, or disk has been full.", programName);
        return errorMessageW;
    }
    fclose (pidFile);
#elif win
    /*
     * Get the window handle of the "Objects" window of a running Praat-shell program.
     */
    swprintf (windowName, 256, L"PraatShell1 %ls", programName);
    window = FindWindowW (windowName, NULL);
    if (! window) {
        swprintf (errorMessageW, 1000, L"Program %ls not running (or an old version).", programName);
        return errorMessageW;
    }
#elif mac
    /*
     * Convert the program name to a Macintosh signature.
     * I know of no system routine for this, so I'll just translate the two most common names:
     */
    if (! wcscmp (programName, L"praat") || ! wcscmp (programName, L"Praat") || ! wcscmp (programName, L"PRAAT"))
        signature = 'PpgB';
    else if (! wcscmp (programName, L"als") || ! wcscmp (programName, L"Als") || ! wcscmp (programName, L"ALS"))
        signature = 'CclA';
    else
        signature = 0;
    AECreateDesc (typeApplSignature, & signature, 4, & programDescriptor);
#endif

    /*
     * Send the message.
     */
#if xwin
    /*
     * Be ready to receive notification of completion.
     */
    if (timeOut)
        signal (SIGUSR2, handleCompletion);
    /*
     * Notify running program.
     */
    if (wid != 0) {   /* Praat shell version October 21, 1998 or later? Send event to window. */
        /*
         * Notify main window.
         */
        XEvent event;
        int displaySupplied = display != NULL;
        if (! displaySupplied) {
            display = XOpenDisplay (NULL);
            if (display == NULL) {
                swprintf (errorMessageW, 1000, L"Cannot open display %s.", XDisplayName (NULL));
                return errorMessageW;
            }
        }
        event. type = ClientMessage;
        event. xclient. serial = 0;
        event. xclient. send_event = True;
        event. xclient. display = display;
        event. xclient. window = (Window) wid;
        event. xclient. message_type = XInternAtom (display, "SENDPRAAT", False);
        event. xclient. format = 8;   /* No byte swaps. */
        strcpy (& event. xclient.data.b [0], "SENDPRAAT");
        if(! XSendEvent (display, (Window) wid, True, KeyPressMask, & event)) {
            if (! displaySupplied) XCloseDisplay (display);
            swprintf (errorMessageW, 1000, L"Cannot send message to %ls (window %ld). "
                      "The program %ls may have been started by a different user, "
                      "or may have crashed.", programName, wid, programName);
            return errorMessageW;
        }
        if (! displaySupplied) XCloseDisplay (display);
    } else {
        /*
         * Use interrupt mechanism.
         */
        if (kill (pid, SIGUSR1)) {
            swprintf (errorMessageW, 1000, L"Cannot send message to %ls (process %ld). "
                      "The program %ls may have been started by a different user, "
                      "or may have crashed.", programName, pid, programName);
            return errorMessageW;
        }
    }
    /*
     * Wait for the running program to notify us of completion,
     * but do not wait for more than 'timeOut' seconds.
     */
    if (timeOut) {
        signal (SIGALRM, handleTimeOut);
        alarm (timeOut);
        theTimeOut = timeOut;   /* Hand an argument to handleTimeOut () in a static variable. */
        errorMessageW [0] = '\0';
        pause ();
        if (errorMessageW [0] != '\0') return errorMessageW;
    }
#elif win
    /*
     * Notify the running program by sending a WM_USER message to its main window.
     */
    if (SendMessage (window, WM_USER, 0, 0)) {
        swprintf (errorMessageW, 1000, L"Program %ls returns error.", programName);   /* BUG? */
        return errorMessageW;
    }
#elif mac
    /*
     * Notify the running program by sending it an Apple event of the magic class 758934755.
     */
    AECreateAppleEvent (758934755, 0, & programDescriptor, kAutoGenerateReturnID, 1, & event);
    AEPutParamPtr (& event, 1, typeUnicodeText, text, wcslen (text) + 1);
#ifdef __MACH__
    err = AESendMessage (& event, & reply,
                         ( timeOut == 0 ? kAENoReply : kAEWaitReply ) | kAECanInteract | kAECanSwitchLayer,
                         timeOut == 0 ? kNoTimeOut : 60 * timeOut);
#else
    err = AESend (& event, & reply,
                  ( timeOut == 0 ? kAENoReply : kAEWaitReply ) | kAECanInteract | kAECanSwitchLayer,
                  kAENormalPriority, timeOut == 0 ? kNoTimeOut : 60 * timeOut, NULL, NULL);
#endif
    if (err != noErr) {
        if (err == procNotFound || err == connectionInvalid)
            swprintf (errorMessageW, 1000, L"Could not send message to program \"%ls\".\n"
                      L"The program is probably not running (or an old version).", programName);
        else if (err == errAETimeout)
            swprintf (errorMessageW, 1000, L"Message to program \"%ls\" timed out "
                      L"after %ld seconds, before completion.", programName, timeOut);
        else
            swprintf (errorMessageW, 1000, L"Unexpected sendpraat error %d.\nNotify the author.", err);
    }
    AEDisposeDesc (& programDescriptor);
    AEDisposeDesc (& event);
    AEDisposeDesc (& reply);
#endif

    /*
     * Notify the caller of success (NULL pointer) or failure (string with an error message).
     */
    return errorMessageW [0] == '\0' ? NULL : errorMessageW;
}
Ejemplo n.º 9
0
char *sendpraat (void *display, const char *programName, long timeOut, const char *text) {
	char nativeProgramName [100];
	#if gtk
		char *home, pidFileName [256], messageFileName [256];
		FILE *pidFile;
		long pid, wid = 0;
	#elif win
		char homeDirectory [256], messageFileName [256], windowName [256];
		HWND window;
		(void) display;
		(void) timeOut;
	#elif mac
		AEDesc programDescriptor;
		AppleEvent event, reply;
		OSStatus err;
		UInt32 signature;
		(void) display;
	#endif

	/*
	 * Clean up from an earlier call.
	 */
	errorMessage [0] = '\0';

	/*
	 * Handle case differences.
	 */
	strcpy (nativeProgramName, programName);
	#if gtk
		nativeProgramName [0] = tolower (nativeProgramName [0]);
	#else
		nativeProgramName [0] = toupper (nativeProgramName [0]);
	#endif

	/*
	 * If the text is going to be sent in a file, create its name.
	 * The file is going to be written into the preferences directory of the receiving program.
	 * On Unix, the name will be something like /home/jane/.praat-dir/message.
	 * On Windows, the name will be something like C:\Users\Jane\Praat\Message.txt,
	 * or C:\Windows\Praat\Message.txt on older systems.
	 * On Macintosh, the text is NOT going to be sent in a file.
	 */
	#if gtk
		if ((home = getenv ("HOME")) == NULL) {
			sprintf (errorMessage, "HOME environment variable not set.");
			return errorMessage;
		}
		sprintf (messageFileName, "%s/.%s-dir/message", home, programName);
	#elif win
		if (GetEnvironmentVariableA ("USERPROFILE", homeDirectory, 255)) {
			;   /* Ready. */
		} else if (GetEnvironmentVariableA ("HOMEDRIVE", homeDirectory, 255)) {
			GetEnvironmentVariableA ("HOMEPATH", homeDirectory + strlen (homeDirectory), 255);
		} else {
			GetWindowsDirectoryA (homeDirectory, 255);
		}
		sprintf (messageFileName, "%s\\%s\\Message.txt", homeDirectory, programName);
	#endif

	/*
	 * Save the message file (Unix and Windows only).
	 */
	#if gtk || win
	{
		FILE *messageFile;
		if ((messageFile = fopen (messageFileName, "w")) == NULL) {
			sprintf (errorMessage, "Cannot create message file \"%s\" "
				"(no privilege to write to directory, or disk full, or program is not called %s).\n", messageFileName, programName);
			return errorMessage;
		}
		#if gtk
			if (timeOut)
				fprintf (messageFile, "#%ld\n", (long) getpid ());   /* Write own process ID for callback. */
		#endif
		fprintf (messageFile, "%s", text);
		fclose (messageFile);
	}
	#endif

	/*
	 * Where shall we send the message?
	 */
	#if gtk
		/*
		 * Get the process ID and the window ID of a running Praat-shell program.
		 */
		sprintf (pidFileName, "%s/.%s-dir/pid", home, programName);
		if ((pidFile = fopen (pidFileName, "r")) == NULL) {
			sprintf (errorMessage, "Program %s not running.", programName);
			return errorMessage;
		}
		if (fscanf (pidFile, "%ld%ld", & pid, & wid) < 1) {
			fclose (pidFile);
			sprintf (errorMessage, "Program %s not running, or disk has been full.", programName);
			return errorMessage;
		}
		fclose (pidFile);
	#elif win
		/*
		 * Get the window handle of the "Objects" window of a running Praat-shell program.
		 */
		sprintf (windowName, "PraatShell1 %s", programName);
		window = FindWindowA (windowName, NULL);
		if (! window) {
			sprintf (errorMessage, "Program %s not running (or an old version).", programName);
			return errorMessage;
		}
	#elif mac
		/*
		 * Convert the program name to a Macintosh signature.
		 * I know of no system routine for this, so I'll just translate the two most common names:
		 */
		if (! strcmp (programName, "praat") || ! strcmp (programName, "Praat") || ! strcmp (programName, "PRAAT"))
			signature = 'PpgB';
		else if (! strcmp (programName, "als") || ! strcmp (programName, "Als") || ! strcmp (programName, "ALS"))
			signature = 'CclA';
		else
			signature = 0;
		AECreateDesc (typeApplSignature, & signature, 4, & programDescriptor);
	#endif

	/*
	 * Send the message.
	 */
	#if gtk
		/*
		 * Be ready to receive notification of completion.
		 */
		if (timeOut)
			signal (SIGUSR2, handleCompletion);
		/*
		 * Notify running program.
		 */
		if (wid != 0) {   /* Praat shell version October 21, 1998 or later? Send event to window. */
			/*
			 * Notify main window.
			 */
			GdkEventClient gevent;
			g_type_init ();
			int displaySupplied = display != NULL;
			if (! displaySupplied) {
				display = gdk_display_open (getenv ("DISPLAY"));   /* GdkDisplay* */
				if (display == NULL) {
					sprintf (errorMessage, "Cannot open display %s", getenv ("DISPLAY"));
					return errorMessage;
				}
			}
			gevent. type = GDK_CLIENT_EVENT;
			gevent. window = 0;
			gevent. send_event = 1;
			gevent. message_type = gdk_atom_intern_static_string ("SENDPRAAT");
			gevent. data_format = 8;
			if (! gdk_event_send_client_message_for_display (display, (GdkEvent *) & gevent, wid)) {
				if (! displaySupplied) gdk_display_close (display);
				sprintf (errorMessage, "Cannot send message to %s (window %ld). "
					"The program %s may have been started by a different user, "
					"or may have crashed.", programName, wid, programName);
				return errorMessage;
			}
			if (! displaySupplied) gdk_display_close (display);
		}
		/*
		 * Wait for the running program to notify us of completion,
		 * but do not wait for more than 'timeOut' seconds.
		 */
		if (timeOut) {
			signal (SIGALRM, handleTimeOut);
			alarm (timeOut);
			theTimeOut = timeOut;   /* Hand an argument to handleTimeOut () in a static variable. */
			errorMessage [0] = '\0';
			pause ();
			if (errorMessage [0] != '\0') return errorMessage;
		}
	#elif win
		/*
		 * Notify the running program by sending a WM_USER message to its main window.
		 */
		if (SendMessage (window, WM_USER, 0, 0)) {
			sprintf (errorMessage, "Program %s returns error.", programName);   /* BUG? */
			return errorMessage;
		}
	#elif mac
		/*
		 * Notify the running program by sending it an Apple event of the magic class 758934755.
		 */
		AECreateAppleEvent (758934755, 0, & programDescriptor, kAutoGenerateReturnID, 1, & event);
		AEPutParamPtr (& event, 1, typeChar, text, strlen (text) + 1);
		#ifdef __MACH__
			err = AESendMessage (& event, & reply,
				( timeOut == 0 ? kAENoReply : kAEWaitReply ) | kAECanInteract | kAECanSwitchLayer,
				timeOut == 0 ? kNoTimeOut : 60 * timeOut);
		#else
			err = AESend (& event, & reply,
				( timeOut == 0 ? kAENoReply : kAEWaitReply ) | kAECanInteract | kAECanSwitchLayer,
				kAENormalPriority, timeOut == 0 ? kNoTimeOut : 60 * timeOut, NULL, NULL);
		#endif
		if (err != noErr) {
			if (err == procNotFound || err == connectionInvalid)
				sprintf (errorMessage, "Could not send message to program \"%s\".\n"
					"The program is probably not running (or an old version).", programName);
			else if (err == errAETimeout)
				sprintf (errorMessage, "Message to program \"%s\" timed out "
					"after %ld seconds, before completion.", programName, timeOut);
			else
				sprintf (errorMessage, "Unexpected sendpraat error %d.\nNotify the author.", err);
		}
		AEDisposeDesc (& programDescriptor);
		AEDisposeDesc (& event);
		AEDisposeDesc (& reply);
	#endif

	/*
	 * Notify the caller of success (NULL pointer) or failure (string with an error message).
	 */
	return errorMessage [0] == '\0' ? NULL : errorMessage;
}