Back to the main page

Table Of Contents

THIS IS AN OLD FIX ...
AN UPDATED VERSION CAN BE FOUND BY FOLLOWING THE LINK ON THE MAIN PAGE

Intro

A while ago I bought a tablet for the kids (at a local ALDI store).  Although I bought it at the ALDI, it was actually WACOM volito.  Like with most pieces of hardware, you get a bunch of CD-ROM's with the product.  Because I thought it would be kind of nice that the kids could learn to draw a bit, I tried to install the "Corel Painter Classic" that came with the tablet.

That's where the journey started... After installing this free piece of software I could not get it to run, because the software complained about the following

Initialization Error

Not enough free memory to run Painter.

You have to know I was trying to run this on a computer with 2GB of internal memory and 2GB of page file configured, so I knew this had to be a software bug.

The first thing you try to do is look for updates.  Of course this software had no updates, even worse, it was not supported anymore by Corel and WACOM didn't seem to be much help either.

Still, knowing that I couldn't be the only one with this problem, I decided to google my way down until I found a solution!  The only things I found were...

So soon I came to the conclusion that I WACOM didn't bother doing anything about it and Corel was not helping either (how difficult could such a fix be?).

Therefore I decided I would be ...

Fixing it myself

It didn't take too long to figure out that the only way the application could report "not enough memory" would be by a logic error in their code.  Of course there is nothing I can do to fix the logic error, but one of the workarounds was to limit the pagefile size to 500Mb.

After some more searching I found the problem was not related with the actual pagefile size (see: Corel Website), but with the pagefile to RAM ratio (should be less than 3).  I think the developers were trying to prevent people running this software on hardware that was not designed for it.

Anyway, instead of worrying about the ratio (and whatever possible bugs would happen when the memory * 3 would overflow their variables), I decided to go the hard and easy way ... why not let the application think that I only have 500Mb of pagefile.

Once I figured that out, the rest was easy

  1. Find out which call they use to determine the pagefile size

  2. Intercept that call

  3. Change the return value

If you have ever done DLL call interception then this is a piece of cake ... and by using some tracing utilities I was able to find the call I needed to intercept (GlobalMemoryStatus in kernel32.dll)... more about that below

Therefore I wrote the couple of lines needed to for my interception API (I used MadCodeHook's API instead of messing with the assembler myself), and also wrote a small program that would launch the PClassic.exe (the main program) with the DLL injected into it.

And 1 hour later ... Painter Classic was running !

Below you can download the executable and the dll, and I also wrote a bit of information on the installation.... And because some of you really want to see the source code so you can build it yourself, I also added that at the bottom of the page.  The code assumes you use __stdcall calling conventions.
 

Download

Warning: A more recent fix has been made available ... check the main page under development projects

Click here to download a zip file that contains 2 files.

Instructions

First download the zip file and unpack it.  The zip file contains 2 files : intercept.dll and NewPClassic.exe.

Both files should be copied to your Painter installation directory.  This is usually C:\Program Files\Corel\Painter Classic.  You can now launch the program by double clicking NewPClassic.exe

After you did that, you probably want to modify the shortcut used to start Painter Classic and let it point to NewPClassic.exe

That should be it.

Source Code dll

// ***************************************************************
// Intercept.dll version: 1.0 · date: 2005-02-18
// Author: Geert De Peuter
// Email: dev at depeuter. (org)
// -------------------------------------------------------------
// this dll intercepts GlobalMemoryStatus in kernel32.dll
// ***************************************************************

#include <windows.h>
#include "madCHook.h"

// ***************************************************************

void ( *ori_GlobalMemoryStatus ) (LPMEMORYSTATUS lpBuffer);

// ***************************************************************

static void catch_GlobalMemoryStatus(LPMEMORYSTATUS lpBuffer)
{
    // Call the original GlobalMemoryStatus
    ori_GlobalMemoryStatus(lpBuffer);

    // Adjust the reported values for the pagefile
    if (lpBuffer -> dwTotalPageFile > 500000000)
    lpBuffer -> dwTotalPageFile = 500000000;
    if (lpBuffer -> dwAvailPageFile > 500000000)
    lpBuffer -> dwAvailPageFile = 500000000;
}

// ***************************************************************

BOOL WINAPI DllMain(HANDLE hModule, DWORD fdwReason, LPVOID lpReserved)
{
    if (fdwReason == DLL_PROCESS_ATTACH) {
    // InitializeMadCHook is needed only if you're using 
    // the static madCHook.lib
    InitializeMadCHook();

    HookAPI("kernel32.dll", "GlobalMemoryStatus", 
            catch_GlobalMemoryStatus, 
            (PVOID*) &ori_GlobalMemoryStatus);

    } else if (fdwReason == DLL_PROCESS_DETACH) {
        // FinalizeMadCHook is needed only if you're using 
        // the static madCHook.lib
        FinalizeMadCHook();
    }

    return true;
}

Source code exe

// ***************************************************************
// NewPClassic.exe version: 1.0 · date: 2005-02-18
// Author: Geert De Peuter
// Email: dev at depeuter. (org)
// -------------------------------------------------------------
// this program starts PClassic while injecting the intercept.dll
// ***************************************************************

#include <windows.h>
#include <stdio.h>
#include "madCHook.h"

DWORD main(int argc, char *argv[])
{
    DWORD rc = 0;
    STARTUPINFO si;
    PROCESS_INFORMATION pi;

    // Initialize si and pi
    ZeroMemory( &si, sizeof(si) );
    si.cb = sizeof(si);
    ZeroMemory( &pi, sizeof(pi) );

    // Start the child process. 
    if( !CreateProcessEx( NULL, // No module name (use command line). 
                        "PClassic.exe", // Command line. 
                        NULL, // Process handle not inheritable. 
                        NULL, // Thread handle not inheritable. 
                        FALSE, // Set handle inheritance to FALSE. 
                        0, // No creation flags. 
                        NULL, // Use parent's environment block. 
                        NULL, // Use parent's starting directory. 
                        &si, // Pointer to STARTUPINFO structure.
                        &pi // Pointer to PROCESS_INFORMATION structure.
                        ,"intercept.dll"
                        ))
    {
        printf( "Unable to start the process 'PClassic.exe'\n"
            " Did you copy this file in the Painter Classic installation"
            " directory?\n"
            " Did you copy the file 'intercept.dll' to the Painter "
            " Classic installation directory?\n\n"
            " This should be the only reason why this is not working :\n"
            , GetLastError());

        printf( "\n\nPress Enter >>>");

        fflush(stdout);
        getc(stdin);
        return(2);
    }

    // Wait until child process exits.
    //WaitForSingleObject( pi.hProcess, INFINITE );

    // Get the exit code of the process
    //GetExitCodeProcess(pi.hProcess, &rc); 

    // Close process and thread handles. 
    CloseHandle( pi.hProcess );
    CloseHandle( pi.hThread );

    // Return the exit code of the child process
    return(rc);
}

Finding The Call "GlobalMemoryStatus"

There are plenty of API spying utilities available on the web (google for APISpy32 for example).  However, if you feel comfortable with debuggers, you can install the debugging tools for windows (download from MS website).

After installing this, do the following ...

  1. Start the debugger

  2. File -> Open Executable

  3. Point to PClassic.exe

  4. You will see the executable is loaded and you see a list of dll's (modules)

  5. At the command prompt (bottom of the screen of the debugger), type the following

  1. Type "g" or F5 or Debug -> go

  2. The executable will start running

  3. Find the window that displays the error message from Painter Classic and press the OK button (you may have to minimize all windows to find it)

  4. You should now have a PClassic.lgv file in the output directory (as mentioned in item 5)

Now start logviewer.exe (you will find it in the directory where you installed the debug utilities) and open the lgv file.

In the logviewer, select view -> show first level calls only ... and you will see the last "meaningful" call (before showing the messagebox) is GlobalMemoryStatus.

Want a really cool fix (only for the brave) ?

One of the things I've always found fascinating was patching binary files.  So I did one for CPainter.exe. 

The fix mentioned above will work for all versions of Painter because it just intercepts each call the program makes for memory statistics and fakes the return values.  This fix will actually modify the binary and change the behaviour so the binary will not show the "not enough memory" Messagebox, but simply continues, thinking all is well.

So, if you don't want to take any risks, then download the fix above.  If you are not afraid to try something "new", you can use the following procedure to fix the problem

The following explanation is done for ASI32_12.dll version 12.1 (see file properties in explorer). The byte sequence to modify for version 12.2 can be found lower on the page.

  1. Make a copy of ASI32_12.dll (can be found in the Corel Painter subdirectory) just in case this doesn't work for you.

  2. Open "ASI32_12.dll" in a hex editor (ultraedit for example ...)

  3. Look for the following byte sequence : "c7 45 fc 00 00 00 00 8b 55 fc 2b 15 a8 29 10 10 89 55"

  4. Verify that this sequence only happens once in the whole file

If you were not able to find the sequence at all or you found it multiple times, then see if one of the codes lower in this paragraph will work for you.  Probably you have a different version of the dll than used in this explanation.

  1. Replace the sequence with "c7 45 fc 00 00 00 20 8b 55 fc 2b 15 a8 29 10 10 89 55"  (note that only one character has changed) - This change tell PClassic.exe that you have 512Mb of memory available on the machine instead of dying with not enough memory...

  2. Save the new ASI32_12.dll

  3. Start PClassic.exe

If you have the ASI32_12.dll (version 12.2), you can try the following
Find: BC 0D 10 8B 44 24 10 85 C0 75 16 8B 4C 24 0C F7 C1 00 00 00 80 75 06 3B 4C 24 08 7C 1C 33
Replace: BC 0D 10 8B 44 24 10 85 C0 90 90 8B 4C 24 0C F7 C1 00 00 00 80 90 90 3B 4C 24 08 EB 1C 33

Just for the curious, you could run both fixes together, but there is no reason to.  If you intercept the call to the GlobalMemoryStatus function, the "2" change you made will never be ran.

Looking for the generic Metacreations fix?

If you are experiencing a similar problem with Poser or other versions of Painter Classic (like the Japanese version or Painter 5), you might want to look at a generic fix I created for this problem.

You will find more info about this fix here.

 

Back to the main page