Microsoft KB Archive/248137

= BUG: COleCurrency::operator/ Not Backward Compatible with Previous Versions of MFC =

Article ID: 248137

Article Last Modified on 11/21/2006

-

APPLIES TO


 * Microsoft Visual C++ 6.0 Enterprise Edition
 * Microsoft Visual C++ 6.0 Professional Edition
 * Microsoft Visual C++ 6.0 Standard Edition
 * Microsoft Foundation Class Library 4.2

-



This article was previously published under Q248137



SYMPTOMS
When an application developed with Visual C++ 6.0 using MFC dynamically is installed on a computer, it may cause existing applications built with Visual C++ 4.2 or 5.0 that use MFC dynamically to stop working as expected. Specifically, calculations that are done using the COleCurrency::operator/ function may give incorrect results. It does not cause existing applications built with Visual C++ 4.2 or 5.0 that use the COleCurrency::operator/ function and MFC statically to behave differently.



CAUSE
This is a bug in the Visual C++ 6.0 MFC COleCurrency class. The Visual C++ 6.0 MFC code for the COleCurrency::operator/ function does an incorrect calculation. This results in incorrect results after this function is called.



RESOLUTION
For any existing applications relying on the functionality of the COleCurrency::operator/ function that shipped in MFC42.DLL with Visual C++ 4.2 or 5.0, you can copy the older Mfc42.dll into the application's directory. By doing so, the older Mfc42.dll is not used nor the newer one located in the windows system directory. If you are using Visual C++ 4.2 or 5.0 currently, you could link to MFC statically to prevent using any version of Mfc42.dll that may be on the target machines.

For newer applications being developed with Visual C++ 6.0, the best solution is to not call COleCurrency::operator/. You can work around the bug in Visual C++ 6.0 by writing a new class that subclasses COleCurrency. To do so, add a new header file to your project called MyOleCurrency.h and insert the following code into it:
 * 1) ifndef MYOLECURRENCY_H
 * 2) define MYOLECURRENCY_H

// Return the highest order bit composing dwTarget in wBit. { \    if (dwTarget != 0) \ for (wBit = 32; (dwTarget & (0x00000001 << (wBit-1))) == 0; wBit--) \ ; \    else \ wBit = 0; \ } while (0)
 * 1) define HI_BIT(dwTarget, wBit) do \

// Left shift an (assumed unsigned) currency by wBits. { \    for (WORD wTempBits = wBits; wTempBits > 0; wTempBits--) \ { \        cur.m_cur.Hi = ((DWORD)cur.m_cur.Hi << 1); \ cur.m_cur.Hi |= (cur.m_cur.Lo & 0x80000000) >> 31; \ cur.m_cur.Lo = cur.m_cur.Lo << 1; \ } \ } while (0)
 * 1) define LSHIFT_UCUR(cur, wBits)    do \

// Right shift an (assumed unsigned) currency by wBits. { \    for (WORD wTempBits = wBits; wTempBits> 0; wTempBits--) \ { \        cur.m_cur.Lo = cur.m_cur.Lo >> 1; \ cur.m_cur.Lo |= (cur.m_cur.Hi & 0x00000001) << 31; \ cur.m_cur.Hi = ((DWORD)cur.m_cur.Hi >> 1); \ } \ } while (0)
 * 1) define RSHIFT_UCUR(cur, wBits)    do  \


 * 1) define DELETE_EXCEPTION(e) do { e->Delete; } while (0)

class CMyOleCurrency : public COleCurrency { public: const COleCurrency& operator=(CURRENCY cySrc) {        m_cur = cySrc; SetStatus(valid); return *this; }

const COleCurrency& operator=(const COleCurrency& curSrc) {       m_cur = curSrc.m_cur; m_status = curSrc.m_status; return *this; }

const COleCurrency& operator=(const VARIANT& varSrc) {      if (varSrc.vt != VT_CY) {        TRY {           COleVariant varTemp(varSrc); varTemp.ChangeType(VT_CY); m_cur = varTemp.cyVal; SetStatus(valid); }        // Catch COleException from ChangeType, but not CMemoryException CATCH(COleException, e)        { // Not able to convert VARIANT to CURRENCY m_cur.Hi = 0; m_cur.Lo = 0; SetStatus(invalid); DELETE_EXCEPTION(e); }        END_CATCH }      else {         m_cur = varSrc.cyVal; SetStatus(valid); }

return *this; }

COleCurrency CMyOleCurrency::operator/(long nOperand) const {      // If operand not Valid, just return if (!GetStatus == valid) return *this;

COleCurrency curTemp(m_cur); DWORD nTempOp;

// Check for divide by 0b if (nOperand == 0) {     curTemp.SetStatus(invalid);

// Set to maximum negative value curTemp.m_cur.Hi = 0x80000000; curTemp.m_cur.Lo = 0x00000000;

return curTemp; }

// Compute absolute values if (curTemp.m_cur.Hi < 0) curTemp = -curTemp;

nTempOp = labs(nOperand);

// Optimization - division is simple if Hi == 0 if (curTemp.m_cur.Hi == 0x0000) {         //vc5 SP3 done this..., changed back to VC++ 5.0 SP3 behavior curTemp.m_cur.Lo /= nTempOp;

//vc6 did this... //curTemp.m_cur.Lo = m_cur.Lo / nTempOp;

// Compute result sign if ((m_cur.Hi ^ nOperand) & 0x80000000) curTemp = -curTemp; return curTemp; }

// Now curTemp represents remainder COleCurrency curResult; // Initializes to zero COleCurrency curTempResult; COleCurrency curOperand;

curOperand.m_cur.Lo = nTempOp;

WORD wHiBitRem; WORD wScaleOp;

// Quit if remainder can be truncated while (curTemp >= curOperand) {    // Scale up and divide Hi portion HI_BIT(curTemp.m_cur.Hi, wHiBitRem);

if (wHiBitRem != 0) wHiBitRem += 32; else HI_BIT(curTemp.m_cur.Lo, wHiBitRem);

WORD wShift = (WORD)(64 - wHiBitRem); LSHIFT_UCUR(curTemp, wShift);

// If Operand bigger than Hi it must be scaled wScaleOp = (WORD)((nTempOp > (DWORD)curTemp.m_cur.Hi) ? 1 : 0);

// Perform synthetic division curTempResult.m_cur.Hi = (DWORD)curTemp.m_cur.Hi / (nTempOp >> wScaleOp);

// Scale back to get correct result and remainder RSHIFT_UCUR(curTemp, wShift); wShift = (WORD)(wShift - wScaleOp); RSHIFT_UCUR(curTempResult, wShift);

// Now calculate result and remainder curResult += curTempResult; curTemp -= curTempResult * nTempOp; }

// Compute result sign if ((m_cur.Hi ^ nOperand) & 0x80000000) curResult = -curResult;

return curResult; } };

Instead of using COleCurrency in your code, use CMyOleCurrency instead. Also, be sure to use #include "MyOleCurrency.h" after #include "stdafx.h" before using the CMyOleCurrency class in any source files.
 * 1) endif MYOLECURRENCY_H



STATUS
Microsoft has confirmed that this is a bug in the Microsoft products that are listed at the beginning of this article.



Steps to Reproduce Behavior
 Using Visual C++ 6.0, create a new Win32 Console Application with AppWizard. Type TestCurr in the Project name edit box. Click the OK button. In the next dialog box, click the radio button An application that supports MFC. Click Finish. Click OK in the New Project Information dialog box.  Open the TestCurr.cpp file and add the following code below before the return statement in _tmain: COleCurrency a;

a.SetCurrency (4,500); a *= -100; a=a/100;

cout << "Hi   = " << a.m_cur.Hi << endl; cout << "Lo   = " << a.m_cur.Lo << endl; cout << "int64 = " << (INT)a.m_cur.int64 << endl;   The code for _tmain should look something like the following after doing the last step: int _tmain(int argc, TCHAR* argv[], TCHAR* envp[]) {   int nRetCode = 0;

// initialize MFC and print and error on failure if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine, 0)) {      // TODO: change error code to suit your needs cerr << _T("Fatal Error: MFC initialization failed") << endl; nRetCode = 1; }   else {      // TODO: code your application's behavior here. CString strHello; strHello.LoadString(IDS_HELLO); cout << (LPCTSTR)strHello << endl; }

COleCurrency curr; curr.SetCurrency (4,500); curr *= -100; curr = curr/100;

cout << "Hi   = " << curr.m_cur.Hi << endl; cout << "Lo   = " << curr.m_cur.Lo << endl; cout << "int64 = " << (INT)curr.m_cur.int64 << endl;

return nRetCode; }                        Build the project and execute it.</li></ol>

You should see the following in the output window for the results of the calculations:

Hi   = -1 Lo   = 4252058124 Int64 = -42909172

If the same code was compiled using Visual C++ 4.2 or 5.0, you get the following results instead:

Hi   = -1 Lo   = 4294926796 int64 = -40500

Additional query words: backward compatible port incompatible wrong results answer MFC42.DLL

Keywords: kbautomation kbbug kbpending KB248137

-

[mailto:TECHNET@MICROSOFT.COM Send feedback to Microsoft]

© Microsoft Corporation. All rights reserved.