• Welcome to PlanetSquires Forums.
 

CWindow Release Candidate 29

Started by José Roca, July 01, 2017, 04:21:09 PM

Previous topic - Next topic

José Roca

#60
Calling a WMI method using low-level COM is very complicated, but using Dispatch.Invoke is straightfoward.

I deliberately have chosen this example because it has a VT_BYREF OUT BSTR parameter, to test the CVAR constructor designed for this purpose.

I have modified the CWmiServices class to allow to use


DIM pDispServices AS CDispInvoke = CDispInvoke(pServices.ServicesObj)


instead of


DIM pDispServices AS CDispInvoke = CDispInvoke(cast(IDispatch PTR, cast(ANY PTR, pServices.ServicesObj)))


This is why extensive testing is a must. Not only to discover bugs, but to improve the code.


#include "windows.bi"
#include "Afx/CWmiDisp.inc"
using Afx

' // Connect to WMI using a moniker
DIM pServices AS CWmiServices = $"winmgmts:{impersonationLevel=impersonate}!\\.\root\default:StdRegProv"
IF pServices.ServicesPtr = NULL THEN END

' // Assign the WMI services object pointer to CDispInvoke
' // CWmiServices.ServicesObj returns an AddRefed pointer, whereas CWmiServices.ServicesPtr not.
DIM pDispServices AS CDispInvoke = CDispInvoke(pServices.ServicesObj)

' Parameters of the GetStringValue method:
' %HKEY_LOCAL_MACHINE ("2147483650") - The value must be specified as an string and in decimal, not hexadecimal.
' vDefKey = [IN]  "2147483650"
' vPath   = [IN]  "Software\Microsoft\Windows NT\CurrentVersion"
' vValue  = [OUT] "ProductName"

DIM cbsValue AS CBSTR
DIM cvValue AS CVAR = CVAR(cbsValue.vptr, VT_BSTR)
DIM cvRes AS CVAR = pDispServices.Invoke("GetStringValue", 4, CVAR("2147483650"), _
   CVAR($"Software\Microsoft\Windows NT\CurrentVersion"), CVAR("ProductName"), cvValue)
PRINT cvValue.ToStr
' --or-- PRINT cbsValue

PRINT
PRINT "Press any key..."
SLEEP


aloberr

QuoteIMO there is nothing wrong in using PowerBASIC techniques.
OK!

Richard Kelly

Quote from: Jose Roca on July 04, 2017, 05:58:25 PM
I have begin to translate the high level glut functions. Tested the torus, cube, sphere and octahedron and got them working. See capture.

NeHe Lesson 5 work perfectly for me.

Rick

Richard Kelly

Quote from: Jose Roca on July 09, 2017, 09:58:03 PM
I have added some methods to the CVAR class and have implemented a news class: CDispInvoke.

Works perfectly for me.

Rick

Richard Kelly

Quote from: Jose Roca on July 10, 2017, 02:53:29 AM
This example has allowed me to test that the Invoke method works fine with OUT variant parameters.

$"Software\Microsoft\Windows NT\CurrentVersion"


Correctly shows Windows 10 Pro.

Rick

Richard Kelly

Rather than bore with the details, I ran every example you provided in the order of the threads you presented without error.

Rick

José Roca

#66
Thanks very much for testing. I'm currently updating the documentation with the new classes and will revise the ADO classes because the differences between CVAR and the old CVARIANT. After that, I will post the new version and will begin to write many small examples with the purpose of testing the framework systematically. Trying the tests in other OSes such WIndows 10 is very important because all my hardware amounts to a desktop computer with Windows 7, an internet connection and a semi-broken printer.

CBSTR, CVAR, CSafearray and CDispInvoke allow to use COM Automation. This means that now we should be able to automate Office applications, but I can't test because I don't have Office installed.  Although I almost always work with COM at low-level, sometimes the use of Automation is unavoidable or convenient.

CWmiDisp allows to easily use the powerful WMI technology without having to resort to VBScript or PowerShell. I have added a constructor to allow to connect to remote servers, but I can't test it because I don't have access to a server.

Paul Squires

Quote from: Jose Roca on July 18, 2017, 12:40:58 AM
CBSTR, CVAR, CSafearray and CDispInvoke allow to use COM Automation. This means that now we should be able to automate Office applications, but I can't test because I don't have Office installed.
I can certainly help you with that. I have access to an older version of Office (2010) and a little newer version (2013). We haven't upgraded to the very latest Office version yet. I have tried it but I didn't like it. I also use LibreOffice on my laptop but I don't think that has COM automation.

Maybe start new separate posts that specifically ask the group here to test certain code? If the posts are separate it might be easier for me and others to zero in on what you need tested. Sometimes I may miss code that is buried in long threads.
Paul Squires
PlanetSquires Software
WinFBE Editor and Visual Designer

Richard Kelly

I'm using Win 10 Pro with all the latest updates on a home network behind a NetGear Nighthawk router. The router has a USB drive plugged in that appears to my PC as remote that I use for all kinds of backups. My printer is a wireless network HP Envy. I have a full copy of MS Office 2013 and can get the latest version should I want it for $10 from my employer. I have IE, EDGE, Chrome, and, Firefox browsers and am running Norton for protection. There is also a MAC, PS4, and, other windows computers on the network.

I'll run tests when you ask for 'em....

Rick

Paul Squires

....ah yes, similarly to Rick, I am also running Windows 10 so I can run tests on that OS as well.
Paul Squires
PlanetSquires Software
WinFBE Editor and Visual Designer

Richard Kelly

Quote from: TechSupport on July 18, 2017, 04:43:09 PM
....ah yes, similarly to Rick, I am also running Windows 10 so I can run tests on that OS as well.

Well Paul, in the misspent idle moments of my younger days, I ran a complete windows AD network with my own  DNS, Print, NTP, Mail, Web, FTP, Domain controller, and, SQL database servers in my home. I had RJ45 outlets all over the place "homerun" wired to a patch panel. I also had a "honeypot" web server I would put on the public side of the router periodically to check on what the bad guys were trying to do to me.

Life is much simpler now and wifi is my new queen.... :o

Rick

José Roca

I have added a base class to the GDI+ classes to avoid to having to initialize and shutdown GDI+ with the programs that use the classes:


DIM SHARED AFX_CGDIP_TOKEN AS ULONG_PTR   ' // GDI+ token
DIM SHARED AFX_CGDIP_CREF AS LONG         ' // Reference count

' ########################################################################################
' CGpBase class
' ########################################################################################
TYPE CGpBase

   m_Status AS GpStatus   ' // Last status code

   DECLARE CONSTRUCTOR
   DECLARE DESTRUCTOR
   DECLARE FUNCTION GetLastStatus () AS GpStatus
   DECLARE FUNCTION SetStatus (BYVAL status AS GpStatus) AS GpStatus

END TYPE
' ########################################################################################

' ========================================================================================
' Default constructor
' ========================================================================================
PRIVATE CONSTRUCTOR CGpBase
   OutputDebugStringW("*** BEGIN - CONSTRUCTOR CGpBase - cRef = " & STR$(AFX_CGDIP_CREF))
   ' // Initialize the GDI+ library
   IF AFX_CGDIP_CREF = 0 THEN
      DIM StartupInput AS GdiplusStartupInput
      DIM version AS ULONG = 1
      IF AfxWindowsVersion >= 600 THEN version = 2
      StartupInput.GdiplusVersion = version
      m_Status = GdiplusStartup(@AFX_CGDIP_TOKEN, @StartupInput, NULL)
      IF m_Status <> 0 THEN
         MessageBoxW(0, "GDI+ initialization failed - Error: " & STR$(m_Status), "Error", MB_OK OR MB_ICONERROR OR MB_APPLMODAL)
      ELSE
         AFX_CGDIP_CREF = 1
      END IF
      OutputDebugStringW("+++ CONSTRUCTOR CGpBase Initialize GDI+ - version = " & str$(version))
   ELSE
      ' // Increase the reference count
      AFX_CGDIP_CREF += 1
   END IF
   OutputDebugStringW("*** END - CONSTRUCTOR CGpBase - cRef = " & STR$(AFX_CGDIP_CREF))
END CONSTRUCTOR
' ========================================================================================

' ========================================================================================
' Destructor
' ========================================================================================
PRIVATE DESTRUCTOR CGpBase
   OutputDebugStringW("*** BEGIN - DESTRUCTOR CGpBase - cRef = " & STR$(AFX_CGDIP_CREF))
   ' // Decrease the reference count
   AFX_CGDIP_CREF -= 1
   IF AFX_CGDIP_CREF < 1 THEN
      ' // Shutdown GDI+
      IF AFX_CGDIP_TOKEN THEN GdiplusShutdown(AFX_CGDIP_TOKEN)
      OutputDebugStringW("--- DESTRUCTOR CGpBase Shutdown GDI+")
   ELSE
   END IF
   OutputDebugStringW("*** END - DESTRUCTOR CGpBase - cRef = " & STR$(AFX_CGDIP_CREF))
END DESTRUCTOR
' ========================================================================================

' ========================================================================================
' Returns the last status code.
' ========================================================================================
PRIVATE FUNCTION CGpBase.GetLastStatus () AS GpStatus
   RETURN m_Status
END FUNCTION
' ========================================================================================

' ========================================================================================
' Sets the last status code.
' ========================================================================================
PRIVATE FUNCTION CGpBase.SetStatus (BYVAL status AS GpStatus) AS GpStatus
   m_Status = status
   RETURN m_Status
END FUNCTION
' ========================================================================================


Have needed to use two globals, that is not something that I like, but...

José Roca

UDTs can be stored in CVARs. They are stored as an array of bytes.


TYPE Foo
  x AS long
  y as long
  b as WSTRING * 260
END type
 
DIM t AS Foo
t.x = 12345
t.y = 72727
t.b = "Test string"
   
DIM cv AS CVAR
cv.AssignBuffer(@t, SIZEOF(t))

DIM t2 as Foo
cv.ToBuffer(@t2, SIZEOF(t))

print t2.x
print t2.y
print t2.b


Paul Squires

Quote from: Jose Roca on July 19, 2017, 03:56:59 AM
I have added a base class to the GDI+ classes to avoid to having to initialize and shutdown GDI+ with the programs that use the classes:


DIM SHARED AFX_CGDIP_TOKEN AS ULONG_PTR   ' // GDI+ token
DIM SHARED AFX_CGDIP_CREF AS LONG         ' // Reference count

' ########################################################################################
' CGpBase class
' ########################################################################################
TYPE CGpBase

   m_Status AS GpStatus   ' // Last status code

   DECLARE CONSTRUCTOR
   DECLARE DESTRUCTOR
   DECLARE FUNCTION GetLastStatus () AS GpStatus
   DECLARE FUNCTION SetStatus (BYVAL status AS GpStatus) AS GpStatus

END TYPE
' ########################################################################################

' ========================================================================================
' Default constructor
' ========================================================================================
PRIVATE CONSTRUCTOR CGpBase
   OutputDebugStringW("*** BEGIN - CONSTRUCTOR CGpBase - cRef = " & STR$(AFX_CGDIP_CREF))
   ' // Initialize the GDI+ library
   IF AFX_CGDIP_CREF = 0 THEN
      DIM StartupInput AS GdiplusStartupInput
      DIM version AS ULONG = 1
      IF AfxWindowsVersion >= 600 THEN version = 2
      StartupInput.GdiplusVersion = version
      m_Status = GdiplusStartup(@AFX_CGDIP_TOKEN, @StartupInput, NULL)
      IF m_Status <> 0 THEN
         MessageBoxW(0, "GDI+ initialization failed - Error: " & STR$(m_Status), "Error", MB_OK OR MB_ICONERROR OR MB_APPLMODAL)
      ELSE
         AFX_CGDIP_CREF = 1
      END IF
      OutputDebugStringW("+++ CONSTRUCTOR CGpBase Initialize GDI+ - version = " & str$(version))
   ELSE
      ' // Increase the reference count
      AFX_CGDIP_CREF += 1
   END IF
   OutputDebugStringW("*** END - CONSTRUCTOR CGpBase - cRef = " & STR$(AFX_CGDIP_CREF))
END CONSTRUCTOR
' ========================================================================================

' ========================================================================================
' Destructor
' ========================================================================================
PRIVATE DESTRUCTOR CGpBase
   OutputDebugStringW("*** BEGIN - DESTRUCTOR CGpBase - cRef = " & STR$(AFX_CGDIP_CREF))
   ' // Decrease the reference count
   AFX_CGDIP_CREF -= 1
   IF AFX_CGDIP_CREF < 1 THEN
      ' // Shutdown GDI+
      IF AFX_CGDIP_TOKEN THEN GdiplusShutdown(AFX_CGDIP_TOKEN)
      OutputDebugStringW("--- DESTRUCTOR CGpBase Shutdown GDI+")
   ELSE
   END IF
   OutputDebugStringW("*** END - DESTRUCTOR CGpBase - cRef = " & STR$(AFX_CGDIP_CREF))
END DESTRUCTOR
' ========================================================================================

' ========================================================================================
' Returns the last status code.
' ========================================================================================
PRIVATE FUNCTION CGpBase.GetLastStatus () AS GpStatus
   RETURN m_Status
END FUNCTION
' ========================================================================================

' ========================================================================================
' Sets the last status code.
' ========================================================================================
PRIVATE FUNCTION CGpBase.SetStatus (BYVAL status AS GpStatus) AS GpStatus
   m_Status = status
   RETURN m_Status
END FUNCTION
' ========================================================================================


Have needed to use two globals, that is not something that I like, but...


I haven't tested this but....

Instead of using globals could you create the variables as STATIC member variables of the TYPE and then access them using the BASE keyword in subsequent class instances?
Paul Squires
PlanetSquires Software
WinFBE Editor and Visual Designer

José Roca

> Instead of using globals could you create the variables as STATIC member variables of the TYPE and then access them using the BASE keyword in subsequent class instances?

No. Each instance of a class gets its own allocation. Extending from another class you get access to its methods, but not to its data (that is, you save some coding because you don't have to reimplement the methods of the inherited class, but nothing else). Either I have to use globals or allow that each instance of any of the classes will initialize and shutdown GDI+. That works, but don't know what the speed penalty will be.