windows - 在 Go 中调用 SHGetImageList

标签 windows winapi go

我正在尝试在 Go 中调用 SHGetImageList 以便从 .EXE 文件中提取图标。问题是我不知道如何在 Go 中创建/传递“IImageList2 接口(interface)”,这是 SHGetImageList 所需要的。

几个小时以来,我尝试了各种各样的事情,但都导致了相同的 E_NOINTERFACE 错误。基本上它们都是“黑暗中的镜头”([]byte 数组以查看我是否可以接收任何“数据”,Go 中的实际接口(interface){}包含与 MSDN 定义的 IImagelist2 接口(interface)相同的功能等) .如果它有任何相关性,我确实有一个在 C# 中使用类似 http://www.pinvoke.net/default.aspx/shell32.shgetimagelist 的东西的工作版本。 ,但我根本不知道如何将其“翻译”成 Go。任何帮助将不胜感激。

下面的示例 Go 代码,在评论中有一些信息和指向 MSDN 的链接。

package main

import (
    "fmt"
    "syscall"
    "unsafe"
)

var (
    shell32 = syscall.MustLoadDLL("shell32.dll")

    // https://msdn.microsoft.com/en-us/library/windows/desktop/bb762179(v=vs.85).aspx
    procSHGetFileInfo = shell32.MustFindProc("SHGetFileInfoW")

    //https://msdn.microsoft.com/en-us/library/windows/desktop/bb762185(v=vs.85).aspx
    procSHGetImageList = shell32.MustFindProc("SHGetImageList")
)

func main() {
    someExeFile := `c:\windows\explorer.exe`

    iconIndex := GetIconIndex(someExeFile)

    // The problem:
    HRESULT, _, _ := procSHGetImageList.Call(
        uintptr(SHIL_JUMBO),
        uintptr(unsafe.Pointer(&IID_IImageList2)),

        // I don't know how pass/create an "IImageList interface" in Go,
        // or if it's even possible without relying on CGO.
        // IImageList interface:
        // https://msdn.microsoft.com/en-us/library/windows/desktop/bb761419(v=vs.85).aspx

        // Currently there's just a pointer to an empty []byte so that the code will compile.
        // HRESULT naturally contains the error code E_NOINTERFACE (2147500034),
        // which makes sense seeing as I'm not passing a valid interface.
        uintptr(unsafe.Pointer(&[]byte{})),
    )

    fmt.Println(iconIndex, HRESULT)
}


const SHIL_JUMBO = 0x4

const shGetFileInfoLen = 3
const shGetFileInfoFlags = 16400 //(SysIconIndex|LargeIcon|UseFileAttributes)
// use SHGetFileInfo to get the icon index (only value we care about)
func GetIconIndex(fileName string) int {
    buf := make([]uint16, shGetFileInfoLen)
    ret, _, _ := procSHGetFileInfo.Call(
        uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(fileName))),
        0,
        uintptr(unsafe.Pointer(&buf[0])),
        shGetFileInfoLen,
        shGetFileInfoFlags,
    )

    if ret != 0 && buf[2] > 0 {
        return int(buf[2])
    }

    return 0
}

// From: "192B9D83-50FC-457B-90A0-2B82A8B5DAE1"
var IID_IImageList2 = &GUID{0x192b9d83, 0x50fc, 0x457b, [8]byte{0x90, 0xa0, 0x2b, 0x82, 0xa8, 0xb5, 0xda, 0xe1}}

// http://msdn.microsoft.com/en-us/library/windows/desktop/aa373931.aspx
type GUID struct {
    Data1 uint32
    Data2 uint16
    Data3 uint16
    Data4 [8]byte
}

更新

现在的问题是在调用系统调用以获取实际的图标指针时出现错误 (0xC0000005)。

package main

import (
    "fmt"
    "syscall"
    "unsafe"
)

var (
    shell32 = syscall.MustLoadDLL("shell32.dll")

    // https://msdn.microsoft.com/en-us/library/windows/desktop/bb762179(v=vs.85).aspx
    procSHGetFileInfo = shell32.MustFindProc("SHGetFileInfoW")

    //https://msdn.microsoft.com/en-us/library/windows/desktop/bb762185(v=vs.85).aspx
    procSHGetImageList = shell32.MustFindProc("SHGetImageList")

    ole32 = syscall.MustLoadDLL("ole32.dll")
    procCoInitialize = ole32.MustFindProc("CoInitialize")
)

func main() {
    someExeFile := `c:\windows\explorer.exe`

    procCoInitialize.Call()

    iconIndex := GetIconIndex(someExeFile)

    var imglist *IImageList
    hr, _, _ := procSHGetImageList.Call(
        uintptr(SHIL_JUMBO),
        uintptr(unsafe.Pointer(&IID_IImageList)),
        uintptr(unsafe.Pointer(&imglist)),
    )

    // These look OK
    fmt.Println(iconIndex, hr, imglist.Vtbl.GetIcon)

    var hIcon uintptr
    // GetIcon: https://msdn.microsoft.com/en-us/library/windows/desktop/bb761463(v=vs.85).aspx
    hr, _, _ = syscall.Syscall(imglist.Vtbl.GetIcon,
        uintptr(unsafe.Pointer(imglist)),
        uintptr(iconIndex),
        getIconFlags,
        uintptr(unsafe.Pointer(&hIcon)),
    )

    // Errors: "Process finished with exit code -1073741819 (0xC0000005)"

    fmt.Println("hIcon:", hIcon) // Never reaches this
}

// ILD_TRANSPARENT | ILD_IMAGE
const getIconFlags = 0x00000001 | 0x00000020

const SHIL_JUMBO = 0x4

const shGetFileInfoLen = 3
const shGetFileInfoFlags = 16400 //(SysIconIndex|LargeIcon|UseFileAttributes)
// use SHGetFileInfo to get the icon index (only value we care about)
func GetIconIndex(fileName string) int {
    buf := make([]uint16, shGetFileInfoLen)
    ret, _, _ := procSHGetFileInfo.Call(
        uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(fileName))),
        0,
        uintptr(unsafe.Pointer(&buf[0])),
        shGetFileInfoLen,
        shGetFileInfoFlags,
    )

    if ret != 0 && buf[2] > 0 {
        return int(buf[2])
    }

    return 0
}


// From: "46EB5926-582E-4017-9FDF-E8998DAA0950"
var IID_IImageList = GUID{0x46eb5926, 0x582e, 0x4017, [8]byte{0x9f, 0xdf, 0xe8, 0x99, 0x8d, 0xaa, 0x09, 0x50}}

// http://msdn.microsoft.com/en-us/library/windows/desktop/aa373931.aspx
type GUID struct {
    Data1 uint32
    Data2 uint16
    Data3 uint16
    Data4 [8]byte
}

type IImageList struct {
    Vtbl *IImageListVtbl
}

type IImageListVtbl struct {
    Add                uintptr
    ReplaceIcon        uintptr
    SetOverlayImage    uintptr
    Replace            uintptr
    AddMasked          uintptr
    Draw               uintptr
    Remove             uintptr
    GetIcon            uintptr
    GetImageInfo       uintptr
    Copy               uintptr
    Merge              uintptr
    Clone              uintptr
    GetImageRect       uintptr
    GetIconSize        uintptr
    SetIconSize        uintptr
    GetImageCount      uintptr
    SetImageCount      uintptr
    SetBkColor         uintptr
    GetBkColor         uintptr
    BeginDrag          uintptr
    EndDrag            uintptr
    DragEnter          uintptr
    DragLeave          uintptr
    DragMove           uintptr
    SetDragCursorImage uintptr
    DragShowNolock     uintptr
    GetDragImage       uintptr
    GetItemFlags       uintptr
    GetOverlayImage    uintptr
}

最佳答案

哦,我现在明白了实际的问题。

        uintptr(unsafe.Pointer(&IID_IImageList2)),
...
var IID_IImageList2 = &GUID{0x192b9d83, 0x50fc, 0x457b, [8]byte{0x90, 0xa0, 0x2b, 0x82, 0xa8, 0xb5, 0xda, 0xe1}}

您的 IID_IImageList2 已经是一个指针。在您的调用中,您正在获取指向该指针的指针,这意味着该地址用作 GUID。你要么做

        uintptr(unsafe.Pointer(&IID_IImageList2)),
...
var IID_IImageList2 = GUID{0x192b9d83, 0x50fc, 0x457b, [8]byte{0x90, 0xa0, 0x2b, 0x82, 0xa8, 0xb5, 0xda, 0xe1}}

        uintptr(unsafe.Pointer(IID_IImageList2)),
...
var IID_IImageList2 = &GUID{0x192b9d83, 0x50fc, 0x457b, [8]byte{0x90, 0xa0, 0x2b, 0x82, 0xa8, 0xb5, 0xda, 0xe1}}

这样,GUID 本身用作 GUID,而不是它在内存中的位置。

关于windows - 在 Go 中调用 SHGetImageList,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38191972/

相关文章:

c++ - 在提取 SFX 存档时显示进度条

json - 将所有数据保存到 json 文件中,但只获取最后一个索引

reflection - 如何使用反射将数组值设置为 golang 中的字段?

c++ - QueryPerformanceCounter 的性能影响

python - pyttsx3初始化错误,不能使用pyttsx3

windows - 无法更改 Windows Universal 应用程序中 Hub 控件的标题

windows - 如何以编程方式检查当前进程是否在 Windows 上具有长路径感知能力?

C# - 何时调用 base.On Something?

c - mingw64 pjsip sizeof(fd_set) 总是双倍 sizeof(pj_fd_set_t) 导致 sock_select.c 中的断言

go - 解码 yaml 不正确?