COMatePLUS version 1.2

Developed or developing a new product in PureBasic? Tell the world about it.
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

COMatePLUS version 1.2

Post by srod »

Purebasic 5.42.

COMatePLUS has been modified to work with PB 5.42. Download: http://www.rsbasic.de/backupprogramme/COMatePLUS.zip or https://backups.rsbasic.de/COMatePLUS.zip


Update - 9th July 2010.

Version 1.2 (Purebasic 4.5 edition).

A bug in COMatePLUS' error reporting has been fixed in the Purebasic 4.5 edition of COMatePLUS. Other editions were not affected by this bug because I inadvertently created COMatePLUS for PB 4.5 from an out of date version of COMatePLUS... doh!!! How the hell I did that is beyond me? :)

See the nxSoftware site for the download.

===============================


Update - 5th June 2009.

Version 1.1.

COMatePLUS version 1.1 seems to have passed the x64 test (though I still keep an open mind on that score!) :wink: Therefore, COMatePLUS 1.1 is for both PB x86 and PB x64.

I have also upgraded the error reporting to take into account the FACILITY_WIN32 HRESULT values. In the case of such errors being reported, and when COMatePLUS does not recognise the error in question, COMatePLUS will query the system for a suitable Win32 error description etc.

See the nxSoftware site for the download.

===============================


Update - 4th May 2009.

Version 1.0.4.

COMatePLUS version 1.0.4 makes a slight alteration to event handlers in that a global handler (\SetEventHandler(#COMate_CatchAllEvents,...)) can now return values through the #COMate_OtherReturn flag. That is, you can specify that your global handler be geared up for returning values through a variant structure which is passed to your handler etc.

This was motivated by a new web demo which essentially shows how to use COMatePLUS to validate HTML form data without any Javascript etc. This demo will be posted in the tips-and-tricks section as well.

See the nxSoftware site for the download.

===============================


Update - 30th April 2009.

Version 1.0.3.

COMatePLUS version 1.0.3 adds a new 'helper' function :

Code: Select all

COMate_GetIIDFromName()
which takes a 'friendly name' of a COM interface and, if successful, places the 128-bit IID of the interface into a specified buffer.

I added this to assist with the SetEventHandler() method of the COMateObject class which, for more advanced uses, takes an optional IID etc.

NOTE that this function cannot be used to obtain a CLSID from a COM component's friendly name. COMatePLUS already does that automatically.

Tested on Vista only.

Examples of friendly names for COM interfaces :
  • iUnknown
  • iDispatch
  • DWebBrowserEvents2
  • ... etc.
===============================


Update - 29th April 2009.

Version 1.0.2.

COMatePLUS version 1.0.2 adds a facility for specifying an 'out-going interface' when setting up an event handler for some object or other.

This is essentially for 'advanced users' only, but, basically, COMatePLUS event handlers are based upon an 'out-going' interface which is 'connected' to the underlying COM object through what is called a 'connection point'. Now, many components support different 'out-going' interfaces offering a different range of events which we can tap into and thus we can, if appropriate, direct COMatePLUS to use a specific out-going interface (and thus effectively choose a set of events which we might wish to consider).

To direct COMatePLUS to use a specific out-going interface, simply pass the address of a buffer holding the IID of the relevant interface to the SetEventHandler() method of the COMateObject class (through a newly added optional parameter).

Otherwise, COMatePLUS will simply use the first 'suitable' out-going interface which it encounters (which was the case with all previous versions of COMate) !

There is a new demo (thanks hm) to show this in action with a web-gadget.

The manual has also been updated, in particular limitations of COMatePLUS' event handling are discussed.

===============================


Hi,

COMatePLUS is a complete replacement for COMate for Purebasic 4.3, so much so that I will no longer support the earlier versions of COMate in any way, shape of form! Got to draw the line somewhere! I have asked for the original COMate thread to be locked.

The original COMate thread is here.

First thing to say is that COMatePLUS is what COMate should have been in the first place! :wink:

Second thing to say is that all existing COMate programs (for Purebasic 4.3) should run without modification with COMatePLUS.

So what does COMatePLUS bring to the COM party then?

Let me quote from the user manual :
COMatePLUS represents quite a jump forward over earlier versions of COMate, both in terms of it's internal workings and the facilities offered to the developer when working with automation servers. These changes were motivated by a desire to allow much faster access to COM methods/properties than was currently available through COMate's existing command processor.

COMatePLUS can process 'strings' of 'closely related' method/property calls far far faster than earlier versions of COMate, although you do have to specifically make use of the new facilities in order to take advantage of any possible speed increases etc.


Earlier versions of COMate, when given a command string as part of a method/property call, would first undertake the sometimes lengthy process of tokenising and parsing the command string looking for method calls, parameter types, type modifiers ...... etc. and only when this process was complete would it consider executing the COM method etc. When the COM method finished its work, the results of all that tokenising and parsing would be discarded, meaning that if the same method/property needed to be called again (perhaps with modified parameters) then the processing of the command string would need to be undertaken again!

A somewhat wasteful affair don't you think? :)


COMatePLUS takes a different approach, one inspired by SQLite's use of 'prepared statements' etc.

Basically, COMatePLUS allows you to pre-compile a command string right down to a level suitable for passing directly to a COM server. The result of this compilation is what I have termed a COMatePLUS statement which can be executed at any time and as many times (against different COMate objects if required) as required etc. Parameters can be altered between calls very quickly.

The important thing is that the original COMateObject class now uses these pre-compiled statements 'behind the scenes' and so all existing COMate based code should run fine with COMatePLUS with no modifications whatsoever. Of course, all existing code will not run any faster; only code specifically making use of these prepared statements can benefit from these changes.


A couple of newly added demo programs written to showcase COMatePLUS' prepared statements show a 10-fold increase in speed on my system, -something which is not to be sniffed at! :)

The following functions have been added by COMatePLUS for working with prepared statements (see the relevant sections of this manual for details) :

1. COMate_PrepareStatement()
2. COMate_GetStatementParameter()
3. COMate_FreeStatementHandle()

In addition, a new method has been added to the COMateObject class :

GetCOMObject()

which is, in a sense, the reverse of the COMate_WrapCOMObject() function.

All of the COMateObject class methods dealing with calling COM methods/properties have also been modified by the addition of an optional parameter in which we can pass a COMatePLUS statement handle etc. See the COMateObject class section of this manual for details.


Other alterations to the COMate library.

You can now also opt to remove all error reporting from your COMatePLUS application. Simply define the constant #COMATE_NOERRORREPORTING = 1 at the top of your source, before including the main COMatePLUS source file.
In a sense, if the original COMate is to be considered as an interpreter (programming), then COMatePLUS is to be considered a compiler! :wink:

Whilst you work your way through the modifications to COMate, you may wonder why I have opted to implement certain features in the way I have? For example, take a look at the mechanism through which you modify parameters within a compiled statement! Here, you are required to deal directly with COM variant structures.

All of these questions are addressed in the extensive modifications to the user manual and so I will not address them here. (All related to issues of speed and efficiency which is what these compiled statements are designed to enchance.)

Needless to say that I advise even the most experienced COMate users to read the section on modifying statement parameters - and I mean every word! :) There are some serious issues for you to consider and some potentially application wrecking pit-falls to avoid!

In terms of speed gains, as stated above, only those applications designed to explicitly use pre-compiled statements will see any benefit and then only if the application is such that a compiled statement requires executing many times, -usually whilst modifying parameters etc.

In my first test, I wrote a quick program to write 30000 cells to an Excel spreadsheet and found only moderate increases in speed etc. This is because Excel is slow at writing data and caches all write operations anyhow! The best way of quickly writing data to an Excel sheet is via a 2-dimensional safearray (thanks MrMat).

My second very contrived test involved the VBScript regular expression object to which I applied 1 million string replacements.
Without using pre-compiled statements the operation took around 65 seconds on my system to complete.
Using compiled statements the same program took less than 6 seconds! 'nuff said! :)


Please see the nxSoftware site for the download. You can still download the last version of the original COMate as well if you wish, though why you might do so is beyond me at the moment? :)

__________________________________________________
Download link added
16.02.2016
RSBasic
Last edited by srod on Tue Feb 16, 2016 2:55 pm, edited 14 times in total.
I may look like a mule, but I'm not a complete ass.
User avatar
graves
Enthusiast
Enthusiast
Posts: 160
Joined: Wed Oct 03, 2007 2:38 pm
Location: To the deal with a pepper

Post by graves »

SROD
Can I "prepare" statements in the form "Cells(1,1) = '" +value+"'" , where "value" is a PB variable with their value asigned at execution
time?

like this:

Code: Select all

value.s = ""
*hStatement = COMate_PrepareStatement("Cells(1,1) = '" +value+"'")
value = "first" 
ExcelObject\SetProperty("",*hStatement)
value = "other" 
ExcelObject\SetProperty("",*hStatement)
**EDITED (I've forgotten change the cell reference)

Code: Select all

value.s = ""
nrow=0
ncol=0
*hStatement = COMate_PrepareStatement("Cells("+str(nrow)+","+str(ncol)+") = '" +value+"'")
value = "first" 
nrow=1
ncol=1
ExcelObject\SetProperty("",*hStatement)
value = "other" 
ncol=2
ExcelObject\SetProperty("",*hStatement)
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Post by srod »

First, if that were possible, you would need to pass the address of the variables; not their values.

Second... it is not possible in that form. Using 'bound parameters' like that is something which I have been thinking about; maybe in a later version. See point 6 in the post below.

Third... there is no point using pre-compiled statements in an attempt to speed up writing to an array of cells in an Excel spreadsheet because Excel caches such writes. Pass a safearray to an Excel range object instead.

Forthly... it is all in the user manual! :wink:

Fifth.... here is a basic demo showing how to use a basic statement in a loop to write to 10000 cells in an Excel worksheet. As I have said previously, such statements actually make little difference where Excel is concerned because of the caching, but the code does show how to modify your statement parameters within the loop.

Code: Select all

XIncludeFile "COMatePLUS.pbi"

Define.COMateObject ExcelObject, WorkBook
ExcelObject = COMate_CreateObject("Excel.Application")


;Prepare our statement for writing to an individual cell. Our choice of cell is unimportant.
;What is important is that we use type-modifiers to set the parameter types.
;===========================================================================================
  hStatement = COMate_PrepareStatement("Cells(1 as long,1 as long) = 1 as long")
  If hStatement = 0
    MessageRequester("COMatePLUS!", "Couldn't create the statement!" + #CRLF$ + #CRLF$ + "COMatePLUS error : " + COMate_GetLastErrorDescription())
    End
  EndIf
;Retrieve pointers to the first two parameters so that we can alter them later.
;==============================================================================
  *row.VARIANT = COMate_GetStatementParameter(hStatement, 1)
  *col.VARIANT = COMate_GetStatementParameter(hStatement, 2)


If ExcelObject
  If ExcelObject\SetProperty("Visible = #True") = #S_OK
    WorkBook = ExcelObject\GetObjectProperty("Workbooks\Add")
    If WorkBook
      ;Write 10000 cells using our pre-compiled statement.
      ;We fill each of the 10000 cells with the value 1.
        For row = 1 To 100
          For col = 1 To 100
            ;Set parameters.
              *row\lVal = row
              *col\lVal = col
              ExcelObject\SetProperty("", hStatement)
          Next
        Next
      ExcelObject\Invoke("Quit()") 
      WorkBook\Release()
    EndIf
  EndIf
  ExcelObject\Release()
Else
  MessageRequester("COMate -Excel demo", "Couldn't create the application object!")
EndIf


;Free our statement.
;===================
  COMate_FreeStatementHandle(hStatement)
Note how when we set up our statement, the initial parameter values are totally unimportant really (unless you intend using the same values every time you execute the statement!) What is important (as explained in the user manual) is that you ensure that the statement parameters take the correct form. Here I used the 'as long' type-modifier. The reason this is important can be found in the manual! :wink:

Sixth... continued in the next post!
Last edited by srod on Fri Apr 24, 2009 9:42 pm, edited 3 times in total.
I may look like a mule, but I'm not a complete ass.
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Post by srod »

Sixth... :wink:

Using bound parameters is possible if you use the BYREF modifier!

Code: Select all

XIncludeFile "COMatePLUS.pbi"

Define.COMateObject ExcelObject, WorkBook
ExcelObject = COMate_CreateObject("Excel.Application")


;Prepare our statement for writing to an individual cell.
;What is important is that we use type-modifiers to set the parameter types.
;===========================================================================================
  hStatement = COMate_PrepareStatement("Cells(" + Str(@row) + " As long BYREF," + Str(@col) + " As long BYREF) = 1 As long")
  If hStatement = 0
    MessageRequester("COMatePLUS!", "Couldn't create the statement!" + #CRLF$ + #CRLF$ + "COMatePLUS error : " + COMate_GetLastErrorDescription())
    End
  EndIf

If ExcelObject
  If ExcelObject\SetProperty("Visible = #True") = #S_OK
    WorkBook = ExcelObject\GetObjectProperty("Workbooks\Add")
    If WorkBook
      ;Write 10000 cells using our pre-compiled statement.
      ;We fill each of the 10000 cells with the value 1.
        For row = 1 To 100
          For col = 1 To 100
              ExcelObject\SetProperty("", hStatement)
          Next
        Next
      ExcelObject\Invoke("Quit()") 
      WorkBook\Release()
    EndIf
  EndIf
  ExcelObject\Release()
Else
  MessageRequester("COMate -Excel demo", "Couldn't create the application object!")
EndIf


;Free our statement.
;===================
  COMate_FreeStatementHandle(hStatement)
Of course not all COM compoents will deal with BYREF parameters like this.
I may look like a mule, but I'm not a complete ass.
User avatar
Kiffi
Addict
Addict
Posts: 1353
Joined: Tue Mar 02, 2004 1:20 pm
Location: Amphibios 9

Re: COMatePLUS version 1.0.0

Post by Kiffi »

srod wrote:COMatePLUS
Great news! Thanks a lot, srod! :D

it seems that there is an issue with GetObjectProperty() in combination
with the Prepare-Statement:

Code: Select all

XML.s = "<this><that></that></this>"

Define DOMDocument.COMateObject
Define XmlNode1.COMateObject
Define XmlNode2.COMateObject

DOMDocument = COMate_CreateObject("MSXML.DOMDocument")

DOMDocument\Invoke("LoadXml('" + XML + "')")

; this works
XmlNode1 = DOMDocument\GetObjectProperty("SelectSingleNode('this')")
If XmlNode1
  Debug "Yeah: XmlNode1!"
  XmlNode1\Release()
Else
  Debug COMate_GetLastErrorDescription()
EndIf

; this doesn't work
hStatement = COMate_PrepareStatement("SelectSingleNode('this')") 
If hStatement
  
  XmlNode2 = DOMDocument\GetObjectProperty("", hStatement)
  If XmlNode2
    Debug "Yeah: XmlNode2!"
    XmlNode2\Release()
  Else
    Debug COMate_GetLastErrorDescription()
  EndIf
  
  COMate_FreeStatementHandle(hStatement) 
  
Else
  Debug COMate_GetLastErrorDescription()
EndIf
as you see, GetObjectProperty("SelectSingleNode(...") returns a valid
XmlNode. If i prepare the same statement, i get an COMate-Error:
Debugger wrote:One or more arguments are invalid. Possibly a numerical overflow or too
many nested objects, -if so, try splitting your method call into two or
more subcalls.
Thanks in advance & Greetings ... Kiffi
Hygge
User avatar
graves
Enthusiast
Enthusiast
Posts: 160
Joined: Wed Oct 03, 2007 2:38 pm
Location: To the deal with a pepper

Post by graves »

Sixth...

Using bound parameters is possible if you use the BYREF modifier!
Thnks srod. I know that my "prepare" does not work but I knew what you get.

I not know in SQLite, but in several other SQL-DB "prepares" are similar to "printf " in C, ie, with parameters (%1, %2) that enable their replacement each time, changing their values.

Perhaps this may be an idea to enhance COMatePLUS

(Sorry, this translation is from Google, I hope you understand it)
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Post by srod »

I not know in SQLite, but in several other SQL-DB "prepares" are similar to "printf " in C, ie, with parameters (%1, %2) that enable their replacement each time, changing their values.
That is the DispHelper way of doing things - although DH does not use any kind of prepared statements.

COMatePLUS will not be going down that path because I do not like that kind of syntax.

With the BYREF modifier you effectively have bound parameters (as you can see with the second Excel demo above). Most components will recognise such parameters and retrieve the correct values accordingly.


@Kiffi : I shall take a look right now.
Last edited by srod on Sat Apr 25, 2009 12:27 pm, edited 2 times in total.
I may look like a mule, but I'm not a complete ass.
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Post by srod »

Kiffi you lunatic - read the manual!!! :wink:

The GetObjectProperty() method actually has two optional parameters now (with earlier versions of COMate it had just one optional parameter!)
object.i = myCOMateObject\GetObjectProperty(command.s [, objectType [, hStatement]])
Adjust your code by using the following line :

Code: Select all

XmlNode2 = DOMDocument\GetObjectProperty("", #VT_DISPATCH, hStatement) 
:)
I may look like a mule, but I'm not a complete ass.
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Post by srod »

Kiffi : I have just uploaded a new version which switches the order of the optional parameters with the GetObjectProperty() method. I think it makes more sense this way.

You can now use your original XML code.

Download version 1.0.1.

All users.
Please make sure you upgrade to version 1.0.1.
I may look like a mule, but I'm not a complete ass.
User avatar
flaith
Enthusiast
Enthusiast
Posts: 704
Joined: Mon Apr 25, 2005 9:28 pm
Location: $300:20 58 FC 60 - Rennes
Contact:

Post by flaith »

That's great srod, i just hope to find examples for OOo (cause i do not have office installed) :wink:
“Fear is a reaction. Courage is a decision.” - WC
akj
Enthusiast
Enthusiast
Posts: 665
Joined: Mon Jun 09, 2003 10:08 pm
Location: Nottingham

Post by akj »

I have found a few minor errors in the file COMatePLUS.chm

N.B.
Positive line numbers refer to text BELOW the heading at the very top of the page.

Negative line numbers refer to text ABOVE the horizontal rule that precedes the explanation of HRESULT values.

Code: Select all

Page:	COM overview
Line:	12
For :	visa-versa
Read:	vice-versa


Page:	GetObject()
Line:	-5
For :	load a COM object form
Read:	load a COM object from


Page:	GetObjectProperty()
Line:	Three different lines
For :	iUnkown
Read:	iUnknown


Pages:	GetDateProperty() .. GetRealProperty()
Pages:	GetIntegerEventParam() .. GetRealEventParam() 
Line:	-1
For :	what it might represent?
Read:	what it might represent.


Page:	SetEventHandler()
Line:	NOTES: 1
For :	1.  not all COM objects raise events
Read:	1.  Not all COM objects raise events


Page:	SetEventHandler()
	The following two sets of lines are contradictory.
	Is an integer returned or is a quad returned?
Lines:	#COMate_IntegerReturn  
	Returns a signed integer.
Lines:	#COMate_IntegerReturn  
	Procedure.q myEventCallback(object.COMateObject, eventName.s, parameterCount)
	Protected result.q


Page:	GetObjectEventParam()
Line:	Three different lines
For :	iUnkown
Read:	iUnknown
Anthony Jordan
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Post by srod »

Hi,

thanks for the report, but, well, I probably will not make any adjustments. It is a freely available product and I don't think any of this really detracts from use of COMatePLUS. At least the typos do not! It's a question of time really.

You are of course correct about vice-versa.

As for the integer / quad contradiction... well yes and no I guess. COMate will return a signed quad but you can use any integer type as appropriate for the application in hand.

Thanks again.
Last edited by srod on Sat Apr 25, 2009 8:35 pm, edited 1 time in total.
I may look like a mule, but I'm not a complete ass.
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Post by srod »

flaith wrote:That's great srod, i just hope to find examples for OOo (cause i do not have office installed) :wink:
I think a couple of people posted OO examples in the original COMate thread.
I may look like a mule, but I'm not a complete ass.
User avatar
flaith
Enthusiast
Enthusiast
Posts: 704
Joined: Mon Apr 25, 2005 9:28 pm
Location: $300:20 58 FC 60 - Rennes
Contact:

Post by flaith »

srod wrote:
flaith wrote:That's great srod, i just hope to find examples for OOo (cause i do not have office installed) :wink:
I think a couple of people posted OO examples in the original COMate thread.
Yes, i've seen, and i tried to make another one :

Code: Select all

;/////////////////////////////////////////////////////////////////////////////////
;***COMatePLUS***  COM automation through iDispatch.
;*===============
;*
;*OOo demo.
;/////////////////////////////////////////////////////////////////////////////////

IncludePath ".."
XIncludeFile "COMatePLUS.pbi"
XIncludeFile "VariantHelper_Include.pb"

Procedure.s ConvertToUrl(strFile.s)
  strFile = ReplaceString(strFile, "", "/")
  strFile = ReplaceString(strFile, ":", "|")
  strFile = ReplaceString(strFile, " ", "%20")
  strFile = "file:///" + strFile
  ProcedureReturn strFile
EndProcedure

Define.COMateObject oSM, oDesk, oCalc, oSheet, oCell

Define.safearray *openpar
Define.variant openarray
Define.variant value_pi_double
Define.d result

V_ARRAY_DISP(openarray) = safearray

V_DOUBLE(value_pi_double) = 3.1415926

oSM = COMate_CreateObject("com.sun.star.ServiceManager")
If oSM
  ;Creating instance of Desktop
  oDesk = oSM\GetObjectProperty("CreateInstance('com.sun.star.frame.Desktop')")
    If oDesk
      ;Opening a new calc Document   
      oCalc = oDesk\GetObjectProperty("loadComponentFromURL('private:factory/scalc', '_blank', 0, " + Str(openarray) + " as variant)")
      If oCalc
        oSheet = oCalc\GetObjectProperty("Sheets\getByName('Feuille1')")
        If oSheet
          oCell = oSheet\GetObjectProperty("getCellRangeByName('A3')")
          oCell\SetProperty("String = 'Hello World !' as string")

          oCell = oSheet\GetObjectProperty("getCellRangeByName('B5')")
          oCell\SetProperty("value = "+Str(value_pi_double)+" as variant")

          result = oCell\GetVariantProperty("value")
          MessageRequester("COMate PLUS", "Result getCellRangeByName('B5'): " + Str(result))

          Debug COMate_GetLastErrorDescription()

          oSheet\Release()
        EndIf
        oCalc\Release()
      EndIf
      oDesk\Release()   
    EndIf
  oSM\Release()
EndIf 
But i can't retrived the value of the cell in B5, a little help ? :D
“Fear is a reaction. Courage is a decision.” - WC
Mistrel
Addict
Addict
Posts: 3415
Joined: Sat Jun 30, 2007 8:04 pm

Post by Mistrel »

Out of curiosity, can you make a COM dll with this?
Post Reply