Tutorial on Accessing COM objects in ASM link

Just starting out? Need help? Post your questions and find answers here.
ricardo
Addict
Addict
Posts: 2438
Joined: Fri Apr 25, 2003 7:06 pm
Location: Argentina

Tutorial on Accessing COM objects in ASM link

Post by ricardo »

Hi

I dont know nothing of ASM but for anyone who knows it it could be interesant to see this info, its a Tutorial on Accessing COM objects in ASM :

http://com4me.net/win32asm/tutes/genera ... &idTute=38
ARGENTINA WORLD CHAMPION
^OO^
User
User
Posts: 27
Joined: Mon Apr 28, 2003 5:58 am
Location: United Kingdom
Contact:

Post by ^OO^ »

Thanks, Ricardo

I will give this a good squint.
I might learn something from the text if not from the ASM.

8)
Justin
Addict
Addict
Posts: 948
Joined: Sat Apr 26, 2003 2:49 pm

Post by Justin »

Very good.

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.
User avatar
Danilo
Addict
Addict
Posts: 3036
Joined: Sat Apr 26, 2003 8:26 am
Location: Planet Earth

Post by Danilo »

> 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.. :wink:

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... :D

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... :D :wink:
cya,
...Danilo
...:-=< http://codedan.net/work >=-:...
-= FaceBook.com/DaniloKrahn =-
traumatic
PureBasic Expert
PureBasic Expert
Posts: 1661
Joined: Sun Apr 27, 2003 4:41 pm
Location: Germany
Contact:

Post by traumatic »

Danilo wrote:Please... somebody tell me he understood my class-code... please... :D :wink:
heh :)
Good programmers don't comment their code. It was hard to write, should be hard to read.
Justin
Addict
Addict
Posts: 948
Joined: Sat Apr 26, 2003 2:49 pm

Post by Justin »

Wow ! 8O

Thanks for sharing, that's an excellent tutorial, i'll study it.

Good you are still around :D
ricardo
Addict
Addict
Posts: 2438
Joined: Fri Apr 25, 2003 7:06 pm
Location: Argentina

Post by ricardo »

z x
Danilo wrote: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.
Yes and the Datasection... for me THATS (constants and Datasection) is the hardest thing here.

And about the MS Platafform SDK... one question, i suppouse that contains the data about Microsoft controls, but what if i want to call a unknown activex?

A VERY NICE OPTION is the MS Ole Viewer (free!! and small to download!!) to get the same info of the SDK files

The Ole Viewer let you see very usefull info too... you can download it from http://www.microsoft.com/com/resources/oleview.asp its only 206 k

i run Ole Viewer and click the "View TypeLib" button, choose an OCX and wooow many many numbers are there!! Its beautifull i can see even the structures!!!

There are the GUID:

[
uuid(57B18084-FCCF-4D2C-968B-43A345DC13B0),
version(1.0),
helpstring("NCTAudioFile 1.0 Type Library"),
custom(DE77BA64-517C-11D1-A2DA-0000F8773CE9, 84082968),
custom(DE77BA63-517C-11D1-A2DA-0000F8773CE9, 1007803369)

]

How make the Datasection from this Danilo? :lol::lol::lol:

I think i can see the constants:
[id(0x00000002), helpstring("method CloseFile")]

Maybe using this tool (Ole Viewer) you can explain us Danilo how to make the datasection :D or even make a small shreware tool that help us :twisted:
ARGENTINA WORLD CHAMPION
ricardo
Addict
Addict
Posts: 2438
Joined: Fri Apr 25, 2003 7:06 pm
Location: Argentina

Post by ricardo »

Danilo wrote:Please... somebody tell me he understood my class-code... please... :D :wink:
I do, but have some questions... If i follow your example... i understand everything (comparing to a vbs code) but at this point i dont kn ow where or how you get that this need to be done:

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
If i compare to a vbs or vb code that does the same, all the first steps are almost the same, but here i find it different.


Lets see:

Code: Select all

Set oFSO = CreateObject("Scripting.FileSystemObject")
     Set oWshShell = WScript.CreateObject("WScript.Shell")
     
     Set oWshLink = oWshShell.CreateShortcut(sDesktop & "" & sLinkName & ".lnk")
     
     sCurDir = oFSO.GetAbsolutePathName(".")
     oWshLink.TargetPath = sCurDir
     oWshLink.WorkingDirectory = sCurDir
     oWshLink.Save
The Save is my problem to understand how did you get that have to do all this stuff and dont not just call the 'SAVE' method as they do in the example (you call it but do another things...). I hope i make myself clear.

--------------------------------------------------------------------------------------

Of course i dont know either how to get this:
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
ARGENTINA WORLD CHAMPION
Justin
Addict
Addict
Posts: 948
Joined: Sat Apr 26, 2003 2:49 pm

Post by Justin »

Danilo, there's something i don't understand,

if as you say the object is a pointer to a pointer to a list of function pointers(methods), why when i apply that logic to get the method address, using peekl() and using callfunctionfast() to call the method does not work?

in other words, it should be possible to use callfunctionfast() to call a method?
Justin
Addict
Addict
Posts: 948
Joined: Sat Apr 26, 2003 2:49 pm

Post by Justin »

i think i figured it out, to get the address of the method, in the link ex:

CoCreateInstance_(?CLSID_ShellLink,0,1,?IID_IShellLink,@psl)

avt=peekl(psl)
asp=peekl(avt+#ShellLink_SetPath) ;address of method
callfunctionfast(asp,psl,@PATH$)

i created a link using callfunctiofast()
Justin
Addict
Addict
Posts: 948
Joined: Sat Apr 26, 2003 2:49 pm

Post by Justin »

Is the MS SDK redistributable? , could someone share the includes in C/C++ headers ( in psdk\include\ ) ? , downloading all the sdk just for that is a bit heavy. it would be very appreciated. the com lib is almost unuseful without that info.
ricardo
Addict
Addict
Posts: 2438
Joined: Fri Apr 25, 2003 7:06 pm
Location: Argentina

Post by ricardo »

Justin wrote:Is the MS SDK redistributable? , could someone share the includes in C/C++ headers ( in psdk\include\ ) ? , downloading all the sdk just for that is a bit heavy. it would be very appreciated. the com lib is almost unuseful without that info.
I think its not possible, since its not redistributable :(
ARGENTINA WORLD CHAMPION
Justin
Addict
Addict
Posts: 948
Joined: Sat Apr 26, 2003 2:49 pm

Post by Justin »

Now i see that the sdk is not supported on Windows 95, Windows 98, or Windows Millennium Edition, very cool from MS..

i have W98 is there any chance from me to access that include folder? , could someone zip it and upload it somewhere? , i don't think it can hurt anyone.
Justin
Addict
Addict
Posts: 948
Joined: Sat Apr 26, 2003 2:49 pm

Post by Justin »

never mind, i think all that includes are in the lcc compiler.
El_Choni
TailBite Expert
TailBite Expert
Posts: 1007
Joined: Fri Apr 25, 2003 6:09 pm
Location: Spain

Post by El_Choni »

I have Win 98 and the last SDK installed. I only use the help, which is all I need. It also includes some headers not present in lcc, but not the important ones.
El_Choni
Post Reply