Page 1 of 1

Setting the window callback for a window outside of PB?

Posted: Tue Nov 27, 2007 9:48 pm
by Mistrel
Is there a way to do something similar to SetWindowCallback(function_ptr,#window) but to a window handle outside of PB?

I program in DarkBasic and would like to wrap something like this into a plugin so that I can capture window events from the DBP window and send them to a function.

Posted: Tue Nov 27, 2007 10:09 pm
by Trond
Use SetWindowLong_() with #GWL_WNDPROC. Make sure you use CallWindowProc_() to call the old window procedure if you want default events to be handled. (Get the address of the old procedure with GetWindowLong_() and #GWL_WNDPROC.)

Posted: Tue Nov 27, 2007 10:24 pm
by srod
I'm not sure you can do this in a cross-process manner Trond. Well, I've just tried it and it didn't work, and using GetLastError_() returned an access denied error.

I think the only way to do this if the Window in question is from another process is to use a global hook.

Posted: Tue Nov 27, 2007 10:28 pm
by Trond
No, you can't do it in other processes, but it works on windows created in the same process but by a different programming language. If you can load a dll into the process you can do it.

Posted: Fri Nov 30, 2007 1:36 am
by Mistrel
Am I doing something wrong? I can't make it work in this test program.

Code: Select all

Global ptr

Procedure EnumProc(hwnd, lParam)
  *hwnd.LONG = lParam
  *hwnd\l = hwnd
  ProcedureReturn 0
EndProcedure

Procedure GetCallingHwnd()
  *hwnd.LONG = AllocateMemory(SizeOf(LONG))
  EnumThreadWindows_(GetCurrentThreadId_(), @EnumProc(), *hwnd)
  ProcedureReturn *hwnd\l
EndProcedure 

Procedure WinCallback(hWnd,uMsg,wParam,lParam)
	Select uMsg
		Case #WM_SIZE
			; do nothing
		Case #WM_LBUTTONUP
			MessageRequester("","Hello!")
	EndSelect
	If ptr
		CallFunctionFast(ptr,hWnd,uMsg,wParam,lParam)
	EndIf
	ProcedureReturn #PB_ProcessPureBasicEvents 
EndProcedure

ProcedureCDLL _SetWindowCallback()
	hwnd=GetCallingHwnd()
	ptr=GetWindowLong_(hwnd,#GWL_WNDPROC) ; capture the old callback ptr
	SetWindowLong_(hwnd,#GWL_WNDPROC,@WinCallback()) ; set the new callback ptr
EndProcedure

OpenWindow(0,100,200,320,240,"Window",#PB_Window_SystemMenu)

OpenLibrary(0,"windowcallback.dll")
CallFunction(0,"_SetWindowCallback")

Repeat
Until WaitWindowEvent()=#PB_Event_CloseWindow

Posted: Fri Nov 30, 2007 2:12 am
by srod
Well first you need to separate this into 2 programs; one for the dll and one a separate program to open the window and call the dll etc. At the moment you are combining it all into one which is a big no-no!

Secondly, you're mixing calling conventions. Change the ProcedureCDLL for just ProcedureDLL.

Posted: Fri Nov 30, 2007 12:08 pm
by Mistrel
Nevermind. I forgot to forward the return value from DarkBasic's old callback. That's what was causing the problem. :)

That and my example was just bad. :roll:

Posted: Sat Dec 01, 2007 2:24 am
by Mistrel
I'm being told on the Dark Basic Professional board that there is a problem with the method I came up with for using an alternative callback. I don't understand what the problem is because I only see it working. Would someone explain to me what's wrong?

http://forum.thegamecreators.com/?m=for ... 60&b=1&p=0

This is the source for my callback.dll.

Code: Select all

Define old_wndproc.l

Procedure EnumProc(hwnd, lParam)
  *hwnd.LONG = lParam
  *hwnd\l = hwnd
  ProcedureReturn 0
EndProcedure

Procedure GetCallingHwnd()
  *hwnd.LONG = AllocateMemory(SizeOf(LONG))
  EnumThreadWindows_(GetCurrentThreadId_(), @EnumProc(), *hwnd)
  ProcedureReturn *hwnd\l
EndProcedure 

ProcedureCDLL _SetWindowCallback(wndproc)
	Shared old_wndproc ; the old callback ptr to be used for passing on the old events
	; hWnd, handle of the window to register the callback to
	; wndproc, the pointer for the procedure to be used for this callback
	dbp_hwnd=GetCallingHwnd()
	old_wndproc=GetWindowLong_(dbp_hwnd,#GWL_WNDPROC) ; capture the old callback ptr
	SetWindowLong_(dbp_hwnd,#GWL_WNDPROC,wndproc) ; pet the new callback ptr
	ProcedureReturn old_wndproc ; return the old callback ptr to pass-through the old events
EndProcedure

ProcedureCDLL CallOldWndProc(hWnd,uMsg,wParam,lParam)
	Shared old_wndproc ; the old callback ptr to be used for passing on the old events
	result=CallFunctionFast(old_wndproc,hWnd,uMsg,wParam,lParam)
	ProcedureReturn result
EndProcedure

Posted: Sat Dec 01, 2007 12:47 pm
by srod
Well, putting aside the fact that the very idea of what you are doing strikes me as a little odd, the code looks okay.

Just remember that because you have used ProcedureCDLL() that you have declared your two exported procedures as using the CDECL calling convention.

Thus if you are calling these dll procedures from PB, then use either CallCFunction() or CallCFunctionFast() or ImportC etc. Do not use CallFunction() or CallfunctionFast() or Import etc. If calling from another language then ensure that language is setup to use the CDECL convention.

Posted: Sat Dec 01, 2007 8:33 pm
by Mistrel
Well, putting aside the fact that the very idea of what you are doing strikes me as a little odd, the code looks okay.
This dll allows me to register my own wincallback function natively within DBP. This is really useful for trapping critical system messages and acting upon them while the game is in a critical loop.

It's basically the same as SetWinCallback() for PB but this one preserves the original callback and is specificall written for DBP.

Now I can work with the wincallback just like any other function.

Code: Select all

function WinCallback(hWnd,uMsg,wParam,lParam)
	Select uMsg
		Case WM_SIZE
			` do nothing
		EndCase
		Case WM_LBUTTONUP
			print "lbutton up"
		EndCase
	EndSelect
	result=CallOldWndProc(hWnd,uMsg,wParam,lParam)
endfunction result

Posted: Tue Dec 04, 2007 4:22 am
by PurePWNRER
LOL, talk about limited languages.
Why would anyone use that language when it doesn't even support this...