I am trying to open a Windows Explorer window with specified items in a particular folder selected.
I tried to implement the program using SHOpenFolderAndSelectItems Win32 API with C#. Following the answer here: https://stackoverflow.com/a/12262552
using System.Runtime.InteropServices;
SelectInFileExplorer("C:\\Users");
void SelectInFileExplorer(string fullPath)
{
if (string.IsNullOrEmpty(fullPath))
throw new ArgumentNullException("fullPath");
fullPath = Path.GetFullPath(fullPath);
IntPtr pidlList = NativeMethods.ILCreateFromPathW(fullPath);
if (pidlList != IntPtr.Zero)
try
{
// Open parent folder and select item
Marshal.ThrowExceptionForHR(NativeMethods.SHOpenFolderAndSelectItems(pidlList, 0, IntPtr.Zero, 0));
}
finally
{
NativeMethods.ILFree(pidlList);
}
}
static class NativeMethods
{
[DllImport("shell32.dll", ExactSpelling = true)]
public static extern void ILFree(IntPtr pidlList);
[DllImport("shell32.dll", CharSet = CharSet.Unicode, ExactSpelling = true)]
public static extern IntPtr ILCreateFromPathW(string pszPath);
[DllImport("shell32.dll", ExactSpelling = true)]
public static extern int SHOpenFolderAndSelectItems(IntPtr pidlList, uint cild, IntPtr children, uint dwFlags);
}
It worked, but now I would like to implement the same thing using Go.
Here is my code:
package main
import (
"fmt"
"path/filepath"
"syscall"
"unsafe"
)
func main() {
SelectInFileExplorer("C:\\Users")
fmt.Println("Hello, World!")
}
func SelectInFileExplorer(fullPath string) {
if fullPath == "" {
panic("fullPath cannot be empty")
}
fullPath, _ = filepath.Abs(fullPath)
pidlList, err := ILCreateFromPathW(fullPath)
if err != nil {
panic(err)
}
defer ILFree(pidlList)
// Open parent folder and select item
err = SHOpenFolderAndSelectItems(pidlList, 0, 0)
if err != nil {
panic(err)
}
}
func ILFree(pidlList uintptr) {
shell32 := syscall.NewLazyDLL("shell32.dll")
proc := shell32.NewProc("ILFree")
proc.Call(pidlList)
}
func ILCreateFromPathW(pszPath string) (uintptr, error) {
shell32 := syscall.NewLazyDLL("shell32.dll")
proc := shell32.NewProc("ILCreateFromPathW")
pszPathPtr, err := syscall.UTF16PtrFromString(pszPath)
if err != nil {
return 0, err
}
ret, _, err := proc.Call(uintptr(unsafe.Pointer(pszPathPtr)))
if ret == 0 {
return 0, err
}
return ret, nil
}
func SHOpenFolderAndSelectItems(pidlList uintptr, cild uint32, children uintptr) error {
shell32 := syscall.NewLazyDLL("shell32.dll")
proc := shell32.NewProc("SHOpenFolderAndSelectItems")
ret, _, err := proc.Call(pidlList, uintptr(cild), children, 0)
if ret != 0 && err.Error() != "The operation completed successfully." {
return err
}
return nil
}
It doesn't work as nothing happened after I executed the code. The Explorer didn't appear.
I read some documents regarding the usage of the API: https://learn.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shopenfolderandselectitems
Still not sure what's going on. Maybe it is not because of the programming language itself. I think calling DLL functions is a little bit complex in GO, and I probably did something wrong with the function calls.
As explained in official SHOpenFolderAndSelectItems
documentation remarks:
CoInitialize or CoInitializeEx must be called before using SHOpenFolderAndSelectItems. Not doing so causes SHOpenFolderAndSelectItems to fail.
So you must add something like this in your main
for example:
func main() {
ole32 := syscall.NewLazyDLL("ole32.dll")
proc := ole32.NewProc("CoInitialize")
proc.Call(0)
SelectInFileExplorer("C:\\Users")
}
It works in .NET because .NET does this automatically