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!

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

Manu Gopinath said...

You are a Gem.. It worked like a charm!!. Thanks a lot

Anonymous said...

public Shell32.Folder GetShell32NameSpace(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 });
}


Shell32.Folder SrcFlder = GetShell32NameSpace(from);
Shell32.Folder DestFlder = GetShell32NameSpace(to);
Shell32.FolderItems items = SrcFlder.Items();
DestFlder.CopyHere(items, 16);

Anonymous said...

Old thread, but a simple solution exists (or does now)...

http://stackoverflow.com/a/24967301/625349

Unknown said...

Why does it work perfectly on win10 but doesn't work at all on XP and Win7? I've tried all solutions but noone help me. I don't understand what's wrong :(

Unknown said...

This thread is quite old, but I ran into this problem and found a solution, I hope it can help someone else. All I really had to do was create the Shell object by Late Binding, and use ToString and the folder names (for some reason). Also it seems CopyHere options aren't working (the comma 20), so I have to clear out the destination dir before hand to avoid Overwrite pop-ups.

Public Shared Sub UnzipFile(ByVal FileName As String, ByVal DestinationFolder As String)
'Dim oShell As New Shell32.Shell()
Dim oShell = CreateObject("Shell.Application")
Dim oInputFolder As Shell32.Folder, oOutputFolder As Shell32.Folder

If Directory.Exists(DestinationFolder) Then My.Computer.FileSystem.DeleteDirectory(DestinationFolder, FileIO.DeleteDirectoryOption.DeleteAllContents)
Directory.CreateDirectory(DestinationFolder)
oInputFolder = oShell.NameSpace(FileName.ToString()) 'Declare ZIP file as folder; ToString() is needed on XP machines
oOutputFolder = oShell.NameSpace(DestinationFolder.ToString())
oOutputFolder.CopyHere(oInputFolder.Items, 20)
End Sub