Page History
...
Much of the following discussion will be familiar to veteran OpenInsight programmers. This is especially true for any who have already reviewed the OI 4.1 New Features.pdf document (see the References section the References section below) that came with the first 4.1 upgrade. If you are generally comfortable with the distinctions between the old and new OpenEngine then you may want to go directly to the Getting Started section the Getting Started section below.
From its humble v1.0 beginnings, OpenEngine (i.e. OENGINE.exe) was designed so that it could stand alone and communicate with a programmer’s IDE of choice. This essentially gave the developer a “client/server” approach to designing OpenInsight applications. As a result, less effort was put into the OpenInsight toolset in lieu of focusing on how OpenEngine can be used by Microsoft C, Visual Basic, Turbo Pascal, and other Windows application tools.
...
Error | Returns error number or 0 if no error (see the References section below.) | ||||||
hEngine | Returns the handle to the engine connection if successful. Variable should be initialized to null beforehand. | ||||||
Server | Location and name of the engine. Format is should be as follows: \\ServerLocation\ServerName Examples of connecting to an engine using TCP/IP port #777: \\208.252.203.252:777 (Connect to an engine on an IP address) \\ISUPPORT.SRPCS.COM:777 (Connect to an engine on a URL) \\.:777 (Connect to an engine on the local machine) Examples of connecting to an engine using the REP named pipe: \\208.252.203.252\REP (Connect to an engine on an IP address) \\ISUPPORT.SRPCS.COM\REP (Connect to an engine on a URL) \\.\REP (Connect to an engine on the local machine) | ||||||
Database | The database (.DBT file) the engine should be connected to, like SYSPROG or EXAMPLES. | ||||||
CreateEngine | Flag(s) to determine how the remote engine should be connected to. These are included in the REVCAPI_EQUATES insert (see the References section below) and are described as follows:
Another flag, which is undocumented, allows a dynamically launched OpenEngine to be invisible while it is running. This is helpful when the client needs to run local remote engines and the developer does not want the end user to see them on the desktop:
Example of how to use this flag to hide the remote engine:
| ||||||
ShutDown | Boolean flag to determine if the engine should close itself automatically after the connection is done. This only affects dynamically launched remote engines. We recommend always setting this to 1. There appears to be no benefit keeping the engine open because subsequent requests will not be accepted until it has been closed and created again. |
...
Error | CreateQueue(hQueue, hEngine, QueueName, Database, UserName, Password) |
Error | Returns error number or 0 if no error (see the References section below.) |
hQueue | Returns the handle to the queue if successful. Variable should be initialized to null beforehand. |
hEngine | The handle returned by the CreateEngine function. |
QueueName | Set to null. |
Database | The database (.DBT file) the engine should be connected to, like SYSPROG or EXAMPLES. Use the same variable/value as in the CreateEngine function. |
UserName | Any valid username for the database being connected to. |
Password | The password for the username being used. |
...
Any Last Requests?
...
Once a queue has been established, we are clear to submit a request to our remote engine to execute a stored procedure. While most system stored procedures can be executed through a remote engine, one will normally call custom subroutines and functions in production. There are a few caveats when working with remote engines. Please note the following:
- “Out of process” engines do not run in event context. This means commands like Set_Property, Utility, and OIPI calls will not work. Anything GUI and obviously event logic will not run.
- Active selects will not be available once an engine has completed its request and closed the connection. However, one remote request can save a select using the Save_Select command while another remote request activates it using the Activate_Save_Select command. Developers will most likely have a remote request perform the long database query and Save_Select command while allowing the local “in-process” engine to handle the Activate_Save_Select when event context processes (like an OIPI report) can be executed.
- Remote engines only have access to resources that are local to the machine running the engine. For instance, using OS-based commands like Drive and DirList will work within the local settings of that machine. In some cases this is desirable. For instance, the remote engine might have access to a drive that is not shared over the network. Hence, a remote request function could provide extremely controlled access to any information (database or OS files) on that drive. Local clients will be prevented from direct access from both the application and network.
...
Remote requests are made with the CreateRequest function:
Error | CreateRequest(hRequest, hQueue, ExecLine, Arg1, Arg2, Argn, …) | ||||
Error | Returns error number or 0 if no error (see the References section below.) | ||||
hRequest | Returns the handle to the request if successful. Variable should be initialized to null beforehand. | ||||
hQueue | The handle returned by the CreateQueue function. | ||||
ExecLine | Remote process to execute. Use the same syntax as when entering commands in the System Monitor and the System Editor’s Exec line:
Note: Any command line that is passed through the ExecLine parameter will be a literal string. Here is an example of how this might look:
As this example demonstrates, parameters are always literals. Addtionally, CreateRequest does not wait for the remote request to finish processing. Therefore we do not have the ability to detect changes that are made to these parameters as we normally would when passing parameters by reference. If this is a requirement then there are two options: 1.) Design the remote request to send back changes through the normal return data process (see theTwiddling Our Thumbs section below), or 2.) use the previously mentioned CallFunction or CallSubroutine commands instead (see the Requests the Requests for Dummies section Dummies section below.) Using these commands, however, means that our local engine will be waiting for the remote engine to finish processing. While this provides us the ability to connect to remote engines, it defeats our purpose of “multitasking”. | ||||
Arg1…Argn | Actual arguments to pass into the ExecLine if replaceable parameters are used. In the example used above we embedded the RoutineName parameters in the ExecLine string. CreateRequest can also work with replaceable parameters. Using the CHECKLEGALUSER command this alternative syntax would look like this:
Note that the replaceable parameters were not quoted. Otherwise, RemoteRequest will treat them as the literal values to be passed. |
We recommend using the replaceable parameters for remote requests rather than including them directly in the ExecLine string. In addition to making it easier to code, it adds a layer of security. This can be clearly seen by comparing the following two screen shots:
If the machine running the remote engine is in public view then it is possible for sensitive information, like passwords, will be seen in the OpenEngine command log. Using replaceable parameters eliminates this problem. For even more security, remote engines can be launched invisibly using the /HE command line flag:
C:\OI701\OENGINE.exe /SN=REP /HE EXAMPLES
...
Invisible engines, however, must be shutdown using the Task Manager’s Process tab.
Requests for Dummies
...
For a simpler and quicker way to make a request to a remote engine we can use two alternative commands: CallFunction andCallSubroutine. In the Getting the Big Picture section above, our chart indicated that these commands replace four separate steps (and a lot of code) that is required when using CreateRequest. This convenience, however, comes at a price: our local engine is suspended until the remote request is completed. Therefore, if you don’t have the time to put together a generic remote request utility that uses CreateRequest, then these commands might be the solution you need:
Error | CallFunction(hQueue, ReturnValue, Function, Arg1, Arg2, Argn, …) | ||
Error | Returns error number or 0 if no error (see the Reference section below.) | ||
hQueue | The handle returned by the CreateQueue function. | ||
ReturnValue | The value returned by the remote function. | ||
Function | Remote function to execute. Unlike CreateRequest, the RUN command is not needed nor is it supported. If the function is local to the database that was specified in the CreateQueue function then only the function name is required. Otherwise, append an asterisk and the application name to the function name:
| ||
Arg1…Argn | Arguments (parameters) for the remote function. Unlike RemoteRequest, these arguments can be passed by reference. That is, any changes made to the original values in the remote subroutine will be returned to the variables used in our calling routine. |
...
Error = CallSubroutine(hQueue, Subroutine, Arg1, Arg2, Argn, …)
Error | Returns error number or 0 if no error (see the References section below.) | ||
hQueue | The handle returned by the CreateQueue function. | ||
Subroutine | Remote subroutine to execute. Unlike CreateRequest, the RUN command is not needed nor is it supported. If the function is local to the database that was specified in the CreateQueue function then only the subroutine name is required. Otherwise, append an asterisk and the application name to the subroutine name:
| ||
Arg1…Argn | Arguments (parameters) for the remote subroutine. Unlike RemoteRequest, these arguments can be passed by reference. That is, any changes made to the original values in the remote subroutine will be returned to the variables used in our calling routine. |
...
Twiddling Our Thumbs
...
Once RemoteRequest has successfully executed our process, it is our responsibility to monitor the status of the remote engine. Two functions are available for this: WaitForReply and PollForReply. In the Getting the Big Picture chart above only PollForReply is mentioned. This is because WaitForReply does exactly what its name implies; that is, it waits for a reply from the remote engine before proceeding. Therefore, if our remote process is busy performing a long process then our local engine will be forced to wait until it is completed. Actually, to be technically correct, if the remote process uses the Send_Info or Send_Dyn commands to send information back then WaitForReply will allow the local engine to receive it. WaitForReply must then be called again to wait for the next information update or until the remote process has completed. Because many processes do not use Send_Dyn or Send_Info (like RList selects), WaitForReply is only a little better than using CallFunction or CallSubroutine.
PollForReply, on the other hand, provides true asynchronous processing which gives us the ability to allow our local engine to perform other tasks while the remote engine is performing its duties. Like WaitForReply, PollForReply will be notified if the remote process uses the Send_Info or Send_Dyn commands.
Error = PollForReply(hRequest, Status, Reply)
Error | Returns error number or 0 if no error (see the References section below.) |
hRequest | The handle returned by the CreateRequest function. |
Status | Flag to determine the status of the remote process. These are included in the REVCAPI_EQUATES insert (see the References section below) and are described as follows:equ UNPROCESSED$ to 0 ;* Server has not begun request. |
Reply | This is supposed to contain information that has been returned from the remote engine. However, there is a known bug where this information is actually returned to the Status parameter instead. Fortunately this parameter is optional and we can use two other functions, GetReply and GetResponseText, to get this information. |
...
A looping mechanism must now be considered that makes the call to PollForReply. We will use the Status flag to determine if the remote process is done (either because our remote process successfully ended or there was a procedural error) and end our loop. Otherwise, the remote process is still executing. Here is a simple loop to demonstrate how this might look:
Code Block |
---|
Loop Error = PollForReply(hRequest, Status) Begin Case Case Error * An error was returned with the PollForReply function. Retrieve * error text and shutdown connection to remote engine. GoSub Process_Error GoSub Request_Done GoSub Return_Results Case Status EQ PROCESSING$ * Only used with PollForReply. Indicates that the remote engine is * still processing our request. Case Status EQ DATA_AVAILABLE$ * Information is available when the remote engine uses the Send_Dyn * routine in the request. GoSub Process_Status Case Status EQ INFO_AVAILABLE$ * Information is available when the remote engine uses the * Send_Info routine in the request. GoSub Process_Status Case Status EQ INFO_REQUEST$ * The remote engine is requesting information from the client using * the Req_Info routine. Use the SendResponse function to reply. Case Status EQ COMPLETED$ * The remote engine has completed our request (i.e. the end of the * program has been reached.) GoSub Process_Status GoSub Request_Done GoSub Return_Results Case Status EQ PROC_ERROR$ * An error occured in the remote engine request. Any Set_Status * error codes will be returned. GoSub Process_Status GoSub Request_Done GoSub Return_Results End Case Yield() Until Error OR Status EQ COMPLETED$ OR Status EQ PROC_ERROR$ Repeat |
...
Note the use of the Yield command within each iteration. This is necessary to give the local engine the ability to process other local requests and, consequently, give us the ability to simulate “multitasking”. Alternatively one could design a window (running invisibly) to use the TIMER property and event to facilitate the looping mechanism. While this makes our coding more complex, it offers two advantages:
- The ability to control the pace of each iteration with the delay parameter of the TIMER property while
- putting the burden of the wait mechanism on the operating system rather than OpenInsight.
...
There are now four more areas that need to be covered. These are represented by our Process_Status, Process_Error, Request_Done, and Return_Results GoSub references above.
What’s Our Status?
...
Each call to the PollForReply function will set the Status flag. If there was a procedural error in the remote request then we will use the GetStatusText function to retrieve the full error text. Otherwise, we will use the GetReply function to retrieve any data that is being returned. Here is an example of how this section of code might look:
Code Block |
---|
Process_Status:
If Status EQ PROC_ERROR$ then
* A procedural error occured in the remote request. Use GetStatusText to
* retrieve the entire error message.
Error = GetStatusText(hRequest, @RM, StatusText)
end else
* Data/Information available. Use GetReply to retrieve the entire text.
Error = GetReply(hRequest, StatusText)
end
If Error then
GoSub Process_Error
end else
If StatusText[-1, 1] EQ \00\ then StatusText[-1, 1] = ""
*
* StatusText now contains data which has been returned using Send_Info,
* Send_Dyn, or the remote function’s Return command. Put whatever logic is
* desired to manage this information here. Keep in mind that if data
* received before the Return is needed in the Return_Results then we need
* to find a way of storing this away until then.
*
end
return |
...
return |
Error Messages
...
Whenever any of our functions returns a value other than 0 we need to compare this with the list of errors from the Error Definitions (REVCERRS.H) file (see the [[1]] section below for a complete list.) Here is a portion of code that can be used:
Code Block |
---|
Process_Error: Begin Case Case Error EQ 0 ErrorText = "Successfully Completed" Case Error EQ 1 ErrorText = "Error 1: Invalid name" . . Put the other error definitions here. . Case Error EQ 2001 ErrorText = "Error 2001: REV_ERR_GET_PROC_ADDRESS32" Case Error EQ 2002 ErrorText = "Error 2002: REV_ERR_NO_REVCAP32" End Case return |
...
Once we have the error description we then need to make the end user aware of the problem. Use whatever method works best for your application (i.e. message box, statusline, error log report, etc.)
Closing Shop
...
After our remote request is done processing (or in the event of a premature error) we need to properly close our connections. This is perhaps the easiest portion of our code (Note: these must be the same handles that were returned by their respectiveCreateEngine, CreateQueue, and CreateRequest functions):
Code Block |
---|
Request_Done: CloseRequest(hRequest) CloseQueue(hQueue) CloseEngine(hEngine) return |
...
So Now What?
...
Phew, we finally made it to the end. However, we still might need to do something with the returned data. For instance, if our remote request performed a large table select it might have saved the results using the Save_Select routine and returned the name it was saved under. Our local engine, having received the select name back, can then activate it with the Activate_Save_Selectroutine for a report.
If we had put all of our above code into a nice tidy routine (i.e. our “black box”) then we have a couple of options available to us. Let’s assume we create a function called Remote_Request:
Code Block |
---|
ReturnValue = Remote_Request(Server, CreateEngine, HideEngine, Database, UserName, -> Password, ExecLine, Arguments, CallBack) |
...
One approach would be to call our function within another routine that needs the information being returned (e.g. a report as suggested above.) Thus, our calling routine will wait until the remote request is done processing yet our application will be free to run other processes (due to the embedded Yield command in our status loop.) When the remote request is done processing, our calling routine can then automatically move on or prompt the end user if it is okay to continue.
Alternatively we can have Remote_Request call another routine for us. This is why the CallBack parameter has been included in theRemote_Request syntax. Ultimately, then, the Call @SubName command will be needed within Remote_Request to make this work.
With this final piece tied together you now have a “black box” approach toward managing remote engines. While this does not exhaust all that is possible with remote engines, we believe this will give many applications a significant enhancement boost in functionality. We hope that you will agree.
References
...
Further information we think will be helpful are listed here for your convenience:
1. OI 4.1 New Features.pdf. This document is still available from the Works Download page on Revelation Software’s website. This can be found under the Updated!! OpenInsight Version 4.1 Upgrade link. There are, in fact, two links with the same name. Both will work. Most of the information in this document can also be found in The OpenInsight OpenEngine help file (OpenEngine.chm), which installs in the same directory as OpenInsight.exe.
2. OENGINE_DLL prototype record. This allows the developer to use Basic+ to connect with remote engines. Note: If you are running OpenInsight v7.0 or higher, this record should already be current:
Code Block |
---|
OENGINE.EXE * * internal OEngine functions as defined in rev_call.c module * (see functions CallDLL and CallOEFunc) * VOID INTERNAL POINTER_02(VOID) AS RTP90 VOID INTERNAL POINTER_03(VOID) AS C_PACK VOID INTERNAL POINTER_04(VOID) AS BLD_TEMPLATE VOID INTERNAL POINTER_05(VOID) AS OEPUTDATA VOID INTERNAL POINTER_06(VOID) AS OEREQINFO VOID INTERNAL POINTER_07(VOID) AS OEPUTSTAT VOID INTERNAL POINTER_08(VOID) AS OEGETCLIENTSTAT VOID INTERNAL POINTER_09(VOID) AS RETSTACK VOID INTERNAL POINTER_22(VOID) AS SET_BGND_IX_TIME VOID INTERNAL POINTER_23(VOID) AS GETIOFLAG VOID INTERNAL POINTER_24(VOID) AS SETIOFLAG VOID INTERNAL POINTER_25(VOID) AS GETENGINEWINDOW VOID INTERNAL POINTER_26(VOID) AS GETHANDLECOUNT VOID INTERNAL POINTER_27(VOID) AS SETINITDIROPTIONS VOID INTERNAL POINTER_28(VOID) AS ISEVENTCONTEXT * OI 7 - added Remote Engine functions VOID INTERNAL POINTER_30(VOID) AS CREATEENGINE VOID INTERNAL POINTER_31(VOID) AS CREATEQUEUE VOID INTERNAL POINTER_32(VOID) AS CREATEREQUEST VOID INTERNAL POINTER_33(VOID) AS POLLFORREPLY VOID INTERNAL POINTER_34(VOID) AS WAITFORREPLY VOID INTERNAL POINTER_35(VOID) AS GETREPLY VOID INTERNAL POINTER_36(VOID) AS SENDRESPONSE VOID INTERNAL POINTER_37(VOID) AS GETSTATUSTEXT VOID INTERNAL POINTER_38(VOID) AS CLOSEREQUEST VOID INTERNAL POINTER_39(VOID) AS CALLSUBROUTINE VOID INTERNAL POINTER_40(VOID) AS CALLFUNCTION VOID INTERNAL POINTER_41(VOID) AS CLOSEQUEUE VOID INTERNAL POINTER_42(VOID) AS CLOSEENGINE * end |
...
If you had to update OENGINE_DLL on your system then log into the SYSPROG application and run the following command from the Exec line in the System Editor or from the command line in the System Monitor:
Code Block |
---|
RUN DECLARE_FCNS "OENGINE_DLL" |
...
If this is successful, several messages like, “$CLOSEENGINE written to file SYSOBJ.” will appear.
3. REVCAPI_EQUATES insert record. This insert contains the necessary declarations for those functions defined in the OENGINE_DLL prototype record. Additionally common equates have also been defined. If you are running OpenInsight 7.0 or higher, this record should be current with the exception of one equate:
Code Block |
---|
equ REV_CREATE_ENGINE_NO_UI$ to 0x040 |
...
However, the version below includes this and all other necessary equates:
Code Block |
---|
compile Insert REVCAPI_EQUATES // CreateEngine Constants * Connect to an existing engine if possible, else create a new one. * Use this to create an engine connected to another database. equ CREATE_ENGINE_OPEN_EXISTING$ to 0x000 * Always create a new engine. * Engine will be created on the same machine as the calling engine. equ CREATE_ENGINE_CREATE_NEW$ to 0x001 * Only connect to engines that already exist. * This is the best way to connect to a remote engine. equ CREATE_ENGINE_OPEN_ALWAYS$ to 0x002 * Create an engine in indexing mode. equ CREATE_ENGINE_INDEXER$ to 0x010 * Create an engine that will not shut down when the connections end. equ CREATE_ENGINE_WAIT_ON_CLOSE$ to 0x020 * Keep the engine invisible (e.g. BitOr(CREATE_ENGINE_OPEN_ALWAYS$, REV_CREATE_ENGINE_NO_UI$) equ REV_CREATE_ENGINE_NO_UI$ to 0x040 equ UNPROCESSED$ to 0 ;* Server has not begun request. equ PROCESSING$ to 1 ;* Server is processing request. equ DATA_AVAILABLE$ to 2 ;* Server has data available. equ COMPLETED$ to 3 ;* Server has completed request, status ;* information is available. equ PROC_ERROR$ to 4 ;* Server process failed, status information is ;* available. equ INFO_AVAILABLE$ to 5 ;* Server has intermediate status information ;* available. equ INFO_REQUEST$ to 10 ;* Server is requesting information from client. declare function CreateEngine declare function CreateQueue declare function CreateRequest declare function PollForReply declare function WaitForReply declare function GetReply declare function SendResponse declare function GetStatusText declare function CloseRequest declare subroutine CloseRequest declare function CallSubroutine declare function CallFunction declare function CloseQueue declare subroutine CloseQueue declare function CloseEngine declare subroutine CloseEngine |
...
4. Error Definitions. Our thanks to Pat McNerthney for making available these undocumented definitions for the numerical errors that can occur. They have been presented in a Begin/End Case syntax for convenience:
Code Block |
---|
Begin Case Case Error EQ 0 ErrorText = "Successfully Completed" Case Error EQ 1 ErrorText = "Error 1: Invalid name" Case Error EQ 2 ErrorText = "Error 2: Out of memory" Case Error EQ 3 ErrorText = "Error 3: Unable to locate server" Case Error EQ 5 ErrorText = "Error 5: Functionality not implemented" Case Error EQ 6 ErrorText = "Error 6: GlobalLock failed" Case Error EQ 7 ErrorText = "Error 7: Null pointer passed to function" Case Error EQ 8 ErrorText = "Error 8: Unknown request status" Case Error EQ 9 ErrorText = "Error 9: Unable to retrieve argument" Case Error EQ 10 ErrorText = "Error 10: Too many requests active" Case Error EQ 11 ErrorText = "Error 11: Invalid request handle" Case Error EQ 12 ErrorText = "Error 12: Invalid DLL procedure name" Case Error EQ 13 ErrorText = "Error 13: File not found" Case Error EQ 14 ErrorText = "Error 14: Undefined error" Case Error EQ 15 ErrorText = "Error 15: Queue does not exist" Case Error EQ 16 ErrorText = "Error 16: The server is not responding" Case Error EQ 17 ErrorText = "Error 17: Max # of clients exceeded for Queue" Case Error EQ 18 ErrorText = "Error 18: Connection to queue failed" Case Error EQ 19 ErrorText = "Error 19: Failed to send message" Case Error EQ 20 ErrorText = "Error 20: Invalid queue handle" Case Error EQ 21 ErrorText = "Error 21: Invalid parameter passed" Case Error EQ 22 ErrorText = "Error 22: Client-server handshaking error" Case Error EQ 23 ErrorText = "Error 23: Script is incomplete" Case Error EQ 24 ErrorText = "Error 24: Data is not available" Case Error EQ 25 ErrorText = "Error 25: Error accessing template" Case Error EQ 26 ErrorText = "Error 26: Invalid column; column not avail" Case Error EQ 27 ErrorText = "Error 27: Invalid type" Case Error EQ 28 ErrorText = "Error 28: Maximum queues exceeded" Case Error EQ 29 ErrorText = "Error 29: The buffer is too small for data" Case Error EQ 30 ErrorText = "Error 30: Invalid Send or Receive sequence" Case Error EQ 31 ErrorText = "Error 31: Invalid user name" Case Error EQ 32 ErrorText = "Error 32: Invalid password" Case Error EQ 33 ErrorText = "Error 33: Invalid pathname" Case Error EQ 34 ErrorText = "Error 34: Invalid database name" Case Error EQ 35 ErrorText = "Error 35: Max # of engines already running" Case Error EQ 36 ErrorText = "Error 36: Unable to start engine" Case Error EQ 37 ErrorText = "Error 37: Unable to shutdown engine" Case Error EQ 38 ErrorText = "Error 38: Unable to create new engine" Case Error EQ 100 ErrorText = "Error 100: REV_ERR_RCLINITIALIZE" Case Error EQ 101 ErrorText = "Error 101: REV_ERR_RCLVERSION" Case Error EQ 102 ErrorText = "Error 102: REV_ERR_RCLOPENCLIENT" Case Error EQ 103 ErrorText = "Error 103: REV_ERR_RCLCONNECT" Case Error EQ 104 ErrorText = "Error 104: REV_ERR_RCLREQUESTREPLY" Case Error EQ 105 ErrorText = "Error 105: REV_ERR_RCLDISCONNECT" Case Error EQ 106 ErrorText = "Error 106: REV_ERR_RCLCLOSECLIENT" Case Error EQ 107 ErrorText = "Error 107: REV_ERR_RCLTERMINATE" Case Error EQ 108 ErrorText = "Error 108: REV_ERR_RCLCHECKSERVER" Case Error EQ 200 ErrorText = "Error 200: REV_ERR_HEAPCREATE" Case Error EQ 201 ErrorText = "Error 201: REV_ERR_HEAPFREE" Case Error EQ 202 ErrorText = "Error 202: REV_ERR_HEAPDESTROY" Case Error EQ 203 ErrorText = "Error 203: REV_ERR_CREATEEVENT" Case Error EQ 1000 ErrorText = "Error 1000: REV_ERR_INVALID_ENGINE_HANDLE" Case Error EQ 1001 ErrorText = "Error 1001: REV_ERR_ENGINE_BUSY" Case Error EQ 1002 ErrorText = "Error 1002: REV_ERR_DATABASE_INIT" Case Error EQ 1003 ErrorText = "Error 1003: REV_ERR_LOGON" Case Error EQ 1004 ErrorText = "Error 1004: REV_ERR_NO_SERVER_MEMORY" Case Error EQ 1005 ErrorText = "Error 1005: REV_ERR_REQUEST_ACTIVE" Case Error EQ 1006 ErrorText = "Error 1006: REV_ERR_REQUEST_NOT_ACTIVE" Case Error EQ 1007 ErrorText = "Error 1007: REV_ERR_UNEXPECTED_PARAMETER" Case Error EQ 1008 ErrorText = "Error 1008: REV_ERR_MISSING_OBJECT" Case Error EQ 2000 ErrorText = "Error 2000: REV_ERR_LOAD_REVCAP32" Case Error EQ 2001 ErrorText = "Error 2001: REV_ERR_GET_PROC_ADDRESS32" Case Error EQ 2002 ErrorText = "Error 2002: REV_ERR_NO_REVCAP32" Case 1 ErrorText = "Unknown Error: ":Error End Case |
...
5. OpenEngine Reference Manual. This is more for developers who are comfortable with programming in other IDEs, especially C++, to communicate through the OpenEngine API. A free copy of this can be downloaded from Revelation Software’s Knowledge Base at this link: http://www.revelation.com/o4wtrs/oecgi3.exe/O4W_RUN_FORM?INQID=KB_ARTICLE_INQUIRY&SEARCH_1=607983FEBE650BC985256957005921B6&SEARCH=1#/section/breadcrumb/UPDATETABLE/Display