--- Begin Message ---
Operating System: Windows NT 4.0 SP 3
Software: Splus 4.5, Watcom C++ 10.6 and 11.0
Hardware: Various Pentium 2 Machines and a 486 Machine
I have been doing a great deal of mixed language
programming
between S+, C, C++ and FAME. Judging from the emails in the
archived S+ news list and the SYBASE technical support forums
I
am guessing that very few people have successfully done this.
In
addition there are several bugs in the MATHSOFT dll's
and
utilities which make this problem even more difficult. I
thought
that there could be people in the S+ and C++ users groups
who
would be interested in the work I have done.
In this email I will deal with loading and debugging a dll
which
calls the S+ function S_alloc and S_newio_printf. There
are
several bugs in the S+ dll interface which make this
difficult.
In addition the documentation in the programmers manual
is
incomplete and inaccurate.
The first bug is in the dll.load function. All functions
should
be called with the __stdcall convention. The __cdecl
convention
does not work dependably.
The documentation about the compile flags is very incomplete
and
inaccurate. Contrary to the documentation, it does no
matter
whether stack based calling or register based calling is used.
In
fact the compile flags in the examples can be largely
ignored.
The macros WIN32 and SPLUS must be defined in order for
the
header files in splus\include to be compiled correctly. But
other
than that, the compile flags generated by the WATCOM IDE can
be
used.
In order to do source level debugging of staticly loaded
object
modules, or dll's, the -d2 compile switch must be used. This
is
done by default in the WATCOM IDE.
Contrary to the documentation, I have not been able
to
consistently use the SPLUS functions in SQPE.DLL from my
own
dll's. This is very odd. One one machine (266 Megahertz,
Pentium
2, NT 4.0 SV3) the MATHSOFT DLL's worked fine. However when
I
upgraded my machine to a 400 Megahertz machine not a single
DLL
call would work. The operating system generated a GPF as soon
as
I called any function in SQPE.DLL.
However there is a simple work around. What I did was to define
a
dll, patchsq.dll which has a wrapper function for every
function
in SQPE.dll that I which to call (S_alloc and
S_newio_printf).
Patchsq.dll contains a global function pointer for each
wrapper
function. There is one init function which initializes the
global
function pointers. The init function is called from a
dynamically
loaded object module Npatchst.obj (This is an odd name.
N
represents the operating system. st stands for static.
I
originally intended for this object module to be
staticly
loaded.) The wrapper functions are save_alloc and safe_printf.
Notes on function calls and symbol tables: Function calls
from
dynamically and staticly loaded object modules are resolved
from
the symbol table in Splus. Thus you can see what functions
you
can use at any time by typing the dump.loaded() at the
Splus
command line.
Notes on SQPE.DLL and the type of CPU: The functions in SQPE.DLL
can not be safely access from the function DllMain. On my 486
machine, (my old old old backup machine), calling SQPE functions
causes a GPF. However on my pentiums it works just fine. This is
pretty strange behavior. I wonder what in the world SQPE is
doing
that makes it so sensitive to the CPU.
Notes on source level debuggers:
The Watcom debuggers (wdw or wd) work extremely
well for debugging dlls loaded with the dll.load function. If
you
are going to be doing any work with dll's be sure to use the
break on image load function. This will put you write into the
code which you need to observe. Also note that you must compile
with -d2 and link with debug all, (this is not evident in the
SPLUS documentation).
Notes on WATCOM VERSIONS: The SPLUS documentation says that you
can
use only the WATCOM 10.5 compiler. I have used 10.5, 10.6 and
11.0.
The dynamically loaded object modules and the dynamically loaded
dll's
work the same in all 3 version. For static loading you can use
versions 10.5 or 10.6.
Notes no static loading: There are several undocumented or
partially documented problems with the GLOAD function:
1) you must change the S_HOME and SHOME environment variables.
This causes version 4.5 of Splus to be incompatible with
versions 3.x.
2) you must create a TEMP directory under the SHOME directory.
3) you should make a backup copy of SQPE.dll in case the
compile/link
bombs. SQPE.DLL is automatically deleted if there is a problem.
4) you must rename the final dll as SQPE.DLL because the
SPLUS.EXE
loads SQPE.DLL. SPLUS.EXE has no way of knowing what dll you
created
with GLOAD.BAT
5) If you do not rename your dll as SQPE.DLL then Splus will
not
work. You will get an error message that says: SPLUS can not
cmd directory or the necessary components please respecify
directory.
As a demo program I have altered some code from the SPLUS
programers
manual. This programe illustrates the only stable way to call
functions
in SQPE.DLL from dynamically loaded dll's. This codes
works on all cpus and operating systems that I have tested.
The source file is fibdll.c
I have attached the following files:
patchst.c
patchsq.c
patchsq.mak
patchsq.lnk
fibdll.c
fibdll.mak
fibdll.lnk
patchst.c was compiled by the following:
wcc386 patchst.C -fo=Npatchst.obj
-i=..;%S_HOME%\include;%S_HOME%\include\dos -5s -fp5 -bd -bt=nt -sg
-DWIN32=1 -dSPLUS -d2
In SPLUS the following command were executed (from .First)
dll.name <- paste( getenv( "CCLIB"), "\\patchsq.dll", sep="")
val <- dll.load( dll.name,
c( "_safe_alloc@8", "_safe_printf", "_init_patch@8"),
"stdcall", load.wo.sym=F)
if (val!=1)
{
stop( paste( dll.name, "was not loaded"))
}
# CCLIB is an environment variable pointing to my c/c++
libraries
dyn.load( paste( getenv( "CCLIB"), "\\splus\\Npatchst.obj" ,
sep=""), undefined=1)
.C( "loaded_init_patch")
# Initializes the static pointers
The following commands load fibdll and execute the functions
within
fibdll.
dll.load( paste( getenv( "CCLIB"), "\\math\\fibdll.dll"),
c( "_fib@4", "_fib_vect@16"),
"stdcall" ,
load.wo.sym=F )
fib.C <- function(n) .C( "_fib@4", as.integer(n))[[1]]
fib.vect.C <- function(n) .C( "_fib_vect@16", as.integer(n),
length(n), integer(0), pointers=c(F,F,T))[[3]]
The output from the Splus dialogue is:
> fib.C( 4)
hello from fib
[1] 3
>
> fib.vect.C( c( 1, 2, 5, 6))
hello from fib_vect
[1] 1 1 5 8
>
I hope other people will benefit from the debugging that I have done.
Good Luck,
Clark Sims
patchst.c
Description: application/unknown-content-type-cfile
patchsq.c
Description: application/unknown-content-type-cfile
CMPL= -5s -fp5 -bd -bt=nt -sg -d2 -dWIN32 -dSPLUS -dCOMPDLL
obja= patchsq.obj
patchsq.dll: $(obja) patchsq.mak patchsq.lnk
wlink d all SYS nt_dll op m op maxe=25 op q op symf @patchsq.lnk
wlib patchsq.lib +patchsq.dll
patchsq.obj: splus\patchsq.c patchsq.mak
wcc386 $(CMPL) splus\patchsq.c
patchsq.lnk
Description: application/unknown-content-type-lnkfile
fibdll.c
Description: application/unknown-content-type-cfile
CMPL= -5s -fp5 -bd -bt=nt -sg -d2 -dWIN32 -dSPLUS -dCOMPDLL
obja= fibdll.obj
fibdll.dll: $(obja) fibdll.mak fibdll.lnk
wlink d all SYS nt_dll op m op maxe=25 op q op symf @fibdll.lnk
wlib fibdll.lib +fibdll.dll
fibdll.obj: fibdll.c fibdll.mak
wcc386 $(CMPL) fibdll.c
fibdll.lnk
Description: application/unknown-content-type-lnkfile
--- End Message ---