SwiftInside - the SwiftView Pluggable Object External Specification Release 6.2.1.1 May 10, 2003 SwiftInside is a pluggable SwiftView object that can be instantiated by a program written with 32-bit Windows C/C++ (Visual Studio 6) Visual Basic 5.0 SP3 or later, or Java 1.17. It is a full-featured, fully configurable packaging of SwiftView. SwiftInside permits the client program to create one or more independent SwiftView instances. An instance can optionally display the document in a window of the client program. SwiftInside can provide the standard SwiftView user interface, displaying a toolbar and scrollbars, and assigning actions to keys and the mouse. configurable via ICS commands. Or it can allow the client to implement it's own user interface. SwiftView callback strings are available to the client program. SwiftInside is initially available only on win95/NT, but we also hope to support Solaris and HP/UX in the future. Please email marketing@swiftview.com if you are interested. SwiftInside at runtime consists of: - The standard SwiftView executable. - A dynamically linked library (Windows .dll/Unix .so) that is loaded by the client. - For C programs, a small client library (.h+.a/.lib) that can be statically linked into and called by client C/C++ code, implementing a simple common API across all platforms. - For Java programs, a class that encapsulates a Java Native Interface to the shared library. Note - the .dll is built with static MFC, and does not require the client to link with or install any libraries not included in stock Windows. Windows Developer's Package svdll.exe is a self-extracting zip file for Windows that includes all the files needed to develop a SwiftInside application, except for SwiftView itself. Make a directory, copy svdll.exe to it, and run svdll.exe. It contains: README.txt - package quick start info svdll.txt - this specification svobj.dll (221 KB) - release version of shared library svobj_g.dll (1.22 MB) - debug version of svobj.dll svapi.lib (2 KB) - API library svapid.lib (10 KB) - API library for debug svobj.lib (4 KB) - dll import library svobj_g.lib (4 KB) - dll import library for debug svapi.h - API include file for client source code dlltest.exe - sample client program dlltest.dsp - sample client program Microsoft VC6 project dlltest.mak - sample client program nmake makefile dlltest.c - sample client program source SwiftView/SwiftInside.class - Java Native Interface object SwiftView.SwiftInside.html - Java Native Interface object doc page javatest.java - Java Native Interface test program source javatest.class - Java Native Interface test program vbdlldemo/project1.vbp vbdlldemo/module1.bas vbdlldemo/form1.frm - sample VB dll client For development purposes, the standard standalone SwiftView should be downloaded from http://swiftview.com/dload/dlmain.htm. When you purchase an embedded license (see Licensing below), you will be sent a special copy of standalone SwiftView that matches this license. At runtime, you must install the file svobj.dll. In addition you must install the standard standalone SwiftView, either via the standard installer (recommended), or just sview.exe. If you have an OEM-licensed SwiftView, we will provided you with a standard installer marked for your company. svobj.dll must be installed in one of the standard Windows LoadLibrary() locations (in order, the application's install directory, the current directory, the Windows system directory, , or $PATH). (Apparently, however, VB Studio doesn't load from the application's install directory, so you must install the .dll in the windows/system directory.) To use svobj_g.dll with VB or Java you must rename it to svobj.dll. SwiftInside, unlike SAX or the plugin, searches for sview.exe first in the process's current directory, then, like SAX, in the standard program files location created by the SwiftView installer, then the CreateProcess() search path. This permits svobj.dll and sview.exe to be installed with an OEM software distribution, e.g. on a CD. Possible future enhancement: a signed, IE or Netscape auto-installable, self-extracting zip file on Windows, like the SwiftView plug-in, that installs the runtime files. This would be particularly useful for browser-based Java apps. C Library API The following opaque structure and three functions comprise the API implemented by the files svapi.h and svapi.lib. These functions are a thin layer that loads the .dll and calls directly into it. typedef void *Svobj; #if defined(__NT__) || defined(__nt__) || defined(_MSC_VER) #include typedef HWND SVWINDOW; #else #include typedef Window SVWINDOW; #endif #ifdef __cplusplus extern "C" { #endif Svobj CreateSwiftView(SVWINDOW window, void (*handler)(Svobj, const char *), int poll_callbacks, const char *license) Create an instance of controllable SwiftView. If the window argument is NULL or "gui inline disable" is sent, the object functions in "partner" mode, displaying the document in a separate SwiftView window. Otherwise, SwiftView draws in the supplied window, optionally constructing its standard user interface in it. ATTENTION! The following is the most commmon programmer pilot error!!! No user interface is displayed until a "gui controls update" command is sent. See the discussion below under CmdSwiftView. In partner mode a separate SwiftView window is displayed, with it's usual default exit and file open buttons removed. The SwiftView window is initially hidden, and is made visible on the "gui controls update" command. The caller can hide or display the window as desired, using gui winstate commands. If the window argument is not NULL, SwiftInside by default draws a "ShowDoc" button in the window that brings the SwiftView window forward. Sending "gui drawinput client" via CmdSwiftView prior to sending "gui controls update" prevents the default "ShowDoc" button from being displayed. Standalone SwiftView's various user message/error/print dialogs are displayable with the normal commands. SwiftView callback strings are sent to the specified handler function if not NULL, one callback line per call, with the terminating newline removed. The Svobj argument allows associating the callback string with the object. The handler function may be called by a separate thread so it must be thread-safe; any access of data shared with the main thread must be protected, using e.g. a win32 CRITICAL_SECTION object. If the handler is NULL and poll_callbacks is true, the client will be calling getCallback() or vbGetCallback() to get callback strings. If the handler is NULL and poll_callbacks is false, the client will not get callback strings. See the SwiftView Technical Reference Manual for the specification of callback strings. The callback string should not be modified or freed. license, if non-NULL, is a customer-specific license string provided by SwiftView, Inc. that must match the customer name recorded by SwiftView, Inc. in the customer's copy of sview.exe. If license is NULL, SwiftInside requires a LAN license or must be a limited-time demo copy. The string is copied, and can be freed or modified after this call. The standard plug-in-style initialization ICS commands "hpgl startsoon off|set browser iexplore" are sent when SwiftView is started. CreateSwiftView returns NULL if the create fails, with an error message string "SVC:ERROR:string" sent to the callback handler with a NULL Svobj. const char *getCallback(Svobj sview, int check) int vbGetCallback(Svobj sview, int check, char *buffer, int bufsiz) If the poll_callbacks argument to CreateSwiftView() is true, the client will call getCallback() or vbGetCallback() to get callback strings. The functions return one callback line with the terminating newline removed. If check is true and no callback string is immediately available they returns NULL, otherwise they block until a callback string is available. getCallback returns the line as it's value, vbGetCallback fills in the caller-supplied buffer up to bufsiz bytes, and returns the length of the callback string as it's value. If poll_callbacks is false, the behavior when calling these functions is undefined. Typically the client starts a separate callback reader thread that continuously calls the function with check == false. VB applications must use vbGetCallback(), see notes below. The recommended bufsiz to read the largest possible callback string is 2620 (containing long SwiftServe URLs). void CmdSwiftView(Svobj sview, const char *cmds) Send an ICS command string to Svobj. The string passed to cmds does not need to be newline-terminated. Multiple commands separated by the pipe character may be used. Any errors are reported in a callback. See the SwiftView Technical Reference Manual for the specification of ICS commands and callback strings. If the client sends the command "gui drawinput client" it is responsible for all user input into the drawing area. SwiftInside still handles it's standard dialogs and scrollbars. "gui drawinput enable" allows the standard SwiftView user interface to be created, as configured by other gui commands, including the standard SwiftView keyboard and mouse user interface. "gui drawinput client" forces "gui controls display none". Conversely, "gui controls display rightbuttons" or "topbuttons" forces "gui drawinput enable". The default is "gui drawinput enable|gui controls display rightbuttons". (rightbuttons is an arbitrary choice). The "gui borderwidth" command is ineffective with "gui drawinput client"; the client controls the window border. SwiftInside draws directly to the supplied window if "gui drawinput client", else it creates and draws in a child window of the supplied window. It creates scrollbars as sibling windows of the drawing window as required, adjusting the drawing window size accordingly (note potential bug in "Known Problems" below). WM_PAINT and WM_SIZE messages are not forwarded to the client's message handler even if "gui drawinput client"; SwiftInside handles these events. The application can resize the draw window to it's normal size (e.g. when it's main window is resized), and SwiftInside will readjust it as required for the scrollbars. Because SwiftInside is attaching sibling scrollbars, it is not recommended that you pass an application main window to SwiftInside. The client is required to send the command "gui controls update" when it is finished sending gui configuration commands to cause the commands to take effect and the user interface to be (re-)constructed. This includes "gui inline disable"; the window supplied to CreateSwiftView is not used until "gui controls update" is sent. "gui controls update" may be resent at any time to reconfigure the user interface. THE USER INTERFACE IS NOT CONSTRUCTED INITIALLY WHEN AN Svobj IS CREATED. A "gui controls update" must be sent to display SwiftView. This is necessary to prevent the user from seeing the user interface reconfigure to the desired presentation. void CloseSwiftView(Svobj sview) Terminate the SwiftView instance. After the last instance is terminated, the library is not unloaded due to a known crash problem noted below. #ifdef __cplusplus } #endif Examples: A typical initialization sequence for a simple SwiftView instance with no controls, drawing into the client's own window, and allowing the client to handle drawing area input might be: Svobj sview = CreateSwiftView(mywindow, handler, "mylicense"); if (!sview) { sprintf("error creating SwiftView window: %s\n", error_string); /* error_string is set by the client's callback function */ return; } CmdSwiftView(sview, "ldoc mydoc"); CmdSwiftView(sview, "gui controls display none"); CmdSwiftView(sview, "gui drawinput client"); CmdSwiftView(sview, "gui controls update"); CmdSwiftView(sview, "draw"); ... CloseSwiftView(sview); Note that in this example, the client's gui commands will override those in a "mydoc" ICS file; this order is recommended but not required. Also note that draw should not be sent until after gui controls update. To allow SwiftInside to create the full standard SwiftView user interface in your window, send just the following commands: CmdSwiftView(sview, "ldoc mydoc"); CmdSwiftView(sview, "gui controls update"); CmdSwiftView(sview, "draw"); To create a simple SwiftView instance drawing into the external SwiftView window instead of a window in your application, pass NULL to CreateSwiftView, and do not send "gui controls display none": Svobj sview = CreateSwiftView(NULL, handler, "mylicense"); ... CmdSwiftView(sview, "ldoc mydoc"); CmdSwiftView(sview, "gui controls update"); CmdSwiftView(sview, "draw"); ... Alternatively, you can send "gui inline disable", and SwiftInside will draw a "ShowDoc" button in your window. Linking with C/C++ Applications Your C/C++ application should include svapi.h, and statically link with svapi[d].lib and svobj[d].lib to call the API. The API automatically loads the shared library as needed; it currently does not unload the shared library due to a known crash problem noted below. Svobj.dll or svobjd.dll statically links mfc42.dll or mfc42d.dll, version 6.0.8168. Statically linking avoids any runtime .dll dependencies, at the price of a 200K .dll instead of 50K. The debug libraries are provided for use in a C debugger, such as Visual Studio. They are built with debugger symbols, as well as useful debug printf messages written to the files $NDGDBUG_FILE and "svobj.log" in the directory of $NDGDBUG_FILE, which defaults to c:\ if writable, else $TMP. Sample Program dlltest.exe is a simple Windows application, built under Microsoft Visual Studio 5, that allows you to start and stop an instance of SwiftInside and send ICS commands to it. The instance by default draws into the test program's window. A scrolling window shows the ICS commands you have sent and the resulting callback strings. Attention: As noted above, the SwiftView user interface is not constructed until you press Start and send the "gui controls update" command (enter "gui controls update" in the Command: field and press return.) Examine the dlltest source for coding hints, particularly on how to write the callback handler so that it is thread-safe (see the SetScrollList function). Client-Controlled User Interface Implementation Examine the code in dlltest.c for an example of implementing mouse input. This test program implements the basic SwiftView "rubber band zoom". The client is free to set cursors, draw in the window, etc, as long as they are careful to properly interleave their drawing with SwiftInside's drawing actions. Visual Basic Usage SwiftInside can be used directly from Visual Basic 5.0 SP3, or from VB6 if you do not use a callback handler. It can also be used with earlier VB 5.0 versions, but has only been tested wth VB5 SP3 and VB6. Contact us if you have a requirement for other versions of VB, and we will consider qualifying SwiftInside for them. The rest of this section discusses how to call the .dll functions directly from your VB code. The demo in vbdlldemo/project1.vbp demonstrates all the basic capabilities. The external C language declarations for the functions in the .dll are similar to the above, but by different names and calling conventions: Svobj __stdcall Svobj_create(VWINDOW window, void (*handler)(Svobj, const char *), int poll_callbacks, int use_stdcall, int callback_bstr, long callback_id, const char *license); Svobj __stdcall Svobj_vbCreate(VWINDOW window, int poll_callbacks, const char *license); void __stdcall Svobj_cmd(Svobj sview, const char *cmds); void __stdcall Svobj_close(Svobj sview); char *__stdcall Svobj_getCallback(Svobj sview, int check); int __stdcall Svobj_vbGetCallback(Svobj sview, int check, char *buffer, int bufsiz); The caller is responsible for loading and unloading the .dll (In VB the Declare statement handles this for you). Otherwise, function usage is exactly as the C API; see the examples in the "C Library API" section above. In your VB module, declare these external functions as: Public Declare Function Svobj_create Lib "svobj" _ Alias "_Svobj_create@28" _ (ByVal hwnd As Long, _ ByVal cbfunc As Long, _ ByVal poll_callbacks As Integer, _ ByVal use_stdcall As Integer, _ ByVal callback_bstr As Integer, _ ByVal callback_id As Long, _ ByVal license As String) As Long Public Declare Function Svobj_vbCreate Lib "svobj" _ Alias "_Svobj_vbCreate@12" _ (ByVal hWnd As Long, _ ByVal poll_callbacks As Integer, _ ByVal license As String) As Long Public Declare Sub Svobj_cmd Lib "svobj" _ Alias "_Svobj_cmd@8" _ (ByVal sview As Long, _ ByVal cmds As String) Public Declare Sub Svobj_close Lib "svobj" _ Alias "_Svobj_close@4" _ (ByVal sview As Long) Public Declare Function Svobj_vbGetCallback Lib "svobj" _ Alias "_Svobj_vbGetCallback@16" _ (ByVal sview as Long, ByVal check As Integer, _ ByVal buffer as String, _ ByVal bufsiz as Integer) As Integer The aliases are necessary because of differences in __stdcall and __cdecl naming conventions. VB requires that the calls and callbacks follow the __stdcall convention, rather than __cdecl. VB should always set use_stdcall to non-zero to force the .dll to call the handler with the __stdcall convention. If use_stdcall is 0, the handler will be called according to __cdecl. Svobj_vbCreate is a simplified version of Svobj_create that configures the recommended options for VB use: NULL callback handler, use_stdcall=1, use BSTRs, callback_id unused. Svobj_close closes down SwiftView. We recommend that you call this function in the Form_QueryUnload event--if you place it in subsequent events (Unload or Terminate) VB may destroy windows that SwiftView is trying to operate on. Svobj_vbGetCallback is recommended to get callback strings because of the lack of support for callback functions in VB6. Svobj_getCallback is difficult to use from VB, and not recommended. A buffer size of 2620 is recommended for Svobj_vbGetCallback to handle the largest possible callback string. The caller will typically call Svobj_vbGetCallback from a timer, see form1.frm for an example. If VB calls a C function like Svobj_vbGetCallback() that takes a char * buffer, the C code simply fills the buffer with a NULL terminated string, and the VB code must do the following: - Define a string of the necessary buffer length, and fill it with NULL's. This is a very important step, because at this point this is an array of characters and not a Visual Basic String. - Call the function with this String. - After the call, use VB's Left() to convert the string into Visual Basic BSTR format: cb_str = String(2048, 0) cb_size = Svobj_vbGetCallback(SvInstance, 1, cb_str, Len(cb_str)) If cb_size Then ' Resize the string to the length returned by the dll cb_str = Left(cb_str, cb_size) ... If you really want to supply a callback function, read on, else skip the rest of this... Callback Functions in VB VB stores strings internally as the C-language type BSTR, but by default converts to char * (LPCSTR) when calling the API. If callback_bstr is non-zero, the .dll will do the conversion of char * return parameters to BSTR * for the callback handler function. If you are using a callback function, it must be declared with ByVal parameters: Public Sub CBFromDLL(ByVal svinst As Long, ByVal cbstring As String) where "CBFromDLL" is your callback function name. Your application should be built with Apartment Model threading if you supply a callback handler function. The .dll will call your callback on a separate thread; so you must use win32 API mechanisms such as CRITICAL_SECTION objects to syncronize any communcations between your callback and the rest of your VB code. Note - be careful in your win32 API declarations - the API text viewer can give bogus declarations, for example in the case of CRITICAL_SECTION. Always check your declarations against the win32 header files. To register a VB function as a callback, you can use the VB5 SP3 AddressOf() operator. A limitation on AddressOf(), however, is that it can only operate on functions that are in .bas modules. For ActiveX controls, this is a problem because code in .bas modules is shared by all instances of the control, and you will probably want to set a property, call a method or raise and event for a specific instance. MSDN topic Q179398 discusses a technique for working around the .bas file limitations. If the .bas file knows the address of the instance, the callback function can declare (thus instantiate) a "shadow object" and use the instance pointer to copy the instance's VTABLE to the shadow object. You can then get at the instance's properties, methods and events using the shadow object. If you are using the callback function (instead of polling), use the ObjPtr(Me) in your .ctl file to obtain a pointer to the object's instance. Pass this pointer in the callback_id field of Svobj_create. When the .dll calls the callback function it will return this pointer in the svinst parameter. You can then use it to set up the shadow object in your .bas file. If you call Svobj_create with a null callback_id, the .dll sets the callback function svinst parameter to the sview pointer. To recap, in the .ctl file, call Svobj_create with an instance pointer: Dim sv as Long 'SwiftView DLL handle--you'll 'need it to send commands and close sv = Svobj_create(hwnd, 'Drawing window handle AddressOf CBFromDLL, 'Callback function (in .bas) 0, 'Don't poll (use callback ptr) 1, 'Use __stdcall convention, this is VB 1, 'Have .dll convert C string to BSTR ObjPtr(Me), 'The address of this instance "licenseinfo") 'Your license string In the .bas module, Option Explicit 'Use an API function to do the copy, it's easier... Public Declare Sub CopyMemory _ Lib "kernel32" Alias "RtlMoveMemory" _ (pDest As Any, _ pSrc As Any, _ ByVal ByteLen As Long) 'Here's the callback function Public Sub CBFromDLL(ByVal svinst As Long, ByVal cbstring As String) '.dll is taking care of converting C style string to BSTR, but 'we must, must, must declare the String as ByVal!!! Also the svinst 'pointer!! 'Make the shadow object so we can hijack a VTABLE; type = your class name Dim ShadowObj As SwiftControl Dim dptr As Long On Error Resume Next 'svinst is the callback_id parameter from Svobj_create dptr = svinst 'Get the VTABLE (see MSDN Q179398) CopyMemory ShadowObj, dptr, 4 'Set a property, call a method raise an event, whatever.... ShadowObj.SVCallback = cbstring 'Destroy the Shadow Copy--This Is Important!!! CopyMemory ShadowObj, 0&, 4 Set ShadowObj = Nothing End Sub Java Usage The SwiftView package includes a Java class, SwiftInside, that encapsulates this API, implemented with Java Native Interface (JNI) code in the shared library. For more information on JNI, read "Java Native Interface" by Rob Gordon. The package was compiled under JDK1.17b; compatibility with other Java versions has not been tested. Currently, it cannot display a document in a Java window; only "partner" mode is supported. Ultimately, the Java object should be supportable under all Java virtual machines that support JNI - this includes Sun Java VM, (including the plug-in), Netscape after 4.03, and the latest Microsoft VM shipped with Internet Explorer in December 1998. The initial implementation is on win95/NT only, but we also expect to support Solaris and possibly HP/UX, if HP's Java support permits. We are investigating making a SwiftView Java Bean. Please email marketing@swiftview.com if you are interested. The SwiftView.SwiftInside class exposes the following methods: public native SwiftInside(boolean poll_callbacks, String license) Construct a controllable SwiftInside object. The object functions in "partner" mode. A separate SwiftView window is displayed, with it's usual default exit and file open buttons removed. The SwiftView window is initially hidden, and is made visible on the "gui controls update" command. The caller can hide or display the window as desired, using gui winstate commands. If poll_callbacks is true, the client will be calling getCallback() or vbGetCallback() to get callback strings. license, if non-NULL, is a customer-specific license string provided by SwiftView, Inc. that must match the customer name recorded by SwiftView, Inc. in the customer's copy of sview.exe. If license is NULL, SwiftInside requires a LAN license or must be a limited-time demo copy. The standard plug-in-style initialization ICS commands "hpgl startsoon off|set browser iexplore" are sent when SwiftView is started. No exception is thrown if the construction fails; instead an error message string "SVC:ERROR:" is returned to a call to getCallback() or vbGetCallback(), and other calls to the created object do nothing. public native void sendCommands(char *cmds) Send an ICS command string to the object. See CmdSwiftView() above for details on the commands. Any errors are reported in a callback; an error dialog is also displayed. This dialog can be suppressed with the ICS command "gui dialogs disable", but note that if this is done the GUI:MESSAGE, OUTPUT:START, and OUTPUT:START callbacks that pop dialogs must be implemented by the client. public native String getCallback(boolean check) This function returns as its value one callback line with the terminating newline removed. If no complete callback line is available, it returns null if check is true, else it blocks until one is available. Note that null may be returned if the function is called before you send the first ICS command to the object. See the SwiftView Technical Reference Manual for the specification of callback strings. Normally, the client starts a separate callback reader thread that continuously calls getCallback with check == false. If the client allows a significant amount of callback data to accumulate unread, the callback pipe may break. See the C/C++ examples for the proper ICS initialization commands for your desired usage. javatest.class is a simple test program - run java javatest. svobj.dll must be installed as discussed above. See javatest.java for details. Licensing Unlike SAX or the plug-in, Svobj does not check licensing - licensing is deferred to the standalone sview.exe run under it. Therefore it does not have to be ndgmodded for licensing purposes. (svobj.dll's copyright dialog (F10) tells the user to hit F11 for licensing info.) Standalone sview can be licensed with the standard sview LAN standalone license, a SwiftStamped file, or a customer-specific license string generated by SwiftView, Inc. This string should be embeded in the user's application and passed to the CreateSwiftView() call, which in turn passes it to the sview -lic command line option. sview checks it against the customer's name embedded in it. SwiftView will provide a special version of sview.exe to you when you purchase your license that matches your license string. Note that this license string is identical to that for sview -lic, so one license string licenses both standalone and embedded SwiftView. The LAN license is found according to the same rules as standalone SwiftView: the value of the registry entry HKEY_LOCAL_MACHINE\SOFTWARE\SwiftView\lanlicense (version 5.3 or later), else the "lanlicense" parameter in the Windows default sview.ini file, else "sview.lic" in $NDGUTIL, else in the same directory as sview.exe. Note that unlike SAX or plug-in, the PATH is not searched for a LAN license. The result is that the LAN license must be located wherever sview.exe is found by the dll, unless you use the "lanlicense" registry entry or parameter. LAN license installations should set this registry entry on all client systems, using an install-time batch script which then runs the SwiftView installer. This insures that sview.exe is installed only on the LAN server. See http://swiftview.com/tech/techfaq.htm#LanLicense for details and installation procedures. Fixed bugs ver 610: SwiftInside no longer leaks win32 handles when sview.exe is started or stopped. Known bugs On win95/98, repeated dll load-unload sequences may crash. A C/C++ application should never unload the dll. If the application uses "gui drawinput client" mode and responds to a resize of the draw window by resizing the draw window, SwiftInside and the application may enter an oscillating fight, resulting in strange draw window sizes and crashes. Watch the DVIEW callbacks for this. Sending "gui scrollbars display none" with "gui drawinput client" will eliminate the problem. An application built using callbacks with VB6 will crash - AddressOf is not supported by VB6. No "ShowDoc" button is displayed in the supplied window in Partner mode. Under Java, killing the program leaves SwiftView running. For a while, when licensing failed, licensing and other dialogs didn't display. UI-less client draw windows with borders not handled correctly - the scrollbars cover up the border, and it is not redrawn when the scrollbars are removed. For now, client draw windows with borders are not recommended. A similar problem can occur when moving the button bar. Resizing the client window with scrollbars on occasionally fails to adjust the draw window to the new size. Horizontal scrollbar not adjusting properly on window resize in wide mode; sometimes is on when it should be off. Sometimes when re-enabling scrollbars from the toolbar dialog the scrollbars and drawing are not redrawn.