Calling PM from AVIO Applications
Written by Roman Stangl
The SwitchTo/2 sample
As you may know, OS/2 gives the foreground session a priority boost, unless you have specified PRIORITY=ABSOLUTE in your CONFIG.SYS.
For an unattended workstation, which is just accessed remotely with the Netfinity Systems Management application to force daily reboots, I had the need to ensure that the workstation automatically boots into a full-screen OS/2 application to get it the mentioned priority boost. Unfortunately, IBM's CM/2 (Communications Manager/2) which gets also started from STARTUP.CMD switches the input focus to one of its own windows. As only the active window can start another session into the foreground, it resulted in a SYS1806 message when STARTUP.CMD tried to start the full-screen application into the foreground (using the /F parameter).
Therefore I wrote SwitchTo/2 which, when added as the last line to CONFIG.SYS ensures that the mentioned OS/2 full-screen application gets switched into foreground automatically (exactly by specifying SwitchTo "HPTS Logon Manager").
PM APIs and AVIO sessions
As you may know too, calling PM APIs from a non-PM session will fail, as creating a message queue with WinCreateMsgQueue() returns with an error PMERR_NOT_IN_A_PM_SESSION In other words, you can initialize the PM environment successfully only for programs running in a PM session.
It is widely assumed that specifying the attribute WINDOWAPI in a module definition files ensures that a program will start in a PM session, whereas WINDOWCOMPAT causes the program to start in a AVIO session (that is into a windowed or full-screen command prompt). This is true in part. When the WPS and likely most other program starters launch a program, they take this attribute (which the linker puts into the executable; you can use the EXEHDR.EXE tool to verify that) from the executable and create the corresponding session environment to launch the program in (to be exact, this is done by DosStartSession() and WinStartApp() implicitly when you specify to launch a program in its default session).
To PM or not to PM
Having said that, it seems to be clear that to call PM APIs one would have to add the attribute WINDOWAPI into the module definition file, which on the other hand prevents to print messages to stdout when invoked from a command prompt as a child session (PM sessions do have defined stdout, stderr and stdin, but there is no "console" to display the output or read the input unless you use redirection).
However you may have seen that you can launch with the JAVA applet viewer non-AWT and AWT (and AWT on OS/2 is based on PM) applets from a single viewer program. This viewer seems to be an executable which is a AVIO program that can also do PM APIs if required. But, we found that AVIO programs can't call PM APIs, so what's going on?
Exploring the magic behind SwitchTo/2
The key point is that I said calling PM APIs from a non-PM session will fail, but I didn't say that programs marked having the attribute WINDOWCOMPAT can't be launched into a PM session. SwitchTo/2 exactly does show that it can be done.
When you unzip the source of SwitchTo/2 you can see that SWITCHTO.EXE will be linked as a WINDOWCOMPAT session as the module definition file shows:
NAME SWITCHTO WINDOWCOMPAT
So, when you run the executable from an OS/2 (windowed or full-screen) command prompt without specifying commandline parameters, it will show you its syntax. Also using EXEHDR.EXE you can prove that SWITCHTO.EXE is not marked as a PM application.
At line 308 in SWITCHTO.CPP you can see however that I do create a message queue, and even I haven't used WinGetLastError() you can trust me that I didn't get an PMERR_NOT_IN_A_PM_SESSION error ;-), you may even uncomment the few lines that display a small window:
hab=WinInitialize(0); hmq=WinCreateMsgQueue(hab, 0);
Note: Just to enumerate the Window List and switch to a session by calling WinSwitchToProgram() no message queue is required and could therefore be done from a non-PM program too, but you can easily imagine enhancements that do require a message queue (which most PM APIs do).
So where is the magic now? Actually you can even use two methods, both demonstrated by SwitchTo/2:
1. To activate the first method just add the parameter "LAUNCHCOPY=1" when building SwitchTo/2 by NMAKE. When SwitchTo/2 is started (as an AVIO session from a commandline) then, it launches another instance of itself, however this second instance is launched as a PM session as line 252 shows:
Launching AVIO sessions as PM sessions (e.g. from a commandline you can do that by specifying the parameter /PM when using the START command, for DosStartSession() and WinStartApp() you just have to specify the session type being PM) is all you need to do to call PM APIs from executables having been marked as WINDOWCOMPAT.
2. Alternatively just add the parameter "CHANGETYPE=1" when building SwitchTo/2 by NMAKE. When SwitchTo/2 is started (as an AVIO session from a commandline) then, it changes its process type by overriding the process type from the PIB (Process Information Block) as line 211 shows (and ulProcessType has the value PROG_PM):
So there is no magic at all! With both methods just uncomment lines 310 to 320 and a small window confirms what I've said.
Of course it would be nice if the AVIO session does not terminate while its copy launched as a PM session is running, which can be done by starting the PM copy as a child session. By using a termination queue, the parent AVIO session receives also the termination return code of its PM session instance as line 263 shows:
apiretRc=DosReadQueue(hqueueSelf, &requestdataSelf, &ulDataLen, (PPVOID)&pszBuffer, 0L, DCWW_WAIT, &bPriority, 0); ... presultcodeSelf=(RESULTCODE *)pszBuffer;
One additional thing about the termination return code. I had to define my own structure, as the Warp 4 Toolkit I was using has also defined two of them, one with 2 USHORT elements and one with 2 ULONG elements, and both have the same name, so be cautious!
One final thing to mention is, how do the instances of SwitchTo/2 know in which session environment they are running in (that is how do they know creating a message queue will fail or not fail)? Well, that's actually easy to determine, just ask OS/2 about the process type, as line 188 shows:
DosGetInfoBlocks(&ptib, &ppib); ulProcessType=ppib->pib_ultype;
By the way, the only reliable way to find out the fully qualified path an executable was launched from is also by asking OS/2 which line 190 shows:
DosQueryModuleName(ppib->pib_hmte, sizeof(acPathExecutable), acPathExecutable);
The well known method of getting that information from argv will work in most cases (it depends on how the path was specified when the program was launched), but definitely not in all!
I hope I've shed some light onto one of those funny mysterious things of OS/2 that are not well documented ;-).
Some discussions on the IBMPC conference disk about how the JAVA VM does that have inspired me that this can be done quite easily.