Porting REALbasic applications to Windows

I recently ported Belle Nuit Textfilter from Macintosh to Windows 2000 using the latest version of REALbasic 4.

REALbasic does a great job in porting the code, so I needed only some adaptations to have a working application in Windows 2000.

I want to explain some of the adaptations so you can use them for your application.

Platform

Decide what platform you are going to support. I only have access to Windows 2000 systems, so I will only support them. They should work on 95,98,NT and XP as well, but i will not be able to test them. Avoid the situation having clients complaining about bugs on configurations you cannot test.

File format

If you are going to provide a cross-platform application, your documents should be cross-platform, too. You can go three ways

  • Use a public format with the limitations of this format and the necessity to read documents in this format written by foreign applications. This works well for pictures where you can rely on QuickTime
  • Use a proprietary binary format and taking care of problems with LittleEndian and translation of text.
  • Use a XML-Style textfile format avoiding characters over 128.

The last option works great for small data like TextFilter projects. It uses HTML-entities for special characters, binhex-style encoding for binary data and is human-readable as textfile, which is great for debugging.

Filetypes

Be sure to use the order of file extensions you want for the filetypes. The first extension is the one that will be used for saving files. If you use ".ascii;.text;.txt" files will be saved as "myfile.ascii", however if you use ".txt;.ascii;.text", it will still open all these types, but save them as "myfile.txt".

Given you have defined the filetype ".tfp" for your documents, you can tell Windows 2000 that it should open ".tfp" files with your application when you double-click on it, using Folder Options.

  • In the Windows Explorer, choose the command Folder Options of the Tools menu.
  • Choose the File Types tab.
  • Click New and add a file extension "TFP"
  • Select the new file extension in the list.

Click Change, then Other to select yourapplication.exe as the connected application.

Drop me a mail to matti@belle-nuit.com if you know a better solution (like a shell command which could be executed on installation).

Conditional compiling

Avoid splitting the project in two versions, you will not be able to maintain the code. Use conditional compiling instead.

#if targetmacos then
do this
#else
do the windows stuff
#end if

But on repeating code, it is even better to use Constants or Small platform dependent methods as below.

Constants

Use constants whenever possible for the most platform-dependent strings. This avoids too much conditional compiling.

Name Macintosh Windows
Newline chr(13) chr(13)+chr(10)

It is sometimes a little bit difficult to copy special characters in the constants-definitions. You can, however, also define global properties in a module and assign values to them in an Init method.
Sometimes, it is even more complex, so you will write a global method (see the Preferences File example). The global method is still better than using conditional compiling with the same code on two places.

Preferences file

As there is no preferences folder, I use a textfilter.ini file in the applications folder.This makes some changes in the readpreferences and the writepreferences method of the application class.As the rest of the code is not platform-specific, i create a global getpreferencesfolderitem method

Function GetPreferenceFolderItem() as FolderItem
dim f as folderItem
#if targetmacos
f = PreferencesFolder.child("TextFilter Preferences")
#else
f = Getfolderitem("textfilter.ini")
#endif
return f
End Function

SDI or MDI application

You have to decide if you want to make a single document interface (SDI) or a multiple document interface (MDI). If you do MDI, there is a mother window with the menus which does hold the document windows. You do not have much things to add. In SDI, there can only be one document window at one time, with its menu. However, you can have multiple instances of the application.

When you decide for SDI, then you have to change some code.

  • You need to put a separator between the menu bar and the document window.
  • When you create a new document, do not open a new window instance, because it will not have the menubar with it. Instead, you will launch another instance of your program.

f=getfolderItem("textfilter.exe")
if f<>nil then
f.launch
else
beep
end

Be sure that the user does not rename the application.

  • Now, the open document is more complex. If you open the document in the existing window, this is not very elegant, because you will have to ask the user if he wants to change the existing document. If you launch a new instance, however, how do you tell the new application what document it has to open. In my case, i use a temporary .ini file.
    • The openfile menu handler writes a textfilteropen.ini file only with the file path
    • Then it just launches a new instance of the application like above
    • The application checks on launch if there is a textfilteropen.ini file. if there isn't, it opens a new empty document. If there is, it gets the path, deletes the file and opens the specified path.
      This procedure doesn't check however, if multiple instances of the same document are open. This would only be possible if i opened the files as binary file for writing and would leave it open
  • The SDI application cannot have a window menu of open documents, of course.
  • The Close event handler of the document must have the quit command. If you do not quit, you will still have a process running (visible in the Task Manager:Processes), though without window.

User Interface

Checkboxes have a gray background (in fact the gray of the window). This poses a problem for me, because I have a rectangle behind them. I choose then a background color according to the gray background of the window, but the problem is that this window color depends on the appearance settings of the application. I found a workaround which does work half way. I look for a pixel the windows.graphics at a place where I know there are no interface elements. The problem is, that i can ask this property only once the window is drawn, so that this code can neither be used in the rectangles nor in the windows open event. I had to put a timer in the window (single execution, 1 ms) with the code

#if targetwin32 then
parameterrect.fillColor = self.graphics.pixel(self.width-3,3)
#endif

I still set the initial color to middle gray to reduce flicker. If somebody has a better solution how to get this colors, please drop me a mail to matti@belle-nuit.com

I had problems with Popup-menus which did not pop-up in windows. It was difficult to find the actual reason, has to do with some dynamic resizing and control arrays, anyway, I use the popup-menus now only in the mac version and use bevelbuttons in the windows version. This makes them also more ressemble the pushbuttons.

Rectcontrols which have common left,top,width and height properties will be visible and invisible at the same time (parent-child relationship). I had the problem with an Editfield and a Listbox to be shown alternatively, this is not possible. The workaround is to displace the second control by one pixel.

Application icon

Apparently, you cannot override the application icon with an imported ressource file as you can with Macintosh. If you copy a normal icon from ResEdit to the application build settings, you are screwed, because you did not drag the mask with it (and you cannot even remove it any more!), so that the application will just have a blank rectangle as icon. The RB IDE help recommends an Icon Paste utility I did not found on the CD.The solution is to build the application for Macintosh, then to copy the icon from the GetInfo window to the application settings.

Text Encoding

You can use text encoding to convert text, but you must be aware that the native text encoding is not the same. Define a constant for TextEncoding, 0 for MacRoman and &h0500 for Windows. Now if someone has a clue how to get the native encoding other than the MacRoman from the system (in both platforms), please drop me a mail to matti@belle-nuit.com

Known Limitations

I had problems with the following features of Realbasic

  • RB scripts do not work with single and doubles
  • Drag and drop to Global Floating Windows does not work.
  • The volume is a folderitem, but it does not have a creation date.
  • You cannot create new folders in the selectfolder dialog
  • Mac Creator and Mactype do not make sense in FolderItems, so you better make conditional compiling (actually, it doesn't hurt if it is still there). You have to write your own routine for pctypes (last field with separator "." if it is longer than one character. pctypes can now be longer than 3 characters, but also shorter).

DLLs

The application is self-contained, but on the first run, it does actually install some DLLs in the system folder (C:WINNT, C:WINNTsystem32). The files i suspect being from REALbasic are:

  • NONAME.dll
  • rbap350.dll
  • RBRegEx350.dll
  • RBScript350.dll
  • rbselectfolder350.dll

The same file are installed twice in both folders.It looks like these files are created on every new build and modified on every run.

It may happen now the rare case where you are installing on another disk, while the disk C: is full and it cannot create the DLLs. In this case you will get some very strange error message about invalid DLLs.