2009-07Jul-14
Have a date, or two, but never half
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.
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:
ld1 = DATE()
ld2 = DATE()+0.25
? ld1
? ld2
? ld1 = ld2
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:
? Dtot(m.ld2)
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:
? Date()+0.9999999
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.
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.
2009-07Jul-07
Fatal error 0x8000FFFF when using a COM server
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
loClient = CREATEOBJECT("Acme.Client")
loClient.acmeConnect("App")
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:
Set o = CreateObject("Acme.Client")
msgbox o.acmeConnect("App")
Same error here. But who uses VB Script, anyway? Next in C#:
var x = new Acme3Client.Client();
var n = x.acmeConnect("App");
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:
int _tmain(int argc, _TCHAR* argv[])
{
CoInitialize(NULL);
using namespace Acme3Client;
IClientPtr px("Acme.Client");
px->acmeConnect("App");
return 0;
}
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:
CAcmeClient *p = new CAcmeClient();
p->acmeConnect(CString("App"));
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.