ADDING USER-DEFINED EXTENSIONS TO WCL ------------------------------------- Over the past few months, I have received incessant requests for me to provide support for user extensions to WCL. I have finally decided to provide a simple interface or hook to WCL for Windows programmers so that they can add their own commands. This provides endless opportunities for extending the command set which I have provided as internal commands, since such user extensions are treated as internal commands by WCL. The interface I have provided is very simple, as long as the rules are adhered to. It revolves around DLLs which must be created by the user, and which export two USER FUNCTIONS that WCL will interface to (the DLLs can export other functions as well, but WCL will only interface to two named functions). Rules ----- 1. The DLLs must be given the extension .WXX (instead of .DLL), and must be compiled with the FAR directive (and for C/C++ programmers, it must be "FAR Pascal"). 2. The .WXX DLLs *must* be in the WCL directory (i.e., in the same directory as WCL.EXE, WCLDLL.EXE, and BIGWCL.EXE). 3. [i] The first of the two user functions must be called IsValidCommand. It takes a null terminated string as a parameter, and returns an integer. This is the function that WCL polls to see if any command that is not recognised by WCL is one that is recognised by the user DLLs. It is only if after polling all the .WXX files and none of them recognised the command that WCL will present a "command not found" error message to the user. In this scheme, when the user types a command, WCL looks for the command in this order; [a] from the list of command aliases [b] from the list of WCL internal commands [c] from the list of WCL .CBF batch files [d] from .EXE, .COM, .BAT, programs in the "path" [e] if the "command" is a file name, from the list of associations in the "Extensions" part of WIN.INI [f] from the list of .WXX DLL files found in the WCL directory. If the command is not found in any of these places, then an error is reported "this command cannot be found". If the command is found in a .WXX file, then the whole command line is passed to that DLL's "UserProc()" function. The user can jump directly the "[f]" above, by-passing all the others, by typing "EXT" or "USERPROC" before the name of the extension command that he/she wants to execute. This ensures speedier access to extensions. Command aliases can be created for frequently used extensions, with the "EXT" prefix to the commands. Since there may be any number of .WXX files in the WCL directory, please ensure that this function is optimized for speed, and that it returns as soon as possible. The whole command line typed at the WCL prompt is passed to the IsValidCommand() function. It is up to you to parse the command line in your DLL. e.g.; [a] In Borland PASCAL Function IsValidCommand (Command : PChar) : Integer; [b] In C/C++ int IsValidCommand (LPSTR Command); RETURN CODE ----------- 0 = success - the command is recognised by this DLL 1 = error - the command is not recognised by this DLL. If this function returns any value other than 0 then WCL will not proceed any further with the particular DLL. It will then proceed to the next DLL (if any). If the command is recognised, you may also want to perform some other initialization functions in this function. [ii] The SECOND function which WCL will interface to is the main function in your DLL. It takes TWO parameters. The first parameter is an HWnd (this is where I pass the handle of the WCL window to your DLL). The second parameter is a null terminated string. It returns an INTEGER; [a] In Borland PASCAL Function UserProc (Wnd : HWnd; Var Parm : PChar) : Integer; [b] In C/C++ int UserProc (HWND Wnd; LPSTR Parm); (I am not sure whether the syntax here is correct!) RETURN CODE ----------- -1 = success - but do not print the contents of "Parm" at the WCL window 0 = success - and print the contents of "Parm" 1 = error - and print the contents of "Parm" > 1 = error - but do not print the contents of "Parm" at the WCL window *** Please NOTE that *each* USER DLL (those with the .WXX extension) MUST have these 2 functions. 4. Because WCL passes the handle of the WCL window to your DLL in the UserProc() function, you should be able to output text to the WCL window. You can do this through TextOut() or some other function. Using TextOut() here is probably a precarious thing, and does not seem to be the best way to do this (at least, not in my tests) - but it can be attempted anyway, and something can sometimes be directed to the WCL window. However, you are on your own here. The best I can do is provide the handle to the WCL window. You can get its device context with a call to GetDC() - (remember to call ReleaseDC() afterward!!!) - or you can do with the window handle as you please. 5. If you want the output from the commands to be echoed in the WCL window, you have to do 2 things; [a] Let function UserProc() return ZERO or 1 (see above) [b] Put whatever you want to be output at the WCL window into the null terminated string that was passed as a parameter to UserProc If UserProc() returns 0 or 1, WCL will re-direct whatever it finds in "Parm" to the WCL window, when your function returns. Note that the output in the string is dumped to the WCL window exactly as it is. If you want a formatted output, you have to format the null terminated string yourself, in your function (eg by inserting carriage returns after every 60 characters). If UserProc() returns a value less than 0 (eg -1) or greater than 1 then nothing will be written to the WCL window. This is the ONLY thing that WCL uses the return value of UserProc() for. Subject to what I have said above, it can return ANYTHING. RESTRICTIONS ------------ User extensions in .WXX DLL files cannot be called from WCL batch files. Comments -------- I have decided to use this method of providing hooks for other programmers for a number of reasons. First, it is straightforward. Secondly, it does not add much to the bulk of WCL itself - something I have to take seriously, because most of my users are not programmers and are not interested in this feature. I expect the function UserProc() to be used as a "container" or "interface" function only - although you can use it as you please. What I really envisage is that programmers can define any number of functions and commands in their DLLs, and that they will use the function UserProc() as their "command line", which they will then parse, and then call other functions in the DLL or elsewhere as may be necessary. Check this pseudo code; {------------------------------------------------------------} function UserProc (Wnd : HWnd; Var Parm : PChar) : integer; begin UserProc if Parm = "SHUTDOWN" then call function ONE else if Parm = "OK" then call function TWO else if Parm = "MAKE_NOISE" then call MessageBeep (0) else if Parm = "DISK_FORMAT" then WinExec("D_FORMAT.EXE", sw_Normal) else MessageBox(Wnd, 'Not, Not, Not', 'Hello', mb_ok); {etc, etc, etc } Return -1 end UserProc {-----------------------------------------------------------} For those programmers who need it, I have provided an interface to a number of functions in WCLCODE2.DLL, which comes standard with WCL. These are string functions - to break up the "Parm" string parameter into as many substrings as are found in it (up to 20) and return the number of such substrings. This way, it is easy to parse the parameter passed to the UserProc function. eg if you passed "BPC -CW MYPROG.PAS /$F+ /L /$N+" in "Parm", calling the function BreakString() will return 6 (there are 6 substrings in this string) and the 6 substrings will be returned in an array. These string functions were originally written to process Pascal type strings only, but I have added equivalents for null terminated strings so that C/C++ users can use the same functions. I have provided a Borland Pascal interface unit (WCL_INT.PAS) for these functions, which contains all the declarations, and the ordinal numbers of the functions, etc. Pascal programmers can just add this unit to their USES clause, and start using the functions right away. C/C++ programmers will have to create a header file using the supplied information, and also a LIB file for WCLCODE2.DLL, using their compiler's IMPLIB utility. If anybody does this successfully, please send me a copy of the header file, and of the LIB file (indicating what compiler this was created for). NOTE: ONLY *ONE* INSTANCE OF WCL CAN USE WCLCODE2.DLL AT ANY PARTICULAR TIME BECAUSE OF SHARED MEMORY. I have provided a sample .WXX DLL file (WCLMAP.WXX) - which implements one command "WCLMAP" - a subset of the Netware MAP command. Please do NOT attempt to run the WCLMAP command if you are not on a Netware network. You are CERTAIN to get a General Protection Fault if you try it. I have also provided the Pascal source code to a working (but virtually useless) .WXX DLL file (WCL_EXT.PAS), which uses the parsing functions I referred to above. This is supplied just as an example - to see what types of things you could do in your DLL. NOTES ------ 1. A .WXX file is a normal Windows DLL, and should be compiled as such. After compilation, then you change the extension to .WXX (from .DLL), and you place it in the WCL directory. 2. Permission to use this interface to WCL is given on the basis that the interface is being used for each person's own PERSONAL AND PRIVATE use. You can obviously freely distribute your own extension DLLs and any of your own binaries and documentation that complement it, but ONLY as a package which is totally SEPARATE from the WCL package. 3. I envisage that if this interface becomes well established, people might want to write "custom" WCL extension packages, which others can then download from ftp sites. 4. Please do NOT attempt to distribute the WCL program with your own extensions package - any attempt to do this WILL be a breach of my copyright. If people want to use your extensions with WCL, then they should get a copy of WCL as distributed by me, through the normal distribution channels, and THEN they should get your extensions for themselves (meaning that I don't want to take the blame or the credit for other people's work - and I want it to be ABSOLUTELY CLEAR what was distributed by me and what wasn't). 5. If you write extensions DLLs which you then wish to distribute as an add-on to WCL, you should distribute ONLY your own binaries and documentation, and ALSO, please do the following; Include in your documentation for your extensions CLEAR AND CONSPICUOUS MESSAGES, (i) OF YOUR ACCEPTANCE OF TOTAL RESPONSIBILITY FOR ANY LOSS OR DAMAGE CAUSED BY YOUR EXTENSIONS, AND ABSOLVING ME OF ALL RESPONSIBILITY THEREFOR, and (ii) THAT ALL QUESTIONS ABOUT YOUR EXTENSIONS AND THEIR EFFECT ON WCL OR ON WINDOWS, SHOULD BE DIRECTED TO YOU, AND NOT TO ME. DISCLAIMER ---------- I AM SUPPLYING THIS INTERFACE ONLY AS A SERVICE TO USERS. I ACCEPT ABSOLUTELY NO RESPONSIBILITY FOR ANY LOSS OR DAMAGE OR LOSS CAUSED OR SUFFERED AS A RESULT OF THE USE OR THE PURPORTED USE OF THIS INTERFACE, FOR ANY PURPOSE WHATSOEVER. IF THESE TERMS ARE NOT ACCEPTABLE TO YOU, THEN YOU HAVE NO LICENSE TO USE THIS INTERFACE TO WCL, AND YOU SHOULD NEVER ATTEMPT IT. FURTHERMORE, I WILL ANSWER ABSOLUTELY NO QUESTIONS ABOUT THIS INTERFACE FROM ANYBODY WHO HAS NOT REGISTERED HIS/HER COPY OF WCL. YOU WILL ALL APPRECIATE THAT SINCE THERE ARE NO FREE LUNCHES, I CAN ONLY SUPPORT REGISTERED USERS. Dr. A. Olowofoyeku June 1994.