Basic+ is a dynamically-typed language, and sometimes the system will attempt to change the type of a variable before using it in an operation. For example, if we have variable containing a numeric value as a string, and we attempt to compare it to a variable containing an actual numeric value, then the former will be coerced to a numeric format before the comparison, e.g.:
StrVar = "3" ; // String representing an integer
NumVar = 7 ; // Integer
Ans = ( StrVar = NumVar ) ; // Ans is FALSE$ and StrVar
; // is converted to an Integer
This works well in the vast majority of cases, but causes issues when attempting to compare strings that that could be numbers but really aren’t for the purposes of the comparison. For example, consider the following:
Var1 = "12" ; // String
Var2 = "012" ; // String
Ans = ( VarA = VarB ) ; // Ans = TRUE$ - should be FALSE$!!
The system first attempts to coerce the operands to a numeric format, and of course it can because they both evaluate to the number 12, and are therefore treated as equal. If you wish to compare them both as strings, the usual solution has been to change them so that they cannot be converted to a number, usually by prefixing them with a letter or symbol like so:
Var1 = "12" ; // String
Var2 = "012" ; // String
Ans = ( ( "X" : VarA ) = ( "X" : VarB ) ) ; // Ans = FALSE$
Although this works it hurts performance and can be a cumbersome solution.
OpenInsight 10.2 introduces a new set of extended comparison operators to Basic+ that ensure both operands are coerced to a string type (if needed) before being evaluated, and provides the following benefits:
Removes the need for any concatenation thereby avoiding unnecessary copying
Removes the need to check if a variable can be a number before the comparison
Makes it obvious that you are performing a “string-only” comparison so your code looks cleaner.
The new (case-sensitive) operators are:
Operator
Description
_eqs
Equals operator
_nes
Not Equals operator
_lts
Less Than operator
_les
Less Than Or Equals operator
_gts
Greater Than operator
_ges
Greater Than Or Equals operator
There is also matching set of case-insensitive operators too:
Operator
Description
_eqsc
Case-Insensitive Equals operator
_nesc
Case-Insensitive Not Equals operator
_ltsc
Case-Insensitive Less Than operator
_lesc
Case-Insensitive Less Than Or Equals operator
_gtsc
Case-Insensitive Greater Than operator
_gesc
Case-Insensitive Greater Than Or Equals operator
So we could write the previous example like so:
Var1 = "12" ; // String
Var2 = "012" ; // String
Ans = ( VarA _eqs VarB ) ; // Ans = FALSE$
Hopefully you’ll find this a useful addition when performing string-only comparisons in your own applications.
One of the most popular “raw” Windows API functions that OpenInsight developers have used over the years is the ShellExecute function, which allows you to launch an application via its filename association, e.g. you can launch Word by using a document file name, or Excel using a spreadsheet filename and so on.
However, because it was never really made an “official” part of the product (it was normally passed on in forums), developers were left to create their own DLL Prototype definitions in order to use it – this gave rise to many variations over the years, many of which were not compatible with others. For example, some use LPCHAR as an argument type, some use LPSTR or LPASTR, whilst others use LPVOID with GetPointer(); some definitions use the “Wide” version of the function, some the “Ansi” version, and there are many different aliases, with or without the “A/W” suffix too. The list goes on.
For OpenInsight 10 we decided that we couldn’t move forward with this as we would run the risk of conflicting with established applications, so we moved all of the DLL Prototypes we used into a new namespace called “MSWIN_” and claimed it as our own. This left developers to bring forward their own DLL prototypes into version 10 as and when needed, and therefore we didn’t supply a “ShellExecute” function as such, though we did supply “MsWin_ShellExecute” instead (see below).
Another decision we took was to try and move away from the need for developers to use raw Windows API function calls as much as possible, as some of them can be complex and require knowledge of C/C++ programming, which is not necessarily a skill set that everyone has the time or desire to learn. Ergo, we moved a lot of functionality into the Presentation Server (PS) and created some Basic+ wrapper functions around others to shield developers from the sometimes gory internals.
(We also chose to use the “W” versions of functions rather than the “A” versions where possible, because these would translate better when in UTF8 mode and remove the need for an extra “A”->”W” conversion in Windows itself.)
So, coming back to ShellExecute, and in light of the above, we have three “official” and supported ways of calling it in OpenInsight 10 as detailed below:
The SYSTEM object SHELLEXEC method
The RTI_ShellExecuteEx stored procedure
The MSWin_ShellExecute DLL Prototype stored procedure
The SYSTEM object SHELLEXEC method
If your program is running in “Event Context”, (i.e. it is executing in response to an event originating from the PS) then you may use the SYSTEM SHELLEXEC method which invokes ShellExecuteW internally.
Name of a form to use as a parent for displaying UI messages.
Operation
No
Operation to be performed; “open”, “edit”, “print” etc.
File
Yes
File to perform the operation on.
Parameters
No
If File is an executable file this argument should specify the command line parameters to pass to it.
WorkingDir
No
The default working directory for the operation. If null the current working directory is used.
ShowCmd
No
Determines how an application is displayed when it is opened (as per the normal VISIBLE property).
The return value is the value returned by ShellExecuteW.
The RTI_ShellExecuteEx method
This stored procedure is a wrapper around the Windows API ShellExecuteExW function (which is used internally by ShellExecuteW itself), and may be used outside of event context – it can also return the handle to any new process it starts as a result of executing the document. As you can see it’s quite similar to the SHELLEXEC method:
Whilst you are free to use one of the methods outlined above, this may not be optimal if you are still sharing code between your existing version 9 application and your new version 10 one. In this case there are a couple of options you could use:
Define your preferred DLL prototype in v10.
Use a wrapper procedure and conditional compilation.
Defining your own prototype
This is probably the easiest option – you simply use the same prototype in v10 that you did in version 9, with the same alias (if any), and this way the code that uses it doesn’t need to be changed. The only downside to this if you’ve used any 32-bit specific data types instead of 32/64-bit safe types like HANDLE (this could happen if you have a really old prototype) – you must ensure that you use types that are 64-bit compliant.
Using conditional compilation
This is a technique we used when writing the initial parts of v10 in a v9 system so our stored procedures would run the correct code depending on the platform they were executing on (it was actually first used to share code between ARev and OI many years ago!).
The v10 Basic+ compiler defines a token called “REVENG64” which is not present in the v9 compiler – this means that you can check for this in your source code with “#ifdef/#ifndef” directives and write code for the different compiler versions.
For example, you could write your own wrapper procedure for ShellExecute that looks something like this:
Compile Function My_ShellExecute( hwnd, verb, file, params, dir, nShow )
#ifdef REVENG64
// V10 Compiler - use RTI function
Declare Function RTI_ShellExecuteEx
RetVal = RTI_ShellExecuteEx( hwnd, verb, file, params, dir, nShow, "" )
#endif
#ifndef REVENG64
// V9 Compiler - use existing "raw" prototype
Declare Function ShellExecute
RetVal = ShellExecute( hwnd, verb, file, params, dir, nShow )
#endif
Return RetVal
And then call My_ShellExecute from your own code.
So, there ends the Saga of ShellExecute … at least for now.