How do I search an ActiveX/COM object for a method? - dll

I have an ActiveX/COM DLL. It contains many methods and properties. I would like to be able to ask it if it has a particular symbol, as per the following snippet:
If HasMethod( "StdLib.DLL", "ReadFileE" ) Then
...
End If
Is there a way to do this from, say, VBScript or JScript? If not, where do I go to get the information I need?

After Googling around not quite finding what I wanted, I remembered the Edanmo site which got me thinking about TLBINF32.DLL, downloading Microsoft's TLBINF32.CHM and reading up on GetMembersWithSubStringEx. Below is the implementation of it (done in VB6 with a reference to TLBINF32.DLL), some demo VBScript and output, and the wrapping of that functionality in some VBA.
Public Function SearchTLIMethodsAndProperties(sTypelib As Variant, sSymbol As Variant) As Variant
Dim SI As SearchItem
Dim aResults As Variant
Dim bFound as boolean
Dim Groups(1) As InvokeKinds
Groups(0) = INVOKE_FUNC Or INVOKE_PROPERTYGET Or _
INVOKE_PROPERTYPUT Or INVOKE_PROPERTYPUTREF
ReDim aResults(0)
bFound = False
With TypeLibInfoFromFile(sTypelib)
.SearchDefault = tliStClasses Or tliStEvents
For Each SI In .GetMembersWithSubStringEx(sSymbol, Groups)
bFound = True
arr.AAdd_PostIncrement aResults, SI.Name
Next
End With
if bFound then
ReDim Preserve aResults(UBound(aResults) - 1)
end if
SearchTLIMethodsAndProperties = aResults
End Function
VBScript demo. The above code was included in my StdLib DLL in the Registry coclass.
Dim O, R
Set O = CreateObject("Std.Registry")
Set R = CreateObject("Std.Arrays")
WScript.Echo R.ShowStructure( O.SearchTLIMethodsAndProperties( "MSSCRIPT.OCX",""))
Output from the demo (script was run in SciTE).
>cscript "C:\foo\foo.vbs"
{Add,AddCode,AddObject,AllowUI,Clear,CodeObject,Column,Count,Description,Error,Eval,ExecuteStatement,HasReturnValue,HelpContext,HelpFile,Item,Language,Line,Modules,Name,NumArgs,Number,Procedures,Reset,Run,SitehWnd,Source,State,Text,Timeout,UseSafeSubset}
>Exit code: 0
Finally, the VBA code. A cell has a symbol in it and this routine finds it or returns an error string.
Public Function LookupSymbol(sSym As String) As String
Dim aRes As Variant
aRes = reg.SearchTLIMethodsAndProperties("MSSCRIPT.OCX", sSym)
Dim i As Integer
LookupSymbol = "!!NotFound!!"
For i = 0 To UBound(aRes)
If LCase$(aRes(i)) = LCase$(sSym) Then
LookupSymbol = sSym
Exit For
End If
Next
End Function
Looking back on it now, I think I might pass in the path to the DLL/OCX as the first parameter.

I have used Microsofts interactive OLE/COM-Object viewer to find mehods and their parameters in ActiveX-DLLs. Maybe looking at the source code of the viewer will lead you in the right direction: MSDN OleView sample

If you want to do it programmatically - I'm not aware of a simple way to do that. Anyway, if you really need to (and if your programming language is capable enough) - you can query the type library (refer to ITypeLib description somewhere at http://msdn.microsoft.com/en-us/library/ms221549.aspx).
Also, if you already have an IDispatch pointer - you might consider using its services to dynamically enumerate methods supported by the interface (refer to IDispatch description in MSDN).

Related

why is a string variable strVar not the same as cStr(strVar)?

Why does a string variable need to be enclosed in a cStr() conversion to be used as a key string for the CreateObject("WScript.Shell").SpecialFolders collection?
Demonstration of this absurdity(?):
Sub sdfgdfsg()
Const strCon_SpecialFolderName As String = "MyDocuments"
Dim strVar_SpecialFolderName As String
strVar_SpecialFolderName = "MyDocuments"
Debug.Print CreateObject("WScript.Shell").SpecialFolders(strCon_SpecialFolderName) ' CORRECT
Debug.Print CreateObject("WScript.Shell").SpecialFolders(strVar_SpecialFolderName) ' WRONG! (it returns the Desktop path instead)
Debug.Print CreateObject("WScript.Shell").SpecialFolders(CStr(strVar_SpecialFolderName)) ' CORRECT
Debug.Print CreateObject("WScript.Shell").SpecialFolders("MyDocuments") ' CORRECT
End Sub
I read the documentation about the index argument for a collection without finding an answer.
The WScript library was designed to be used from scripting languages such as VBScript which use Variants. The SpecialFolders.Item that you are calling expects a Variant containing a string, too.
The result you are seeing appears to come from the fact that the library is not able to read the Variant-wrapped string value VB passes, and does something wrong instead. You can achieve the same result with
Dim strVar_SpecialFolderName As String
strVar_SpecialFolderName = vbNullString
'Returns C:\Users\Public\Desktop
Debug.Print CreateObject("WScript.Shell").SpecialFolders(strVar_SpecialFolderName)
For reasons I don't fully understand, there sometimes is a problem with passing Variant-wrapped data from VBA to an external object. I speculate that it may have something to do with the presence or absence of the VT_BYREF flag in the Variant that VB(A) produces, and it produces it differently for constants, local variables and temporaries.
I believe this to be a problem on the receiving side, not in VBA.
Workarounds include:
Declaring the variable as Variant to begin with:
Dim strVar_SpecialFolderName As Variant
strVar_SpecialFolderName = "MyDocuments"
Debug.Print CreateObject("WScript.Shell").SpecialFolders(strVar_SpecialFolderName)
Forcing an evaluation which turns the local variable into a temporary (that is what your CStr does, too) which apparently changes how VB packs it into a Variant:
Dim strVar_SpecialFolderName As String
strVar_SpecialFolderName = "MyDocuments"
Debug.Print CreateObject("WScript.Shell").SpecialFolders((strVar_SpecialFolderName))

How to get a container header id to a variable and passed to vba to write a new header?

I have created this script to generate a container that I want to use it to generate containers It works fine but I also need to be able to give the containers a custom header. As you can see I tried to capture the shape id in a variable so I could use the variable to get the shape Id for the container. Nevertheless, I cannot get the shape id or assign one statically I also found out that the container has more than one shape ID. How do I Identify the ID for the header portion. I also need to be able to drop shapes in the container. I followed Microsoft instructions and tried using
vsoContainerShape.ContainerProperties.AddMember vsoShape,
visMemberAddExpandContainer
However that doesn’t work.
Sub Add_Container()
Dim DiagramServices As Integer
DiagramServices = ActiveDocument.DiagramServicesEnabled
ActiveDocument.DiagramServicesEnabled = visServiceVersion140 +
visServiceVersion150
Dim visapp As Visio.Application
Dim vlan30 As Visio.Document
Dim node As Visio.Shape
Dim vlan30id As Integer
Application.Documents.OpenEx(Application.GetBuiltInStencilFile(visBuiltInStencilContainers, visMSUS), visOpenHidden)
Application.Windows.ItemEx("container.vsdm").Activate 'need to activate
Application.ActiveWindow.Page.DropContainer vlan30.Masters.ItemU("Classic"), Nothing
vlan30id = vlan30.ID
Debug.Print vlan30id
Dim v30chars As Visio.Characters
Set v30chars = Application.ActiveWindow.Page.Shapes.ItemFromID(vlan30id).Characters
v30chars.Begin = 0
v30chars.End = 7
v30chars.Text = "Vlan_30"
vlan30.Close
ActiveWindow.DeselectAll
'Restore diagram services
ActiveDocument.DiagramServicesEnabled = DiagramServices
End Sub
I need to be able to get the shape id for the heading of the containers and stored in a variable so I can use the variable for passing the argument in the ItemFromID. Thanks
First things first: your exact question was already answered her: http://visguy.com/vgforum/index.php?topic=6787.0
I packaged the whole code into a function, calling the function will drop a classic container on the page you passed as argument and fill the caption with the passed caption argument. The return value is the dropped Container, bottom function shows how to use the function and add shapes to the container.
'#Folder("ExampleDropContainer")
Option Explicit
Public Function DropContainerWithCaption(pg As Visio.Page, caption As String) As Visio.Shape
Dim vsPg As Visio.Page
Set vsPg = pg
Dim vsStencilName As String
vsStencilName = Application.GetBuiltInStencilFile(visBuiltInStencilContainers, visMSUS)
Dim vsStencil As Visio.Document
Set vsStencil = Application.Documents.OpenEx(vsStencilName, visOpenHidden)
Dim vsMas As Visio.Master
Set vsMas = vsStencil.Masters.ItemU("Classic")
'If you already had the shapes you want to have inside the continer you can replace "Nothing" with them.
Dim droppedContainer As Visio.Shape
'Set droppedContainer = vsPg.DropContainer(vsMas, Nothing)
'Using page.Drop circumvents some issues when a shape already occupies the space where the shape is to be dropped.
Set droppedContainer = vsPg.Drop(vsMas, 0, 0)
droppedContainer.Text = caption
Set DropContainerWithCaption = droppedContainer
End Function
Sub TestExample()
Dim newContainer As Visio.Shape
Set newContainer = DropContainerWithCaption(ActivePage, "Bananas")
'Example on how to add a Shape to the container, someShape is a visio.shape object
'newContainer.ContainerProperties.AddMember someShape
End Sub
You seem to have posted a lot of questions concerning the same or similar problems lately, most of which are quite basic once you get to know VBA. You should read up a bit, especially on the concept of return values of functions. Also be aware that most questions regarding programming in Visio are exactly the same in VBA for Excel, only the interaction with the document/workbook is sometime different. Many answers can be found when searching properly
Some good links are:
How to avoid Select Probably the most read article concerning VBA in Stackoverflow
ExcelMacroMastery Good Resource, a lot on the basics of programming in VBA
Chip Pearson's Website Lots of great and deep information
RubberduckVBA Blog Fantastic resource with many examples on how to bring VBA-Code up to modern standards. He's also active here on SO
The first two links are a must-read IMHO, the other two are great if you want to dive deeper into VBA.

Reference to Window Object in VBA

I have a problem referencing a windows object in VBA. it throws the following error: "Error 5 (Invalid procedure call or argument). I cannot find the cause, because I see no programming error.
Public Sub TestWindowhandle()
Dim lResult As Long
Dim objShell, wins, winn
Dim IE_Count As Long, i As Long, This_PID As Long
On Error GoTo TestWindowhandle_Error
Set objShell = CreateObject("Shell.Application")
Set wins = objShell.Windows
IE_Count = wins.Count
For i = 0 To (IE_Count - 1)
Set winn = wins.Item(i)
Next i
On Error GoTo 0
Exit Sub
TestWindowhandle_Error:
MsgBox "Error " & Err.Number & " (" & Err.Description & ") in line " & Erl & " in procedure TestWindowhandle of Module Module1"
Stop
End Sub
Something odd with that interface, it seems to only work with a copy of the control variable so:
Set winn = wins.Item(i + 0)
or
Set winn = wins.Item((i))
I believe here is what is happening.
The Item method accepts a Variant parameter.
When calling an external method that accepts a Variant parameter, VB likes to create and pass Variants that provide the value by reference - that is, with the VT_BYREF flag set.
However VB does not set this flag when sending out intermediate results (temporaries, not stored in a variable), which makes sense because even if the called method updates the value, no one will be able to see it.
So when you call .Item(i), VB sends out a Variant of type VT_I4 | VT_BYREF, and when you call .Item(i + 0), a Variant of type VT_I4 is dispatched, without the VT_BYREF.
In most situations the difference is not significant because VARIANT-aware methods should be able to cope with either. However this particular method does different things depending on exactly which VT_ it receives, and because of this it is explicitly willing to reject any VT_s other than the three accepted ones. So in order to call it you need to trick VB into removing the byref flag.
Interestingly, when you declare the variable as Variant, VB still dispatches a VT_VARIANT | VT_BYREF, but the method seems to support this situation and correctly resolves to the pointed-to inner Variant that has the non-reference type of VT_I4.
Note this has nothing to do with VB's ByVal/ByRef - this is about the internal structure of the VARIANT data type.

Can VBA enumerate a COM object's methods or fields?

I have a COM object (built with C#.NET) I'm using in VBA (Excel) and it would be really nice enumerate the COM object's fields and reference them automatically. In .NET, this could be done with reflection. Is there any way to do this in VBA?
So, instead of
Dim x As MyCOMObject
Set x = New MyCOMObject
x.f1 = 1
x.f2 = 2
x.f3 = 3
Something more like:
Dim x As MyCOMObject
Set x = New MyCOMObject
For i = 0 to COMFieldCount(x) - 1
SetCOMField(x, GetCOMFieldName(i), i+1)
Next i
You will probably need to refine this code a bit, but it does roughly what you are looking for.
First, you need to add reference to "Typelib information", TLBINF32.dll. I'm not sure if this is a part of Windows or came with some of the numerous SDKs I have installed on my machine, but it is in the System32 folder.
I am assuming that you are setting properties of a COM object, so you will be calling "property put" functions to set the values of your object. You may need to check for the datatypes of those properties, I haven't done any datatype conversion in my code.
The code looks like this:
'Define the variables
Dim tliApp As TLI.TLIApplication
Dim typeinfo As TLI.typeinfo
Dim interface As TLI.InterfaceInfo
Dim member As TLI.MemberInfo
'Initialize typelib reflector
Set tliApp = New TLI.TLIApplication
'Get the type information about myObject (the COM object you want to process)
Set typeinfo = tliApp.ClassInfoFromObject(myObject)
'Set all properties of all the object's interfaces
For Each interface In typeinfo.Interfaces
For Each member In interface.Members
'If this is a "property put" function
If member.InvokeKind = INVOKE_PROPERTYPUT Then
'Invoke the mebmer and set someValue to it.
'Note that you'll probably want to check what datatype to use and do some more error checking
CallByName myObject, member.Name, VbLet, someValue
End If
Next
Next

How can I evaluate a string into an object in VBA?

In my previous question, How do I assign a value to a property where the property name is supplied at runtime in VBA?, I learned to use CallByName to set a property in a class at run time.
This time, however, I'm trying to figure out how to get an object at run time from a string.
For example, let's say I have a string with the following data: Worksheets("RAW DATA").Range("A1").QueryTable.
Here's what I might try to do where the data above is the input for strParam below:
Function GetObject(strParam As String) As Object
GetObject = SomeFunction(strParam)
End Function
In this case, GetObject should return a QueryTable when evaluated against Worksheets("RAW DATA").Range("A1").QueryTable. Is there anything in VBA that could take the place of SomeFunction from the example above?
Active Scripting Engine can help you. Instantiate ScriptControl ActiveX, use .AddObject() method to add reference to Excel's Application object to the script control's execution environment, set the third parameter to True to make all Application's members accessible too. Then just use .Eval() method to evaluate any property or method, which is the Application's member. The example below shows evaluation of Worksheets() property:
Sub TestQueryTable()
Dim objQueryTable As QueryTable
Dim strEvalContent As String
strEvalContent = "Worksheets(""RAW DATA"").Range(""A1"").QueryTable"
Set objQueryTable = EvalObject(strEvalContent)
objQueryTable.Refresh
MsgBox objQueryTable.Connection
End Sub
Function EvalObject(strEvalContent As String) As Object
With CreateObject("ScriptControl")
.Language = "VBScript"
.AddObject "app", Application, True
Set EvalObject = .Eval(strEvalContent)
End With
End Function
If you are on 64-bit Office, this answer may help you to get ScriptControl to work.
This time you're out of luck. There is no VBA equivalent of eval (not in Excel anyway...there is in Access VBA).
(Application.Evaluate() evaluates strings as Excel expressions, not as VBA code.)
There's the "Evaluate" method (or [ ] brackets). I don't think it will do exactly what you expect - as in run VBA code found in a string. You can look it up in the VBA help menu.