Page 1 of 1

Three ways to obtain hInstance

Posted: Mon Jul 05, 2004 7:38 pm
by GedB
There are 3 ways to obtain hInstance.

You can obtain the hInstance by popping the value from the stack at the very beginning of your program.

You can obtain the hInstance by reading it from the system variable [_PB_Instance].

You can obtain hInstance by calling the API.

Code: Select all

DefType.l pophInst, apihInst, pbhInst
;Inline ASM must be enabled!

;Pop from the stack
POP eax
POP eax
MOV pophInst, eax
Debug pophInst

;Read from [_PB_Instance]
MOV eax, [_PB_Instance]
MOV pbhInst, eax
Debug pbhInst

;call the api
apihInst = GetModuleHandle_(0)
Debug apihInst
If this code leaves you a little puzzled, please allow me to explain further.

In windows every instance of an application is identified by a unique handle, referred to as the hInstance (h is for handle). Many API calls require this value.

In Purebasic we normally obtain hIntance by the following API call:

Code: Select all

    hInstance = GetModuleHandle_(0)
GetModuleHandle can be used to obtain the handle for any running module. By passing 0 it returns the default value, the current one.

This is not how C Programmers obtain hInstance. For C Programmers hInstance is one of the pieces of information passed as a paramater to the WinMain function.

WinMain is the opening function for all C windows programs. It is a function that windows calls with 4 paramaters:

Code: Select all

int WINAPI WinMain(

    HINSTANCE hInstance,	// handle to current instance
    HINSTANCE hPrevInstance,	// handle to previous instance
    LPSTR lpCmdLine,	// pointer to command line
    int nCmdShow 	// show state of window
   );	
This definition is from the Windows API reference.

If you use the /Inline option on the command line compiler you can see that PureBasic also uses WinMain, it is just that Fred has taken care of its implementation in the compiler.

Code: Select all

_WinMain@16:
; 
  PUSH   dword I_BSSEnd-I_BSSStart
  PUSH   dword 0
  PUSH   dword I_BSSStart
  CALL  _memset
  ADD     esp,12
  MOV    eax,[esp+4]
  MOV    [_PB_Instance],eax
Before we can understand this ASM, we need to understand better the way in which C passes paramaters to functions. C passes paramaters to a function by pushing them onto the stack.

You can see this in the first four lines. The three paramaters are pushed onto the stack, and then _memset is called.

The stack uses the register esp, the stack pointer, to manage the stack. The function POP retrieves the value pointed to by esp and then move the stack on 4 bytes. 4 bytes being the length of a long.

Always out to save a few cycles Fred bypasses the POP command and directly manipulates esp himself. Rather than call pop 3 times to clear these paramaters from the stack he adds 12 ( 4 x 3) directly to esp.

This leaves the stack back where it started, pointing to the first paramater: hPrevInstance. This paramater is a throwback to Windows 3.1, and in win32 is always 0.

Again Fred saves a few cycles by accessing the stack pointer directly. Instead of POPing twice he just moves esp + 4 into eax in order to get to the next paramater: hInstance. He moves the value from this paramater through a register and into the system variable [_PB_Instance]. This is where we get it from when we say:

Code: Select all

;Read from [_PB_Instance]
MOV eax, [_PB_Instance]
MOV pbhInst, eax
Debug pbhInst
As well as saving some time by avoiding POP Fred has also ensured that when our program starts running the stack is in exactly the same place for us as it is for the c programmers. This gives us full access, through ASM, to WinMain's paramaters simply by POPing them from the stack:

Code: Select all

;Pop from the stack
POP eax
POP eax
MOV pophInst, eax
Debug pophInst

Posted: Tue Jul 06, 2004 12:21 am
by Dare2
Hi GedB.

Very interesting and informative, thanks!