Author Topic: Late binding to DLL / Call DWORD equiv / (for Ray)  (Read 7807 times)

0 Members and 3 Guests are viewing this topic.

Charles Pegge

  • Guest
Late binding to DLL / Call DWORD equiv / (for Ray)
« on: March 04, 2018, 05:58:31 PM »
There are many ways to declare a procedure. Here are some of the combinations for late binding:

In these examples we are using '!' instead of 'declare function'.

Code: [Select]
'2018-03-05 T 01:42:51
'
'LATE BINDING TO DLL
====================
'
'BASIC WITH PROTOTYPE
'! ptr MessageBox (sys hwnd,char*msg,*title,int mode) as int
'C STYLE
'int (*MessageBox) (sys hwnd,char*text,char*title,int mode)
'BASIC UNPROTOTYPED
!* MessageBox
'
sys u32=LoadLibrary "user32.dll"
@MessageBox=GetProcAddress  u32, "MessageBoxA"
'
'USE LIKE ANY OTHER FUNCTION CALL
MessageBox 0,"helo","greet",0
'EXPLICIT CALL
call MessageBox 0,"helo","greet",1

Raymond Leech

  • Guest
Re: Late binding to DLL / Call DWORD equiv / (for Ray)
« Reply #1 on: March 05, 2018, 07:16:49 AM »
Thanks Charles, I see the similarities now.

Mike Lobanovsky

  • Guest
Re: Late binding to DLL / Call DWORD equiv / (for Charles)
« Reply #2 on: March 05, 2018, 05:21:54 PM »
Hi Charles,

"Late binding to DLL" declares, header files, prototyping and similar stuff are retrograde, ridiculous, and redundant.

Luckily, you are still king and god in your alpha world of Oxygen Basic.

O2 in all the three of its hypostases (IDE, Oxygen.dll, and end user executables via the O2 runtime library they are linked against) is always linked against at least four system DLLs: kernel32.dll, user32.dll, oleaut32.dll, and msvcrt.dll. Add gdi32.dll into the bargain (BASIC without graphics primitives like LINE, CIRCLE, PSET, etc. is not a BASIC, is it?) and be merciful to O2 users.

Since the 5 DLLs are already in the process memory regardless of the user code, spare them this monkey business messing with the Windows includes and late bindings.

1. Run the WINDOWS_LEAN_AND_MEAN set of include files once through a utility that would convert them to the O2 IDE's table of intellisense entries.

2. Map the vtables of these DLLs in their entirety at app load time dynamically into the O2 namespace and make their APIs part of the O2 user's extra default arsenal.

Looping through the DLL export API name table is trivial. Call GetProcAddress() for each name found. This system function is more robust and reliable than manual hopping from thunk to thunk in the DLL vtable.

3. Add an #import MM.DLL meta (DLL name is exemplary here) to the O2 vocabulary. If the user would like to see a Windows multimedia API call in their code, they either:
  • know exactly what they're doing
or
  • have a corresponding MSDN page/help file open before their eyes and have no further use for any bloody headers or declares that would only flood and bloat their scripts
or both. Make this O2 meta do exactly the same as what's described in Item 2 above for each DLL the user would care to import. Loading a DLL for just one API call is
  • exactly as memory consuming as pre-loading it for a zillion API calls
  • nearly as time consuming as mapping a coupla hundred API names dynamically once into a permanent name table
and let late bindings be done with once and for all.

FBSL has been doing that for decades in its BASIC interpreter and DynAsm JIT compiler, and for years, additionally in separate DynC code blocks. The DynC JIT compiler uses an ELF executable object format but I'm too old and respectful to end my days roaming through the debris of an inferior OS finding out how to set up shared memory for dozens of DynC blocks I'm using in almost every FBSL app I write. And still FBSL is practically twice faster than OxygenBasic to launch a script of comparable size and complexity in the JIT compilation mode (remember oxyscheme/tinyscheme olympics? :) ).

(I've just had a look into my O2 folder to see what Oxygen.dll imports are, and noticed the InMemoriam.o2bas script in the root there. You are very kind, Charles, and you always have been. Thank you.)

Charles Pegge

  • Guest
Re: Late binding to DLL / Call DWORD equiv / (for Ray)
« Reply #3 on: March 05, 2018, 06:45:38 PM »
Hi Mike,

Yes, I would be delighted to dump the 'bloody' headers. That will work for the most part  of those Windows DLLs. But how do you handle the A and W calls in FBSL?

As for Opengl, we have mixed floats and ints, so we would have to rely on users being rigorous in making that distinction when passing parameters.

Mike Lobanovsky

  • Guest
Re: Late binding to DLL / Call DWORD equiv / (for Charles)
« Reply #4 on: March 05, 2018, 08:13:44 PM »
MS has a default convention that every system API whose name ends in a capital A stands for ASCII if there is also a matching W version found, that would then stand for WCHAR. Same API without an A or W is not a DLL name table entry but a PP header #define sensible to what platform bitness the compiler is currently dealing with.

You need no PP to do that for you in real time. Regardless of bitness, O2 would always find and map the MessageBoxA and MessageBoxW names in the name table of respective 32- or 64-bit user32.dll. But then it would also create one more "synthetic" O2 name MessageBox and would point it to either MessageboxA or MessageBoxW depending on which bitness version of an O2 executable is currently in the process of launching.

Re. "rigorous in making that distinction"

Exactly. That's what intellisense, MSDN and help files are for. And if they are still passing an int instead of a float despite all these three aids open on their desktops, let their apps crash and rot in hell. Hopefully they will still be able to find peace and happiness someplace else, milking cows or breeding pigs.

Tip: like any other respectful BASIC, FBSL supports type identifiers to its vars and literals:

% = int/long/bool
%% = long-long/quad
! = float/single
!! = double

and whenever I see a * qualifier in the MSDN or help file, I'm supposed to pass my var reference as an explicit @varname parameter in my FBSL statement. When the time comes and I won't be able to do just that little, I'll go hang my poor self quietly in the khazi.

Re. "bloody"

BASIC headers and declares have been invented by the evil dudes from the VB and PB sites to shake out yet more money from our pockets. Let's not let them do it any more to us, Charles. :D

But if it ever comes to cloning PB in x64 O2, let's let them have those headers in 64 bits as a matter of their culture and tradition. FBSL does allow line numbers in its BASIC scripts for the sake of Darthmouth busyworkers from retro-BASIC sites, but trashes them silently while parsing. ;)

Charles Pegge

  • Guest
Re: Late binding to DLL / Call DWORD equiv / (for Ray)
« Reply #5 on: March 06, 2018, 08:55:56 PM »
Thanks, Mike,

We can extract all the names and call address RVAs in one go. To get the call address for each function, its RVA is simply added as an offset to the library handle.

But what do we do about all those wretched equates that take up the bulk of header files?

Mike Lobanovsky

  • Guest
Re: Late binding to DLL / Call DWORD equiv / (for Charles)
« Reply #6 on: March 06, 2018, 11:35:44 PM »
... its RVA is simply added as an offset to the library handle.

Charles, beware! This attitude is naive, or Microcoft wouldn't be who they effectively are and there wouldn't be such a thing as GetProcAddress! Take my advice or as I said you're going to find yourself grasshopping all over the DLL's PE header pretty soon chasing the real entry point rather than another thunk! :D

Did you know MS VB6's original DoEvents takes perhaps a thousand lines of C code to re-implement? Yes, my middle initials stand for Ida Pro ... :D

(Seriously, I'm under an NDA with my former FBSL coworkers, and I'll always be, so I can't provide ready-made solutions. But I can still give hints and tips and applaud to bingos. :) )

But what do we do about all those wretched equates that take up the bulk of header files?

Let them stay, Charles. They aren't that bad and they are all pretty logical and helpful to keep human readability of any programming language pretty high. FBSL sports an approx. 200KB large (LF only rather than CRLF line endings) equate-only Windows.inc header. My personal statistics runs as follows:
  • 99% of code failures are due to faulty external function declarations (except for when the header has been written by José Roca himself, in which case the error level falls down to negligible)
  • 1% of errors are due to the inability of a particular BASIC dialect to ensure proper alignment of its UDT members as compared to their C prototypes
  • Equates never fail :)

Charles Pegge

  • Guest
Re: Late binding to DLL / Call DWORD equiv / (for Ray)
« Reply #7 on: March 07, 2018, 12:20:16 AM »
A modest tweak in the standard dll sectional info, and the whole world would be spared the curse of headers, completely.

I'll do some research on the Kernel and his fellow officers. It is very easy to check all the call addresses against GetProcAddress values and list any anomalies.

Charles Pegge

  • Guest
Re: Late binding to DLL / Call DWORD equiv / (for Ray)
« Reply #8 on: March 07, 2018, 04:49:31 AM »
Well, here are my findings on a selection of DLLs.

The Kernel is the worst offender with 174 GetProcAddress mismatches, followed by GDI with 54.

As you can see from the lists, most of the calls are obscure. And it's only a small percentage of the thousands of API calls in the system.


Mike Lobanovsky

  • Guest
Re: Late binding to DLL / Call DWORD equiv / (for Charles)
« Reply #9 on: March 07, 2018, 09:15:37 AM »
Thank you, Charles.

You may call me anything you want but don't call me late for supper. 8)

(One can also utilize this MS "feature" as sorta protection against fiddling with custom PE loaders used to avoid AV alarms.)

Charles Pegge

  • Guest
Re: Late binding to DLL / Call DWORD equiv / (for Ray)
« Reply #10 on: March 09, 2018, 07:17:33 AM »
Since most programs will only use a tiny fraction of the Windows API, on reflection, I think we should use this facility for generating unprototyped 'omni' headers, and store them unprocessed, somewhere low priority, until called forth.

Then the overhead of using GetProcAddress is minimal anyway.

Mike Lobanovsky

  • Guest
Re: Late binding to DLL / Call DWORD equiv / (for Charles)
« Reply #11 on: March 09, 2018, 07:49:16 AM »
Quote
... we should use this facility for generating unprototyped 'omni' headers ... low priority, until called forth

There is nothing more permanent than something temporary, postponed as a TODO till better times.

Now think about name mangling as in "@n" appended at the end of API names to denote their call stack size, or C-style "_" DLL API prefixes.

Dumping function declarations altogether IS very handy, I assure you. Try your "unprototyped" declares with explicit pointer and type identifiers in your API calls as per MSDN and DLL documentation, and you'll see how obviously logical and natural this scheme appears to be.

Try loading and mapping the 5 DLLs at app launch time and enjoy 2,500+ most usable Win32 SDK APIs at your immediate disposal on default without stirring a finger. ;)

Charles Pegge

  • Guest
Re: Late binding to DLL / Unprototyped Windows Headers
« Reply #12 on: March 11, 2018, 05:33:36 AM »
Well, this is what I have come up with:

It works well without any further compiler optimisation. The extra compilation time is barely perceptible.

Code: [Select]
'2018-03-10 T 15:53:37

------------
'CoreWin.inc
============


'equates,types,macros
=====================

uses WinData


'dll declarations
=================

uses Kernel  '1595
uses User    '985
uses Gdi     '945
uses Comctl  '118
uses Comdlg  '28
uses Oleaut  '409
uses Shell   '484

'4564 declarations

The 'A' procedures use an alias, but the 'W' procedures are left intact, to call explicitly.

Comdlg.inc, for example:
Code: [Select]
extern lib "Comdlg32.dll"
! ChooseColor "ChooseColorA"
! ChooseColorW
! ChooseFont "ChooseFontA"
! ChooseFontW
! CommDlgExtendedError
! DllCanUnloadNow
! DllGetClassObject
! FindText "FindTextA"
! FindTextW
! GetFileTitle "GetFileTitleA"
! GetFileTitleW
! GetOpenFileName "GetOpenFileNameA"
! GetOpenFileNameW
! GetSaveFileName "GetSaveFileNameA"
! GetSaveFileNameW
! LoadAlterBitmap
! PageSetupDlg "PageSetupDlgA"
! PageSetupDlgW
! PrintDlg "PrintDlgA"
! PrintDlgEx "PrintDlgExA"
! PrintDlgExW
! PrintDlgW
! ReplaceText "ReplaceTextA"
! ReplaceTextW
! Ssync_ANSI_UNICODE_Struct_For_WOW
! WantArrows
! dwLBSubclass
! dwOKSubclass
end extern

« Last Edit: March 11, 2018, 05:43:49 AM by Charles Pegge »

Charles Pegge

  • Guest
Re: Late binding to DLL / Attaching protptypes
« Reply #13 on: March 11, 2018, 07:06:10 AM »
A minor tweak to o2 allows prototypes to be 'attached' to unprototyped declarations.

In this messagebox example, default values are also specified:

Code: [Select]
'2018-03-11 T 10:13:29
'prototype overlay
uses corewin
'Create protype with default values for MessageBox
! messagebox(
  sys hwnd=0,
  char*text="",
  char*title="OxygenBasic",
  int mode=0
) at @messagebox
'
'TEST
messagebox
messagebox text="helo"

https://github.com/Charles-Pegge/OxygenBasic/blob/master/OxygenBasicProgress.zip



José Roca

  • Guest
Re: Late binding to DLL / Call DWORD equiv / (for Ray)
« Reply #14 on: March 11, 2018, 07:20:46 AM »
And what about the return type?