BorlandTalk.com Forum Index BorlandTalk.com
Borland discussion newsgroups
 
Archives   FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

Passing Class Functions To CreateThread, etc...

 
Post new topic   Reply to topic    BorlandTalk.com Forum Index -> C++ Builder (Native API)
View previous topic :: View next topic  
Author Message
Alistair
Guest





PostPosted: Sun Dec 04, 2005 12:01 pm    Post subject: Passing Class Functions To CreateThread, etc... Reply with quote



Me : "I can't pass the pointer of a class function to CreateThread"

Gambit : "Well, technically you can, but not easily.
You would essentially have to use a proxy function
in order to do it. Using a static method is easier,
though, especially since CreateThread() allows a
user-defined value to be passed to the thread instance.
That makes a proxy function unnecessary."

You got me thinking Gambit, how would you create a proxy function to
make a call back of type 'void Do()' take a 'void myclass:Very Happyo()'?

Purely on a interest basis, I am not planning to do it at the moment.

I have looked on yahoo and google for this and I can't find anything useful.

Can you expand on what you mean Gambit or can someone else shed some
light on this?

Cheers,
Alistair
Back to top
Thomas Maeder [TeamB]
Guest





PostPosted: Sun Dec 04, 2005 6:53 pm    Post subject: Re: Passing Class Functions To CreateThread, etc... Reply with quote



Alistair <NoSpamPlease (AT) Somewhere (DOT) Com> writes:

Quote:
Me : "I can't pass the pointer of a class function to CreateThread"

Gambit : "Well, technically you can, but not easily.
You would essentially have to use a proxy function
in order to do it. Using a static method is easier,
though, especially since CreateThread() allows a
user-defined value to be passed to the thread instance.
That makes a proxy function unnecessary."

You got me thinking Gambit, how would you create a proxy function to
make a call back of type 'void Do()' take a 'void myclass:Very Happyo()'?

That's what I'd try:

CreateThread() is declared like this:

HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes,
SIZE_T dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
DWORD dwCreationFlags,
LPDWORD lpThreadId
);

lpStartAddress can be used to pass the address of the callback
function, and lpParameter for the address of the callback object.

So something like this should work:

class ThreadFunction
{
public:
virtual DWORD execute() = 0;

HANDLE startThread();
};

DWORD WINAPI callbackFct(LPVOID lpParameter)
{
assert(lpParameter!=0);
ThreadFunction *tf(static_cast<ThreadFunction *>(lpParameter));
return tf->execute();
}

HANDLE ThreadFunction::startThread()
{
return CreateThread(a1,a2,&callbackFct,this,a3,a4);
// for some appropriate a1..a4
}

Since the function called back is to return a DWORD, I think that
should also be the return type of execute().

callbackFct is the proxy function. Technically, it can't be a static
member of a class (although you may get away with one using most
implementations), but it can well be a non-member function defined in
the unnamed namespace of the translation unit defining
ThreadFunction::startThread().

Back to top
Ed Mulroy
Guest





PostPosted: Sun Dec 04, 2005 8:19 pm    Post subject: Re: Passing Class Functions To CreateThread, etc... Reply with quote



I suggest that _beginthread or _beginthreadNT be used instead of
CreateThread. While CreateThread works correctly, the use of one of the
other two functions is required for proper operation of the runtime library.

You MIGHT get away with CreateThread but it is more likely that the program
would display serious problems that are difficult to track down because they
only appear at irregular intervals.

_beginthread and _beginthreadNT are documented in the compiler's help.

.. Ed

Quote:
Thomas Maeder wrote in message
news:m2zmngr8wf.fsf (AT) madbox2 (DOT) Arbeitsgruppe...

That's what I'd try:

CreateThread() is declared like this:

HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes,
SIZE_T dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
DWORD dwCreationFlags,
LPDWORD lpThreadId
);

lpStartAddress can be used to pass the address of the callback
function, and lpParameter for the address of the callback object.

So something like this should work:

class ThreadFunction
{
public:
virtual DWORD execute() = 0;

HANDLE startThread();
};

DWORD WINAPI callbackFct(LPVOID lpParameter)
{
assert(lpParameter!=0);
ThreadFunction *tf(static_cast<ThreadFunction *>(lpParameter));
return tf->execute();
}

HANDLE ThreadFunction::startThread()
{
return CreateThread(a1,a2,&callbackFct,this,a3,a4);
// for some appropriate a1..a4
}

Since the function called back is to return a DWORD, I think that
should also be the return type of execute().

callbackFct is the proxy function. Technically, it can't be a static
member of a class (although you may get away with one using most
implementations), but it can well be a non-member function defined in
the unnamed namespace of the translation unit defining
ThreadFunction::startThread().



Back to top
Alistair
Guest





PostPosted: Mon Dec 05, 2005 8:28 am    Post subject: Re: Passing Class Functions To CreateThread, etc... Reply with quote

I see what you mean, but CreateThread was an example. Try this one:

If I have a callback parameter of a function that does NOT take a
parameter, i.e. for passing 'this' to it.

How would you get a class member function into the call back, that is
defined as a non-member call back like CreateThread?

You can't pass the object instance to it, but you want the member
function to be called.

Any ideas?

Cheers,
Alistair
Back to top
Alan Bellingham
Guest





PostPosted: Mon Dec 05, 2005 9:46 am    Post subject: Re: Passing Class Functions To CreateThread, etc... Reply with quote

Alistair <NoSpamPlease (AT) Somewhere (DOT) Com> wrote:

Quote:
I see what you mean, but CreateThread was an example. Try this one:

If I have a callback parameter of a function that does NOT take a
parameter, i.e. for passing 'this' to it.

You have a global function without any user-available parameter?

Then you need to handle it globally. It's designed not to be used as a
member function.

Alan Bellingham
--
ACCU Conference 2006 - 19-22 April, Randolph Hotel, Oxford, UK

Back to top
Thomas Maeder [TeamB]
Guest





PostPosted: Mon Dec 05, 2005 5:15 pm    Post subject: Re: Passing Class Functions To CreateThread, etc... Reply with quote

Alistair <NoSpamPlease (AT) Somewhere (DOT) Com> writes:

Quote:
I see what you mean, but CreateThread was an example. Try this one:

If I have a callback parameter of a function that does NOT take a
parameter, i.e. for passing 'this' to it.

Like std::qsort? Don't use it; there's std::sort.

Apart from that, I don't remember having seen such a function in a
very long time, neither on Windows nor PalmOS. Luckily.

Back to top
Thomas Maeder [TeamB]
Guest





PostPosted: Mon Dec 05, 2005 5:15 pm    Post subject: Re: Passing Class Functions To CreateThread, etc... Reply with quote

Alan Bellingham <alanb (AT) episys (DOT) com> writes:

Quote:
If I have a callback parameter of a function that does NOT take a
parameter, i.e. for passing 'this' to it.

You have a global function without any user-available parameter?

Then you need to handle it globally.

With the appropriate scaffolding if the program is multi-threaded, of
course.

Back to top
Remy Lebeau (TeamB)
Guest





PostPosted: Mon Dec 05, 2005 7:33 pm    Post subject: Re: Passing Class Functions To CreateThread, etc... Reply with quote


"Alistair" <NoSpamPlease (AT) Somewhere (DOT) Com> wrote


Quote:
If I have a callback parameter of a function that does NOT
take a parameter, i.e. for passing 'this' to it.

How would you get a class member function into the call back,
that is defined as a non-member call back like CreateThread?

Borland handles this topic in a very elegant manner for the VCL. The VCL
has a MakeObjectInstance() function that dynamically generates proxy
functions for class methods to act as window procedures. A window procedure
has the same problem that you describe - no access to the class 'this'
pointer (well, technically, Get/SetWindowLong(GWL_USERDATA) can be used for
that, but we'll ignore that in this discussion).

Basically, what MakeObjectInstance() does is allocate a block of virtual
executable memory via VirtualAlloc() and then stores the class 'this'
pointer and method pointer into that memory block, along with a few assembly
instructions that call a helper proxy function. The proxy function extracts
the pointers from the memory block and then manipulates the call stack and
CPU registers in order to call the class method correctly on the proper
class instance, as if it had been called directly. When the class is
finished being used, the memory block can be freed.

The assembly instructions are located at the beginning of the memory block
so that the memory pointer can be passed to any API as a callback and the
API can call into it like any other function. When the memory is executed,
the proxy function is called automatically, which then calls the class
object method. A callback function is really nothing more than a jump to a
memory address anyway. The method can even return values back through the
proxy to the original caller if desired.

As for the assembly instructions themselves, which instructions to use
exactly are directly dependant on which parameters need to be passed to the
method as well as the calling convention of the method. Different
parameters for different conventions are passed via the stack or CPU
registers differently. As an example, MakeObjectInstance() takes a VCL
WndProc() method:

void __fastcall WndProc(TMessage &Message);

And produces a proxy function that behavles like it has the signature of a
Win32 WNDPROC callback:

LRESULT __stdcall WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM
lParam);

The code that uses the callback only knows about the WindowProc(). When
called, the proxy function pops the uMsg, wParam, and lParam values from the
call stack and rearranges them into a TMessage, then places the class 'this'
pointer into the EAX CPU register and a pointer to the TMessage in the EDX
CPU register (the __fastcall convention uses CPU registered for parameters
when possible, whereas the __stdcall convention uses the stack only), and
then finally jumps to the memory address of the class method. The method
has access to the class 'this' pointer because of the EAX CPU register, and
can do everything it would be able to do had you called the method normally.
When the method exits, the TMessage::Result member is passed to the EAX
register as the return value for the WindowProc(). That value is then
returned to the original caller.

With this approach, you can allocate multiple proxy functions, each one
specific to a particular object, and they allow you to use object methods as
callback functions, where the caller has no concept of the object being used
behind the scenes.

Now, with the dirty details said, you can go to http://www.deja.com and do a
search for past discussions on this topic. Sample code implementations have
been posted before, and you can search online for details about how the
various calling conventions manage the call stack and parameter values.
Below is a (simplified) C++ translation of MakeObjectInstance() from the VCL
to get you started:

typedef void (__fastcall *TWndMethod)(TMessage &Message);

#pragma pack(push, 1)
struct TObjectInstance
{
BYTE Code;
UINT Offset;
TWndMethod Method;
};

struct TInstanceBlock
{
BYTE Code[2];
void *WndProcPtr;
TObjectInstance Instance;
};
#pragma pack(pop)

/*
Standard window procedure
In ECX = Address of method pointer
Out EAX = Result
*/

LRESULT __stdcall StdWndProc(HWND Window, UINT Message, WPARAM WParam,
LPARAM LParam)
{
asm
{
XOR EAX, EAX
PUSH EAX
PUSH LParam
PUSH WParam
PUSH Message
MOV EDX, ESP
MOV EAX, [ECX+4]
CALL [ECX]
ADD ESP, 12
POP EAX
}
}


UINT __fastcall CalcJmpOffset(void *Src, void *Dest)
{
return ((UINT)Dest) - (((UINT)Src) + 5);
}

void* __fastcal MakeObjectInstance(TWndMethod Method)
{
TInstanceBlock *Block = (TInstanceBlock*) ::VirtualAlloc(NULL,
sizeof(TInstanceBlock), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if( Block )
{
Block->Code[0] = 0x59; // POP ECX
Block->Code[1] = 0xE9; // JMP StdWndProc
Block->WndProcPtr = (void*) CalcJmpOffset(&Block->WndProcPtr,
&StdWndProc);

Block.Instance.Code = 0xE8; // CALL NEAR PTR Offset
Block.Instance.Offset = CalcJmpOffset(Instance, Block->Code);

Block.Instance.Method = Method;
return &Block->Instance;
}

return NULL;
}

void __fastcall FreeObjectInstance(void *ObjectInstance)
{
if( ObjectInstance != NULL )
::VirtualFree((void*)(((UINT)ObjectInstance)-6),
sizeof(TInstanceBlock), 0);
}



To use it:

class TMyObject
{
public:
void *Callback;

TMyObject()
{
Callback = MakeObjectInstance(DoIt);
}

~TMyObject()
{
if( Callback )
FreeObjectInstance(Callback);
}

void __fastcall DoIt(TMessage &Message)
{
// use Message as needed...
// set Message.Result accordingly...
}
};


TMyObject *obj = new TMyObject;
//...
if( obj->Callback )
SomeCodeThatNeedsACallback(obj->Callback);
//...
delete obj;


Gambit



Back to top
Alistair
Guest





PostPosted: Mon Dec 05, 2005 8:11 pm    Post subject: Re: Passing Class Functions To CreateThread, etc... Reply with quote

As usual Gambit, an excellent reply.

Thanks for taking the time to answer this in detail (with example code!).

Alistair
Back to top
Display posts from previous:   
Post new topic   Reply to topic    BorlandTalk.com Forum Index -> C++ Builder (Native API) All times are GMT
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2006 phpBB Group
SEO toolkit © 2004-2006 webmedic.