草庐IT

windows - 在 Go 中调用 SHGetImageList

coder 2023-06-26 原文

我正在尝试在 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/

有关windows - 在 Go 中调用 SHGetImageList的更多相关文章

  1. ruby - 在 Ruby 程序执行时阻止 Windows 7 PC 进入休眠状态 - 2

    我需要在客户计算机上运行Ruby应用程序。通常需要几天才能完成(复制大备份文件)。问题是如果启用sleep,它会中断应用程序。否则,计算机将持续运行数周,直到我下次访问为止。有什么方法可以防止执行期间休眠并让Windows在执行后休眠吗?欢迎任何疯狂的想法;-) 最佳答案 Here建议使用SetThreadExecutionStateWinAPI函数,使应用程序能够通知系统它正在使用中,从而防止系统在应用程序运行时进入休眠状态或关闭显示。像这样的东西:require'Win32API'ES_AWAYMODE_REQUIRED=0x0

  2. 使用 ACL 调用 upload_file 时出现 Ruby S3 "Access Denied"错误 - 2

    我正在尝试编写一个将文件上传到AWS并公开该文件的Ruby脚本。我做了以下事情:s3=Aws::S3::Resource.new(credentials:Aws::Credentials.new(KEY,SECRET),region:'us-west-2')obj=s3.bucket('stg-db').object('key')obj.upload_file(filename)这似乎工作正常,除了该文件不是公开可用的,而且我无法获得它的公共(public)URL。但是当我登录到S3时,我可以正常查看我的文件。为了使其公开可用,我将最后一行更改为obj.upload_file(file

  3. c# - 如何在 ruby​​ 中调用 C# dll? - 2

    如何在ruby​​中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL

  4. ruby - 在 Windows 机器上使用 Ruby 进行开发是否会适得其反? - 2

    这似乎非常适得其反,因为太多的gem会在window上破裂。我一直在处理很多mysql和ruby​​-mysqlgem问题(gem本身发生段错误,一个名为UnixSocket的类显然在Windows机器上不能正常工作,等等)。我只是在浪费时间吗?我应该转向不同的脚本语言吗? 最佳答案 我在Windows上使用Ruby的经验很少,但是当我开始使用Ruby时,我是在Windows上,我的总体印象是它不是Windows原生系统。因此,在主要使用Windows多年之后,开始使用Ruby促使我切换回原来的系统Unix,这次是Linux。Rub

  5. java - 从 JRuby 调用 Java 类的问题 - 2

    我正在尝试使用boilerpipe来自JRuby。我看过guide从JRuby调用Java,并成功地将它与另一个Java包一起使用,但无法弄清楚为什么同样的东西不能用于boilerpipe。我正在尝试基本上从JRuby中执行与此Java等效的操作:URLurl=newURL("http://www.example.com/some-location/index.html");Stringtext=ArticleExtractor.INSTANCE.getText(url);在JRuby中试过这个:require'java'url=java.net.URL.new("http://www

  6. ruby - 调用其他方法的 TDD 方法的正确方法 - 2

    我需要一些关于TDD概念的帮助。假设我有以下代码defexecute(command)casecommandwhen"c"create_new_characterwhen"i"display_inventoryendenddefcreate_new_character#dostufftocreatenewcharacterenddefdisplay_inventory#dostufftodisplayinventoryend现在我不确定要为什么编写单元测试。如果我为execute方法编写单元测试,那不是几乎涵盖了我对create_new_character和display_invent

  7. 【鸿蒙应用开发系列】- 获取系统设备信息以及版本API兼容调用方式 - 2

    在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList​()Obt

  8. Vscode+Cmake配置并运行opencv环境(Windows和Ubuntu大同小异) - 2

    之前在培训新生的时候,windows环境下配置opencv环境一直教的都是网上主流的vsstudio配置属性表,但是这个似乎对新生来说难度略高(虽然个人觉得完全是他们自己的问题),加之暑假之后对cmake实在是爱不释手,且这样配置确实十分简单(其实都不需要配置),故斗胆妄言vscode下配置CV之法。其实极为简单,图比较多所以很长。如果你看此文还配不好,你应该思考一下是不是自己的问题。闲话少说,直接开始。0.CMkae简介有的人到大二了都不知道cmake是什么,我不说是谁。CMake是一个开源免费并且跨平台的构建工具,可以用简单的语句来描述所有平台的编译过程。它能够根据当前所在平台输出对应的m

  9. C51单片机——实现用独立按键控制LED亮灭(调用函数篇) - 2

    说在前面这部分我本来是合为一篇来写的,因为目的是一样的,都是通过独立按键来控制LED闪灭本质上是起到开关的作用,即调用函数和中断函数。但是写一篇太累了,我还是决定分为两篇写,这篇是调用函数篇。在本篇中你主要看到这些东西!!!1.调用函数的方法(主要讲语法和格式)2.独立按键如何控制LED亮灭3.程序中的一些细节(软件消抖等)1.调用函数的方法思路还是比较清晰地,就是通过按下按键来控制LED闪灭,即每按下一次,LED取反一次。重要的是,把按键与LED联系在一起。我打算用K1来作为开关,看了一下开发板原理图,K1连接的是单片机的P31口,当按下K1时,P31是与GND相连的,也就是说,当我按下去时

  10. 深度学习部署:Windows安装pycocotools报错解决方法 - 2

    深度学习部署:Windows安装pycocotools报错解决方法1.pycocotools库的简介2.pycocotools安装的坑3.解决办法更多Ai资讯:公主号AiCharm本系列是作者在跑一些深度学习实例时,遇到的各种各样的问题及解决办法,希望能够帮助到大家。ERROR:Commanderroredoutwithexitstatus1:'D:\Anaconda3\python.exe'-u-c'importsys,setuptools,tokenize;sys.argv[0]='"'"'C:\\Users\\46653\\AppData\\Local\\Temp\\pip-instal

随机推荐