I have not been able to obtain an answer to this question anywhere (including here) so I’ll ask it again. I can understand that folks don’t want to wade through hundreds of lines of code looking for someone else’s bug, so I’ve worked very hard to produce the very smallest possible program I can to illustrate my difficulty. Below is a working program using all __cdecl function calling – and I know I could have eliminated the __cdecl as that is the default. However, I put it in to emphasize the fact that in the next program it will be replaced with __stdcall. Here is the program and the output is directly following…
#include <stdio.h>
struct IX
{
virtual void __cdecl Fx1()=0;
virtual void __cdecl Fx2()=0;
};
struct IY
{
virtual void __cdecl Fy1()=0;
virtual void __cdecl Fy2()=0;
};
class CA : public IX, public IY
{
virtual void __cdecl Fx1(){printf("Called Fx1()\n");}
virtual void __cdecl Fx2(){printf("Called Fx2()\n");}
virtual void __cdecl Fy1(){printf("Called Fy1()\n");}
virtual void __cdecl Fy2(){printf("Called Fy2()\n");}
};
int main(void)
{
void (__cdecl*pFn)(void); //function pointer to void function that returns void
unsigned int* ptrVTbl=0; //pointer to Vtable. There are two,e.g., an IX & IY
unsigned int* VTable=0; //Virtual Function Table for IX and IY, respectively
unsigned int i=0,j=0; //iterator variables
CA* pCA=0; //ptr to class CA
printf("sizeof(CA) = %u\n\n", sizeof(CA));
pCA=new CA;
ptrVTbl=(unsigned int*)pCA;
printf("i\t&ptrVTbl[]\t&VTable[]\tVTable[]\tpFn()\n");
printf("====================================================================\n");
for(i=0;i<2;i++)
{
VTable=(unsigned int*)ptrVTbl[i];
for(j=0;j<2;j++)
{
printf
(
"%u\t%u\t\t%u\t\t%u\t\t", //format specifiers
i, //iterator
&ptrVTbl[i], //bytes 0-3 offset from pCA for i=0 and 4-7 when i=1
&VTable[j], //address of function pointer held in VTable
VTable[j] //value of function pointer in Vtable, i.e., address of function
);
pFn=(void(__cdecl*)())VTable[j]; //call function through function pointer
pFn();
}
}
printf("\n");
getchar();
return 0;
}
//sizeof(CA) = 8
//
//i &ptrVTbl[] &VTable[] VTable[] pFn()
//====================================================================
//0 3146560 4214960 4198544 Called Fx1()
//0 3146560 4214964 4198560 Called Fx2()
//1 3146564 4214952 4198576 Called Fy1()
//1 3146564 4214956 4198592 Called Fy2()
//Press any key to continue
As can be seen, the above program works perfectly, produces perfect output, and generates no GPFs. In case you havn’t figured out what the program does, it obtains the addresses of the Vtable pointers from the pointer to class CA, i.e., pCA, then obtains from those (there are two – one for each Vtable) the addresses of the functions in the Vtables and calls those functions through a suitably cast function pointer. Now below is the exact same program with the __cdecl modifier replaced with __stdcall.
#include <stdio.h>
struct IX
{
virtual void __stdcall Fx1()=0;
virtual void __stdcall Fx2()=0;
};
struct IY
{
virtual void __stdcall Fy1()=0;
virtual void __stdcall Fy2()=0;
};
class CA : public IX, public IY
{
virtual void __stdcall Fx1(){printf("Called Fx1()\n");}
virtual void __stdcall Fx2(){printf("Called Fx2()\n");}
virtual void __stdcall Fy1(){printf("Called Fy1()\n");}
virtual void __stdcall Fy2(){printf("Called Fy2()\n");}
};
int main(void)
{
void (__stdcall*pFn)(void); //function pointer to void function that returns void
unsigned int* ptrVTbl=0; //pointer to Vtable. There are two,e.g., an IX & IY
unsigned int* VTable=0; //Virtual Function Table for IX and IY, respectively
unsigned int i=0,j=0; //iterator variables
CA* pCA=0; //ptr to class CA
printf("sizeof(CA) = %u\n\n", sizeof(CA));
pCA=new CA;
ptrVTbl=(unsigned int*)pCA;
printf("i\t&ptrVTbl[]\t&VTable[]\tVTable[]\tpFn()\n");
printf("====================================================================\n");
for(i=0;i<2;i++)
{
VTable=(unsigned int*)ptrVTbl[i];
for(j=0;j<2;j++)
{
printf
(
"%u\t%u\t\t%u\t\t%u\t\t", //format specifiers
i, //iterator
&ptrVTbl[i], //bytes 0-3 offset from pCA for i=0 and 4-7 when i=1
&VTable[j], //address of function pointer held in VTable
VTable[j] //value of function pointer in Vtable, i.e., address of function
);
pFn=(void(__stdcall*)())VTable[j]; //call function through function pointer
pFn();
}
}
printf("\n");
getchar();
return 0;
}
//sizeof(CA) = 8
//
//i &ptrVTbl[] &VTable[] VTable[] pFn()
//====================================================================
//0 3146560 4214960 4198544 Called Fx1()
//0 3146560 4214964 4198560 Called Fx2()
//1 3146564 4214952 4198576 Called Fy1()
//1 3146564 4214956 4198592 Called Fy2()
//
//Press any key to continue
This program produces just as perfect output as the first using __cdecl, but generates a GPF upon exiting. I’m using Microsoft Visual C++ 6 SP5 for these programs. These particular two programs I have also tested with Dev-C++ 4.9.9.2 gcc and Code::Blocks gcc. They seem to be working fine with these latter two environments for these two specific programs, but I am convinced nontheless that something is wrong. The somewhat more complex programs from which these evolved do produce some rather odd behavior with the new Code::Blocks gcc implementation. With Dev-C++ I have not been able to produce the aberrant behavior. With the Dev-C++ setup I probably have a several years older gcc compiler implementation. I have been in the pits for days fighting this stuff, and am convinced something is amiss. I think its some kind of memory corruption, but more than that I don’t know. I do not know if the problem is a sin of ommission or comission.
Since I’m basically a Win32 Api coder I use __stdcall functions all the time, but I can’t say I’ve ever written any. When specifying a __stdcall function and writing the imlementation of the function is it I who has to set up the stack frame, deal with the registers, etc? I thought the compiler took care of all that nasty low level stuff! I don’t have to do it when I write __cdecl functions!
Can someone please help me here? At least try the programs and let me know the results?