Wednesday, June 4, 2008

VB.NET: Shell32 code compiled on Vista does not run on XP/2003

This was a tricky one, and I had a hard time finding a working solution. I finally found the solution by reading comments in differnt forum. I had to mix it together myself - here's the finished soup:

Situation: I had a VB.NET code that was based on VBscript code from "Hey, scripting guy" to programmatically pin shortcuts from the desktop onto the start menu. This line of code created a bit of a headache:

Dim oShell As Shell32.Shell = New Shell32.Shell
Dim oFolder As Shell32.Folder = oShell.NameSpace(0) '0 refers to user desktop

Problem: The code compiled and ran on my Vista computer, but once I moved the .exe file to a Server 2003 computer, it threw a runtime exception stating: Unable to cast COM object of type 'Shell32.ShellClass' to interface type 'Shell32.IShellDispatch5 and then the usual garble (actually, the garble did reveal the line of code in question)

Cause: The Shell32 library comes in a new version in Vista compared to XP/2003 - or that is my conception of what I could find on the subject. The problem is that under XP/2003, the Shell32.ShellClass is not casted to Shell32.IShellDispatch5, but to its predecessor, Shell32.IShellDispatch4. Now I knew the difference, but it meant that the program needs to reference the right one.

Solution: I found the two first lines of code below buried in a comment in a forum - when they replace the first line of code above, voilla: It works on all platforms (the term 'all' referring to XP, Server 2003 and Vista):

Dim ShellAppType As Type = Type.GetTypeFromProgID("Shell.Application")
Dim oShell As Object = Activator.CreateInstance(ShellAppType)
Dim oFolder As Shell32.Folder = oShell.NameSpace(0) '0 refers to user desktop

Nerds like me may be simple to please - it certainly made my day!

16 comments:

Anonymous said...

This doesn't work for me. It fail on seconds line Activator.CreateInstance with a Com exception.

Development machine=Vista
IDE=VS2005
User machine=XP Sp3
Error="Unable to cast COM object of type 'System.__ComObject' to interface type 'Shell32.Shell'. This operation failed because the QueryInterface call on the COM component for the interface with IID '{866738B9-6CF2-4DE8-8767-F794EBE74F4E}' failed due to the following error: No such interface supported (Exception from HRESULT: 0x80004002 (E_NOINTERFACE))."

Anonymous said...

Thanks!
Here is a c# method I wrote based on the above code:

public Shell32.Folder GetShell32NameSpaceFolder(Object folder)
{
Type shellAppType = Type.GetTypeFromProgID("Shell.Application");

Object shell = Activator.CreateInstance(shellAppType);
return (Shell32.Folder)shellAppType.InvokeMember("NameSpace",
System.Reflection.BindingFlags.InvokeMethod, null, shell, new object[] { folder });
}

Anonymous said...

Hi all,

Thank you for this. I'm not able to apply this change to my C# function even if the help of the last post.

Here it is, can someone help me?

/// This Zip the content of a given folder using (Microsoft Shell Controls And Automation API)

private void ZipThisFolder(string strSrcFolderPath, string strDestFolderPath)
{
//Create an empty zip file
byte[] arrEmptyZip = new byte[] { 80, 75, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
System.IO.File.WriteAllBytes(strDestFolderPath, arrEmptyZip);
// Init Shell32 Class
Shell32.ShellClass sc = new Shell32.ShellClass();
// The source folder is always the same
Shell32.Folder oSrcFolder = sc.NameSpace(strSrcFolderPath);
// the destination folder is retrieved from above
Shell32.Folder oDestFolder = sc.NameSpace(strDestFolderPath);
// Collect the source items
Shell32.FolderItems oFolderItems = oSrcFolder.Items();
//Copy the items into the empty zip
oDestFolder.CopyHere(oFolderItems, 20);
}

Regards
Benjamin

Anonymous said...

Thanks,
this solved my problem.

Meublo said...

Excellent work, thanx a million!
The c# bypass in the comments is perfect as well.
Allows a working compatibility between all the mentionned windows platforms.

Anonymous said...

Hmm How can edit That C# code for VB.NET ?? pls Help i cant understand.. Can anybody explait it :(

Anonymous said...

this line here:
Dim oFolder As Shell32.Folder = oShell.NameSpace(0)

how do you pass in a string instead of 0 to get folders in a particular path?

Anonymous said...

Thank you much!! I just moved my development platform to Windows 7 and encountered this same problem. It was driving me crazy until I found your solution. Bravo!

-Skip in Portland, Oregon.

Anonymous said...

Thanks man. Your code really helped me out. It will be useful in the future considering I am moving alot of Windows 7 compiled projects to a Windows XP machine.

Anonymous said...

Thanks for this!

Alas, I was specifically using Shell32 for its zip File handling, so the work around doesn't actually work for me :(

If anyone finds out how to work around this and keep the zip file stuff working that would be great to know about.

Anonymous said...

I was actually using a folder path instead of the zero (desktop) and was getting the folder object empty (nothing)... after putting a .tostring it worked... very odd cus the variable was a string type....

Dim oFolder as Shell32.Folder = oShell.Namespace(mypath.ToString)

Alan said...

Similar problem to the last post, but using late binding of Shell32 in VBA. Shell.Namespace returns Nothing for the specified folder:
[code]
Sub VBA()
Dim Shell As Object, Folder As Object, folderPath As String
folderPath = "C:\Path\To\An\Existing\Folder"
Set Shell = CreateObject("Shell.Application")
Set Folder = Shell.Namespace(folderPath)
If Not Folder Is Nothing Then
'...
Else
MsgBox folderPath & " not found"
End If
End Sub[/code]

The solution is to surround the Namespace argument with parentheses to force it to be passed by value:

Shell.Namespace((folderPath))

Oddly, this problem doesn't occur with early binding of Shell32.

Mahesh said...

I also encounter this prb my prog was working fine in win7 but use to crash on XP when I was extracting file.
So here is solution for the prb hope will help someone. I am using VB.net 2008

'EXTRACT FILE IN BACKGRAOUND
Private Sub ExtractZip(ByVal zipFile As String, ByVal destination As String)
Try
If Not Directory.Exists(destination) Then
My.Computer.FileSystem.CreateDirectory(destination)
End If

Dim ShellAppType As Type = Type.GetTypeFromProgID("Shell.Application")
Dim objShell As Object = Activator.CreateInstance(ShellAppType)

Dim InpurFile = objShell.NameSpace(zipFile.ToString)
Dim OutputFile As Folder = objShell.NameSpace(destination.ToString)

OutputFile.CopyHere(InpurFile.Items, 20)

Catch ex As Exception
DisplayError(ex)
End Try
End Sub

Anonymous said...

I started to have this issue yesterday. I made the changes suggested here and they work perfect. Thanks,

Anonymous said...

Thank You for the direction. My code ran into that famous :Object not set to a reference happiness. So I worked around that as usual. Also had to add a reference to Microsoft Shell Controls and Automation. Then tweak your code to the following

Dim ShellAppType As Type = Type.GetTypeFromProgID("Shell.Application")
Dim oShell As Object = Activator.CreateInstance(ShellAppType)
Dim oFolder As Shell32.Folder
oFolder = oShell.NameSpace(Path.GetDirectoryName(m_fileName))

Anyway Thank you again for the time and effort you put into this and sharing

Anonymous said...

I found the same error in a Windows Server 2003 and applying the method TosTring did not worked. The application is blocked. The solution:

Dim ShellAppType As Type = Type.GetTypeFromProgID("Shell.Application")
Dim sc As Object = Activator.CreateInstance(ShellAppType)

Dim output As Shell32.Folder = sc.NameSpace((sTargetPath.ToString))

the variable between brackets. Why? I don't know, hehe.