|
|
Comments and Discussions
|
|
 |

|
IS it me, or in Thunk32.h at the bottom where it reads
#define BOOST_PP_ITERATION_PARAMS_1 (3,(0,THUNK32_MAX_ARGS,"Thunk32_template.h"))
??=include BOOST_PP_ITERATE()
#undef BOOST_PP_ITERATION_PARAMS_1
should it instead be
#define BOOST_PP_ITERATION_PARAMS_1 (3,(0,THUNK32_MAX_ARGS,"Thunk32_template.h"))
#include BOOST_PP_ITERATE()
#undef BOOST_PP_ITERATION_PARAMS_1
I had to make such a change to get the Thunk32 project to compile. I had downloaded the demo file, and then added the project into my solution (talking visual studio 2010 here) But now when I try to build the project that is using the Thunk32 functionality, I get linker errors; unresolved externals, which from the error message I believe are pointing the a problem with some thunk code that is public trying to use code that is protected... or a public template code is trying to use some protected thunkbase code.
Any help would be really appreciated.
|
|
|
|

|
An excellent article! Thanks for this.
And i have question:
When i want to use a __cdecl covention - do I must to change an asm code for it? I'm looking on msdn for the difference of the __stdcall and __cdecl but didn't find how it realise in this case. Can you help?
|
|
|
|

|
I have been searching for a solution for this prob for some time and couldn't find a decent solution for using member functions as callbacks.
Really good work. But one thing that annoys me a lot is the size of joost library. I am still not able to install it correctly therefore the demo project is not working. But i am sure if i spent some time on it then this will be the most efficient and generic solution. If this project worked correctly then i will be going to integrate this solution in my project. Can I selectively add in the source control only those joost library file which are used in your project? or I have to add the complete library in the source control?
Take a very good care of Yourself.
Sayed Ahmad Mursil
|
|
|
|

|
Support for 64 bit is also one issue where i am not sure this approach will be the best alternative.. I have seen your article on 64 bit support as well and i dont think that thunking is Win64 is feasible.
Mursil
|
|
|
|

|
I'm fairly certain the dependencies the Thunk32 libraries have to boost are headers only. You do not have to build, or include, any of the library files for it to work. Just download the source package (or binary distribution) from http://www.boost.org, set the include paths accordingly in your development environment, and you should be ready to go.
|
|
|
|

|
This method is close to the one in ATL 3.0 for window thunk in 'AtlBase.h' (struct _stdcallthunk), but in call WndProc the ATL thunk change the first parameter (HWND) with the pointer of the CWindowImpl derived class to access the related instance in the static callback (WindowProc).
I remake ATL to compile with GCC (in a posix environment) without using this method. It's dangerous and is not pure C/C++, the assembly code is not 'portable'. I used another method, more expensive, but in pure C/C++ code. You need a global object (like in ATL CComModule) and before the callback invocation (in same example, before the CreateWindowEx API) enter a critical section and call a method of the global object that save the pointer of the object you need in the callback in a array (vector or map), then call the API that invoke the callback and leave the critical section.
In the callback enter critical section, get the pointer to the object from the global object (CComModule) and leave critical section.
To do this you need an hash value or a unique identifier to save the pair into map (or the index of an array) and to retrieve it. The callback is in sama scope of your code, but the trick is to exit from the object instance and remain in global scope, so the global object maintain a pointer of all the instancies of the objects used in the callback. The callback can access this object to ask it the right pointer based on an identifier. but you can use this method only if in the callback there is a value to distinguish each object into array, for WindowProc is natural use the HWND, it's unique for each window and can be associated with the object we need to reference.
This method is not fast if there are many object to store into array, but don't make use of assembly. If there is no global object, you can use thread local storage TLS to save object pointers.
bye, caio
-- modified at 0:35 Sunday 5th August, 2007
|
|
|
|

|
Yeah, there are many different approaches to the wndproc problem. Another widely used technique is to add windows hooks to capture the first few messages arriving prior to the WM_CREATE. That enables you to do the jump from a global or static wndproc, to a member function wndproc, in a pure C++ way -- without missing any messages, nor keeping a map to target the right class instance.
As for the thunking, it isn't as much "dangerous" as it is unportable and horribly impractical for x64 builds (I made a post about that on my blog).
|
|
|
|

|
my system is the linux/ps2 kit. will your code work on this? the processor is mips so im pretty sure the assembler stuff wont work at all, will it?
loving your work.
|
|
|
|

|
Sorry for being such a terribly slow responder.
I'm afraid the code won't work given that architecture. At the same time, it shouldn't be too much work to rewrite the asm code. Once that's completed, all that's left to do is avoid all platform specific function calls, which I'll have to admit I haven't focused much on in this library.
|
|
|
|

|
more easy to use
support parameters and return value
class c1
{
public:
void test1(){}
};
....
c1 o1;
xThisCall calltest( &o1, &c1::test1 );
calltest.call();
xThisCall calltestcopy = calltest;
calltestcopy.call();
////////////////////////////////////////////code//////////////////////////////////////
namespace xlibplus
{
template < class TRet, class T >
inline TRet ThisCall0( T func, LPVOID pThis )
{
TRet lpRet;
__asm mov ecx, pThis;
//__asm push eax
__asm call dword ptr[func];
__asm mov lpRet, eax
return lpRet;
}
template < class TRet, class T, class Tp1 >
inline TRet ThisCall1( T func, LPVOID pThis, Tp1 lpParam1 )
{
TRet lpRet;
__asm push dword ptr[lpParam1]
__asm mov ecx, pThis;
//__asm push eax
__asm call dword ptr[func];
__asm mov lpRet, eax
return lpRet;
}
template < class TRet, class T, class Tp1, class Tp2 >
inline TRet ThisCall2( T func, LPVOID pThis, Tp1 lpParam1, Tp2 lpParam2 )
{
TRet lpRet;
__asm push dword ptr[lpParam2]
__asm push dword ptr[lpParam1]
__asm mov ecx, pThis;
//__asm push eax
__asm call dword ptr[func];
__asm mov lpRet, eax
return lpRet;
}
template < class TRet, class T, class Tp1, class Tp2, class Tp3 >
inline TRet ThisCall3( T func, LPVOID pThis, Tp1 lpParam1, Tp2 lpParam2, Tp3 lpParam3 )
{
TRet lpRet;
__asm push dword ptr[lpParam3]
__asm push dword ptr[lpParam2]
__asm push dword ptr[lpParam1]
__asm mov ecx, pThis;
//__asm push eax
__asm call dword ptr[func];
//__asm push eax
__asm mov lpRet, eax
return lpRet;
}
template < class TRet, class T, class Tp1, class Tp2, class Tp3, class Tp4 >
inline TRet ThisCall4( T func, LPVOID pThis, Tp1 lpParam1, Tp2 lpParam2, Tp3 lpParam3, Tp4 lpParam4 )
{
TRet lpRet;
__asm push dword ptr[lpParam4]
__asm push dword ptr[lpParam3]
__asm push dword ptr[lpParam2]
__asm push dword ptr[lpParam1]
__asm mov ecx, pThis;
//__asm push eax
__asm call dword ptr[func];
__asm mov lpRet, eax
return lpRet;
}
template < class TRet, class T, class Tp1, class Tp2, class Tp3, class Tp4, class Tp5 >
inline TRet ThisCall5( T func, LPVOID pThis, Tp1 lpParam1, Tp2 lpParam2, Tp3 lpParam3, Tp4 lpParam4, Tp5 lpParam5 )
{
TRet lpRet;
__asm push dword ptr[lpParam5]
__asm push dword ptr[lpParam4]
__asm push dword ptr[lpParam3]
__asm push dword ptr[lpParam2]
__asm push dword ptr[lpParam1]
__asm mov ecx, pThis;
//__asm push eax
__asm call dword ptr[func];
__asm mov lpRet, eax
return lpRet;
}
template < class T >
inline LPVOID F2P( T func )
{
__asm mov eax, dword ptr[func];
}
template< class TRet = LPVOID >
class xThisCallCustom
{
typedef xThisCallCustom xThisClass;
public:
xThisCallCustom()
{
m_lpThis = NULL;
m_lpFunc = NULL;
}
xThisCallCustom(const xThisClass & thiscall )
{
m_lpThis = thiscall.m_lpThis;
m_lpFunc = thiscall.m_lpFunc;
}
template
xThisCallCustom(LPVOID lpThis, T func)
{
m_lpThis = lpThis;
m_lpFunc = F2P(func);
}
LPVOID getThisPtr()const{ return m_lpThis;}
VOID setThisPtr( LPVOID lpThis ){ m_lpThis = lpThis;}
LPVOID getFuncPtr()const{ return m_lpFunc;}
template VOID setFuncPtr( T func ){ m_lpFunc = F2P( func );}
TRet call()
{
if( !Assigned() )return NULL;
return ThisCall0( m_lpFunc, m_lpThis );
}
template < class Tp1 >
TRet call( Tp1 lpParam1 )
{
if( !Assigned() )return NULL;
return ThisCall1( m_lpFunc, m_lpThis, lpParam1 );
}
template < class Tp1, class Tp2 >
TRet call( Tp1 lpParam1, Tp2 lpParam2 )
{
if( !Assigned() )return NULL;
return ThisCall2( m_lpFunc, m_lpThis, lpParam1, lpParam2 );
}
template < class Tp1, class Tp2, class Tp3 >
TRet call( Tp1 lpParam1, Tp2 lpParam2, Tp3 lpParam3 )
{
if( !Assigned() )return NULL;
//LPVOID lpRet = NULL;
return ThisCall3( m_lpFunc, m_lpThis, lpParam1, lpParam2, lpParam3 );
//__asm pop lpRet;
//return lpRet;
}
template < class Tp1, class Tp2, class Tp3, class Tp4 >
TRet call( Tp1 lpParam1, Tp2 lpParam2, Tp3 lpParam3, Tp4 lpParam4 )
{
if( !Assigned() )return NULL;
return ThisCall4( m_lpFunc, m_lpThis, lpParam1, lpParam2, lpParam3, lpParam4 );
}
template < class Tp1, class Tp2, class Tp3, class Tp4, class Tp5 >
TRet call( Tp1 lpParam1, Tp2 lpParam2, Tp3 lpParam3, Tp4 lpParam4, Tp5 lpParam5 )
{
if( !Assigned() )return NULL;
return ThisCall5( m_lpFunc, m_lpThis, lpParam1, lpParam2, lpParam3, lpParam4, lpParam5 );
}
template < class TTHIS >
TRet callWithThis(TTHIS * pThis)
{
if( !Assigned() )return NULL;
return ThisCall0( m_lpFunc, pThis );
}
template < class TTHIS, class Tp1 >
TRet callWithThis( TTHIS * pThis, Tp1 lpParam1 )
{
if( !Assigned() )return NULL;
return ThisCall1( m_lpFunc, pThis, lpParam1 );
}
template < class TTHIS, class Tp1, class Tp2 >
TRet callWithThis( TTHIS * pThis, Tp1 lpParam1, Tp2 lpParam2 )
{
if( !Assigned() )return NULL;
return ThisCall2( m_lpFunc, pThis, lpParam1, lpParam2 );
}
template < class TTHIS, class Tp1, class Tp2, class Tp3 >
TRet callWithThis( TTHIS * pThis, Tp1 lpParam1, Tp2 lpParam2, Tp3 lpParam3 )
{
if( !Assigned() )return NULL;
return ThisCall3( m_lpFunc, pThis, lpParam1, lpParam2, lpParam3 );
}
template < class TTHIS, class Tp1, class Tp2, class Tp3, class Tp4 >
TRet callWithThis( TTHIS * pThis, Tp1 lpParam1, Tp2 lpParam2, Tp3 lpParam3, Tp4 lpParam4 )
{
if( !Assigned() )return NULL;
return ThisCall4( m_lpFunc, pThis, lpParam1, lpParam2, lpParam3, lpParam4 );
}
template < class TTHIS, class Tp1, class Tp2, class Tp3, class Tp4, class Tp5 >
TRet callWithThis( TTHIS * pThis, Tp1 lpParam1, Tp2 lpParam2, Tp3 lpParam3, Tp4 lpParam4, Tp5 lpParam5 )
{
if( !Assigned() )return NULL;
return ThisCall5( m_lpFunc, pThis, lpParam1, lpParam2, lpParam3, lpParam4, lpParam5 );
}
xThisClass & operator =( const xThisClass & thiscall )
{
this->m_lpFunc = thiscall.m_lpFunc;
this->m_lpThis = thiscall.m_lpThis;
return *this;
}
BOOL Assigned(){ return (m_lpFunc != NULL);}
protected:
LPVOID m_lpThis;
LPVOID m_lpFunc;
};
typedef xThisCallCustom<> xThisCall;
typedef xThisCallCustom<> xEventCall;
typedef xThisCallCustom xCompareCall;
};
|
|
|
|

|
No, your code doesn't do the same thing. You cannot pass your functors to winapi functions which call by stdcall. What you've got is a boost::bind lookalike. There are a bunch of very, very nice class sets for this on this site already. See e.g. the fastest possible delegate article.
Also, my code does support return values and parameters -- it's all in the source code.
|
|
|
|
|

|
I do not think FlushInstructionCache is applicable in this case because the memory is newly allocated and has not been executed or read before. Good point in the general case however.
|
|
|
|

|
While you are absolutely right: It should be present; I wouldn't say that the implications are quite as horrific as "not safe" makes it sound. On x86 architecture, cache coherency is guaranteed by snooping protocols such as MESI, so there's really no way to stumble upon an uninitialized thunk.
|
|
|
|

|
The x86 architecture is a moving target; even if flushing is not required now (which I'm not saying is true) it may be on a future CPU. The documentation is quite explicit: "Applications should call FlushInstructionCache if they generate or modify code in memory." The ATL WNDPROC thunking code flushes. On some platforms the call may do nothing but on others platforms (perhaps future platforms) this may not be the case. The HAL (Hardware Abstraction Layer) will implement the flushing as appropriate for the platform in question. In short follow the rules and minimise assumptions to stay out of trouble both now and in the future. A classic example of Microsoft getting into trouble for not following their own rules based on assumptions about the existing hardware can be found in the ATL (MSVC 6 version) WNDPROC thunking code: Even though the documentation for VirtualAlloc states that one of the PAGE_EXECUTE protections must be present for code to be executed the ATL code didn’t apply it and it didn’t matter on existing platforms: once DEP was implemented this assumption was no longer valid.
Steve
|
|
|
|

|
Steve,
I'm not arguing the fact that it shouldn't be present, I'm just saying that it wouldn't cause a crash on any current (or previous) mainstream desktop processor. If I recall correctly, all Intel based CPUs starting with the 486 has had snooping cache mechanics. This means that there's no way a dirty piece of cache can be attempted executed. What could make for trouble, was if the application was multi-threaded in such a (bad) way that the thunk was initialized "at the same time" as it was passed along as a callback. Under such circumstances, read and write re-ordering could possibly cause an uninitialized thunk to execute. Then again, under such circumstances, there's no guarantee that the rest of the class (in which the thunk resides) would be fully initialized, so the application would crash anyway.
|
|
|
|

|
VirtualAlloc allocates a minimum of one page (4K) so if this technique was used for every WindowProc of a windows application it could end up using a lot of virtual memory. It would be better to have a simple suballocation package that allows multiple trampolines to use a single VirtualAlloc.
-- modified at 3:09 Monday 18th December, 2006
|
|
|
|

|
Yes, you are probably right. I have previously decided against such allocators, based on weighing of the two evils that are footprint overhead, and a threadsafe singleton allocator. I will include such functionality, possibly policy-provided, in the next update, though.
Thanks for the feedback!
|
|
|
|

|
I've replaced VirtualAlloc with suballocation in a private heap object. That should take care of the problem
Thanks!
|
|
|
|

|
Any chance on 64bit support?
Todd Smith
|
|
|
|

|
I'll be getting around to that, hopefully within the next few days. Updates will be posted here when it's done
|
|
|
|
|

|
Why you put code with very error in code project !!!???
What is BOOST folder ???
#include "boost/static_assert.hpp"
#include "boost/preprocessor/repetition.hpp"
#include "boost/preprocessor/iterate.hpp"
|
|
|
|
|
 |
|
|
General News Suggestion Question Bug Answer Joke Rant Admin
|
A quick introduction to thunking, as well as a demonstration of a simple library which does the work for us.
| Type | Article |
| Licence | Apache |
| First Posted | 16 Dec 2006 |
| Views | 55,774 |
| Downloads | 414 |
| Bookmarked | 52 times |
|
|