Laden...

Shell32 einbinden

Erstellt von martin_salo vor 9 Jahren Letzter Beitrag vor 9 Jahren 5.303 Views
M
martin_salo Themenstarter:in
27 Beiträge seit 2015
vor 9 Jahren
Shell32 einbinden

Hallo Forum,

ich habe ein Projekt erstellt und nun meldet ein Anwender diesen Fehler.

Der Fehler tritt dann auf wenn mein Programm mit Hilfe der Windows Shell eine Zip Datei entpacken will.

Laut Internet Recherche liegt das daran das ich auf einem Windows 7 System entwickele und der Anwender noch WinXP benutzt. Bei WinXP sei eine ältere Shell32.dll dabei die nicht kompatibel sei. Zufällig habe ich eine Windows Server 2008 VM mit Visual Studio Express 2010 bei mir am Laufen. Das dort kompilierte Programm läuft dann auch auf der WinXP Maschine des Anwenders. Mir ist dieser Weg jedoch zu aufwendig.

Wie kann ich auf einer Windows 7 Maschine mit einem Visual Studio 2013 für eine alte Shell32.dll kompilieren? Ich habe schon versucht die alte Shell32.dll direkt in mein Projekt einzubinden (Add Reference+Browse) Das hat aber nicht funktioniert. Gibt es noch einen anderen Weg?

I solved it by extracting the interface declarations from Interop.Shell32.dll, and including only the ones I need, directly into the software. Wie könnte ich das machen?

Vielen Dank

Martin

Hinweis von MrSparkle vor 9 Jahren

Bitte keine Bilder von externen Filehostern einbinden, sondern Dateianhänge verwenden. Siehe [Hinweis] Wie poste ich richtig?, Punkt 6.1

C
2.121 Beiträge seit 2010
vor 9 Jahren

Bin kein Freund davon sich für jeden Pups gleich was externes zu suchen, aber in diesem Fall würde ich mir .NET Code zum zip bearbeiten suchen.
"C# zip free" in deine Suchmaschine eintippen, sollte einiges finden.

Gelöschter Account
vor 9 Jahren

Typischerweise über späte Bindung.

Type shellType = Type.GetTypeFromProgID("Shell.Application")
object shell = Activator.CreateInstance(shellType);
// usw...

Du gibt damit etwas Comfort ab, das ist aber bei nur 1 oder 2 genutzen Methoden/Eigenschaften mit einer eigenen Wrapper Klasse schnell gelöst.

Evtl. interessant für dich in dem Zusammenhang:
Allgemeiner COM-Wrapper für Späte Bindung

M
martin_salo Themenstarter:in
27 Beiträge seit 2015
vor 9 Jahren

Ich möchte auch keine extra Dll ausliefern und ich möchte auch nicht das neueste .net Framework voraussetzen (wo Zip Funktionalität inkludiert ist). Ich würde gerne bei der Shell bleiben. Ich versuche den Weg von Sebastian zu gehen, so ganz funktioniert es aber noch nicht. Der Code wird ohne Fehler ausgeführt, jedoch wird nichts extrahiert.

Vorbereitungen:
-- Konsolenprojekt erstellen
--"Microsoft Shell Controls and Automation" als Referenz hinzufügen
-- Ein Zip Datei testweise nach C:\Temp\x.zip verschieben.


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Shell32;
using System.Reflection;
using System.Runtime.InteropServices;

namespace ConsoleApplication1 {
    class Program {

        [STAThread]
        static void Main(string[] args) {

            // Pfad zu irgendeiner Zip Datei:
            string DownloadPath = @"C:\Temp\x.zip";

            // Datei soll im Temp Folder entpackt werden.
            string ExtractedFileFolder = System.IO.Path.GetTempPath() + Guid.NewGuid().ToString();
            System.IO.Directory.CreateDirectory(ExtractedFileFolder);

            // Neue Methode:
            ComObject Shell32 = new ComObject("Shell.Application");
            ComObject TempFolder = Shell32.InvokeObjectReturningFunction("NameSpace", ExtractedFileFolder);
            ComObject x = Shell32.InvokeObjectReturningFunction("NameSpace", DownloadPath);
            ComObject FolderItems = x.InvokeObjectReturningFunction("Items");
            TempFolder.InvokeFunction("CopyHere", FolderItems);

            // Alte Methode
            Shell32.Shell _shell = new Shell32.Shell();
            Shell32.Folder _TempFolder = _shell.NameSpace(ExtractedFileFolder);
            FolderItems _FolderItems = _shell.NameSpace(DownloadPath).Items();
            _TempFolder.CopyHere(_FolderItems);
        }
    }

    /// <summary>
    /// Allgemeiner Wrapper, um spätgebunden mit COM-Objekten (bzw. ActiveX-Komponenten) zu arbeiten.
    /// </summary>
    public class ComObject : IDisposable {
        // COM-Objekt
        private object _realComObject = null;

        /// <summary>
        /// Übernimmt einen vorhanden Verweis auf ein COM-Objekt.
        /// </summary>
        /// <param name="injectedObject">Vorhandenes COM-Objekt</param>
        public ComObject(object injectedObject) {
            // Wenn kein Objekt angegeben wurde ...
            if (injectedObject == null)
                // Ausnahme werfen
                throw new ArgumentNullException("injectedObject");

            // Wenn das angegebene Objekt kein COM-Objekt ist ...
            if (!injectedObject.GetType().IsCOMObject)
                // Ausnahme werfen
                throw new ArgumentException("Das angegebene Objekt ist kein COM-Objekt!", "injectedObject");

            // Verweis übernehmen
            _realComObject = injectedObject;
        }

        /// <summary>
        /// Erzeugt ein neues COM-Objekt anhand einer COM ProgId.
        /// </summary>
        /// <param name="progId"></param>
        public ComObject(string progId) {
            // Wenn keine ProgId angegeben wurde ...
            if (string.IsNullOrEmpty(progId))
                // Ausnahme werfen
                throw new ArgumentException();

            // Typinformationen über die ProgId ermitteln 
            Type comType = Type.GetTypeFromProgID(progId);

            // Wenn keine Typeninformationene gefunden wurden ...
            if (comType == null)
                // Ausnahme werfen
                throw new TypeLoadException(string.Format("Fehler beim Laden der Typinformationen zu ProgId '{0}'.", progId));

            // Instanz erzeugen
            _realComObject = Activator.CreateInstance(comType);
        }

        /// <summary>
        /// Ruft eine Funktion auf, die als Rückgabewert ein COM-Objekt (also keinen primitiven Datentyp) zurückgibt.
        /// </summary>
        /// <param name="functionName">Funktionsname</param>
        /// <param name="parameters">Parameter</param>
        /// <returns>Rückgabeobjekt</returns>
        public ComObject InvokeObjectReturningFunction(string functionName, params object[] parameters) {
            // Methode aufrufen
            object result = _realComObject.GetType().InvokeMember(functionName, BindingFlags.InvokeMethod | BindingFlags.OptionalParamBinding, null, _realComObject, parameters);

            // Wenn ein Objekt zurückgegeben wurde ...
            if (result != null)
                // Rückgabeobjekt in Wrapper einpacken und zurückgeben
                return new ComObject(result);

            // Nichts zurückgeben
            return null;
        }

        /// <summary>
        /// Ruft eine Funktion auf.
        /// </summary>
        /// <param name="functionName">Funktionsname</param>
        /// <param name="parameters">Parameter</param>
        /// <returns>Rückgabewert</returns>
        public object InvokeFunction(string functionName, params object[] parameters) {
            // Methode aufrufen und Rückgabewert zurückgeben
            return _realComObject.GetType().InvokeMember(functionName, BindingFlags.InvokeMethod | BindingFlags.OptionalParamBinding, null, _realComObject, parameters);
        }

        /// <summary>
        /// Ruft eine Prozedur auf.
        /// </summary>
        /// <param name="procedureName">Prozedurname</param>
        /// <param name="parameters">Parameter</param>
        public void InvokeProcedure(string procedureName, params object[] parameters) {
            // Methode aufrufen
            _realComObject.GetType().InvokeMember(procedureName, BindingFlags.InvokeMethod | BindingFlags.OptionalParamBinding, null, _realComObject, parameters);
        }

        /// <summary>
        /// Ruft den Wert einer Eigenschaft ab.
        /// </summary>
        /// <param name="propertyName">Eigenschaftsname</param>
        /// <returns>Rückgabewert</returns>
        public object GetProperty(string propertyName) {
            // Methode aufrufen und Rückgabewert zurückgeben
            return _realComObject.GetType().InvokeMember(propertyName, BindingFlags.GetProperty | BindingFlags.OptionalParamBinding, null, _realComObject, new object[0]);
        }

        /// <summary>
        /// Legt den Wert einer Eigenschaft fest.
        /// </summary>
        /// <param name="propertyName">Eigenschaftsname</param>
        /// <param name="parameters">Parameter</param>
        public void SetProperty(string propertyName, object value) {
            // Methode aufrufen
            _realComObject.GetType().InvokeMember(propertyName, BindingFlags.OptionalParamBinding | BindingFlags.SetProperty, null, _realComObject, new object[1] { value });
        }

        /// <summary>
        /// Ruft den Wert einer Eigenschaft ab (für Eigenschaften, die Objekte zurückgeben).
        /// </summary>
        /// <param name="propertyName">Eigenschaftsname</param>
        /// <returns>Rückgabeobjekt</returns>
        public ComObject GetObjectReturningProperty(string propertyName) {
            // Methode aufrufen und Rückgabewert zurückgeben
            object result = _realComObject.GetType().InvokeMember(propertyName, BindingFlags.GetProperty | BindingFlags.OptionalParamBinding, null, _realComObject, new object[0]);

            // Wenn ein Objekt zurückgegeben wurde ...
            if (result != null)
                // Rückgabeobjekt in Wrapper einpacken und zurückgeben
                return new ComObject(result);

            // Nichts zurückgeben
            return null;
        }

        #region IDisposable Member

        /// <summary>
        /// Verwendete Ressourcen freigeben-
        /// </summary>
        public void Dispose() {
            // Wenn das COM-Objekt nocht existiert ...
            if (_realComObject != null) {
                // COM-Objekt freigeben und entsorgen
                Marshal.ReleaseComObject(_realComObject);
                _realComObject = null;
            }
        }
        #endregion
    }
}

3.511 Beiträge seit 2005
vor 9 Jahren

Ich möchte auch keine extra Dll ausliefern

Warum nicht? Was spricht dagegen? Ggf. mit ILMerge die DLLs mergen. Wobei das IMHO immer sehr unschön ist.

Das Problem welches du hast ist einfach, dass XP nun mal diese Funktionalität nicht bietet. Ob nun Late-Binding oder Peng. Was nicht da ist, kann nicht aufgerufen werden.

"Jedes Ding hat drei Seiten, eine positive, eine negative und eine komische." (Karl Valentin)

M
martin_salo Themenstarter:in
27 Beiträge seit 2015
vor 9 Jahren

Die Funktionalität ist aber da. Wenn ich den Source der alten Methode unverändert auf einen Windows Server 2008 bringe und dort mit dem Visual Studio 2010 Express kompiliere und dieses Kompilat dann zum WindowsXP User schicke, dann wird die Datei über Shell32 entpackt. Die Funktionalität ist also da.

16.835 Beiträge seit 2008
vor 9 Jahren

Es kann gut sein, dass Du bei solchen PInvokes pro Betriebssystem-Version eine extra Anpassung durchführen musst.
Es macht hier aber gut gemeint wirklich absolut keinen Sinn das Rad neu zu erfinden; gerade zum Thema Kompatibilität.
Am Markt gibt es auch ZIP Bibliotheken deren Lizenz das Einbetten erlaubt und nichts extra mitgeliefert werden muss.
Im Zweifel musst Du mit einer externen Lib sogar weniger mitliefern als pro Betriebssystem-Version eine extra DLL.

Gelöschter Account
vor 9 Jahren

Der COM Wrapper(ComObject) ist nicht darauf vorbereitet das man ComObject direkt als Argument an eine Methode übergibst.
Er reicht das Argument 1:1 weiter und die Shell kennt ComObject natürlicht nicht. (Das kein TypeMismatch ausgelöst wird ist allerdings merkwürdig)

Der einfachste Weg wäre das private Feld "_realComObject" von ComObject hier mittels eines Properties "RealComObject" freizulegen und den Aufruf anzupassen. Also:

TempFolder.InvokeFunction("CopyHere", FolderItems.RealComObject);

Ginge natürlich noch schöner in dem man in die Invoke Methoden von ComObject auf die Verwendung von Arg=>ComObject vorbereitet.

M
martin_salo Themenstarter:in
27 Beiträge seit 2015
vor 9 Jahren

Es funktioniert. Vielen Dank Sebastian 😃

Hier nochmal der Source. Einfach in ein Konsolenprojekt einfügen. Es läuft auf Windows Server 2008/2012 und Windows 7. XP konnte ich bis jetzt noch nicht testen, ich hoffe es läuft dort dann auch.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Shell32;
using System.Reflection;
using System.Runtime.InteropServices;

namespace ConsoleApplication1 {
    class Program {

        [STAThread]
        static void Main(string[] args) {

            // Pfad zu irgendeiner Zip Datei:
            string DownloadPath = @"C:\Temp\a.zip";

            // Datei soll im Temp Folder entpackt werden.
            string ExtractedFileFolder = System.IO.Path.GetTempPath() + Guid.NewGuid().ToString();
            System.IO.Directory.CreateDirectory(ExtractedFileFolder);

            ComObject Shell32 = new ComObject("Shell.Application");
            ComObject TempFolder = Shell32.InvokeObjectReturningFunction("NameSpace", ExtractedFileFolder);
            ComObject x = Shell32.InvokeObjectReturningFunction("NameSpace", DownloadPath);
            ComObject FolderItems = x.InvokeObjectReturningFunction("Items");
            TempFolder.InvokeFunction("CopyHere", FolderItems.RealComObject);
        }
    }

    /// <summary>
    /// Allgemeiner Wrapper, um spätgebunden mit COM-Objekten (bzw. ActiveX-Komponenten) zu arbeiten.
    /// </summary>
    public class ComObject : IDisposable {
        // COM-Objekt
        private object _realComObject= null;
        public object RealComObject {
            get {
                return _realComObject;
            }
            set { 
                _realComObject = value; 
            }
        }
        
        /// <summary>
        /// Übernimmt einen vorhanden Verweis auf ein COM-Objekt.
        /// </summary>
        /// <param name="injectedObject">Vorhandenes COM-Objekt</param>
        public ComObject(object injectedObject) {
            // Wenn kein Objekt angegeben wurde ...
            if (injectedObject == null)
                // Ausnahme werfen
                throw new ArgumentNullException("injectedObject");

            // Wenn das angegebene Objekt kein COM-Objekt ist ...
            if (!injectedObject.GetType().IsCOMObject)
                // Ausnahme werfen
                throw new ArgumentException("Das angegebene Objekt ist kein COM-Objekt!", "injectedObject");

            // Verweis übernehmen
            _realComObject = injectedObject;
        }

        /// <summary>
        /// Erzeugt ein neues COM-Objekt anhand einer COM ProgId.
        /// </summary>
        /// <param name="progId"></param>
        public ComObject(string progId) {
            // Wenn keine ProgId angegeben wurde ...
            if (string.IsNullOrEmpty(progId))
                // Ausnahme werfen
                throw new ArgumentException();

            // Typinformationen über die ProgId ermitteln 
            Type comType = Type.GetTypeFromProgID(progId);

            // Wenn keine Typeninformationene gefunden wurden ...
            if (comType == null)
                // Ausnahme werfen
                throw new TypeLoadException(string.Format("Fehler beim Laden der Typinformationen zu ProgId '{0}'.", progId));

            // Instanz erzeugen
            _realComObject = Activator.CreateInstance(comType);
        }

        /// <summary>
        /// Ruft eine Funktion auf, die als Rückgabewert ein COM-Objekt (also keinen primitiven Datentyp) zurückgibt.
        /// </summary>
        /// <param name="functionName">Funktionsname</param>
        /// <param name="parameters">Parameter</param>
        /// <returns>Rückgabeobjekt</returns>
        public ComObject InvokeObjectReturningFunction(string functionName, params object[] parameters) {
            // Methode aufrufen
            object result = _realComObject.GetType().InvokeMember(functionName, BindingFlags.InvokeMethod | BindingFlags.OptionalParamBinding, null, _realComObject, parameters);

            // Wenn ein Objekt zurückgegeben wurde ...
            if (result != null)
                // Rückgabeobjekt in Wrapper einpacken und zurückgeben
                return new ComObject(result);

            // Nichts zurückgeben
            return null;
        }

        /// <summary>
        /// Ruft eine Funktion auf.
        /// </summary>
        /// <param name="functionName">Funktionsname</param>
        /// <param name="parameters">Parameter</param>
        /// <returns>Rückgabewert</returns>
        public object InvokeFunction(string functionName, params object[] parameters) {
            // Methode aufrufen und Rückgabewert zurückgeben
            return _realComObject.GetType().InvokeMember(functionName, BindingFlags.InvokeMethod | BindingFlags.OptionalParamBinding, null, _realComObject, parameters);
        }

        /// <summary>
        /// Ruft eine Prozedur auf.
        /// </summary>
        /// <param name="procedureName">Prozedurname</param>
        /// <param name="parameters">Parameter</param>
        public void InvokeProcedure(string procedureName, params object[] parameters) {
            // Methode aufrufen
            _realComObject.GetType().InvokeMember(procedureName, BindingFlags.InvokeMethod | BindingFlags.OptionalParamBinding, null, _realComObject, parameters);
        }

        /// <summary>
        /// Ruft den Wert einer Eigenschaft ab.
        /// </summary>
        /// <param name="propertyName">Eigenschaftsname</param>
        /// <returns>Rückgabewert</returns>
        public object GetProperty(string propertyName) {
            // Methode aufrufen und Rückgabewert zurückgeben
            return _realComObject.GetType().InvokeMember(propertyName, BindingFlags.GetProperty | BindingFlags.OptionalParamBinding, null, _realComObject, new object[0]);
        }

        /// <summary>
        /// Legt den Wert einer Eigenschaft fest.
        /// </summary>
        /// <param name="propertyName">Eigenschaftsname</param>
        /// <param name="parameters">Parameter</param>
        public void SetProperty(string propertyName, object value) {
            // Methode aufrufen
            _realComObject.GetType().InvokeMember(propertyName, BindingFlags.OptionalParamBinding | BindingFlags.SetProperty, null, _realComObject, new object[1] { value });
        }

        /// <summary>
        /// Ruft den Wert einer Eigenschaft ab (für Eigenschaften, die Objekte zurückgeben).
        /// </summary>
        /// <param name="propertyName">Eigenschaftsname</param>
        /// <returns>Rückgabeobjekt</returns>
        public ComObject GetObjectReturningProperty(string propertyName) {
            // Methode aufrufen und Rückgabewert zurückgeben
            object result = _realComObject.GetType().InvokeMember(propertyName, BindingFlags.GetProperty | BindingFlags.OptionalParamBinding, null, _realComObject, new object[0]);

            // Wenn ein Objekt zurückgegeben wurde ...
            if (result != null)
                // Rückgabeobjekt in Wrapper einpacken und zurückgeben
                return new ComObject(result);

            // Nichts zurückgeben
            return null;
        }

        #region IDisposable Member

        /// <summary>
        /// Verwendete Ressourcen freigeben-
        /// </summary>
        public void Dispose() {
            // Wenn das COM-Objekt nocht existiert ...
            if (_realComObject != null) {
                // COM-Objekt freigeben und entsorgen
                Marshal.ReleaseComObject(_realComObject);
                _realComObject = null;
            }
        }
        #endregion
    }
}
Gelöschter Account
vor 9 Jahren

Die Referenz auf die Shell solltest du eigentlich nicht mehr brauchen,
Wenns ohne nicht geht, stecken noch Early-Bind Abhängigkeiten in der Lösung.

M
martin_salo Themenstarter:in
27 Beiträge seit 2015
vor 9 Jahren

Du hast Recht. Ich hatte es hingeschrieben ohne nachzudenken. Ich habe es editiert.