Window explorer is freezing when open - vb.net

I need to open the specific folder for a file and I am doing it with:
file = Directory.GetFiles(filepath,Filename,
SearchOption.AllDirectories).FirstOrDefault()
Process.Start("explorer.exe", "/select," & file.ToString)
This code is immediately opening the folder which is already fully loaded, but it doesnt seem enabled, endeed I cant do any action in it. The form is not freezing.
Thanks

I'll give you an answer in two parts...
Firstly, if the GetFiles() call takes to long and freezes the form (which doesn't seem to be the current problem), you should do the following:
Use EnumerateFiles() instead because in this case, FirstOrDefault() will return immediately after finding a matching file, unlike GetFiles() which will get all the files first before calling FirstOrDefault().
Wrap the call to EnumerateFiles() in a Task.Run() to execute it on a worker thread in case the search takes a little too long:
' Or:
' Private Async Sub SomeEventHandler()
Private Async Function ParentMethod() As Task
Dim filePath As String = Await Task.Run(
Function()
Return Directory.EnumerateFiles(dirPath, FileName, SearchOption.AllDirectories).
FirstOrDefault()
End Function)
' TODO: Use `filePath` to open the folder and select the file.
End Function
Secondly, do not use Process.Start("explorer.exe", "/select") because a) it starts a new Explorer.exe process rather than opening the directory in the current one, b) it seems to be causing you some issues, and c) it has some limitations.
Instead, use the approach demonstrated in the answer linked in point (c) above. The code is in C# but it can be easily converted to VB. Here's the VB version of the code (with an additional overload).
Add the following class to your project:
Imports System.IO
Imports System.Runtime.InteropServices
Public Class NativeMethods
<DllImport("shell32.dll", SetLastError:=True)>
Private Shared Function SHOpenFolderAndSelectItems(
pidlFolder As IntPtr, cidl As UInteger,
<[In], MarshalAs(UnmanagedType.LPArray)> apidl As IntPtr(),
dwFlags As UInteger) As Integer
End Function
<DllImport("shell32.dll", SetLastError:=True)>
Private Shared Sub SHParseDisplayName(
<MarshalAs(UnmanagedType.LPWStr)> name As String,
bindingContext As IntPtr, <Out> ByRef pidl As IntPtr,
sfgaoIn As UInteger, <Out> ByRef psfgaoOut As UInteger)
End Sub
Public Shared Sub OpenFolderAndSelectFile(filePath As String)
Dim dirPath As String = Path.GetDirectoryName(filePath)
Dim fileName As String = Path.GetFileName(filePath)
OpenFolderAndSelectFile(dirPath, fileName)
End Sub
Public Shared Sub OpenFolderAndSelectFile(dirPath As String, fileName As String)
Dim nativeFolder As IntPtr
Dim psfgaoOut As UInteger
SHParseDisplayName(dirPath, IntPtr.Zero, nativeFolder, 0, psfgaoOut)
If nativeFolder = IntPtr.Zero Then
' Log error, can't find folder
Return
End If
Dim nativeFile As IntPtr
SHParseDisplayName(Path.Combine(dirPath, fileName),
IntPtr.Zero, nativeFile, 0, psfgaoOut)
Dim fileArray As IntPtr()
If nativeFile = IntPtr.Zero Then
' Open the folder without the file selected if we can't find the file
fileArray = New IntPtr(-1) {}
Else
fileArray = New IntPtr() {nativeFile}
End If
SHOpenFolderAndSelectItems(nativeFolder, CUInt(fileArray.Length), fileArray, 0)
Marshal.FreeCoTaskMem(nativeFolder)
If nativeFile <> IntPtr.Zero Then
Marshal.FreeCoTaskMem(nativeFile)
End If
End Sub
End Class
Then, you can easily call it like this:
NativeMethods.OpenFolderAndSelectFile(filePath)
Some additional notes:
You should choose meaningful variable names. filePath should refer to the path of a file. If you want to refer to a folder/directory path, use something like dirPath or folderPath instead.
You don't need to call .ToString() on a variable that's already of a String type.

I would change the variable name file to something else. Maybe foundFile. After all File is the name of a class in System.IO and vb.net is not case sensitive. Your code works fine for me with the variable name change. Also got rid of the .ToString. I used .EnumerateFiles as commented by #jmcilhinney in the question you deleted. I purposely chose a path with all sorts of strange characters and it still worked.
Private Sub OPCode()
Dim filepath = "C:\Users\xxxx\Documents\TextNotes\Dogs & Cats (Pets)"
Dim Filename = "Specialty Vets.txt"
Dim foundFile = Directory.EnumerateFiles(filepath, Filename,
IO.SearchOption.AllDirectories).FirstOrDefault()
Process.Start("explorer.exe", "/select," & foundFile)
End Sub

Related

Check folder is open or not

how should i make this go open folder only if only folder it is not open? the folder should only open if it is not open. and put an if, and an else.
Private Shared Function FindWindow(ByVal lpClassName As String, ByVal lpWindowName As String) As IntPtr
End Function
Dim folderpath As String
Dim foldername As String
'Process.Start(System.Environment.CurrentDirectory)
folderpath = My.Application.Info.DirectoryPath + ("\Check")
foldername = System.IO.Path.GetFileName(folderpath)
If FindWindow(vbNullString, foldername) = 0 Then
Process.Start("explorer.exe", folderpath)
End If
Windows has a function built in to do this. https://learn.microsoft.com/en-us/windows/win32/shell/ishelldispatch-windows and look at the windows property.
When Internet Explorer 4 Desktop Update was released there was no difference between local files and internet files. Therefore it lists Internet Explorer (but no other browser) and Windows Explorer windows.
The win in AllWindows is actually an Internet Explorer object - see https://learn.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/platform-apis/aa752084(v=vs.85)
'ListOpenShellWindows.vb
Imports System.Runtime.InteropServices
Public Module MyApplication
Sub Main()
Dim ObjShell as Object
Dim AllWindows as Object
objShell = CreateObject("Shell.Application")
AllWindows = objShell.Windows
For Each win in AllWindows
Msgbox(win.LocationUrl & " - " & win.LocationName)
Next
End Sub
End Module
To compile copy both files into same folder and double click the batch file.
REM ListOpenShellWindows.bat
REM This file compiles ListOpenShellWindows.vb to ListOpenShellWindows.exe using the system VB.NET compiler
"C:\Windows\Microsoft.NET\Framework\v4.0.30319\vbc.exe" /target:winexe /out:"%~dp0\ListOpenShellWindows.exe" "%~dp0\ListOpenShellWindows.vb"
pause
Edit
If interacting with the Shell it is best to use the Shell functions. You use ObjShell.Open to open a folder. See https://learn.microsoft.com/en-us/windows/win32/shell/ishelldispatch-open.
If the goal is to not open multiple explorer instances for the same directory, then you can simply pass a new ProcessStartInfo object to the Process.Start(...) function. Assign the directory path to the ProcessStartInfo.FileName property and the "open" command to the ProcessStartInfo.Verb property. This way, an already open instance will be activated rather than opening a new one for the same dir.
' Some caller...
Dim dirInfo = New DirectoryInfo(Path.Combine(My.Application.Info.DirectoryPath, "Check"))
Dim psi As New ProcessStartInfo With {
.FileName = dirInfo.FullName,
.Verb = "open"
}
Process.Start(psi)
On the other hand, if you still need to find out whether a directory is already open in the explorer, then you could pinvoke the FindWindowByCaption function which returns a handle to a window if any.
Dim dirInfo = New DirectoryInfo(Path.Combine(My.Application.Info.DirectoryPath, "Check"))
Dim p = FindWindowByCaption(IntPtr.Zero, dirInfo.Name)
If p = IntPtr.Zero Then
Process.Start(dirInfo.FullName)
Else
Console.WriteLine("Already Open!")
End If
<DllImport("user32.dll", EntryPoint:="FindWindow", SetLastError:=True, CharSet:=CharSet.Auto)>
Private Shared Function FindWindowByCaption(zero As IntPtr, lpWindowName As String) As IntPtr
End Function
Of course, the target directory should exist in the first place. Just in case, see the DirectoryInfo.Exists property and the DirectoryInfo.Create method.

How to copy multiple files/folders using single windows copy GUI in VB.Net

I am making an app in VB.Net that copies many files and folders to the same directory and I wish to use windows explorer for that (so the user has GUI and I do not have to worry about showing any errors or compare files).
So, if I do this for each file/folder:
My.Computer.FileSystem.CopyDirectory(source_path, target_path, FileIO.UIOption.AllDialogs)
My.Computer.FileSystem.CopyFile(source_path, target_path, FileIO.UIOption.AllDialogs)
It works correctly and shows this window:
Which is fine, however, if I have many files and/or folders and I loop through them and call commands above, they launch a new copy window for each file/folder, instead of launching a single GUI that combines them all, like so:
Is it possible to combine multiple files/folders copy process into a single windows explorer copy window GUI?
Thanks to #Jimi, I got pointed in the direction of SHFileOperations, so i figured out how to do this. I made a small class to do this:
Imports System.Runtime.InteropServices
Public Class NativeCopy
Private Enum FO_Func As Short
FO_COPY = &H2
FO_DELETE = &H3
FO_MOVE = &H1
FO_RENAME = &H4
End Enum
Private Structure SHFILEOPSTRUCT
Public hwnd As IntPtr
Public wFunc As FO_Func
<MarshalAs(UnmanagedType.LPWStr)>
Public pFrom As String
<MarshalAs(UnmanagedType.LPWStr)>
Public pTo As String
Public fFlags As UShort
Public fAnyOperationsAborted As Boolean
Public hNameMappings As IntPtr
<MarshalAs(UnmanagedType.LPWStr)>
Public lpszProgressTitle As String
End Structure
<DllImport("shell32.dll", CharSet:=CharSet.Unicode)>
Private Shared Function SHFileOperation(
<[In]> ByRef lpFileOp As SHFILEOPSTRUCT) As Integer
End Function
Private Shared _ShFile As SHFILEOPSTRUCT
Public Shared Sub Copy(ByVal sSource As List(Of String), ByVal sTarget As String)
_ShFile.wFunc = FO_Func.FO_COPY
_ShFile.pFrom = String.Join(vbNullChar, sSource) + vbNullChar
_ShFile.pTo = sTarget
SHFileOperation(_ShFile)
End Sub
End Class
To copy files and/or folders is as simple as this:
Dim copy_items_paths As List(Of String)
Dim target_path As String
NativeCopy.Copy(copy_items_paths, target_path)

Replicate Windows Unhide folders and files function

I'm re-visiting a tool that I wrote in VB.Net for my helpdesk team a while back and want to add a couple of checkboxes to replicate the same function that Windows uses to show hidden files and folders / re-hide, as well as protected operating system files.
I know I can do this by editing a registry entry and restarting explorer.exe, but that closes all open Explorer Windows and I don't want that.
Does anyone know how Windows is able to do this by a simple click of a checkbox and how I may be able to code it in VB.net?
Any input on this is greatly appreciated in advance.
EDIT: So it looks like I have found a refresh method that works to refresh Windows Explorer / File Explorer which can be applied to Drarig's answer below but I am having trouble converting it to VB.net as the original example is in C#.
'Original at http://stackoverflow.com/questions/2488727/refresh-windows-explorer-in-win7
Private Sub refreshExplorer(ByVal explorerType As String)
Dim CLSID_ShellApplication As Guid = Guid.Parse("13709620-C279-11CE-A49E-444553540000")
Dim shellApplicationType As Type = Type.GetTypeFromCLSID(CLSID_ShellApplication, True)
Dim shellApplication As Object = Activator.CreateInstance(shellApplicationType)
Dim windows As Object = shellApplicationType.InvokeMember("Windows", Reflection.BindingFlags.InvokeMethod, Nothing, shellApplication, New Object() {})
Dim windowsType As Type = windows.GetType()
Dim count As Object = windowsType.InvokeMember("Count", Reflection.BindingFlags.GetProperty, Nothing, windows, Nothing)
For i As Integer = 0 To CType(count, Integer)
Dim item As Object = windowsType.InvokeMember("Item", Reflection.BindingFlags.InvokeMethod, Nothing, windows, New Object() {i})
Dim itemType As Type = item.GetType()
'Only fresh Windows explorer Windows
Dim itemName As String = CType(itemType.InvokeMember("Name", Reflection.BindingFlags.GetProperty, Nothing, item, Nothing), String)
If itemName = explorerType Then
itemType.InvokeMember("Refresh", Reflection.BindingFlags.InvokeMethod, Nothing, item, Nothing)
End If
Next
End Sub
I am getting an exception Object reference not set to an instance of an object when I set itemType as Type = item.GetType() above. I can't figure out which object isn't being created. When I step through the code it looks like windowsType contains an object for windows. Does anyone have any idea on this? Once this is worked out I can then apply it to Drarig's solution below.
Alright I wish I could have got this to you sooner, but busy lately at work. I took a little time today to figure this out as I love digging into something I have not done before. This is the whole class from a new project; didn't have time to wrap it up in a separate class. I am sure this will get you what you need. It was a little harder than I thought as getting the correct handle and then send the command, but I got it. I hope you find it useful.
P.S. Some of the things you can leave out, specifically the boolean used for loading, this was so I can pull the current value back on load and either check/uncheck the CheckBox.
Note: This is tried and tested on Windows 7, 8 and 10
Imports Microsoft.Win32
Imports System.Reflection
Imports System.Runtime.InteropServices
Public Class Form1
<Flags()> _
Public Enum KeyboardFlag As UInteger
KEYBOARDF_5 = &H74
End Enum
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
Private Shared Function GetWindow(ByVal hl As Long, ByVal vm As Long) As IntPtr
End Function
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
Private Shared Function PostMessage(ByVal hWnd As IntPtr, ByVal Msg As UInteger, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As Boolean
End Function
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
Private Shared Function FindWindow(ByVal lpClassName As String, ByVal lpWindowName As String) As IntPtr
End Function
Private blnLoading As Boolean = False
Private Sub CheckBox1_CheckedChanged(sender As Object, e As EventArgs) Handles CheckBox1.CheckedChanged
Form1.HideFilesExtension(Me.CheckBox1.Checked)
If Not blnLoading Then NotifyFileAssociationChanged()
RefreshExplorer()
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim name As String = "Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced"
Dim key As RegistryKey = Registry.CurrentUser.OpenSubKey(name, False)
blnLoading = True
Me.CheckBox1.Checked = CBool(key.GetValue("Hidden"))
key.Close()
blnLoading = False
End Sub
Private Shared Sub HideFilesExtension(ByVal Hide As Boolean)
Dim name As String = "Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced"
Dim key As RegistryKey = Registry.CurrentUser.OpenSubKey(name, True)
key.SetValue("Hidden", If(Hide, 1, 0))
key.Close()
End Sub
Public Shared Sub RefreshExplorer()
Dim clsid As New Guid("13709620-C279-11CE-A49E-444553540000")
Dim typeFromCLSID As Type = Type.GetTypeFromCLSID(clsid, True)
Dim objectValue As Object = Activator.CreateInstance(typeFromCLSID)
Dim obj4 As Object = typeFromCLSID.InvokeMember("Windows", BindingFlags.InvokeMethod, Nothing, objectValue, New Object(0 - 1) {})
Dim type1 As Type = obj4.GetType
Dim obj2 As Object = type1.InvokeMember("Count", BindingFlags.GetProperty, Nothing, obj4, Nothing)
If (CInt(obj2) <> 0) Then
Dim num2 As Integer = (CInt(obj2) - 1)
Dim i As Integer = 0
Do While (i <= num2)
Dim obj5 As Object = type1.InvokeMember("Item", BindingFlags.InvokeMethod, Nothing, obj4, New Object() {i})
Dim type3 As Type = obj5.GetType
Dim str As String = CStr(type3.InvokeMember("Name", BindingFlags.GetProperty, Nothing, obj5, Nothing))
If (str = "File Explorer") Then
type3.InvokeMember("Refresh", BindingFlags.InvokeMethod, Nothing, obj5, Nothing)
End If
i += 1
Loop
End If
End Sub
Public Shared Sub NotifyFileAssociationChanged()
'Find the actual window...
Dim hwnd As IntPtr = FindWindow("Progman", "Program Manager")
'Get the window handle and refresh option...
Dim j = GetWindow(hwnd, 3)
'Finally post the message...
PostMessage(j, 256, KeyboardFlag.KEYBOARDF_5, 3)
End Sub
End Class
Here's a solution for everything excepting the refreshing of the explorer.
I've translated the code, but I'm unable to find how to refresh the explorer/desktop without restarting it.
Const keyName As String = "HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced"
Const Hidden As String = "Hidden"
Const SHidden As String = "ShowSuperHidden"
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim St As Integer = GetRegValue(Hidden)
If St = 2 Then
SetRegValue(Hidden, 1)
SetRegValue(SHidden, 1)
Else
SetRegValue(Hidden, 2)
SetRegValue(SHidden, 0)
End If
End Sub
Private Function GetRegValue(valueName As String) As Integer
Return CInt(My.Computer.Registry.GetValue(keyName, valueName, 0))
End Function
Private Sub SetRegValue(valueName As String, value As Integer)
My.Computer.Registry.SetValue(keyName, valueName, value, Microsoft.Win32.RegistryValueKind.DWord)
End Sub
I have a few ideas to refresh the desktop :
Send a key to a running process. I tried this (source) :
Dim pp As Process() = Process.GetProcessesByName("explorer")
If pp.Length > 0 Then
For Each p In pp
AppActivate(p.Id)
SendKeys.SendWait("{F5}")
Next
End If
Refresh using SHChangeNotify (source),
Refresh broadcasting a WM_SETTINGCHANGE message (source),
etc.
I think you'll be forced to manually refresh or restart the explorer.

VB.NET How to write text to a .ini file?

I'm creating a program that will help users create a certain config file for another program that usually has to be done by hand.
The programs config file reads it like; 'This_Setting = 0/1 (Off/On)'
So I want to make it so that if the user ticks say a checkbox on my program, it will write in the text file '1' and if it is unticked, it will write '0'.
Another way I thought about doing it was having a text box, the user ticks the boxes they want, and then click a button and then it would paste the config code in the text box so they could copy/paste it. I personally think this would be a better option, but I still have not the slightest clue how to do it.
Any help is appreciated!
If you just need to create a file, then File.WriteAllText is probably what you need. If it is a large file, you can use the StringBuilder class to build up the contents of the file, or if it is a small file, you can use simple string concatenation. After you have your string, you can use File.WriteAllText to write it to disk.
The traditional way is to use GetPrivateProfileString (or GetPrivateProfileSection) to retrieve INI settings, and WritePrivateProfileString (or WritePrivateProfileSection) to change them.
You can find the syntax at PInvoke
Here is the VB.NET code to write to INI file,
Imports System.IO
Imports System.Text
Imports System.Runtime.InteropServices
Public Class Form1
<DllImport("kernel32")>
Private Shared Function WritePrivateProfileString(ByVal lpSectionName As String, ByVal lpKeyName As String, ByVal lpString As String, ByVal lpFileName As String) As Long
End Function
Private Function SetIniValue(section As String, key As String, filename As String, Optional defaultValue As String = "") As String
Dim sb As New StringBuilder(500)
If WritePrivateProfileString(section, key, defaultValue, filename) > 0 Then
Return sb.ToString
Else
Return defaultValue
End If
End Function
Private Sub WriteToINI()
SetIniValue("default", "This_Setting", "C:\myconfigfile.ini", "1")
End Sub
End Class
Reference: http://vbnet.mvps.org/index.html?code/file/pprofilebasic.htm
This should be very easy. What you would want to do is use the following code.
FileOpen(1, "WHATEVER-THE-FILE-PATH-IS.ini", OpenMode.Output)
PrintLine(1, "WHATEVER-TEXT-YOU-WANT-TO-WRITE")
FileClose(1)
All you have to do is just change some things to make it suit your needs. First of all, on FileOpen() you want to change where it says "WHATEVER-THE-FILE-PATH-IS.ini" to your file path (make sure you have .ini on the end.)
The next thing you have to do to make this work is change where it says OpenMode.Output. You use OpenMode.Output to write to a text file, you use OpenMode.Input when you want to read from a text file (you would use that when you load the application) and you use OpenMode.Append to just add text on.
Now there are some things you need to look out for:
When you use OpenMode.Output it actually clears all the text from the text file first and then writes the text you want to write.
When you use OpenMode.Input you can't use PrintLine(1, "WHATEVER") as that is for writing to the text file not reading - so it will just crash. When using OpenMode.Input to read from the text file you have to use LineInput(1)
For Example:
Dim bool As Boolean
FileOpen(1, "WHATEVER", OpenMode.Input)
If LineInput(1) = "1" Then bool = True Else bool = False
FileClose(1)
This code will read the .ini file and if it says 1 in it then it will set the value to True and if it has 0 in it then it will set the value to False!
So here is the code after all going through all that!
To load the values:
Dim bool As Boolean
FileOpen(1, "WHATEVER.ini", OpenMode.Input)
If LineInput(1) = "1" Then bool = True Else bool = False
FileClose(1)
To save the values:
Dim bool As Boolean
FileOpen(1, "WHATEVER.ini", OpenMode.Input)
If bool = True Then PrintLine(1, "1") Else PrintLine(1, "0")
FileClose(1)
(don't forget you can add as many PrintLine(1, "") and LineInput(1) as you want)
A Class for reading and writing .INI files easily.
I didn't make these.
Instructions
Put this code into your project.
VB Code:
Class INIReadWrite
<DllImport("kernel32.dll", SetLastError:=True)> _
Private Shared Function GetPrivateProfileString(ByVal lpAppName As >String, ByVal lpKeyName As String, ByVal lpDefault As String, ByVal >lpReturnedString As StringBuilder, ByVal nSize As Integer, ByVal lpFileName >As String) As Integer
End Function
<DllImport("kernel32.dll", SetLastError:=True)> _
Private Shared Function WritePrivateProfileString(ByVal lpAppName As >String, ByVal lpKeyName As String, ByVal lpString As String, ByVal >lpFileName As String) As Boolean
End Function
Public Shared Function ReadINI(ByVal File As String, ByVal Section As >String, ByVal Key As String) As String
Dim sb As New StringBuilder(500)
GetPrivateProfileString(Section, Key, "", sb, sb.Capacity, File)
Return sb.ToString
End Function
Public Shared Sub WriteINI(ByVal File As String, ByVal Section As >String, ByVal Key As String, ByVal Value As String)
WritePrivateProfileString(Section, Key, Value, File)
End Sub
End Class
To read an .INI file
Code:
ReadINI(File, Section, Key)
To write to an .INI file
Code:
WriteINI(File, Section, Key, Value)

Icon.ExtractAssociatedIcon for things that are not files?

Is this possible? It gives me an error, and I had previously thought it could work for folders and drives and stuff like that as well.
Icon.ExtractAssociatedIcon("C:\") did not work when I tried it, and threw an error.
How can I get the associated icon from EVERYTHING? This is vb.net
The SHGetFileInfo() shell function can provide you with the icon you are looking for. This code worked well, it generated appropriate icons for drives, folders and files:
Imports System.Drawing
Imports System.Reflection
Imports System.Runtime.InteropServices
Public Module NativeMethods
Public Function GetShellIcon(ByVal path As String) As Icon
Dim info As SHFILEINFO = New SHFILEINFO()
Dim retval as IntPtr = SHGetFileInfo(path, 0, info, Marshal.SizeOf(info), &H100)
If retval = IntPtr.Zero Then Throw New ApplicationException("Could not retrieve icon")
'' Invoke private Icon constructor so we do not have to copy the icon
Dim cargt() As Type = { GetType(IntPtr) }
Dim ci As ConstructorInfo = GetType(Icon).GetConstructor(BindingFlags.NonPublic Or BindingFlags.Instance, Nothing, cargt, Nothing)
Dim cargs() As Object = { info.IconHandle }
Dim icon As Icon = CType(ci.Invoke(cargs), Icon)
Return icon
End Function
'' P/Invoke declaration
Private Structure SHFILEINFO
Public IconHandle As IntPtr
Public IconIndex As Integer
Public Attributes As Integer
<MarshalAs(UnmanagedType.ByValTStr, SizeConst:=260)> _
Public DisplayString As String
<MarshalAs(UnmanagedType.ByValTStr, SizeConst:=80)> _
Public TypeName As String
End Structure
Private Declare Auto Function SHGetFileInfo lib "Shell32.dll" (path As String, _
attributes As Integer, byref info As SHFILEINFO, infoSize As Integer, flags As Integer) As IntPtr
End Module
It is not possible to use Icon.ExtractAssociatedIcon on anything other than files. This API is a thin wrapper on top of the Win32 call ExtractAssociatedIcon. While the documentation for the managed code is a bit ambiguous, the native documentation is much clearer that the target must be a file. It goes further to say that it must be an executable file.
Unfortunately I'm not sure if there is an equivalent function for Directories or not.