2015-07-14 6 views
7

Próbuję użyć powłoki Shell32, aby uzyskać rozszerzone właściwości pliku w języku C#.Wyjątek podczas używania powłoki Shell32 w celu uzyskania rozszerzonych właściwości pliku

Mój kod jest następujący.

 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); 
     } 

Otrzymuję wyjątek w następujący sposób. enter image description here

Wiadomość - Nie można przekazać obiektu COM typu "Shell32.ShellClass" do typu interfejsu "Shell32.IShellDispatch6". Ta operacja nie powiodła się, ponieważ wywołanie QueryInterface na komponencie COM interfejsu z IID "{286E6F1B-7113-4355-9562-96B7E9D64C54}" nie powiodło się z powodu następującego błędu: Brak takiego interfejsu obsługiwanego (Wyjątek od HRESULT: 0x80004002 (E_NOINTERFACE)) .

ślad stosu - w System.StubHelpers.StubHelpers.GetCOMIPFromRCW (Przedmiot objSrc, IntPtr pCPCMD, IntPtr & ppTarget, logiczna & pfNeedsRelease) w Shell32.ShellClass.NameSpace (Przedmiot vdir) w PBSWebApplication.Test.Button1_OnClick (Przedmiot sender, EventArgs e) w c: \ Projects \ PBSWebApplication \ PBSWebApplication \ PBSWebApplication \ Test.aspx.cs: line 33 w System.Web.UI.WebControls.Button.OnClick (EventArgs e) at System.Web.UI. WebControls.Button.RaisePostBackEvent (String eventArgument) w System.Web.UI.WebControls.Button.System.Web.UI.IPostBackEventHandler.RaisePostBackEvent (String eventArgument) w System.Web.UI.Page.RaisePostBa ckEvent (IPostBackEventHandler sourceControl, String eventArgument) na System.Web.UI.Page.RaisePostBackEvent (NameValueCollection postData) na System.Web.UI.Page.ProcessRequestMain (Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)

Jak rozwiązać ten problem?

Dzięki.

Odpowiedz

7

Zidentyfikowano, ponieważ Shell32 wymaga gwintu STA. Jeśli nie możesz po prostu skonfigurować swojej aplikacji tak, aby działała z wątkiem STA tak jak w twoim rozwiązaniu, możesz jako alternatywny utworzyć osobny wątek STA, użyć go do uruchomienia kodu Shell32, a następnie kontynuować wykonywanie. na przykład this jest tym, co otrzymałem podczas pisania zadania SSIS Script, które, jak rozumiem, zawsze działa w wątku MTA. W moim przypadku Dzwonię inną metodę shell32 (CopyHere), ale sama logika miałaby zastosowanie którykolwiek sposób chcesz zadzwonić:

/// <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); 
     } 
    } 
+0

To dobrze złapać i zbyt dobre rozwiązanie! –

1

Okazało się być prostym rozwiązaniem dodania atrybutu STAThread do mojej klasy, a problem zniknął magicznie.

Oto mój kompletny kod po aktualizacji.

Uwaga: Jest to prosta aplikacja konsolowa.

class Program 
{ 
    [STAThread] 
    static void Main(string[] args) 
    { 
     Console.Title = "Extended file properties."; 

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

     Shell32.Shell shell = new Shell32.Shell(); 
     Shell32.Folder objFolder; 

     objFolder = shell.NameSpace(@"C:\Users\Admin\Pictures\PBS Docs"); 

     for (int i = 0; i < short.MaxValue; i++) 
     { 
      string header = objFolder.GetDetailsOf(null, i); 
      if (String.IsNullOrEmpty(header)) 
       break; 
      arrHeaders.Add(header); 
     } 

     foreach (Shell32.FolderItem2 item in objFolder.Items()) 
     { 
      for (int i = 0; i < arrHeaders.Count; i++) 
      { 
       Console.WriteLine("{0}\t{1}: {2}", i, arrHeaders[i], objFolder.GetDetailsOf(item, i)); 
      } 
     } 
} 
1

miałem simliar problemu i odpowiedź z jeronevw na tym forum stałe to dla mnie : https://social.msdn.microsoft.com/Forums/vstudio/en-US/b25e2b8f-141a-4a1c-a73c-1cb92f953b2b/instantiate-shell32shell-object-in-windows-8?forum=clr

public Shell32.Folder GetShell32NameSpaceFolder(Object folder) 
{ 
    Type shellAppType = Type.GetTypeFromProgID("Shell.Application"); 

    Object shell = Activator.CreateInstance(shellAppType); 
    return (Shell32.Folder)shellAppType.InvokeMember("NameSpace", 
System.Reflection.BindingFlags.InvokeMethod, null, shell, new object[] { folder }); 
} 

wszystkie kredyty dla jeronevw

+0

Czy musimy przekazać ścieżkę folderu jako argument do tej funkcji? –

Powiązane problemy