|
|||||||||||||||||||||||
|
|||||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
IntroductionThis bug was first reported by Jochen Kalmbach on April 12th 2002 (no links available to original posting), when VS.NET 7.0 was doing its initial rounds; and it's quite inconceivable why the bug still exists in VS.NET 2003. Just about every week, at least two people report issues related to this bug and I thought it might be a good idea to have an article on it here on CodeProject. What's really annoying is that the developer might spend several hours or even a full day on the problem before realizing that it is not a problem with his code. The bugThe most common scenario where the bug is reported is when someone has a
mixed mode C++ program that has a managed class, which accesses an unmanaged
class in an unmanaged DLL. Now if the unmanaged class has a Minimal code to reproduce bug#pragma unmanaged
class Unmanaged
{
public:
virtual bool IsAlive()
{
return false;
}
};
#pragma managed
__gc class Managed
{
public:
void Test()
{
Unmanaged* um = new Unmanaged();
if(um->IsAlive())
{
//Always executes
Console::WriteLine("Function returned true. BUG!!!");
}
else
{
//Never executes
Console::WriteLine("Function returned false. No Bug :-)");
}
delete um;
}
};
int _tmain()
{
Managed* mg = new Managed();
mg->Test();
return 0;
}
Trying to figure it outLet's examine the disassembly for the ;virtual bool IsAlive()
004010B0 push ebp
004010B1 mov ebp,esp
004010B3 push ecx
004010B4 mov dword ptr [ebp-4],ecx
;return false
004010B7 xor al,al ; Notice how AL is made 0 (false)
004010B9 mov esp,ebp
004010BB pop ebp
004010BC ret
As you can see, the result of the function is returned in the AL register and this is what the contents of my registers looked like at this point :- EAX = 00401000 EBX = 0012EFB4 ECX = 06C42C88 EDX = 00425410
ESI = 00168930 EDI = 00000000 EIP = 004010B9 ESP = 0012EFA8
EBP = 0012EFAC EFL = 00000246
Now let's see the disassembly for the caller code :- ;if(um->IsAlive())
00000065 mov eax,dword ptr [ebp-18h]
00000068 mov eax,dword ptr [eax]
0000006a mov esi,dword ptr [eax]
0000006c mov ecx,dword ptr [ebp-18h]
0000006f mov eax,esi
00000071 push 1692D0h
00000076 call F9759F50 ; The call to the function
0000007b movzx esi,al ; Copying the return value to ESI
0000007e test esi,esi ; Checking for true
00000080 je 0000009A ; If false then jump to 9A
The return value is obtained from the EAX = 00000001 EBX = 0012F0C8 ECX = 00000004 EDX = 00000000
ESI = 00000001 EDI = 04A719C8 EBP = 0012F070 ESP = 0012F044
Horror of horrors! WorkaroundsThe simple workaround is to use a class Unmanaged
{
public:
virtual int IsAlive()
{
return false;
}
};
The casting is implicit from am A slightly bizarre looking workaround [see section titled "More info" for
heheh more info] suggested by someone (possibly Microsoft
Support) is to set class Unmanaged
{
public:
virtual bool IsAlive()
{
__asm mov eax,100
return false;
}
};
More infoI got some more information regarding this issue from Tom Archer (my friend,
fellow-CPian and co-author) who got this information from a friend of his, who
is in the VC++ compiler team. It seems this bug occurs when one of the upper 24
bits of the Still more info (Thanks Jochen)Jochen's post gave me a few links which provided even more info on this bug.
The bug occurs due to the way the CLR marshals boolean values. The CLR thinks
that a boolean is 4 bytes (as it is under .NET) but the C++ Related Microsoft KB linksConclusionWhat's really dangerous about this bug is that it's quite easy not to see it,
because most functions that return History
|
||||||||||||||||||||||