Excel VBA Shell.Namespace returns Nothing - excel

I'm trying to extract a .CAB file using Excel VBA, but I'm getting the following error:
Run-time error '91': Object variable or With block variable not set
I usually get this when I forget to use Set with an Object, but I've checked for that.
All examples I can find are variations on this theme:
Private Function DeCab(vSource, vDest) As Long
Dim objShell, objFileSource, objFileDest As Object
Set objShell = CreateObject("Shell.Application")
Set objFileSource = objShell.Namespace(vSource)
Set objFileDest = objShell.Namespace(vDest)
Call objFileDest.MoveHere(objFileSource.Items, 4 Or 16) 'Fails here
Decab = objFileDest.Items.Count
End Function
It's not failing on the Set line, but it's setting both objFileSource and objFileDest to Nothing even though I've confirmed vSource and vDest exist.
To confirm it has nothing to do with the .CAB file, I've also tried it without setting objFileSource and checking the value of objFileDest after it's set. It still returns Nothing. Why would that be? I'm on Windows 7, 64-bit, running Office 2010.

Your parameters must be submitted as Variant, not String
Sub Tester()
Dim src, dest '<< works
'Dim src As String, dest As String '<< gives the error you see
src = "D:\temp\test.zip"
dest = "D:\temp\unzip"
DeCab src, dest
End Sub
https://msdn.microsoft.com/en-us/library/windows/desktop/bb774085(v=vs.85).aspx

Tim's answer is correct. I found an alternative, as well:
Private Function DeCab(vSource, vDest) As Long
Dim objShell, objFileSource, objFileDest As Object
Set objShell = CreateObject("Shell.Application")
Set objFileSource = objShell.Namespace((vSource)) '<-extra parentheses
Set objFileDest = objShell.Namespace((vDest)) '<-extra parentheses
Call objFileDest.MoveHere(objFileSource.Items, 4 Or 16) 'Fails here
Decab = objFileDest.Items.Count
End Function
When you place an object in parentheses in VBA, it returns the default value of the object. Apparently, objShell.Namespace can't handle a pointer. It can only handle a string literal. Changing the signature to the following also works if you're passing in Strings:
Private Function DeCab(ByVal vSource, ByVal vDest) As Long

for my case. i used the shell
Set oShell = CreateObject("Shell.Application")
x= oShell.xxxx
y= oShell.Namespace(x)
in two different lines. seems like i'll need to re initialized to able to use on second line. eg
Set oShell = CreateObject("Shell.Application")
x= oShell.xxxx
y= CreateObject("Shell.Application").Namespace(x)
then only it works.

Related

Excel VBA throws "Run-time error '91': Object variable or With block variable not set" when setting a range [duplicate]

Pardon me as am a newbie in VBA.
Sometimes I use
Dim r as Range
r = Range("A1")
Other times I use
Set r = Range("A1")
What is the difference? And when should I use what?
There's no reason to use set unless referring to an object reference. It's good practice to only use it in that context. For all other simple data types, just use an assignment operator. It's a good idea to dim (dimension) ALL variables however:
Examples of simple data types would be integer, long, boolean, string. These are just data types and do not have their own methods and properties.
Dim i as Integer
i = 5
Dim myWord as String
myWord = "Whatever I want"
An example of an object would be a Range, a Worksheet, or a Workbook. These have their own methods and properties.
Dim myRange as Range
Set myRange = Sheet1.Range("A1")
If you try to use the last line without Set, VB will throw an error. Now that you have an object declared you can access its properties and methods.
myString = myRange.Value
Dim declares the variable.
Dim r As Range
Set sets the variable to an object reference.
Set r = Range("A1")
However, I don't think this is what you're really asking.
Sometimes I use:
Dim r as Range
r = Range("A1")
This will never work. Without Set you will receive runtime error #91 Object variable or With block variable not set. This is because you must use Set to assign a variables value to an object reference. Then the code above will work.
I think the code below illustrates what you're really asking about. Let's suppose we don't declare a type and let r be a Variant type instead.
Public Sub test()
Dim r
debug.print TypeName(r)
Set r = Range("A1")
debug.print TypeName(r)
r = Range("A1")
debug.print TypeName(r)
End Sub
So, let's break down what happens here.
r is declared as a Variant
`Dim r` ' TypeName(r) returns "Empty", which is the value for an uninitialized variant
r is set to the Range containing cell "A1"
Set r = Range("A1") ' TypeName(r) returns "Range"
r is set to the value of the default property of Range("A1").
r = Range("A1") ' TypeName(r) returns "String"
In this case, the default property of a Range is .Value, so the following two lines of code are equivalent.
r = Range("A1")
r = Range("A1").Value
For more about default object properties, please see Chip Pearson's "Default Member of a Class".
As for your Set example:
Other times I use
Set r = Range("A1")
This wouldn't work without first declaring that r is a Range or Variant object... using the Dim statement - unless you don't have Option Explicit enabled, which you should. Always. Otherwise, you're using identifiers that you haven't declared and they are all implicitly declared as Variants.
Dim: you are defining a variable (here: r is a variable of type Range)
Set: you are setting the property (here: set the value of r to Range("A1") - this is not a type, but a value).
You have to use set with objects, if r were a simple type (e.g. int, string), then you would just write:
Dim r As Integer
r=5
Dim simply declares the value and the type.
Set assigns a value to the variable.
If a variable is defined as an object e.g. Dim myfldr As Folder, it is assigned a value by using the keyword, "Set".
Dim is short for Dimension and is used in VBA and VB6 to declare local variables.
Set on the other hand, has nothing to do with variable declarations. The Set keyword is used to assign an object variable to a new object.
Hope that clarifies the difference for you.
According to VBA help on SET statement it sets a reference to an object.so if you change a property the actual object will also changes.
Dim newObj as Object
Set var1=Object1(same type as Object)
Set var2=Object1(same type as Object)
Set var3=Object1(same type as Object)
Set var4=Object1(same type as Object)
Var1.property1=NewPropertyValue
the other Vars properties also changes,so:
Var1.property1=Var2.property1=Var3.property1=Var4.property1=Object1.Property1=NewpropertyValue`
actualy all vars are the same!

Lotus Notes Cannot Get subject from .Value or ColumnValues '451 Error

I seem to be getting the same "'451 Property let procedure not defined and property get procedure did not return an object" run time error when trying to get the subject of my emails from Lotus Notes. I have tried by using both GetFirstItem("subject") as well as ColumnValues(5) but both drop the same error.
The below is the GetFirstItem method, NSubject in this case does hold the variable Values(0) with the subject line captured correctly. I just can't seem to figure out how to pull it and use it.
Sub Test_subject()
Dim NSession As Object
Dim NMailDb As Object
Dim NFolder As Object
Dim NView As Object
Dim NDocument As Object
Dim NSubject As Object
Dim NItem As Variant
Set NSession = CreateObject("Lotus.NotesSession")
Call NSession.Initialize("password")
Set NMailDb = NSession.GetDatabase("Server", "Directory")
If Not NMailDb.IsOpen = True Then
Call NMailDb.Open
End If
Set NFolder = NMailDb.GetView("($Inbox)")
Set NView = NFolder.CreateViewNav()
Set NDocument = NView.GetLastDocument
Set NItem = NDocument.Document
Set NSubject = NItem.GetFirstItem("subject")
MsgBox (NSubject.Values(0))
End Sub
Changing to .ColumnValues(6) which contains the correct value also drops the same error. I'm clearly not understanding something but I'm not sure where I'm going wrong.
Set NDocument = NView.GetLastDocument
NItem = NDocument.ColumnValues(6)
Replacing .GetFirstItem("subject") with .GetItemValue("Subject") works fine for this. I would like to know however why the above doesn't work because I feel like pulling .ColumnValues may be useful in the future.
It's complaining about this line:
Set NItem = NDocument.Document
You had previously assigned Set NDocument = NView.GetLastDocument, so NDocument is an object in the NotesDocument class. NDocument.Document throws an error because there is no Document property in the NotesDocument class.
This line of code is pointless, anyhow. You already have your NotesDocument object. And it's kind of weird to try to assign a NotesDocument object to a variable named NItem. You should be using an object variable named NItem only if you are using the GetFirstItem method.
Your next line of code needs to be:
Set NSubject = NDocument.GetFirstItem("subject")
instead of
Set NSubject = NItem.GetFirstItem("subject")
And if you want to use ColumnValues, it will be NDocument.ColumnValues, not NItem.ColumnValues.
The only thing that seems really off in your code is the declaration of NItem as Variant, and not as Object. Could you change that and try again?

Excel VBA Reading XML Errors (Blank attributes?)

I have some code (subroutine macro) that's meant to search through a list of US state, get their name and abbreviation and then search a sheet for that state to get a corresponding integer value.The code largely works, but at the end of the loop through the results, there is always a blank result (even if I check for it, which I've done in a variety of different ways). This seems to be the cause of errors if I try to apply a function to the result.
Dim xmlDoc As DOMDocument30
Set xmlDoc = New DOMDocument30
xmlDoc.Load ("C:\xampp\htdocs\election\states.xml")
Dim list As IXMLDOMNodeList
Set list = xmlDoc.SelectNodes("//states/state")
Dim abbrNode As IXMLDOMAttribute
Dim abbr As String
Dim stateNode As IXMLDOMAttribute
Dim state As String
Dim resultNode As IXMLDOMAttribute
Dim result As String
Dim node As IXMLDOMNode
Dim childNode As IXMLDOMNode
Dim NumberOfVotes As Integer
For Each node In list
Set abbrNode = node.Attributes.getNamedItem("abbreviation")
Set stateNode = node.Attributes.getNamedItem("name")
Set resultNode = node.Attributes.getNamedItem("result")
If (Trim(abbrNode.Text) <> "") Then
MsgBox stateNode.Text
End If
Even with the check for an empty string (also checked for if abbrNode was nothing), I always get an empty MsgBox at the end, when I'd rather it end at Wyoming.
If I run the lookup function in the loop:
Set NumberOfVotes = lookupElectoralVotes(stateNode.Text)
I get a compile error (object required), I assume because it can't perform that function on a blank result.
I also get an error if I try to set a variable to equal stateNode.Text or similar:
Set state = stateNode.Text
Also generates a compile error, I'm not sure why, but again possibly due to the final result being blank. But I can use stateNode.Text by itself so maybe I'm just missing a core concept of VBA.
The XML file is nothing complex:
<?xml version="1.0" encoding="utf-8"?>
<states>
<state name="ALABAMA" abbreviation="AL" result="U"/>
<state name="ALASKA" abbreviation="AK" result="U"/>
<state name="ARIZONA" abbreviation="AZ" result="U"/>
</states>
Thanks for any help.

Replace string with string variable - Error 91

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
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?
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.

Object.Namespace path error

I wrote this code to return some properties from file:
Dim strMTitle As String
Dim objMshell As Object
Dim objMfolder As Object
Dim objMFolderItem As Object
Dim strMpath As String
strMpath = "C:\Users\User1\Desktop\Test4\"
Set objMshell = CreateObject("shell.application")
Set objMfolder = objMshell.Namespace(strMpath)
Set objMFolderItem = objMfolder.ParseName("test2.xlsm")
strMTitle = objMfolder.GetDetailsOf(objMFolderItem, 21)
Debug.Print strMTitle
The problem is that it keeps returning run time error 91 - Object variable with block variable not set. Weirdest thing is that when I "Hardcode" objMfolder with path like this:
Set objMfolder = objMshell.Namespace("C:\Users\User1\Desktop\Test4\") the code works perferct.
I use this path in multiple places in my macro so I would really like to "store" it in strMpath and use it like this:
Set objMfolder = objMshell.Namespace(strMpath)
Please help!
The code seems to work with the string variable if you use early-binding and the Shell32.Shell as below. Also, .GetDetailsOf with a column argument of 21 returns nothing, but 0 returns the file name.
Option Explicit
'Set Reference to Microsoft Shell Controls and Automation
Sub dural()
Dim strMTitle As String
Dim objMshell As Shell32.Shell
Dim objMfolder As Folder
Dim objMFolderItem As FolderItem
Dim strMpath As String
strMpath = "C:\Users\Ron\Desktop\"
Set objMshell = New Shell32.Shell
Set objMfolder = objMshell.Namespace(strMpath)
Set objMFolderItem = objMfolder.ParseName("20161104.csv")
strMTitle = objMfolder.GetDetailsOf(objMFolderItem, 0)
Debug.Print strMTitle
End Sub

Resources