Szakáts Viktor wrote:
I thought you'll know the solutions to these problems :)
Hi,
if I've understood a problem, you are trying to call dynamic C function
(function with no parameters count known for compiler). I'm using this
solution for some 9 years under Win16, Win32. It's not multiplatform,
not Win64 compatible, but it works in the cases I need. The idea is:
static const BYTE CallProcByStackFrameCode[] = {
0x55, // push ebp
0x8B, 0xEC, // mov ebp,esp
0x8B, 0x4D, 0x10, // mov ecx,dword ptr [ebp+16]
0x67, 0xE3, 0x10, // jcxz @@noparam
0x8B, 0x5D, 0x0C, // mov ebx,dword ptr [ebp+12]
0x2B, 0xE1, // sub esp,ecx
0x8B, 0xD4, // mov edx,esp
0x8A, 0x03, // @@loop: mov al,byte ptr [ebx]
0x36, 0x88, 0x02, // mov byte ptr ss:[edx],al
0x43, // inc ebx
0x42, // inc edx
0xE2, 0xF7, // loop @@loop
0x8B, 0x45, 0x08, // @@noparam: mov eax,dword ptr [ebp+8]
0xFF, 0xD0, // call eax
0x8B, 0xE5, // mov esp,ebp
0x5D, // pop ebp
0xC3}; // ret
typedef int (* CallProcByStackFrameFunc) (void*, void*, int);
CallProcByStackFrameFunc CallProcByStackFrame =
(CallProcByStackFrameFunc)CallProcByStackFrameCode;
You are free to call any C function with any stackframe directly from
.prg, but expect GPF is you are not good enough on internals:
HB_FUNC ( CALLPROCBYSTACKFRAME )
{
if ( ISCHAR( 2 ) )
hb_retnl( CallProcByStackFrame( hb_parptr( 1 ), hb_parc( 2 ),
hb_parclen( 2 ) ) );
else
hb_retnl( CallProcByStackFrame( hb_parptr( 1 ), NULL, 0 ) );
}
CALLDLL(), DLLEXECUTE(), etc .prg calls can be implemented in a way
similar to this:
HB_FUNC ( CALLPROC )
{
/*
TODO: this function always returns LONG type. It could be pointer, etc.
Tipu konvertavimas:
NUMERIC -> int
CHAR -> char*
LOGIC -> BOOL
Jei ByRef, tai atgal sukisami su _storn, _storclen.
Pvz.:
cBuf := SPACE(1000)
nRead := 0
CallProc("ReadFile", hFile, @cBuf, 1000, @nRead, NIL);
*/
long pbuf[ 10 ];
int i, j, pcount, storn = 0, storc = 0, storp = 0;
char *pstr, *pFuncName;
HINSTANCE hInst;
void* pFunc;
pcount = hb_parinfo( 0 );
j = 9;
for ( i = 2; i <= pcount; i++ )
{
if ( i - 2 >= j )
{
// ### runerror
hb_ret();
return;
}
if ( ISNUM( i ) )
{
if ( ISBYREF( i ) )
{
pbuf[ j ] = (long) hb_parnl( i );
pbuf[ i - 2 ] = (long) &pbuf[ j ];
j--;
storn |= 1 << ( i - 2 );
}
else
pbuf[ i - 2 ] = (long) hb_parnl( i );
}
else if ( ISCHAR( i ) )
{
if ( ISBYREF( i ) )
{
pbuf[ i - 2 ] = (long) hb_xgrabcopy( hb_parclen( i ) + 1,
hb_parc( i ) );
storc |= 1 << ( i - 2 );
}
else
pbuf[ i - 2 ] = (long) hb_parc( i );
}
else if ( ISPOINTER( i ) )
{
if ( ISBYREF( i ) )
{
pbuf[ j ] = (long) hb_parptr( i );
pbuf[ i - 2 ] = (long) &pbuf[ j ];
j--;
storp |= 1 << ( i - 2 );
}
else
pbuf[ i - 2 ] = (long) hb_parptr( i );
}
else if ( ISLOG(i) )
pbuf[ i-2 ] = (long) hb_parl( i );
}
hInst = NULL;
if ( ISPOINTER( 1 ) ) pFunc = hb_parptr( 1 );
else
{
pstr = hb_parc(1);
if ( ( pFuncName = strrchr( pstr, ':' ) ) == NULL )
{
// ### runerror
hb_ret();
return;
}
*pFuncName = '\0';
hInst = LoadLibrary( pstr );
*pFuncName = ':';
pFunc = GetProcAddress( hInst, pFuncName + 1 );
}
if ( ! pFunc )
{
// ### runerror
hb_ret();
return;
}
hb_retnl( CallProcByStackFrame( pFunc, &pbuf, ( pcount - 1 ) * 4 ) );
if ( hInst )
FreeLibrary( hInst );
for ( i = 0; i < pcount - 1; i++ )
{
if ( storn & (1 << i) )
{
hb_stornl( * (long*) pbuf[ i ], i + 2 );
}
else if ( storc & (1 << i) )
{
hb_storclen( (char*) pbuf[ i ], hb_parclen( i + 2 ), i + 2 );
hb_xfree( (char*) pbuf[ i ] );
}
else if ( storp & (1 << i) )
{
hb_storptr( (void*) pbuf[ i ], i + 2 );
}
}
}
This is one of the most hackiest function in my Windows code. One more
solution I use for the same 9 years is dynamic WndProc registration.
I've not seen it in other libs. For example, contrib\what32\wincorec.c
uses ten callbacks __WndProc, ..., __WndProc10. But it's not a problem
to have any number of window procedures and create it dynamically. I
create one for every class like Button, ListBox, Static, Dialog, during
.prg level class initialisation.
#define LENProcCode 13
typedef struct
{
BYTE code[ LENProcCode ];
} WProcCode;
static WProcCode pWPCode[ 256 ];
Some library initialisation code should do this:
for ( i = 0; i < 256; i++ )
{
pWPCode[ i ].code[ 0 ] = 0x6A; // push imm8
pWPCode[ i ].code[ 1 ] = (unsigned char) i;
pWPCode[ i ].code[ 2 ] = 0xB8; // mov ax,imm32
* (int*) &( pWPCode[i].code[ 3 ] ) = (int) &WProcHandler;
pWPCode[ i ].code[ 7 ] = 0xFF; // call eax
pWPCode[ i ].code[ 8 ] = 0xD0;
pWPCode[ i ].code[ 9 ] = 0x59; // pop ecx
pWPCode[ i ].code[ 10 ] = 0xC2; // retn 0010h
pWPCode[ i ].code[ 11 ] = 0x10;
pWPCode[ i ].code[ 12 ] = 0x00;
}
Now you can allocate any of 256 entries dynamicaly. I use this simple
allocation scheme, it fits my needs, but you can change push imm8, to
push imm32 and allocate pWPCode array items by hb_xgrab(), you'll have
dynamic window procedure allocation with limit of 2^32 procedures :)
Oh, I forgot:
static LRESULT WProcHandler( int iEntryPoint, void* pRetAddr, HWND hWnd,
UINT uiParam, WPARAM wParam, LPARAM lParam )
{
....
}
Here is real entry handling function. Use iEntryPoint (it's imm8
parameter from machine code above) to identify allocated procedure's
resources such as PHB_ITEM pCodeblock to call Harbour level handlers, etc.
Best regards,
Mindaugas
P.S. this is great solutions for Win32, but it's not multiplatform. It's
hacky, so, try to this keep code isolated. Hacking should not be a habit
of developer (or you'll need to rewrite your code from the scratch soon).
_______________________________________________
Harbour mailing list
[email protected]
http://lists.harbour-project.org/mailman/listinfo/harbour