Dangers with morphing

From EDM2
Jump to: navigation, search

I cannot reproduce the problem any longer - to prevent false information, the article has been withdrawn (for the time being).

Dangers with morphing

In this article I want to warn about reversing morphing before the exit of your application.

Morphing is an undocumented feature to let your program create PM windows as well as have a console. It is done by changing the application type in the process info block. For more information about morphing, look at Doodle's article.

Now look at the following program:

// should be linked with /PM:VIO or WINDOWCOMPAT option

#define INCL_WIN
#define INCL_DOS
#include <os2emx.h>
#include <stdio.h>

int main(void) {
    HAB hab;
    HMQ hmq;
    HPS hps;
    PPIB pib;
    POINTL ptl = { 100, 100 };

    // morph application to PM
    DosGetInfoBlocks(NULL, &pib);
    pib->pib_ultype = 3;

    hab = WinInitialize(0);
    hmq = WinCreateMsgQueue(hab, 0);

    // we forget to free this presentation space - with consequences ...
    hps = WinGetScreenPS(HWND_DESKTOP);

    // console and screen output
    GpiBox(hps, DRO_OUTLINE, &ptl, 50, 50);
    printf("Test!\n");

    WinDestroyMsgQueue(hmq);
    WinTerminate(hab);


    // these two lines prevent the PM from freeing resources!
    // thus creating zombie windows outside their parents!!!

    DosGetInfoBlocks(NULL, &pib);
    pib->pib_ultype = 2;


    return 0;
}

We declare some variables, morph the program to PM, and create a message queue. Now we do not only put text on the console, but also draw the outline of a small box on the screen. Finally we give back the PM handles, reverse the morphing and exit the program.

Everything seems to have worked as expected ... until ...

http://www.projectalpha.org/ReversedMorph.png

Message queues are created on a per-thread basis. So with WinDestroyMsgQueue not everything can be cleaned up by PM! It is delayed until the terminating of our program. But only for programs with application type 3 (PM)! By reversing the morphing we interfere with this safety process. Now every small error in our application has bad consequences.

But you can easily top that:

void APIENTRY ExitHandler(ULONG ulTerm) {
    DosGetInfoBlocks(NULL, &pib);
    pib->pib_ultype = 2;
}

int main(void) {
    ...
    DosExitList(EXLST_ADD, ExitHandler);
    ...
}

Now you are doomed! Every small crash or program abort will cause the exit handler to set back the application type. When the exit handler of PM is called after yours, there is a high probability that any handles are still open.

So, please do not reverse the morphing of your application to PM. Once PM - always PM!

PS If you experience any problems by doing so, please tell me and put your comments here.