Foxpert Software Development & Consulting

Menu

Whitepapers
Downloads
Knowlbits
Guineu

2007-02Feb-26

Modal forms in hidden screens

When you call the Show() method of a form passing 1 as a parameter, you expect the form to show modally. There are some cases, when this is not the case, though. For instance, when the form is already visible, you receive a "Cannot change modality of a visible form" error message. Or, if the form is top-level form it cannot be modal.

There's another situation I recently came across when reviewing an application. If _SCREEN is not visible and the form is neither a Top-Level form nor a Desktop form, the Show line won't suspend the program waiting for the user. Even more confusing, if you make _SCREEN visible shortly after, the form remains active, yet is modeless. That makes this a problem that might be hard to spot. Here's some code to reproduce this behaviour:

Public oForm
oForm = CreateObject("Form")
_Screen.Visible = .F.
oForm.Show(1)
MessageBox("Done")
_Screen.Visible = .T.
2007-02Feb-26

The Two Engines

Many databases have optimizers, but Visual FoxPro has two of them. Usually, both work similarly pretending to be one powerful engine. There are times, however, when you need to know the differences between the two engines to write efficient code. The engines I'm talking about are the xBase and the SQL Rushmore optimizer engines.

One such difference is what constitutes an expression that can be optimized. The help file states that such expressions have one of the following forms:

eIndex relOp eExp

or

eExpr relOp eIndex

In other words, both of the following expressions are optimizable:

Name = "Smith"
"Smith" = Name

If you analyze what happens when you run a query with this condition you will see that there are differences between using a FOR clause in a traditional command and a WHERE clause in a SQL statement. A command with a FOR clause runs blazingly fast. It's fully optimized.

The SQL statement, on the other hand, displays a progress bar for the same query. SYS(3054), the function used to determine the optimization level, states that the query is not optimized at all.

In other words: In XBase it doesn't matter whether you write the field name first. In SQL queries, however, it does matter a great deal. To avoid confusion it's a good idea to always use the notation with the field name first.

2007-02Feb-21

Windows Vista

New computers come with Windows Vista preinstalled. Here's my summary of replacing a desktop computer with a new machine:

  1. The new computer doesn't have a PS/2 plug anymore. Since the old and the new keyboard are standard Dell keyboards it wasn't a problem to simply replace the keyboard.

  2. Windows Vista doesn't recognize the Logitech Cordless Click! Optical Mouse. A new Vista compatible driver is available here. That's the location of the German driver. The English one should be easy to locate, too.

  3. There's no Vista driver for the CanoScan N650U. The Windows XP driver seems to work just fine, though. The driver is available here.

  4. One of the most annoying features of Windows Vista is UAC. I applaud Microsoft to default to less privileged accounts. Since I'm running as a regular user on my computer for many years now, I'm really looking forward to have more software available to me that doesn't attempt to write in the program folder and respects my profile folders. However, why does UAC have to bring up an "Unverified publisher warning" every single time I launch a particular application? Once would be more than sufficient.

    As it turns out, there's a simple solution to that problem. The program folder contains an XML file that has the same name as the EXE with the extension ".manifest" added to it. You need to run an editor with administrative privileges, in order to edit the file. Removing the following block fixed the problem for me:

    <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
      <security>
        <requestedPrivileges>
          <requestedExecutionLevel
            level="requireAdministrator"
            uiAccess="false"/>
        </requestedPrivileges>
      </security>
    </trustInfo>
    
  5. 1 GB is definitely the absolute minimum amount of memory for a Vista computer. In the Dell default configuration of Windows Vista Business, all running applications consume about 650 MB of RAM. Once tools like Google Desktop kick in to reindex files, the number of page faults raises significantly.

  6. 2007-02Feb-20

    SYS(2027)

    FoxPro has a great history of remaining backward compatible. Yet, sometimes the behaviour changes without that the documentation reflects this. SYS(2027) is still available in VFP 9, although it's not documented anymore. The syntax for SYS(2027) is:

    cPathOut = SYS(2027,cPathIn)
    

    Back when Visual FoxPro was really a cross platform development tool, you would use SYS(2027) to convert the internally used DOS path into a platform specific path. On FPM this functions returns a Mac-compatible path, on FPU a SCO Unix compatible path.

    During the various versions of FoxPro the behaviour of this function has changed. FoxPro 2.6 for DOS returns the input parameter unchanged. If you pass in an empty string, you get back an empty string. VFP 3 introduced a major change. If you pass in anything that's not a full path, VFP 3 would take the current path and append whatever you passed in.

    In VFP 9 SYS(2027) works like LOWER(FULLPATH()). The passed string is combined with the current directory. Dots and forward slashes are normalized and the whole string is converted into lower case. With the change in VFP 3 you have to be more careful about what you pass to the function. If you pass a valid Mac path such as "::", FPD simply ignored it. In Visual FoxPro you get an "Invalid file or path name" error.

    2007-02Feb-11

    The foxNET series: The language doesn't matter?

    After working in a language like Visual FoxPro for so many years, there are some things that seem so natural that you might not even recognize them as a potential problem. The following code is perfectly useless, but working Visual FoxPro code:

    Local obj
    obj = Null
    If not IsNull(obj) and obj.ToString() <> ""
      ? "true"
    EndIf
    

    If obj is NULL, the first part of the IF condition returns .F. Visual FoxPro is smart enough to realize that this expression can never become .T. again. Therefore it doesn't evaluate the remainder. The following code does the same in C#:

    Object obj = null;
    if (obj != null && obj.ToString() != "")
    {
      Console.WriteLine("true");
    }
    

    Works just as fine as the VFP code. However, you might prefer VB over C# because it's so much closer to VFP. Try running the following code:

    Dim obj As Object
    obj = Nothing
    If obj <> Nothing And obj.ToString() <> "" Then
      Console.WriteLine("true")
    End If
    

    This time you'll see an exception. Unlike C# and VFP, VB.NET always evaluates all parts of an expression. To avoid problems like this, you have two choices. You can either rewrite the expression to use nested Ifs, or you use the AndAlso operator:

    Dim obj As Object
    obj = Nothing
    If obj <> Nothing AndAlso obj.ToString() <> "" Then
      Console.WriteLine("true")
    End If
    
    2007-02Feb-11

    The foxNET series: protected is protected, or is it?

    As a Visual FoxPro developer you discover many familiarities when you read (or write) .NET code. After all, you have been working in an object oriented environment for over a decade whereas the poor VB guys had to jump right from infancy into adulthood, right?

    There are subtle differences between the two platforms, though, even for such basic stuff like visibility of members. In Visual FoxPro you have hidden, protected and public. The last one is not available as a keyword, though. In .NET you have some more modifiers. Among them are private, protected and public. Protected and public sound identical. Hidden can easily be related to private.

    Is it that easy? No. In Visual FoxPro the scope of these modifiers is the object. The following program produces an error in Visual FoxPro:

    ox = CreateObject("Sample")
    ? ox.SetValue(ox,"ox")
    oy = CreateObject("Sample")
    ? oy.SetValue(ox,"oy")
    
    Define Class Sample as Custom
      Protected cValue
      Procedure SetValue( toRef, tcValue )
        toRef.cValue = m.tcValue
      Return m.tcValue
    EndDefine
    

    The first method call works just fine. You can call a method of ox and then use a reference to ox to change the property. If you pass a different instance of the same class, you cannot access the protected properties of the second object. This behaviour is one reason that protected properties are not that widely used among Visual FoxPro developers (compared to other languages, at least). If you have a class where two instances need to exchange data, you have to use a public interface that is open to everyone else, too. Now look at the same program written in C#:

    class Program
    {
      static void Main(string[] args)
      {
        Sample ox = new Sample();
        Console.WriteLine(ox.SetValue(ox, "ox"));
        Sample oy = new Sample();
        Console.WriteLine(oy.SetValue(ox, "oy"));
      }
    }
    class Sample
    {
      protected string cValue;
      public string SetValue(Sample toRef, string tcValue)
      {
        toRef.cValue = tcValue;
        return tcValue;
      }
    }
    

    This code compiles and runs just fine. In Visual FoxPro the visibility of members is scoped to the object instance. In .NET it is scoped to the class (actually, the type). Any instance of a class has access to every single private member of every other instance of the same class. This makes non-public members much more useful than in Visual FoxPro.

    2007-02Feb-10

    The foxNET series: Rounding

    In the past year I've received an increasing amount of incorrect invoices. I'm not talking about fake invoices, virus attachments or subscription reminders. The invoices I'm talking about were real invoices, except that the amount of VAT was off by one cent, sometimes up, sometimes down. What does this have to do with VFP or .NET?

    There are several ways to round numbers. In most cases these varying rounding rules produce the same result. The major difference is how fives are treated. A value like 3.5 can be rounded up to 4 or rounded down to 3. The two rules that are most common in this regard are "rounding away from zero" and "round to even".

    Rounding away from zero means that positive numbers are rounded up (2.5 becomes 3) and negative numbers are rounded down (-2.5 becomes -3). When you round to even, you round up or down depending on the digit just before 5. If the digit is even, you drop the five. Otherwise you round up to the next even digit. So 1.5 is rounded up to 2, but 2.5 is rounded down to 2. Visual FoxPro rounds away from zero:

    ? Round(1.5,0)  && prints 2
    ? Round(2.5,0)  && prints 3
    

    The .NET framework, on the other hand, defaults to round to even:

    Console.WriteLine("1.5 rounded is {0}", Math.Round(1.5));
    Console.WriteLine("2.5 rounded is {0}", Math.Round(2.5));
    

    Both lines print 2. If you want to get the same results as in Visual FoxPro, you have to specify the rounding algorithm as a second parameter:

    Console.WriteLine("1.5 rounded is {0}",
      Math.Round(1.5, MidpointRounding.AwayFromZero));
    Console.WriteLine("2.5 rounded is {0}",
      Math.Round(2.5, MidpointRounding.AwayFromZero));
    

    Now you get the same results as in Visual FoxPro. For the US this change has probably increased the amount of correct invoices, since rounding to even is what accountants use there. In Germany, though, accountants use the "round away from zero" rule. A surprisingly high number of developers don't seem to be aware of the difference. Hence, as accounting software is ported over to .NET here, the number of rounding errors increases.

Previous KnowlBits

RSS

February 2011 (2)

December 2010 (1)

October 2009 (2)

September 2009 (1)

August 2009 (4)

July 2009 (2)

June 2009 (2)

May 2009 (1)

April 2009 (1)

March 2009 (1)

August 2008 (1)

July 2008 (2)

May 2008 (1)

April 2008 (2)

January 2008 (2)

December 2007 (2)

November 2007 (2)

October 2007 (1)

September 2007 (1)

August 2007 (5)

July 2007 (4)

May 2007 (6)

March 2007 (3)

February 2007 (7)

January 2007 (6)

November 2006 (1)

October 2006 (3)

September 2006 (10)

June 2006 (2)

May 2006 (6)

April 2006 (1)


Impressum Kontakt Contact