A question to COMate experts

Just starting out? Need help? Post your questions and find answers here.
Yuri_D
User
User
Posts: 68
Joined: Wed Apr 13, 2016 7:39 am

A question to COMate experts

Post by Yuri_D »

Hello!

Could you please advise how to read properties from an COMate Enumeration object?
This COMate object doesn't support GetString/Integer etc. methods but in my case, besides the collection itself it returns a number of elements and other useful properties.
Of course I can create two objects with CreateEnumeration() and GetObjectProperty() but it triggers 2x same queries to the DB which is not good
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Re: A question to COMate experts

Post by srod »

Yes COMate destroys the temporary object it creates whilst getting hold of the relevant Enum interface pointer in the COMateClass_CreateEnumeration() procedure. You are thus correct in thinking that you would need to create 2 separate objects in your case in order to make use of any additional properties etc.

You can easily modify the above procedure to, say, not destroy the temporary object and instead place it in a separate buffer so that you can make use of it separately. Just make sure you destroy that object when you are done with it using the COMate \Release() method. Alternatively, create a bespoke version of the COMateClass_CreateEnumeration() procedure just for your use.
I may look like a mule, but I'm not a complete ass.
Yuri_D
User
User
Posts: 68
Joined: Wed Apr 13, 2016 7:39 am

Re: A question to COMate experts

Post by Yuri_D »

Hello srod,

Thank you for your explanation!
Just checked the COMate code and I think I found the temp object destroyer

Code: Select all

    If command$
      COMateClass_Release(*tempCOMateObject)
    EndIf
but there are too many asterisks for my programming level so I'm not sure how to modify it to return 2x objects and not to spoil anything else :oops: (through a structure?)
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Re: A question to COMate experts

Post by srod »

Struggling for time right now to do anything with this. I'll sort something out as soon as I can. In the meantime just use the two separate objects.
I may look like a mule, but I'm not a complete ass.
Yuri_D
User
User
Posts: 68
Joined: Wed Apr 13, 2016 7:39 am

Re: A question to COMate experts

Post by Yuri_D »

Thank you in advance!
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Re: A question to COMate experts

Post by srod »

Hang on a cotton picking minute! COMatePLUS will already do what you are after - doh! :)
If you invoke the \CreateEnumeration() method with an empty string as a parameter then it will simply return the enumeration object associated with the underlying object!

So taking some code from one of the COMate demo programs, instead of the following :

Code: Select all

;Declare COMate objects.
  Define.COMateObject DomObject, NodeObject
;Declare COMateEnum objects.
  Define.COMateEnumObject DomENUMObject

DomObject = COMate_CreateObject("MSXML.DOMDocument")
If DomObject
  DomObject\Invoke("LoadXml('" + XML$ + "')")
  ;Begin enumeration.
    DomENUMObject = DomObject\CreateEnumeration("SelectNodes('addresses/address')")
    If DomENUMObject
      etc.
We can use the following instead :

Code: Select all

;Declare COMate objects.
  Define.COMateObject DomObject, NodesObject, NodeObject
;Declare COMateEnum objects.
  Define.COMateEnumObject DomENUMObject

DomObject = COMate_CreateObject("MSXML.DOMDocument")
If DomObject
  DomObject\Invoke("LoadXml('" + XML$ + "')")
  ;Begin enumeration.
    NodesObject = DomObject\GetObjectProperty("SelectNodes('addresses/address')")
    If NodesObject    
      DomENUMObject = NodesObject\CreateEnumeration("")
      If DomENUMObject
        etc.
This second version gives you access to the original 'NodesObject' and the enumeration object 'DomENUMObject' without any additional (and repeated) object creation (well no more than would be the case under the first snippet above). Tested and works fine with the above demo program.

Wonder why this didn't end up in the COMatePLUS manual?
I may look like a mule, but I'm not a complete ass.
Yuri_D
User
User
Posts: 68
Joined: Wed Apr 13, 2016 7:39 am

Re: A question to COMate experts

Post by Yuri_D »

Hi srod!

Thank you so much! It works like a charm!)

May I ask you a couple of other questions regarding COMate?

1- "COM Error [1]: The operation completed, but was only partially successful." during enumeration processing (I filter it out but I wonder why it appears)
2 - When I'm using COMate with Excel with one common event callback procedure + objExcel and objWorkbook objects, as soon as I resize the Excel window (from the program, didn't tried manual resize) any attempt to use objWorkbook causes COMate crash but if I disable events before resize, it works. Can it happens because I need to attach events handling to the Workbook as well?
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Re: A question to COMate experts

Post by srod »

1. I would need more info.
2. I am not that familiar with the Excel object, but can you post some code for me to look at? Just did some basic tests and it all seemed to work fine.
I may look like a mule, but I'm not a complete ass.
Yuri_D
User
User
Posts: 68
Joined: Wed Apr 13, 2016 7:39 am

Re: A question to COMate experts

Post by Yuri_D »

1 - it appears when no more objects in the enumeration left and \GetNextObject() returns zero. I think it shall be silent...
2 - I can't make it working separately.. I don't know why but it stops processing events after workbook is opened and 4 events received :shock: :oops:
It works normally in my project. I can tell only that everything is forking if I'm operating only with Excel object like

Code: Select all

\objExcel\SetProperty( "WindowState ="+ #xlMaximized)
\objExcel\GetStringProperty( "ActiveWorkbook\Name")
\objExcel\GetIntegerProperty( "ActiveWorkbook\Sheets\Count")
But it doesn't with

Code: Select all

\objExcel\SetProperty( "WindowState ="+ #xlMaximized)
\objWorkbookl\GetStringProperty( "Name")
\objWorkbook\GetIntegerProperty( "Sheets\Count")
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Re: A question to COMate experts

Post by srod »

Code: Select all

\objExcel\SetProperty( "WindowState ="+ #xlMaximized)
You cannot combine a string with a numeric constant so there is something wrong there.

I tested using the "WindowState" property of the "Application" object and it resized the window fine and all events seemed to work fine with no crashes.
I may look like a mule, but I'm not a complete ass.
Yuri_D
User
User
Posts: 68
Joined: Wed Apr 13, 2016 7:39 am

Re: A question to COMate experts

Post by Yuri_D »

PB casts them automatically so Str( 100) = ""+ 100
The problem is not in the resizing but what happening with objWorkbook after that if events handling is active.
Can you try this please (but it has a different problem I mentioned earlier)

Code: Select all


XIncludeFile "\COMatePLUS.pbi"

;--#xlWindowState
#xlMaximized = -4137
#xlMinimized = -4140
#xlNormal = -4143



objExcel.COMateObject
objWorkbook.COMateObject



Declare ComEventCallback( _object.COMateObject, _sEventName.s, _objUnknowneterCount.i, *_result.VARIANT)

objExcel = COMate_CreateObject( "Excel.Application")
If objExcel
  objExcel\SetEventHandler( #COMate_CatchAllEvents, @ComEventCallback(), #COMate_OtherReturn)
  objExcel\SetProperty( "Visible = #True")
  
  objWorkbook = objExcel\GetObjectProperty( "Workbooks\Open('C:.............................') = #True")
  If objWorkbook
    
    Debug objWorkbook\GetStringProperty( "Name")
    Debug objExcel\GetStringProperty( "ActiveWorkbook\Name")
    
    objExcel\SetProperty( "WindowState ="+ #xlMaximized)
    
    Debug objWorkbook\GetStringProperty( "Name")                  ;<<<< COMate crashes here
    Debug objExcel\GetStringProperty( "ActiveWorkbook\Name") ;<<<< this one will work If above is commented
    
  EndIf
  
  Repeat
  Until objExcel = 0
  
EndIf
End


Procedure ComEventCallback( _object.COMateObject, _sEventName.s, _objUnknowneterCount.i, *_result.VARIANT)
  
  Shared objExcel
  
  Debug "Event: < "+ _sEventName +" >  Params: "+ _objUnknowneterCount
  
  Select _sEventName
    Case "WorkbookBeforeClose"
      objExcel\SetEventHandler( #COMate_CatchAllEvents, #Null, #COMate_OtherReturn)
      objExcel\Release()
  EndSelect
EndProcedure
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Re: A question to COMate experts

Post by srod »

You have an infinite loop running hogging all the program time! Remove that (stick a message requester in there) and it all runs fine here.
I may look like a mule, but I'm not a complete ass.
Yuri_D
User
User
Posts: 68
Joined: Wed Apr 13, 2016 7:39 am

Re: A question to COMate experts

Post by Yuri_D »

Yep... I tried to use Delay() there but it seems it doesn't release CPU and WaitWindowEvent( xx) doesn't work without window :/
With the messagebox it works and has no crashes so I need to look into my code, thank you!

Another question: is it possible to clear the COMate error code after it was processed and what is the bestest way to realise "OnError" for COMate?
User avatar
Olliv
Enthusiast
Enthusiast
Posts: 542
Joined: Tue Sep 22, 2009 10:41 pm

Re: A question to COMate experts

Post by Olliv »

Hello Stephen,

hope u r fine, and thank you for your presence !

I add some remarks, but it s imho : can be corrected.

@Yuri hello first,

1)

Code: Select all

Repeat
   Delay(100)
Until objExcel
Normally, it seems to work fine. Give details of the argument (milliseconds) of your Delay() invoking. But normally 100 ms is good.

2) WaitWindowEvent() requires a window : all right, and it is not a problem. A window can be invisible by setting its flag during its opening, or by sizing (or resizing) to a null dimension, or by moving to an extraneous position. The first of these 3 ways seems to be the best way.

3) You declare objExcel. All right, but declare it globallly

Code: Select all

Global objExcel.TaratataMagic
And, if you do not like global var, what it could be normal sometimes... No reason to sing nake everytime... In this way, you have to insure by inserting a shared directive in every procedure which uses such a variable, obviously, in your example, in the callback procedure. Actually neither Global, neither Shared : Sure there is a problem.


Also I wish you a good pleasure to use Comate. I used it only once, 9 years ago but appreciate a lot this feature added by SRod.
Yuri_D
User
User
Posts: 68
Joined: Wed Apr 13, 2016 7:39 am

Re: A question to COMate experts

Post by Yuri_D »

Hello community!

Once again your help wanted, could you please help?

There are few VBA methods which can do a single or batch data manipulation.

Code: Select all

Create(DObject[], async As Boolean)
Delete(DObject[], async As Boolean)
......
I know how to perform single opertations
VBA usage (single):

Code: Select all

Dim user As DObject >>>>>>>>>>>>>>>>>>>>>>>  Define.COMateObject objUser
Set user = Api.CreateObject("user") >>>>>>>>>>>> objUser = objAPI\GetObjectProperty( "CreateObject('user')")
user("name") = "Test0" >>>>>>>>>>>>>>>>>>>>>> objUser\SetProperty( "Item('Name')\Value = 'Test0'")
user.Create() >>>>>>>>>>>>>>>>>>>>>>>>>>>>> objUser\Invoke("Create")
but I don't understand how to create an array of objects and to feed it to these methods to perform batch operations

VBA usage (batch):

Code: Select all

Dim users(2) As DObject >>>>>>>>>>>>>>>>>>>>>>>> ???
Set users(0) = dApi.CreateObject("user") >>>>>>>>>>>> ??? = objAPI\GetObjectProperty( "CreateObject('user')")
users(0)("name") = "Test0" >>>>>>>>>>>>>>>>>>>>>> ???\SetProperty( "Item('Name')\Value = 'Test0'")
Set users(1) = dApi.CreateObject("user") >>>>>>>>>>>> ??? = objAPI\GetObjectProperty( "CreateObject('user')")
users(1)("name") = "Test1" >>>>>>>>>>>>>>>>>>>>>> ???\SetProperty( "Item('Name')\Value = 'Test1'")
'call batch method
dApi.Create users, False >>>>>>>>>>>>>>>>>>>>>>>> objAPI\Invoke("Create ????, #False")
I think I shall create a safearray of objects but how to do it - no ideas(
If somebody knows, please explain.
Post Reply