Get Window Instance/Object From Handle (hWnd) - vba

Working in VB/VBA, I have a window handle and I need to convert it to a window object/instance which I can use to access the window object's properties.
AccessibleObjectFromWindow has not proven useful.
How do I do this?
Update
Below is additional detail.
I am working with a window created using mshta.exe and configured with some scripts:
CreateObject("WScript.Shell").Run "%systemroot%\syswow64\mshta.exe about:""<head><script>moveTo(-32000,-32000);document.title='" & x86WindowSignature & "'</script><hta:application showintaskbar=no /><object id='shell' classid='clsid:8856F961-340A-11D0-A96B-00C04FD705A2'><param name=RegisterAsBrowser value=1></object><script>shell.putproperty('" & x86WindowSignature & "',document.parentWindow);</script></head>""", 0, False
For Each Window In CreateObject("Shell.Application").Windows
On Error Resume Next
Set Getx86Window = Window.GetProperty(x86WindowSignature)
Error = Err.Number
On Error GoTo 0
If Error = 0 Then Exit For
Pause 0.01, True
Next Window
' Configure the window environment - global object variables are defined, one for each scripting object - they are instantiated by calling the Initialize routine
Getx86Window.execScript "var VBScript, JScript;"
Getx86Window.execScript "Sub Initialize() : Set VBScript = CreateObject(""MSScriptControl.ScriptControl"") : VBScript.Language = ""VBScript"" : Set JScript = CreateObject(""MSScriptControl.ScriptControl"") : JScript.Language = ""JScript"" End Sub", "VBScript"
' Initialize the window environment
Getx86Window.Initialize
x86WindowSignature is a function that returns a unique string or key.
I'm going through this process because Microsoft doesn't provide 64-bit versions of the VBScript and JScript engines and this process allows me to create 32-bit versions and use them from a 64-bit world.
To reuse this scripting container I need to look at the existing windows and query one of the properties I created:
Set Getx86Window = Window.GetProperty(x86WindowSignature)
Normally I use this logic to find the window of interest:
' Look for an existing window
For Each Window In CreateObject("Shell.Application").Windows
On Error Resume Next
Set Getx86Window = Window.GetProperty(x86WindowSignature)
Error = Err.Number
On Error GoTo 0
If Error = 0 Then Exit Function
Next Window
But, if the application crashes or otherwise fails to close the scripting window, it remains open but is not listed with CreateObject("Shell.Application").Windows.
But I can find it using FindWindow:
WindowHandle = API_FindWindow("HTML Application Host Window Class", x86WindowSignature)
I'm stuck trying to convert the window handle to a VB/VBA "Window" object variable.
I tried a simple copy memory from the handle variable to the object variable but that failed.

It turns out I made an incorrect observation in my initial work. I thought that I was not getting all the windows returned to me that I had created. That is not true. This line:
CreateObject("Shell.Application").Windows
always returns all the windows that were created by Shell.Application. So I had at my disposal all the windows my application had created in the past.
When I posed my question, I thought that I could only get those "lost" window objects by using lower level Windows SDK calls and then somehow convert those hWnds into Shell.Application window object references. I never did figure out how to do that or if it is even possible but that need is no longer required.

Related

GetRuntimes Method in Microsoft Task Scheduler "Schedule.service"

I am working on code in VBA trying to determine when the next several scheduled tasks will run. I am using the Task Scheduler COM object (Schedule.Service). I can enumerate all the tasks, all their triggers, and even show the next run time by using the NextRunTime property. What I am after is the next several runtimes. Microsoft has the GetRunTimes method documented, which sounds like what I am after. But anytime I try to use the method I get the error that the object doesn't support the property method.
I have tried in VBA, VBS, and PowerShell all with the same results. I have tried on two Windows 7 PCs, Windows 2008 R2, and Windows 2012 and all have the same results. On one Windows 7 PC the Get-Member shows that method, but gives the error "Cannot find an overload for GetRunTimes". COM object browsers don't show the method either.
I have never encountered this before but I don't code all the time. It is as if Microsoft documented it but left it out. Has anyone successfully used the GetRunTimes method of the Task Scheduler object?
The error I get in VBA is
Run-time error '438': Object doesn't support this property or method
This happens as soon as it gets to the GetRunTimes line
Here is a portion of the code:
Set objTS = CreateObject("Schedule.Service")
objTS.Connect (strComputer)
Set rootFolder = objTS.GetFolder("\")
Set ColTasks = rootFolder.GetTasks(0)
If ColTasks.Count = 0 Then
tmpArray(0) = ""
Else
For Each task In ColTasks
With task
If .Name = "RebootSchedule" Then
Set triggers = task.Definition.triggers
'This next line generates the error, everything else works)
Set runtimes = .GetRunTimes("8/1/2017", "9/1/2017")
...

Remove a VBA Project Reference

In VBA I can see three different references for PDFCreator. One of them (see the second image) is a version of the software stored locally, and it works. I'd like to use this reference.
The other two are references to versions stored on a server, and they're broken (at this stage, I don't have permission to reinstall or delete them).
My problem is that after selecting the desired reference (see the second image) and clicking 'Ok', it resets to an incorrect reference, as shown in the third image.
How can I either override whatever's going on and select the desired reference or remove the incorrect references? While I'm not able to uninstall these versions from the server, I see no reason that my Excel would need to reference them. Can they just be removed from the list?
Image 1: Default state of the VBA Project References (PDFCreator not selected)
Image 2: Selecting the correct PDFCreator version
Image 3: Re-opening the menu shows that the incorrect PDFCreator version is selected
You might be able to something like the following...
To Remove broken references:
Private Sub RemoveBrokenReferences(ByVal Book As Workbook)
'////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Dim oRefS As Object, oRef As Object
'////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Set oRefS = Book.VBProject.References
For Each oRef In oRefS
If oRef.IsBroken Then
Call oRefS.Remove(oRef)
End If
Next
End Sub
To Remove a specific reference:
Use something like:
Call ActiveWorkbook.VBProject.References.Remove(oReference)
and you can get the oReference from:
Private Function GetReferenceFromPath(ByVal FilePathName As String) As Object
'////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Dim oFs As Object, oReferenceS As Object, oReference As Object
Dim sFileName As String, sRefFileName As String
'////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Set oFs = Interaction.CreateObject("Scripting.FileSystemObject")
sFileName = oFs.GetFileName(FilePathName)
Set oReferenceS = ActiveWorkbook.VBProject.References
For Each oReference In oReferenceS
sRefFileName = oFs.GetFileName(oReference.FullPath)
If StrComp(sFileName, sRefFileName, vbTextCompare) = 0 Then
Set GetReferenceFromPath = oReference
End If
Next
End Function
Public Sub RemoveReference()
On Error GoTo EH
Dim RefName As String
Dim ref As Reference
RefName = "Selenium"
Set ref = ThisWorkbook.VBProject.References(RefName)
ThisWorkbook.VBProject.References.Remove ref
Exit Sub
EH:
'If an error was encountered, inform the user
Select Case Err.Number
Case Is = 9
MsgBox "The reference is already removed"
Exit Sub
Case Is = 1004
MsgBox "You probably do not have to have Trust Access To Visual Basic Project checked or macros enabled"
Exit Sub
Case Else
'An unknown error was encountered
MsgBox "Error in 'RemoveReference'" & vbCrLf & vbCrLf & Err.Description
End Select
End Sub
P.S It is not possible to remove A MISSING/ broken references programmatically after MISSING occurs, only before it happens or manually after it happens. Most cases of MISSING/ broken references are caused because the type library has never before been registered on that system.
See How to Remove Reference programmatically?
I had a broken reference problem with a large number of Excel spreadsheets when I uninstalled Flash (which for some unknown reason I had included as a reference).
I got round the problem as follows:
BE CAREFUL BECAUSE THIS INVOLVES A REGISTRY HACK AN IS COMPLICATED.
MAKE A BACKUP OF REGISTRY BEFORE HACKING.
I wrote VBA code to find the Guid of the broken reference.
I used Regedit to insert a DUMMY TypeLib entry as follows:
D27CDB6B-AE6D-11CF-96B8-444553540000 was the Guid of the Broken Reference.
HKEY_CLASSES_ROOT\TypeLib{D27CDB6B-AE6D-11CF-96B8-444553540000}
HKEY_CLASSES_ROOT\TypeLib{D27CDB6B-AE6D-11CF-96B8-444553540000}\1.0 Adobe Acrobat 7.0 Browser Control Type Library 1.0
HKEY_CLASSES_ROOT\TypeLib{D27CDB6B-AE6D-11CF-96B8-444553540000}\1.0\0
HKEY_CLASSES_ROOT\TypeLib{D27CDB6B-AE6D-11CF-96B8-444553540000}\1.0\0\win32 C:\Program Files (x86)\Common Files\Adobe\Acrobat\ActiveX\AcroPDF.dll
HKEY_CLASSES_ROOT\TypeLib{D27CDB6B-AE6D-11CF-96B8-444553540000}\1.0\FLAGS 0
HKEY_CLASSES_ROOT\TypeLib{D27CDB6B-AE6D-11CF-96B8-444553540000}\1.0\HELPDIR C:\Program Files (x86)\Common Files\Adobe\Acrobat\ActiveX\
I based the above on another TypeLib entry.
Then I wrote VBA code to read each Reference.Guid in turn and if the Guid matched {D27CDB6B-AE6D-11CF-96B8-444553540000} to remove the reference using References.Remove Reference.
Code for doing this is available all over the forums so I won't repeat here.
After modifying all the affected Workbooks I reinstated the saved registry.
Hope this works for you.
BE CAREFUL BECAUSE THIS INVOLVES A REGISTRY HACK AN IS COMPLICATED.
MAKE A BACKUP OF REGISTRY BEFORE HACKING.

CreateObject function for "Shell.Application" vs "InternetExplorer.Application"

In an answer to this post VBA Macro For Already Open IE Window CreateObject() is used twice, once with "Shell.Application" and another time with "InternetExplorer.Application".
The first time it is used to reference an already open or already active window.
The second time it is used to create a new browser window.
Sub GetIE_LateBinding()
Dim IE As Object
With CreateObject("Shell.Application").Windows
If .Count > 0 Then
' Get IE
Set IE = .Item(0) ' or .Item(.Count - 1)
Else
' Create IE
Set IE = CreateObject("InternetExplorer.Application")
IE.Visible = True
End If
IE.Navigate "http://support.microsoft.com/kb/q176792/"
Set IE = Nothing
End With
End Sub
I tried the following code. It results in a new IE window each time even when there is already an instance open.
Public Sub Trial()
Dim IE As Object
Set IE = CreateObject("InternetExplorer.Application")
IE.Visible = True
End Sub
I can understand the behavior of "InternetExplorer.Application" - creating new IE application instance each time hence new window. But "Shell.Application" seems to fetch already open shell application instance and hence, we are able to browse the already open IE windows. Seems a bit confusing. What am I missing?
Note: I am guessing something related to class_initialize() for "Shell.Application".
IE and Explorer used to be the same program which is why Shell.Application lists open IE Windows. It is only doing this because they used to both be Explorer windows. You are lucky this meets your needs.
CreateObject is used to create a new object.
GetObject(filename) connects to an open file, and if not open, opens it.
Set xlBook = GetObject("C:\Users\David Candy\Documents\Super.xls")
msgbox xlbook.name
GetObject("","shell.application") connect to an existing object and fails if it's not running.
Set GetExcelApp = GetObject("", "Excel.Application")
Msgbox GetExcelApp
Don't get too hung up on objects. Many objects are just function libraries like Shell.Application. You created a new function library object.
From COM help.
**Component Automation**
Mapping Visual Basic to Automation
Visual Basic provides full support for Automation. The following table lists how Visual Basic statements translate into OLE APIs.
Visual Basic statement OLE APIs
CreateObject (ProgID)
CLSIDFromProgID
CoCreateInstance
QueryInterface to get IDispatch interface.
GetObject (filename, ProgID)
CLSIDFromProgID
CoCreateInstance
QueryInterface for IPersistFile interface.
Load on IPersistFile interface.
QueryInterface to get IDispatch interface.
GetObject (filename)
CreateBindCtx creates the bind context for the subsequent functions.
MkParseDisplayName returns a moniker handle for BindMoniker.
BindMoniker returns a pointer to the IDispatch interface.
Release on moniker handle.
Release on context.
GetObject (ProgID)
CLSIDFromProgID
GetActiveObject on class ID.
QueryInterface to get IDispatch interface.
Dim x As New interface
Find CLSID for interface.
CoCreateInstance
QueryInterface
© Microsoft Corporation. All rights reserved.

Access autocad object properties without opening it by VBA

I have been using folder browser for VBA, I could paste the code of it, but bottom line is that I get returned file name as a string.
Is there any way to access drawing properties (i.e number of layouts) without open?
Public Sub TestFileDialog()
dwgname = FileBrowseOpen("C:", "*", ".dwg", 1) 'dwgname is typeof string
End Sub
Its only the first step (use of FileBrowseOpen function is shown, but also i can use FolderBrowse and collect all .dwg inside of folder),actually i had in mind to batch export all layouts of selected .dwgs to currenty open one. Is there any chance for that?
To effectively read a .dwg file you'll need to open AutoCAD, otherwise the information is not accessible. Some properties may be, such as author, but not number of layouts...
But you can use AutoCAD Console (accoreconsole.exe) to run a headless AutoCAD and use APIs to read any information you need. This is really fast for reading lot's of files and the user will not see it running (but it needs to be installed anyway).
http://aucache.autodesk.com/au2012/sessionsFiles/3338/3323/handout_3338_CP3338-Handout.pdf
you could stay in VBA and use ObjectDBX
it leads to a very similar approach as accoreconsole.exe on in .NET does, i.e you won't see any drawing open in UI since it works on the database itself
It requires adding library reference (Tools->References) to "AutoCAD/ObjectDBX Common XX.Y Type Library", where "XX.Y" is "19.0" for AutoCAD 2014
a minimal functioning code is
Sub main()
Dim myAxDbDoc As AxDbDocument
Dim FullFileName As String
FullFileName = "C:\..\mydrawing.dwg" '<== put here the full name of the file to be opened
Set myAxDbDoc = AxDb_SetDrawing(FullFileName)
MsgBox myAxDbDoc.Layers.Count
End Sub
Function AxDb_SetDrawing(FullFileName As String) As AxDbDocument
Dim DBXDoc As AxDbDocument
Set DBXDoc = Application.GetInterfaceObject("ObjectDBX.AxDbDocument.19") '<== place correct AutoCAD version numeber ("19" works for AutoCAD 2014)
On Error Resume Next
DBXDoc.Open FullFileName
If Err <> 0 Then
MsgBox "Couldn't open" & vbCrLf & vbCrLf & FullFileName, vbOKOnly + vbCritical, "AxDB_SetDrawing"
Else
Set AxDb_SetDrawing = DBXDoc
End If
On Error GoTo 0
End Function
Still, you must have one AutoCAD session running from which make this sub run! But you should have it since talked about "currently open" drawing

Reading, Writing and controlling Autocad using external VBA

I'm using MS-Access 2010 and Autocad 2012 64bit and work in manufacturing.
I want to be able to at the very least, populate fields in a title block, better still I would like to use data in my access database to write data into a sheet set (the current system works by reading the sheet set values such as sheet title and number into my title block).
The following code is all I have at the moment and it will open autocad and write the date into the command line.
Private Sub OpenAutocad_Click()
Dim CadApp As AcadApplication
Dim CadDoc As AutoCAD.AcadDocument
On Error Resume Next
Set CadApp = GetObject(, "AutoCAD.Application")
If Err.Number <> 0 Then
Set CadApp = CreateObject("AutoCAD.Application")
End If
On Error GoTo 0
CadApp.Visible = True
CadApp.WindowState = acMax
Set CadDoc = CadApp.ActiveDocument
CadDoc.Utility.Prompt "Hello from Access, the time is: " & TheTime
Set CadApp = Nothing
End Sub
I have no idea where to go from here. What are the commands to control the sheet set manager and change data, and can the .dst file be edited without even opening up autocad? is there a list of all available autocad vba commands and functions?
If you are declaring CadApp as AcadApplication you must have added a reference to AutoCAD.
That means you should be able to see the object model using your Object Browser in your VBA IDE. No?
There is also a very helpful site www.theswamp.org which has a whole section devoted to AutoCAD VBA.
If I understand your question correctly, you want to automate filling attributes in a drawing title blocks (such as title, drawer, part number, etc) right from MS Access.
Your code can access the Autocad command line already, but Autocad doesn't seem to have the exact command for filling drawing attribute. (command list)
So looks like you need to fill the attributes programatically using the COM API.
The following question appears to be relevant with yours and the accepted answers does provide a sample code:
Is it possible to edit block attributes in AutoCAD using Autodesk.AutoCAD.Interop?
Note that in that question the asker was developing a standalone application in C# .NET, where as you will be using VB Automation from MS Access. Shouldn't be too different since the Component Object Model (COM) being used is the same.
What are the commands to control the sheet set manager and change data and can the .dst file be edited without even opening up autocad?
(sorry can't post more than 2 links)
docs.autodesk.com/ACD/2010/ENU/AutoCAD%202010%20User%20Documentation/files/WS1a9193826455f5ffa23ce210c4a30acaf-7470.htm
No mention about data change, though.
is there a list of all available autocad vba commands and functions?
Yes.
%ProgramFiles%\Common Files\Autodesk Shared\acad_aag.chm - Developer's Guide
%ProgramFiles%\Common Files\Autodesk Shared\acadauto.chm - Reference Guide
Online version:
help.autodesk.com/cloudhelp/2015/ENU/AutoCAD-ActiveX/files/GUID-36BF58F3-537D-4B59-BEFE-2D0FEF5A4443.htm
help.autodesk.com/cloudhelp/2015/ENU/AutoCAD-ActiveX/files/GUID-5D302758-ED3F-4062-A254-FB57BAB01C44.htm
More references here:
http://usa.autodesk.com/adsk/servlet/index?id=1911627&siteID=123112
:) Half the way gone ;)
If you has a open autocad with a loaded drawing you can access the whole thing directly.
Sub block_set_attribute(blo As AcadBlockReference, tagname, tagvalue)
Dim ATTLIST As Variant
If blo Is Nothing Then Exit Sub
If blo.hasattributes Then
tagname = Trim(UCase(tagname))
ATTLIST = blo.GetAttributes
For i = LBound(ATTLIST) To UBound(ATTLIST)
If UCase(ATTLIST(i).TAGSTRING) = tagname Or UCase(Trim(ATTLIST(i).TAGSTRING)) = tagname & "_001" Then
'On Error Resume Next
ATTLIST(i).textString = "" & tagvalue
Exit Sub
End If
Next
End If
End Sub
Sub findtitleblock(TITLEBLOCKNAME As String, attributename As String,
attributevalue As String)
Dim entity As AcadEntity
Dim block As acadblcck
Dim blockref As AcadBlockReference
For Each block In ThisDrawing.BLOCKS
For Each entity In block
If InStr(LCase(entity.objectname), "blockref") > 0 Then
Set blockref = entity
If blockref.effectivename = TITLEBLOCKNAME Then
Call block_set_attribute(blockref, attributename, attributevalue)
exit for
End If
End If
End If
Next
Next
End Sub
call findtitleblock("HEADER","TITLE","Bridge column AXIS A_A")
So assume you has a title block which has the attribute TITLE then it will set the Attribute to the drawing name. it mioght also possible you has to replace the thisdrawing. with your Caddoc. I usually control Access and Excel form autocad and not vice versa ;)
consider also to use "REGEN" and "ATTSYNC" if "nothing happens"
thisdrawing.sendcommens("_attsync" 6 vblf )