Quantcast
Channel: Thinfinity VirtualUI Archives - Blog Cybele Software, Inc.
Viewing all 130 articles
Browse latest View live

Recovering browser information using ‘BrowserInfo’

$
0
0

Recovering browser information using BrowserInfo propertyWe start with this lines a new series of small articles to offer some simple but useful tips to help provide a better end user experience with Thinfinity® VirtualUI™ extended applications.

When the application was originally designed, the underlying assumption was that it would be used in desktop devices. As a result, we did not spend too much time thinking about the characteristics of the device from where our application would be run.

But when using VirtualUI, it is often necessary to be aware of some features and capabilities of the device from which end users are accessing our application.

The VirtualUI class has a property called BrowserInfo, which contains data about the used browser to assist us in delivering the best possible user experience.

In the list below we will find a detail of the information that BrowserInfo will deliver us:

 
General information related to the current connected browser:

 

Attribute Description
Username Returns the logged-on username.
IPAddress Returns the client’s ip address.
Location Returns the URL of the current application.
UniqueBrowserId Identifies an instance of a browser. Each time an end-user opens the application from a different browser window, this identifier will be have a different value.
UserAgent Returns the browser’s User Agent string. This value is very important to identify which browser type is used, if the remote device is a mobile, etc.

An important value to consider is the UserAgent property. When establishing a connection, the browser sends VirtualUI a string data indicating its brand and version, which OS it is running on, and the brand and model of the device that is being used. This value is known as the userAgent. Using this value we can infer, for example, if the user connects from a mobile device and then modify some specific feature, layout or functionality of our application to better suit that device.
 

Information related to the available screen space:

 

Attribute Description
BrowserWidth Returns the width of the HTML element containing the VirtualUI viewer, in pixels.
BrowserHeigth Returns the height of the HTML element containing the VirtualUI viewer, in pixels.
ScreenWidth Returns the width of the end-user’s device screen, in pixels.
ScreenHeight Returns the height of the end-user’s device screen, in pixels.
ScreenResolution Returns the application screen resolution defined in the application profile.
ViewWidth Returns the width of the VirtualUI viewer, in pixels.
ViewHeight Returns the height of the VirtualUI viewer, in pixels.
Orientation Returns the browser’s orientation.

 
It is also worth mentioning that when using VirtualUI, the desktop size is dynamic. This means that the available space to display the application is correlated to the available space in the browser screen and it is not limited to a fixed desktop size —as it would  happen, for example, in an RDP connection, where the desktop size is defined when the connection is established.

The following example in C# shows how to center a window of our application on this virtual desktop as needed, previously controlling their size if it exceeds the available space:
 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
using Cybele.Thinfinity;...
...
...
 
private VirtualUI vui; 
public Form1()
{
    InitializeComponent();
    vui = new VirtualUI();}
 
private void CenterThisWindow(object sender, EventArgs e)
{
    this.WindowState = FormWindowState.Normal;
 
    if (vui.BrowserInfo.ViewHeight < (this.Height + 20))    {
        this.Height = vui.BrowserInfo.ViewHeight - 20;
    }
    if (vui.BrowserInfo.ViewWidth < (this.Width + 20))
    {
        this.Width = vui.BrowserInfo.ViewWidth - 20;
    }
 
    this.Top = (vui.BrowserInfo.ViewHeight - this.Height) / 2;
    this.Left = (vui.BrowserInfo.ViewWidth - this.Width) / 2;
 
}

 
As we mentioned above, this new series of small technical notes will be aimed at providing simple tips to help you take advantage of the features added to your applications through the adoption of Thinfinity VirtualUI.

We invite you to share with us your concerns and/or experiences.

The post Recovering browser information using ‘BrowserInfo’ appeared first on Cybele Software.


Change browser behavior using ‘ClientSettings’

$
0
0

ClientSettings is an additional interface variable for Thinfinity VirtualUI LibraryIn our last post we started with a new notes and tips series aimed at helping developers take full advantage of Thinfinity® VirtualUI™’s existing possibilities.

In the present note, we will focus on ClientSettings.

ClientSettings is an additional interface available at the Thinfinity VirtualUI Library that allows developers to remotely and programmatically configure particular browser settings. These settings are related to the cursor visibility and some specific touch action behaviors.

We can find, in the table below, a detailed explanation of the ClientSettings interface’s current properties:

 

Property Use Values
CursorVisible It used to set show or hide the mouse pointer. True / False
MouseMoveGestureStyle It defines whether the mouse movement is readed as relative or as absolute, in touch devices. MM_STYLE_RELATIVE = 0MM_STYLE_ABSOLUTE = 1
MouseMoveGestureAction It indicates if the drag should be interpreted as a scrolling or as a turn of the mouse wheel, causing a shift only item on which the action is triggered. MM_ACTION_MOVE = 0,MM_ACTION_WHEEL = 1

These properties, as previously mentioned, were created to provide a better end-user experience when using touch devices.

When using a touch device, it is unusual for a mouse pointer to be displayed. Instead, finger gestures are interpreted by the device as different commands, acting on the element or elements that are located where the gesture is made.

But this is not always the best way, especially when our applications were not designed for these gestures. Oftentimes, the best solution to this issue is to show the pointer and simulate the mouse actions from the gestures captured.

The properties that we have presented above will help us define the ideal settings to enjoy the best possible and intuitive user experience for our application.

The following example shows how to recognize a touch or mobile device, and if detected, to change the pointer behavior. (Be careful! The detection list is incomplete):

1
2
3
4
5
6
7
8
9
10
11
12
 vui = new VirtualUI();
 userAgent = vui.BrowserInfo.UserAgent.ToLower();
 
 if ((userAgent.IndexOf("touch") != -1) || (userAgent.IndexOf("mobile") != -1) ||
     (userAgent.IndexOf("iphone") != -1) || (userAgent.IndexOf("ipod") != -1) ||
     (userAgent.IndexOf("ipad") != -1) || (userAgent.IndexOf("android") != -1) ||
     (userAgent.IndexOf(" cros ") != -1)) {
 
         vui.ClientSettings.MouseMoveGestureStyle = MouseMoveGestureStyle.MM_STYLE_ABSOLUTE;         vui.ClientSettings.MouseMoveGestureAction = MouseMoveGestureAction.MM_ACTION_WHEEL; 
 }

This would be the end of this week’s small technical note aimed at providing a simple tip to help you take advantage of the features added to your applications through the adoption of Thinfinity VirtualUI.

We invite you again to share with us your concerns and/or experiences.

The post Change browser behavior using ‘ClientSettings’ appeared first on Cybele Software.

How to manage and customize the homepage of a Thinfinity VirtualUI application

$
0
0

How to customize the homepage of a Thinfinity VirtualUI applicationWe continue providing hints and tips with the goal of helping you take full advantage of Thinfinity® VirtualUI™’s existing possibilities.

In the present note we will explain how to manage and customize the homepage of a Thinfinity VirtualUI application.

If we take a look at Thinfinity VirtualUI’s installation folder (by default the folder is “C:\Program Files\Thinfinity\VirtualUI\dev\web”) we will find, in the dev/web subfolder, a file labeled app.html. This file (actually, an html page) loads all of VirtualUI’s web-side components so that our application can be controlled from the browser.

The file’s default content is the following:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!DOCTYPE html>
<html>
<head>
   <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
   <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
   <meta name="apple-mobile-web-app-capable" content="yes" />
   <meta name="apple-mobile-web-app-status-bar-style" content="black" />
   <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no, target-densityDpi=device-dpi" />
   <title>Thinfinity VirtualUI</title>
   <link rel="apple-touch-icon" href="images/icon.png" />
   <link rel="stylesheet" type="text/css" href="css/splash.css"/>
   <link rel="stylesheet" type="text/css" href="css/thinfinity.virtualui.css" />
   <link rel="stylesheet" type="text/css" href="css/print.dialog.css" />
   <script src="virtualui.sdk.min.js" type="text/javascript"></script>
   <script src="js/splash.js" type="text/javascript"></script>
   <script src="js/app.js" type="text/javascript"></script>
   <script src="https://www.google.com/cloudprint/client/cpgadget.js" type="text/javascript"></script>
</head>
<body style="background-color: #f2f2f2;">
     <div id="virtualui" style="position:absolute;display:none;">
     </div>
</body>
</html>

 
This page holds the basic content needed for VirtualUI to work, but it is nothing more than the default and basic page. We will now show how we can turn it into something different. This will be a small but very important first step to reach a full web-integration of our application.

 

Modifying the homepage

Modifying the application’s homepage is quite simple. All we have to do is the following:

The first step would be to create a copy of app.html and rename it to myapp.html.

After that, we should open the VirtualUI Server Manager. On the Applications tab we must select the application item, and then open the configuration dialog by clicking on Edit:

We must now set the home page, pointing to the new file, as shown in the image below:

Customizing a VirtualUI appplication homepage

We are now ready to modify the page and to see the changes from the browser. We could certainly change the basic layout of the page: its settings, for example; or select another color, or insert an image in the application background. More importantly, we can change the out of the box event handlers defined in the app.js file:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
var virtualUI = null;
var splash = null;
 
$(document).ready(function () {
   splash = new Splash();
   virtualUI = new Thinfinity.VirtualUI();
   virtualUI.onError = function (errorMsg) {
       splash.show("Application load failed", errorMsg || "", false, false);
   };
   virtualUI.onLoading = function () {
       splash.show(
          (virtualUI.devMode)? "Waiting for application..." : "Loading...",
          "", true, false);
   };
   virtualUI.onShow = function () {
       splash.hide();
   };
   virtualUI.onClose = function (url) {
       // -- The url argument is used to allow to back
       // -- to specific page, when a virtualPath is used
       // -- and url is not null.
       if ((typeof url != 'undefined') &amp;&amp; (url != '') &amp;&amp; (url != null)) {
           // tries to close the window.top
           if ((virtualUI.devMode != true) || (window.top.opener)) {
               window.top.close();
           }
           window.top.location.href = url;
           return;
       }
       // if in devMode, reload the page
       if (virtualUI.devMode) {
           location.reload();
       }
       // tries to close the window/tab
       if ((virtualUI.devMode != true) || (window.opener)) {
           window.close();
       }
       // else returns to the calling page
       if ((window.top == window) &amp;&amp; (document.referrer) &amp;&amp; (location.href != document.referrer)) {
           location.href = document.referrer;
       } // else shows the splash
       else {
           splash.show("Application closed", "", false, false);
       }
   };
   // -- Connect to server...
   virtualUI.connect();
});

As we can see, handling the published events of VirtualUI API allows us to control each session step, from the connection to the disconnection. Each .on… event handler can be modified to do what we want. Therefore, we could customize the message, or reshape it to be displayed in a different way, simply by replacing the splash object.

 

Example: Tailoring the error handling

In the following example we will change the error handling, redirecting the message to a  new custom error page:

Create a copy of app.js and rename it to myapp.js.

Modify the link to the app.js file into myapp.html:

1
<script src="js/myapp.js" type="text/javascript"></script>

Edit the new myapp.js and modify the onError event handler:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var virtualUI = null;
var splash = null;
 
$(document).ready(function () {
  ...
  ...
  virtualUI.onError = function (errorMsg) {
     location.href = "oops.html?msg=" + encodeURIComponent(errorMsg || "");  };
  ...
  ...
  // -- Connect to server...
  virtualUI.connect();
});

Finally, create the oops.html with the code below and add it to the web folder:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Thinfinity VirtualUI Demo - Oops!</title>
<style>
.errmsg {  width: 100%; height: 100%; text-align: center; }
.errmsg h1 { font-size: 8em; }
.errmsg h2 { font-size: 2em; color: #808080; }
.errmsg h3 { font-size: 1.5em; }
.errmsg h4 { font-size: 1.5em; color: #808080; }
</style>
<script type="text/javascript">
function getmsg() {
  var regexS = "[\\?&]msg=([^&#]*)";
  var regex = new RegExp(regexS);
  var results = regex.exec(window.location.href);
  delete regex;
  document.write((results) ? decodeURIComponent(results[1]) : "Unspecified error");
  document.close();
}
</script>
</head>
<body>
<div class="errmsg">
<h1>Oops!</h1>
<h2>An error occurs when connecting to the application:</h2>
<h3><script>getmsg()</script></h3>
<h4>Sorry, please try again later :(</h4>
</div>
</body>
</html>

This example can be almost trivial, but knowing how to replace or modify the app.html page can help you provide your end users a custom-made experience of your VirtualUI application.

 

The post How to manage and customize the homepage of a Thinfinity VirtualUI application appeared first on Cybele Software.

Browser within the browser: breaking the Matryoshka effect.

$
0
0

When the browsers are nested.
image01

It has become increasingly common to expect desktop applications to be capable of integrating to the web to incorporate available external information. There are several ways this could be achieved: by consuming web services to get the raw data; or, by simply including a browser component within the application, in the form of a panel. In this second case, we’ll be capable of navigating to sites displaying up-to-date information or external multimedia content, such as maps, videos, images and sound —as an example, visit Fish Facts, a live demo that shows basic GUI virtualization capabilities as well as an integration with the web browser and external web resources.

Once we transform these applications with Thinfinity® VirtualUI™, and we invoke them from the web, an effect similar to the famous Russian Nested Dolls case occurs: we run from the browser an application that contains within itself another browser. In short, we surf to an application that internally re-surfs. This is not bad in itself, but we can take advantage of already being in the web to access the final resource directly from the main browser. By doing this we will avoid a roundabout that, among other things, would unnecessarily increase the consumption of resources.

Let’s take a look at the diagrams of the same application running from the desktop…

Matryoshka-image1

… and from the Web:

Matryoshka-image2

As we can see, when we run the application from the desktop we must necessarily navigate through an embedded Webbrowser component. The application is the one that connects to the desired web address, and this webpage is seen in an internal panel, within the application.

But once we load the application from a browser we would experience the aforementioned effect. As a result, the application browses on its own, and the visible result of that surfing is re-transferred to our browser.

If we transfer that navigation to the only browser we need to use, we will access the intended resource without delay or additional bandwidth, processing, or time consumption.

image00

The demo application

The application we use in the example (whose complete source can be found here) attempts to break this browser-within-the-browser effect, simply by using the internal browser only when the application is accessed from the desktop. If the application is being accessed from the web, instead of using that component, the app would load the desired page in an iframe created on the fly. To achieve this integration (which requires a little bit more interaction between the application and the browser) we will add a jsRO object, which will control the remote iframe from the application.

The first addition to our application will be the adding of VirtualUI and its initialization:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
using System;
using System.Windows.Forms;
 
namespace matryoshka
{
   static class Program
   {
       /// <summary>
       /// The main entry point for the application.
       /// </summary>
       [STAThread]
       static void Main()
       {
           new Cybele.Thinfinity.VirtualUI().Start();           Application.EnableVisualStyles();
           Application.SetCompatibleTextRenderingDefault(false);
           Application.Run(new Form1());
       }
   }
}

Let’s now see what we have to add to the main form code, which it only has a panel where to enter the browsing URL and another one where the WebBrowser component is located.

The following is the application code before adding the changes:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
using System;
using System.Drawing;
using System.Windows.Forms;
 
namespace matryoshka
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            // Sets Google Maps URL to run in the embedded browser
            txtURL.Text = "https://maps.google.com";
            Navigate();
        }
 
        private void btnGo_Click(object sender, EventArgs e)
        {
            Navigate();
        }
 
        private void txtURL_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (Convert.ToInt32(e.KeyChar) == 13)
            {
                Navigate();
            }
        }
 
        private void Navigate()
        {
            if (!txtURL.Text.Equals(""))
            {
                if (!txtURL.Text.StartsWith("http"))
                {
                    txtURL.Text = "http://" + txtURL.Text;
                }
                webBrowser1.Navigate(new Uri(txtURL.Text));
            }
            else {
                webBrowser1.Navigate(new Uri("about:_blank"));
            }
        }
    }
}

As a first step, we will add to the class the objects in the Cybele.Thinfinity library:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
using System;
using System.Drawing;
using System.Windows.Forms;
using Cybele.Thinfinity; 
namespace matryoshka
{
    public partial class Form1 : Form
    {
 
        private VirtualUI vui = new VirtualUI();        private IJSObject mRemoteLayout = null; 
        public Form1()
        ...

This demo application loads a Google Map. To be run embedded in an iframe, Google Maps needs a different URL from the one used on a full page. We know that it will run embedded in the iframe (using VirtualUI) and directly when loaded into the internal browser.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public Form1()
{
    InitializeComponent();
    if (vui.Active)    {
        // Code executed only when the application runs with Thinfinity VirtualUI
        // Sets Google Maps URL to run embedded in an iframe
        txtURL.Text = "https://www.google.com/maps/embed";    }
    else
    {
        //* Code executed only when the application runs from desktop 
        // Sets Google Maps URL to run in the embedded browser
        txtURL.Text = "https://maps.google.com";    }
}

Additionally, we must also make some changes to Navigate( ):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private void Navigate()
{
    string sURL = "";
    if (txtURL.Text.Equals("")){
        sURL = "about:_blank";
    } 
    else
    {
        sURL = txtURL.Text;
        if (!sURL.StartsWith("http"))
        {
            sURL = "http://" + sURL;
        }    }    if (vui.Active) {
        mRemoteLayout.Properties["url"].AsString = sURL;
    }    else {
        webBrowser1.Navigate(new Uri(sURL));
    }
}<strong><strong> </strong></strong>

The last (but not least important) addition is the one concerning the jsRO object management: the first method, for its instantiation and definition of their getters; the other one, for the later updating of the iframe’s bounds.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
private void Form1_Shown(object sender, EventArgs e)
{
    // Creates jsRO object for iframe control
    mRemoteLayout = new JSObject("layout");    mRemoteLayout.Properties.Add("windowId").AsString =
                String.Format("virtualui_canvas_{0}", Handle);
    mRemoteLayout.Properties.Add("url").AsString = txtURL.Text;
    mRemoteLayout.Properties.Add("bounds")
        .OnGet(new JSBinding(        // Adds a "getter" to bounds
            delegate(IJSObject AObj, IJSProperty AProp)
            {
                // Returns a JSON object
                Point p = pnlNav.PointToScreen(new Point(0, 0));
                p.Offset(-Left, -Top);
                AProp.AsJSON = "{" + String.Format("\"left\":{0}, \"top\":{1}, \"width\":{2}, \"height\":{3}",
                     p.X, p.Y, pnlNav.Width, pnlNav.Height) + "}";
            }));    mRemoteLayout.ApplyModel();
}
 
private void pnlNav_Resize(object sender, EventArgs e)
{
    // Updates all mRemoteLayout's properties "getters"
    if (mRemoteLayout != null)
    {        mRemoteLayout.ApplyChanges();
    }
}

We can now introduce the additional information that must be included, at javascript level, to the HTML page.

What follows is the base page to which we will be adding the functionality:

1
2
3
4
<script src="virtualui.sdk.min.js" type="text/javascript"></script><script type="text/javascript">// <![CDATA[
$(document).ready(function () {
        });
// ]]></script>

 

We will now create three global variables for the VirtualUI and jsRO objects, and for the iframe created on the fly.

1
2
3
4
5
6
7
<script type="text/javascript">
var virtualUI = null;
var jsro = null;
var nav = null;
 
$(document).ready(function () {
    ...

We will add three functions intended to create the iframe and keep it updated:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
...
$(document).ready(function () {
 
    var navigate = function(url) {
        nav.src = url;
    }
 
    var applyBounds = function(bounds) {
        nav.style.left = bounds.left + "px";
        nav.style.top = bounds.top + "px";
        nav.style.width = bounds.width + "px";
        nav.style.height = bounds.height + "px";
    }
 
    var createNav = function () {
        nav = document.createElement("iframe");
        nav.id = "nav";
        nav.style.position = "absolute";
        nav.style.display = "none";
        nav.style.zIndex = 2;
        nav.style.border = "none";
    }
    ...

We will now instantiate the VirtualUI object, configure its events, and then connect to the application:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
var createNav = function () {
   ...
   ...
}
 
virtualUI = new Thinfinity.VirtualUI(); 
virtualUI.onError = function (errorMsg) {    alert("Application load failed");
};
 
virtualUI.onLoading = function () {    console.log((virtualUI.devMode) ? "Waiting for application..." : "Loading...");
};
 
virtualUI.onClose = function (url) {    if ((typeof url != 'undefined') && (url != '') && (url != null)) {
        if ((virtualUI.devMode != true) || (window.top.opener)) {
           window.top.close();
        }
        window.top.location.href = url;
        return;
    }
    if (virtualUI.devMode) { location.reload(); }
    if ((virtualUI.devMode != true) || (window.opener)) { window.close(); }
    if ((window.top == window) && (document.referrer) && (location.href != document.referrer)) {
        location.href = document.referrer;
    } 
    else {
        if (nav) { nav.style.display = "none"; }
        alert("Application closed");
    }
};
 
// -- Connect to server...
virtualUI.connect();

Finally, we will create the jsRO object instance. Please note that it’s in the creation of the layout jsRO model (defined in the application) where the iframe is injected into the application window. The other two events are used to keep both the URL and the inserted iframe bounds updated:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
...
virtualUI.connect();
 
// Defining Javascript Remote Objects Elements
jsro = new Thinfinity.JsRO(); 
jsro.on('model:layout', 'created', function (obj) {
    layout = jsro.model.layout;    if (nav == null) {
        createNav();        document.getElementById(layout.windowId).appendChild(nav);
        nav.style.display = "inline-block";
    }
    applyBounds(layout.bounds);});
 
jsro.on('model:layout.url', 'changed', function (obj) {    navigate(obj.value);
});
 
jsro.on('model:layout.bounds', 'changed', function (obj) {    applyBounds(layout.bounds);
});

With this procedure, we’ve been able to break the “Matryoshka Effect”. From now on, the embedded browser will be used from the desktop, while the external one will be used from the web —but always being controlled by the remote application.

 

Running the application from the browser

To see the end result, we must first add the application to the list of registered apps in the Thinfinity VirtualUI Server and configure its home page, making sure it points to the page modified by us in this tutorial.

Once this is done, we can access the application from the browser. If we are running the application from the Thinfinity VirtualUI Development Lab, we must change the virtual path’ address so as to match the one defined in the configuration —in the VirtualUI’s manager.

 

The post Browser within the browser: breaking the Matryoshka effect. appeared first on Cybele Software.

Remote data persistence using cookies

$
0
0

Thinfinity VirtualUI: Remote data persistence using cookiesIn early May we started with a series of brief and simple, yet useful articles to help you take better advantage of the existing possibilities that Thinfinity® VirtualUI™ provides.

In the present note we will see a way to store remote data in the browser to be later retrieved from the application, when needed.

Remembering login parameters, saving the last position on a map or a form’s data are typical cases of such a scenario. One possible way to address this need would to keep that information somewhere in the disk from where the application can recover it promptly. This would seem to be the most natural way of achieving that goal. But what would happen when we run the application remotely from the browser, greatly opening the range of users? In these cases, saving the information in the application does not seem to be the best way to do it; the most reasonable way, however, seems to be storing that information “near the user”; i.e, in the browser.

One of the most effective and classical method of sharing information between the browser and any remote application is by using cookies. Cookies are small pieces of information that the application can store in each browser and later recover them in a clear way. In our particular case, we can create and enquire the value of a cookie directly from the application through the SetCookie and GetCookie methods of Thinfinity.BrowserInfo.

 

SetCookie

The SetCookie method has the following format:

1
VirtualUI.BrowserInfo.SetCookie(name, value, expirationDate);

where,

Argument Data Type Description
name string Name of the cookie. With this name it is stored, properly identified, in the browser.
value string Value of the cookie
expirationDate string Stored in UTC/GMT format, indicating the expiration date of the cookie. If its value is “”, the cookie does not expire.

This example written in Delphi shows how store a data value (in this case the value of the username field) into a cookie:

1
2
3
4
5
6
7
8
9
10
11
12
procedure TForm1.Button1Click(Sender: TObject);
begin
  if Trim(edtUsername.Text) = '' then begin
    ShowMessage('The username field must have a value');
  end else begin
    // saving the cookie when running with Thinfinity VirtualUI only.
    if VirtualUI.Active then begin
      VirtualUI.BrowserInfo.SetCookie('CookiesDemo_Username', edtUsername.Text, '');      ShowMessage('Username was saved as a cookie in your browser.');
    end;
  end;
end;

Please note the use of VirtualUI.Active before taking any action. This is because we only want to enable the use of cookies to those instances of the application running through VirtualUI. Also, the expirationDate argument is set to empty, indicating that the cookie will never expire.

 

GetCookie

The GetCookie method has a single argument:

1
VirtualUI.BrowserInfo.GetCookie(name);

where,

Argument Data Type Description
name string Name of the cookie. With this name the cookie will be retrieved from the browser. If the cookie doesn’t exist, an empty string will be received.

And the example written in Delphi is:

1
2
3
4
5
procedure TForm1.FormShow(Sender: TObject);
begin
  if VirtualUI.Active then
    edtUserName.Text := VirtualUI.BrowserInfo.GetCookie('CookiesDemo_Username');end;

Just like in the case of SetCookie, in the present case we also making sure that VirtualUI is active (i.e., that this instance of the application runs remotely, using VirtualUI).

Thus, with very few code lines, we can integrate a little further the browser with our application, and add minor enhancements that help improve user experience.

 

The post Remote data persistence using cookies appeared first on Cybele Software.

Enhanced support for DirectX-based GUI frameworks (WPF, FireMonkey, etc) and new C++ native library

$
0
0

Entrada-blog-CPP-and-DirecX

Since its beta release back in May, 2014 we’ve been working nonstop to consolidate and enhance Thinfinity® VirtualUI™, our Instant GUI Remoting and Web Integration solution for Windows applications.

Created to help developers who are confronted with a need for deep modernization of existing Windows-based apps, Thinfinity VirtualUI has always integrated with almost all programming languages through ActiveX/COM and .NET interfaces. We also provided dedicated libraries with source code for popular languages like C#, VB.NET, Visual Basic 6 and Delphi. By adding a new C++ interface library we continue pursuing our goal of trying to make Thinfinity VirtualUI available to an ever increasing number of languages.

We are also continuously working on improving support for DirectX technologies. We currently have full support for Windows Presentation Foundation (WPF) and Delphi FireMonkey GUI frameworks, extending VirtualUI’s readiness to an always growing range of applications.

Fully convinced that you will greatly benefit from what Thinfinity VirtualUI brings to the table, we would like to invite you to take a look at our online live demos, or to visit the product landing page for more information. You will love it!

If you are interested in learning more about Thinfinity VirtualUI just send us an email to support@cybelesoft.com. We’ll be happy to help you!

The post Enhanced support for DirectX-based GUI frameworks (WPF, FireMonkey, etc) and new C++ native library appeared first on Cybele Software.

Controlling the Desktop size with Thinfinity® VirtualUI™

$
0
0

As already discussed in a previous post, one of Thinfinity VirtualUI’s features is that the desktop application doesn’t need to be limited to a fixed or predetermined size. So, each application can be set to determine its desktop size according its needs, or be left to be dynamic in nature.

The image below shows an application configuration panel (available from the Thinfinity VirtualUI Server manager). In this panel we can see the Resolution attribute, which is set, in this case to “Fit to browser window”, its default value.

Control Desktop size with VirtualUI

The available values for Resolution are the following: “Fit to browser window” (that adjust the desktop size to the browser window size), “Fit to screen” (adjust the desktop size to the device’s screen size), and a list of possible predefined fixed values, ranging from 640 x 480 pixels to 1920 x 1200 pixels.

Both “Fit to browser window” and “Fit to screen” values are clearly dynamic, while all the remaining sizes are fixed. In desktop browsers when the defined desktop size is fixed and it does not fit within the available browser window, scroll bars will appear. In the case of mobile devices, the desktop will be automatically scaled to adjust it into the available screen canvas. However, in none of these cases the contents of the desktop (ie, all application windows) may exceed this field, and the excess will be hidden such as in the normal Windows desktop.

 

An in-depth look: the OnBrowserResize event

The VirtualUI.OnBrowserResize event, available in the application side, will receive any change occurring in the browser size. This would allow us to adapt the window layout as needed  (i.e. change sizes, hide, show or move elements or any other adjust what we want, depending on the received width and height values).

Below, a Delphi example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
procedure TForm9.FormCreate(Sender: TObject);
begin
   ...
   VirtualUI.OnBrowserResize := AdjustWindowSize;   ...
end;
 
procedure TMainForm.AdjustWindowSize(Sender: TObject; var Width, Height: Integer;   var ResizeMaximized: Boolean);
   var borderwidth : Integer;
begin
   if WindowState = TWindowState.wsMaximized then
      borderwidth := 16
   else
      borderwidth := 0;
   Width := Self.Width - borderwidth;
   Height := Self.Height - borderwidth;
end;

 

The post Controlling the Desktop size with Thinfinity® VirtualUI appeared first on Cybele Software.

How to send cross-domain messages using Javascript

$
0
0

Thinfinity  - Cross-Domain Messages

A basic rule in Web security holds that there is no direct javascript access between different windows loaded in a browser unless these windows are not from the same origin. This means that the windows must share protocol, host and port or it will not be possible to access a value or object on another page from the JavaScript code, or to add content. So, how can we establish and maintain the communication between different pages loaded in a cross-domain environment?

Fortunately, benefiting from the window.postMessage API, the windows can send non intrusive messages to each other via javascript in a cross-domain environment. These messages can be text, or an object in JSON format (JavaScript Object Notation) —something already available in most of modern web browsers.

In this article we will show how to apply this API to send cross-domain messages using Javascript in a secure way.

 

The window.postMessage API

In order to send a message to another window, one must invoke a postMessage() method, introduced below:

1
targetWindow.postMessage(message, targetDomain, [extra])

where:

targetWindow Reference to the window that will receive the message.
message JSON object or text that will be sent to the target window.
targetDomain Domain to which the message should be posted.
It can also be written “*” not to limit messages to a particular domain, although this is not recommended —unless absolutely necessary.
extra A sequence of extra —optional— objects that could be transferred with the message. The ownership of these optional objects is no longer on the sending side, but is transferred to the destination one.

But if a tree falls in a forest and nobody’s around to hear it, does it make a sound? In other words, it is useless to send the message if no one is prepared to listen to it. In order to be heard, the event message must be attended. Although most modern web browsers employ the addEventListener() method to add the treatment of the event, old IE versions makes use of its own; so we will cover both alternatives:

1
2
3
4
5
if (window.addEventListener){
    addEventListener("message", listenerFunction, false)
} else {
    attachEvent("onmessage", listenerFunction)
}

Where listenerFunction will be used to process the message coming from another window. This function will receive the message in the data attribute of the event received:

1
2
3
4
5
6
7
function listenerFunction(e) {
    if (typeof e.data == "string") {
        console.log(“The message is a text: ” +  e.data);
    } else {
        console.log(“The message is a JSON object:” + JSON.stringify(e.data));
    }
}

 

Some considerations regarding security

Cross-window messaging security model is two-sided. In the event of knowing the domains of the both parties involved —which is usually the case—, and in order to make the exchange of information between different domains safer, it is recommended to check, both when sending and receiving, the domains that participate in the messaging exchange. The sender ensures that the receiving domain is targetDomain. If the sender tries to send a message to a domain different to targetDomain, an error will occur.

The receiver can check the origin attribute of the received message event object to make sure that this came from a valid origin. Therefore, if the domain of origin does not match a valid domain, it can be ignored.

 

Let’s do it!

The following example implements cross-domain communication between two pages: one loaded in the localhost domain, and the other one in IP 127.0.0.1. To handle the exchange of messages we will create two javascript classes (MessageSender and MessageReceiver).

The MessageSender class has a single published method (sendMessage) and, during its creation, it will receive the window to where the message should be sent (targetWindow, mandatory) and the domain to which the message should be directed (targetDomain, optional), which if not received will be replaced by “*”. Both arguments are sent within a JSON object.

1
2
3
4
5
6
7
8
9
10
11
12
var MessageSender = function(args) {
    args = args || {};
    var targetWindow = args.targetWindow;
    var targetDomain = args.targetDomain || "*";
    var ready = false;
    var sendMessage = function(message) {
        targetWindow.postMessage(message, targetDomain);
    }
    return {
        "sendMessage": sendMessage
    }
}

The MessageReceiver class has two published methods. The start method initiates the listening of the message, while the stop method ends it. Both methods could receive a callback function to do an additional processing in the start/stop messaging. This class, when instantiated, is capable of receiving a JSON object with a pair of attributes, both optional. The first is a callback function to process the message; the other, a validOrigin string that indicates the domain from which it would be valid to receive messages. Should any of these parameters is not sent, the first one will be replaced by a default message processor , while the other, by the same page’s domain (i.e., it will not be cross-domain and will only receive messages from its own domain). In case you do not want to control the origin of the message, the validOrigin attribute must explicitly assert “*”.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
var MessageReceiver = function (args) {
    args = args || {};
    var started = false;
    var validOrigin = args.validOrigin || window.location.origin;
    var defaultMsgProcessor = function (e) {
        if (typeof e.data == "string") {
            alert("The message is test:\n'" + e.data + "'");
        } else {
            alert("The message is a JSON object:\n" + JSON.stringify(e.data));
        }
    }
 
    var msgProcessor = args.callback || defaultMsgProcessor;
    var processMessage = function (e) {
        if (validOrigin == "*" || validOrigin == e.origin) {
            msgProcessor(e);
        }
    };
    var startListening = function (onStartCallback) {
        if (!started) {
            if (window.addEventListener) {
                window.addEventListener("message", processMessage, false);
            } else {
                attachEvent("onmessage", processMessage);
            }
            started = true;
            if (onStartCallback) onStartCallback();
        }
    }
    var stopListening = function (onStopCallback) {
        if (started) {
            if (window.removeEventListener) {
                window.removeEventListener("message", processMessage);
            } else {
                window.detachEvent("onmessage", processMessage);
            }
            started = false;
            if (onStopCallback) onStopCallback();
        }
    }
    return {
        "start": startListening,
        "stop": stopListening
    };
};

Both classes will be included in the same javascript file, which we will call crossDomainMessenger.js.

In order to use these classes, we will create two html pages. The first one, crossDomainSource.html, includes the crossDomainMessenger.js file and displays, on the top part, a line containing a text field (where the messages to be sent will be added) and some buttons to send messages. At the bottom of this page, the second page will load in an iframe, which would receive the messages.

From javascript, it will create both a MessageSender and a MessageReceiver; the first one to send messages triggered by the buttons to the page loaded in the iframe; and the last one to address messages that could be sent to the internal page.

This second page will also create a MessageSender and MessageReceiver, with the difference that in this case, the important work that will done by the latter.

The page also has buttons to toggle the message listener; a button to clear the list of messages received; and, a last button to send a message to the other page, which would close the communication circuit between the two windows.

Below you will find the code for the two pages of the example:

crossDomainSource.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Cross domain message example - Sender</title>
    <style>
        html, body { height: 100%; margin: 0px; padding: 0px; }
        #divmsg { height: 50px; line-height: 50px; vertical-align: middle; padding-left: 10px; padding-right: 10px; }
        #divtarget, #targetWindow {display: block; position: absolute; }
        #divtarget { top: 51px; bottom: 5px; left: 5px; right: 5px; border: solid 1px; box-sizing: border-box; }
        #targetWindow { width: 100%; height: 100%; border: none; }
    </style>
    <script type="text/javascript" src="crossDomainMessenger.js"></script>
    <script type="text/javascript">
        var msgSender = null;
        var msg = null;
 
        function init() {
            var targetDomain = window.location.protocol + "//127.0.0.1:" + window.location.port;
            var tw = document.getElementById("targetWindow");
            tw.src = targetDomain + "/crossDomainTarget.html";
            msgSender = new MessageSender({ "targetWindow": window.frames["target"], "targetDomain": targetDomain });            msg = document.getElementById("txtmsg");
 
            // Creates a MessageReceiver that accepts messages from targetDomain only
            new MessageReceiver({ "validOrigin": targetDomain }).start();        }
 
        function sendText() {
            msgSender.sendMessage(msg.value);
        }
 
        function sendObject() {
            msgSender.sendMessage({ "message": msg.value, "currentDate": new Date().toJSON() });
        }
 
        function sendClear() {
            msgSender.sendMessage({ "cmd": "CLEAR" });
        }
    </script>
</head>
<body onload="init()">
    <div id="divmsg">
        <label for="txtmsg">Message:</label>
        <input type="text" id="txtmsg" size="30" />
        <input type="button" value="send text" onclick="sendText()"/>
        <input type="button" value="send object" onclick="sendObject()"/>
        <input type="button" value="send clear" onclick="sendClear()"/>
    </div>
    <div id="divtarget">
        <iframe id="targetWindow" name="target"></iframe>
    </div>
</body>
</html>

crossDomainTarget.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Cross domain message example - Receiver</title>
    <style>
        html, body { height: 100%; margin: 0px; padding: 0px; }
        #msgPanel > div { border-bottom: dashed 1px #808080; }
    </style>
    <script type="text/javascript" src="crossDomainMessenger.js"></script>
    <script type="text/javascript">
        var msgSender = null;
        var msgReceiver = null;
        var msgPanel = null;
        function init() {
            msgPanel = document.getElementById("msgPanel");
            // This MessageReceiver accepts messages from localhost only.
            msgReceiver = new MessageReceiver({                "validOrigin": window.location.protocol + "//localhost:" + window.location.port,                "callback": function (e) {                    if (typeof e.data.cmd != "undefined" && e.data.cmd == "CLEAR") {                        clearMsgs();                    } else {                        addMessage("The message is " +                            ((typeof e.data == "string") ?                                "test:\n'" + e.data + "'" :                                "a JSON object:\n" + JSON.stringify(e.data)));                    }                }            });            start();
            msgSender = new MessageSender({ "targetWindow": window.parent });        }
 
        function start() {
            msgReceiver.start(function () { addMessage("*** msgReceiver was started!") });
        }
 
        function stop() {
            msgReceiver.stop(function () { addMessage("*** msgReceiver was stopped!"); });
        }
 
        function clearMsgs() {
            msgPanel.innerHTML = "";
        }
 
        function sendHellow() {
            msgSender.sendMessage(window.location.origin + " says Hello!");
        }
 
        function addMessage(message) {
            var newmsg = document.createElement("div");
            newmsg.innerHTML = message;
            msgPanel.appendChild(newmsg);
        }
    </script>
</head>
<body onload="init()">
    <div>
        <input type="button" value="start" onclick="start()"/>
        <input type="button" value="stop" onclick="stop()"/>
        <input type="button" value="clear" onclick="clearMsgs()"/>
        <input type="button" value="send msg to opener window" onclick="sendHellow()" />
    </div>
    <div id="msgPanel"></div>
</body>
</html>

This is enough to understand how the messaging between windows works, even when they are in different domains. In upcoming posts we will demonstrate how to use this technique to establish a smooth communication between Thinfinity VirtualUI or Thinfinity Remote Desktop with other applications to which they are integrated.

 

The post How to send cross-domain messages using Javascript appeared first on Cybele Software.


White Labeling and Bundling of web-enabled apps

$
0
0

White labeling

This article describes the new Bundling and White Labeling customization options for a Thinfinity VirtualUI web-enabled Windows application.

In the present note we will give a brief explanation about the Bundling and White Labeling customization of a VirtualUI adapted application. This is the last of a series of three features that we are implementing to assist companies applying for OEM licensing.

In previous articles we described two features that help developers with the management of OEM licensed web-enabled applications:

  • a Server Configuration API, that allows for an automatic and transparent programmatic registration of an application on your Thinfinity VirtualUI Server.
  • an OEM Licensing API to create and revoke end-user’s Thinfinity VirtualUI licenses.

By using Bundling and White Labeling customization, companies now acquire a wider range of tools that will allow them to completely take control of their product’s final branding.

 

Bundling and White Labeling

Let’s start by reminding everyone that White Labeling is applicable only to projects that have applied —and qualified— for OEM partnership. Each registered OEM partner is entitled to bundle Thinfinity VirtualUI along with his/her application and will be responsible for the installation of Thinfinity VirtualUI individual components, as well as their proper registration.

The White labeling process is based on the custom tailoring of the branding information: replacement of images (i.e. the Thinfinity VirtualUI logo), product and company names, services descriptions and some registry keys. To make these changes the developer will fill a set of pre-installation parameters located in an OEM.ini file, which will allow them to assign key registration parameters to avoid conflicting with other possible VirtualUI installations and it will enable them to place his/her own brand in some Thinfinity VirtualUI Manager key places.

All-in-all, Thinfinity VirtualUI white labeling customization will help OEM partners utilize their business’ unique branding. Getting started is quick and easy —the way it should be!

 

OEM Licensing Partnership Program

When you sign up to our OEM Licensing Partnership Program, you can expect:

  • OEM enablement benefits
  • Licensing benefits
  • Technical benefits
  • Marketing benefits

OEM licensing is available upon request. If you are interested in becoming  an OEM partner, please contact us and we will be happy discuss with you the conditions and requirements to qualify as one.

 

The post White Labeling and Bundling of web-enabled apps appeared first on Cybele Software.

[Press Release] Cybele Software Releases Thinfinity VirtualUI v1.1

$
0
0

Thinfinity® VirtualUI v1.1, now with improved scalability and fault tolerance

VirtualUI v1.1The new Thinfinity® VirtualUI™ platform helps developers to effortlessly take to the web Windows applications developed with .Net, Delphi, Visual C++ and the like.

For Immediate Release.

WILMINGTON, DE (August 5th, 2015)—Cybele Software, Inc. today announced the release of version 1.1 of Thinfinity VirtualUI, its Web-Enabling solution for Windows apps. This new release brings important changes to its architecture, which enhances the existing possibilities in terms of availability, scalability and fault tolerance.

New features in Thinfinity VirtualUI v1.1

Complementing the current architecture, two new components have been added to this release: a VirtualUI Gateway Service, which makes possible the redirection of http and websockets traffic to VirtualUI Servers, and a VirtualUI Broker Service, that keeps the load balanced across servers.

These modules provide access to the VirtualUI applications in three new ways:
– By server instances running in different RDS sessions on the same machine.
– Providing balanced access to different physical servers.
– The previous two can also be combined to make the most out of your environment.

 

Support for multiple RDS sessions

Thinfinity VirtualUI now includes support for multiple RDS sessions (RDS – Remote Desktop Services). This new feature makes it possible to increase the number of applications that can be run per server. The new VirtualUI Broker service will be responsible for opening new Windows sessions when needed. Also, this service will keep a balanced load between all open sessions.

Thinfinity VirtualUI - Multiple RDS sessions

Support for Load Balancing scenarios

Enabling the workload distribution across multiple servers has many advantages. Firstly, the load balancing procedure enables developers to achieve optimal resource utilization. Moreover, it avoids overload. Finally, it allows the system to operate properly in the event of failure of some of its components. Thinfinity VirtualUI currently supports two different Load Balancing schemes.

Thinfinity VirtualUI - Load Balancing Scheme

– Gateway/Broker Load Balancing: The VirtualUI Gateway, with the help of the new VirtualUI Broker, is designed to efficiently distribute the load among all registered servers.

– External Load Balancer + Multiple VirtualUI Gateways: In this case, the External Load Balancer will rotate across VirtualUI Gateways. Each VirtualUI Gateway will, in turn, process the connections and balance them among all the available servers.

“This is the first of many notable additions we have in the works”, said Gustavo Ricardi, President and CEO at Cybele Software. “We will continue increasing Thinfinity VirtualUI’s performance”, he added.

###

Documentation:

Product Pagehttps://www.cybelesoft.com/Thinfinity/VirtualUI/
White paper: http://www.cybelesoft.com/docs/thinfinity_virtualui_whitepaper.pdf
Online manualhttp://www.cybelesoft.com/helps/thinfinity/virtualui/

Contact:
For more information about Thinfinity VirtualUI, contact Cybele Software, Inc.
3422 Old Capitol Trail, Suite 1125
Wilmington, DE – 19808, USA.
Phone: (302) 892-9625 or (866) 462-9768 (USA & Canada)
Email: info@cybelesoft.com
Website: www.cybelesoft.com

The post [Press Release] Cybele Software Releases Thinfinity VirtualUI v1.1 appeared first on Cybele Software.

Using One-Time URL for single sign-on scenarios or one-time invitations

$
0
0

Thinfinity VirtualUI offers a special access method called “One-Time URL”. This mechanism was designed to create a temporary, unique url to provide one-time access to a specific application. This temporary url is disposed as soon as it is used or after a specified period of time has elapsed.

These are the main scenarios where the One-Time URL access method is most useful:

 

  • Single Sign-on scenarios.
  • External authentication methods.
  • One-time invitations to run a program  (i.e. application demos/presentations).

 

How it works

The One-Time URL is a unique, disposable URL leading to a specific VirtualUI application. What makes it useful is that it allows for passing credentials and/or custom data to the application through an independent secure channel, hidden to the end user.

 

single sign-on dialog scheme

 

A usual scenario involves a backend service (ie. a web server), where the user’s credentials are validated. This backend service communicates with a VirtualUI Server to request the creation of a One-Time URL, passing information about the application to run, credentials and custom data. This information is stored temporarily and indexed by a unique access key. Also a random passcode is created and used to encrypt the stored information. This access key and passcode are returned to the backend service to build the final One-Time URL.
Once the user is directed to the provided URL (automatically or by clicking on a link), VirtualUI validates the access key and passcode and starts the application passing the associated data. Finally, this key and associated data are removed from memory and therefore the URL becomes invalid. The same happens if the URL was not used for the amount of time specified in the creation request.

 

Creating a One-Time URL

The VirtualUI Server processes a One-Time URL creation request in the form of an http(s) request, as follows:

 

serverurl + "/ws/oturl/get?apikey=" + apikey + "&accesskey=" + accesskey +
    "&userid=" + userid + "&password=" + password + "&customdata=" + customData +
    "&plen=" + passlen + "&expires=" + expires,

where:

serverurl optional VirtualUI Server address (protocol, domain and port)
apikey required VirtualUI installation [API] key. Find this information in Thinfinity.VirtualUI.Server.ini at C:\ProgramData\Cybele Software\Thinfinity\VirtualUI
accesskey required Identifies the application that will be run. Complete this parameter with the “access key” parameter found in the application profile in the VirtualUI Server Manager.
userid optional A valid user that meets the criteria set in the application profile’s ‘Permission’ tab.
password optional The password of the user specified in the ‘userid’ parameter.
customdata optional Use this field to send any information you may need to make available to the application. This is the right place to pass sign-on credentials. This information will be accessible in the application through the BrowserInfo.CustomData property.
plen optional Length of the passcode to be returned.
expires optional Ticket expiration time, in minutes.

 

If the request is unsuccessful, the following HTTP codes can be received:

  • 400: Invalid parameters
  • 401: Userid/password invalid

If the request is successful, the http call returns a 200 HTTP status code, and a JSON object consisting of two fields:

{
    "key": "LnJwsxGHp5d@6MHeiEswRdfxFCiIcLAUttRS$9FSUs-Utz3o",
    "pass":"1U4KRLN0"
}

 

With this information, the backend can build the final URL,following this format:

http(s)://server-url/oturl.html?key=[accesskey]&pass=[passcode]

 

Here’s an example that uses the JSON object shown above:

http(s)://server-url/oturl.html?key=LnJwsxGHp5d@6MHeiEswRdfxFCiIcLAUttRS$9FSUs-Utz3o&pass=1U4KRLN0

 

Single Sign-on Sequence Example

The sequence diagram below shows a complete one-time-URL example using single sign-on. In this example, the same credentials —the ‘jdoe’ username and ‘pass’ password—  are first used for the single sign-on, and then sent to the application in the customdata parameter.

single sign-on sequence

In conclusion, the One-Time URL offers a useful way to extend web-enhanced applications to new scenarios. We are fully convinced that you will greatly benefit from this new Thinfinity VirtualUI feature.

The post Using One-Time URL for single sign-on scenarios or one-time invitations appeared first on Cybele Software.

Multiple RDS sessions to scale Thinfinity VirtualUI availability

$
0
0

Customizing Thinfinity VirtualUIThree weeks ago we released Thinfinity VirtualUI v1.1. This new version includes important changes in the software architecture. One of the additions is the possibility to use multiple Remote Desktop Server (RDS) accounts simultaneously. With this feature, VirtualUI can create more instances of each remotable application and distribute them across different Windows sessions.

Multiple sessions architecture explained

The scheme below shows the basic multiple RDS architecture implemented in a single computer. The VirtualUI Broker, running in the Service session, opens an RDS session for each RDS account specified in the VirtualUI Server Manager. In each of these sessions, an instantiated VirtualUI Server can, in turn, handle several application instances independently.

The VirtualUI Broker administrates the server instances. It checks up on them to make sure they are functional, and it works together with the VirtualUI Gateway to distribute the connections among them.

Thinfinity VirtualUI running multiple RDS sessionsThis capability can be combined with a load balancing architecture, making the most out of your deployment.

 

Adding multiple RDS accounts

In order to enable multiple RDS accounts, go to the ‘RDS’ tab in the VirtualUI Server Manager and press the ‘Add’ button.
Adding RDS Accounts

When adding an account in the RDS tab, you can choose between already existing accounts or use the account that VirtualUI will suggest. If the account suggested by VirtualUI does not exist, it will be created. In any case, the account must have administrator permissions and must allow remote access.

The RDS sessions specified in this tab will be opened by the Broker when Thinfinity VirtualUI starts. Each of them will handle a Server and several independent application instances.

 

Multiple RDS sessions will allow a single computer to overcome some system limitations and handle more concurrent connections than was previously possible.

Summing up, this feature makes a better use of your computer resources and improves the service availability at all times. Enable it to enhance your VirtualUI implementation.

 

 

The post Multiple RDS sessions to scale Thinfinity VirtualUI availability appeared first on Cybele Software.

Keep child windows in front!

$
0
0

VirtualUI: keep child windows in front!Early this year, we shared an article about a Delphi error. This error happens when the Application.MainFormOnTaskBar property is set to False and an application main form is minimized. In this case, this window is, in fact, hidden, but it has no way to be restored.

We also found a similar behavior in applications where child windows —modal or modeless— were drawn or hidden behind the main window; and not in front, as expected. We know that this can happen under certain circumstances when the project has the Application.MainFormOnTaskBar property set to False. This seems to suggest that Delphi could be the culprit.

 

Why the error occurs

All programs send messages to the operative system. Some messages indicate a change in the window “z” order, which defines which form is in the front, and which forms are behind.

When the Application.MainFormOnTaskBar property is set to false, all forms —modal and modeless— are siblings of the mainform. They all share the application window as common parent. Being all siblings, all of them have the chance to go behind the other.

At some point during the display of a modal form, a WM_WINDOWPOSCHANGED message is sent to the mainform, moving it to the top.

 

How to fix the error and keep the child windows in front

The best solution is to set the Application.MainformOnTaskbar property to True right after the Application.CreateForm statement.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
program KeepInFrontExample;
uses
  Forms,
  VirtualUI_AutoRun,
  MainForm in 'MainForm.pas' {FormMain};
 
{$R *.res}
 
begin
  Application.Initialize;
  Application.CreateForm(TFormMain, FormMain);
  Application.MainFormOnTaskbar := True;  Application.Run;
end.

 

In this way, Delphi will set the mainform as parent of all the other forms. This workaround leaves no way for any form to go behind the mainform, as it would be their parent element.

That is how you can make sure child windows are always in front of the mainform.

 

The post Keep child windows in front! appeared first on Cybele Software.

Fixing “Error 5: Access is Denied” when using “Run As” command

$
0
0

Error 5 access is deniedSome days ago, we received an issue report related to an “Access is Denied” error that came up trying to run a Thinfinity VirtualUI web-enabled application. We replicated the user environment, and made absolutely sure that the configuration was identical, but we could not reproduce the error.

In the user environment, the program insisted on running perfectly from the desktop, but it didn’t run through the web using VirtualUI.

We double checked all security settings, unsuccessfully looking for a clue as to why this could be happening.

There’s nothing more annoying than a non reproducible error!

 

Digging for an “Access is denied”

Persistence pays off. The clue to solve the Access Denied error was related to the way VirtualUI runs a program. When you run a program from the desktop (by double clicking on the executable file, etc.) you are using your user account. But when accessing the program through VirtualUI, it is run under a secondary logon, which is exactly what happens when the “Run as…” command is used.

That is why we asked the user to run their desktop application in their environment using the “Run as” command. This test produced the same error and enabled us to isolate it out of VirtualUI.

This put us on the right track. We did some research and we found that the problem occurred in upgraded Windows installations only. In this scenario, when trying to use the “Run as…” command, an “Error 5: Access is Denied” occurs.

In its support site, Microsoft says:

“This issue occurs because the discretionary access control list (DACL) for the Secondary Logon service is not set correctly when you upgrade from Windows Server 2003 or from Windows Server 2008. This problem prevents a standard user from starting this service and from running an application as a different user.”

 

The workaround

We found that the first of the two possible workarounds provided by Microsoft fixed the issue.

To solve the problem, please execute the following command from a cmd prompt window:

net stop seclogon
sc sdset seclogon "D:(A;;CCLCSWRPWPDTLOCRRC;;;SY)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)(A;;CCLCSWRPDTLOCRRC;;;IU)(A;;CCLCSWDTLOCRRC;;;SU)(A;;CCLCSWRPDTLOCRRC;;;AU)S:(AU;FA;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;WD)"

Make sure to copy this command correctly, without the double quotes it fails with this error:

[SC] ConvertStringSecurityDescriptorToSecurityDescriptor FAILED 87:
The parameter is incorrect.

 

By running this command that fixes the secondary logon error, we make sure that it doesn’t produce an “Access Denied” error and the application can work as expected.

The post Fixing “Error 5: Access is Denied” when using “Run As” command appeared first on Cybele Software.

Enhanced Browser and Device DPI support

$
0
0

Among the wide range of valid resolutions that both Thinfinity Remote Desktop and Thinfinity VirtualUI offer, the most commonly used —because of its flexibility and simplicity— is “Fit to Browser”.

This configuration allows you to adjust the remote desktop / remote application to the available browser size. However, when it comes to accessing a desktop from different devices, the “Fit to Browser” propertysometimes huge differences between screen sizes and resolutions (i.e. iPhone 4 vs a 27 inch iMac Retina Display) make it impossible to have a simple rule to determine the best remote size. Even when the application is adjusting properly to the available size, the screen rendered might still look tiny or disproportionate, making the user experience not as satisfactory as expected.

 

Tailoring “fit to browser”

Now, using a new configurable browser detection ruleset, we can tailor the way we want to see of the remote desktop/application on every device. This ruleset allows you to specify rules that will detect the web browser, device and display characteristics, and set the parameters to adjust the remote desktop/application resolution according to your own taste.

The main characteristics that are taken into account are:

  • The browser User Agent, that tells about the web browser and device
  • The device pixel ratio, that tells about the real display resolution
  • The device display size
  • The display orientation (landscape or portrait)

The browser detection ruleset is stored in a file with entries that contain specifications (rules) that match general or specific devices. Each entry (model) can inherit matching parameters (properties) from a more general model. For example, you can define an iOS model and an iPhone4 can inherit the iOS model properties.

A default ruleset file named BrowserRules.ini is installed in the corresponding Thinfinity’s program folder. Then, if it doesn’t exist there yet, it is copied to “\programData\Cybele Software\Thinfinity\{product}\” and renamed as Thinfinity.{product}.BrowserRules.ini. (where {product} is either RemoteDesktop or VirtualUI). You can safely customize this file as it won’t be overridden with a program update.

The structure of this file is as follow:

 

1
2
3
4
5
6
7
8
9
10
[default]
min-width = 640
min-height = 480
max-width = 2560
max-height = 1600
max-device-pixel-ratio = 1
 
[mobile]
parent-model = default
match-mobile = true

 

Model Inheritance

 

A section defines a model, and each model contains a set of properties divided in two groups: matching properties and applying properties.

Models are organized in an inheritance tree, The relationship between models is defined by a special property rule called parent-model, present in all models except [default], which is the tree’s root node and includes some base properties.

Every other model must directly or indirectly inherit from the [default] model. Also, each model contains its own rules and inherits all specifications from its ancestors.

When more than one criteria is met for a device, a scoring system is used to resolve this conflict.

Property reference

Properties can be divided in two groups: matching properties and applying properties.

Matching properties are those used to test the browser and device properties (such as the browser user agent, the device pixel ratio, the display orientation width and height, etc.) in order to choose the best model for each case.

 

match-device-pixel-ratio Matches any device with a specific pixel ratio.
match-mobile Matches any mobile device.
match-orientation Matches any device with the specified orientation: landscape or portrait.
match-screen-height-range Matches any device with a screen height in the specified range. This range is expressed as From-To (for example, 900-1200).
match-screen-width-range Matches any device with a screen width in the specified range. This range is expressed as From-To (for example, 400-600).
match-screen-height Matches any device with a specified screen height.
match-screen-width Matches any device with a specified screen width.
match-user-agent Matches devices by comparing the device browser user agent to the string value supplied. This string is a regular expression.

 

Applying properties are those used to determine the final size and resolution.

 

Use the parent-model property to set the parent model:

 

parent-model Establish the parent model for this model.

 

The following properties deal with the display resolution:

 

device-pixel-ratio Overrides the original device pixel ratio, scaling the content accordingly.
max-device-pixel-ratio This property determines the maximum device pixel ratio accepted. The lesser of the device’s device pixel ratio and this value is applied to scale the display.

 

The following properties deal with the screen size of the remote desktop, in pixels. You can determine it by setting the actual height and width, or by establishing maximum and minimum values for these properties.

 

height Remote desktop height.
width Remote desktop width.
max-height Remote desktop maximum height.
max-width Remote desktop maximum width.
min-height Remote desktop minimum height.
min-width Remote desktop minimum width.

 

The following properties allow you to specify device screen areas that will never be used for displaying the remote connection, such as when a browser or device bar cannot be hidden and uses up screen space. These margins will be excluded for screen size calculations.

 

margin-left Width of an area at the left of the device screen that will not be used for displaying the remote desktop.
margin-bottom Width of an area at the bottom of the device screen that will not be used for displaying the connection.
margin-right Width of an area at the right of the device screen that will not be used for displaying the connection.
margin-top Width of an area at the top of the device screen that will not be used for displaying the connection.

 

Miscellaneous properties (only available to Thinfinity Remote Desktop):

 

use-full-screen For mobile only. If the device’s browser supports the full-screen mode, this property indicates the remote desktop size should be calculated to occupy the whole screen. When not in full screen, the content will be scaled.

 

The calculation process

 

In order to choose a model from the ruleset, Thinfinity uses the client device type, dimensions, resolution, orientation and browser:

 

  1. If match-mobile exists, it tests if device is a mobile.
  2. If match-user-agent exists, it tests the browser’s User Agent.
  3. If match-device-pixel-ratio exists, it tests the device’s pixel ratio.
  4. If match-orientation exists, it tests the device’s orientation.
  5. If match-screen-width-range and match-screen-height-range exist, it tests to see if the screen size is in range.
  6. If match-screen-width and match-screen-height exist, it tests the exact screen size.

 

Once the model is selected, the parameters are applied in this way:

  1. If the width and height properties exist, then it applies them.
  2. If the browser width is less than the min-width, it applies min-width.
  3. If the browser height is less than the min-height, it applies min-height.
  4. If the browser width is greater than the max-width, it applies max-width.
  5. If the browser height is greater than the max-height, it applies max-height.
  6. If a specific device-pixel-ratio was specified, it applies it.
  7. If a max-device-ratio was specified, it takes the minimum of the real device pixel ratio and max-device-ratio property and applies it.

 

This example shows a possible ruleset and how it will affect different devices.

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[default]
min-width = 640
min-height = 480
max-width = 2560
max-height = 1600
max-device-pixel-ratio = 1
 
[mobile]
parent-model = default
match-mobile = true
max-device-pixel-ratio = 2
 
[ipad]
parent-model = mobile
match-user-agent = ipad
 
[iphone4]
parent-model = mobile
match-user-agent = iphone
match-screen-width = 480
match-screen-height = 320
device-pixel-ratio = 1.5

 

In this case, when connecting with an ipad, the following models will be matched:

[default]: This model applies to all devices.

[mobile]: The ipad will match the match-mobile property.

[ipad]: The ipad will match the user agent keyword ‘ipad’ specified in the match-user-agent property.

 

The resulting properties for this device will be:

 

 

1
2
3
4
5
min-width = 640
min-height = 480
max-width = 2560
max-height = 1600
max-device-pixel-ratio = 2

 

Using the same ruleset, when connecting with an iphone4, the following models will be matched:

[default]: This model applies to all devices.

[mobile]: The iphone will match the match-mobile property.

[iphone4]: The ipad will match the user agent keyword ‘iphone’ specified in the match-user-agent property, together with the match-screen-width and match-screen-height properties. An iphone6, with a screen width of 667px, and a screen height of 375px, would match the ‘iphone’ user agent keyword, but not the size.

 

The resulting properties for this device will be:

 

1
2
3
4
5
6
min-width = 640
min-height = 480
max-width = 2560
max-height = 1600
max-device-pixel-ratio = 2
device-pixel-ratio = 1.5

 

Try it yourself to find the best fit for your end-users.

The post Enhanced Browser and Device DPI support appeared first on Cybele Software.


Thinfinity VirtualUI Questions (and Answers)

$
0
0

Questions and Answers about Thinfinity VirtualUIIt’s been a while since Thinfinity VirtualUI was first released. During these months, we have corresponded heavily with our users, who have presented us with all sorts of enquiries.

We include here some of the Thinfinity VirtualUI questions we have received, in the hopes that they can be useful for other users. Some of this feedback has been useful for polishing our documentation, or has turned into other blog entries. Some of our users questions have even played a key role in enhancing VirtualUI, providing us with insights on how to develop a product that better fits our users’ needs.
We would like to thank all of our users who work with us, sending their suggestions and enquiries, and also encourage those who are evaluating our product to contact us if they need any kind of assistance with their deployment.


“I’m using Delphi and I would like to keep the app single-source. Is there any way to make it so that the app does not start in web mode during development? Can I add a switch so that it runs as a desktop application, and only runs in web mode when it is started from a browser?”

If you want to run the application in desktop mode from Delphi, you just have to run the application without debugging.
But if you need to debug the application in desktop mode, you’ll have to comment the VirtualUI_Autorun unit reference.


“After an app is added to the development server manager, I am not sure how to make it start from the browser. Or is this not possible during development testing?”

Yes, it’s possible.
In order to start the application from the browser, you will have to run it in debug mode from the development environment and call it from the browser using the Virtual Path value defined for the application, following this format:

http[s]://<myVirtualUIServerURL>/<Virtual Path value>/

“Users will be accessing my application from multiple devices with different screen sizes. Is it possible to detect what the screen size is at the start of the session and then adjust accordingly? And is it possible to run the application maximized in the browser window, even if this means scaling up?”

You can do it from your application using the BrowserInfo property of the VirtualUI class.
Also you can use the “Fit to browser” resolution mode and take advantage of the new Enhanced Browser and Device DPI support.


“On a mobile device, VirtualUI is positioning a cursor that you have to drag around, and the click event occurs where the cursor is, instead of occurring in the coordinates where the finger tap was done. Is it possible to use absolute positioning and don’t show the cursor?”

By default, Thinfinity VirtualUI transforms touch gestures into relative mouse position coordinates in order to replicate the way a touchpad works, since web enabled applications bear more similarities with desktop than mobile applications in terms of touch behavior. However, if you prefer the classic mobile absolute positioning look and feel, you can enable it modifying the VirtualUI cursor behavior by code:

virtualui.ClientSettings.MouseMoveGestureStyle := MM_STYLE_ABSOLUTE;
virtualui.ClientSettings.MouseMoveGestureAction := MM_ACTION_WHEEL;
virtualui.ClientSettings.CursorVisible := FALSE;

Setting the above lines in the application code hides the cursor and enables the “like mobile” touch mode.

The post Thinfinity VirtualUI Questions (and Answers) appeared first on Cybele Software.

Passing command line arguments to VirtualUI apps

$
0
0

VirtualUI command-line argumentsAn application can accept command line arguments when invoked. This allows the user to send additional information to the application when it is executed.

When an application is launched, the OS passes the command line arguments to the application as a collection of string values, using a white-space as separator.

Applications receive external arguments on a regular basis: when you open a document by clicking on its icon, the OS selects the associated executable program and calls it, sending the full document filename to the program as an argument.

Thinfinity VirtualUI allows you to send external arguments to applications in a transparent way, which works exactly like sending arguments from the command line.

 

Setting command line arguments in the application profile

Thinfinity VirtualUI enables external argument definition from the VirtualUI Server Manager. When you create an application profile, you can specify the execution arguments in the General tab panel of the Application Profiles editor.

settingcommandlinearguments

These arguments will be received by the application as a list of string values, using the white-space character as argument delimiter.

application profile arguments

In Delphi you can get command line arguments using the System.ParamCount and System.ParamStr methods. ParamCount returns the number of arguments, and ParamStr(index) returns each argument, with ParamStr(0) always being the path and name of the invoked program. Here is a way you can show the received arguments list:

1
2
3
  Writeln(Format(‘Arguments = %d’, [ParamCount]))
  for p := 1 to ParamCount do
    Writeln(Format(‘argument %d = “%s”’, [p, ParamStr(p)]);

 

In C++, both the argument counter and the argument list will be received in the two main() function arguments. Like in Delphi, the first argument in the string array is the path and name of the program itself:

1
2
3
4
5
void main(int argCounter, char* arguments[]) {
  cout << "Arguments = " << argCounter << endl;
  for(int i = 1; i < argCounter; i++)
  cout << "arguments[" << i << "] = " << arguments[i] << endl;
}

You can also write the main function header in this way:

1
void main(int argCounter, char** arguments) {}

 

Unlike Delphi and C++, C# excludes the name and path of the program from the arguments collection. Also, C# offers two ways to get the arguments, by using a traditional for…

1
2
3
4
5
6
7
8
public static void Main(string[] arguments)
{
    Console.WriteLine("Arguments = {0}", args.Length);
    for(int i = 0; i < arguments.Length; i++)
    {
        Console.WriteLine("Arguments[{0}] = [{1}]", i, arguments[i]);
    }
}

or by using the foreach:

1
2
3
4
foreach(string s in arguments)
{
    Console.WriteLine(s);
}

 

Sending command line arguments to the application in the VirtualUI url

VirtualUI allows you to send external arguments in the url. Instead of passing static arguments from the application profile definition, using this feature you can send dynamic application arguments in a simple way. The only requirement to be met is that the arguments must be url encoded.

 

To build the url you need:

  • The communication protocol (http or https).
  • The Thinfinity VirtualUI server domain or IP address, and port.
  • The application virtual path.

 

Build the url like this:

1
protocol://server.address:port/virtualpath/?arguments

As an example, we will send the “one”, “two” and “three” argument values to the TestArguments program:

1
http://192.168.0.229:6580/TestArguments/?one%20two%three

The following image shows the submit result:

URL passed arguments

 

Building the URL in Javascript

When necessary, modern browsers encode URLs automatically, but it is convenient to do it by code. The Javascript lines below show how to build the URL used in the previous example:

1
2
3
4
5
6
7
var buildURL = function(server, virtualpath, arguments) {
  return server +/+ virtualpath +/?+ encodeURIComponent(arguments.join(‘ ‘));
}
var baseURL = “http://192.168.0.229:6580/”;
var applicationPath = “TestArguments”;
var arguments = [“one”, “two”, “tree”];
var url = buildURL(baseURL, applicationPath, arguments);

 

Combining both application profile and url command line arguments

If you have defined command line arguments in the application profile and need to send new arguments to the application by url, don’t worry. Thinfinity VirtualUI merges both argument lists, first adding the application profile arguments, and then the arguments that were passed in the url.

merged command-line arguments

The post Passing command line arguments to VirtualUI apps appeared first on Cybele Software.

Thinfinity jsRO remote events in C++

$
0
0

Thinfinity jsRO remote events in C++

Thinfinity jsRO allows programmers to get a better integration between a VirtualUI-extended desktop application and the web browser Javascript world.

We first presented the jsRO Delphi and .Net native interfaces when Thinfinity VirtualUI was launched, and we wrote several articles about it. Some months ago we implemented the jsRO native interface for C++. Today we will share with you how to create custom events in your application and replicate them in the web page.

A brief introduction to jsRO and remote events

Javascript Remote Objects (jsRO) is a framework created to facilitate the best desktop-web integration possible. In order to do this, jsRO implements a bidirectional communication channel between a web page or application and a Windows application. But, most importantly, it makes this blending with the greatest transparency possible.

The key point is that jsRO enables the creation of objects in the Windows application which can be replicated on the browser as native Javascript objects. Every pair of objects stays in sync on both ends of the communication, and behaves as the same entity. This synchronization ensures bidirectional interaction, therefore replicating changes immediately in the jsRO objects’ properties, both on the browser and the Windows application. Also, this communication channel enables messaging between both parts: remote methods, which are defined and handled in the Windows application, and triggered from the browser using javascript; and remote events, which are declared in and triggered from the Windows application, and handled in the browser.

In short, we can say that custom events allow the developer to send messages from the desktop application to be processed in the browser using javascript.

Thinfinity jsRO - Comunication Model

The jsRo remote events allow developers to submit non-visual changes from the application to the javascript side. When the application fires an event, it is transmitted to the browser, enabling the javascript code to receive and process it with a callback function.

Thinfinity jsRO Events

 

Custom events have to be added by the developer to a jsRO model. The following example shows how to tell the browser that a datetime component value was updated in the remote application. To do this, you have to create a JSObject (the jsRO model) and then add the remote event to it.

 

Implementing jsRO remote events in C++

In the form header file, add the Thinfinity VirtualUI header file and declare the JSObject:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#pragma once
 
#include "Thinfinity.VirtualUI.h" 
// CJSROEventsDlg dialog
class CJSROEventsDlg : public CDialog
{
    // Construction
    public:
        CJSROEventsDlg(CWnd* pParent = NULL);
        ~CJSROEventsDlg();
        enum { IDD = IDD_JSROEVENTS_DIALOG };
    protected:
        virtual void DoDataExchange(CDataExchange* pDX);
        HICON m_hIcon;
 
        JSObject * ro; 
        virtual BOOL OnInitDialog();
        afx_msg void OnPaint();
        afx_msg HCURSOR OnQueryDragIcon();
        DECLARE_MESSAGE_MAP()
    public:
        afx_msg void OnDtnDatetimechangeDateselector(NMHDR *pNMHDR, LRESULT *pResult);
};

In the form constructor, create the JSObject, then create the event and add it to the object. Take note of the event argument declaration, with the argument name and type. The third argument (the evt_aux event) is necessary for compatibility purposes only:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
CJSROEventsDlg::CJSROEventsDlg(CWnd* pParent /*=NULL*/) : CDialog(CJSROEventsDlg::IDD, pParent)
{
    EnableActiveAccessibility();
    m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
 
    IJSEvent* evt;    IJSEvent* evt_aux; 
    ro = new JSObject(L"ro");    ro->Events()->Add(_bstr_t(L"dateUpdated"), &evt);    evt->AddArgument(_bstr_t(L"date"), JSDT_JSON, &evt_aux);    ro->ApplyModel(); 
}

Now we are ready to trigger the event. In this case, we want to send the selected date as a JSON structure containing the year, month and day fields:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void CJSROEventsDlg::OnDtnDatetimechangeDateselector(NMHDR *pNMHDR, LRESULT *pResult)
{
    LPNMDATETIMECHANGE pDTChange = reinterpret_cast<LPNMDATETIMECHANGE>(pNMHDR);
 
    _bstr_t year = pDTChange->st.wYear;
    _bstr_t month = pDTChange->st.wMonth;
    _bstr_t day = pDTChange->st.wDay;
 
    _variant_t key(L"date");
 
    _bstr_t value = L"{ \"year\": " + year + ", \"month\": " + month + ", \"day\": " + day + "}";
 
    IJSEvent* evt;    IJSEvent* evt_aux; 
    evt = ro->Events()->Item(L"dateUpdated");    evt->ArgumentAsJSON(key, value, &evt_aux);    evt->Fire(); 
    *pResult = 0;
}

And this is all you need to generate a remote custom event with Thinfinity jsRO in C++. But we still need to catch the event in javascript.

 

Capturing and handling the remote event in Javascript

To capture the remote event in Javascript, we need to declare a jsRO javascript model and the event that will be captured with its respective callback function:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Thinfinity VirtualUI - jsRO Events in C++</title>
 
<script src="virtualui.sdk.min.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function () {
    var virtualUI = new Thinfinity.VirtualUI();
    ...
    ...
    virtualUI.connect();
 
    var jsro = new Thinfinity.JsRO();
 
    jsro.on('ro', ‘dateUpdated’, function (robj) {
        var date = new Date(robj.year, robj.month-1, robj.day);
        console.log('the current selected date is ' + date.toDateString());
    })
 
});
</script>
<body>
....
....
</body>
</html>

Now we are finally ready to send custom events from the remote desktop application to the Web to be handled using Javascript, in a true integration.

The post Thinfinity jsRO remote events in C++ appeared first on Cybele Software.

Download files from VirtualUI web-enabled apps

$
0
0

Download files with Thinfinity Virtualui

Thinfinity VirtualUI web-enabled applications allow you to programmatically send files to be downloaded in the browser. There are no secrets to that, but the VirtualUI.DownloadFile method has variations enough to deserve a short post in this blog.

There are three ways to call this method:

 

  • Passing the local file name as an argument,
  • Passing the local and remote file names as arguments,
  • Passing both filenames and the MIME file type as arguments.

 

Each argument of the DownloadFile method defines more precisely how the file will be handled in the browser.

 

Sending a file to the browser

Sending a file to the browser is easy. Set the FileName argument —including the path, if necessary— to determine the local name of the file to be sent. This name will also be used as the remote file name in the browser. The file type will be resolved by the browser.

The following examples show how to send a file to the browser:

Delphi

VirtualUI.DownloadFile(‘c:\path\filename.ext);

C#

private VirtualUI vui = new VirtualUI();
...
...
vui.DownloadFile(“c:\\path\\filename.ext);

C++

VirtualUI* vui = new VirtualUI();
...
...
vui->DownloadFile(L"c:\\path\\filename.ext”);

 

Changing the remote file name

The two-argument call is used to specify a new remote name for the file. The RemoteFilename value doesn’t include the path, only the LocalFilename value can have a path reference.

Change the remote filename in a download as follows:

Delphi

VirtualUI.DownloadFile(‘c:\path\filename.ext, ‘newname.ext);

C#

private VirtualUI vui = new VirtualUI();
...
...
vui.DownloadFile(“c:\\path\\filename.ext”, “newname.ext);

C++

VirtualUI* vui = new VirtualUI();
...
...
vui->DownloadFile(L"c:\\path\\filename.ext”, L“newname.ext”);

As in the previous case, the file type is resolved by the browser.

 

Why the MIME type?

MIME means “Multipurpose Internet Mail Extensions”. It’s a way of identifying file types on the Internet according to their nature and format. So, the third argument of the DownloadFile method defines the file type and format, which helps browsers select the appropriate handling of the downloaded file. If the value of this argument is blank, VirtualUI internally sets “application/binary” as the MIME type to force the remote download, preventing the browser to try to open it.

Here’s how to send a file to the browser defining its MIME type:

Delphi

VirtualUI.DownloadFile(‘c:\path\filename.ext, ‘newname.ext, ‘application/binary’);

C#

private VirtualUI vui = new VirtualUI();
...
...
vui.DownloadFile(“c:\\path\\filename.ext”, “newname.ext”, “application/binary”);

C++

VirtualUI* vui = new VirtualUI();
...
...
vui->DownloadFile(L"c:\\path\\filename.ext”, L“newname.ext”, L“application/binary”);

 

The OnDownloadEnd event

The file download is an asynchronous process. When you send a file to be downloaded, the program doesn’t stop functioning while it waits for the download to end.

When the file download has finished, VirtualUI triggers an event message that you can catch by attaching an event handler to VirtualUI.OnDownloadEnd. You can use it to inform when the file has been downloaded or, for example, to delete a temporary file that will nevermore be used.

Here’s how to attach a handler for the OnDownloadEnd event:

Delphi

procedure TMainForm.DownloadEndHandle(Sender: TObject; const filename: string);
begin
   ...
end;
...
...
VirtualUI.OnDownloadEnd := Mainform.DownloadEndHandle;

C#

private VirtualUI vui = new VirtualUI();
...
...
vui.OnDownloadEnd += DownloadEndHandle;
...
...
private void DownloadEndHandle(object sender, DownloadEndArgs e)
{
   ...
}

C++

void DownloadEndHandle(std::wstring &filename) {
  ...
}
VirtualUI* vui = new VirtualUI();
...
...
vui->OnDownloadEnd = DownloadEndHandle;

 

A Delphi example

The following example in Delphi shows how to process a file download, selecting the corresponding method call depending on the current options:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
uses
  ..., VirtualUI_SDK;
  ...
  ... 
 
procedure TMainForm.doDownload(localFilename: string; remoteFilename: string; mimeType: string; forceDownload: boolean);
var path: string;
begin
  if forceDownload then begin
    if remoteFilename = ‘’ then
       remoteFilename := localFilename;
    VirtualUI.DownloadFile(localFilename, remoteFilename, '');
  end else if mimeType <> '' then
    VirtualUI.DownloadFile(localFilename, remoteFilename, mimeType)
  else if remoteFilename <> '' then
    VirtualUI.DownloadFile(localFilename, remoteFilename)
  else
    VirtualUI.DownloadFile(localFilename);
end;
 
procedure TMainForm.ShowDownloadEnd(Sender: TObject; const filename: string);
begin
  ShowMessage(ExtractFilename(filename) + ' has been downloaded');
end;
 
procedure TMainForm.FormCreate(Sender: TObject);
begin
  if VirtualUI.Active
    VirtualUI.OnDownloadEnd := Mainform.ShowDownloadEnd;
end;

 

Conclusion

If you need to download files from your web-enabled application, Thinfinity VirtualUI offers you many options to do so. In upcoming articles we will review other exciting Thinfinity VirtualUI features, including the file upload. Stay tuned!

The post Download files from VirtualUI web-enabled apps appeared first on Cybele Software.

AVAST Antivirus is blocking VirtualUI applications in Dev Mode

$
0
0

The Thinfinity VirtualUI Development Mode

Thinfinity VirtualUI Tray IconThe Thinfinity VirtualUI development mode was designed to provide a simple way to implement and test the desktop application web-enabling. When you run your web-enabled desktop application in debug mode from your favorite integrated development environment (IDE) —Visual Studio, Delphi RAD Studio, etc.—, the VirtualUI component, included in the application, makes a call to the Thinfinity VirtualUI Development Server. This call starts the server, if it’s not running yet. When the server starts, the VirtualUI tray icon is added to the system tray bar and you will also find a Thinfinity.VirtualUI.Server.exe instance running, if you explore the Task Manager. Calling the Thinfinity VirtualUI Server address —http://127.0.0.1:6080 by default— from an internet browser will show the application in dual mode: in the desktop and in the browser.

 

The Development Mode Interaction with AVAST Antivirus

But this sequence can’t always be completed successfully. A VirtualUI user contacted our support team because their program didn’t run with Thinfinity VirtualUI in debug mode. When they launched the program, the VirtualUI welcome popup was shown, but nothing else happened. After some testing, we found out that the AVAST antivirus is blocking VirtualUI Development Manager, thus preventing the execution of web-enabled programs in debug mode. This is because the antivirus considers that VirtualUI might incur in “dangerous behavior”. How can you tell if you are having this problem? Symptoms are:

 

  • The Thinfinity VirtualUI icon does not appear in the system tray bar.
  • Thinfinity VirtualUI Server Manager does not run when invoked from the web-enabled application.

 

How to solve this problem

In order to solve this situation, all you need to do is to add the Thinfinity VirtualUI installation folder —typically c:\Program Files\Thinfinity\VirtualUI— to the AVAST exception list. Once this is done, you will be able to run your web-enabled applications in the Thinfinity VirtualUI development mode.

The post AVAST Antivirus is blocking VirtualUI applications in Dev Mode appeared first on Cybele Software.

Viewing all 130 articles
Browse latest View live