Kbase P23531: How to call a DLL routine with a variable number of parameters ?
Autor |
  Progress Software Corporation - Progress |
Acesso |
  Público |
Publicação |
  16/09/2008 |
|
Status: Verified
GOAL:
How to call a DLL routine with a variable number of parameters ?
FIX:
Some DLL routines accept a variable number of parameters.
However, in Progress 4GL, the hooks to these routines are defined as a PROCEDURE ... EXTERNAL .... , and follow the rules for the RUN statement.
Since the RUN statement does not allow for a variable number of parameters to be passed, this functionality can not be directly used from the 4GL.
One way to work around this using only 4GL, is by writing a wrapping procedure that is compiled on the fly, in which arguments passed via the RUN statement combined with preprocessor directives will determine how many parameters will actually be used -> If the argument is passed, the parameter will be defined. Otherwise it will be ignored.
An example wrapping procedure, which calls Windows' GetCurrentDirectoryA function in KERNEL32.DLL:
/* wrapgcda.p
Wrapper to GetCurrentDirectoryA with one non-required parameter
(the return-value in this case) */
/* set up 4GL entry points */
DEFINE INPUT PARAMETER intBufferSize AS INTEGER NO-UNDO.
DEFINE INPUT-OUTPUT PARAMETER ptrToString AS MEMPTR NO-UNDO.
&IF {1} &THEN DEFINE OUTPUT PARAMETER intResult AS INTEGER NO-UNDO. &ENDIF
/* set up DLL entry points */
PROCEDURE GetCurrentDirectoryA EXTERNAL "KERNEL32.DLL":
DEFINE INPUT PARAMETER intBufferSize AS LONG.
DEFINE INPUT-OUTPUT PARAMETER ptrToString AS MEMPTR.
&IF {1} &THEN DEFINE RETURN PARAMETER param3 AS SHORT. &ENDIF
END.
/* Run the DLL routine with either 2 or 3 parameters */
RUN GetCurrentDirectoryA (
INPUT intBufferSize
,INPUT-OUTPUT ptrToString
&IF {1} &THEN ,OUTPUT intResult &ENDIF
).
And an example procedure that calls this wrapper with different numbers of parameters:
/* wrapgcda.p
Wrapper to GetCurrentDirectoryA with one non-required parameter */
&SCOPED-DEFINE Buffersize 256
DEFINE VARIABLE chrDirectoryName AS CHARACTER NO-UNDO FORMAT "X(256)".
DEFINE VARIABLE intResult AS INTEGER NO-UNDO.
DEFINE VARIABLE ptrToString AS MEMPTR NO-UNDO.
/* 2 params */
SET-SIZE(ptrToString) = {&Buffersize}.
RUN wrapGcdA.p (INPUT {&Buffersize},
INPUT-OUTPUT ptrToString).
MESSAGE "Current dir: " GET-STRING(ptrToString,1) skip(0)
"intresult returned: " intResult SKIP(0)
"intresult should be 0 because parameter wasn't used"
VIEW-AS ALERT-BOX INFO BUTTONS OK.
/* 3 params, pass 1 argument to signify use of 1 variable parameter... */
RUN wrapGcdA.p (INPUT {&Buffersize},
INPUT-OUTPUT ptrToString,
OUTPUT intResult)
1.
MESS.AGE "Current dir: " GET-STRING(ptrToString,1) skip(0)
"intresult returned: " intResult SKIP(0)
"intresult should be length of returned string because parameter was used"
VIEW-AS ALERT-BOX INFO BUTTONS OK.
SET-SIZE(ptrToString) = 0.
Using this technique, it's possible to pass up to 9 non-required parameters/sets of parameters to a DLL routine.
If multiple non-required parameters are used, use argument 1 for the first, argument 2 for the second etc..