> could be the asm macro converted to pb?, i know we have
> the callcom lib but it would be very interesting to understand
> how com works.
I understood it... and made CallCOM() for using OOP with PureBasic..
You want to know how it works internally ??
Make a coffey, sit down and read slowly and carefully...
..i will
try to explain.
Here is an example that shows a little bit what a 'COM-Object'
is internally.
The object itself is a variable (long) that holds a pointer to
a pointer to a list.
This list has entries to all procedures in it.
Code: Select all
;
; by Danilo, 17.03.2003
;
;
#Function1 = 0 * 4 ; 0
#Function2 = 1 * 4 ; 4
#ADDITION = 2 * 4 ; 8
;
Procedure Function1(this)
Beep_(800,100)
EndProcedure
;
Procedure Function2(this,string.s)
MessageRequester("INFO",string,0)
EndProcedure
;
Procedure Function3(this,a,b)
ProcedureReturn a+b
EndProcedure
;
Procedure CreateMyClass()
Shared x
Dim CreateMyClass_Interface.l(3)
CreateMyClass_Interface(0) = @Function1()
CreateMyClass_Interface(1) = @Function2()
CreateMyClass_Interface(2) = @Function3()
x = @CreateMyClass_Interface()
ProcedureReturn @x
EndProcedure
;
object = CreateMyClass()
;
CallCOM(#Function1,object)
CallCOM(#Function2,object,"Hello")
CallCOM(#Function1,object)
CallCOM(#Function2,object,"Hello World!")
CallCOM(#Function1,object)
MessageRequester("Result", Str( CallCOM(#ADDITION ,object,12,8) ),0)
What is very important here is that the first parameter
of every procedure/method in the class must be "this".
This first parameter is the object itself.
If you look at this code carefully, you will see how the Constants
for CallCOM() are made.
The constant for a function/method is the 'index in the list * 4'
('*4' because its LONG, 4 bytes).
The command CallCOM() is good because it has variable argument count.
We dont have this for PureBasic-Procedures now, so we must
make separate procedures for every count.
Lets name the procedures myCallCOMx() - where x is the number of arguments:
Code: Select all
;
; by Danilo, 04.05.2003
;
;
;>---------
;>---------
;
;- CallCOM procedures
Structure LONG
l.l
EndStructure
;
Procedure myCallCOM0(index,*object.LONG)
*pointer_to_list.LONG = *object\l
*pointer_to_list + index
*address_of_procedure = *pointer_to_list\l
ProcedureReturn CallFunctionFast(*address_of_procedure,*object)
EndProcedure
;
Procedure myCallCOM1(index,*object.LONG,arg1)
*pointer_to_list.LONG = *object\l
*pointer_to_list + index
*address_of_procedure = *pointer_to_list\l
ProcedureReturn CallFunctionFast(*address_of_procedure,*object,arg1)
EndProcedure
;
Procedure myCallCOM2(index,*object.LONG,arg1,arg2)
*pointer_to_list.LONG = *object\l
*pointer_to_list + index
*address_of_procedure = *pointer_to_list\l
ProcedureReturn CallFunctionFast(*address_of_procedure,*object,arg1,arg2)
EndProcedure
;
;>---------
;>---------
;- Class Interface Definition
#Function1 = 0 * 4 ; 0
#Function2 = 1 * 4 ; 4
#ADDITION = 2 * 4 ; 8
#SUBTRACTION = 3 * 4 ; 12
;- Class methods/procedures
;- method 1
Procedure Function1(this)
Beep_(800,100)
EndProcedure
;
;- method 2
Procedure Function2(this,string.s)
MessageRequester("INFO",string,0)
EndProcedure
;
;- method 3
Procedure Function3(this,a,b)
ProcedureReturn a+b
EndProcedure
;
;- method 4
Procedure Subtraction(this,a,b)
ProcedureReturn a-b
EndProcedure
;- class creation
Procedure CreateMyClass()
Shared x
Dim CreateMyClass_Interface.l(4)
CreateMyClass_Interface(0) = @Function1()
CreateMyClass_Interface(1) = @Function2()
CreateMyClass_Interface(2) = @Function3()
CreateMyClass_Interface(3) = @Subtraction()
x = @CreateMyClass_Interface()
ProcedureReturn @x
EndProcedure
;
;
;- Program START
;
; create an object
object = CreateMyClass()
;
; call some functions/methods in the object
myCallCOM0(#Function1,object)
myCallCOM1(#Function2,object,@"Hello World!") ; (we must use '@' here, because its a pointer to the string)
myCallCOM0(#Function1,object)
MessageRequester("ADD", Str( myCallCOM2(#ADDITION ,object, 50,112) ),0)
myCallCOM0(#Function1,object)
MessageRequester("SUB", Str( myCallCOM2(#SUBTRACTION,object,118, 2) ),0)
myCallCOM0(#Function1,object)
Now, this should give you the rest and enlighten you guys...
The people who fully understand the above code, know how
simple object oriented programming (OOP) works internally.
Now, the above example-class is very wrong, because all classes
inherit from a standard class with the 3 methods
QueryInterface, AddRef and Release.
EVERY class has this 3 standard-methods, so the interface
definition begins always with:
Code: Select all
#QueryInterface = 0 * 4
#AddRef = 1 * 4
#Release = 2 * 4
Oh well... sorry, its not easy for me to explain all this in english.
I hope somebody could understand it...
The hardest thing to explain is how you get/make the constants
for interfaces used by windows.
For this, you must have the MS Platform SDK and search the
C/C++ headers ( in psdk\include\ ) for the interface definition.
For completeness sake, here my examples again:
Example 1 - Create Shell-Links:
Code: Select all
Procedure CreateLink(PATH$, LINK$, Argument$, DESCRIPTION$, WorkingDirectory$, ShowCommand.l, HotKey.l, IconFile$, IconIndexInFile.l)
;
; IShellLInk CONSTANTS for CallCOM
;
#ShellLink_QueryInterface = 0
#ShellLink_AddRef = 4
#ShellLink_Release = 8
#ShellLink_GetPath = 12
#ShellLink_GetIDList = 16
#ShellLink_SetIDList = 20
#ShellLink_GetDescription = 24
#ShellLink_SetDescription = 28
#ShellLink_GetWorkingDirectory = 32
#ShellLink_SetWorkingDirectory = 36
#ShellLink_GetArguments = 40
#ShellLink_SetArguments = 44
#ShellLink_GetHotkey = 48
#ShellLink_SetHotkey = 52
#ShellLink_GetShowCmd = 56
#ShellLink_SetShowCmd = 60
#ShellLink_GetIconLocation = 64
#ShellLink_SetIconLocation = 68
#ShellLink_SetRelativePath = 72
#ShellLink_Resolve = 76
#ShellLink_SetPath = 80
;
; Interface IPersistFile : IPersist
;
#PersistFile_QueryInterface = 0
#PersistFile_AddRef = 4
#PersistFile_Release = 8
#PersistFile_IsDirty = 16
#PersistFile_Load = 20
#PersistFile_Save = 24
#PersistFile_SaveCompleted = 28
#PersistFile_GetCurFile = 32
result = 0
CoInitialize_(0)
If CoCreateInstance_(?CLSID_ShellLink,0,1,?IID_IShellLink,@psl) = 0
Set_ShellLink_preferences:
; The file TO which is linked ( = target for the Link )
;
CallCOM(#ShellLink_SetPath, psl, PATH$)
; Arguments for the Target
;
CallCOM(#ShellLink_SetArguments, psl, Argument$)
; Working Directory
;
CallCOM(#ShellLink_SetWorkingDirectory, psl, WorkingDirectory$)
; Description ( also used as Tooltip for the Link )
;
CallCOM(#ShellLink_SetDescription, psl, DESCRIPTION$)
; Show command:
; SW_SHOWNORMAL = Default
; SW_SHOWMAXIMIZED = aehmm... Maximized
; SW_SHOWMINIMIZED = play Unreal Tournament
CallCOM(#ShellLink_SetShowCmd, psl, ShowCommand)
; Hotkey:
; The virtual key code is in the low-order byte,
; and the modifier flags are in the high-order byte.
; The modifier flags can be a combination of the following values:
;
; HOTKEYF_ALT = ALT key
; HOTKEYF_CONTROL = CTRL key
; HOTKEYF_EXT = Extended key
; HOTKEYF_SHIFT = SHIFT key
;
CallCOM(#ShellLink_SetHotkey, psl, HotKey)
; Set Icon for the Link:
; There can be more than 1 icons in an icon resource file,
; so you have to specify the index.
;
CallCOM(#ShellLink_SetIconLocation, psl, IconFile$, IconIndexInFile)
ShellLink_SAVE:
; Query IShellLink For the IPersistFile interface For saving the
; shortcut in persistent storage.
If CallCOM(#ShellLink_QueryInterface,psl,?IID_IPersistFile, @ppf) = 0
; Ensure that the string is Unicode.
mem.s = Space(1000) ;AllocateMemory(1,1000)
MultiByteToWideChar_(#CP_ACP, 0, LINK$, -1, mem, 1000)
;Save the link by calling IPersistFile::Save.
;hres = ppf->Save(wsz, TRUE);
hres = CallCOM(#PersistFile_SAVE,ppf,mem,#TRUE)
result = 1
CallCOM(#PersistFile_Release,ppf)
;FreeMemory_()
EndIf
CallCOM(#ShellLink_Release,psl)
EndIf
CoUninitialize_()
ProcedureReturn result
DataSection
CLSID_ShellLink:
; 00021401-0000-0000-C000-000000000046
Data.l $00021401
Data.w $0000,$0000
Data.b $C0,$00,$00,$00,$00,$00,$00,$46
IID_IShellLink:
; DEFINE_SHLGUID(IID_IShellLinkA, 0x000214EEL, 0, 0);
; C000-000000000046
Data.l $000214EE
Data.w $0000,$0000
Data.b $C0,$00,$00,$00,$00,$00,$00,$46
IID_IPersistFile:
; 0000010b-0000-0000-C000-000000000046
Data.l $0000010b
Data.w $0000,$0000
Data.b $C0,$00,$00,$00,$00,$00,$00,$46
EndDataSection
EndProcedure
; CreateLink
; - TARGET$ for the Link ("c:\PureBasic\purebasic.exe")
; - LINK$ - name of the Link ("c:\pb.lnk")
; - Argument$ for the target ("%1")
; - Description$ = Description and Tooltip ("Start PureBasic")
; - Working Directory ("c:\PureBasic\")
; - Show command: #SW_SHOWNORMAL or #SW_SHOWMAXIMIZED or #SW_SHOWMINIMIZED
; - HotKey - no need to use this :)
; - IconFile + Index ( "c:\PureBasic\purebasic.exe" , 1 )
If CreateLink("D:\BASIC\PureBasic\purebasic.exe","c:\PB.lnk","","Pure FUN","D:\BASIC\PureBasic\",#SW_SHOWMAXIMIZED,0,"%SystemRoot%\system32\SHELL32.dll",12)
beep_(800,100)
EndIf
WinDir$ = Space(100): GetSystemDirectory_(WinDir$,100)
If CreateLink(WinDir$+"\calc.exe","c:\CALC.lnk","","Calculator","",0,0,"%SystemRoot%\system32\SHELL32.dll",23)
beep_(800,100)
EndIf
Example 2 - Hide Window from Taskbar:
Code: Select all
;
; ITaskBarList CONSTANTS for CallCOM
;
#QueryInterface = 0
#AddRef = 4
#Release = 8
#HrInit = 12
#AddTab = 16
#DeleteTab = 20
#ActivateTab = 24
#SetActiveAlt = 28
;
; Open a Window
;
hWnd = OpenWindow(0,10,10,500,500,#PB_Window_SystemMenu,"Hello World")
;
; Create the OBJECT by using the GUIDs
;
CoInitialize_(0)
If CoCreateInstance_(?CLSID_TaskbarList,0,1,?IID_ITaskbarList,@myObject) = 0
;
; Call 2 Methods in the Object to hide the
; TAB for our window from the Taskbar
;
CallCOM(#HrInit,myObject) ; Init the Object
CallCOM(#DeleteTab,myObject,hWnd) ; Delete TAB for hWnd from Taskbar
;
; Our Window-Message loop: do nothing
;
Repeat
Until WaitWindowEvent() = #PB_EventCloseWindow
EndIf
;
; Release our Object (free memory for it)
;
CallCOM(#RELEASE,myObject)
;
; Thats it !! Have a nice day.... ;)
;
End
; For COM Programming, we need
; the GUID, that identify the CLASS and the INTERFACE
; we request from windows with CoCreateInstance_()
DataSection
CLSID_TaskbarList:
Data.l $56FDF344
Data.w $FD6D,$11D0
Data.b $95,$8A,$00,$60,$97,$C9,$A0,$90
IID_ITaskbarList:
Data.l $56fdf342
Data.w $FD6D,$11D0
Data.b $95,$8A,$00,$60,$97,$C9,$A0,$90
EndDataSection
Direct Examples:
Here some examples how to call/use DirectX directly:
DOWNLOAD PureDX
CallCOM() is the same as CallDX() internally.
Whatever you want to call.... ATL,ActiveX,OLE,DirectX,whatever..
(all together = COM, Component Object Model) -
it works all the same way internally.
Please... somebody tell me he understood my class-code... please...
