AESendMessage

Mac OSX specific forum
Rinzwind
Enthusiast
Enthusiast
Posts: 679
Joined: Wed Mar 11, 2009 4:06 pm
Location: NL

AESendMessage

Post by Rinzwind »

Any PB example for AESendMessage available? As of now not :(

https://developer.apple.com/documentati ... endmessage
User avatar
Mijikai
Addict
Addict
Posts: 1517
Joined: Sun Sep 11, 2016 2:17 pm

Re: AESendMessage

Post by Mijikai »

wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: AESendMessage

Post by wilbert »

Rinzwind wrote:Any PB example for AESendMessage available? As of now not :(
Do you have any example code in Objective-C or Swift of what you want to do with the command ?
Windows (x64)
Raspberry Pi OS (Arm64)
Rinzwind
Enthusiast
Enthusiast
Posts: 679
Joined: Wed Mar 11, 2009 4:06 pm
Location: NL

Re: AESendMessage

Post by Rinzwind »

I do have a C-example

Code: Select all

#include <stdio.h> 
#include <CoreServices/CoreServices.h>
#include <Carbon/Carbon.h>

static OSStatus SendAppleEventToSystemProcess(AEEventID EventToSend);

int main(void)
{
	const int bufferSize = 256;
	OSStatus error = noErr;
	char select [bufferSize];

	printf("1: Restart computer\n");
	printf("2: Shutdown computer\n");
	printf("3: Logout computer\n");
	printf("4: Sleep computer\n");
	printf("5: Quit All\n");
	printf("q: quit program\n");
	printf("please enter choice:\n");fflush(stdout);
	fgets(select, bufferSize, stdin);

	switch (select[0])
	{
		 case '1':
			//sending restart event to system
			error = SendAppleEventToSystemProcess(kAERestart);
			if (error == noErr)
				{printf("Computer is going to restart!\n");}
			else
				{printf("Computer wouldn't restart\n");}
		 break;
		
		 case '2':
			//sending shutdown event to system
			error = SendAppleEventToSystemProcess(kAEShutDown);
			if (error == noErr)
				{printf("Computer is going to shutdown!\n");}
			else
				{printf("Computer wouldn't shutdown\n");}
		 break;
		 case '3':
			//sending logout event to system
			error = SendAppleEventToSystemProcess(kAEReallyLogOut);
			if (error == noErr)
				{printf("Computer is going to logout!\n");}
			else
				{printf("Computer wouldn't logout");}
		 break;
		 case '4':
			//sending sleep event to system
			error = SendAppleEventToSystemProcess(kAESleep);
			if (error == noErr)
				{printf("Computer is going to sleep!\n");}
			else
				{printf("Computer wouldn't sleep");}
		break;				
	};

	return(0);
}

OSStatus SendAppleEventToSystemProcess(AEEventID EventToSend)
{
	AEAddressDesc targetDesc;
	static const ProcessSerialNumber kPSNOfSystemProcess = { 0, kSystemProcess };
	AppleEvent eventReply = {typeNull, NULL};
	AppleEvent appleEventToSend = {typeNull, NULL};

	OSStatus error = noErr;

	error = AECreateDesc(typeProcessSerialNumber, &kPSNOfSystemProcess, 
											sizeof(kPSNOfSystemProcess), &targetDesc);
	if (error != noErr)
	{
		return(error);
	}
	

	error = AECreateAppleEvent(kCoreEventClass, EventToSend, &targetDesc, 
				   kAutoGenerateReturnID, kAnyTransactionID, &appleEventToSend);

	AEDisposeDesc(&targetDesc);
	if (error != noErr)
	{
		return(error);
	}

	//error = AESend(&appleEventToSend, &eventReply, kAENoReply, 
	//			  kAENormalPriority, kAEDefaultTimeout, NULL, NULL);


	error = AESendMessage(&appleEventToSend, &eventReply,
				                          kAENormalPriority, 0);//kAEDefaultTimeout);
	AEDisposeDesc(&appleEventToSend);
	if (error != noErr)
	{
		return(error);
	}

	AEDisposeDesc(&eventReply);

	return(error); 
}

Even the 'abcd' AEEvents is driving me crazy in PB. How to easily handle those..
The example code runs fine btw.

For what it's worth, here some definitions I played with:

Code: Select all

ImportC ""
  AECreateDesc(typeCode, *dataPtr, dataSize,  *result)
  AECreateAppleEvent(theAEEventClass, theAEEventID,  *target, returnID, transactionID, *result)
  AESendMessage(*event, *reply, sendMode, timeOutInTicks.l)
  AEDisposeDesc(theAEDesc)
EndImport

Structure ProcessSerialNumber
  HighLongOfPSN.l
  LowLongOfPSN.l
EndStructure

Structure AEDesc
  descriptorType.a[4]
  dataHandle.l
EndStructure

#typeNull = 'null'
#typeProcessSerialNumber = 'psn '
#kCoreEventClass = 'aevt'
#kAutoGenerateReturnID         = -1
#kAnyTransactionID             = 0 
#kAENormalPriority             = $00000000
#kAEDefaultTimeout             = -1
#kAEShutDown                   = 'shut'
#kAESleep                      = 'slep'
#noErr = 0
How to easily set descriptorType to one of the predefined constants? Guess PB doesn't like that (overflow compiler error).
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: AESendMessage

Post by wilbert »

Rinzwind wrote:Even the 'abcd' AEEvents is driving me crazy in PB. How to easily handle those..
The example code runs fine btw.
I'm doing something wrong but I don't know what.
I thought it had to be something like the code below but it keeps crashing. :?

Edit:
See post below for the updated working code
viewtopic.php?p=541150#p541150

Code: Select all

#noErr                    = 0
#kAnyTransactionID        = 0
#kAENormalPriority        = 0
#kAENoReply               = 1
#kSystemProcess           = 1
#kAEDefaultTimeout        = -1
#kAutoGenerateReturnID    = -1
#kAERestart               = $72657374; 'rest'
#kAEShutDown              = $73687574; 'shut'
#kAEReallyLogOut          = $726C676F; 'rlgo'
#kAESleep                 = $736C6570; 'slep'
#typeNull                 = $6E756C6C; 'null'
#typeProcessSerialNumber  = $70736E20; 'psn '
#kCoreEventClass          = $61657674; 'aevt'

ImportC ""
  AECreateDesc(typeCode, *dataPtr, dataSize, *result)
  AECreateAppleEvent(theAEEventClass, theAEEventID, *target, returnID, transactionID, *result)
  AEDisposeDesc(*theAEDesc)
  AESend(*event, *reply, sendMode, sendPriority, timeOutInTicks, *idleProc, *filterProc)
  AESendMessage(*event, *reply, sendMode, timeOutInTicks)
EndImport

Structure ProcessSerialNumber
  HighLongOfPSN.l
  LowLongOfPSN.l
EndStructure

Structure AEDesc
  desctiptorType.l
  dataHandle.l
EndStructure

Procedure.l SendAppleEventToSystemProcess(EventToSend.l)
  Protected.ProcessSerialNumber kPSNOfSystemProcess
  Protected.AEDesc targetDesc, eventReply, appleEventToSend
  Protected.l error
  
  kPSNOfSystemProcess\LowLongOfPSN = #kSystemProcess
  eventReply\desctiptorType = #typeNull
  appleEventToSend\desctiptorType = #typeNull
  
  error = AECreateDesc(#typeProcessSerialNumber, @kPSNOfSystemProcess, SizeOf(kPSNOfSystemProcess), @targetDesc)
  If error
    ProcedureReturn error
  EndIf
  
  error = AECreateAppleEvent(#kCoreEventClass, EventToSend, @targetDesc, #kAutoGenerateReturnID, #kAnyTransactionID, @appleEventToSend)
  AEDisposeDesc(@targetDesc)
  If error
    ProcedureReturn error
  EndIf
  
  error = AESendMessage(@appleEventToSend, @eventReply, #kAENormalPriority, 0)
  ;error = AESend(@appleEventToSend, @eventReply, #kAENoReply, #kAENormalPriority, #kAEDefaultTimeout, #Null, #Null)
  AEDisposeDesc(@appleEventToSend)
  If error
    ProcedureReturn error
  EndIf  
  
  AEDisposeDesc(@eventReply)
  
  ProcedureReturn error
  
EndProcedure



SendAppleEventToSystemProcess(#kAESleep)
Last edited by wilbert on Wed Aug 28, 2019 3:44 pm, edited 1 time in total.
Windows (x64)
Raspberry Pi OS (Arm64)
Rinzwind
Enthusiast
Enthusiast
Posts: 679
Joined: Wed Mar 11, 2009 4:06 pm
Location: NL

Re: AESendMessage

Post by Rinzwind »

Thanks so far..
Huh?! moment:
After the line...
error = AECreateDesc(#typeProcessSerialNumber, @kPSNOfSystemProcess, SizeOf(kPSNOfSystemProcess), @targetDesc)
...the field eventReply\desctiptorType is changed from 1853189228 to 1? :?
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: AESendMessage

Post by wilbert »

Rinzwind wrote:Huh?! moment:
That's great. You found the problem. It is working now. :D

Code: Select all

#noErr                    = 0
#kAnyTransactionID        = 0
#kAENormalPriority        = 0
#kAENoReply               = 1
#kSystemProcess           = 1
#kAEDefaultTimeout        = -1
#kAutoGenerateReturnID    = -1
#kAERestart               = $72657374; 'rest'
#kAEShutDown              = $73687574; 'shut'
#kAEReallyLogOut          = $726C676F; 'rlgo'
#kAESleep                 = $736C6570; 'slep'
#typeNull                 = $6E756C6C; 'null'
#typeProcessSerialNumber  = $70736E20; 'psn '
#kCoreEventClass          = $61657674; 'aevt'

ImportC ""
  AECreateDesc(typeCode, *dataPtr, dataSize, *result)
  AECreateAppleEvent(theAEEventClass, theAEEventID, *target, returnID, transactionID, *result)
  AEDisposeDesc(*theAEDesc)
  AESend(*event, *reply, sendMode, sendPriority, timeOutInTicks, *idleProc, *filterProc)
  AESendMessage(*event, *reply, sendMode, timeOutInTicks)
EndImport

Structure ProcessSerialNumber
  HighLongOfPSN.l
  LowLongOfPSN.l
EndStructure

Structure AEDesc
  desctiptorType.l
  *dataHandle
EndStructure

Procedure.l SendAppleEventToSystemProcess(EventToSend.l)
  Protected.ProcessSerialNumber kPSNOfSystemProcess
  Protected.AEDesc targetDesc, eventReply, appleEventToSend
  Protected.l error
  
  kPSNOfSystemProcess\LowLongOfPSN = #kSystemProcess
  eventReply\desctiptorType = #typeNull
  appleEventToSend\desctiptorType = #typeNull
  
  error = AECreateDesc(#typeProcessSerialNumber, @kPSNOfSystemProcess, SizeOf(kPSNOfSystemProcess), @targetDesc)
  If error
    ProcedureReturn error
  EndIf
  
  error = AECreateAppleEvent(#kCoreEventClass, EventToSend, @targetDesc, #kAutoGenerateReturnID, #kAnyTransactionID, @appleEventToSend)
  AEDisposeDesc(@targetDesc)
  If error
    ProcedureReturn error
  EndIf
  
  error = AESendMessage(@appleEventToSend, @eventReply, #kAENormalPriority, 0)
  ;error = AESend(@appleEventToSend, @eventReply, #kAENoReply, #kAENormalPriority, #kAEDefaultTimeout, #Null, #Null)
  AEDisposeDesc(@appleEventToSend)
  If error
    ProcedureReturn error
  EndIf  
  
  AEDisposeDesc(@eventReply)
  
  ProcedureReturn error
  
EndProcedure



SendAppleEventToSystemProcess(#kAESleep)
Windows (x64)
Raspberry Pi OS (Arm64)
Rinzwind
Enthusiast
Enthusiast
Posts: 679
Joined: Wed Mar 11, 2009 4:06 pm
Location: NL

Re: AESendMessage

Post by Rinzwind »

Great it works. Heel erg bedankt voor de hulp.
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: AESendMessage

Post by wilbert »

Alternative code (macOS 10.11+) ...

Code: Select all

#typeProcessSerialNumber  = $70736E20; 'psn '
#kCoreEventClass          = $61657674; 'aevt'
#kAERestart               = $72657374; 'rest'
#kAEShutDown              = $73687574; 'shut'
#kAEReallyLogOut          = $726C676F; 'rlgo'
#kAESleep                 = $736C6570; 'slep'

#kAEDefaultTimeout        = -1
#kAENormalPriority        =  0
#kAnyTransactionID        =  0
#kAutoGenerateReturnID    = -1
#NSAppleEventSendNoReply  =  1


Procedure.l SendAppleEventToSystemProcess(EventToSend.l)
  
  Static kPSNOfSystemProcess.q = $100000000
  
  Protected.i target, event, error
  
  target = CocoaMessage(0, 0, "NSAppleEventDescriptor descriptorWithDescriptorType:", #typeProcessSerialNumber, 
                        "bytes:", @kPSNOfSystemProcess, "length:", 8)
  
  event = CocoaMessage(0, 0, "NSAppleEventDescriptor appleEventWithEventClass:", #kCoreEventClass, 
                       "eventID:", EventToSend, "targetDescriptor:", target, 
                       "returnID:", #kAutoGenerateReturnID, "transactionID:", #kAnyTransactionID)
  
  CocoaMessage(0, event, "sendEventWithOptions:", #NSAppleEventSendNoReply, 
               "timeout:", #kAEDefaultTimeout, "error:@", @error)
  
  ProcedureReturn Bool(error = #nil)
  
EndProcedure



SendAppleEventToSystemProcess(#kAESleep)
Windows (x64)
Raspberry Pi OS (Arm64)
User avatar
robertfern
User
User
Posts: 38
Joined: Fri Feb 02, 2018 10:33 pm
Location: New Jersey

Re: AESendMessage

Post by robertfern »

How does one send an AppleEvent to the Finder?
and also how does one send the data to go with the event?
Such as tell application "Finder" to get selection
then get the first item of the selection
and then tell application "Finder" to open information window of I
Applescript is

Code: Select all

tell application "Finder"
	set mySelection to selection
	repeat with i in mySelection
		set i to contents of i
		open information window of i
	end repeat
end tell
raw chevron code is

Code: Select all

tell «class capp» "Finder"
	set mySelection to «property sele»
	repeat with i in mySelection
		set i to «property pcnt» of i
		«event aevtodoc» «class iwnd» of i
	end repeat
end tell
Mac OSX Ventura & Windows 10, PB 6.12
User avatar
Piero
Addict
Addict
Posts: 863
Joined: Sat Apr 29, 2023 6:04 pm
Location: Italy

AppleScript to PB

Post by Piero »

robertfern wrote: Wed Jan 15, 2025 12:19 amAppleEvent to the Finder?
In case you wanted to use AppleScript directly:

Code: Select all

EnableExplicit

; for simple AppleScripts:
Procedure simpleShell(ShellCommand$, wait = #False)
   Protected shell = RunProgram("/bin/sh","","", #PB_Program_Open|#PB_Program_Write)
   If shell
      WriteProgramStringN(shell,ShellCommand$)
      WriteProgramData(shell,#PB_Program_Eof,0)
      If wait
         While ProgramRunning(shell) : Delay(10) : Wend
      EndIf
      CloseProgram(shell)
   EndIf
EndProcedure

; for AppleScripts:
Structure daResults : Out.s : Err.s : ExC.w : EndStructure
Global Sh.daResults

Procedure shShell(ShellCommand$, AddLF = #False)
   Protected Err$, tmper$, Output$, shell, Exc.w =-1 ; exit code -1 on failed launch
   shell = RunProgram("/bin/sh","","",
      #PB_Program_Open|#PB_Program_Write|#PB_Program_Read|#PB_Program_Error )
   If shell
      WriteProgramStringN(shell,ShellCommand$)
      WriteProgramData(shell,#PB_Program_Eof,0)
      While ProgramRunning(shell)
         If AvailableProgramOutput(shell)
            Output$ + ReadProgramString(shell)
            If AddLF : Output$ + ~"\n" : EndIf
         EndIf
         tmper$ = ReadProgramError(shell)
         If tmper$ : Err$ + tmper$ + ~"\n" : EndIf
      Wend
      Exc = ProgramExitCode(shell) : CloseProgram(shell)
   EndIf
   Sh\Out = Output$ : Sh\Err = Err$ : Sh\ExC = Exc
EndProcedure

; Translates AppleScript code on clipboard to obtain a PB string like below
; AppleScript MUST use TABS to indent. Better compile your AppleScript code, before copying it…
Define MyAScr.s = ~"osascript\n"+
~"set c to the clipboard as string\n"+
~"property dacut : \"\\\\n\\\"+\\r~\\\"\"\n"+
~"set pbt to \"\"\n"+
~"set rc to false -- false = do not remove comments\n"+
~"try\n"+
~"display dialog \"Remove -- Comments?\\r\\rAVOID -- in strings!!!\" buttons {\"Keep\", \"Remove\"} cancel button 1 default button 2\n"+
~"set rc to true\n"+
~"end try\n"+
~"repeat with i in paragraphs of c\n"+
~"set i to i as string\n"+
~"set i to replace_chars(i, tab, \"\") -- editor MUST use TABS to indent...\n"+
~"if rc then -- remove comments\n"+
~"set o to offset of \"--\" in i\n"+
~"if o > 1 then\n"+
~"set i to (characters 1 thru (o - 2) of i) as string\n"+
~"else if o = 1 then\n"+
~"set i to \"\"\n"+
~"end if\n"+
~"end if\n"+
~"if i is not \"\" then\n"+
~"set i to replace_chars(i, \"\\\\\", \"\\\\\\\\\") -- backslash to double backslash\n"+
~"set i to replace_chars(i, \"\\\"\", \"\\\\\\\"\") -- double-quote to backslash double-quote\n"+
~"set pbt to pbt & \"\\\\n\" & i\n"+
~"end if\n"+
~"end repeat\n"+
~"set pbt to \"Define MyAScr.s = ~\\\"osascript\" & pbt & \"\\\"\\r\\r\"\n"+
~"if (count pbt) > 80 then\n"+
~"try\n"+
~"display dialog \"CUT?\" buttons {\"NO\", \"Cut\"} cancel button 1 default button 2\n"+
~"copy replace_chars(pbt, \"\\\\n\", dacut) to pbt\n"+
~"end try\n"+
~"end if\n"+
~"set dla to display dialog \"Set the Clipboard to this?\\r\\rYou can edit here…\" default answer pbt buttons {\"Cancel\", \"Copy\"} cancel button 1 default button 2\n"+
~"set the clipboard to text returned of dla -- set the clipboard to pbt -- no editing\n"+  
~"on replace_chars(this_text, search_string, replacement_string)\n"+
~"local asd\n"+
~"set asd to AppleScript's text item delimiters\n"+
~"set AppleScript's text item delimiters to the search_string\n"+
~"set the item_list to every text item of this_text\n"+
~"set AppleScript's text item delimiters to the replacement_string\n"+
~"set this_text to the item_list as string\n"+
~"set AppleScript's text item delimiters to asd\n"+
~"return this_text\n"+
~"end replace_chars"

simpleShell(~"osascript\ntell app \"Finder\"\nbeep\nend", #True) ; #True = waits until exit
simpleShell("osascript -e beep") ; one word: doesn't even need to be quoted ('beep')

shShell(MyAScr) ; Eventual AppleScript returned output or error will be in Sh\Out, Sh\Err, Sh\ExC
Edit: added simpleShell
Last edited by Piero on Thu Jan 30, 2025 4:49 am, edited 3 times in total.
User avatar
robertfern
User
User
Posts: 38
Joined: Fri Feb 02, 2018 10:33 pm
Location: New Jersey

Re: AESendMessage

Post by robertfern »

Nice, but I already knew how to do that, but I want to be able to send the AppleEvents directly like the examples here.


** EDIT **

What are the "~" (tilde) character doing in your string concatinations?
Mac OSX Ventura & Windows 10, PB 6.12
User avatar
Shardik
Addict
Addict
Posts: 2058
Joined: Thu Apr 21, 2005 2:38 pm
Location: Germany

Re: AESendMessage

Post by Shardik »

robertfern wrote: Wed Jan 15, 2025 12:19 am How does one send an AppleEvent to the Finder?
and also how does one send the data to go with the event?
Such as tell application "Finder" to get selection
then get the first item of the selection
and then tell application "Finder" to open information window of I
You don't need to use AppleEvent to display the information window of a selected file in the finder. You may take a look into my second example which demonstrates how to open the finder, select the logo.png file in the PureBasic.app and display the information window for logo.png.
User avatar
robertfern
User
User
Posts: 38
Joined: Fri Feb 02, 2018 10:33 pm
Location: New Jersey

Re: AESendMessage

Post by robertfern »

OK, but I still would like to know how to send an AppleEvent with data attached. SO I can use the same process to handle other programs and events.
I don't need an aletrnative way to do this. I want to KNOW how it gets done so I can LEARN.
Mac OSX Ventura & Windows 10, PB 6.12
User avatar
Piero
Addict
Addict
Posts: 863
Joined: Sat Apr 29, 2023 6:04 pm
Location: Italy

Re: AESendMessage

Post by Piero »

robertfern wrote: Wed Jan 15, 2025 7:33 pmWhat are the "~" (tilde) character doing in your string concatinations?
Hope you already found it, anyway it's to use "escape codes", like:

Code: Select all

Debug ~"\n \r \t \\ \" "
PS: in AppleScript you don't need to prepend '~' (tilde) to use escape codes in strings…
Post Reply