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

Reply via email to