Info: VisualOffice, CRM Software for all kind of customers.


Already running VFP 9.0 SP1 apps on Windows Vista?

VisualRep - Report Writer and Query Engine
My clients are not willing to wait for SP2 to fix VFP9's problems on Vista. Perhaps your clients cannot wait to run your apps on Vista, too. Here you find my ideas to work around problems that VFP 9.0 SP1 apps may have on Windows Vista. This page is in english because some people from outside germany may be curious, too. I cannot guarantee that everything that worked for me will work for you, too. But I'd like to hear from you if something doesn't work for you.

BTW, talking of 'doesn't work': Visual SourceSafe's diff viewer doesn't work correctly on Vista, too. The left pane is usually blank until you select the code using your mouse or hit some key that shows a different part of your source code (e.g., CTRL+HOME or CLTR+END). Happens with both VSS 6.0 and VSS 2005.

Markus Winhard (Lauton Software GmbH)
mw_at_bingo-ev_dot_de



ManifestTools

Samples how to export, clear and import the manifest included in VFP exe files. I was even successful in removing the built-in manifest from vfp9.exe. See also how to make your VFP exe ask Windows Vista for administrative privilege. Inspired by and built after some sample code from Calvin Hsia's blog of April 13th, 2007. These samples may work on VFP 9.0 SP2 CTP but I didn't try.

If you have problems running my code on Vista run VFP as Administrator (see your VFP9 shortcut's context menu).
Download ManifestTools.zip
(updated April 15th, 2007)

Vista borderless window workaround (updated April 29th, 2007)

Whenever a form's BorderStyle property is different than its parent form class BorderStyle property Vista refuses to repaint the form's borders and title bar when necessary (e.g., after moving part of a form outside of the visible area). Most of the time the affected forms even show up completely borderless when they are run for the first time after the app's start. Top-level forms and form classes are not affected. You also won't run into this problem as long as your app runs with "real" administrative privilege on Vista. Anyway, as most VFP apps are using forms and are not required to be run as administrator, they will run into this problem. I cannot imagine a reason why Vista is blocking a window message to paint a window's border but the people at Microsoft might be more enlightened than me.

You can work around this issue by opening all your forms as a table and reorder the form's properties memo field (look for the record that contains DoCreate in its properties memo field). You have to make sure that the BorderStyle line is before the DoCreate line. Calvin Hsia's blog of April 27th, 2007 shows how to do this programmatically.

The problem with Calvin's approach is that VFP rewrites the whole properties memo field as soon as you change any property of your form. If you don't use Visual SourceSafe with your VFP projects you can run Calvin's code in your project hook's BeforeBuild event. If you're like me using VSS you're out of luck as there's no BeforeCheckIn or AfterSave event.

Fortunately Ricardo Almeida showed a different workaround for forms that are shown In Screen (ShowWindow=0). Indeed none of my forms is shown In Screen but In Top-Level Form or As Top-Level Form. But anyway, I was able to build a workaround based on Ricardo's suggestion. It works for Visual SourceSafe users like me, too, and doesn't require me to change all my forms.

DEFINE CLASS VistaForm AS Form

    PROCEDURE Init
        DO CASE
            CASE
NOT OS(1) == "Windows 6.00"
                
* No Vista, no problem.
            
CASE EMPTY(SYS( 1271, m.Thisform ))
                
* No SCX form, no problem.
            
CASE m.Thisform.ShowWindow == 0        && In Screen.
                
ACTIVATE WINDOW (m.Thisform.Name) IN SCREEN NOSHOW
            CASE
m.Thisform.ShowWindow == 1        && In Top-Level Form.
                
LOCAL lcWindowName
                lcWindowName
= m.Thisform.GetTopLevelFormName()
                
ACTIVATE WINDOW (m.Thisform.Name) IN WINDOW (m.lcWindowName) NOSHOW
            OTHERWISE                            
&& As Top-Level Form.
                * Top-level forms are not affected.
        
ENDCASE
    ENDPROC

    PROCEDURE
GetTopLevelFormName
        
* Get the top level form this form is
        * running in (might be the Vfp Screen).
        
DECLARE INTEGER GetParent IN Win32Api INTEGER ThisHWnd
        
LOCAL lnParentHWnd, i, lcParentName
        lnParentHWnd
= GetParent(GetParent( m.Thisform.HWnd ))
        
IF INLIST( m.lnParentHWnd, _Vfp.HWnd, _Screen.HWnd )
            
lcParentName = "Screen"
        
ELSE
            FOR
i = 1 TO _Screen.FormCount
                IF _Screen
.Forms[ i ].HWnd == m.lnParentHWnd
                    lcParentName
= _Screen.Forms[ i ].Name
                    EXIT
                ENDIF
            ENDFOR
        ENDIF
        RETURN
m.lcParentName
    
ENDPROC

ENDDEFINE



Vista combobox multiselect workaround (updated April 27th, 2007)

There is a coloring issue with some dropped down combo boxes on Vista. When moving your mouse every item under the mouse pointer gets the 'selected item' colors. It looks like you have selected multiple items at once. The combo box continues to work as expected but users may be irritated. Apparently only combo boxes having ColumnCount < 2 are affected (thanks to Ladislaus Fleiss of all-for-one.de for this tip).

Dear reader, just while I was working on this workaround Calvin "the boss" Hsia blogged on a different solution to this problem. And his solution is way cheaper than mine:

DECLARE INTEGER GdiSetBatchLimit IN WIN32API INTEGER
GdiSetBatchLimit(1)

Put it somewhere in your code and call it once. That's it.


If you're still interested in my original workaround, read on.

I added some code to my combobox baseclass to work around this. As you see my apps are only using RowSourceType Value, Array and Fields. If you are using different RowSourceTypes feel free to send me your enhancements. I will publish it here. If you find a problem in my code please drop me a note.

DEFINE CLASS VistaCombo AS Combobox

    
nOriginalColumnCount = -1

    
PROCEDURE Init
        
m.This.VistaBugWorkaround()
    
ENDPROC

    PROCEDURE
VistaBugWorkaround
        
* Only on Vista.
        
IF NOT OS(1) == "Windows 6.00"
            
RETURN
        ENDIF
        WITH This
            
* Remember original ColumCount.
            
IF .nOriginalColumnCount == -1 ;
                    AND .
ColumnCount < 2
                .
nOriginalColumnCount = .ColumnCount
            ENDIF
            
* Make sure ColumnCount >= 2.
            
DO CASE
                CASE
.RowSourceType == 1    && Value.
                    
IF .ColumnCount < 2
                        .
ColumnCount = 2
                        .
ColumnLines = .F.
                        
* Trigger RowSource_Assign().
                        
.RowSource = .RowSource
                    ENDIF
                CASE
.RowSourceType == 5    && Array.
                    
IF .ColumnCount < 2
                        .
ColumnCount = 2
                        .
ColumnLines = .F.
                        
IF .ColumnWidths == ""
                            
.ColumnWidths = LTRIM(STR( .Width ))
                        
ENDIF
                        IF
NOT "," $ .ColumnWidths
                            
.ColumnWidths = .ColumnWidths + ",0"
                        
ENDIF
                    ENDIF
                CASE
.RowSourceType == 6    && Fields.
                    
IF .ColumnCount < 2
                        .
ColumnCount = 2
                        .
ColumnLines = .F.
                    
ENDIF
            ENDCASE
        ENDWITH
    ENDPROC

    PROCEDURE
RowSource_Assign
        
LPARAMETERS tcRowSource
        
WITH This
            IF
.RowSourceType == 1 ;    && Value.
                    
AND .ColumnCount == 2 ;
                    AND
BETWEEN( .nOriginalColumnCount, 0, 1 )
                .
RowSource = STRTRAN( m.tcRowSource, ",", ",," ) + ","
            
ELSE
                
.RowSource = m.tcRowSource
            
ENDIF
        ENDWITH
    ENDPROC

ENDDEFINE



Installing a VFP application on Vista without administrative privilege

There was an article Least Privilege in MSDN magazine recently. It was quite a technical story on problems with msi installer files, COM servers, the UAC in general and programming for least privilege. I thought why not go one step further and write a simple installer for least privilege? Be warned: The following setup method works for me even when I'm logged in on Vista using the built-in 'Guest' account. It also works on Windows XP and should work on Windows 2000, too.

I have the pleasure of maintaining two VFP 9.0 SP1 apps that are given away. If there are problems with the setup of these apps or running them most clients simply tend not to use it. So it is very important for my clients that these apps install and run without any problem at all. For this reason none of these apps are using ActiveX controls or COM servers.

BUT: I've heard that it should be possible to use ActiveX controls in VFP apps without registration. You just have to copy the ActiveX control's *.ocx or *.dll files to your exe's directory. DBI's latest ActiveX controls should support this feature. Other ActiveX controls should work, too, by additionally including their properties to your exe's manifest (see ManifestTools above). If you succeed in using the latter method please let me know.

Tools I used
  • Some program to create zip files
  • WinZip Self Extractor (NOT the 'Personal Edition' but the full-blown one)
  • Visual FoxPro
How I did it

Created a zip file from my app's files and subdirectories. Created a self extracting exe from the zip file using WinZip Self Extractor 3.0. Used the option Self-extracting Zip file for Software Installation. Checked the Unzip automatically checkbox (optional). Entered the name of my setup program in the box Command to issue when unzip operation completes.

My setup program

I'm not allowed to publish the code here so I will only tell what it does. The WinZip self extractor exe unzips everything to a new directory below the user's TEMP directory (on Vista usually C:\Users\<UserName>\AppData\Local\Temp) and runs my setup program. First I have to find out where my preferred install directory is: GETENV("ALLUSERSPROFILE"). Usually on Vista this points to C:\Users\Public. Then I will create a new folder for my application in this directory and copy all files and subdirectories from the current directory to this new folder. The last step is to call my shortcut creation program using VFP's DO command.

Creating shortcuts in the user's start menu and on her desktop

I'm using the SHGetFolderPath() function of ShFolder.dll to determine the current user's start menu 'All Programs' folder (CSIDL_PROGRAMS) and desktop folder (CSIDL_DESKTOP). Usually this is C:\Users\<UserName>\AppData\Roaming\Microsoft\Windows\Start Menu\Programs and C:\Users\<UserName>\Desktop. Then VB6STKit.dll's fCreateShellLink() function is used to create shortcuts in the user's start menu. The safest method I found is always creating the shortcuts at the same location. Afterwards I move them to the desired start menu or desktop directory using the MoveFile() Win32API function. Start menu sub folders can be created by VFP's MD command. Caution: When the user is logged in as a member of the administrators group VB6STKit.dll creates the shortcut in the All Users start menu folder (CSIDL_COMMON_PROGRAMS). Usually this is C:\ProgramData\Microsoft\Windows\Start Menu\Programs. So I'm checking both directories before calling MoveFile().

Hidden pitfalls

Having the letters setup or install in any of my program file names, in any of their file properties or in my VFP project file names would trigger Vista's UAC asking the user for administrative privilege. So I avoided it.

Installing in corporate environments

There are situations when users have even less privilege than Vista's built-in 'Guest' account has. They cannot even create start menu shortcuts or desktop shortcuts. Sometimes they cannot write to their local disk at all (besides the TEMP directory). In these cases I usually get a phone call from an administrator. The administrators are amazed that they can use their unzip program of choice to extract my app to any location they want. They also like the fact that they can run the icon creation program without running the installer. And what they like most is the fact that my apps don't need write access to the registry.


Links:
Session Notes DevCon 2005
Session Notes DevCon 2006