COMate - control COM objects via automation - OBSOLETE!

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

Post by srod »

Little John wrote:Hello srod,

I've just finished reading the "COMate.chm" file. It's very well written, good understandable and instructive. And it actually answered all my million dollar questions. :D
So thanks again for so much good work!

Regards, Little John
:D

Then that's a million dollars you owe me then! :wink:
I may look like a mule, but I'm not a complete ass.
Xombie
Addict
Addict
Posts: 898
Joined: Thu Jul 01, 2004 2:51 am
Location: Tacoma, WA
Contact:

Post by Xombie »

Well, I suppose I can forgive y'all this one time. Funny thing is, I tried that. I think it's because I was trying different combinations of things to see what the procedure would take and I don't think I had the chr(34) plus the enclosed ' at the same time.

Thanks! I will now happily motor on about my business :) You may continue with whatever irrelevant thing y'all were doing before ~_~
Little John
Addict
Addict
Posts: 4777
Joined: Thu Jun 07, 2007 3:25 pm
Location: Berlin, Germany

Post by Little John »

srod wrote:Then that's a million dollars you owe me then! :wink:
Got me! ;-)
It's a pity that I can't hold my tongue sometimes. ;-)

Regards, Little John
Little John
Addict
Addict
Posts: 4777
Joined: Thu Jun 07, 2007 3:25 pm
Location: Berlin, Germany

Post by Little John »

I've got another WMI demo for COMate; it enumerates the printers on the system.
I just used the demo file "Demo_MonitorInfo.pb" as template, and combined it with information from a post on the German PB forum by scholly. Although I don't know too much about COM and WMI, it seems that with good information from these sources I managed to create a new demo for COMate. :-)
It works fine here (Windows XP Pro SP3), it would be good if someone with appropriate knowledge checks the code, though:

Code: Select all

IncludePath "..\..\"
XIncludeFile "COMate.pbi"

Procedure Get_Printers() 
   Protected objWMIService.COMateObject, printer.COMateObject
   Protected colPrinter.COMateEnumObject 

   strComputer.s = "." 
   Net_I_Index$ = Str(Interface_Index) 
   objWMIService = COMate_GetObject("winmgmts:\\" + strComputer + "\root\cimv2", "") 
   If objWMIService 
      colPrinter = objWMIService\CreateEnumeration("ExecQuery('Select * FROM Win32_Printer')") 
      If colPrinter 
         printer = colPrinter\GetNextObject() 
         While printer 
            Debug "Name        =  " + printer\GetStringProperty("Name")
            Debug "Attributes   =  " + Str(printer\GetIntegerProperty("Attributes")) 
            Debug "----------------------------------------------------------------"
            printer\Release() 
            printer = colPrinter\GetNextObject() 
         Wend 
         colPrinter\Release() 
      EndIf 
      objWMIService\Release()  
   EndIf 
EndProcedure 

Get_Printers()
I learned another interesting point from the post mentioned above: We can use this code to check whether a printer is online or offline. Just run the code above with your printers switched off, and save the attribute values. When the printers are on, their attribute values have changed.

For instance, the above code returns the following output on my system:
Name = EPSON Stylus DX4000 Series
Attributes = 3660
Then I can use the following code to check the status of this printer:

Code: Select all

IncludePath "..\..\"
XIncludeFile "COMate.pbi"

Procedure.i Check_Printer (name.s, attributes_off) 
   Protected objWMIService.COMateObject, printer.COMateObject
   Protected colPrinter.COMateEnumObject 
   Protected ret = -1           ; return vcalue is undefined in the beginning

   strComputer.s = "." 
   Net_I_Index$ = Str(Interface_Index) 
   objWMIService = COMate_GetObject("winmgmts:\\" + strComputer + "\root\cimv2", "") 
   If objWMIService 
      colPrinter = objWMIService\CreateEnumeration("ExecQuery('Select * FROM Win32_Printer')") 
      If colPrinter 
         printer = colPrinter\GetNextObject() 
         While printer 
            If printer\GetStringProperty("Name") = name
               If printer\GetIntegerProperty("Attributes") = attributes_off
                  ret = 0             ; offline
               Else   
                  ret = 1             ; online
               EndIf
               printer\Release()
               Break
            EndIf 
            printer = colPrinter\GetNextObject() 
         Wend 
         colPrinter\Release() 
      EndIf 
      objWMIService\Release()  
   EndIf 
   
   ProcedureReturn ret
EndProcedure 

Debug Check_Printer("EPSON Stylus DX4000 Series", 3660)
Regards, Little John
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Post by srod »

Demo added for the next update! :wink: Thanks.

I'd be wary of the 'check online' procedure though because any number of factors could alter the attributes property which would fool your procedure into thinking the printer is online etc. Perhaps there is a single bit which you can mask out which will notify us of whether the printer is online etc?
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 »

New demo.

Have extended Little John's list-printers demo to list all printers + their supported paper types. Uses safe arrays.

Code: Select all

;/////////////////////////////////////////////////////////////////////////////////
;***COMate***  COM automation through iDispatch.
;*===========
;*
;*WMI demo - list printers and supported paper types.
;/////////////////////////////////////////////////////////////////////////////////

IncludePath "..\..\"
XIncludeFile "COMate.pbi"
XIncludeFile "VariantHelper_Include.pb" 


Procedure Get_Printers() 
  Protected objWMIService.COMateObject, printer.COMateObject 
  Protected colPrinter.COMateEnumObject
  Protected *var.variant, *varPaper.VARIANT
  Protected *sa.SafeArray, i

  strComputer.s = "." 
  objWMIService = COMate_GetObject("winmgmts:\\" + strComputer + "\root\cimv2", "") 
  If objWMIService 
    colPrinter = objWMIService\CreateEnumeration("ExecQuery('Select * FROM Win32_Printer')") 
    If colPrinter 
      printer = colPrinter\GetNextObject() 
      While printer 
        Debug "Name        =  " + printer\GetStringProperty("Name") 
        Debug "===========================================" 
        Debug "Supported paper types :"
        *var = printer\GetVariantProperty("PrinterPaperNames") 
        If *var
          If *var\vt <> #VT_NULL 
            *sa = *var\parray 
            For i = saLBound(*sa) To saUBound(*sa) 
              *varPaper = SA_VARIANT(*sa, i) 
              If *varPaper\vt <> #VT_BSTR 
                VariantChangeType_(*varPaper, *varPaper, 0, #VT_BSTR) 
              EndIf 
              Debug "    " + PeekS(*varPaper\bstrVal, -1, #PB_Unicode) 
            Next
            saFreeSafeArray(*sa) ;This will clear all variants within the safe array.
          EndIf
          VariantClear_(*var)
          FreeMemory(*var)
        EndIf
        Debug "===========================================" 
        printer\Release() 
        printer = colPrinter\GetNextObject() 
      Wend 
      colPrinter\Release() 
    EndIf 
    objWMIService\Release()  
  EndIf 
EndProcedure 

Get_Printers()
I may look like a mule, but I'm not a complete ass.
Little John
Addict
Addict
Posts: 4777
Joined: Thu Jun 07, 2007 3:25 pm
Location: Berlin, Germany

Post by Little John »

Hello srod,

when I run the new demo program (using PB 4.20 and COMate 1.1.7), I get an IMA on line 1301

Code: Select all

result = iDisp\Invoke(dispID, ?IID_NULL, #LOCALE_USER_DEFAULT, invokeType, dp, *ret, excep, @uiArgErr)
in COMate.pbi.

I have some virtual printers installed on my system. Maybe the IMA occurs because the program tries to ask for supported paper types for virtual printers? The only real printer that I have installed is "EPSON Stylus DX4000 Series". When I only look for supported paper types for this printer, then I don't get an IMA:

Code: Select all

If printer\GetStringProperty("Name") = "EPSON Stylus DX4000 Series"
   Debug "Supported paper types :"
   *var = printer\GetVariantProperty("PrinterPaperNames")
   If *var
      If *var\vt <> #VT_NULL
         *sa = *var\parray
         For i = saLBound(*sa) To saUBound(*sa)
            *varPaper = SA_VARIANT(*sa, i)
            If *varPaper\vt <> #VT_BSTR
               VariantChangeType_(*varPaper, *varPaper, 0, #VT_BSTR)
            EndIf
            Debug "    " + PeekS(*varPaper\bstrVal, -1, #PB_Unicode)
         Next
         saFreeSafeArray(*sa) ;This will clear all variants within the safe array.
      EndIf
      VariantClear_(*var)
      FreeMemory(*var)
   EndIf
   Debug "==========================================="
EndIf
A wish for the next COMate release: Maybe you can add a search tab to "COMate.chm"?

Regards, Little John
Little John
Addict
Addict
Posts: 4777
Joined: Thu Jun 07, 2007 3:25 pm
Location: Berlin, Germany

Post by Little John »

srod wrote:I'd be wary of the 'check online' procedure though because any number of factors could alter the attributes property which would fool your procedure into thinking the printer is online etc. Perhaps there is a single bit which you can mask out which will notify us of whether the printer is online etc?
Now I've found some information on the internet. According to http://search.cpan.org/~wasx/Win32-Prin ... er/Enum.pm we have to check for $0400, which is in conformance with my tests. So this is my new demo code:

Code: Select all

IncludePath "..\.."
XIncludeFile "COMate.pbi"

Procedure Get_Printers() 
   Protected objWMIService.COMateObject, printer.COMateObject
   Protected colPrinter.COMateEnumObject 
   Protected name.s

   strComputer.s = "." 
   objWMIService = COMate_GetObject("winmgmts:\" + strComputer + "\root\cimv2", "") 
   If objWMIService 
      colPrinter = objWMIService\CreateEnumeration("ExecQuery('Select * FROM Win32_Printer')") 
      If colPrinter
         Debug "Printers" 
         Debug "-----------"
         printer = colPrinter\GetNextObject() 
         While printer 
            name = printer\GetStringProperty("Name")
            If printer\GetIntegerProperty("Attributes") & $0400
               Debug name + " is offline"
            Else
               Debug name + " is online"
            EndIf
            printer\Release() 
            printer = colPrinter\GetNextObject() 
         Wend 
         colPrinter\Release() 
      EndIf 
      objWMIService\Release()  
   EndIf 
EndProcedure 

Get_Printers() 
Here is also some interesting code concerning WMI and printers.

Regards, Little John
Last edited by Little John on Fri Oct 17, 2008 5:58 pm, edited 1 time in total.
Little John
Addict
Addict
Posts: 4777
Joined: Thu Jun 07, 2007 3:25 pm
Location: Berlin, Germany

Post by Little John »

There is a minor glitch in the demo program "Demo_MonitorInfo.pb"":
The procedure Monitor_Info() does not return any value. So instead of
Procedure.s
it should read
Procedure
//edit:
Also the following line isn't needed there, is it?

Code: Select all

Net_I_Index$ = Str(Interface_Index)
Regards, Little John
Little John
Addict
Addict
Posts: 4777
Joined: Thu Jun 07, 2007 3:25 pm
Location: Berlin, Germany

Post by Little John »

I've now translated the code from the above mentioned page to PureBasic. IMHO this is currently the most interesing COMate demo code regarding printers. :-)

Code: Select all

; Source: <http://www.computerperformance.co.uk/vbscript/wmi_printer.htm>
; translated to PureBasic and adapted by Little John

; Sample VBScript to interrogate Printer properties with WMI
; Author Guy Thomas http://computerperformance.co.uk/
; Version 2.3 - December 2005

EnableExplicit

IncludePath "..\..\"
XIncludeFile "COMate.pbi"

Define.COMateObject objWMIService, objItem
Define.COMateEnumObject colItems
Define.s strComputer, msg
Define intPrinters

strComputer = "."
intPrinters = 1

objWMIService = COMate_GetObject("winmgmts:\\" + strComputer + "\root\cimv2", "")
If objWMIService
   colItems = objWMIService\CreateEnumeration("ExecQuery('Select * FROM Win32_Printer')")
   If colItems
      objItem = colItems\GetNextObject()
      While objItem
         msg = "Printer Number " + Str(intPrinters) + ": " + objItem\GetStringProperty("Name") + #CR$
         msg + "====================================" + #CR$
         msg + "Availability: " + objItem\GetStringProperty("Availability") + #CR$
         msg + "Description: " + objItem\GetStringProperty("Description") + #CR$
         msg + "Printer: " + objItem\GetStringProperty("DeviceID") + #CR$
         msg + "Driver Name: " + objItem\GetStringProperty("DriverName") + #CR$
         msg + "Port Name: " + objItem\GetStringProperty("PortName") + #CR$
         msg + "Printer State: " + objItem\GetStringProperty("PrinterState") + #CR$
         msg + "Printer Status: " + objItem\GetStringProperty("PrinterStatus") + #CR$
         msg + "PrintJobDataType: " + objItem\GetStringProperty("PrintJobDataType") + #CR$
         msg + "Print Processor: " + objItem\GetStringProperty("PrintProcessor") + #CR$
         msg + "Spool Enabled: " + objItem\GetStringProperty("SpoolEnabled") + #CR$
         msg + "Separator File: " + objItem\GetStringProperty("SeparatorFile") + #CR$
         msg + "Queued: " + objItem\GetStringProperty("Queued") + #CR$
         msg + "Status: " + objItem\GetStringProperty("Status") + #CR$
         msg + "StatusInfo: " + objItem\GetStringProperty("StatusInfo") + #CR$
         msg + "Published: " + objItem\GetStringProperty("Published") + #CR$
         msg + "Shared: " + objItem\GetStringProperty("Shared") + #CR$
         msg + "ShareName: " + objItem\GetStringProperty("ShareName") + #CR$
         msg + "Direct: " + objItem\GetStringProperty("Direct") + #CR$
         msg + "Location: " + objItem\GetStringProperty("Location") + #CR$
         msg + "Priority: " + objItem\GetStringProperty("Priority") + #CR$
         msg + "Work Offline: " + objItem\GetStringProperty("WorkOffline") + #CR$
         msg + "Horizontal Res: " + objItem\GetStringProperty("HorizontalResolution") + #CR$
         msg + "Vertical Res: " + objItem\GetStringProperty("VerticalResolution")
         MessageRequester("", msg)
         intPrinters + 1
         objItem\Release()
         objItem = colItems\GetNextObject()
      Wend
      colItems\Release()
   EndIf
   objWMIService\Release()
EndIf
Regards, Little John
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Post by srod »

I disagree!!! :) That is just a list of many of the available properties whilst the listing paper types shows how to drill down into a safe-array returned from one of the properties! Many WMI properties return such arrays! :wink: If you use the WMI tool which is included with the COMate download you will be able to pull up examples of using just about every kind of WMI object! Very useful! :)

Anyhow I have a virtual printer installed (pdf) and this is listed fine with the paper types example which crashes for you! I've tested with PB 4.2 and 4.3 and it all works fine. Afraid that I cannot explain the crash.

**EDIT : can you try the WMI tool which is part of the COMate package and see if it crashes? Select the Win32_Printer class and then click run etc. Amongst other things this will list the paper types for each printer it finds. Let us see if this crashes.
Last edited by srod on Sat Oct 18, 2008 12:01 am, 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 »

A wish for the next COMate release: Maybe you can add a search tab to "COMate.chm"?
Done.
I may look like a mule, but I'm not a complete ass.
Little John
Addict
Addict
Posts: 4777
Joined: Thu Jun 07, 2007 3:25 pm
Location: Berlin, Germany

Post by Little John »

Hello srod!
srod wrote:
A wish for the next COMate release: Maybe you can add a search tab to "COMate.chm"?
Done.
Thank you!

"ScriptomaticV2.hta" only runs with administrator privileges here (on XP), and running the code for Win32_Printer class worked fine. Then I tried running your demo program also with administrator privileges, but it crashed as before -- so missing privileges obviously weren't the cause of the problem.

After some more testing, I discovered something strange:
Asking for the supported paper types only for a single printer (like I did in this post) works fine for each printer on my system!

But when I do without a narrowing condition such as

Code: Select all

If printer\GetStringProperty("Name") = "FreePDF XP"
because I want to examine all installed printers, then I get that IMA! :? (using PB 4.20)

Regards, Little John
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Post by srod »

Very strange indeed.

I am presuming that the GetVariantProperty() line is the one invoking the COM method which is causing the problems. I would suspect that one of these virtual printer drivers is playing silly buggers!

**EDIT : when you ran the scripting utility under xp did it list the virtual printers okay? And if so what does it report for the paper types?
I may look like a mule, but I'm not a complete ass.
Little John
Addict
Addict
Posts: 4777
Joined: Thu Jun 07, 2007 3:25 pm
Location: Berlin, Germany

Post by Little John »

srod wrote:**EDIT : when you ran the scripting utility under xp did it list the virtual printers okay? And if so what does it report for the paper types?
As far as I can see, Scriptomatic2 listed everything OK. All entries related to paper are of the same form for all virtual printers and for the real one as well, e.g.:
Caption: SnagIt 7
CurrentPaperType:
DefaultPaperType:
PaperSizesSupported: 7,8,1,22,11,1,1,1,1
PaperTypesAvailable:
PrinterPaperNames: Letter,Legal,Executive,A4,Umschlag 10,Umschlag DL,Umschlag C5,Umschlag B5,Umschlag Monarch
In other words, the entries
CurrentPaperType:
DefaultPaperType:
PaperTypesAvailable:
are empty for all printers. Is this a problem?

I can send you the complete output, if you like (long!) :-)

Regards, Little John
Post Reply