Thursday, May 31, 2012

OS X 10.7.4 and HTMLViewer text fields

With the 10.7.4 update, some of you may have noticed that text input fields inside of HTMLViewers display with a black background. We have investigated the bug and unfortunately it is a bug in OS X and thus, out of our control (filed in Apple's bug system #11454320).

This issue only applies to unstyled input fields that draw with the normal system backgrounds. If you are in complete control of the content you are displaying, you can work around it by adding a style to your field. For example, changing the background color or adding a border.

Since some applications are not in control of all of the content they display with HTMLViewer, there exists a more drastic workaround. Real Studio 2011r4 contains a hidden preference that controls what version of the system your application will appear to be linked against. The flag can be changed by executing this Terminal command while the IDE is quit:

defaults write com.realsoftware.realstudio "Mac SDK Version" -string "10.6"

It an be undone by executing the following while the IDE is quit:

defaults delete com.realsoftware.realstudio "Mac SDK Version"

We do not support the use of this flag and recommend against using it unless you absolutely must. It is a global setting that applies to all projects compiled in the IDE while it is set. If you do use this, test your applications intensively.

Tuesday, May 22, 2012

Doing Progress Right


The ProgressBar control and loops go hand-in-hand. If there is a ProgressBar on a Window, you can bet a For loop is close by. But there are right and wrong ways to do this, and most of the time I see progress code, I see the wrong way. It'll look something like the following, inside the action event of a PushButton:

Dim I As Integer
For I = 0 To 100
ProgressBar1.Value = I

But this is very, very bad code.

Why is it so bad?

In short, your app locks up entirely while the loop is doing its work. The user interface only updates once all your code is finished, so buttons will not be clickable, windows will not be movable, menus not be usable, etc. The OS will probably even list your app as "not responding" if your code takes more than a few seconds to complete. So your user experience suffers.

It's also slower. The Refresh call demands the ProgressBar be redrawn before the next line of code is triggered. This is wasted effort as the ProgressBar doesn't need to update so frequently.

Experienced users may have heard all this before. But there are also more subtle reasons this code is awful. On Windows 7 and Vista, ProgressBars now animate from their previous value to the new value. Since you're locking up your app while processing however, the ProgressBar can never animate, so it will always appear to be one "step" behind.

And in Web projects, the situation is even worse. WebControls have no Refresh method. All user actions, such as a push of a button, get a single "payload" of data to send back to the browser. If we included such a method, the first call to Refresh would work, but then we use up our one payload, and all following UI updates will never reach the browser.

Doing it right

The answer is the Thread class. To oversimplify, Threads allow you to execute code that does not get in the way of the UI update code. However, you absolutely must not interact with any controls from within the Thread. This poses an obvious problem for updating a ProgressBar. That problem is solved by a Timer. The setup is a little on the tedious side, but it is necessary.

Both objects can be placed on any Window, WebPage, or Container. Create a property on your Window called Progress As Integer. In your Thread1.Run event, put the following code:

Dim I As Integer
For I = 0 To 100
// Do your work here
Self.Progress = I

In this sample code, we're not doing any actual work. Yours will of course. Rather than attempting to update the ProgressBar directly, we update the Progress property so the timer can know how much work has completed. And speaking of your timer, use this code for its Action event:

ProgressBar1.Value = Self.Progress
If Self.Progress >= ProgressBar1.Maximum Then
Me.Mode = Timer.ModeOff
End If

We use the timer to update the ProgressBar. There is no need for a Refresh call, because the ProgressBar will redraw automatically when the Timer Action event completes. If the ProgressBar is full, the work is done, so we turn off the Timer. And lastly, to start the work, use this code:

Self.Progress = 0
Timer1.Mode = Timer.ModeMultiple

We first reset the Progress property, just in case. Then "turn on" the Timer so it updates the ProgressBar frequently while the work is being done, and finally actually start the work by calling Thread1.Run. Now all you need to do is update Timer1's properties to adjust the rate you want to ProgressBar to update. On a Desktop app, you can easily get away with a Period of 250. On a Web app, you'll want to slow it down though. A minimum of Period of 1000 is strongly recommended, though I would use 2000 personally. Set Timer1.Mode to Off, there's no need to update the ProgressBar if nothing is happening.

You could also turn all this into a handy reusable class.

That's it. Your ProgressBar will update nicely, your app will be a "good citizen" and your users will be happier.

Monday, May 21, 2012

IDE Tips and Tricks

Here are a few tricks for using the Real Studio IDE more efficiently:

Finding all declarations of a method
Let's assume you have a several methods named "DoSomething" and they are called all over your code, hundreds of times. To find the methods' code you could enter it into the Finder box or right-click on the name where it's called and use the Find command from the contextual menu. But that will reveal not only the method declarations but also all the calls to it. How do you find just the declarations then?

Note that even method declarations in the IDE are internally known with their "proper" RbScript-like code wrapper, e.g.:

  Sub DoSomething (action as Integer)

And you can search these entire lines.

So, to find just the methods, you search for "Sub DoSomething", or even shorter " DoSomething" (note the blank at the beginning), assuming DoSomething is a class member method which is usally referenced with a object name and a ".".

Finding the one declaration of a method
You can quickly jump to a method or property declaration if you hold down the Command (Windows: Control) key and then double click on its name in the code.

Preserving previous Find results when performing another search
Normally, when you perform a Find operation, the results will always be shown in the same Tab, overwriting the previous results.

To prevent that, simply turn the results Tab into its own window, by either right-clicking on the Tab and choosing "Open In New Window", or by dragging the Tab out of the main window. That will open a new window showing just the results. These results won't get replaced when you perform the next search operation.

Remembering breakpoints
Do you use breakpoints often, would you like to keep them when you save and re-open your project? Note that only the XML and RBP project file formats preserve breakpoints, the VCP format doesn't.

If you don't want to switch to the other formats but like your code to stop in certain places, place the statement "break" into the code instead of using a breakpoint. The break statement has the same effect (though you can't simply turn it off during a debugging run like you can with a breakpoint).

Friday, May 18, 2012

Remote Debugging

Did you know that Real Studio includes a Remote Debugger that can make your cross-platform development even simpler than it already is?

When creating cross-platform software, a question I am frequently asked is how to test projects on a platform other than what you are using. Often people think they need to install Real Studio on the other platform and figure out a way to share source code.  I'm happy to say that you do not need to go to all that trouble. Instead use the Remote Debugger.

The Remote Debugger is a small program that lets you run your project on a different target platform. Real Studio communicates with it so that when you run your project, it sends it to the target platform rather than running it locally. Normally when you run your project from within Real Studio (Project->Run), it runs the debug build locally. If you have the Remote Debugger configured, you can instead have Real Studio send and run your debug build on a remote computer, which is incredibly useful for testing cross-platform applications.  This remote build still communicates with Real Studio, so you can access the debugger and test just as if you were running it locally.

To use the Remote Debugger, you need to configure it on both the development machine (where you use Real Studio) and the remote machine. For this example, the development machine is running OS X 10.7.3 and the remote machine is running Windows 7 Ultimate x86.

Remote Machine Configuration

On the remote machine, you need to run the Remote Debugger Stub.  If Real Studio is installed on the remote machine then you already have it. If not, you need to either install Real Studio on the remote machine or copy the Remote Debugger Stub from the development machine to the remote machine.

The Remote Debugger Stub is located in the Extras folder in the Real Studio application folder. Within Extras is a folder called "Remote Debugger Stubs" that contains the stubs for OS X, Windows and Linux.  Once you have the Windows Remote Debugger Stub on the remote machine, run "Remote Debugger Stub.exe". At this point, I also create a shortcut on the desktop (and pin it to the taskbar) so that I can access it quickly.

The Remote Debugger Stub has an Options window that lets you configure some settings.  In the General tab, you want to give the machine a name and select the Download location. You typically do not need to change the settings in the Networking tab.

Remote Debugger Options

After you have set the Options, click OK and leave the Remote Debugger Stub running.

Remote Debugger

If you are using a firewall on the remote machine, you need to make sure that port 44553 is open for UDP and TCP connections.

Development Machine Configuration

On the development machine, you need to configure Real Studio so that it can see the remote debugger stub. In the Preferences for Real Studio, select Debugger. There you'll see a list of configured remote machines.

Debugger Preferences in Real Studio

Click Add to add your remote machine. Depending on your network configuration, the remote machine may appear as an "auto-discovered remote machine". If it does, you can just click its name and then OK to add it. If it does not appear, you can enter the IP address (specified on the Remote Debugger Stub on the remote machine) and give it a name.

Adding a Remote Machine for Remote Debugging

If you are using a firewall on your development machine, you need to make sure that port 44553 is open for UDP and TCP connections and port 13897 is open for TCP connections.

Debugging Remotely

That is it for configuration. Now you can try running a project remotely. Using Real Studio on the development machine, create a new desktop project.

Choose Project->Run Remotely and click your remote machine name. Real Studio will compile and link the project as it usually does, but you will now see an additional step where it sends this debug build over to the Remote Debugger Stub on the remote machine.

When the Remote Debugger Stub has received the debug build, it runs it. Interact with it on the remote machine. Any breakpoints you have set will jump to the debugger on the development machine.

That's It!

Remote Debugging makes cross-platform development with Real Studio even easier than it already is. Of course you can remote debug between any platform, not just from OS X to Windows. You can go from Windows to OS X, OS X to Linux or any combination. You can even remote debug to the same platform, for example OS X to OS X. This is useful when you need to debug things that occur in Refresh or Paint events, which would be triggered when running everything locally on the same machine.

I'd also like to note that the remote machine does not need to be an actual physical machine.  VMware Fusion or Parallels Desktop can host virtual machines that work great with the Real Studio Remote Debugger.

Thursday, May 17, 2012

Monday, May 14, 2012

Debugging Tips

In this article I am going to share a nifty way to help with debugging your code. There are times when you need to figure out which methods of your code are called when. A common solution for this is to add lines like this to your code:

Sub myMethod()
System.DebugLog CurrentMethodName + " Entered"

... your code ...

System.DebugLog CurrentMethodName + " Exited"
End Sub

This can quickly get overwhelming if the methods you want to test have lots of exit points, i.e. return statements, as you'll have to spot all of those and add a line before each of the return statements. It gets even worse if the return statement calls a function, like this:

return mySubroutine()

To get this right, you'd have to modify your code like this:

dim tmpResult as String = mySubroutine()
System.DebugLog CurrentMethodName + " Exited"
return tmpResult

But there's a much simpler solution which brings along a few other enhancements as well: 

Use an object that exists for the time the method is running, like this:

Sub myMethod()
dim myTmpObject as new MyMethodLogger(CurrentMethodName)

With this construct, the class MyMethodLogger will be constructed at the start of the method, and it'll be destructed when the method is exited, automatically. That's all thanks to RB's method of object management (it wouldn't work with the garbage disposal management as it's used by Java and Objective C).

The MyMethodLogger class just needs its constructor and destructor methods implemented, like this:

Sub Constructor(name as String)
System.DebugLog "<"+name+"> Entered"
mName = name
End Sub

Sub Destructor()
LogMsg "<"+mName+"> Exited"
End Sub

No more need to add numerous DebugLog calls before return statements.

But wait, there's more!

With that single class taking care of all the tracking, you can easily add code to have it measure the time that the method takes to execute. And it could count the depth, allowing you to indent the output, making it more readable if you have nested calls you want to trace.

For your convenience, here is a demo project with a ready-to-use MethodProfiler class:


Thomas Tempelmann, fluent in many languages, has been programming for 30 years.
Thomas has been using Real Studio for over 12 years. Thomas will be speaking at Real World 2012 and is looking forward to meeting Realbasic users in Orlando and teaching tricks and tools for Real Studio. Come by and say hello!

Thursday, May 10, 2012

Adding a CommandLink Button on Windows

This brief tutorial will show you how to add native controls on Windows using only Realbasic code.  This assumes some basic knowledge of Win32 APIs.  You can download the complete project here.

In this example we'll be adding a native CommandLink button.  Here are some examples of what a CommandLink button looks like:

For information on when you would use such a button please refer to Microsoft's page on this:

To create our native control we'll start by subclassing Canvas, it will act as our "host."  We will create and setup the control in the Canvas Open event (not the Constructor because the Canvas window handle, i.e. Canvas.Handle, isn't ready at that point):

Sub Open()
  Declare Function CreateWindowExW Lib "User32" ( ex as Integer, className as WString, _
  title as WString, style as Integer, x as Integer, y as Integer, width as Integer, _
  height as Integer, parent as Integer, menu as Integer, hInstance as Ptr, _
  lParam as Integer ) as Ptr

  Declare Function GetModuleHandleA Lib "Kernel32" ( name As Ptr ) as Ptr

  Dim hInstance As Ptr = GetModuleHandleA( Nil )

  Const WS_CHILD = &h40000000
  Const WS_VISIBLE = &h10000000

  mCommandHandle = CreateWindowExW( 0, "BUTTON", "", _
  0, 0, 0, 0, Me.Handle, 0, hInstance, 0 )
End Sub

Notice that we passed Me.Handle to CreateWindowExW as the parent, this allows the Canvas to "host" the control and handle things like clipping, visibility, and Mouse/Drag events.  However, setting the Focus on the CommandLink button is not automatic. Since the Realbasic framework only knows about the Canvas control, tabbing into it does not automatically give the CommandLink button the focus. You will have to add code in the GotFocus event for that:

Sub GotFocus()
  Declare Sub SetFocus Lib "user32" (hwnd As Ptr)
  SetFocus( mCommandHandle )
End Sub

You will also have to manage resizing the control yourself:

  // This code lives in the Open event
  Declare Sub SetWindowPos Lib "User32" ( hwnd as Ptr, after as Integer, _
  x as Integer, y as Integer, width as Integer, height as Integer, flags as Integer )

  Const SWP_NOZORDER = &h4
  SetWindowPos( mCommandHandle, 0, 0, 0, Me.Width, Me.Height, SWP_NOZORDER )

One thing to keep in mind is that the Canvas doesn't have a resized event, so you will have to detect for yourself when the control is resized, for example in the Paint event.  (Side note: for those following Feedback Case #16099 you can use a ContainerControl subclass instead of our Canvas subclass for this).

This next section deals with setting up events for our CommandLink button, we just want to know when the button is clicked. Realbasic tries to shield you from a lot of the nitty gritty details, like calling functions that were built with different calling conventions (i.e. how parameters are pushed and popped off the stack when the function returns).  However, when you deal with Callbacks (i.e. when the OS calls into Realbasic land), you will have to play nice since it expects a certain calling convention for that function. In the Windows world the calling convention is StdCall. In Realbasic we can setup a Shared Method and supply the correct calling convention for that function:

Function WndProc(hWnd as Integer, msg as Integer, wParam as Integer, lParam as Integer) As Integer
  #pragma X86CallingConvention StdCall

  Dim obj As CommandLink = sWndProcMap.Value( hWnd )
  If obj <> Nil Then
    Return obj.InternalWndProc( msg, wParam, lParam )
  End If
End Function

Since this is a shared method it doesn't know what object called it, but we can store that information in a Dictionary:

  // This code lives in the Open event
  If sWndProcMap = Nil Then sWndProcMap = New Dictionary
  sWndProcMap.Value( Me.Handle ) = Self

  Declare Function SetWindowLongW Lib "user32" ( hwnd As Integer, nIndex As Int32, dwNewLong As Ptr ) As Ptr

  Const GWL_WNDPROC = -4
  mOldWndProc = SetWindowLongW( Me.Handle, GWL_WNDPROC, AddressOf WndProc )

Note that we're hooking up the events for the Canvas control (i.e. we're passing Me.Handle instead of mCommandHandle), this will not always be the case but for CommandLink Buttons the WM_COMMAND message that we want gets passed to the parent:

Function InternalWndProc(msg as Integer, wParam as Integer, lParam as Integer) As Integer
  Const WM_COMMAND = &h111
  Const BN_CLICKED = 0

  Select Case msg
    If HIWORD(wParam) = BN_CLICKED Then
      RaiseEvent Action
    End If
  End Select

  Declare Function CallWindowProcW Lib "User32" ( oldProc As Ptr, handle As Integer, msg As Integer, wParam As Integer, lParam As Integer ) As Integer
  Return CallWindowProcW( mOldWndProc, Me.Handle, msg, wParam, lParam )
End Function

This is what our finished product looks like:

Download the complete project to see how to add the detail note and swap the arrows with a custom picture, enjoy!

Tuesday, May 8, 2012

Method Overloading

The Realbasic language supports the ability for methods to have different sets of parameters. One version of a method might take two integers, and another an integer and a string. This is called "method overloading" and the act of determining which overload to invoke is called "overload resolution". 

When you call an overloaded method, the compiler has to figure out which one you mean. It first determines what overloads are valid to call with the given parameters. Then, it takes this list of valid overloads and counts the conversions required to invoke each one. Whichever overload requires the least amount of conversions is the winner and ends up being called. If there is no winner (i.e. there are multiple methods with the same number of conversions), the call is ambiguous and the compiler throws an error.

Now, applying this knowledge to a real world case: consider ListBox.AddRow, which has two overloads:
Sub AddRow(items() as String)
Sub AddRow(ParamArray items() as String)

and the following code:
dim v as Variant = "My string"
listBox1.AddRow( v )

When you pass in a variant, the compiler first sees that both overloads are valid (since variants can convert to arrays or strings). Next, it looks at the number of conversions required and discovers that both of them require one conversion (Variant -> String() or Variant -> String). Since the compiler can't tell which one you want, it gives back an error:

"There are several items with this name and it is not clear which one the call refers to"

In this case, the problem could have been avoided by not using variants (or saying 'v.StringValue', which makes your intention explicit). The issue with variants is that they can implicitly convert themselves to any type and often have unintended side effects. They also effectively bypass some of the compiler's type checking, delaying errors until you run your program.