<?xml version="1.0" encoding="iso-8859-1" ?>
<rss version="2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
 <channel>
  <title>Christof Wollenhaupt</title>
  <link>http://www.foxpert.com/knowlbits.htm</link>
  <description>My notes on Development, the Universe and Everything</description>
  <generator></generator>
  <item>
<title><![CDATA[
Packing tables with FinalBuilder
]]></title>
<link>http://www.foxpert.com/knowlbits_201102_2.htm</link>
<description><![CDATA[<DIV class="fp-text" style="font-family:Verdana,Arial,Helvetica,sans-serif;
"><p>
We use FinalBuilder to package our bi-monthly updates of our FoxPro DOS pharmacy application. It’s a set of multiple FinalBuilder files with hundreds of individual actions. One of the tasks involves creating a clean set of DBF files for specific modules that we ship to our clients when their version got corrupt.
</p><p>
Every two weeks we get updates for these tables. The import, as likely most imports do, involves updating, adding and deleting records. What we ship to our clients should not contain deleted records, so we need to pack them first. That’s a simple job in a FoxPro application. For my FinalBuilder scripts I prefer to use built-in solutions whenever possible.
</p><p>
External programs aren’t totally banned here, but they add subtle problems. Some of my external programs are Visual FoxPro code. FinalBuilder (which runs on a different machine than my development environment is, I must add) calls a compiled Visual FoxPro EXE. More than once I made changes to a program, tested the EXE on my development machine and commited the changes to the SubVersion repository... but forgot to compile an EXE on the build server and place it into the bin directory for the build project.
</p><p>
Hence, whenever I can find a built-in solution with reasonable performance, I’ll use that. FinalBuilder supports ADO to access tables, which means the OLEDB provider for Visual FoxPro gets used. FinalBuilder isn’t a full blown programming languages that would let you create a connection, use multiple command objects, and the like. Instead you have three actions:
</p><ul>
<li>ADO Execute SQL</li>
<li>ADO Execute Stored Procedure</li>
<li>ADO Dataset Iterator</li>
</ul><p>
Each action will connect to the database, execute the statement, do something with the results and disconnect. The code I want to execute is
</p><pre>
USE %table% EXCLUSIVE
PACK
</pre><p>
As you notice these are two code lines. In a program you could connect, send two commands separately and disconnect. Since the VFP OLEDB provider has an instance of VFP running it will just keep tables open in between commands. This doesn’t work with FinalBuilder, though, because FinalBuilder closes the connection.
</p><p>
Fortunately, EXECSCRIPT() is among the supported functions in the OLEDB provider. Also fortunately, the old code to handle non-VFP types of DBF files is still part of the product. Although packing a table creates a new file without deleted records, the resulting table is still a FoxPro 2.6 DOS compatible DBF file.
</p><p>
The - for now - final solution is to use the ADO Execute SQL action, uncheck the “Fail if 0 rows are affected” option and use the following SQL statement:
</p><pre>
ExecScript("USE %table% EXCLUSIVE"+chr(13)+chr(10)+"PACK")
</pre><p>
The final, final solution will likely involve a loop that builds up a number of PACK commands from a FileSet which then serves as the input into the Create ZIP File action. But that’s for another day.
</p></DIV><STYLE>.fp-text
{
	font-size: 11pt;
	text-align: justify;
	line-height: 15pt;
	width: 600px;
	margin-left: auto;
	margin-right: auto;
	text-indent: 10px;
}

.fp-text pre
{
	border: 1px dashed black;
	padding: 6px;
	background-color: Beige;
	font-family:monospace;
	margin-left: 10px;
	margin-right: 10px;
	text-indent: 0px;
	line-height: 100%;
}

.fp-text,.fp-sidebar p
{
	margin-top: 6pt;
	margin-bottom: 6pt;
}

.fp-sidebar
{
	width: 200px;
	vertical-align: top;
	font-size: 11pt;
}

.fp-sidebar #search
{
	margin-top: 30px;
}


.fp-text h1
{
	text-indent: 0px;
	font-size: 11pt;
	font-weight: bold;
	padding: 0px;
	margin: 0px;
}

.fp-text span
{
	font-size: 9px;
	float: right;
	padding: 0px;
	margin: 0px;
	font-weight: normal;
	font-style: italic;
}

.fp-divider
{
	height: 32px;
}

.fp-text h2
{
	text-indent: 0px;
	font-size: 11pt;
	font-weight: bold;
}

</STYLE>]]></description>
<author>Christof Wollenhaupt</author>
<pubDate>Sat, 12 Feb 2011 22:27:16</pubDate>
</item>
<item>
<title><![CDATA[
Significant digits in Visual FoxPro
]]></title>
<link>http://www.foxpert.com/knowlbits_201102_1.htm</link>
<description><![CDATA[<DIV class="fp-text" style="font-family:Verdana,Arial,Helvetica,sans-serif;
"><p>
Steve Bodnar asked an interesting question on Twitter yesterday. Why does he get different results for the following two lines?
</p><pre>
? Round(512.9250000000,2) && 512.92
? Round(512.925000000,2) && 512.93
</pre><p>
Our mathematical education tells us that it shouldn't matter how many zeros the number is padded with. The result has to be the same. Computers commonly don't share the view of their users, nor do they use the same foundation as humans do. We live in a decimal world (except for a few non-metric measurements some countries just refuse to give up), computers live in a binary world. What is an almost perfect number to use, 512.925, is in fact 512.924999999999 to the computer.
</p><p>
Visual FoxPro, like most other languages, uses floating point numbers to express fractions. Those number do not have a fixed precision unlike decimal numbers we use. Working with 15-digits floating point numbers would quickly be cumbersome, nor do they match what we use Visual FoxPro for in reality. Nobody wants an an invoice with the amount spelled out down to the millionths cent.
</p><p>
Fox Software found several solutions to what is basically a presentation issue. On the one side we have obscure commands that hardly anyone can explain properly such as SET DECIMALS and SET FIXED.
To simplify things - down to the point that we are not seeing any longer what's going on behind the scene - Visual FoxPro keeps track of the number of digits every floating value has. This is an extra piece of information that is not part of the numeric value itself.
</p><p>
512.9250000000 and 512.925000000 aren't the same values. Both have the same floating point value 512.924999999999. The former having a precision of 13, the second a precision of 12 attached to it. With a number like this it's quite easy to figure out how many significant places the number has. Just count its digits. But what about using this number in any kind of calculation? How does Visual FoxPro know how many digital places the result shall have? When you run the following code:
</p><pre>
? 512.925000000 font "Courier new"
? 512.925000000+0 font "Courier new"
? 512.925000000*1 font "Courier new"
</pre><p>
you'll notice that the number remains the same (fortunately, otherwise we would more serious things to worry about), however it keeps shifting to the right. Why's that?
</p><p>
When Visual FoxPro prints a value onto the screen, it does so according to the precision stored with the value. There's no way to directly ask VFP for those values.
</p><p>
Someone - probably a poor trainee 25 years ago - specified for every single operation by how many digits the result could vary from the values that participate in the operation. Just printing the number has no effect, therefore it's printed aligned to the left border. When you add two numbers, you can never ever add more than one digit to the left. 9+9 is 18. Hence, the add operation makes room for a potential overflow and adds one digit. Visual FoxPro looks at both sides of the decimal point separately. Adding a 4.1 and a 1.4 number results in a 5.4 number - that is the maximum number of digits on either side plus one for the overflow. Multiplications combine the number of digits:
</p><pre>
? 1.11*1.1 font "Courier new"
? 1.11*1.10 font "Courier new"
? 01.11*01.10 font "Courier new"
</pre><p>
Multiplications also make space for the sign character. In the first line we multiply a 1.2 and a 1.1 value. The result has a total of 6 significant digits: One for the sign, two for the whole numbers and three for the fraction. By adding zeros you change the precision of the expression. Adding zero to the fraction results in four decimal places (2+2), but doesn't move the number to the right. Adding zeros to the whole number though results in the whole number part now being four instead of two digits. The whole number shifts two characters to the right.
</p><p>
With this in mind it becomes easy to understand the rounding issue. The last piece you need to know - as Jody Meyer kindly pointed out on Twitter - is that VFP uses an internal precision of 15 digits. ROUND() - just like any other operation - changes the number of significant places of its result. If you pass in an integer value, you get back the same value plus two decimal places.
</p><p>
Let's start with the second value of Steve's example to show how Visual FoxPro works. The operand has 12 decimal places. ROUND() will add another 2. The result will be a floating point value with 14 significant places. Every value participating in the operation is rounded to that precision. 512.924999999999 - the stored value with the maximum of 15 significant places - is rounded to 512.92500000000, a value with 14 digits. The result of the ROUND() operation is therefore 512.93.
</p><p>
The first line, though has 13 digits. Adding the two digits from the ROUND() operations results in a whopping 15 digit number. This maxes out the number of digits that Visual FoxPro supported. Following the same pattern, the value of 512.924999999999 is rounded to 15 digits. In other words, it remains unchanged. The third decimal place is now a four instead of a five. As a result, Visual FoxPro rounds down to 512.92.
</p></DIV><STYLE>.fp-text
{
	font-size: 11pt;
	text-align: justify;
	line-height: 15pt;
	width: 600px;
	margin-left: auto;
	margin-right: auto;
	text-indent: 10px;
}

.fp-text pre
{
	border: 1px dashed black;
	padding: 6px;
	background-color: Beige;
	font-family:monospace;
	margin-left: 10px;
	margin-right: 10px;
	text-indent: 0px;
	line-height: 100%;
}

.fp-text,.fp-sidebar p
{
	margin-top: 6pt;
	margin-bottom: 6pt;
}

.fp-sidebar
{
	width: 200px;
	vertical-align: top;
	font-size: 11pt;
}

.fp-sidebar #search
{
	margin-top: 30px;
}


.fp-text h1
{
	text-indent: 0px;
	font-size: 11pt;
	font-weight: bold;
	padding: 0px;
	margin: 0px;
}

.fp-text span
{
	font-size: 9px;
	float: right;
	padding: 0px;
	margin: 0px;
	font-weight: normal;
	font-style: italic;
}

.fp-divider
{
	height: 32px;
}

.fp-text h2
{
	text-indent: 0px;
	font-size: 11pt;
	font-weight: bold;
}

</STYLE>]]></description>
<author>Christof Wollenhaupt</author>
<pubDate>Sat, 12 Feb 2011 22:24:40</pubDate>
</item>
<item>
<title><![CDATA[
Unit tests
]]></title>
<link>http://www.foxpert.com/knowlbits_201012_1.htm</link>
<description><![CDATA[<DIV class="fp-text" style="font-family:Verdana,Arial,Helvetica,sans-serif;
"><p>
There are some topics that I've been talking about for years at conferences, user group meetings, in articles, and so forth. One of these topics is code quality. My recent attempt to increase code quality of my fellow developers was the introduction of test-driven development.
</p><p>
To make this a short story: Even after listening to my talk the majority seems to believe that – while this all very nice – in practice writing tests first is way too much for work for too little return. Well, obviously I haven't yet found the best way to get my message across. So, here's another attempt.
</p><p>
If you've been developing in FoxPro for a while you know that Rushmore doesn't work with certain navigation commands, namely GO and SKIP. This makes GO TOP and GO BOTTOM relatively slow commands if a filter is applied to the table that narrows the visible area to just a few records. Both commands will read the entire table from the beginning or the end, respectively, until they encounter the first record that matches the current filter.
</p><p>
The workaround for GO TOP is very easy: You use the LOCATE command. GO BOTTOM is a bit more difficult. You invert the current index order turning the last record into the first record, then you use LOCATE, and finally you revert the index order back to the original one.
</p><p>
Your task for today: Create a procedure GoBottom that navigates to the last record in the current work area using Rushmore and respecting all filters, SET DELETED, and the like. Write the code the same way you would write any other code. BTW, leave HackFox in the shelf. Its code is good, but not good enough.
</p><p>
Now that you've written the procedure, try the following unit tests with your GoBottom procedure (use FoxUnit to create and run the unit tests). Does your GoBottom procedure pass all of them on your first try?
</p><p>
If not, what about writing the unit tests first, next time? Then you wouldn't have released that buggy piece of code.
</p><pre>
Procedure Test_NoIndexSet
	Create Cursor Test (nField I)
	Insert into Test Values (1)
	Insert into Test Values (2)
	Go top
	GoBottom()
	This.AssertEquals ("fail", 2, Test.nField)
	Use
EndProc

Procedure Test_SpaceInPath
	Local lcPath
	lcPath = GetEnv("temp")+"\Test dir"+Sys(2015)
	MkDir (m.lcPath)
	Create Table (m.lcPath+"\Test") (nField I)
	Insert into Test Values (1)
	Insert into Test Values (3)
	Insert into Test Values (2)
	Index on nField Tag nField
	Go top
	GoBottom()
	This.AssertEquals ("fail", 3, Test.nField)
	Use
EndProc

Procedure Test_AscendingIndexSet
	Create Cursor Test (nField I)
	Insert into Test Values (1)
	Insert into Test Values (3)
	Insert into Test Values (2)
	Index on nField Tag nField
	Go top
	GoBottom()
	This.AssertEquals ("fail", 3, Test.nField)
EndProc

Procedure Test_DescendingIndexSet
	Create Cursor Test (nField I)
	Insert into Test Values (1)
	Insert into Test Values (3)
	Insert into Test Values (2)
	Index on nField Tag nField
	Set Order To nField DESCENDING
	Go top
	GoBottom()
	This.AssertEquals ("fail", 1, Test.nField)
	Use
EndProc

Procedure Test_MultipleCdxFilesActive
	Create Cursor Test (nField I)
	Insert into Test Values (1)
	Insert into Test Values (3)
	Insert into Test Values (2)
	Index on nField Tag nField
	Index on nField Tag nField2 of Test.cdx for nField == 2
	Set Order To nField
	Set Order To nField2
	Locate RECORD Reccount()+1
	GoBottom()
	This.AssertEquals ("fail", 2, Test.nField)
	Use
EndProc

Procedure Test_IndexCreatedWithDESCENDING
	Create Cursor Test (nField I)
	Insert into Test Values (1)
	Insert into Test Values (3)
	Insert into Test Values (2)
	Index on nField Tag nField DESCENDING
	Set Order To nField
	Go top
	GoBottom()
	This.AssertEquals ("fail", 1, Test.nField)
	Use
EndProc

Procedure Test_IndexCreatedWithDESCENDINGSetToAscending
	Create Cursor Test (nField I)
	Insert into Test Values (1)
	Insert into Test Values (3)
	Insert into Test Values (2)
	Index on nField Tag nField DESCENDING
	Set Order To nField ASCENDING
	Go top
	GoBottom()
	This.AssertEquals ("fail", 3, Test.nField)
	Use
EndProc

Procedure Test_JustOneIdxFile
	Create Cursor Test (nField I)
	Insert into Test Values (1)
	Insert into Test Values (3)
	Insert into Test Values (2)
	Index on nField To Test.idx
	GoBottom()
	This.AssertEquals ("fail", 3, Test.nField)
	Use
EndProc

Procedure Test_CdxFileAndOneActiveIdxFile
	Create Cursor Test (nField I)
	Insert into Test Values (1)
	Insert into Test Values (3)
	Insert into Test Values (2)
	Index on nField Tag nField2
	Index on nField To Test.idx
	GoBottom()
	This.AssertEquals ("fail", 3, Test.nField)
	Use
EndProc

Procedure Test_IdxFileAndActiveCdxFile
	Create Cursor Test (nField I)
	Insert into Test Values (1)
	Insert into Test Values (3)
	Insert into Test Values (2)
	Index on nField To Test.idx
	Index on nField Tag nField2
	GoBottom()
	This.AssertEquals ("fail", 3, Test.nField)
	Use
EndProc

Procedure Test_IdxFileAndCdxTagHaveSameName
	Create Cursor Test (nField I)
	Insert into Test Values (1)
	Insert into Test Values (3)
	Insert into Test Values (2)
	Index on nField Tag nField2
	Index on nField To nField2.idx for nField == 2
	GoBottom()
	This.AssertEquals ("fail", 2, Test.nField)
	Use
EndProc

Procedure Test_IdxFileAndCdxTagHaveSameNameReverse
	Create Cursor Test (nField I)
	Insert into Test Values (1)
	Insert into Test Values (3)
	Insert into Test Values (2)
	Index on nField Tag nField2
	Index on nField To nField2.idx for nField == 2
	Set Fullpath on
	Set Order To nField2 of (cdx(1))
	GoBottom()
	This.AssertEquals ("fail", 3, Test.nField)
	Use
EndProc
</pre>

</DIV><STYLE>.fp-text
{
	font-size: 11pt;
	text-align: justify;
	line-height: 15pt;
	width: 600px;
	margin-left: auto;
	margin-right: auto;
	text-indent: 10px;
}

.fp-text pre
{
	border: 1px dashed black;
	padding: 6px;
	background-color: Beige;
	font-family:monospace;
	margin-left: 10px;
	margin-right: 10px;
	text-indent: 0px;
	line-height: 100%;
}

.fp-text,.fp-sidebar p
{
	margin-top: 6pt;
	margin-bottom: 6pt;
}

.fp-sidebar
{
	width: 200px;
	vertical-align: top;
	font-size: 11pt;
}

.fp-sidebar #search
{
	margin-top: 30px;
}


.fp-text h1
{
	text-indent: 0px;
	font-size: 11pt;
	font-weight: bold;
	padding: 0px;
	margin: 0px;
}

.fp-text span
{
	font-size: 9px;
	float: right;
	padding: 0px;
	margin: 0px;
	font-weight: normal;
	font-style: italic;
}

.fp-divider
{
	height: 32px;
}

.fp-text h2
{
	text-indent: 0px;
	font-size: 11pt;
	font-weight: bold;
}

</STYLE>]]></description>
<author>Christof Wollenhaupt</author>
<pubDate>Mon, 6 Dec 2010 15:24:26</pubDate>
</item>
<item>
<title><![CDATA[
Implementing SmartCopy
]]></title>
<link>http://www.foxpert.com/knowlbits_200910_2.htm</link>
<description><![CDATA[<DIV class="fp-text" style="font-family:Verdana,Arial,Helvetica,sans-serif;
">
<p>
The following program implements sort of a smart copy in Visual FoxPro. When something is selected, the selected
text is copied. Otherwise the entire current line. So far, only Ctrl+C is implemented.
</p><pre>
*==========================================================
* Implements SmartCopy. When nothing is highlighted, the
* entire line is copied.
*==========================================================

  *--------------------------------------------------------
  * Remove existing keyboard short cut
  *--------------------------------------------------------
  Release Bar _med_copy of _medit
  DEFINE BAR _med_copy OF _medit PROMPT "\&lt;Copy" ;
    After _med_cut ;
    PICTRES _med_copy ;
    MESSAGE "Copies the selection onto the Clipboard"

  *--------------------------------------------------------
  * Invisible popup to capture short cut key.
  *--------------------------------------------------------
  Local lcFile
  lcFile = Sys(16)
  Define Popup SmartCopy
  Define Bar 1 of SmartCopy prompt "Copy" Key Ctrl+C
  On Selection Bar 1 of SmartCopy ;
    do HandleCopy in "&amp;lcFile"


*==========================================================
* Copies the entire line if nothing is selected, default
* behavior otherwise.
*==========================================================
Procedure HandleCopy

  *--------------------------------------------------------
  * Check if we need to execute the default behavior
  *--------------------------------------------------------
  Local llDefault
  _Cliptext = ""
  Sys(1500,"_med_copy","_medit")
  Doevents
  llDefault = not Empty(_Cliptext)

  *--------------------------------------------------------
  * Make sure, FoxTools.Fll is loaded.
  *--------------------------------------------------------
  If not "FOXTOOLS.FLL" $ Upper(Set("Library"))
    Set Library to (Home()+"FoxTools.Fll") Additive
  Endif

  *--------------------------------------------------------
  * Find current window handle
  *--------------------------------------------------------
  Local lnWHandle, laEnv[25]
  If not m.llDefault
    lnWHandle = _WOnTop()
    If m.lnWHandle &lt;= 0
      llDefault = .T.
    Else
      Try
        _EdGetEnv( m.lnWHandle, @laEnv )
        llDefault = (laEnv[25] &lt; 0)
      Catch
        llDefault = .T.
      EndTry
    EndIf
  EndIf

  *--------------------------------------------------------
  * Get the current line content
  *--------------------------------------------------------
  Local lnCurPos, lnLineNo, lnStart, lnEnd
  If not m.llDefault
    lnCurPos = _EdGetPos( m.lnWHandle )
    lnLineNo = _EdGetLNum( m.lnWHandle, m.lnCurPos )
    lnStart = _EdGetLPos( m.lnWHandle, m.lnLineNo )
    lnEnd = _EdGetLPos( m.lnWHandle, m.lnLineNo+1 )
    If m.lnStart &lt; m.lnEnd
      lnEnd = m.lnEnd - 1
      _Cliptext = _EdGetStr(m.lnWHandle,m.lnStart,m.lnEnd)
    Endif
    _EdSetPos(m.lnWHandle, m.lnCurPos)
  EndIf

EndProc
</pre>
</DIV><STYLE>.fp-text
{
	font-size: 11pt;
	text-align: justify;
	line-height: 15pt;
	width: 600px;
	margin-left: auto;
	margin-right: auto;
	text-indent: 10px;
}

.fp-text pre
{
	border: 1px dashed black;
	padding: 6px;
	background-color: Beige;
	font-family:monospace;
	margin-left: 10px;
	margin-right: 10px;
	text-indent: 0px;
	line-height: 100%;
}

.fp-text,.fp-sidebar p
{
	margin-top: 6pt;
	margin-bottom: 6pt;
}

.fp-sidebar
{
	width: 200px;
	vertical-align: top;
	font-size: 11pt;
}

.fp-sidebar #search
{
	margin-top: 30px;
}


.fp-text h1
{
	text-indent: 0px;
	font-size: 11pt;
	font-weight: bold;
	padding: 0px;
	margin: 0px;
}

.fp-text span
{
	font-size: 9px;
	float: right;
	padding: 0px;
	margin: 0px;
	font-weight: normal;
	font-style: italic;
}

.fp-divider
{
	height: 32px;
}

.fp-text h2
{
	text-indent: 0px;
	font-size: 11pt;
	font-weight: bold;
}

</STYLE>]]></description>
<author>Christof Wollenhaupt</author>
<pubDate>Wed, 28 Oct 2009 00:42:30</pubDate>
</item>
<item>
<title><![CDATA[
Locking done quickly
]]></title>
<link>http://www.foxpert.com/knowlbits_200910_1.htm</link>
<description><![CDATA[<DIV class="fp-text" style="font-family:Verdana,Arial,Helvetica,sans-serif;
">
<div style="border-width:thin; border-style:solid; margin-top: 20px; padding: 5px; border-color: gray; background-color: lightyellow" >
<p style="margin-top: 0px;"><b>UPDATE</b></p>
<p>
Joel Leach <a href="http://twitter.com/joel_leach">pointed out on Twitter</a> that the following code accomplishes the same even though Visual FoxPro's documentation states that SET REPROCESS TO 0 SECONDS is an invalid statement:
</p><pre>
LOCAL llLock
SET REPROCESS TO 0 SECONDS
llLock = RLOCK()
UNLOCK
</pre><p>
I'll leave the article on here since the low level approach might be useful for something else, too.
</div>
</p><p>
The only possible approach for finding out whether you can lock a record is to actually lock it. Aside from editing records there can be various reasons why you need to lock records. For instance, one application I'm working on, uses locks in a table to validate the number of seats that the application is licensed to. Every instance tries to find an unlocked record, then locks the record and updates it with some more details. If there's no unlocked record, the user ran out of licenses. Another module determines all locked records and displays all recorded information. This allows administrators and supporters to find out who is still in the application.
</p><p>
A straight forward approach would be to scan through the table, lock each record and continue until RLOCK() returns .T. Problems arise when the number of work stations and licenses grows. The first record is way more likely to be locked than the last one. Hence, the more users are logged on the longer it takes to launch the application for new users.
</p><p>
One approach I implemented in the past to get around this is to check for random record numbers instead of scanning through the table. Just generate a random number between 1 and RECCOUNT(). Navigate to the record, attempt to lock it, repeat with a new record. Probably you want to limit the number of attempts to something like two or three times the total number of records before exiting the application. This approach distributes locked records equally in the table. It's still slow, though.
</p><p>
Visual FoxPro never tries to lock a record just once (let me know if you disagree with this statement). Instead Visual FoxPro uses the value of SET REPROCESS TO for a number of attempts. There are various options such as locking for a number of seconds, a number of attempts or eternally until the user cancels out. In the end, though, it all boils down to Visual FoxPro repeatedly trying to lock the record until a termination condition is met.
</p><p>
SYS(3051) controls the interval between attempts. Possible values for this function are limited, though. The interval can be anything between 100 ms and one second. The default is 333 ms meaning that a value of 3 for SET REPROCESS makes the app wait up to one second before RLOCK() returns .F.
</p><p>
If you want to check a huge number of records, you want to minimize this time. The fastest possible setting in Visual FoxPro is
</p><pre>
SET REPROCESS TO 1
SYS(3051,100)
</pre><p>
When you attempt to lock a record that is already locked, you will know after 100 ms, more or less. Sufficient when all you want is to save a record, way to slow if you need to check 100 records to find an unlocked one. Even with this fast configuration, Visual FoxPro still performs two attempts to lock the record. One that fails right away putting VFP into a wait state. The second one occurs after the specified interval.
</p><p>
When I needed to process potentially more than 100 records recently, this approach was unusable. Therefore I searched my blog for a solution and came across the description of <a href="http://www.foxpert.com/knowlbits_200707_2.htm">how locking works</a> in Visual FoxPro.
</p><p>
Obviously, the problem in Visual FoxPro is not the attempt to lock the record, but the interval between the two attempts that you can't make shorter than 100 ms. Therefore my solution works on the API level to only make a single locking attempt. Either it fails, or doesn't. Calculating the locking position is fairly easy to do. It's 0x7FFFFFFE-Recno(). For the LockFile function you need a handle to the DBF file. Lacking an easy way of obtaining a file handle to a table opened by VFP, I opted for the API solution of opening the file a second time in shared mode. Having said this the code is really straight forward.
</p><pre>
*==========================================================
* Attempts to lock a record exactly once. If the record
* cannot be locked for whatever reason, .F. is returned.
* The record does not remain locked.
*
* There's no guarantee that a subsequent RLOCK() command
* works. It might fail even when isLocked() returns .T.
* when another machine locked the record in the meantime.
* It might had worked, if you tried to lock the record as
* the other machine released the lock already.
*
* This method is for code that needs to quickly scan a
* larger number of records to find a locked or unlocked
* one, for instance, when you need to clean certain
* records, or when you use locked records in a license
* tables to limit the number of instances.
*==========================================================
Procedure LockingPossible()

  *--------------------------------------------------------
  * If this instance has locked the record, we return
  * immediately.
  *--------------------------------------------------------
  If IsRLocked()
    Return .T.
  EndIf

  *--------------------------------------------------------
  * We can't check exclusively opened files. But then there
  * shouldn't be a problem obtaining a lock.
  *--------------------------------------------------------
  If IsExclusive()
    Return .T.
  EndIf

  *--------------------------------------------------------
  * API declarations
  *
  * source: http://www.news2news.com
  *--------------------------------------------------------
  DECLARE INTEGER LockFile IN kernel32;
      INTEGER hFile, INTEGER dwFileOffsetLow,;
      INTEGER dwFileOffsetHigh,;
      INTEGER nNumberOfBytesToLockLow,;
      INTEGER nNumberOfBytesToLockHigh
  DECLARE INTEGER OpenFile IN kernel32;
      STRING   lpFileName,;
      STRING @ lpReOpenBuff,;
      INTEGER  wStyle
  DECLARE INTEGER CloseHandle IN kernel32;
      INTEGER hObject

  *--------------------------------------------------------
  * Open the table a second time and attempt to lock the
  * record using low level access.
  *--------------------------------------------------------
  Local lcBuffer, lnFile, llLock
  #DEFINE GENERIC_READ 0x80000000
  llLock = .F.
  lcBuffer = Replicate(Chr(0), 250)
  lnFile = OpenFile(Dbf(), @lcBuffer, GENERIC_READ)
  If m.lnFile > 0
    If LockFile(m.lnFile, 0x7FFFFFFE-Recno(), 0, 1, 0) <> 0
      llLock = .T.
    EndIf
    CloseHandle(m.lnFile)
  EndIf

Return m.llLock
</pre>
</DIV><STYLE>.fp-text
{
	font-size: 11pt;
	text-align: justify;
	line-height: 15pt;
	width: 600px;
	margin-left: auto;
	margin-right: auto;
	text-indent: 10px;
}

.fp-text pre
{
	border: 1px dashed black;
	padding: 6px;
	background-color: Beige;
	font-family:monospace;
	margin-left: 10px;
	margin-right: 10px;
	text-indent: 0px;
	line-height: 100%;
}

.fp-text,.fp-sidebar p
{
	margin-top: 6pt;
	margin-bottom: 6pt;
}

.fp-sidebar
{
	width: 200px;
	vertical-align: top;
	font-size: 11pt;
}

.fp-sidebar #search
{
	margin-top: 30px;
}


.fp-text h1
{
	text-indent: 0px;
	font-size: 11pt;
	font-weight: bold;
	padding: 0px;
	margin: 0px;
}

.fp-text span
{
	font-size: 9px;
	float: right;
	padding: 0px;
	margin: 0px;
	font-weight: normal;
	font-style: italic;
}

.fp-divider
{
	height: 32px;
}

.fp-text h2
{
	text-indent: 0px;
	font-size: 11pt;
	font-weight: bold;
}

</STYLE>]]></description>
<author>Christof Wollenhaupt</author>
<pubDate>Tue, 27 Oct 2009 10:22:56</pubDate>
</item>
<item>
<title><![CDATA[
Visual FoxPro crashes after installing hot fix or service pack 2
]]></title>
<link>http://www.foxpert.com/knowlbits_200909_1.htm</link>
<description><![CDATA[<DIV class="fp-text" style="font-family:Verdana,Arial,Helvetica,sans-serif;
"><p>
This is an error report that increasingly comes up lately. Visual FoxPro applications load two files when you run the EXE or instantiate a COM server: VFP9R.DLL and VFP9Rxxx.DLL. The first is the actual runtime library; the second file is a localized resource file. This file contains all localized strings such as menus, error messages, dialogs, names for months and week days, and so forth. There are multiple versions of this file. VFP9RENU.DLL is the English version, VFP9RDEU.DLL the German version, and so on. Which file Visual FoxPro loads depends:
<p></p>
If you have specified a particular version using the -L command line switch, this is the library that VFP loads.
<p></p>
Otherwise Visual FoxPro uses the regional settings of the operating system to load the correct runtime.
<p></p>
If the corresponding file cannot be found, Visual FoxPro always reverts to VFP9RENU.DLL, the English version.
<p></p>
These files are searched for in the current directory and the Windows path. That means, if you have one of these files in your System directory, Visual FoxPro might very well find and use this file. A crash happens when there's a version mismatch. The RTM version of VFP9R.DLL needs the RTM version of VFP9RENU.DLL, the SP1 version needs the SP1 version, and the SP2 version needs the corresponding SP2 file.
<p></p>
One scenario that causes a crash is having a SP1 VFP9Rxxx.DLL somewhere in the path and then installing the hotfix. If the resource file hasn't been replaced, too, Visual FoxPro will crash. This is more likely to happen outside English speaking countries, because SP1 and SP2 do contain localized versions, but the hot fix does not: There's only a VFP9RENU.DLL embedded into the MSM file. Before you can install the hotfix you should install SP2 to make sure that all runtime files are up-to-date.
<p></p>
If you or your clients experience crashes after installing the new version, search the entire hard disk to see if there are any older VFP9R*.DLL files around. Make sure to search hidden folders, as well.
<p></p>
In my tests the version difference between SP2 and the hotfix does not seem to inhibit the same behavior. That is, the hotfix version of VFP9R.DLL works with the VFP9RDEU.DLL version of service pack 2.
</p>
</DIV><STYLE>.fp-text
{
	font-size: 11pt;
	text-align: justify;
	line-height: 15pt;
	width: 600px;
	margin-left: auto;
	margin-right: auto;
	text-indent: 10px;
}

.fp-text pre
{
	border: 1px dashed black;
	padding: 6px;
	background-color: Beige;
	font-family:monospace;
	margin-left: 10px;
	margin-right: 10px;
	text-indent: 0px;
	line-height: 100%;
}

.fp-text,.fp-sidebar p
{
	margin-top: 6pt;
	margin-bottom: 6pt;
}

.fp-sidebar
{
	width: 200px;
	vertical-align: top;
	font-size: 11pt;
}

.fp-sidebar #search
{
	margin-top: 30px;
}


.fp-text h1
{
	text-indent: 0px;
	font-size: 11pt;
	font-weight: bold;
	padding: 0px;
	margin: 0px;
}

.fp-text span
{
	font-size: 9px;
	float: right;
	padding: 0px;
	margin: 0px;
	font-weight: normal;
	font-style: italic;
}

.fp-divider
{
	height: 32px;
}

.fp-text h2
{
	text-indent: 0px;
	font-size: 11pt;
	font-weight: bold;
}

</STYLE>]]></description>
<author>Christof Wollenhaupt</author>
<pubDate>Tue, 15 Sep 2009 11:04:30</pubDate>
</item>
<item>
<title><![CDATA[Installing Windows 7 on VMware Server 2.0.1
]]></title>
<link>http://www.foxpert.com/knowlbits_200908_4.htm</link>
<description><![CDATA[<DIV class="fp-text" style="font-family:Verdana,Arial,Helvetica,sans-serif;
"><p>
VMware Server does not yet support Windows 7, but that doesn't mean you have to wait for a new release. When creating a new virtual machine, pick either Windows Vista 32 bit or Windows Vista 64 bit instead. Downloading the ISO file from MSDN Subscriber Download, placing this file in one of the data stores and mounting the image into the virtual CD drive is the easiest way to get the setup running.
</p><p>
Windows starts the installation right after powering on the virtual machine. The remaining steps are pretty self-explanatory. When you are prompted to enter a key, you can skip this step. It's easier to enter the key later in Windows 7 when you have access to the MSDN Key list and simply can copy and paste the key.
</p><p>
After Windows 7 started, the first change should be to disable the mouse pointer shadow in Start > Control Panel > Mouse.
</p><p>
The next step is usually to install VMware tools. At least with Windows 7 in the 64 bit version clicking on the link in VI Web Access to activate the VMware Tools image only results in an error message: The operating system is not supported. You can activate the image manually, though. On the server that hosts VMware Server open C:\Program Files (x86)\VMware\VMware Server and copy the Windows.iso file into your data store. Then use VI Web Access to mount this ISO image.
</p><p>
Inside Windows 7 open the CD drive and run Setup64.EXE. Following the steps and rebooting the system enables VMware tools with the known benefits such as having better mouse support.
</p>

</DIV><STYLE>.fp-text
{
	font-size: 11pt;
	text-align: justify;
	line-height: 15pt;
	width: 600px;
	margin-left: auto;
	margin-right: auto;
	text-indent: 10px;
}

.fp-text pre
{
	border: 1px dashed black;
	padding: 6px;
	background-color: Beige;
	font-family:monospace;
	margin-left: 10px;
	margin-right: 10px;
	text-indent: 0px;
	line-height: 100%;
}

.fp-text,.fp-sidebar p
{
	margin-top: 6pt;
	margin-bottom: 6pt;
}

.fp-sidebar
{
	width: 200px;
	vertical-align: top;
	font-size: 11pt;
}

.fp-sidebar #search
{
	margin-top: 30px;
}


.fp-text h1
{
	text-indent: 0px;
	font-size: 11pt;
	font-weight: bold;
	padding: 0px;
	margin: 0px;
}

.fp-text span
{
	font-size: 9px;
	float: right;
	padding: 0px;
	margin: 0px;
	font-weight: normal;
	font-style: italic;
}

.fp-divider
{
	height: 32px;
}

.fp-text h2
{
	text-indent: 0px;
	font-size: 11pt;
	font-weight: bold;
}

</STYLE>]]></description>
<author>Christof Wollenhaupt</author>
<pubDate>Wed, 19 Aug 2009 12:00:42</pubDate>
</item>
<item>
<title><![CDATA[Object reference counters in VFP
]]></title>
<link>http://www.foxpert.com/knowlbits_200908_3.htm</link>
<description><![CDATA[<DIV class="fp-text" style="font-family:Verdana,Arial,Helvetica,sans-serif;
"><p>
Visual FoxPro maintains a reference counter for every object. Whenever a reference to an object is stored in a variable or property, the reference counter is incremented by one. When the same property or variable is filled with a different value, or when it goes of scope, Visual FoxPro decrements the counter by one. Visual FoxPro releases the object once the counter reaches zero.
</p><p>
The Visual FoxPro API provides two functions to manage this reference counter from C++ libraries: <a href=" http://msdn.microsoft.com/en-us/library/z1t8fxz0%28VS.80%29.aspx">_ObjectReference</a> and <a href=" http://msdn.microsoft.com/en-us/library/b67ybx6z%28VS.80%29.aspx">_ObjectRelease</a>. To make them available to Visual FoxPro developers, I wrote a small FoxPro library called <a href=" http://www.foxpert.com/download/OBJREF.FLL">ObjRef.FLL</a>.
</p><p>
The syntax is very simple. If you want to increment the object reference counter, you call the IncrementRef() function passing the object as the only parameter. The following sample creates a form and stores the reference in a local variable. Normally, the form would be released as soon as the code finishes. That is, you would see the form flash very briefly. With the call to IncrementRef you basically create a dangling reference to the form letting the form stay around even after the code returns:
</p><pre>
* Create a form with just a local reference
Local loForm
loForm = CreateObject("Form")
loForm.Show()

* Create artificial dangling reference
Set Library To ObjRef.FLL
IncrementRef(loForm)
</pre><p>
Similarly you can force an object to release by decrementing the reference counter. Even so the method hasn't finished yet, the form is gone:
</p><pre>
* Create a form
Local loForm
loForm = CreateObject("Form")
loForm.Show()

* Release form
Set Library To ObjRef.FLL
DecrementRef(m.loForm)
Wait window
</pre><p>
Playing around with object reference counters isn't something you should normally do. Visual FoxPro doesn't expect this and in some cases might respond erratically. When you, for instance, execute the following code and then type in the Command Window, Visual FoxPro either crashes or brings up error messages.
</p><pre>
Set Library To ObjRef.FLL
DecrementRef(_Screen)
</pre>
</DIV><STYLE>.fp-text
{
	font-size: 11pt;
	text-align: justify;
	line-height: 15pt;
	width: 600px;
	margin-left: auto;
	margin-right: auto;
	text-indent: 10px;
}

.fp-text pre
{
	border: 1px dashed black;
	padding: 6px;
	background-color: Beige;
	font-family:monospace;
	margin-left: 10px;
	margin-right: 10px;
	text-indent: 0px;
	line-height: 100%;
}

.fp-text,.fp-sidebar p
{
	margin-top: 6pt;
	margin-bottom: 6pt;
}

.fp-sidebar
{
	width: 200px;
	vertical-align: top;
	font-size: 11pt;
}

.fp-sidebar #search
{
	margin-top: 30px;
}


.fp-text h1
{
	text-indent: 0px;
	font-size: 11pt;
	font-weight: bold;
	padding: 0px;
	margin: 0px;
}

.fp-text span
{
	font-size: 9px;
	float: right;
	padding: 0px;
	margin: 0px;
	font-weight: normal;
	font-style: italic;
}

.fp-divider
{
	height: 32px;
}

.fp-text h2
{
	text-indent: 0px;
	font-size: 11pt;
	font-weight: bold;
}

</STYLE>]]></description>
<author>Christof Wollenhaupt</author>
<pubDate>Fri, 14 Aug 2009 19:37:30</pubDate>
</item>
<item>
<title><![CDATA[
VMWare Server installation notes
]]></title>
<link>http://www.foxpert.com/knowlbits_200908_2.htm</link>
<description><![CDATA[<DIV class="fp-text" style="font-family:Verdana,Arial,Helvetica,sans-serif;
"><p>
After using VMWare for various purposes for many, many years now, I've moved my development environment to virtual machines two years ago. These are mostly Windows XP and Windows Vista clients with around 1 GB of RAM. I stored and executed them on my main development laptop. While using the laptop provided various advantages one disadvantage became more and more prevalent: Notebook hard drives simply can't deal very well with multiple requests at the same time.
</p><p>
Running a single virtual machine never really put too muchstress on my notebook. There was plenty of RAM (3.5 GB) and I didn't use the host OS that much when working in a virtual machine. Running two virtual machines maxed out available RAM, but was still manageable when. and that's a big when. only one of the two machines accessed the hard disk.
</p><p>
Suspending one machine, a virus scan, Windows swapping memory, Lookout indexing Outlook: Whenever the hard disks where spinning performance went downhill.
</p><p>
At the same time one advantage of using a notebook diminishes. Three years ago basically being able to take the office with me was a big plus. Since then connectivity has more and more become a commodity. Fast internet access (we used to call it high-speed internet not long ago) is available in more and more places. Fast mobile internet access such as UMTS has become downright cheep with unlimited data transfer for less than 50 Euros/Dollars a month.
</p><p>
Therefore I moved some of my development machines onto a new server and use a combination of VMWare Remote Client and Windows Remote Desktop to connect from the laptop to the server. The following are a few notes I took during the installation.
</p><p>
The mouse pointer can be a source of frustration if you move to a remote server. By default Windows displays a tiny shadow beneath the pointer.  There's no doubt that this increases the <i>discoverability experience</i> of the mouse pointer. It seems, though, that drawing a mouse pointer with a shadow is significantly different from drawing one without shadow.
</p><p>
If the mouse pointer jumps around and there is a delay between moving the mouse and updating the mouse pointer, pointer shadows are most likely activated. Go to Control Panel > Mouse > Pointers and uncheck the "Enable pointer shadow" option. Sometimes this isn't sufficient and additionally you need to change the scheme to "Windows Default (System scheme)".
</p><p>
Installing VMWare server automatically adds VMWare to the list of exception in the Windows Firewall. Windows 2008 and later support multiple profiles, one each for public, private and domain access. It seems that VMWare only enables the public profile. If you server is connected to a private network, you can't access VMWare  VI Web Access to configure the virtual machines.
</p><p>
To change this, open Server Manager.If Server Manager doesn't come automatically when you log onto the server, right click Computer in the Start menu and choose Manage.  Expand the node Configuration > Windows Firewall with Advanced Security > Inbound Rules. Locate the "VMWare Hostd" and "VMWare Authd" rules. Open the properties dialog for each rule and on the Advanced page check the "All profiles" checkbox, or pick profiles individually.
</p><p>
Choosing the right client is difficult. I switched back between VMWare Remote Client and Windows Terminal Services. In between these I even experimented with using VMWare Workstation instead of VMWare Server on the server. Each of these has advantages and disadvantages:
</p><p>
<b>VMWare Workstation</b>
</p><p>
On the pro side: The network can be connected and disconnected right from the client. It also much easier to boot Windows into safe mode since the virtual machine appears immediately with the BIOS screen. Of course, VMWare Workstation also has more features and better hardware support.
</p><p>
There is one disadvantages of using VMWare, though. You always have to use Remote Desktop to connect to the server before you can run VMWare Workstation. Unfortunately, you cannot have WMWare Workstation and VMWare Server installed on the same computer.
</p><p>
<b>VMWare Remote Client</b>
</p><p>
Remote Client has a quite simplified interface. You can control the power for the virtual machine (reset, stop and suspend). Additionally you can disconnect CD drives, floppy disks and the audio device.  USB devices connected to the client computer appear in a list, but the connect option is grayed out. The same applies to the Network symbol.
</p><p>
As my friend Peter Steinke pointed out to me, the easiest way to connect to a virtual machine with the Remote Client is by creating a desktop short cut to the virtual machine from within the web interface (it's the last option in the list of machine's action). There's no need to use the web interface every time you want to access a virtual machine.
</p><p>
The remote client has a few advantages. If you connect to a virtual machine that is powered off, it will automatically power on. That means you can minimize the number of machines that running on the server without having to use the web interface to start them manually.
</p><p>
The VMWare Remote Client also provides direct access to the CD drive on the client. If you want to burn a CD inside a virtual machine, you can use the CD writer on your client machine instead of having to put the CDs into the server CD drive. Nonetheless you need a good network connection, since otherwise you get errors when burning the CD.
</p><p>
The catch with this feature is that it only works when you launch the Remote Client as a local administrator on your client machine. If you have a desktop short cut, you can right click and pick the "Run as administrator" option. If you use the web interface to launch the Console, you have to run the browser as an administrator first. As a regular user you merely get the following error message when you attempt to connect the CD drive:
</p><p>
Failed to connect device to remote :<br>
Could not connect to "D:". It is being used by another virtual machine or some other program.
</p><p>
Whenever you change the windows size of the Remote Client, the screen size in the virtual machine is adjusted accordingly. So you never get scrollbars and always see everything on the screen.
</p><p>
Sound is a disadvantage for the Remote client. With VMWare Server you can only connect audio devices that physically exist. If the server doesn't have audio (and why should a real server have audio?), you won't be able to forward audio to the client machine.
</p><p>
Logging onto the client is more annoying that with the other two options because you have to enter the user name and password every single time. The user name and password are supposed to be configurable with the -u and -p command line arguments in the shortcut. But I never could get those to work. No matter what I entered for the user name (user, .\user, server\user, user@server, user@127.0.0.1, .\\user, server\\user, and a variety of combinations), I always ended up with the error message:
</p><p>
Error opening the remote virtual machine servername:5000\8: <br>
 Login failed due to a bad username or password.
</p><p>
<b>Remote Desktop</b>
</p><p>
Connecting to a virtual machine with the Remote Desktop client provides the closest experience to the native desktop experience. When the client and the guest operating system are both Windows Vista or Windows 7 and the client computer is Aero compatible, you even get the Aero interface on the remote virtual machine including glass effects and three dimensional task switching.
</p><p>
Microsoft's Remote Desktop is also the only possibility to get audio from the remote machine onto the local machine. The play back quality is way below what you have with local access from VMWare Workstation or natively, but at least there is audio. Recording isn't possible, though.
</p><p>
One drawback of Remote Desktop is that you can't connect or disconnect any of the hardware. VMWare Tools provide access to some of these features from within the virtual machine, but it's far from being as convenient as in VMWare Workstation or Remote Client. You also don't have access to the power options. To suspend the machine you need to access the web interface from the client. The same is true for machines that are powered off. You first have to power on through the web interface before you can connect to a virtual machine.
</p><p>
There's no way to boot into safe mode or make any BIOS changes through Remote Desktop. The virtual machine must be working and properly configured for Remote Desktop to work. For anything else you need VMWare's Remote Client and the web interface (when using VMWare Server) or the VMWare Workstation interface (when using Workstation on the server).
</p><p>
<b>Conclusion</b>
</p><p>
Right now it seems the best approach to use VMWare Remote Client and Remote Desktop together. Remote Client is used for maintenance tasks, everything that requires access to local devices and to start up the virtual machine if it's not running. Day-to-day jobs are done in Remote Desktop.
</p>
</DIV><STYLE>.fp-text
{
	font-size: 11pt;
	text-align: justify;
	line-height: 15pt;
	width: 600px;
	margin-left: auto;
	margin-right: auto;
	text-indent: 10px;
}

.fp-text pre
{
	border: 1px dashed black;
	padding: 6px;
	background-color: Beige;
	font-family:monospace;
	margin-left: 10px;
	margin-right: 10px;
	text-indent: 0px;
	line-height: 100%;
}

.fp-text,.fp-sidebar p
{
	margin-top: 6pt;
	margin-bottom: 6pt;
}

.fp-sidebar
{
	width: 200px;
	vertical-align: top;
	font-size: 11pt;
}

.fp-sidebar #search
{
	margin-top: 30px;
}


.fp-text h1
{
	text-indent: 0px;
	font-size: 11pt;
	font-weight: bold;
	padding: 0px;
	margin: 0px;
}

.fp-text span
{
	font-size: 9px;
	float: right;
	padding: 0px;
	margin: 0px;
	font-weight: normal;
	font-style: italic;
}

.fp-divider
{
	height: 32px;
}

.fp-text h2
{
	text-indent: 0px;
	font-size: 11pt;
	font-weight: bold;
}

</STYLE>]]></description>
<author>Christof Wollenhaupt</author>
<pubDate>Thu, 6 Aug 2009 16:38:32</pubDate>
</item>
<item>
<title><![CDATA[
Changing the TCP ports for VMWare Server 2
]]></title>
<link>http://www.foxpert.com/knowlbits_200908_1.htm</link>
<description><![CDATA[<DIV class="fp-text" style="font-family:Verdana,Arial,Helvetica,sans-serif;
"><p>
<a href="http://www.vmware.com/products/server/">VMWare Server 2</a> has significant changes user interface-wise compared to the first version of the free VMWare Server product. The previous version (which itself is the successor of GSX server) provided access to the server and virtual machines through the VMWare Server Console. The Server Console could be replaced by Virtual Center 1.4, provided you had a license.
</p><p>
For the current release VMWare decided to replace these two tools with VMWare Infrastructure Web Access, the web client used in ESX server and VMWare Infrastructure 3.x. While the new interface offers additional features, it has received some bad critics due to its slowness and it's additional resource requirements on the server.  On my server this adds up to 128 MB RAM.
</p><p>
Web-based software needs a web server to run on. VI Web Access is written in Java and runs on top of Apache Tomcat. VI Web Access increases the product complexity by a magnitude compared to Server Console. This wasn't really an issue when VI Web Access was part of VMWare Infrastructure. This product is usually deployed by dedicated administrators.
</p><p>
VMWare Server 2, on the other hand, targets the average small business user and even non-business usage. The number of postings on the web shows that web based software can be very difficult to manage when you can't even connect to the web application.
</p><p>
During the installation VMWare Server 2 prompts for two TCP ports. Port 8222 is used for http traffic, port 8333 for https traffic. In many Windows applications you can change the port after the installation as a matter of typing a new value into the options dialog. If you hoped the same would be true for VMWare Server 2, you'll be disappointed.
</p><p>
A number of web sites suggest that the only way to change the port is to uninstall VMWare server, scrub the hard disk and clean the registry from any trace of the product. Then reinstall with the correct ports. There is an easier solution, though.
</p><p>
While you still can't change the port in a single place, you only need to change the ports in a few files. For the reminder I assume you are running Windows Server 2008. For other operating systems you need to change the paths accordingly. I assume that you want to change the ports to 5000 (from 8333) for https and 5001 (from 8222) for http transmissions.
</p><p>
The tools you need are a file explorer (Windows Explorer will work) and a text editor (Notepad would be sufficient). First open
</p><pre>
C:\Users\All Users\VMware\VMware Server\hostd\proxy.xml
</pre><p>
Change the ports in the bolded values. These are right at the beginning of the file:
</p><pre>
&lt;ConfigRoot&gt;
  &lt;httpPort&gt;<b>5001</b>&lt;/httpPort&gt;
  &lt;httpsPort&gt;<b>5000</b>&lt;/httpsPort&gt;
  &lt;EndpointList&gt;
</pre><p>
Next open
</p><pre>
C:\Program Files (x86)\VMware\VMware Server\tomcat\
webapps\ui\jslib-1.0.122589\modules\
com.vmware.webaccess.app_1.0.0\WebAccess.properties
</pre><p>
At the end of the file you find the following part. Change the port in the bolded line:
</p><pre>
new Object({
   login_url: "http://localhost:<b>5001</b>/sdk",
   login_show_webservice_url: "false",
   login_name: "",
   login_password: ""
});
</pre><p>
If you miss this file, you get to the login prompt, but entering a user name and password results in an error message. To update the link to the server page open
</p><pre>
C:\Program Files (x86)\VMware\VMware Server\serverui.url
</pre><p>
Change the file like this (there are just two lines in there):
</p><pre>
[InternetShortcut]
URL=https://MyServer:<b>5000</b>/ui/
</pre><p>
Finally, you need to update the location of the client plugin. Open
</p><pre>
C:\Program Files (x86)\VMware\VMware Server\hostd\
docroot\client\clients.xml
</pre><p>
Then change the port number in the following line
</p><pre>
&lt;downloadUrl&gt;https://*:<b>5000</b>/client/VMware-viclient.exe&lt;/downloadUrl&gt;
</pre><p>
Those are all changes you need to make. They won't take effect until you restart VMWare server, or more precisely, the services that make up VMWare Server.
</p><p>
In the Start menu right click Computer and pick the "Manage" option. Navigate to the Services node (Configuration > Services on Windows 2008). Sort the list by name and restart all services whose name start with "VMWare" to be on the safe side. Now you should be able to connect to the web interface by typing the following URL into your browser:
</p><p>
<a href="https://MyServer:5000"> https://MyServer:5000</a>
</p><p>
replace "MyServer" with the computer name or IP address of your VMWare Server host machine. If you access your virtual machines through Terminal Services, VNC, or other clients, you're done now. Use the web interface to configure, start, stop, and suspend virtual machines.
</p><p>
Should you rely on the VMWare Remote Console, though, you need to know that while 8333 and 8222 are the only ports that you can configure during the setup, they aren't the only ones that VMWare uses. The Remote Console uses by default port 902 to communicate with the server.
</p><p>
Initially the Remote Console connects to the server using the https port. After authenticating the web server provides the client with details on how to connect to the console. To change these connection information, open the following file on the server:
</p><pre>
C:\Users\All Users\VMware\VMware Server\config.ini
</pre><p>
The first line is a new one that you won't find in your file. In the INI files you can specify the port the server is listening on and the port that the client uses. Both can be different if there's a firewall that translates the port:
</p><pre>
<b>authd.port = "5002"</b>
authd.client.port = "<b>5002</b>"
</pre><p>
I personally prefer the remote console to use a port adjacent to the Tomcat ports. I can then open up the firewall with a single port range rule instead of using multiple rules. The fewer rules the better usually.
</p><p>
Before the new settings take effect, you have to restart the "VMWare Authorization Service", or VMAuthdService for short.
</DIV><STYLE>.fp-text
{
	font-size: 11pt;
	text-align: justify;
	line-height: 15pt;
	width: 600px;
	margin-left: auto;
	margin-right: auto;
	text-indent: 10px;
}

.fp-text pre
{
	border: 1px dashed black;
	padding: 6px;
	background-color: Beige;
	font-family:monospace;
	margin-left: 10px;
	margin-right: 10px;
	text-indent: 0px;
	line-height: 100%;
}

.fp-text,.fp-sidebar p
{
	margin-top: 6pt;
	margin-bottom: 6pt;
}

.fp-sidebar
{
	width: 200px;
	vertical-align: top;
	font-size: 11pt;
}

.fp-sidebar #search
{
	margin-top: 30px;
}


.fp-text h1
{
	text-indent: 0px;
	font-size: 11pt;
	font-weight: bold;
	padding: 0px;
	margin: 0px;
}

.fp-text span
{
	font-size: 9px;
	float: right;
	padding: 0px;
	margin: 0px;
	font-weight: normal;
	font-style: italic;
}

.fp-divider
{
	height: 32px;
}

.fp-text h2
{
	text-indent: 0px;
	font-size: 11pt;
	font-weight: bold;
}

</STYLE>]]></description>
<author>Christof Wollenhaupt</author>
<pubDate>Thu, 6 Aug 2009 14:31:26</pubDate>
</item>
<item>
<title><![CDATA[
Have a date, or two, but never half
]]></title>
<link>http://www.foxpert.com/knowlbits_200907_2.htm</link>
<description><![CDATA[<DIV class="fp-text" style="font-family:Verdana,Arial,Helvetica,sans-serif;
"><p>
Date is one of those data types that is very unique to FoxPro, or at least, not that common in other programming languages. Most other languages have some sort of datetime data type that combines a date with the time during that day.
</p><p>
FoxPro isn't that much different from other languages, as even FoxPro simulates the date data type. Internally, everything is just a datetime value. This can cause a bit of confusion with date operations. Consider the following code snippet:
</p><pre>
ld1 = DATE()
ld2 = DATE()+0.25
? ld1
? ld2
? ld1 = ld2
</pre><p>
If you look at the screen output, both date values seem to be equal. Yet, the equals to operator returns .F. ld2 has an invisible time portion of "06:00" which you would only see when you cast the date into a datetime value:
</p><pre>
? Dtot(m.ld2)
</pre><p>
To make this more confusing: When you store ld2 in a date field and read it back, the time portion is lost and suddenly ld2 equals ld1. Of course, you won't write code that adds 0.25 to a date value intentionally. In real code this bug creeps up in a more evil way. Consider you are calculating the end date of a process. This might involve fractions if the duration is something like 3 days at 80% capacity. So you might end up with:
</p><pre>
? Date()+0.9999999
</pre><p>
which still prints the current day. If you store this value in a table, your process finishes one day early. A similar problem is adding fractions repeatedly, because you need the end date of a process that consists of multiple steps with a finite but fractional duration.
</p><p>
If you perform date operations by adding values, make sure you use ROUND(nValue,0) for any numeric value. Or convert the date into a datetime variable.
</p>
</DIV><STYLE>.fp-text
{
	font-size: 11pt;
	text-align: justify;
	line-height: 15pt;
	width: 600px;
	margin-left: auto;
	margin-right: auto;
	text-indent: 10px;
}

.fp-text pre
{
	border: 1px dashed black;
	padding: 6px;
	background-color: Beige;
	font-family:monospace;
	margin-left: 10px;
	margin-right: 10px;
	text-indent: 0px;
	line-height: 100%;
}

.fp-text,.fp-sidebar p
{
	margin-top: 6pt;
	margin-bottom: 6pt;
}

.fp-sidebar
{
	width: 200px;
	vertical-align: top;
	font-size: 11pt;
}

.fp-sidebar #search
{
	margin-top: 30px;
}


.fp-text h1
{
	text-indent: 0px;
	font-size: 11pt;
	font-weight: bold;
	padding: 0px;
	margin: 0px;
}

.fp-text span
{
	font-size: 9px;
	float: right;
	padding: 0px;
	margin: 0px;
	font-weight: normal;
	font-style: italic;
}

.fp-divider
{
	height: 32px;
}

.fp-text h2
{
	text-indent: 0px;
	font-size: 11pt;
	font-weight: bold;
}

</STYLE>]]></description>
<author>Christof Wollenhaupt</author>
<pubDate>Tue, 14 Jul 2009 07:21:10</pubDate>
</item>
<item>
<title><![CDATA[
Fatal error 0x8000FFFF when using a COM server
]]></title>
<link>http://www.foxpert.com/knowlbits_200907_1.htm</link>
<description><![CDATA[<DIV class="fp-text" style="font-family:Verdana,Arial,Helvetica,sans-serif;
"><p>
Our application needs to talk to quite a few other applications. Recently I replaced the existing DDE based interface with a COM based one. The vendor of the other application provides a COM server that controls their application and passes data back and forth. My first attempt in Visual FoxPro looked like this
</p><pre>
loClient = CREATEOBJECT("Acme.Client")
loClient.acmeConnect("App")
</pre><p>
The first line creates an instance of the COM server and works just fine. The second line, however, crashes with a COM exception 0x8000FFFF. As a VFP developer I don't take it for granted that COM objects just work. So I tried the same component in other environments. First in VB Script:
</p><pre>
Set o = CreateObject("Acme.Client")
msgbox o.acmeConnect("App")
</pre><p>
Same error here. But who uses VB Script, anyway? Next in C#:
</p><pre>
var x = new Acme3Client.Client();
var n = x.acmeConnect("App");
</pre><p>
Surprisingly, I got the same error. The control ships with a sample application written in C++ in which the COM server works. Unfortunately, the sample application is just a compiled EXE. So, next I tried this in C++ with ATL:
</p><pre>
int _tmain(int argc, _TCHAR* argv[])
{
  CoInitialize(NULL);
  using namespace Acme3Client;
  IClientPtr px("Acme.Client");
  px->acmeConnect("App");
  return 0;
}
</pre><p>
I wasn't willing to see yet another 0x8000FFFF exception, but my computer didn't ask me. As a last attempt I tried to use the COM server in an MFC application:
</p><pre>
CAcmeClient *p = new CAcmeClient();
p->acmeConnect(CString("App"));
</pre><p>
Finally, I got a different error message. MFC fired an assertion because a window handle was NULL. This lead to the solution: Contrary to what the documentation states, this COM server can only be used by adding it as an ActiveX control to the form. A better error message would have pointed me in the right direction much earlier.
</p>
</DIV><STYLE>.fp-text
{
	font-size: 11pt;
	text-align: justify;
	line-height: 15pt;
	width: 600px;
	margin-left: auto;
	margin-right: auto;
	text-indent: 10px;
}

.fp-text pre
{
	border: 1px dashed black;
	padding: 6px;
	background-color: Beige;
	font-family:monospace;
	margin-left: 10px;
	margin-right: 10px;
	text-indent: 0px;
	line-height: 100%;
}

.fp-text,.fp-sidebar p
{
	margin-top: 6pt;
	margin-bottom: 6pt;
}

.fp-sidebar
{
	width: 200px;
	vertical-align: top;
	font-size: 11pt;
}

.fp-sidebar #search
{
	margin-top: 30px;
}


.fp-text h1
{
	text-indent: 0px;
	font-size: 11pt;
	font-weight: bold;
	padding: 0px;
	margin: 0px;
}

.fp-text span
{
	font-size: 9px;
	float: right;
	padding: 0px;
	margin: 0px;
	font-weight: normal;
	font-style: italic;
}

.fp-divider
{
	height: 32px;
}

.fp-text h2
{
	text-indent: 0px;
	font-size: 11pt;
	font-weight: bold;
}

</STYLE>]]></description>
<author>Christof Wollenhaupt</author>
<pubDate>Tue, 7 Jul 2009 13:46:22</pubDate>
</item>
<item>
<title><![CDATA[Unit tests to validate COM interfaces
]]></title>
<link>http://www.foxpert.com/knowlbits_200906_2.htm</link>
<description><![CDATA[<DIV class="fp-text" style="font-family:Verdana,Arial,Helvetica,sans-serif;
"><p>
Creating a COM server in VFP is easy, maybe too easy. Check the OLEPUBLIC checkbox or add the OLEPUBLIC keyword to the class definition and you are done. Building a DLL creates a COM server that you can pass to anyone who needs to interface with your application. Using a COM server in VFP is similar uneventful. Instantiate the COM server using the ProgId and call methods on the object, that's it.
</p><p>
The ease of creating a COM server comes at a high price. We VFP developer tend to not think about how our COM server is consumed. Unlike Visual FoxPro, which due too its use of late binding is very forgiving when consuming COM servers, this isn't true for most other environments, especially .NET.
</p><p>
A COM interface consists of a list of method pointers. Each method is accessed by its index number. When .NET imports a COM server, it internally creates a mapping which forwards any call of a named method to the corresponding index in the interface. As long as the interface doesn't change, life is good. Have an updated version of the COM server? Just copy it over the existing DLL or EXE.
</p><p>
Any interface change, however, requires a lot of work for the .NET developer. The COM server needs to be imported again. The InterOp library must be generated anew. Depending on how the COM server is used by the application, the entire application might need to be recompiled. The old COM server has to be unregistered; the new one to be distributed and registered.
</p><p>
That's quite a bit of work, but manageable if you tell your clients about the modified interface. Unfortunately VFP makes it way too easy to introduce interface changes accidentally.
</p><p>
How does VFP generate the interface for a COM server?
</p><p>
For every COM class, VFP loops through all public properties and methods and assigns id values. The first method or property in the PRG file is id 0, the second PEM becomes id 1, and so forth. In other words, the physical order determines the id value. This index is the same one that early binding languages use to call methods and access properties.
</p><p>
It's probably obvious now that there're at least two ways to alter the COM interface in a way which wouldn't have any impact on Visual FoxPro, at all: If you rearrange methods in your source code, the index numbers change. Secondly, any new property or method that you don't mark protected or hidden, is added to the interface, as well.
</p><p>
At least for me the second one is a major source for headache. Visual FoxPro uses an object based approach to protected members where almost any other OOP language uses a class based approach. This renders protected PEMs in Visual FoxPro basically useless, which is why I often don't bother making procedures protected. I do even less so with properties as protected properties in Visual FoxPro make it hard to debug code properly and therefore affect code quality negatively. But that's food for another controversy discussion..
</p><p>
After refactoring code and accidentally changing the interface, I took a defensive approach to make sure this won't happen again. I added a unit test similar to the following one for every COM class in my project. It doesn't detect changes to the order of PEMs, but it's sufficient to make sure I'm aware of interface changes to the COM server:
</p><pre>
Procedure Test_CheckInterface

  Local loBO
  loBO = CreateObject("MyCOM.Server")
  This.AssertTrue( "Error loading MyCOM.Server", ;
    Vartype(m.loBO)=="O" )

  Local laPem[1]
  This.AssertEquals( "Wrong number of PEMs", 4, ;
    AMembers(laPem, m.loBO, 3) )
  This.AssertEquals( "#1", "Method1", laPem[1,1] )
  This.AssertEquals( "#2", "Method2", laPem[2,1] )
  This.AssertEquals( "#3", "PEM1", laPem[3,1] )
  This.AssertEquals( "#4", "PEM1", laPem[4,1] )

EndProc
</pre><p>
For every method there's one item in the array created by AMembers. Properties have two entries, one for the getter, and one for the setter. Since I added this unit tests, I made the same mistake several times. But I always spotted the problem before I even prepared the distribution set.
</p>
</DIV><STYLE>.fp-text
{
	font-size: 11pt;
	text-align: justify;
	line-height: 15pt;
	width: 600px;
	margin-left: auto;
	margin-right: auto;
	text-indent: 10px;
}

.fp-text pre
{
	border: 1px dashed black;
	padding: 6px;
	background-color: Beige;
	font-family:monospace;
	margin-left: 10px;
	margin-right: 10px;
	text-indent: 0px;
	line-height: 100%;
}

.fp-text,.fp-sidebar p
{
	margin-top: 6pt;
	margin-bottom: 6pt;
}

.fp-sidebar
{
	width: 200px;
	vertical-align: top;
	font-size: 11pt;
}

.fp-sidebar #search
{
	margin-top: 30px;
}


.fp-text h1
{
	text-indent: 0px;
	font-size: 11pt;
	font-weight: bold;
	padding: 0px;
	margin: 0px;
}

.fp-text span
{
	font-size: 9px;
	float: right;
	padding: 0px;
	margin: 0px;
	font-weight: normal;
	font-style: italic;
}

.fp-divider
{
	height: 32px;
}

.fp-text h2
{
	text-indent: 0px;
	font-size: 11pt;
	font-weight: bold;
}

</STYLE>]]></description>
<author>Christof Wollenhaupt</author>
<pubDate>Tue, 30 Jun 2009 12:40:16</pubDate>
</item>
<item>
<title><![CDATA[Get rid of security warnings
]]></title>
<link>http://www.foxpert.com/knowlbits_200906_1.htm</link>
<description><![CDATA[<DIV class="fp-text" style="font-family:Verdana,Arial,Helvetica,sans-serif;
"><p>
When you download a file with Internet Explorer, the file is tagged as being downloaded from the internet. This has impact on a number of file types. When the file is an EXE and you execute it, you (or more specifically, your users) get a warning:
</p><p>
<b>The publisher could not be verified. Are you sure you want to run this software?</b>
</p><p>
If the file is a CHM help file, the content is never shown. Instead you only see a "Navigation cancelled" error page. For archives the tag is often mysteriously inherited when you extract files. In other words, while this warning might be helpful to prevent malicious code to execute, the system causes a lot of unnecessary support effort for smaller application makers.
</p><p>
Technically, the tag is just an NTFS stream named "Zone.Identifier". This file is actually an INI file specifying the network zone from which the file originates:
</p><pre>
[ZoneTransfer]
ZoneId=3
</pre><p>
Since NTFS streams are only supported on NTFS drives, copying the file to a FAT32 formatted USB stick and back to the disk effectively removes the security tag. This is neither an obvious, nor a very understandable solution. To an average user this approach sounds much like the famous "if your car stops working get our and back in to fix it" approach.
</p><p>
It would be better to handle this within the application. Providing you have write access to the files, the following program removes the security warnings for all files that match the pattern you pass to it:
</p><pre>
*============================================================
* Removes the security warning for all files in a directory
* that match the pattern
*============================================================
LParameter tcPattern

  Local laDir[1], lnFile
  Declare Long DeleteFile in Win32Api String
  For lnFile=1 to ADir(laDir,m.tcPattern)
    DeleteFile( ;
      Addbs(FullPath(JustPath(m.tcPattern)))+;
      laDir[m.lnFile,1]+":Zone.Identifier" ;
  )
  EndFor
</pre><p>
To fix your updated EXE and CHM files, you would use:
</p><pre>
RemSecurityWarning("*.CHM")
RemSecurityWarning("*.EXE")
</pre><p>
You can specify a directory if the current directory is not the right choice.
</DIV><STYLE>.fp-text
{
	font-size: 11pt;
	text-align: justify;
	line-height: 15pt;
	width: 600px;
	margin-left: auto;
	margin-right: auto;
	text-indent: 10px;
}

.fp-text pre
{
	border: 1px dashed black;
	padding: 6px;
	background-color: Beige;
	font-family:monospace;
	margin-left: 10px;
	margin-right: 10px;
	text-indent: 0px;
	line-height: 100%;
}

.fp-text,.fp-sidebar p
{
	margin-top: 6pt;
	margin-bottom: 6pt;
}

.fp-sidebar
{
	width: 200px;
	vertical-align: top;
	font-size: 11pt;
}

.fp-sidebar #search
{
	margin-top: 30px;
}


.fp-text h1
{
	text-indent: 0px;
	font-size: 11pt;
	font-weight: bold;
	padding: 0px;
	margin: 0px;
}

.fp-text span
{
	font-size: 9px;
	float: right;
	padding: 0px;
	margin: 0px;
	font-weight: normal;
	font-style: italic;
}

.fp-divider
{
	height: 32px;
}

.fp-text h2
{
	text-indent: 0px;
	font-size: 11pt;
	font-weight: bold;
}

</STYLE>]]></description>
<author>Christof Wollenhaupt</author>
<pubDate>Thu, 6 May 2010 04:26:44</pubDate>
</item>
<item>
<title><![CDATA[RecordSource - bang.boom.gone
]]></title>
<link>http://www.foxpert.com/knowlbits_200905_1.htm</link>
<description><![CDATA[<DIV class="fp-text" style="font-family:Verdana,Arial,Helvetica,sans-serif;
"><p>
In Visual FoxPro every object belongs to a particular data session. Under normal circumstances this is the data session in which the object is created. The exception are objects that have an intrinsic data session, namely Form, FormSet, Toolbar, and Session. Instantiating these objects may create a new data session in which these objects are then built.
</p><p>
As objects must belong to only one data session, changing the data session inside a form method moves the entire form into the new data session. In my session "The Dark Side of Visual FoxPro" I explained that expressions are evaluated in the context of whatever data session is the current one. Expressions like ControlSources, and so forth might be evaluated at any time. This makes changing the data session risky, but not necessarily unfeasible.
</p><p>
The grid is unfortunately slightly more pro-active. Whenever the record source cursor disappears even for the fraction of a nanosecond, the grid responds immediately. Closing a cursor is an obvious way to hide a cursor. Creating a new cursor in the same work area isn't as obvious, but still understandable. In both cases the grid responds by removing all columns and the RecordSource. On the visual front the grid turns into a white rectangle with a thin black border.
</p><p>
Changing the data session isn't all that obvious. Nonetheless, moving the form - and along the grid - into a different data session constitutes a change in visibility of the cursor. Consequently, the grid updates itself and blanks out the RecordSources property and all ControlSources.
</p><p>
Contrary to closing the cursor in the current data session the column objects remain, only the binding information is gone. As a result the grid looks much like before. Except that there's no record shown. The only visible clue is that the deleted mark column on the left side of the grid is black for all records instead of cleared as it was before.
</p><p>
If this happens to you in a grid where you don't always expect data to be shown this can be a bug that is hard to discover and hard to nail down.  On a screen shot of your application, watch out for the deleted mark column.
</p>
</DIV><STYLE>.fp-text
{
	font-size: 11pt;
	text-align: justify;
	line-height: 15pt;
	width: 600px;
	margin-left: auto;
	margin-right: auto;
	text-indent: 10px;
}

.fp-text pre
{
	border: 1px dashed black;
	padding: 6px;
	background-color: Beige;
	font-family:monospace;
	margin-left: 10px;
	margin-right: 10px;
	text-indent: 0px;
	line-height: 100%;
}

.fp-text,.fp-sidebar p
{
	margin-top: 6pt;
	margin-bottom: 6pt;
}

.fp-sidebar
{
	width: 200px;
	vertical-align: top;
	font-size: 11pt;
}

.fp-sidebar #search
{
	margin-top: 30px;
}


.fp-text h1
{
	text-indent: 0px;
	font-size: 11pt;
	font-weight: bold;
	padding: 0px;
	margin: 0px;
}

.fp-text span
{
	font-size: 9px;
	float: right;
	padding: 0px;
	margin: 0px;
	font-weight: normal;
	font-style: italic;
}

.fp-divider
{
	height: 32px;
}

.fp-text h2
{
	text-indent: 0px;
	font-size: 11pt;
	font-weight: bold;
}

</STYLE>]]></description>
<author>Christof Wollenhaupt</author>
<pubDate>Tue, 19 May 2009 09:43:48</pubDate>
</item>

 </channel>
</rss>
