:Start of DirectIfaceCall DirectIfaceCall.dsc
DragonSphereSoftware

DragonSphere Software Demos


If you are viewing this script in your browser you can save it as a VDS 5.x source file (*.dsc) to run it.




#
# DragonSphere Software's Home Page
#  
# All Source Files for this demo
Title Direct Object Access Example
################################################ DESCRIPTION #################################################
#                                                                                                            #
#  This is a very advanced example                                                                           #
#  The technics demostrated in this example should only be used by an experienced COM developer              #
#                                                                                                            # 
#  This example demostrates the use of                                                                       #
#  @Ole(CreateEx) and @CallFuncPtr() functions to access COM object interfaces directly.                     #
#                                                                                                            # 
#  How to start using COM or ActiveX interfaces directly?                                                    #
#  You start using them by first defining the Interface.                                                     #
#  Note: This has changed from my first version.  Interfaces are now unique high level data types in         #
#        GadgetX.  So Interfaces are now treated different from Structures or Records in the way that        #
#        GadgetX handles the memory allocation and de-allocation of interfaces.                              #
#  Basicly the Define Interface command allows GadgetX to know how to find the addresses of the methods      # 
#  in a COM or ActiveX object without the need of a TypeLibrary or IDispatch as in the OLE commands          #
#  and functions.  The OLE commands and functions are just wrappers around the IDispatch interface and       #
#  require the IDispatch interface in most cases for them to work.                                           #
#  The reasons for not using a TypeLibrary or the IDispatch interface is that not all objects have these     #
#  and they are not required.  As a matter of fact the only interface that is required is the IUnknown       #
#  interface and is defined in the example below.                                                            #
#  Warning:  When defining a Interface the order and number of method names are VERY important!!!            #
#            Use a tool like the GadgetX TLibInfo Browser or Microsoft's OleView to get the order and names  #
#            of the methods, properties, and parameter types.                                                #
#  Second you call the @Ole(CreateEx) function and pass the needed parameters for the Object you want to     #
#  use.  You can think of @Ole(CreateEx) as a fancy combined function in place of Gadget's @LoadDLL() and    #
#  @GetFuncPtr() specifically made for loading COM/ActiveX components/objects into memory instead of just    #
#  DLLs.                                                                                                     #
#  Now you can start calling methods like you would any Win32 API function with the @CallFuncPtr() function  #
#  passing the required parameters.                                                                          #
#  Note:  Since this is such a low level way of calling COM/ActiveX objects you must do reference counting.  #
#         The @CallFuncPtr() is a lov level multi-purpose function for calling generic function addresses    #
#         and will not know to automaticly call the AddRef and Release methods on an object.  Unlike the use #
#         of SmartPointers found in languages such as C++.                                                   #
#                                                                                                            #
#  Using the techniques described in this example allows you to use any COM or ActiveX interface as long     #
#  as you know the names of the methods (ie...Functions), calling convetion, number and usage of parameters  #
#  of the methods, and the return type of the method.  This makes calling these methods very quick and to    #
#  the point.  The overhead is closer to that of languages such as C/C++ for calling COM objects with the    #
#  exception that you are defining and using the objects interface at runtime instead of at compile time.    #
#  Note About Properties: Properties are usually a variable in the object and there is always a method       # 
#                        that returns or sets the properties value so you would place the name of the        #
#                        COM method that returns or sets the properties in the proper order.  Most likely    #    
#                        these methods have the words get_ and set_ prefixed in the method names that        #
#                        deal with that property as is the case for Internet Explorer's ActiveX interfaces.  #
#                                                                                                            #
##############################################################################################################

If @Greater(@Name(@SYSINFO(DSVER)),4)

  External GadgetX.dll,@SYSINFO(DSVER)

  # GadgetX Commands needed for this example
  #DEFINE COMMAND,GadgetX,DEFINE,OLE,INTERFACE,Set,FREEDLL,CALL,MEM
  # GadgetX Functions needed for this example
  #DEFINE FUNCTION,GadgetX,OLE,INTERFACE,Get,ADDROF,SIZEOF,GETLASTERROR,LOADDLL,CALL,OR,AND,GETFUNCPTR,CALLFUNCPTR,MEM
Else
  Warn This Example uses VDS 5 or above syntax.
End

# Enum CLSCTX
Define Constant,CLSCTX_INPROC_SERVER,$1
Define Constant,CLSCTX_INPROC_HANDLER,$2
Define Constant,CLSCTX_LOCAL_SERVER,$4
Define Constant,CLSCTX_INPROC_SERVER16,$8
Define Constant,CLSCTX_REMOTE_SERVER,$10
Define Constant,CLSCTX_INPROC_HANDLER16,$20
Define Constant,CLSCTX_RESERVED1,$40
Define Constant,CLSCTX_RESERVED2,$80
Define Constant,CLSCTX_RESERVED3,$100
Define Constant,CLSCTX_RESERVED4,$200
Define Constant,CLSCTX_NO_CODE_DOWNLOAD,$400
Define Constant,CLSCTX_RESERVED5,$800
Define Constant,CLSCTX_NO_CUSTOM_MARSHAL,$1000
Define Constant,CLSCTX_ENABLE_CODE_DOWNLOAD,$2000
Define Constant,CLSCTX_NO_FAILURE_LOG,$4000
Define Constant,CLSCTX_DISABLE_AAA,$8000
Define Constant,CLSCTX_ENABLE_AAA,$10000
Define Constant,CLSCTX_FROM_DEFAULT_CONTEXT,$20000
Define Constant,CLSCTX_ACTIVATE_32_BIT_SERVER,$40000
Define Constant,CLSCTX_ACTIVATE_64_BIT_SERVER,$80000


# default LCID's which most COM interfaces rely on to display and read variables
# in their proper languages.
Define Constant,LOCALE_SYSTEM_DEFAULT,$800
Define Constant,LOCALE_USER_DEFAULT,$400

# Usually if the method works it will return the HRESULT $00000000 or a error value
Define Constant,S_OK,$00000000

# GUID = UUID = IID = CLSID = Interface ID's
# All these names have the same meaning to GadgetX so treat them the same.
# GadgetX defines these as a 16 byte structures defined as follows
# Define Structure,GUID,DWORD|Data1,WORD|Data2,WORD|Data3,BYTE|Data4[8]
# Lucky for all of us I have built a wrapper for this.
# Most Methods and functions need a pointer to the GUID
# So if you use @AddrOf() you can get the pointer address.
# COM Interface ID's can be found in the following Registry keys.
# HKEY_CLASSES_ROOT\CLSID
# HKEY_CLASSES_ROOT\Interface
Define IID,IID_NULL,{00000000-0000-0000-0000-000000000000}
Define IID,IID_IUnknown,{00000000-0000-0000-C000-000000000046}
Define IID,IID_IDispatch,{00020400-0000-0000-C000-000000000046}

# Pointer variable we will use to hold the Address of a IID
Define Variable,Pointer,piid

#
# Syntax: Define Interface,,,<...>
#
# Example:
Define Interface,IUnknown,QueryInterface,AddRef,Release
# NOTE: All Interfaces require the IUnknown interface as their first 3 methods.
#       I may change GadgetX later to automaticly detect if you defined these or not.
Define Interface,IDispatch,QueryInterface,AddRef,Release
   Interface Add,IDispatch,GetTypeInfoCount,GetTypeInfo,GetIDsOfNames,Invoke

# The astric '*' in front of the Pointer variable type tells the @CallFuncPtr function
# to assign the value of the returned argument to the address of the argument.  A pointer variable without
# the astric '*' will assign the address of the returned argument to the value of the returned argument.
# This variable type will not work with any other GadgetX command or function.
# This is only needed when passing a pointer to a pointer of a interface as 
# in the QueryInterface method.  
# Note: You must use a GadgetX variable for this to work since GadgetX treats Interfaces as special
Define Variable,*Pointer,punk
Define Variable,*Pointer,disp

# Worker variables to hold various data
# bstrMember will hold the memory address of a BSTR/OLESTR
Define Variable,Pointer,bstrMember
# pbstrMember will hold the memory address of bstrMember
Define Variable,Pointer,pbstrMember
# dispid will hold the value of the Dispatch ID
Define Variable,Long,dispid
# pdispid will hold the memory address of dispid
Define Variable,Pointer,pdispid

# Ok we are done with all the definitions now lets have some fun ;)

# Always remember to Initialize Ole before you try to use it.
Ole Init

# save the Address of IID_IDispatch Interface ID to the piid pointer
set piid,@AddrOf(IID_IDispatch)

# Use the CreateEx wrapper function to create a new instance of a COM Interface
# and get a pointer to that COM interface. This wrapper function is not needed but
# you have to agree it is much easier than using all the functions and methods that would be required
# Syntax: 
# = @Ole(CreateEx,,,,,,)
Set disp,@Ole(CreateEx,InternetExplorer.Application,IID_IDispatch,CLSCTX_LOCAL_SERVER,NULL,NULL,IDispatch)
# Now we can use the interface and call any method by name in this case we are requesting the IDispatch interface
# With the advent of the 
# REMEMBER: Normally you would need to pass the interface pointer as the first argument of each method called.
#           Most languages hide the interface pointer like the C++ pre-compilier and it's use of the thiscall.
#           Since this is a common practice and it looks better I made this possible with the @CallFuncPtr function.
#           The first version of this function could not do this.  There is some differences between using the @CallFuncPtr
#           and other languages.  Bellow is an example of the differences especially when
#           calling the QueryInterface method.
# Note: There is really no need to call the AddRef method after @Ole(CreateEx) 
#       is called since @Ole(CreateEx) does this for you.  In most cases as long as you call the Release method
#       for every AddRef method that is called most objects will allow you to do this.
%%RefCount = @CallFuncPtr(STANDARDMETHOD,DWORD,IDispatch.AddRef)
Info Reference count = %%RefCount
# Initialize the dispid to a value
Set dispid,0
# Save the address of dispid to the pdispid pointer
Set pdispid,@AddrOf(dispid)

# Set piid to the Address of IID_NULL
set piid,@AddrOf(IID_NULL)

# This can be any valid Method name that is part of this interface
# Most IDispatch interfaces are what is called a aggregate of the IDispatch interface
# and some other interface.  This means that the object appends another interface to the end
# of the IDispatch interface and treats it as a new unique interface.  Like I said before you
# do not need to use the IDispatch interface at all if you know the names of all the methods.
# See Also: GadgetX TLibInfo Browser to get the names of the methods and properties.
# Note About Properties: Properties are usually a variable in the object and there is always a method
#                        that returns or sets the properties value.
%%MethodName = Visible

# Save a method's name as a BSTR or OLESTR and assign the Address to the bstrMember pointer
Set bstrMember,@Ole(VdsToBstr,%%MethodName)
Set pbstrMember,@AddrOf(bstrMember)
# We have everything we need to call the GetIDsOfNames Method
# Notice that we are returning a HRESULT
# A HRESULT is just a Long variable type represented as a 
# Hex value base 16 instead of a decimal value base 10.
# I don't know why the VDS function @Hex function does not append a $ token character
# in front of the number but it does not so to use Hex numbers with other
# VDS math function I gave GadgetX the ability to return the Long type as a
# hex number.  GadgetX fully reconizes the token $ character in all numeric
# cases and will convert the numbers as nessassary.
%%HResult = @CallFuncPtr(STANDARDMETHOD,HRESULT,IDispatch.GetIDsOfNames,piid,pbstrMember,Integer|1,DWORD|@Get(LOCALE_SYSTEM_DEFAULT),pdispid)
If @Equal(%%HResult,@Get(S_OK))
  # If everything went well we should see the DISPID 402 here.
  Info The Method @Ole(BstrToVds,bstrMember) has a DispID of @Get(dispid)
Else
  # If something went wrong show the error code with will be in %%HResult as a
  # hex number.  You can look up these error codes in the objects documentation.
  # If this is a Mircorsoft Object you should be able to get the errorcode meaning
  # at MSDN
  Warn Method returned errorcode %%HResult
End

# Get the Address of the IUnknown Interface ID
Set piid,@AddrOf(IID_IUnknown)

# Setup the Pointer to Pointer variable
# This is really just to give punk a value of 
# the correct size and shape.  The IUnknown interface
# does not need to be initialized but the punk variable requires
# initialization.
# The name punk is shorthand for "pointer to IUnknown"
# GadgetX does not allocate any memory for a variable until you
# use that variable with the exception of Structures, Records, and Interfaces.
# Assign the Address of the IUnknown Interface to the punk variable to give punk
# the proper meaning.
Set punk,@AddrOf(IUnknown)
# Call the IDispatch QueryInterface method to get a pointer to the IUnknown interface
# Notice that we are returning a HRESULT
# A HRESULT is just a Long variable type represented as a Hex value.
%%HResult = @CallFuncPtr(STANDARDMETHOD,HRESULT,IDispatch.QueryInterface,piid,punk)
If @Equal(%%HResult,@Get(S_OK))
  # If everything is ok we can now get the Interface that is held in the 
  # punk Pointer to Pointer variable
  # We will now save the Interface back into the IUnknown structure
  # so we can call the Interface Methods by name instead of using the 
  # memory addresses and offsets which is a real pain but it can be done
  # but why bother let GadgetX do all these calculations for you.
  Set IUnknown,@Get(punk)
  
  # Now we can call the AddRef method and any other method in this interface just
  # like any Win32 API function.
  %%RefCount = @CallFuncPtr(STANDARDMETHOD,DWORD,IUnknown.AddRef)
  Info Reference count = %%RefCount
  
  # Next we have examples of how to use some of the Gadgetx OLE commands/functions with
  # these interfaces.
  
  # Get the TypeInfo for the Object
  # This GadgetX function can use any interface since it only
  # requires an initial IUnknown interface to start with.
  # NOTE: All Interfaces include the IUnknown interface as the first 3 methods but not
  #       all interfaces have TypeInfo associated with them.
  %%TypeInfo = @Ole(GetControl,TypeInfo,IUnknown)
  # Display the Objects TypeInfo if available.
  If %%TypeInfo
    Info %%TypeInfo@CR()
  Else
    Warn This object does not have any Type Information associated with it.
  End
  # Clean the %%TypeInfo variable since it uses alot of memory.
  %%TypeInfo =
  # We are finished using IUnknown so Release the reference to it.
  %%RefCount = @CallFuncPtr(STANDARDMETHOD,DWORD,IUnknown.Release)
  Info Reference count = %%RefCount
Else
  # If something went wrong show the error code with will be in %%HResult as a
  # hex number.  You can look up these error codes in the objects documentation.
  # If this is a Mircorsoft Object you should be able to get the errorcode meaning
  # at MSDN
  Warn Method returned errorcode %%HResult
End


# Since IDispatch now points to a Dispatch interface 
# we can use any of GadgetX's Ole wrapper commands and functions.
Ole Set,IDispatch,"Visible = ^B",True

Ole Call,IDispatch,"Navigate(^B)",http://www.dragonsphere.net
wait
Info Now VDS has the best of both worlds.@CR()We can use the Simple IDispatch Ole wrappers@CR()or any other COM interface with a little work.

# Start the clean up process
# The Internet Explorer Application object requires that you call it's Quit method
# before closing.
Ole Call,IDispatch,Quit

# Release all references to the Object.
While @Greater(%%RefCount,0)
  # Release while the Reference count is greater than 0
  %%RefCount = @CallFuncPtr(STANDARDMETHOD,DWORD,IUnknown.Release)
  Info Reference count = %%RefCount@CR()
Wend

# UnInitialize Ole
Ole UnInit

Exit
:End of DirectIfaceCall