Consultor Eletrônico



Kbase P43248: prowin32 GPF's when using API calls to Windows DLLs
Autor   Progress Software Corporation - Progress
Acesso   Público
Publicação   2/26/2007
Status: Unverified

SYMPTOM(s):

Prowin32 GPF's when calling Windows API's such as EnumPrintersA()

Occurs intermittently.

Other random errors occur.

**Unable to realize LITERAL widget (4025)

error 5890 for various OCX object properties/methods

Error occurred while accessing component property/method: <property or method name>.
<COM message>
Error code: <Program name> (5890)

CAUSE:

This is not a Progress issue. What happens is the following:
The Windows API's pass data back in Memptrs. Because these particular API's do not size the memory themselves, there are typically two calls required to make effective use of the API; one to determine what the size of the MEMPTR receiving the result should be, and one to actually retrieve the data.

In specific cases memory corruption can occur using this technique, resulting in random application errors and/or failures.
This is because of the way the API's work. In short, they can actually access the value of a variable used to capture an output parameter of the API using memory pointers, so the OUTPUT parameter can effectively also be treated as an INPUT parameter from the API's point of view.
This happens with the OUTPUT parameter used to return the required size of the memory (pcbNeeded in the example below). Because the API actually reads from this variable to perform a memory bounds check, unexpected behavior will occur if the variable mismatches the size of the MEMPTR passed to retrieve data (pPrinterEnum in the example below) and that MEMPTR has a size greater than 0. The bounds check may pass where it should not, causing a memory violation that may or may not be noticed immediately by the OS.

FIX:

The correct way to call the API's is to pass an uninitialized memptr to the first "dummy" call. The API will recognize this as a signal to return only the size of the memory required and not perform any further actions.

For example:
/* Test code */
DEFINE VARIABLE pPrinterEnum AS MEMPTR NO-UNDO.
DEFINE VARIABLE pcbNeeded AS INTEGER NO-UNDO.
DEFINE VARIABLE pcReturned AS INTEGER NO-UNDO.
DEFINE VARIABLE RetValue AS INTEGER NO-UNDO.

/* Dummy call to get required memptr size, pass unallocated memptr */
SET-SIZE(pPrinterEnum) = 0.
RUN EnumPrintersA(INPUT 2, /* = PRINTER_ENUM_LOCAL */
INPUT "",
INPUT 2,
INPUT GET-POINTER-VALUE(pPrinterEnum),
INPUT GET-SIZE(pPrinterEnum),
OUTPUT pcbNeeded,
OUTPUT pcReturned,
OUTPUT RetValue).

/* Set size of memptr to number of bytes needed (pcbNeeded) and
perform the "real" call */
SET-SIZE(pPrinterEnum) = pcbNeeded.
RUN EnumPrintersA(INPUT 2, /* = PRINTER_ENUM_LOCAL */
INPUT "",
INPUT 2,
INPUT GET-POINTER-VALUE(pPrinterEnum),
INPUT GET-SIZE(pPrinterEnum),
OUTPUT pcbNeeded,
OUTPUT pcReturned,
OUTPUT RetValue).

/* Process returned data here */

/* Deallocate memory after use */
SET-SIZE(pPrinterEnum) = 0.