home *** CD-ROM | disk | FTP | other *** search
/ Simtel MSDOS - Coast to Coast / simteldosarchivecoasttocoast2.iso / ddjmag / ddj8911.zip / SCHULMAN.LST < prev    next >
File List  |  1989-10-04  |  16KB  |  661 lines

  1. _LINKING WHILE THE PROGRAM IS RUNNING_
  2. by Andrew Schulman
  3.  
  4. [LISTING ONE]
  5.  
  6. /*
  7. calldll1.c -- run-time dynamic linking to Estes's ALIAS.DLL
  8. cl -Lp calldll1.c
  9. */
  10.  
  11. #include <stdlib.h>
  12. #include <stdio.h>
  13. #define INCL_DOSMODULEMGR
  14. #include "os2.h"
  15.  
  16. #define NIL             ((void far *) 0)
  17.  
  18. void fail(char *msg)    { puts(msg); exit(1); }
  19.  
  20. void main()
  21. {
  22.     unsigned (far pascal *addsyn)(char far *msg);
  23.     unsigned (far pascal *listsyn)(void);
  24.     unsigned alias;
  25.  
  26.     if (DosLoadModule(NIL, 0, "ALIAS", &alias) != 0)
  27.         fail("can't find ALIAS");
  28.     DosGetProcAddr(alias, "ADDSYN", &addsyn);
  29.     DosGetProcAddr(alias, "LIST_SYN", &listsyn);
  30.     (*addsyn)("ep \\os2\\eps\\epsilon");
  31.     (*listsyn)();
  32.     DosFreeModule(alias);
  33. }
  34.  
  35.  
  36.  
  37. [LISTING TWO]
  38.  
  39. MODULE calldll;
  40. (* JPI TopSpeed Modula-2 for OS/2 *)
  41. (* run-time dynamic linking to Estes's ALIAS.DLL *)
  42.  
  43. FROM InOut IMPORT WriteString, WriteLn;
  44. IMPORT Dos;
  45.  
  46. PROCEDURE fail (msg : ARRAY OF CHAR);
  47. BEGIN
  48.     WriteString(msg); WriteLn; HALT;
  49. END fail;
  50.  
  51. VAR
  52.     addsyn : PROCEDURE (ADDRESS) : CARDINAL;
  53.     listsyn : PROCEDURE () : CARDINAL;
  54.     alias : CARDINAL;
  55.     ret : CARDINAL;     (* ignored retval *)
  56.  
  57. BEGIN
  58.     IF (Dos.LoadModule(NIL, 0, "ALIAS", alias) # 0) THEN
  59.         fail("can't find ALIAS");
  60.     END;
  61.     ret := Dos.GetProcAddr(alias, "ADDSYN", PROC(addsyn));
  62.     ret := Dos.GetProcAddr(alias, "LIST_SYN", PROC(listsyn));
  63.  
  64.     (* In the next line, the string _must_ be passed as an
  65.     ADDRESS, not as an ARRAY OF CHAR:  Modula-2 passes open
  66.     arrays as _six_ bytes on the stack -- two bytes for the
  67.     length, followed by the address of the array itself --
  68.     but OS/2 DLL's generally expect only the string itself
  69.     (zero-terminated of course). *)
  70.  
  71.     ret := addsyn(ADR("ep \os2\eps\epsilon"));
  72.     ret := listsyn();
  73.     ret := Dos.FreeModule(alias);
  74. END calldll.
  75.  
  76.  
  77.  
  78.  
  79. [LISTING THREE]
  80.  
  81. ; calldll.lsp
  82. ; OS2XLISP run-time dynamic linking to Estes's ALIAS.DLL
  83.  
  84. (define alias (loadmodule "ALIAS"))
  85. (if (zerop alias)
  86.     (error "can't find ALIAS"))
  87. (call (getprocaddr alias "ADDSYN") "ep \os2\eps\epsilon")
  88. (call (getprocaddr alias "LIST_SYN"))
  89. (freemodule alias)
  90.  
  91.  
  92.  
  93. [LISTING FOUR]
  94.  
  95. /*
  96. proc1.c -- implements higher-level access to OS/2 run-time dynlinks
  97. cl -c -Lp proc1.c
  98. */
  99.  
  100. #define INCL_DOSMODULEMGR
  101. #include "os2.h"
  102. #include "procaddr.h"
  103.  
  104. #define NIL         ((void far *) 0)
  105.  
  106. WORD loadmodule(ASCIIZ name)
  107. {
  108.     WORD h;
  109.     return DosLoadModule(NIL, 0, name, (PHMODULE) &h) ? 0 : h;
  110. }
  111.  
  112. ULONG getprocaddr(WORD module, ASCIIZ name)
  113. {
  114.     ULONG pf;
  115.     return DosGetProcAddr(module, name, (PPFN) &pf) ? 0 : pf;
  116. }
  117.  
  118. ULONG procaddr(ASCIIZ module, ASCIIZ name)
  119. {
  120.     return getprocaddr(loadmodule(module), name);
  121. }
  122.  
  123. BOOL freemodule(WORD h)
  124. {
  125.     return (! DosFreeModule(h));
  126. }
  127.  
  128.  
  129.  
  130. [LISTING FIVE]
  131.  
  132. /*
  133. procaddr.h -- higher-level access to OS/2 run-time dynlinks
  134. */
  135.  
  136. typedef unsigned WORD;
  137. typedef unsigned short BOOL;
  138. typedef unsigned long ULONG;
  139. typedef char *ASCIIZ;
  140.  
  141. WORD loadmodule(ASCIIZ name);
  142. ULONG getprocaddr(WORD module, ASCIIZ name);
  143. ULONG procaddr(ASCIIZ module, ASCIIZ name);
  144. BOOL freemodule(WORD handle);
  145.  
  146.  
  147.  
  148.  
  149.  
  150. [LISTING SIX]
  151.  
  152. /*
  153. calldll2.c -- run-time dynamic linking to CRTLIB.DLL, using PROC1.C
  154. requires MSC 5.1 CRTEXE.OBJ
  155.  
  156. cl -AL -c calldll2.c proc1.c
  157. link /nod/noi crtexe.obj calldll2 proc1,calldll2,,crtlib.lib os2;
  158.  
  159. output:
  160.     Hello from calldll2
  161.     Hello again, using new ANSI C style
  162.     _printf lives at 03EF:1098
  163.     printf returned 27
  164.     Goodbye
  165. */
  166.  
  167. #include "procaddr.h"
  168.  
  169. typedef ULONG (far cdecl *CFN)();
  170.  
  171. main(int argc, char *argv[])
  172. {
  173.     WORD (far cdecl *printf)();
  174.     WORD crtlib;
  175.     WORD ret;
  176.  
  177.     crtlib = loadmodule("CRTLIB");
  178.     printf = (CFN) getprocaddr(crtlib, "_printf");
  179.     (*printf)("Hello from %s\n", argv[0]);                            /* 1 */
  180.     printf("Hello again, using new ANSI C style\n");                  /* 2 */
  181.     ret = printf("_printf lives at %Fp\n", printf);                   /* 3 */
  182.     printf("printf returned %d\n", ret);                              /* 4 */
  183.     ((CFN) getprocaddr(loadmodule("CRTLIB"),"_printf"))("Goodbye");   /* 5 */
  184.     freemodule(crtlib);
  185. }
  186.  
  187.  
  188.  
  189.  
  190. [LISTING SEVEN]
  191.  
  192. /*
  193. calldll3.c -- run-time dynamic linking from the command-line
  194. requires MSC 5.1 CRTEXE.OBJ, uses proc2.obj or procaddr.dll
  195. doesn't include "os.h"
  196.  
  197. to use proc2.obj:
  198. cl -AL -c -Gs2 -Ox -W2 calldll3.c proc2.c
  199. link /nod/noi crtexe.obj calldll3 proc2,calldll3.exe,,crtlib.lib os2.lib;
  200.  
  201. to use procaddr.dll (IMPLIB procaddr.lib):
  202. cl -AL -c -Gs2 -Ox -W2 calldll3.c
  203. link /nod/noi crtexe.obj calldll3,calldll3,,procaddr.lib crtlib.lib os2.lib;
  204.  
  205. to run:
  206. calldll3 <module name> <function name or ordinal number> [args...] [%mask]
  207. examples:
  208. calldll3 VIOCALLS VIOWRTTTY "hello world" 5 0
  209. calldll3 doscalls DosMkDir \foobar 0L
  210. calldll3 doscalls DosRmDir \foobar 0L
  211. calldll3 DOSCALLS DosBeep 2000 300                  
  212. calldll3 DOSCALLS 50 2000 300                       ; DosBeep
  213. calldll3 CRTLIB _printf "goodbye world: %lu" 666L " [%d]"
  214. calldll3 CRTLIB ACOS -1.0 %.15f
  215. calldll3 CRTLIB SQRT -1.0 %f
  216. calldll3 CRTLIB _toupper 'b' %c
  217. calldll3 PROCADDR LOADMODULE PROCADDR %X
  218. */
  219.  
  220. #include <mt\stdlib.h>
  221. #include <mt\stdio.h>
  222. #include <mt\string.h>
  223.  
  224. #include "local.h"
  225. #include "proc2.h"
  226.  
  227. typedef enum { typ_string, typ_byte, typ_word, typ_long, typ_float } TYPE;
  228.  
  229. TYPE NEAR type(char *arg);
  230. TYPE NEAR retval_type(char *s);
  231.  
  232. VOID fail(char *msg) { puts(msg); exit(1); }
  233.  
  234. /* 
  235.     push() : see Cortesi, Programmer's Essential OS/2 Handbook, pp.136-137
  236. */
  237. VOID NEAR PASCAL push() { }
  238. extern WORD pop(void);
  239.  
  240. #define PUSH_ARG(arg)   \
  241. {   \
  242.     switch (type(arg))  \
  243.     {   \
  244.         case typ_string:    push(arg);          c += 2; break;  \
  245.         case typ_byte:      push(arg[1]);       c += 1; break;  \
  246.         case typ_word:      push(atoi(arg));    c += 1; break;  \
  247.         case typ_long:      push(atol(arg));    c += 2; break;  \
  248.         case typ_float:     push(atof(arg));    c += 4; break;  \
  249.     }   \
  250. }
  251.  
  252. #define SYNTAX_MSG  \
  253.     "syntax: calldll3 <module name> <func name or ord#> [args...] [%mask]"
  254.         
  255. main(int argc, char *argv[])
  256. {
  257.     FN f;
  258.     TYPE retval_typ = typ_word;
  259.     char *mask = "%u";
  260.     WORD module;
  261.     BOOL is_cdecl;
  262.     int i, c;
  263.     
  264.     if (argc < 3)
  265.         fail(SYNTAX_MSG);
  266.  
  267.     /* handle optional printf mask */
  268.     if (strchr(argv[argc-1], '%'))
  269.         retval_typ = retval_type(mask = argv[--argc]);
  270.     
  271.     if ((module = loadmodule(argv[1])) == 0)
  272.         fail("can't load module");
  273.  
  274.     /* pass ASCIIZ string or ordinal number */
  275.     f = getprocaddr(module, isdigit(argv[2][0]) ? atol(argv[2]) : argv[2]);
  276.     if (! f)
  277.         fail("can't get function");
  278.     
  279.     is_cdecl = ! (strcmp(strupr(argv[1]), "CRTLIB"));
  280.  
  281.     /* push in reverse order for cdecl */
  282.     if (is_cdecl)
  283.     {
  284.         for (i=argc-1, c=0; i>=3; i--)
  285.             PUSH_ARG(argv[i]);
  286.     }
  287.     else
  288.     {
  289.         for (i=3; i<argc; i++)
  290.             PUSH_ARG(argv[i]);
  291.     }
  292.  
  293.     /* args are on the stack : call (*f)() and print retval */
  294.     switch (retval_typ)
  295.     {
  296.         case typ_string:    printf(mask, ((STRFN) f)()); break;
  297.         case typ_byte:      printf(mask, ((BYTEFN) f)()); break;
  298.         case typ_word:      printf(mask, f()); break;
  299.         case typ_long:      printf(mask, ((LONGFN) f)()); break;
  300.         case typ_float:     printf(mask, ((FLOATFN) f)()); break;
  301.     }
  302.  
  303.     if (is_cdecl)
  304.         for (i=0; i<c; i++)
  305.             pop();
  306.     
  307.     freemodule(module);
  308.     return 0;
  309. }
  310.  
  311. /*
  312.     type() uses some dumb rules to determine the type of an argument:
  313.         if first character of arg is a digit or '-'
  314.             and if arg contains '.' then it's a floating-point number
  315.             else if last character is an 'L' then it's a long
  316.             else it's a unsigned word
  317.         else if first character is an apostrophe
  318.             it's a single-byte character
  319.         otherwise
  320.             it's a string 
  321. */          
  322. TYPE NEAR type(char *arg)
  323. {
  324.     if (isdigit(arg[0]) || (arg[0] == '-' && isdigit(arg[1])))
  325.     {
  326.         char *p = arg;
  327.         while (*p)
  328.             if (*p++ == '.') 
  329.                 return typ_float;
  330.         return (*--p == 'L') ? typ_long : typ_word;
  331.     }
  332.     else
  333.         return (arg[0] == '\'') ? typ_byte : typ_string;
  334. }
  335.  
  336. /*
  337.     retval_type() uses a printf() mask (e.g., %s or %lX) to determine
  338.     type of return value
  339. */
  340. TYPE NEAR retval_type(char *s)
  341. {
  342.     while (*s)
  343.     {
  344.         switch (*s)
  345.         {
  346.             case 's' :  return typ_string; break;
  347.             case 'c' :  return typ_byte; break;
  348.             case 'p' : case 'l' : case 'I' : case 'O' : case 'U' :
  349.                         return typ_long; break;
  350.             case 'e' : case 'E' : case 'f' : case 'g' : case 'G' :
  351.                         return typ_float; break;
  352.         }
  353.         s++;
  354.     }
  355.  
  356.     /* still here */
  357.     return typ_word;
  358. }
  359.  
  360.  
  361.  
  362. [LISTING EIGHT]
  363.  
  364. ; pop.asm
  365.  
  366. DOSSEG
  367. .MODEL large
  368. .CODE pop_text
  369. PUBLIC _pop, _sp
  370.  
  371. _pop proc far
  372.     ; save away far return address
  373.     pop cx
  374.     pop bx
  375.     ; pop word off stack and return it in AX
  376.     pop ax
  377.     ; push far return address back on stack
  378.     push bx
  379.     push cx
  380.     ret
  381. _pop endp
  382.  
  383. ; useful for testing
  384. _sp proc far
  385.     mov ax,sp
  386.     ret
  387. _sp endp
  388.  
  389. end
  390.  
  391.  
  392. [LISTING NINE]
  393.  
  394. /*
  395. proc2.c
  396.  
  397. to make procaddr.dll:
  398. cl -Alfu -c -Gs2 -Ox -W2 -DDLL proc2.c
  399. link /nod/noi proc2,procaddr.dll,,llibcdll.lib os2,procaddr.def;
  400. implib procaddr.lib procaddr.def
  401. copy procaddr.dll \os2\dll
  402. */
  403.  
  404. #include <string.h>
  405.  
  406. #ifdef DLL
  407. int _acrtused = 0;
  408. #endif
  409.  
  410. #define INCL_DOS
  411. #include "os2.h"
  412.  
  413. #include "local.h"
  414. #include "proc2.h"
  415.  
  416. typedef struct {
  417.     char *name;
  418.     USHORT (APIENTRY *f)();
  419.     } DOSCALLS;
  420.  
  421. /*
  422.     include table generated from BSEDOS.H with AWK script DOSCALLS.AWK
  423.     table looks like:
  424.         LOCAL DOSCALLS NEAR dos[] = {
  425.             "", 0,
  426.             ...
  427.             "DosGetHugeShift", DosGetHugeShift,
  428.             "DosGetInfoSeg", DosGetInfoSeg,
  429.             ...
  430.             } ;
  431.     DOSCALLS.C also contains #define NUM_DOSCALLS
  432. */
  433. #include "doscalls.c"   
  434.  
  435. LOCAL FN NEAR getdoscall(ASCIIZ name);
  436. LOCAL USHORT NEAR doscalls = 0;
  437.  
  438. WORD pascal loadmodule(ASCIIZ name)
  439. {
  440.     WORD h;
  441.     return DosLoadModule((void far *) 0, 0, name, (PHMODULE) &h) ? 0 : h;
  442. }
  443.  
  444. /*
  445.     if name is actually a four-byte ordinal number, use it as is
  446.     otherwise if module is not DOSCALLS, use it as is
  447.     otherwise if module is DOSCALLS, get ordinal number and use it instead
  448. */
  449. FN pascal getprocaddr(WORD module, ASCIIZ proc)
  450. {
  451.     FN f;
  452.     
  453.     if (! doscalls) doscalls = loadmodule("DOSCALLS");
  454.  
  455.     if ((module == doscalls) && FP_SEG(proc))
  456.         return getdoscall(proc);
  457.     else
  458.         return DosGetProcAddr(module, proc, (PPFN) &f) ? 0 : f;
  459. }
  460.  
  461. FN pascal procaddr(ASCIIZ module, ASCIIZ proc)
  462. {
  463.     return getprocaddr(loadmodule(module), proc);
  464. }
  465.  
  466. BOOL pascal freemodule(WORD h)
  467. {
  468.     return (! DosFreeModule(h));
  469. }
  470.  
  471. /*
  472.     do binary search of table, looking for name, returning function ptr
  473.  */      
  474. LOCAL FN NEAR getdoscall(ASCIIZ name)
  475. {
  476.     signed cmp, mid;
  477.     signed base = 1, top = NUM_DOSCALLS+1;
  478.     
  479.     name = strupr(name);
  480.     
  481.     for (;;)
  482.     {
  483.         mid = (base + top) / 2;
  484.         cmp = strcmp(name, strupr(dos[mid].name));
  485.         
  486.         if      (cmp == 0)      return (FN) dos[mid].f;
  487.         else if (mid == base)   return 0;
  488.         else if (cmp < 0)       top = mid;
  489.         else if (cmp > 0)       base = mid;
  490.     }
  491. }
  492.  
  493.  
  494. [LISTING TEN]
  495.  
  496. /*
  497. proc2.h
  498. */
  499.  
  500. extern WORD pascal loadmodule(ASCIIZ name);
  501. extern FN pascal getprocaddr(WORD module, ASCIIZ proc);
  502. extern FN pascal procaddr(ASCIIZ module, ASCIIZ proc);
  503. extern BOOL pascal freemodule(WORD handle);
  504.  
  505.  
  506.  
  507. [LISTING ELEVEN]
  508.  
  509. # doscalls.awk
  510. # creates doscalls.c from bsedos.h
  511. # doscalls.c is #included by proc2.c
  512. # C>sort -b +2 \os2\inc\bsedos.h | awk -f doscalls.awk > doscalls.c
  513.  
  514. # bsedos.h contains prototypes such as:
  515. #   USHORT APIENTRY DosCreateThread(PFNTHREAD, PTID, PBYTE);
  516. # doscalls.awk turns these into string name/function ptr pairs:
  517. #   "DosCreateThread", DosCreateThread,
  518.  
  519. BEGIN                           { init() }
  520.  
  521. END                             { fini() }
  522.  
  523. $2 ~ /APIENTRY/ && $3 ~ /Dos/   { doscall($3) }
  524.  
  525. function init() {
  526.     print "/* doscalls.c */"
  527.     print "LOCAL DOSCALLS NEAR dos[] = {"
  528.     print "\"\",\t0,"
  529.     }
  530.  
  531. function fini() {
  532.     print "} ; "
  533.     print "#define NUM_DOSCALLS\t", num_doscalls
  534.     }
  535.  
  536. function doscall(s) {
  537.     gsub(/\(/, " ", s)                  # replace open paren with space
  538.     split(s, arr)                       # tokenize
  539.     print "\"" arr[1] "\", " arr[1] "," # print with and without quotes
  540.     num_doscalls++
  541.     }
  542.  
  543.  
  544.  
  545.  
  546. [LISTING TWELVE]
  547.  
  548. ; procaddr.def
  549.  
  550. LIBRARY PROCADDR
  551. DESCRIPTION 'Run-Time Dynamic Linking'
  552. DATA SINGLE SHARED
  553. PROTMODE
  554. EXPORTS
  555.     LOADMODULE
  556.     GETPROCADDR
  557.     PROCADDR
  558.     FREEMODULE
  559.  
  560.  
  561.  
  562. [LISTING THIRTEEN]
  563.  
  564. /*
  565. local.h -- miscellaneous definitions
  566. */
  567.  
  568. typedef unsigned short WORD;
  569. typedef unsigned short BOOL;
  570. typedef char far *ASCIIZ;
  571. typedef unsigned long ULONG;
  572. typedef double FLOAT;
  573. typedef WORD (far *FN)();
  574. typedef ASCIIZ (far *STRFN)();
  575. typedef char (far *BYTEFN)();
  576. typedef WORD (far *WORDFN)();
  577. typedef ULONG (far *LONGFN)();
  578. typedef FLOAT (far pascal *FLOATFN)();
  579.  
  580. #define FP_SEG(p)   ((WORD) ((ULONG) (p) >> 16))
  581. #define FP_OFF(p)   ((WORD) (p))
  582.  
  583. #define isdigit(c)  ((c) >= '0' && (c) <= '9')
  584.  
  585. #ifndef NEAR
  586. #define NEAR        near
  587. #define PASCAL      pascal
  588. #define VOID        void
  589. #endif
  590.  
  591. #define LOCAL       static
  592.  
  593.  
  594. Example 1: Expressive functions
  595.  
  596.         WORD alias = loadmodule("ALIAS");
  597.         PFN listsyn = (PFN) getprocaddr(module, "LIST_SYN");
  598.         if (listsyn) 
  599.             (*listsyn)();
  600.         freemodule(alias);
  601.  
  602. or:
  603.  
  604.         PFN listsyn;
  605.         if (listsyn = (PFN) procaddr("ALIAS", "LIST_SYN"))
  606.             (*listsyn)();
  607.  
  608.  
  609.  
  610. Example 2: Assembly language equivalent 
  611.  
  612.         PUSH "Goodbye"
  613.         PUSH "_printf"
  614.         PUSH "CRTLIB"
  615.         CALL loadmodule
  616.         ; loadmodule consumed "CRTLIB"
  617.         ; and produced handle to crtlib
  618.         CALL getprocaddr
  619.         ; getprocaddr consumed crtlib-handle and "_printf"
  620.         ; and produced pointer to printf on top of stack
  621.         ; "Goodbye" is still on stack
  622.         CALL [top of stack]
  623.         POP retval from _printf
  624.  
  625.  
  626. Example 3: Legal calls to the interpreter
  627.  
  628.         calldll viocalls VIOWRTTTY "hello world" 11 0
  629.         calldll doscalls DosBeep 2000 300 
  630.         calldll doscalls 50 2000 300                       ; DOSBEEP
  631.         calldll doscalls DosMkDir \foobar 0L
  632.         calldll doscalls DosRmDir \foobar 0L
  633.         calldll pmwin WINQUERYACTIVEWINDOW 1L 0 %lu
  634.         calldll crtlib _printf "goodbye world: %lu" 666L
  635.         calldll crtlib SQRT -1.0 %f
  636.         calldll crtlib _toupper 'b' %c
  637.         calldll jpilib FIO$Exists 12 CALLDLL.EXE
  638.  
  639.  
  640. Example 4: Arguments already on the stack
  641.  
  642.         switch (retval_typ)
  643.         {
  644.             case typ_string:    printf(mask, ((STRFN) f)()); break;
  645.             case typ_word:      printf(mask, f()); break;
  646.             ...
  647.         }
  648.  
  649.  
  650. Example 5: Associating ASCIIZ names with function pointers
  651.  
  652.         LOCAL DOSCALLS NEAR dos[] = {
  653.             "", 0,
  654.             ...
  655.             "DosGetProcAddr", DosGetProcAddr,
  656.             "DosGetPrty", DosGetPrty,
  657.             ...
  658.             } ;
  659.  
  660.  
  661.