iceberg
iceberg

Reputation: 596

SHGetFileInfo doesn't return correct handle to icon

I'm using SHGetFileInfo function for getting icons for folders and different file types. According to MSDN call of this function should be done from background thread and before call Component Object Model (COM) must be initialized with CoInitialize or OleInitialize. My code looks like this:

    public void SetHlinkImage(string path)
    {
        Shell32.OleInitialize(IntPtr.Zero);
        Task task = Task.Factory.StartNew(() => { LoadIcons(path); });
    }

    private void LoadIcons(string path)
    {
        image = GetHlinkImage(path);

        if (OwnerControl.InvokeRequired)
            layout.ModuleControl.BeginInvoke((MethodInvoker)delegate ()
            {
                Shell32.OleUninitialize();
            });
    }

    public Icon GetHlinkImage(string path)
    {
        uint flags = Shell32.SHGFI_ICON | Shell32.SHGFI_ATTRIBUTES | Shell32.SHGFI_SMALLICON;

        Shell32.SHFILEINFO shfi = new Shell32.SHFILEINFO();

        IntPtr result = Shell32.SHGetFileInfo(path,
                            Shell32.FILE_ATTRIBUTE_DIRECTORY,
                            ref shfi,
                            (uint)Marshal.SizeOf(shfi),
                            flags);

        Icon icon = (Icon)Icon.FromHandle(shfi.hIcon).Clone();
        WinApi.DestroyIcon(shfi.hIcon);     // cleanup

        return icon;
    }

Mostly the problem appears after first call of the code and as result I get an exception when I tried to create Icon from icon handle:

System.ArgumentException: Win32 handle that was passed to Icon is not valid or is the wrong type

And further calls of the code work without problems. Actually behaviour also somehow depends on the test system. It is hardly possible to reproduce this issue on Windows10 systems but on Windows 7 it happens quite often.

Has anyone experienced this problem?

Upvotes: 0

Views: 716

Answers (1)

iceberg
iceberg

Reputation: 596

From comment of Hans Passant:

Calling OleInitialize() is pointless, the CLR already initializes COM before it starts a thread. And it failed, something you cannot see because you are not checking its return value. Not knowing that, it just spirals into undiagnosable misery from there. Yes, more of it on Win7. You must provide an STA thread, if this needs to run in the background then consider this solution.

Upvotes: 1

Related Questions