7

Background

Recently I answered a question which involved looking at a file's properties. Eventually the code I put up worked fine, but there is one thing about it that got me puzzled.

Problem

There are two specific lines where I wanted to replace a (to me what looks like) a string, with a variable, more specifically, try the following:

Sub TestForSO()

Dim oDir As Object: Set oDir = CreateObject("Shell.Application").Namespace("C:\Users\...\")
Debug.Print oDir.GetDetailsOf(oDir.Items, 1)

End Sub

Replace the pathname to a directory which includes an excel file, and it should return the property value just fine.

Now when I try to replace the full path with a variable the following throws an "Runtime Error 91: Object variable or with block variable not set" on the debug.print line:

Sub TestForSO()

Dim MainPath As String: MainPath = "C:\Users\...\"
Dim oDir As Object: Set oDir = CreateObject("Shell.Application").Namespace(MainPath)
Debug.Print oDir.GetDetailsOf(oDir.Items, 1)

End Sub

Solution

A bit peculiar to me that the following did work:

Sub TestForSO()

Dim MainPath As String: MainPath = "C:\Users\...\"
Dim oDir As Object: Set oDir = CreateObject("Shell.Application").Namespace(CStr(MainPath))
Debug.Print oDir.GetDetailsOf(oDir.Items, 1)

End Sub

I do not understand the difference per se as the code below will give the same result through "Watches":

Sub test()

Dim check1 As String, check2 As String

check1 = "Hello"
check2 = CStr("Hello")

End Sub

enter image description here

Question

Does somebody understand why the string variable on itself was not enough and would throw an error? Why would adding Cstr() make the code work when seemingly it's the same data type?

JvdV
  • 70,606
  • 8
  • 39
  • 70
  • 2
    Can you declare `MainPath` as a `Variant` and remove the `CStr` and see if it works? When I was answering the same question I ran into issues when I declared my path as a `String` as `NameSpace` expects a `Variant`. But the fact that it accepts it using `CStr` has got me baffled. – Tom Sep 05 '19 at 08:35
  • 3
    Just a shot in the dark, but function `Cstr` returns a variant of subtype string (http://www.csidata.com/custserv/onlinehelp/vbsdocs/vbs89.htm). If you define `Mainpath` as Variant, it will work. And it works with `Cstr` because it returns actually a Variant. – Foxfire And Burns And Burns Sep 05 '19 at 08:37
  • @FoxfireAndBurnsAndBurns That would explain it – Tom Sep 05 '19 at 08:37
  • 2
    I don't think @FoxfireAndBurnsAndBurns is correct. He linked to the **VBScript** help for `CStr()`. In VBScript, everything is a `Variant`. **VBA** however is a typed language and looking at the help for [VBA CStr](https://docs.microsoft.com/en-us/office/vba/language/concepts/getting-started/type-conversion-functions), it states that it returns the data type `String`. Declaring it as `Variant` works, because that's what [Namespace](https://docs.microsoft.com/en-us/windows/win32/shell/shell-namespace) expects. Also: VBA does some weird conversions behind the scenes when passing parameters. – Hel O'Ween Sep 05 '19 at 09:02
  • 2
    That's why I said just a shot in the dark. The clue in the link provided by @HelO'Ween is the line *This technique is consistent with the conversion of all other intrinsic types to their equivalent Variant subtypes* So reading that I understand that all conversions returns a Variant with a subtype. And because `Namespace` expects a Variant, it works. Anyways, I did not found anything official and clear, sorry about that, but I think it's clearer what I say, even if it's a clumsy theory, that *VBA does some weird conversions* – Foxfire And Burns And Burns Sep 05 '19 at 09:13
  • It's funny if I reference shell32.dll and change `Object` to `Shell` it works. Maybe it depends somehow on early/late binding? – scor4er Sep 05 '19 at 11:34
  • @FoxfireAndBurnsAndBurns, your theory seems supported through VBA. If I do not declare check2, the watch on check2 will return `Variant\String` on this line: `check2 = Cstr(check1)`, even when check1 has a confirmed string type value. Declaring MainPath as variant in that case does the job (or keeping `Cstr()`). Seems like you could post an answer. – JvdV Sep 05 '19 at 12:05

1 Answers1

3

According to documentation about Namespace, it needs a parameter that must be a Variant or can be a string that specifies the path of the folder.

That explains why these 2 methods work with no problems:

Set oDir = CreateObject("Shell.Application").Namespace("C:\Users\...\ 'string path

Or defining a Variant variable:

Dim MainPath As Variant: MainPath = "C:\Users\...\"
Dim oDir As Object: Set oDir = CreateObject("Shell.Application").Namespace(CStr(MainPath))

But defining MainPath as string causes error Runtime Error 91: Object variable or with block variable not set

OP found a solution. If MainPath declared as string, and combined with Cstr, the code works.

It's just a theory, but some unnoficial sources (not directly related to VBA) mention that Cstr converts the value to a variant with a subtype.

http://www.csidata.com/custserv/onlinehelp/vbsdocs/vbs89.htm https://docs.oracle.com/cd/E57185_01/HFMAD/ch10s06s04s03.html

Actually, the official documentation it's kind of confusing, because at first lines it says:

Each function coerces an expression to a specific data type.

and later on it says

The function name determines the return type

But if we read carefully, there is also some important information like this:

"...In general, you can document your code using the data-type conversion functions to show that the result of some operation should be expressed as a particular data type rather than the default data type..."

And also:

"...This technique is consistent with the conversion of all other intrinsic types to their equivalent Variant subtypes..."

So after doing some research and thinking about it in the last 24 hours, and reading a lot of times the previous paragraphs I've posted, I would dare to say that all conversion functions returns a Variant with a subtype. In this case, CStr does return a Variant that is being coerced to be expressed as string being string the subtype, but the data is Variant.

That would explain why doing Cstr(MainPath) makes the code works.

  • Thanks for the post. As per my last comment I've checked `check2 = Cstr(check1)` through watches and indeed a `variant/string` is returned unless you declare `check2` other than variant (or not at all). – JvdV Sep 06 '19 at 11:20