-----------------------------------------------------------------------------
Modifications to CLIPS source files for creation of a MS-Windows-based DLL
by Blake Buhlig
2ND RELEASE
August 22, 1994

No copyrights exist on any files or modifications, except those belonging
to COSMIC and/or NASA, Microsoft, and Borland.
-----------------------------------------------------------------------------

If you have a question or suggestion, then you can contact me by my E-mail 
address (that is, until I graduate in December 1994, anyway):

Internet:     bb760597@LANCE.ColoState.edu



INTRODUCTION
------------
	During the summer of 1992, I began implementation of a Windows-based 
expert system development tool using CLIPS v5.1.  The project, which was 
part of an undergraduate research program at Colorado State University,
involved interfacing CLIPS with C++ code.  It was envisioned that the expert 
system part of the development tool would be created with CLIPS, while the 
Microsoft Windows support would be designed with Borland C++ and Application
Frameworks.
	Due to various problems with statically-linking the CLIPS object code 
with my C++ code, I sought to create a Dynamic Link Library out of the CLIPS 
source code.  This package is the result of my efforts thus far. 




DISCLAIMER!!!!        
--------------
	It should be emphasized that THIS CODE IS NOT WELL-TESTED, and hence
any serious developer should be skeptical of every modification and creation
I have given here.  All I can offer is that what I have included here did
produce a functional CLIPS DLL that has helped advance my project.  However,
my project is not finished yet, which means that sometime in the
future I might discover some catastrophic problem that would force me to
somehow change or even scrap this approach altogether.
	I am releasing what I have here to help everyone else trying to
create a CLIPS DLL.  But if for some reason what I suggest doesn't work,
don't hold me accountable.





A POSSIBLE CATASTROPHIC BUG 
---------------------------
	Before I go any further, let me point out a "feature" with this 
code that may discourage you from continuing.  I have noticed that I cannot 
execute more than one instance of my development tool at a time without
crashing Windows with a GPF.  I haven't really investigated further 
because in my particular application, this is a fairly minor problem.  
However, your purpose for investigating the DLL approach might be (in fact,
probably is) to execute multiple CLIPS processes simultaneously.  If this is 
the case, be aware that the bug I discovered might be a problem with the DLL 
involving multiple applications calling the DLL at once.  (Or it might be
a problem with my particular application.  Who knows.)




FILES
-----
	The number one request that I received since the last version was
released was for an already-built DLL.  In fact, that is the primary reason
why I am releasing this version.  If this is what you want, the following
files will satisfy your needs:
	
	CLIPS.DLL
	CLIPS.LIB

CLIPS.LIB is the Import Library for the DLL.  It should be compiled along
with whatever program uses the DLL.  And, obviously, CLIPS.DLL is the DLL.
Note that the DLL contains the basic CLIPS functions only; the
UserFunctions() section is blank.

	I have also included a test program that you can use to
quickly verify the working version of CLIPS.  The program simply loads
the file TESTDLL.CLP into the CLIPS environment, does a ResetCLIPS() and
a RunCLIPS(-1).  TESTDLL.CLP contains a single defrule, which retracts
(initial-fact) and asserts (test-completed).  When the RunCLIPS returns,
the first fact in the database is printed in a dialog box, which should
be (test-completed).  It's a simple test, but it should give you some
confidence if you are leary.  Its also a starting point for you in your
quest to correctly link the DLL to your own program.
	The files for the test program are TESTDLL.*.  The C source code,
Windows DEF, and Borland C++ Project files are yours for examination.

Before you go off and play, make sure you read the rest of the 
document, especially the Notes and Frequently Answered Questions sections 
below.
	
	
	For those of you who want to customize the DLL, for example to
add stuff to the UserFunctions() function, I will describe how you can make 
your own DLL.  First, note that I have included several modified files from 
the original CLIPS distribution:

	CONSTRCT.C   CONSTANT.H
	RETRACT.C    CONSTRCT.H
	ROUTER.C     ROUTER.H
	SYSDEP.C     SETUP.H
	SYSIO.C      SYSDEP.H
	PROCESS.C

Some of these files were modified specifically to facilitate the DLL; others
were changed for other purposes.  In each file, I have tried to document
changes where appropriate.  If I were you, though, I would run a comparison
between the original CLIPS distribution and these files, and explicitly find
out what changes I did make.  When you are satisfied, replace the original
distribution files with these files.

The other files I have included add required extra support for the DLL.
Most of them will need to be modified to suit you own needs.  The files
are as follows:

	USRFUNC.C       CLIPS.PRJ    
	CLIPS.DSK       CLIPS.DEF   
	DLLSHELL.C

The PRJ and DSK files are Borland C++ specific compiler
configuration files.  If you are using MSC or some other compiler, you will 
need to make modifications.  Even if you are using Borland, you will 
probably still need to customize directory names.  If you don't use some
file that I mention below, or want to add your own files, make sure you add
these to the makefile or the project file.

The CLIPS.DEF file is the module definition file for the DLL.  I think
it is compatible with all compilers.  The most important thing to know about
this file is that, in it, one must declare explicitly every CLIPS function 
to be called from the external language.  This includes every function in the
CLIPS Advanced Programming Guide, AS WELL AS any additional functions you
create that are to be called by Windows (see the description of USRFUNC).  
I think I have already declared all documented functions, but you should 
check yourself just to be sure.  Note that macros are NOT be declared in 
this file.

The DLLSHELL.C file includes the required DLL entry and exit functions 
LibMain and WEP.  In my project, these are do-nothing functions.  One could
potentially put the InitializeCLIPS() call in LibMain, but I decided to call 
it from my C++ program.

The file USRFUNC.C declares and defines the UserFunctions() function.  In
this version, it compiles into do-nothing code.  For your investigation,
the code I used in my own program to declare external functions still exists 
but has been commented out. This code was used to provide a message-passing
interface between CLIPS and Windows.  For any robust application, you
will likely need to do something similar to what I've done.

After you have modified all necessary files, transfer them into the same
directory as the rest of the CLIPS distribution, and build the DLL using
the project file.  Then test it out with your own program!



NOTES
-----
	There are some technical things I need to mention before you
start customizing the files and building the DLL:

	* Always #include <windows.h> before <clips.h>.  I'm not sure why,
	  but if I do it the other way around, compiler errors always occur.

	* This DLL was built with the Large Memory Model.  If you do things
	  differently, you may run into trouble with near, far, and huge
	  pointers, and the like.

	* After building the DLL, make sure an import library was created
	  for the DLL.  If not, use IMPLIB or some other tool to make one.
	  Then, make sure you add the import library to your program's 
	  makefile or project file.

	* If you get linkage errors when binding the DLL to your main program,
	  check the .DEF file and insure that you added all functions that
	  you are calling from the CLIPS DLL.


FREQUENTLY ANSWERED QUESTIONS
-----------------------------

Q:  When I compile my own DLL, I get linkage errors referencing the
    CLIPS functions I call in my program.  What am I doing wrong?
A:  First, make sure you listed all DLL functions that you are calling in the
    CLIPS.DEF file.  Next, make sure you have included the import library, 
    CLIPS.LIB, in your project.  Also, make sure you are compiling with the 
    large memory model (under Compiler|Code Generation in BC++ 3.1).  
    Finally, you need to have "Case-sensitive exports" on (under 
    Linker|Settings in BC++ 3.1).

Q:  CLIPS is not working.  Why?
	- or -
Q:  CLIPS is acting weird.  Why?
A:  You probably forgot to call InitializeCLIPS().  You must call this 
	before you call any other CLIPS function.

Q:  When I run the DLL, everything works except the TTY output functions
	(printout, PrintCLIPS(), etc...).  Why?
A:  Windows does not recognize standard TTY output.  Therefore, you
	must provide I/O routing code that captures all such
	output and sends it to something else (a text window, a file, or
	oblivion, for example).  Otherwise, Windows will either crash,
	or the display will be overwritten with TTY text.

    An example of I/O routing code in C++ follows.  For other examples
	see the CLIPS Advanced Programming Guide.

BOOL captureQuery(char *router)
// This is a function used by the CLIPS I/O routing system.  Essentially,
// when TTY output is needed, CLIPS queries this function to determine
// whether this router can handle the output.
{
	// If the router comes from stdout, stdin, wclips, wdisplay, ...,
	// then we will accept the output.  Otherwise, pass the buck onto
	// someone else.
	if ((strcmp(router,"stdout") == 0) ||
	    (strcmp(router,"stdin") == 0) ||
	    (strcmp(router,"wclips") == 0) ||
	    (strcmp(router,"wdisplay") == 0) ||
	    (strcmp(router,"wdialog") == 0) ||
	    (strcmp(router,"werror") == 0) ||
	    (strcmp(router,"wtrace") == 0) ||
	    (strcmp(router,"wagenda") == 0) )
		{ return TRUE; }

	return FALSE;
}

int capturePrint(char *router, char *str)
{
	// Dump all TTY output to the file CLIPSDLL.ERR
	ofstream ofError("CLIPSDLL.ERR",ios::out | ios::app);
	ofError << str;
	return TRUE;
}

The function call that would add the router is:
	
	AddRouter("capture", 20, captureQuery, capturePrint, NULL, NULL,
		  NULL);

You would call this shortly after your call to InitializeCLIPS().



REGARDS
-------
	Well, that's all I can think of right now.  If you find something
wrong with the DLL, or if you just can't get things to work, then feel free
to drop me a message at the address I mentioned earlier.
	I'd also like an idea of how many people are using this code is, so 
if you get it to work and have a minute, drop me a quick line about what
it is you are doing.

	Good Luck...

