2

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
Richie10
  • 45
  • 1
  • 6
  • The error is thrown from: Set winn = wins.Item(i) – Richie10 Jul 25 '15 at 09:34
  • Have you tried to replace `wins.Item(i)` with `wins(i)` or `wins.Items(i)`? – Maciej Los Jul 25 '15 at 09:42
  • Both suggestions also throw an error – Richie10 Jul 25 '15 at 10:06
  • When observing the "locals" window in the VBE the objShell and wins object are created correctly. Also the reference wins.count work well. The .item property is also available in the local window. I cannot understand why it does not work. – Richie10 Jul 25 '15 at 10:08
  • https://msdn.microsoft.com/en-us/library/windows/desktop/bb773938%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396 – Maciej Los Jul 25 '15 at 10:33
  • Shouldn't you be setting the var to the HWND? –  Jul 25 '15 at 10:33
  • @Jeeped: I cannot set the var to the HWND if I cannot set the wins.Item(i) object. HWND is a propery from wins.Item(i): wins.Item(i).HWND. I tried that as well but throws the same error. In the "locals" windows it can be found in the wins object. The problem is setting the variable wins.Items(i) – Richie10 Jul 25 '15 at 11:17
  • In that case shouldn't you ne looking at the [ShellWindows object](https://msdn.microsoft.com/en-us/library/windows/desktop/bb773974(v=vs.85).aspx) rather than the windows? See [Shell Objects for Scripting and Microsoft Visual Basic](https://msdn.microsoft.com/en-us/library/windows/desktop/bb773938%28v=vs.85%29.aspx). –  Jul 25 '15 at 11:25
  • @Jeeped: Shell Object for Scripting and Microsoft Visual Basic contains the ShellWindows object; it is the same :-) – Richie10 Jul 25 '15 at 11:40
  • when I replace "winn=wins.Item(i)" by "winn=wins.Item(0)" then it works. I looks like the variable in the reference is not accepted. – Richie10 Jul 25 '15 at 13:07

2 Answers2

1

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))
Alex K.
  • 171,639
  • 30
  • 264
  • 288
  • `Item` expects a `Variant` as an index, and with late binding VB is not able to correctly figure it out. This trick gives it a nudge, same would do declaring `i` as variant. I believe some byref passing is happening, and this causes byval. – GSerg Jul 25 '15 at 14:20
  • Yes, the problem has been solved. All the solutions work: adding 0 in the reference, add parentheses in the reference and declaring i as variant. It is very weird / odd issue. It is really a good thing we have stackoverflow community saving me a lot of time. Thanks for all support! – Richie10 Jul 26 '15 at 06:44
0

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.

GSerg
  • 76,472
  • 17
  • 159
  • 346
  • quote "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." I do not understand this. In my code I assign a variable with set command: Set winn = wins.Item(i). It is the reference between the parentheses making the problem, which is not logical. Placing an operation in the expression "solves" the problem. As you already mentioned VB does different thing with the same result. It looks like a variant is required! – Richie10 Jul 27 '15 at 01:11
  • I'm talking about parameters, not return values. `i` is [an lvalue](https://en.wikipedia.org/wiki/Value_%28computer_science%29#lrvalue), whereas `i + 0`, [`(i)`](http://stackoverflow.com/a/8070104/11683) or `CLng(i)` are [rvalues](https://en.wikipedia.org/wiki/Value_%28computer_science%29#lrvalue). When you call `Item()`, VB creates a Variant with `VT_BYREF` set and passes that for the parameter. When you call `Item()`, VB again creates a Variant to pass for the parameter, but does not set the `VT_BYREF`. – GSerg Jul 27 '15 at 08:17
  • Normally this does not make a difference, but this particular method, `Item()`, specifically rejects its parameter if `VT_BYREF` is set, because it wants to do different things depending on the flags set in the Variant and does not consider `VT_BYREF` a valid flag. – GSerg Jul 27 '15 at 08:21
  • I can understand an expression creats activity in memory. But what I do not understand, why i is an Ivalue and (i) is a Rvalue? No operation is done, only (). I can understand i+0 us a rvalue as there is an operation done. – Richie10 Jul 29 '15 at 04:10
  • This is [how VB operates](http://stackoverflow.com/a/10262247/11683) because the language designers made it this way. Default way of passing parameters is `ByRef`, and for `ByRef` you need your data types to match exactly (you can't pass `Long` for `Variant` with `ByRef`). Very often it prevents you from writing simple short code (with all the data type conversions done implicitly by VB), so you can use parentheses to turn a variable to an expression. See https://msdn.microsoft.com/en-us/library/aa263527%28VS.60%29.aspx, "Passing Arguments By Reference." – GSerg Jul 29 '15 at 08:13