home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Simtel MSDOS 1992 September
/
Simtel20_Sept92.cdr
/
msdos
/
ddjmag
/
ddj8911.arc
/
SCHULMAN.LST
< prev
next >
Wrap
File List
|
1989-10-04
|
16KB
|
661 lines
_LINKING WHILE THE PROGRAM IS RUNNING_
by Andrew Schulman
[LISTING ONE]
/*
calldll1.c -- run-time dynamic linking to Estes's ALIAS.DLL
cl -Lp calldll1.c
*/
#include <stdlib.h>
#include <stdio.h>
#define INCL_DOSMODULEMGR
#include "os2.h"
#define NIL ((void far *) 0)
void fail(char *msg) { puts(msg); exit(1); }
void main()
{
unsigned (far pascal *addsyn)(char far *msg);
unsigned (far pascal *listsyn)(void);
unsigned alias;
if (DosLoadModule(NIL, 0, "ALIAS", &alias) != 0)
fail("can't find ALIAS");
DosGetProcAddr(alias, "ADDSYN", &addsyn);
DosGetProcAddr(alias, "LIST_SYN", &listsyn);
(*addsyn)("ep \\os2\\eps\\epsilon");
(*listsyn)();
DosFreeModule(alias);
}
[LISTING TWO]
MODULE calldll;
(* JPI TopSpeed Modula-2 for OS/2 *)
(* run-time dynamic linking to Estes's ALIAS.DLL *)
FROM InOut IMPORT WriteString, WriteLn;
IMPORT Dos;
PROCEDURE fail (msg : ARRAY OF CHAR);
BEGIN
WriteString(msg); WriteLn; HALT;
END fail;
VAR
addsyn : PROCEDURE (ADDRESS) : CARDINAL;
listsyn : PROCEDURE () : CARDINAL;
alias : CARDINAL;
ret : CARDINAL; (* ignored retval *)
BEGIN
IF (Dos.LoadModule(NIL, 0, "ALIAS", alias) # 0) THEN
fail("can't find ALIAS");
END;
ret := Dos.GetProcAddr(alias, "ADDSYN", PROC(addsyn));
ret := Dos.GetProcAddr(alias, "LIST_SYN", PROC(listsyn));
(* In the next line, the string _must_ be passed as an
ADDRESS, not as an ARRAY OF CHAR: Modula-2 passes open
arrays as _six_ bytes on the stack -- two bytes for the
length, followed by the address of the array itself --
but OS/2 DLL's generally expect only the string itself
(zero-terminated of course). *)
ret := addsyn(ADR("ep \os2\eps\epsilon"));
ret := listsyn();
ret := Dos.FreeModule(alias);
END calldll.
[LISTING THREE]
; calldll.lsp
; OS2XLISP run-time dynamic linking to Estes's ALIAS.DLL
(define alias (loadmodule "ALIAS"))
(if (zerop alias)
(error "can't find ALIAS"))
(call (getprocaddr alias "ADDSYN") "ep \os2\eps\epsilon")
(call (getprocaddr alias "LIST_SYN"))
(freemodule alias)
[LISTING FOUR]
/*
proc1.c -- implements higher-level access to OS/2 run-time dynlinks
cl -c -Lp proc1.c
*/
#define INCL_DOSMODULEMGR
#include "os2.h"
#include "procaddr.h"
#define NIL ((void far *) 0)
WORD loadmodule(ASCIIZ name)
{
WORD h;
return DosLoadModule(NIL, 0, name, (PHMODULE) &h) ? 0 : h;
}
ULONG getprocaddr(WORD module, ASCIIZ name)
{
ULONG pf;
return DosGetProcAddr(module, name, (PPFN) &pf) ? 0 : pf;
}
ULONG procaddr(ASCIIZ module, ASCIIZ name)
{
return getprocaddr(loadmodule(module), name);
}
BOOL freemodule(WORD h)
{
return (! DosFreeModule(h));
}
[LISTING FIVE]
/*
procaddr.h -- higher-level access to OS/2 run-time dynlinks
*/
typedef unsigned WORD;
typedef unsigned short BOOL;
typedef unsigned long ULONG;
typedef char *ASCIIZ;
WORD loadmodule(ASCIIZ name);
ULONG getprocaddr(WORD module, ASCIIZ name);
ULONG procaddr(ASCIIZ module, ASCIIZ name);
BOOL freemodule(WORD handle);
[LISTING SIX]
/*
calldll2.c -- run-time dynamic linking to CRTLIB.DLL, using PROC1.C
requires MSC 5.1 CRTEXE.OBJ
cl -AL -c calldll2.c proc1.c
link /nod/noi crtexe.obj calldll2 proc1,calldll2,,crtlib.lib os2;
output:
Hello from calldll2
Hello again, using new ANSI C style
_printf lives at 03EF:1098
printf returned 27
Goodbye
*/
#include "procaddr.h"
typedef ULONG (far cdecl *CFN)();
main(int argc, char *argv[])
{
WORD (far cdecl *printf)();
WORD crtlib;
WORD ret;
crtlib = loadmodule("CRTLIB");
printf = (CFN) getprocaddr(crtlib, "_printf");
(*printf)("Hello from %s\n", argv[0]); /* 1 */
printf("Hello again, using new ANSI C style\n"); /* 2 */
ret = printf("_printf lives at %Fp\n", printf); /* 3 */
printf("printf returned %d\n", ret); /* 4 */
((CFN) getprocaddr(loadmodule("CRTLIB"),"_printf"))("Goodbye"); /* 5 */
freemodule(crtlib);
}
[LISTING SEVEN]
/*
calldll3.c -- run-time dynamic linking from the command-line
requires MSC 5.1 CRTEXE.OBJ, uses proc2.obj or procaddr.dll
doesn't include "os.h"
to use proc2.obj:
cl -AL -c -Gs2 -Ox -W2 calldll3.c proc2.c
link /nod/noi crtexe.obj calldll3 proc2,calldll3.exe,,crtlib.lib os2.lib;
to use procaddr.dll (IMPLIB procaddr.lib):
cl -AL -c -Gs2 -Ox -W2 calldll3.c
link /nod/noi crtexe.obj calldll3,calldll3,,procaddr.lib crtlib.lib os2.lib;
to run:
calldll3 <module name> <function name or ordinal number> [args...] [%mask]
examples:
calldll3 VIOCALLS VIOWRTTTY "hello world" 5 0
calldll3 doscalls DosMkDir \foobar 0L
calldll3 doscalls DosRmDir \foobar 0L
calldll3 DOSCALLS DosBeep 2000 300
calldll3 DOSCALLS 50 2000 300 ; DosBeep
calldll3 CRTLIB _printf "goodbye world: %lu" 666L " [%d]"
calldll3 CRTLIB ACOS -1.0 %.15f
calldll3 CRTLIB SQRT -1.0 %f
calldll3 CRTLIB _toupper 'b' %c
calldll3 PROCADDR LOADMODULE PROCADDR %X
*/
#include <mt\stdlib.h>
#include <mt\stdio.h>
#include <mt\string.h>
#include "local.h"
#include "proc2.h"
typedef enum { typ_string, typ_byte, typ_word, typ_long, typ_float } TYPE;
TYPE NEAR type(char *arg);
TYPE NEAR retval_type(char *s);
VOID fail(char *msg) { puts(msg); exit(1); }
/*
push() : see Cortesi, Programmer's Essential OS/2 Handbook, pp.136-137
*/
VOID NEAR PASCAL push() { }
extern WORD pop(void);
#define PUSH_ARG(arg) \
{ \
switch (type(arg)) \
{ \
case typ_string: push(arg); c += 2; break; \
case typ_byte: push(arg[1]); c += 1; break; \
case typ_word: push(atoi(arg)); c += 1; break; \
case typ_long: push(atol(arg)); c += 2; break; \
case typ_float: push(atof(arg)); c += 4; break; \
} \
}
#define SYNTAX_MSG \
"syntax: calldll3 <module name> <func name or ord#> [args...] [%mask]"
main(int argc, char *argv[])
{
FN f;
TYPE retval_typ = typ_word;
char *mask = "%u";
WORD module;
BOOL is_cdecl;
int i, c;
if (argc < 3)
fail(SYNTAX_MSG);
/* handle optional printf mask */
if (strchr(argv[argc-1], '%'))
retval_typ = retval_type(mask = argv[--argc]);
if ((module = loadmodule(argv[1])) == 0)
fail("can't load module");
/* pass ASCIIZ string or ordinal number */
f = getprocaddr(module, isdigit(argv[2][0]) ? atol(argv[2]) : argv[2]);
if (! f)
fail("can't get function");
is_cdecl = ! (strcmp(strupr(argv[1]), "CRTLIB"));
/* push in reverse order for cdecl */
if (is_cdecl)
{
for (i=argc-1, c=0; i>=3; i--)
PUSH_ARG(argv[i]);
}
else
{
for (i=3; i<argc; i++)
PUSH_ARG(argv[i]);
}
/* args are on the stack : call (*f)() and print retval */
switch (retval_typ)
{
case typ_string: printf(mask, ((STRFN) f)()); break;
case typ_byte: printf(mask, ((BYTEFN) f)()); break;
case typ_word: printf(mask, f()); break;
case typ_long: printf(mask, ((LONGFN) f)()); break;
case typ_float: printf(mask, ((FLOATFN) f)()); break;
}
if (is_cdecl)
for (i=0; i<c; i++)
pop();
freemodule(module);
return 0;
}
/*
type() uses some dumb rules to determine the type of an argument:
if first character of arg is a digit or '-'
and if arg contains '.' then it's a floating-point number
else if last character is an 'L' then it's a long
else it's a unsigned word
else if first character is an apostrophe
it's a single-byte character
otherwise
it's a string
*/
TYPE NEAR type(char *arg)
{
if (isdigit(arg[0]) || (arg[0] == '-' && isdigit(arg[1])))
{
char *p = arg;
while (*p)
if (*p++ == '.')
return typ_float;
return (*--p == 'L') ? typ_long : typ_word;
}
else
return (arg[0] == '\'') ? typ_byte : typ_string;
}
/*
retval_type() uses a printf() mask (e.g., %s or %lX) to determine
type of return value
*/
TYPE NEAR retval_type(char *s)
{
while (*s)
{
switch (*s)
{
case 's' : return typ_string; break;
case 'c' : return typ_byte; break;
case 'p' : case 'l' : case 'I' : case 'O' : case 'U' :
return typ_long; break;
case 'e' : case 'E' : case 'f' : case 'g' : case 'G' :
return typ_float; break;
}
s++;
}
/* still here */
return typ_word;
}
[LISTING EIGHT]
; pop.asm
DOSSEG
.MODEL large
.CODE pop_text
PUBLIC _pop, _sp
_pop proc far
; save away far return address
pop cx
pop bx
; pop word off stack and return it in AX
pop ax
; push far return address back on stack
push bx
push cx
ret
_pop endp
; useful for testing
_sp proc far
mov ax,sp
ret
_sp endp
end
[LISTING NINE]
/*
proc2.c
to make procaddr.dll:
cl -Alfu -c -Gs2 -Ox -W2 -DDLL proc2.c
link /nod/noi proc2,procaddr.dll,,llibcdll.lib os2,procaddr.def;
implib procaddr.lib procaddr.def
copy procaddr.dll \os2\dll
*/
#include <string.h>
#ifdef DLL
int _acrtused = 0;
#endif
#define INCL_DOS
#include "os2.h"
#include "local.h"
#include "proc2.h"
typedef struct {
char *name;
USHORT (APIENTRY *f)();
} DOSCALLS;
/*
include table generated from BSEDOS.H with AWK script DOSCALLS.AWK
table looks like:
LOCAL DOSCALLS NEAR dos[] = {
"", 0,
...
"DosGetHugeShift", DosGetHugeShift,
"DosGetInfoSeg", DosGetInfoSeg,
...
} ;
DOSCALLS.C also contains #define NUM_DOSCALLS
*/
#include "doscalls.c"
LOCAL FN NEAR getdoscall(ASCIIZ name);
LOCAL USHORT NEAR doscalls = 0;
WORD pascal loadmodule(ASCIIZ name)
{
WORD h;
return DosLoadModule((void far *) 0, 0, name, (PHMODULE) &h) ? 0 : h;
}
/*
if name is actually a four-byte ordinal number, use it as is
otherwise if module is not DOSCALLS, use it as is
otherwise if module is DOSCALLS, get ordinal number and use it instead
*/
FN pascal getprocaddr(WORD module, ASCIIZ proc)
{
FN f;
if (! doscalls) doscalls = loadmodule("DOSCALLS");
if ((module == doscalls) && FP_SEG(proc))
return getdoscall(proc);
else
return DosGetProcAddr(module, proc, (PPFN) &f) ? 0 : f;
}
FN pascal procaddr(ASCIIZ module, ASCIIZ proc)
{
return getprocaddr(loadmodule(module), proc);
}
BOOL pascal freemodule(WORD h)
{
return (! DosFreeModule(h));
}
/*
do binary search of table, looking for name, returning function ptr
*/
LOCAL FN NEAR getdoscall(ASCIIZ name)
{
signed cmp, mid;
signed base = 1, top = NUM_DOSCALLS+1;
name = strupr(name);
for (;;)
{
mid = (base + top) / 2;
cmp = strcmp(name, strupr(dos[mid].name));
if (cmp == 0) return (FN) dos[mid].f;
else if (mid == base) return 0;
else if (cmp < 0) top = mid;
else if (cmp > 0) base = mid;
}
}
[LISTING TEN]
/*
proc2.h
*/
extern WORD pascal loadmodule(ASCIIZ name);
extern FN pascal getprocaddr(WORD module, ASCIIZ proc);
extern FN pascal procaddr(ASCIIZ module, ASCIIZ proc);
extern BOOL pascal freemodule(WORD handle);
[LISTING ELEVEN]
# doscalls.awk
# creates doscalls.c from bsedos.h
# doscalls.c is #included by proc2.c
# C>sort -b +2 \os2\inc\bsedos.h | awk -f doscalls.awk > doscalls.c
# bsedos.h contains prototypes such as:
# USHORT APIENTRY DosCreateThread(PFNTHREAD, PTID, PBYTE);
# doscalls.awk turns these into string name/function ptr pairs:
# "DosCreateThread", DosCreateThread,
BEGIN { init() }
END { fini() }
$2 ~ /APIENTRY/ && $3 ~ /Dos/ { doscall($3) }
function init() {
print "/* doscalls.c */"
print "LOCAL DOSCALLS NEAR dos[] = {"
print "\"\",\t0,"
}
function fini() {
print "} ; "
print "#define NUM_DOSCALLS\t", num_doscalls
}
function doscall(s) {
gsub(/\(/, " ", s) # replace open paren with space
split(s, arr) # tokenize
print "\"" arr[1] "\", " arr[1] "," # print with and without quotes
num_doscalls++
}
[LISTING TWELVE]
; procaddr.def
LIBRARY PROCADDR
DESCRIPTION 'Run-Time Dynamic Linking'
DATA SINGLE SHARED
PROTMODE
EXPORTS
LOADMODULE
GETPROCADDR
PROCADDR
FREEMODULE
[LISTING THIRTEEN]
/*
local.h -- miscellaneous definitions
*/
typedef unsigned short WORD;
typedef unsigned short BOOL;
typedef char far *ASCIIZ;
typedef unsigned long ULONG;
typedef double FLOAT;
typedef WORD (far *FN)();
typedef ASCIIZ (far *STRFN)();
typedef char (far *BYTEFN)();
typedef WORD (far *WORDFN)();
typedef ULONG (far *LONGFN)();
typedef FLOAT (far pascal *FLOATFN)();
#define FP_SEG(p) ((WORD) ((ULONG) (p) >> 16))
#define FP_OFF(p) ((WORD) (p))
#define isdigit(c) ((c) >= '0' && (c) <= '9')
#ifndef NEAR
#define NEAR near
#define PASCAL pascal
#define VOID void
#endif
#define LOCAL static
Example 1: Expressive functions
WORD alias = loadmodule("ALIAS");
PFN listsyn = (PFN) getprocaddr(module, "LIST_SYN");
if (listsyn)
(*listsyn)();
freemodule(alias);
or:
PFN listsyn;
if (listsyn = (PFN) procaddr("ALIAS", "LIST_SYN"))
(*listsyn)();
Example 2: Assembly language equivalent
PUSH "Goodbye"
PUSH "_printf"
PUSH "CRTLIB"
CALL loadmodule
; loadmodule consumed "CRTLIB"
; and produced handle to crtlib
CALL getprocaddr
; getprocaddr consumed crtlib-handle and "_printf"
; and produced pointer to printf on top of stack
; "Goodbye" is still on stack
CALL [top of stack]
POP retval from _printf
Example 3: Legal calls to the interpreter
calldll viocalls VIOWRTTTY "hello world" 11 0
calldll doscalls DosBeep 2000 300
calldll doscalls 50 2000 300 ; DOSBEEP
calldll doscalls DosMkDir \foobar 0L
calldll doscalls DosRmDir \foobar 0L
calldll pmwin WINQUERYACTIVEWINDOW 1L 0 %lu
calldll crtlib _printf "goodbye world: %lu" 666L
calldll crtlib SQRT -1.0 %f
calldll crtlib _toupper 'b' %c
calldll jpilib FIO$Exists 12 CALLDLL.EXE
Example 4: Arguments already on the stack
switch (retval_typ)
{
case typ_string: printf(mask, ((STRFN) f)()); break;
case typ_word: printf(mask, f()); break;
...
}
Example 5: Associating ASCIIZ names with function pointers
LOCAL DOSCALLS NEAR dos[] = {
"", 0,
...
"DosGetProcAddr", DosGetProcAddr,
"DosGetPrty", DosGetPrty,
...
} ;