Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
437 views
in Technique[技术] by (71.8m points)

c# - Exception when using Shell32 to get File extended properties

I am trying to use Shell32 to get extended file properties in c#.

My code for this is as follows.

        var file = FileUpload1.PostedFile;

        List<string> arrHeaders = new List<string>();

        Shell shell = new ShellClass();

        //Exception is thrown at next line
        Folder rFolder = shell.NameSpace(Path.GetDirectoryName(file.FileName));
        FolderItem rFiles = rFolder.ParseName(Path.GetFileName(file.FileName));

        for (int i = 0; i < short.MaxValue; i++)
        {
            string value = rFolder.GetDetailsOf(rFiles, i).Trim();
            arrHeaders.Add(value);
        }

I am getting exception as follows. enter image description here

Message - Unable to cast COM object of type 'Shell32.ShellClass' to interface type 'Shell32.IShellDispatch6'. This operation failed because the QueryInterface call on the COM component for the interface with IID '{286E6F1B-7113-4355-9562-96B7E9D64C54}' failed due to the following error: No such interface supported (Exception from HRESULT: 0x80004002 (E_NOINTERFACE)).

Stack Trace - at System.StubHelpers.StubHelpers.GetCOMIPFromRCW(Object objSrc, IntPtr pCPCMD, IntPtr& ppTarget, Boolean& pfNeedsRelease) at Shell32.ShellClass.NameSpace(Object vDir) at PBSWebApplication.Test.Button1_OnClick(Object sender, EventArgs e) in c:ProjectsPBSWebApplicationPBSWebApplicationPBSWebApplicationTest.aspx.cs:line 33 at System.Web.UI.WebControls.Button.OnClick(EventArgs e) at System.Web.UI.WebControls.Button.RaisePostBackEvent(String eventArgument) at System.Web.UI.WebControls.Button.System.Web.UI.IPostBackEventHandler.RaisePostBackEvent(String eventArgument) at System.Web.UI.Page.RaisePostBackEvent(IPostBackEventHandler sourceControl, String eventArgument) at System.Web.UI.Page.RaisePostBackEvent(NameValueCollection postData) at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)

How to fix this?

Thanks.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

As you identified this is because Shell32 requires an STA thread. If you can't simply configure your app to run with an STA Thread as in your solution then as an alternative you can create a separate STA Thread, use it to run the Shell32 code, then continue execution. e.g. this is what I ended up with when writing an SSIS Script Task which as I understand it always runs on MTA thread. In my case I'm calling a different method of Shell32 (CopyHere) but same logic would apply whichever method you want to call:

    /// <summary>
    /// Ugh! SSIS runs script tasks on MTA threads but Shell32 only wants to 
    /// run on STA thread. So start a new STA thread to call UnZip, block 
    /// till it's done, then return. 
    /// We use Shell32 since .net 2 doesn't have ZipFile and we prefer not to 
    /// ship other dlls as they normally need to be deployed to the GAC. So this 
    /// is easiest, although not very pretty.
    /// </summary>
    /// <param name="zipFile">File to unzip</param>
    /// <param name="folderPath">Folder to put the unzipped files</param>
    public static void UnZipFromMTAThread(string zipFile, string folderPath)
    {
        object[] args = new object[] { zipFile, folderPath };
        if (Thread.CurrentThread.GetApartmentState() == ApartmentState.STA)
        {
            UnZip(args);
        }
        else
        {
            Thread staThread = new Thread(new ParameterizedThreadStart(UnZip));
            staThread.SetApartmentState(ApartmentState.STA);
            staThread.Start(args);
            staThread.Join();
        }
    }

    /// <summary>
    /// From http://www.fluxbytes.com/csharp/unzipping-files-using-shell32-in-c/ but with 
    /// args packed in object array so can be called from new STA Thread in UnZipFromMTAThread().
    /// </summary>
    /// <param name="param">object array containing: [string zipFile, string destinationFolderPath]</param>
    private static void UnZip(object param)
    {
        object[] args = (object[]) param;
        string zipFile = (string)args[0];
        string folderPath = (string)args[1];


        if (!File.Exists(zipFile))
            throw new FileNotFoundException();

        if (!Directory.Exists(folderPath))
            Directory.CreateDirectory(folderPath);

        Shell32.Shell objShell = new Shell32.Shell();
        Shell32.Folder destinationFolder = objShell.NameSpace(folderPath);
        Shell32.Folder sourceFile = objShell.NameSpace(zipFile);

        foreach (var file in sourceFile.Items())
        {
            // Flags are: No progress displayed, Respond with 'Yes to All' for any dialog, no UI on error
            // I added 1024 although not sure it's relevant with Zip files. 
            // See https://msdn.microsoft.com/en-us/library/windows/desktop/bb787866%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396
            destinationFolder.CopyHere(file, 4 | 16 | 1024); 
        }
    }

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...