Tag Archives: SYSTEM

A focus on the FOCUS property

There are two methods for setting the input focus in OpenInsight, and there is a subtle implementation difference that can impact your applications if you’re not careful how you apply them.

The first method is to use an object’s own FOCUS property and set it to TRUE$ like so:

Call Set_Property_Only( @Window : ".EDL_SURNAME", "FOCUS", TRUE$ )

This moves the focus to the specified object, but the event queue will be flushed both before and after the focus has been set, thereby preventing any events raised as a result of the focus being moved from being processed.  This method was originally designed for use with validation routines so the focus could be reset to an invalid control “safely”.

The second method is to use the SYSTEM object’s FOCUS property:

Call Set_Property_Only( "SYSTEM", "FOCUS", @Window : ".EDL_SURNAME" )

This moves the focus, but any events triggered as a result of moving the focus (like LOSTFOCUS and GOTFOCUS events) will be processed.

That all sounds straightforward enough, but using the first method can lead to unexpected results if you are relying on an event already in the queue that you subsequently need – not a common situation but one we encountered recently while converting an old form to v10.  In our case we had a menu failing to show when the focus was on a specific control, and it turned out that the LOSTFOCUS event for the control was setting it’s FOCUS property to TRUE$.  This had the effect of killing a pending MENUDROPDOWN event (new in v10) that created the menu to display, hence no menu.

In this case the solution is to use the SYSTEM BLOCKEVENTS property to turn off events being triggered while the focus is moved and then restore event processing afterwards:

Call Set_Property_Only( "SYSTEM", "BLOCKEVENTS", TRUE$ )
Call Set_Property_Only( "SYSTEM", "FOCUS", @Window : ".EDL_SURNAME" )
Call Set_Property_Only( "SYSTEM", "BLOCKEVENTS", FALSE$ )

This prevented any LOSTFOCUS and GOTFOCUS events from being raised, but the MENUDROPDOWN event was still in the queue.

Using the SYSTEM FOCUS and BLOCKEVENTS properties in this way is a far “safer” alternative when moving the focus because you have full control over how events are handled, and I would always advocate using this method over using a control’s own FOCUS property.

 

The CHOOSEDIR and CHOOSEFILE methods

In light of the Utility function being deprecated (as mentioned in this post) I was recently asked where the CHOOSEFILE and CHOOSEDIR methods had ended up.  There was some discussion about this as the aforementioned article cited that they were moved to the SYSTEM object, and a case was made for moving them to the FILESYSTEM object instead.

In the end it was decided to move them to the FILESYSTEM object (as feedback indicated that was where they were expected to be), but to expose them from the SYSTEM object as well, meaning that we wouldn’t have to alter any existing code.

For future reference here are the official details of both methods:

FILESYSTEM CHOOSEDIR method

This method displays the common “Choose Folder” dialog box to allow a user to select a folder.  It takes two arguments:

  1. The name of an owner window
  2. A @fm-delimited array of initialization data
<1> Dialog text
<2> Initial folder to select
<3> HideNewFolder flag: If TRUE$ then hide the "New Folder" button
<4> ShowFiles flag: If TRUE$ then show files in the dialog as well as folders

The CHOOSEDIR method is basically a wrapper around the Windows SHBrowseForFolder function.

Example:

dlgOptions    = ""
dlgOptions<1> = "Please select the destination RDK folder"
dlgOptions<2> = "c:\RevSoft\RDK"
dlgOptions<3> = FALSE$ ; * // allow new folders
dlgOptions<4> = FALSE$ ; * // don't show files

folderName = Exec_Method( "FILESYSTEM", "CHOOSEDIR", @window, dlgOptions )

// This works too...
folderName = Exec_Method( "SYSTEM", "CHOOSEDIR", @window, dlgOptions )

 

FILESYSTEM CHOOSEDIR method

This method displays the common “Choose File” dialog box to allow a user to open a folder.  It takes two arguments:

  1. The name of an owner window
  2. A @fm-delimited array of initialization data
<1> Mode: If FALSE$ then show an "Open File" dialog, if TRUE$ then show 
    a "Save As" dialog instead.
<2> Filters: contains an "/" delimited list of filter items in the format:
    
       <displayText> "/" <filter> "/"

    Where <displayText> is the string to show in the filter dropdown list
    and <filter> is the string to apply to the selected folder.

<3> Filter Index: Specifies the index of the filters in <2> to be displayed.
<4> Default file name
<5> OFN (OpenFileName) flags. This is a bitmask number specifying the flags 
    to apply to the dialog. They are defined in the following insert record:
    
        MsWin_GetOpenFileName_Equates

<6> Initial folder to select
<7> Default extension: This is appended to the file name if the user fails 
    to type an extension. This string can be any length, but only the first 
    three characters are appended. The string should not contain a 
    period (.). 
<8> Dialog title

The CHOOSEFILE method is a basically wrapper around the Windows GetOpenFileName function.

Example:

$insert msWin_GetOpenFileName_Equates

* // Create a filter string for common image files
filters = "Bitmap Files (*.bmp;*.rle;*.dib)/*.bmp;*.rle;*.dib/" |
        : "GIF Files (*.gif)/*.gif/"                            |
        : "JPeg Files (*.jpg,*jpeg,*.jpe)/*.jpg;*jpeg;*.jpe/"   |
        : "PNG Files (*.png)/*.png/"                            |  
        : "All Files (*.*)/*.*/"

* // Create a bitmask of flags
ofnFlags   = OFN_READONLY$
ofnFlags   = bitOr( ofnFlags, OFN_HIDEREADONLY$ )
ofnFlags   = bitor( ofnFlags, OFN_FILEMUSTEXIST$ )

dlgOptions    = ""
dlgOptions<1> = FALSE$  ;* // "Open"
dlgOptions<2> = filters
dlgOptions<3> = 4       ; * // Pre-select the PNG filter
dlgOptions<4> = "example.png"
dlgOptions<5> = ofnFlags
dlgOptions<6> = ".\images"
dlgoptions<7> = ""
dlgOptions<8> = "Please select an Image"

imageName = Exec_Method( "FILESYSTEM", "CHOOSEFILE", @window, dlgOptions )

* // This works too ...
imageName = Exec_Method( "SYSTEM", "CHOOSEFILE", @window, dlgOptions )

(Disclaimer: This article is based on preliminary information and may be subject to change in the final release version of OpenInsight 10).

The IDLEPROC Queue

One of the new features added to the SYSTEM object is the “IDLEPROC queue”.  This is an enhancement to the existing IDLEPROC property as it allows you to queue multiple requests via the new SYSTEM ADDIDLEPROC method like so:

* // Run a process in the future 
procID   = "RUN_SOME_PROCESS"
procArg  = "42"
procTime = "12:00:00"  ; * // midday
procDate = oconv( date() + 1, "D4/" ) ; * // tommorrow

call exec_Method( "SYSTEM", "ADDIDLEPROC", procID, procArg, procTime, procDate )

* // Run a process ASAP...
procID = "RUN_SOME_OTHER_PROCESS"
procArg = "X43"
procTime = "" ; * // ASAP
procDate = "" ; * // ASAP

call exec_Method( "SYSTEM", "ADDIDLEPROC", procID, procArg, procTime, procDate )

The contents of the queue can be examined via the new SYSTEM IDLEPROCQUEUE property:

ipQ    = get_Property( "SYSTEM", "IDLEPROCQUEUE" )
xCount = fieldCount ( ipQ, @fm ) 
for x = 1 to xCount
   ip       = ipQ<x>
   procID   = ipQ<0,1>
   procArg  = ipQ<0,2>
   procTime = ipQ<0,3>
   procDate = ipQ<0,4>
next

The normal IDLEPROC property works just as it always has done with the following caveats:

  • Setting the IDLEPROC property will replace the entire contents of the queue (so you can use this to clear the queue if you wish).
  • Getting the IDLEPROC property will only return the first item in the queue if there are multiple items.

(Disclaimer: This article is based on preliminary information and may be subject to change in the final release version of OpenInsight 10).

OpenInsight and High-DPI

With the increasing popularity of high-resolution monitors, one of the biggest usability problems today is the display size of text and UI controls, because they appear smaller as the screen resolution increases. The recommended advice to overcome this is to increase the DPI (dots-per-inch) setting of the system, thereby enlarging these elements and making them easier to see and read. If you’ve been using Windows Vista and above you’ve probably already seen this Control Panel applet that allows you to easily change your DPI settings:

DPI Control Panel Applet

Windows 7 DPI Control Panel Applet

However, unless an application is designed to be DPI-aware this can result in some unsatisfactory results, such as over-large fonts, clipped controls and blurry windows. This is because many older Windows applications assume a constant DPI (96) when setting font and size coordinates and they do not apply any scaling to these values, thereby resulting in the aforementioned problems.

(NB. The “magic number” of 96 that you’ll see throughout this post is due to the fact that at 96 DPI one logical pixel is equal to one screen pixel – this is the “100%” setting in the Control Panel applet shown above).

In an effort to accommodate these applications Microsoft have introduced a couple of OS features over the years:

  • On Windows XP the system fonts and some system UI elements are scaled up at runtime, but this leads to the common problem of text appearing larger and being clipped as the actual size of the bounding control is usually not scaled.
  • On Windows Vista and above a feature called “DPI-virtualization” automatically scales windows belonging to an application not marked as “DPI-aware” – in effect they are rendered at 96 DPI to an off-screen bitmap, resized, and then drawn to the screen, but this can result in some blurry windows due to pixel stretching.  OpenInsight 10 is marked as a DPI-aware application so will not be subjected to DPI-virtualization.

OpenInsight 10, High-DPI and automatic scaling.

OpenInsight 10 supports High-DPI by automatically scaling-up all GUI objects at runtime when created through the SYSTEM object’s CREATE method (formerly known as the “Utility CREATE service”). This affects the following two properties:

  • Size coordinates
  • Fonts

The actual scaling for coordinates is calculated by the following simple formula:

screenPixels = logicalPixels * ( currentDPI / 96 )

For example, if you create a control with a size of 200×100 and you are running at 144 DPI (i.e. 150% as per the Control Panel applet above) then the control will be created with an actual size of 300×150 pixels.

The font point size is similarly multiplied by the scaling factor (i.e. currentDPI / 96 ).

Supporting images under High-DPI

Another noticeable issue when running at high DPI settings are images, which are assumed to have been designed for 96 DPI and therefore have to be scaled up at runtime leading to a potential loss of quality due to the resize.  To help with this the tool-set has been extended to allow repository BITMAP entities to specify multiple image files when being defined. The first will be used for 96 DPI (100%), the second for up to 120 DPI (125%), the third for up to 144 DPI (150%) the fourth for up to 192 DPI (200%) and so on, with further images being defined at 48 DPI (50%) increments (for future-proofing). When a control is created at runtime the system picks the appropriate image size and scales it as needed (preferably down where possible) before applying any other transformations.

Note that this does NOT apply to images set at runtime in code via the BITMAP property.  In this case the developer is assumed to have selected the correct image file size regardless of the DPI setting.

Designing under High-DPI

If you design your forms when running under a High-DPI setting the Form Designer will save and compile all coordinate and font information as though you were developing at 96DPI, so the values will be scaled down appropriately.

Opting out of automatic scaling

Of course, we always try our best not to break existing applications so you can set an option in the RXI file to turn off the automatic DPI scaling if you wish (this option is exposed at runtime via the read-only SYSTEM DPISCALING property).

This same principle can also be applied to individual windows at design-time so you can use it selectively as needed (WINDOW objects also support the read-only DPISCALING property).

Further reading

If you want to find out more information on this topic please see the following link to Microsoft’s documentation on MSDN:

Writing High-DPI Win32 Applications

(Disclaimer: This article is based on preliminary information and may be subject to change in the final release version of OpenInsight 10).

The Reflection API

From a tool-developers point of view one of the weaker areas in the Presentation Server has always been the lack of a “reflection API”, i.e. a way to discover what properties, methods and events an object actually supports at runtime.  This in turn has led to creating manual lists and records of object attributes, bringing with it with the inherent difficulty of keeping this information up-to-date as new features are added.

Primarily designed with the new PS-based Form Designer in mind, this shortcoming has been addressed in version 10 with the addition of the following new methods to the SYSTEM object:

  • REFLECT_PROPERTIES
  • REFLECT_METHODS
  • REFLECT_EVENTS

Each of these methods takes a single argument which is the type of object to query:

e.g.

buttonProperties = exec_Method( "SYSTEM", "REFLECT_PROPERTIES", "PUSHBUTTON" )
buttonMethods    = exec_Method( "SYSTEM", "REFLECT_METHODS", "PUSHBUTTON")
buttonEvents     = exec_Method( "SYSTEM", "REFLECT_EVENTS", "PUSHBUTTON" )

Each method in turn returns a dynamic array of information describing the requested attribute type:

  • REFLECT_PROPERTIES returns the name of each property and the type of the data it represents.  It also identifies the context that the property is valid for (Devtime/Runtime/Get/Set).
  • REFLECT_METHODS returns the name of each method, along with a list of arguments the method accepts and the type of data each argument should contain.
  • REFLECT_EVENTS returns the name of each event the object type can raise along with the names and data types of arguments passed to an event handler.

Note that when these methods return the type of data for a property or argument there are some cases where only a broad hint is given – Basic+ is an untyped language so it is not really feasible to accurately describe some types using a simple set of flags – e.g. in the case where an argument is a complex dynamic array containing a variety of other core data types such a strings and numbers.

Whilst aimed mainly at developers who write development tools for OpenInsight, this API may still be of interest for those of you who like to write your own diagnostic and debugging tools – when a new property, method or event is added to the Presentation Server it is clearly exposed thereby reducing the likelihood of it remaining hidden and undocumented.

[Edit: 24 Apr 13 – Call_Method renamed to Exec_Method]

(Disclaimer: This article is based on preliminary information and may be subject to change in the final release version of OpenInsight 10).

The RXI Files – Part II

In the previous post where we introduced the new RXI files we mentioned some new command line switches added to OpenInsight 10. In this post we’ll take a look at them and describe what they do.

/TB (taskBarID)

Under Windows 7 and 8 multiple instances of an executable launched from the same directory are grouped under one icon on the taskbar, making navigating between them quite tedious. To allow an executable to override this behavior Microsoft introduced a new application property called the “Application User Model ID” – a simple string that can be applied to an executable instance to differentiate it from other sibling instances, thereby allowing them to be un-grouped.

Windows assigns a default value to this property at runtime based on the executable name and the starting directory.  The OpenInsight /TB switch (or taskBarID element in an RXI file) allows you to set your own unique value for this property so that your application can be differentiated on the Windows taskbar (A read-only property called TASKBARID is exposed by the SYSTEM object at runtime to provide access to this value).

The Application User Model ID must be set before any forms are created by an executable which is why it can only be set via the RXI file or the command line switch.

If you wish to find out more details about the Application User Model ID please see the MSDN documentation about the SetCurrentProcessExplicitAppUserModelID function.

/SI (singleInstance)

This is a boolean (1/0) flag that ensures there is only one instance of the specified OI application executing on a workstation.  Any other OI applications from the same executable are unaffected.

/EL (elevate)

This is a boolean flag (1/0) that specifies if the application requires Administrator privileges when it is launched. If this switch is set to “1” and the Windows user is not an administrator they will be asked to provide Administrator level credentials before they can continue (via the standard Windows UAC prompt).

/MD (minDisplaySecs)

When displaying a banner image file at startup this switch specifies the minimum number of seconds to display the banner for.

/EP (enginePath)

This switch specifies an alternative OpenEngine path (normally the Presentation Server expects to load OpenEngine from the same directory where it is located).

/2D (useD2D)

This is a boolean flag (1/0) that specifies if the system should use Direct2D technology for rendering when available.  By default OpenInsight will attempt to use Direct2D where it can (Windows Vista SP2, Windows 7 and later), but it can be forced to use GDI rendering instead if this flag is set to “0”.

/DP (useDPIScaling)

This is a boolean flag (1/0) that specifies if the application should automatically scale coordinates when used on a high-DPI system.

[Edit: 01 Feb 13 – AM switch renamed to TB and taskBarID]

[Edit: 08 Jan 13 – Added DPI Scaling modifications]

(Disclaimer: This article is based on preliminary information and may be subject to change in the final release version of OpenInsight 10).

The LOG_EVENT method

In an earlier post we took a very brief look at some of the new properties of the SYSTEM object, and this time we thought we’d take a closer look at one of it’s new methods called LOG_EVENT.

Simply put the LOG_EVENT method allows you to write a message to the Windows Event Log without the need for you to prototype and use any Windows API functions yourself. It’s very easy to use and takes three arguments:

1) Type Info: This is a dynamic array comprised of the following fields:

   <1> Message Type (Required) : "ERROR", "WARNING" or "INFO".
   <2> Event ID     (Optional) : Integer denoting the event. Defaults to 1.
   <3> Category ID  (Optional) : Integer identifying the category. Defaults to 0.

2) Source Info: This is a dynamic array comprised of the following fields:

   <1> Event Source (Required) : Name of the Event Source (See below).
   <2> Server Name (Optional)  : UNC Name of the system to post the message to.
                                 Defaults to the local workstation.

3) Message Text: This is simply the text of the message to post to the Event Log.

Example: Post an error message to the Event Log from the “RevPS” Event Source:

   x = exec_Method( "SYSTEM", "LOG_EVENT", "ERROR", "RevPS", "FOOBAR!!!!" )

The Event Source name

So far, so good. However if you simply execute the above code you will see the message you posted in the Application Event Log, but you’ll also see that Windows has prefixed your message with some of it’s own text which refers to a missing Event ID description like so:

The reason for this is that Windows expects to find a “registered Event Source” containing the description for the Event ID that you specified.  A registered Event Source is actually a DLL containing a set of strings, each of which corresponds to an Event ID.  Without this DLL you get the warning text you see above which doesn’t look very professional and gives the impression that something is missing from your application.

For OpenInsight v10 we have provided a generic DLL called RevEventMsg.dll that you can register on your system under your desired Event Source name  – if you then use that name in your  call to the LOG_EVENT method you will see your message without any of the warning text prefixed to it.

Registering the DLL is quite simple: you need to create a new key with the name of your Event Source (We used the string “RevPS” for this example) under this path:

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\eventlog\Application\

… and set a couple of values like so (this can be automated easily via a .reg file):

Now if you call the LOG_EVENT method your Event Log entry will look like this:

Much better.

If you are interested in reading further on the subject of the Windows Event Log you can find full documentation on Microsoft’s MSDN website here,

[Edit: 24 Apr 13 – Call_Method renamed to Exec_Method]

(Disclaimer: This article is based on preliminary information and may be subject to change in the final release version of OpenInsight 10).