Standaard heeft Ax een zogenaamde ‘named pipe’. Een named pipe kan je zien als een message-queue in standaard Windows die je aanmaakt en een listener op zet. Beetje vergelijkbaar met een map die actief gepolled wordt; zodra er een bestand binnenkomt wordt dit herkent en kan je er iets mee. Er staat al een goede blog van Max Belugin online die dit omschrijft: http://axcoder.blogspot.nl/2010/10/how-to-open-form-in-running-ax-from.html
Samengevat:
Elke Ax client maakt named-pipe aan met het formaat: Dynamics\Event\0S-1-5-5-x-xxxxxx
- Dynamics\Event – dit is een vaste waarde
- 0 – dit is de waarde ingericht in Ax. Basis \ Instellingen \ Waarschuwingen \ Waarschuwingsparameters:
- S-1-5-5-x-xxxxxx – dit is een Security Id (SID) voor de logon sessie. Dit is dus _niet_ de SID van de gebruiker. Dit is even interessant: de sessie is per gebruiker uniek op een terminal server, zodat de named pipe expliciet voor die gebruiker is en niet andere gebruikers gaat aansturen. Maar: als een gebruiker meerdere Ax sessies open hebt staan (zelfde of meerdere omgevingen), dan lijkt het de langst-actieve client die de queue afhandelt. Met de vorige parameter (Doel voor drilldown) zou je wel een test-omgeving een andere Id kunnen geven.
Wat iets meer in de oplossing zit van Max maar niet is verwoord, is wat er nou gebeurt in Ax:
1. De class ‘EventDrillDownPoller’, methode ‘scheduledPoll’
1 void scheduledPoll() 2 { 3 str pipeData; 4 SysStartUpCmd cmd; 5 ; 6 if (pipe) 7 { 8 pipeData = pipe.read(); 9 if ('' != pipeData) 10 { 11 cmd = SysStartUpCmd::construct(pipeData); 12 if (cmd) // INT - check null value 13 cmd.infoRun(); 14 this.createPipe(); // Re-create the pipe in order to be ready for the next command. 15 } 16 infolog.addTimeOut(this, methodstr(EventDrillDownPoller, scheduledPoll), #idleTimeBetweenExternalDrillDownPolls); 17 } 18 }
2. De SysStartUpCmd en afstammeling doet eigenlijk het werk:
1 static SysStartupCmd construct(str startupCommand) 2 { 3 str s = strLRTrim(startupCommand); 4 int p = strscan(s,'_',1,strlen(s)); 5 str parm; 6 SysStartupCmd sysStartupCmd; 7 ; 8 if (p) 9 { 10 parm = substr(s,p+1,strlen(s)); 11 parm = strrem(parm,'"'); 12 s = substr(s,1,p-1); 13 } 14 setprefix(s); 15 switch (s) 16 { 17 case 'setbuildno': 18 sysStartupCmd = new SysStartupCmdBuildNoSet(s,parm); 19 break; 20 ... 21 case 'callincomming': 22 // INT - process call 23 sysStartupCmd = new SysStartupCmdCallIncomming(s,parm); 24 break; 25 } 26 if (sysStartupCmd) 27 { 28 if (sysStartupCmd.canRun()) 29 return sysStartupCmd; 30 error(strfmt("%1, %2", s, parm)); 31 error("@SYS81158"); 32 } 33 return null; 34 }
Of als je veel commando’s denkt nodig te hebben, dan kan je de code beperken met de volgende switch:
1 ClassName className; 2 ClassId classId; 3 SysDictClass sysDictClass; 4 ; 5 ... 6 default: 7 // INT - use action name for classname 8 className = classid2name(classnum(SysStartupCmd)); 9 className += s; 10 classId = classname2id(className); 11 sysDictClass = new SysDictClass(classId); 12 if (sysDictClass 13 && sysDictClass.allowMakeObject()) 14 { 15 sysStartupCmd = sysDictClass.makeObject(s,parm); 16 } 17 break;