PRB: Problem Using -1 with Double or Float Field Variable

Last reviewed: July 18, 1997
Article ID: Q118420
1.50 WINDOWS kbprg kbprb

The information in this article applies to:

  • The Microsoft Foundation Classes (MFC), included with: Microsoft Visual C++ for Windows, version 1.5

SYMPTOMS

If an edit control is mapped to a double or float field variable using DDX and RFX functions, a value of -1 for the variable is changed to NULL when a CRecordView moves past the record. To see how this works, perform the follow steps:

  1. Use the Enroll sample that ships with Visual C++, version 1.5.

  2. Add a field called "Fee" to the SECTION Table using Access, version 1.1.

  3. Set the field to the Access data type of "Number".

  4. Enter the data "1", "0", and "-1" into some records in the Fee field of the SECTION table.

  5. Map a CRecordset double field variable, "m_Fee", to the column.

  6. Add the label "Fee:" to ENROLL.RC and an edit-control box for "Fee:".

  7. Map the m_Fee variable to the edit control.

  8. Build the project and run it.

  9. Move through the records. When the record with a -1 value for the fee appears, move to the next record and then back again. You will see that the -1 has been replaced with a NULL value. The edit control is empty.

CAUSE

The Microsoft Foundation Classes need some way of determining whether a field value is NULL (no value). Because a double variable always has some value, MFC has defined what it calls "pseudo-null" values. If a variable contains a pseudo-null value, it is basically considered NULL. In the file AFXDB.H, you can see what the pseudo-null values are:

   #define AFX_RFX_INT_PSEUDO_NULL (0x7EE4)
   #define AFX_RFX_LONG_PSEUDO_NULL (0x4a4d4120L)
   #define AFX_RFX_BYTE_PSEUDO_NULL 255
   #define AFX_RFX_SINGLE_PSEUDO_NULL (-1.0)
   #define AFX_RFX_DOUBLE_PSEUDO_NULL (-1.0)
   #define AFX_RFX_BOOL_PSEUDO_NULL 2
   #define AFX_RFX_DATE_PSEUDO_NULL CTime(0)

You can see that a value of -1.0 in a double or float field variable means that the field is to have a NULL value. This is not the only item that the database code uses for determining whether a field is NULL. A NULL flag is also used for each field variable, which indicates whether it contains a NULL value.

The fundamental problem is in the code for "RFX_Double()" and "RFX_Float()" functions where the MarkForUpdate case is handled. Here is the code for RFX_Double():

   case CFieldExchange::MarkForUpdate:
        if (value == AFX_RFX_DOUBLE_PSEUDO_NULL)
            pFX->m_prs->SetFieldFlags(nField,
                AFX_SQL_FIELD_FLAG_NULL, pFX->m_nFieldType);
        else
            pFX->m_prs->ClearFieldFlags(nField,
                AFX_SQL_FIELD_FLAG_NULL, pFX->m_nFieldType);
        goto LDefault;

The problem is that the code simply checks the value of the double field variable and sets the NULL flag if its value is -1 (the pseudo-null value). The MarkForUpdate code is executed every time a CRecordView moves off of a record. Here is some of the code in CRecordView::OnMove():

   if (pSet->CanUpdate())
    {
        pSet->Edit();
        if (!UpdateData())
            return FALSE;

        pSet->Update();
    }

RESOLUTION

There are two possible workarounds. The value of the pseudo-null value can be changed to something that a user of the application is not expected to type into the edit control. So, in the case of RFX_Double(), "AFX_RFX_DOUBLE_PSEUDO_NULL" can be changed in AFXDB.H to something like the following:

   #define AFX_RFX_DOUBLE_PSEUDO_NULL (-9.123e19)

It would be uncommon for a user to type this. If you decide to fix the problem this way, you need to rebuild the MFC library using the information in the README.TXT file in the \MSVC\MFC\SRC directory.

Another way to work around this problem is to copy the code for RFX_Double into the .CPP file where your CRecordset class implementation code is. Change the function name to "RFX_NewDouble()" or whatever you want to call it. Then, delete the "MarkForUpdate" case statement shown above. You can also remove the "LDefault:" label, which is used by the goto statement at the end of the MarkForUpdate case.

Once you have created this new RFX_NewDouble() function, you need to replace the call to RFX_Double() in your CRecordset's DoFieldExchange() function with RFX_NewDouble().

MORE INFORMATION

Beginning with Visual C++ for Windows version 1.51 and Visual C++ 32-bit Edition version 2.0, the definitions of AFX_RFX_SINGLE_PSEUDO_NULL and AFX_RFX_DOUBLE_PSEUDO_NULL use a value of -9.123e19 instead of -1.0.


Additional reference words: 1.50 2.50 ODBC no32bit noupdate
KBCategory: kbprg kbprb
KBSubcategory: MfcDatabase
Keywords : kb16bitonly MfcDatabase kbprb kbprg
Technology : kbMfc
Version : 1.50
Platform : WINDOWS


THE INFORMATION PROVIDED IN THE MICROSOFT KNOWLEDGE BASE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. MICROSOFT DISCLAIMS ALL WARRANTIES, EITHER EXPRESS OR IMPLIED, INCLUDING THE WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL MICROSOFT CORPORATION OR ITS SUPPLIERS BE LIABLE FOR ANY DAMAGES WHATSOEVER INCLUDING DIRECT, INDIRECT, INCIDENTAL, CONSEQUENTIAL, LOSS OF BUSINESS PROFITS OR SPECIAL DAMAGES, EVEN IF MICROSOFT CORPORATION OR ITS SUPPLIERS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. SOME STATES DO NOT ALLOW THE EXCLUSION OR LIMITATION OF LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES SO THE FOREGOING LIMITATION MAY NOT APPLY.

Last reviewed: July 18, 1997
© 1998 Microsoft Corporation. All rights reserved. Terms of Use.