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?

Recommended Answers

All 4 Replies

Well, nobody seems to be replying to my thread in terms of telling me what my error is, and I took that as being ignored. However, in the meantime I just tried a neat piece of code posted several weeks ago by vijayan121 at...

http://www.daniweb.com/forums/thread141366.html

and that crashes too after providing correct output - just like mine. I'm now beginning to wander if there isn't something wrong with the VC 6 compiler that is causing this - not my (our) code? I can't expect for someone to reveal a bug to me if there isn't any I guess.

I tried your program, seemed to run in dev-c++ & visual-c++2008 (with the reported error). Tried debugging it, saw that the stack pointer certainly is different before call to after call (that's bad) - it's due to the function doing a ret 4, when it should be a ret 0... any idea what it's popping?? (i reckon it's 'this')

Have a look at:
http://www.codeproject.com/KB/cpp/CallConventions.aspx
http://www.hackcraft.net/cpp/MSCallingConventions/

I had a look at the default commandline for visual c++ calling convention: it's /Gm for 'thiscall' which makes me think that calling a member function (which I would assume to generally be a thiscall - as it is a non-static member function, and needs the 'this' parameter) with a stdcall is not supported. I think you got lucky that cdecl doesn't screw with the stack. I guess that's doing crazy casting to do backdoor entry into functions is not recommended.

I think dev-c++ was doing the same thing with the stack (it kept freezing in debug mode), it's just that it doesn't check that the stack's ok after every call (visual-c++ calls __RTC_CheckEsp after all your calls - that's how it knows when to report it).

commented: perfect - gave me my answer +1

I really appreciate your looking at this Dougy83. It most definitely looks like there is a problem - as I thought. With my level of expertise I just couldn't take it any further than I did. I've printed out those links and will study them. It looks to me like I need to learn how to use a stepping debugger and understand asm code (so far I've managed without it).

My whole reason for doing this was simply to learn a few of the details of how COM objects are laid out in memory. Somehow just looking at the pictures and drawings in books didn't quite satisfy me. I needed to get my hands dirty I guess. Then, just when I thought I had it all figurred out, I kept getting these strange problems that to me anyway had all the signs of some kind of memory corruption, i.e., GPFs in MS VC++, worked perfect in Dev-CPP, and partially corrupted memory address output in Code::Blocks gcc but no GPFs. I gather from your conclusions that there are no easy 'one liner' type fixes.

I'm also wondering at this point how this affects access to COM objects (which use this memory layout) from other languages. The main language I use for PC programming is PowerBASIC (www.powerbasic.com). I mostly use VC++ for WinCE devices. From what I've read from your links and comments it sounds like that will depend on how that other language handles pushing/poping registers and other esoteric details of stack management.

Well! The solution is obvious! If it wants a this pointer, then pass it one (to balance the stack)! This runs perfect in all three environments and no crashes!

#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)(int);
 unsigned int* ptrVTbl=0;    
 unsigned int* VTable=0;      
 unsigned int i=0,j=0;        
 CA* pCA=0;                    
 
 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*)(int))VTable[j]; //call function through function pointer
         pFn((int)pCA); //give it something to pop, even though its not used!                
     }
 } 
 printf("\n"); 
 getchar();
   
 return 0;
}
commented: Clever! +2
Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.