Blog
SEO - Homepage in Google Place eintragen!
Firma bzw. Verein bei Google Place anmelden
1. Einleitung
Seit geraumer Zeit investiert Google in Produkte die Geodaten beinhalten. Es handelt sich dabei um digitale Informationen denen Lokalitäten zugewiesen werden können.
So werden in Google Maps schon lange Ärzte, Hotels usw. angezeigt. Diese Unternehmen werden dabei automatisch in Google Maps in der Karte angezeigt. In der Hilfe kann man dazu folgendes nachlesen:
„Welche Unternehmen angezeigt werden, wird anhand unseres proprietären Algorithmus festgelegt. Diese Unternehmen ändern sich normalerweise von Zeit zu Zeit. Unser Ziel ist es, auf der Karte Orte darzustellen, die für die Nutzer interessant sein könnten oder ihnen bei der Orientierung auf der Karte helfen. Wir nehmen keine Bezahlung dafür an, dass bestimmte Einträge auf der Karte angezeigt werden. „(Graue Symbole in Google : Suchergebnisse - Google Places-Hilfe, 14.12.2010, http://www.google.com/support/places/bin/answer.py?hl=de&answer=174115).
Man hat also derzeit noch keinen Einfluss, was beim Kartendienst angezeigt wird und was nicht.
2. Google Place
Einer der neueren Plattformen ist Google Place bei der man hingegen die Möglichkeit hat Unternehmen und Vereine in den Kartendienst ein zu tragen (sofern man sich als diese Firma authentifizieren kann).
Dies schafft die Möglichkeit noch effizienter zu werben, denn Suchergebnisse mit übereinstimmenden Standorten werden stärker gewertet.
Such man also z.B. nach „Webdesigner in Wien“ sieht das Ergebnis wie folgt aus.
Aber auch in der „normalen“ Google Suche werden, vorausgesetzt der Name eines Standorts ist vorhanden, die Suchergebnisse entsprechend ausgegeben.
Suchen mit Standorten können wie folgt aussehen. „pension feldkirch“,“ anwaltskanzlei feldkirch“, „masseur feldkirch“ usw.
Wie bereits erwähnt, werden Suchergebnisse mit übereinstimmendem Standort stärker bewertet (siehe Abbildung 2 rot umrahmt) und sind somit ganz oben bei den Ergebnissen aufgelistet (siehe Abbildung 2). Erst danach kommen die normalen Suchergebnisse (siehe Abbildung 2 grüner umrahmt)
3. Bei Google Place eigenen Verein / Firma Anmelden
- Wechseln Sie zur Seite http://Google.de/places
- Um sich bei Google Place an zu melden benötigt man einen Google-Account
- Klicken Sie auf anmelden / Jetzt anmelden
- Formular ausfüllen
- Beim Bestätigungsemail Link anklicken
- Anschließend wechseln Sie nochmals zur Seite http:// Google.de/places und loggen sich ein, sofern sie noch nicht eingeloggt sind
- Wählen Sie danach „Ein Unternehmen hinzufügen“
- Wählen Sie das Land aus und geben Sie eine Telefonnummer (diese kann muss aber nicht stimmen) ein
- Anschließend können Sie das Formular ausfüllen
- Falls die angegebene Telefonnummer nicht stimmt, müssen Sie am Schluss die Authentifizierung per Post wählen
- Wichtig bei diesem Formular sind die Kategorien, diese sind ausschlaggebend, bei welchen Suchanfragen das Unternehmen / der Verein angezeigt wird. Sie sollten sich daher genau überlegen wie ein Außenstehender nach ihrem Unternehmen sucht. Halten Sie daher die Kategorien eher allgemein
- Zum Schluss können noch Details wie Fotos und Youtube Videos , Öffnungszeiten hinzugefügt werden.
- Anschließend muss man auswählen wie man authentifiziert werden will. Das geht entweder über die Post oder per SMS.
- Nach der Authentifizierung hat man es geschafft das Unternehmen in Google Place anzumelden.
Bei Fragen, Anregungen einfach die Kommentarfunktion nutzen oder mich per Mail kontaktieren.
lg
Martin
Links zum Thema: http://enno.verbrennung.org/2010-12-17-smo-und-seo-trends-2011/
Schlüsselwörter: Google, SEO, Get
zuletzt geändert: 21. Dezember 2010 19:38
Link zu diesem Artikel: (in die Zwischenablage)
IIS 7 mit PHP Virtuelles Verzeichnis RewriteRules
Heute habe ich probiert unser CMS mit dem IIS 7 Webserver von MS mit der Webtechnologie PHP zu verwenden.
Also als erstes muss man unter Windows 7 in Systemsteuerung\Programme\Programme und Funktionen\ "Windows Funktionen aktivieren oder deaktivieren" den Internetinformationsdienst installieren.
Das tolle ab der IIS Version 7 ist, dass man ganz Einfach die verschiedensten Module bequem installieren kann.
Mit dem Aufrufen der Seite http://php.iis.net/ und dem klick auf "Install PHP" wird der "Webplatform Installer" aufgerufen sofern er installiert wurde. Ansonsten kann man ihn einfach über den Internet Explorer nach installieren. Danach wählt man im Programm auf PHP installieren. Siehe Abbildung.
Als nächstes erstellt man ein Virtuelles Verzeichnis damit wir dort unsere PHP Anwendung speichern können.
Das macht man im IIS-Manager unter Sites Default Web Site --> Rechter Mausklick --> virtuelles Verzeichnis hinzufügen. Beim physikalischen Verzeichnis müsst ihr jetzt noch die Berechtigungen setzen. Wichtig ist dass der IIS_IUSRS ausreichend Rechte (also zumindest die Dateien im angegeben Verzeichnis lesen kann) hat. Hier gibts das ausführlicher.
Zur Erinnerung, bei den älteren IIS Versionen musste man noch selber die PHP Binaries herunterladen und konfigurieren (Berechtigungen, Pfade usw. setzen), was sehr nerven zerreißend sein konnte.
Eine weitere Erneuerung beim IIS 7 sind die unterstützten RewriteRules. Früher musste man für diesen Komfort auf Drittanbieter-Tools zurückgreifen.
Bei unserem CMS werden auch RewriteRules verwendet und so kam ich in den Genuss dieses Feature mit seinen Eigenheiten kennenlernen zu dürfen. Die .htaccess kann vom PHP Projekt aber nicht so ohne weiteres übernommen werden. Die Rules werden nämlich beim IIS in der web.conf gespeichert und müssen daher aus der .htacess importiert werden. (Zu finden im IIS-Manager unter RewriteRules)
Außerdem müssen vor dem Importvorgang der .htaccess die Pfade angepasst werden. Die relativen Pfade der .htaccess erkennt der IIS-Manager nämlich nicht. So musste ich zum Pfad den Namen des Virtuellen Verzeichnisses hinzufügen.
Nach dem also eigentlich alles richtig eingestellt sein sollte erhielt ich jedoch die Meldung:
"Das Anforderungsfilterungsmodul ist so konfiguriert, dass Pfade in der URL abgelehnt wird, die einen Abschnitt "hiddenSegment" enthalten."
Nach kurzem Googeln fand ich heraus, dass im IIS-Manager unter Anforderungsfilter in der Tab "Ausgeblendete Segmente", Segmente aufgelistet sind, die nicht in der URL oder/RewriteRule (bin mir nicht sicher welches von beiden oder ob es beide betrifft) vorkommen dürfen.
Nach dem Entfernen des entsprechenden Segments funktionierte es dann.
Schlüsselwörter: IIS, PHP
zuletzt geändert: 13. November 2010 09:35
Link zu diesem Artikel: (in die Zwischenablage)
C# Plugins mit AppDomains realisieren
1. Einführung
Im letzten Post konnte man ein einfaches Beispiel sehen, wie während der Laufzeit „Plugins“ geladen und ausgeführt wurden.Wir möchten aber die Plug-Ins von unserer Hauptanwendung isolieren und eigene Sicherheitsrichtlinien für diese festlegen. So heißt es z.B. in der MSDN
„Verwenden Sie Anwendungsdomänen, um Aufgaben zu isolieren, die einen Prozess zum Absturz bringen könnten. Wenn der Zustand der AppDomain, in der eine Aufgabe ausgeführt wird, instabil wird, kann die AppDomain ohne Auswirkungen auf den Prozess entladen werden. Dies ist wichtig, wenn ein Prozess über einen längeren Zeitraum ohne Neustart ausgeführt werden muss. Sie können auch Anwendungsdomänen verwenden, um Aufgaben zu isolieren, die keine Daten gemeinsam verwenden sollten.“
(AppDomain-Klasse, 22.08.10, http://msdn.microsoft.com/de-de/library/system.appdomain.aspx)
Ein .Net Prozess enthält mindestens eine AppDomain. Diese StandardAppDomain wird auch Default AppDomain genannt. Jede AppDomain hat ihren eigenen virtuellen Adressenspeicher. Ein Prozess kann also mehrere AppDomains enthalten.

Da jede AppDomain ihren eigenen virtuellen Adressenspeicher hat, ergeben sich folgende Eigenschaften:
- AppDomains können nicht auf Objekte von anderen AppDomains direkt zugreifen
- AppDomains können entladen werden, mit Ausnahme der Default AppDomain
- AppDomains können unterschiedliche Sicherheitsprivilegien vergeben werden
- AppDomains können unterschiedlich konfiguriert werden (AppDomainSetup)
Um das Verändern der Plugindatei während der Laufzeit zu ermöglichen, gibt es das Feature Shadow Copy.
Dabei wird die zu ladende Assembly in ein temporäres Verzeichnis kopiert. Die Originaldatei kann somit jederzeit ersetzt werden. Siehe dazu auch AppDomain and Shadow Copy (AppDomain and Shadow Copy, 22.08.10, http://blogs.msdn.com/b/junfeng/archive/2004/02/09/69919.aspx )
Bestimmte Grundtypen werden in jeder AppDomain benötigt. Um Ressourcen zu schonen, gibt es eine AppDomain neutrale Zone. In diese wird z.B. die MSCorLib.dll geladen da diese den Typ System.Object, System.Integer usw. enthält. Alle AppDomains können auf diese „Domain-Neutral Assemblies“ zugreifen.
Um auf Objekte von einer AppDomain auf die andere zugreifen zu können, müssen die Objekte MarshalByRefObject implementieren.
2. Sonstige Bemerkungen
Bibliotheken die mit Assemblies.Load geladen werden, „landen“ in der DefaultAppDomain. Diese Dateien werden erst nach dem Beenden des Prozesses wieder freigegeben.Hier ein Beispielcode:
public static void Main(string[] Args) { //Gib die AppDomain zurück in der wir uns befinden System.Console.WriteLine(AppDomain.CurrentDomain); //AppDomain erstellen AppDomain appDomain = AppDomain.CreateDomain("AppDomain #2"); //Schauen wir ob es sich um die Default Domain handelt System.Console.WriteLine(AppDomain.CurrentDomain + " " + AppDomain.CurrentDomain.IsDefaultAppDomain()); System.Console.WriteLine(appDomain + " " + appDomain.IsDefaultAppDomain()); Print(AppDomain.CurrentDomain.GetAssemblies()); //In unsere AppDomain lib laden appDomain.DoCallBack(new CrossAppDomainDelegate(delegate() { System.Console.WriteLine(AppDomain.CurrentDomain); Assembly.LoadFrom(@"C:\Users\martin\Plugin.dll"); })); Print(appDomain.GetAssemblies()); } static void Print(System.Reflection.Assembly[] Assemblies) { int i = 0; foreach (System.Reflection.Assembly assem in Assemblies) System.Console.WriteLine("[{0}] {1}", ++i, assem.GetName().Name); }
Die Ausgabe dazu sieht wie folgt aus:
Die Ausgabe dazu sieht wie folgt aus: Name:ConsoleApplication1.vshost.exe Keine Kontextrichtlinien vorhanden. Name:ConsoleApplication1.vshost.exe Keine Kontextrichtlinien vorhanden. True Name:AppDomain #2 Keine Kontextrichtlinien vorhanden. False [1] mscorlib [2] Microsoft.VisualStudio.HostingProcess.Utilities [3] System.Windows.Forms [4] Microsoft.VisualStudio.HostingProcess.Utilities.Sync [5] System [6] Microsoft.VisualStudio.Debugger.Runtime [7] mscorlib.resources [8] vshost [9] System.Core [10] System.Xml.Linq [11] System.Data.DataSetExtensions [12] System.Data [13] System.Xml [14] WindowsBase [15] ConsoleApplication1 [16] System.Drawing [17] Accessibility [18] DomainManager [19] IPlugin Name:AppDomain #2 Keine Kontextrichtlinien vorhanden. [1] mscorlib [2] Microsoft.VisualStudio.HostingProcess.Utilities [3] ConsoleApplication1 [4] Plugin
Mit dem AppDomains sind wir unserem Ziel, einen robusten und flexiblen Plugin-Manager zu schreiben, einiges näher gekommen. Als nächstes gilt es die Hostanwendung vor Exceptions der Plugins zu schützen. Das heißt ein Plugin soll nicht die komplette Anwendung crashen.
Eine Möglichkeit wäre für die Plugins einen Wrapper oder einen Proxy zu erstellen. Dazu müssen die Plugin-Funktionen mit einem Try/Catch Block versehen werden. Bei einer Exception könnte man auch ein Event auslösen.
Beispiel Code:
public interface IPlugin { public void Execute(); public void Connect(object info); } public class PluginProxy : System.MarshalByRefObject, IPlugin { private IPlugin m_IPlugin; //Other Code for Events etc. void Init(string assemblyName, string typeName) { m_IPlugin = (IPlugin)Activator.CreateInstance(assemblyName, typeof(IPlugin)); } public void Execute() { try { m_IPlugin.Execute(); } catch (Exception ex) { //Event feuern this.OnException(ex); } } public void Connect(object info) { try { m_IPlugin.Connect(info); } catch (Exception ex) { //Event feuern this.OnException(ex); } } }
Damit kann man schon einige Exceptions abfangen. Allerdings sind wir nicht in der Lage auf interne Ereignisse der Plugins zu reagieren. Das heißt, wenn ein Plugin einen Timer oder ein FileSystemWatcher-Ereignis abonniert (könnte genauso irgendein anderes Ereignis sein) das eine Exception auslöst, können wir diese nicht abfangen.
Die Host Anwendung wird also abstürzen wenn eines der internen Plugin –Ereignisse eine Exception wirft. Da nützen uns auch die AppDomains nichts, die die Plugins isolieren. Zumindest kenne ich in diesem Zusammenhang keine Lösung, die das Abstürzen der Hostanwendung verhindern würde. Auch im Internet habe ich dazu nichts gefunden.
Jedoch gibt es einen anderen Lösungsansatz, den ich aufgegriffen habe.
„In den Versionen 1.0 und 1.1 von .NET Framework wird eine unbehandelte Ausnahme, die in einem anderen als dem Hauptanwendungsthread auftritt, von der Laufzeit abgefangen und führt daher nicht zum Beenden der Anwendung. Das UnhandledException-Ereignis kann also ausgelöst werden, ohne dass die Anwendung beendet wird. Ab .NET Framework Version 2.0 wurde diese Option für unbehandelte Ausnahmen in untergeordneten Threads entfernt, da solche stillen Fehler zu einer Verschlechterung der Leistung, beschädigten Daten und Lockups führten, was insgesamt schwer zu beheben war.“ (Ausnahmen in verwalteten Threads, 22.08.10, http://msdn.microsoft.com/de-de/library/ms228965(v=VS.100).aspx)
Die Idee ist also, die Anwendung bei unbehandelten Ausnahmen auf das Verhalten der Common Language Runtime auf Versionen 1.0 und 1.1 zurück zu setzen, damit der Haupt-Thread weiter läuft.
Das macht man indem man mit der Funktion ICLRPolicyManager::SetUnhandledExceptionPolicy die gewünschte Policy setzt. Leider stellt der AppDomainManager keine passende managed Funktion bereit. Deshalb kann man das Runtime verhalten in der Application.config wie folgt setzen.
<?xml version="1.0" encoding="utf-8" ?> <configuration> <runtime> <legacyUnhandledExceptionPolicy enabled="true" /> </runtime> </configuration>
Dabei muss unbedingt beachtet werden, dass sich der Debugger mit der gesetzten Abwärtskompatibilität falsch verhält.
Um zu sehen ob das Programm auch erwartungsgemäß funktioniert, muss man das kompilierte Programm manuell, ohne den Debugger bzw. Visual Studio 2008 starten.
Für reine Debugging Zwecke, die nicht die Plugins betreffen, würde ich die Abwärtskompatibilität temporär entfernen.
Ein Beispiel bzgl. der gesetzten Abwärtskompatibilität wird in diesem Blog Eintrag demonstriert. Allerdings finde ich, dass der Autor die Demo unnötig mit seiner eigenen Exception Klasse aufgebläht hat. Wie man das viel einfacher und mit weniger Code machen kann, sieht man in meiner PluginManger bzw. PluginDomain Klasse.
3. AppDomain und Plugin erstellen
Bevor man aber überhaupt die Plugin-Isolation machen kann, muss man sich Gedanken machen wie man die Plugins in Kombination mit den AppDomains handhabt.
Meine Idee ist für jedes Plugin eine separate AppDomain zu erstellen. Das Erstellen der AppDomain für das Plugin soll eine Klasse für uns erledigen die PluginDomain heißt. Es handelt sich dabei um eine generische Klasse, damit man den Plugin-Typ angeben kann. Den Generischen Typ brauchen wir später beim Erstellen der Plugin-Instanz und um die Instanz speichern. Das macht das Arbeiten mit dem Plugin einfacher, da der „Cast“ Vorgang weg fällt.
Die Klasse wird mit dem Konstruktor PluginDomain(string assemblyPath) initialisiert, wobei der Parameter assemblyPath den Pfad des Plugins enthält. Im Konstruktor wird die AppDomain mit Hilfe der Klasse AppDomainSetup konfiguriert und erstellt.
//Beim erstellen der AppDomain wird die Funktion GetInterfaceTypes aufgerufen appDomainSetup.AppDomainInitializer = new AppDomainInitializer(GetInterfaceTypes); //Parameter der Funktion übergeben, Pfad des Plugins und nach welchem Interface gesucht werden soll appDomainSetup.AppDomainInitializerArguments = new string[] { assemblyPath, typeof(T).ToString() };
Der Eigenschaft AppDomainSetup.AppDomainInitializer wird der delegate GetInterfaceTypes zu gewiesen. Wenn die AppDomain initialisiert wird, wird die Methode GetInterfaceTypesu in der erstellten AppDomain ausgeführt. Wir wollen in die erstellte AppDomain die Plugin-Assembly laden. Theoretisch könnte man dieses Vorhaben auch mit der Funktion AppDomain.DoCallBack realisieren.
/// <summary> /// Diese Methode sucht in der übgergebenen Datei die erste Klassen die das übergebene /// Interface implementieren. /// </summary> /// <param name="args">Der erste Parameter ist der Datepfad der Datei die nach dem gesuchten /// Interface durchsucht werden soll. /// Im zweiten Parameter wird das Interface übergeben.</param> private static void GetInterfaceTypes(string[] args) { AppDomain appDomain = System.AppDomain.CurrentDomain; //Laden wir die Plugin Assembly in unsere AppDomain System.Reflection.Assembly assembly = System.Reflection.Assembly.LoadFrom(args[0]); String pluginType = String.Empty; foreach (Type type in assembly.GetTypes()) { //Check only Public Types if (type.IsPublic) { //Check only not abstract Types if (!type.IsAbstract) { //look up the interface Type typeInterface = type.GetInterface(args[1], true); if (typeInterface != null) { //return the type which implements our interface pluginType = type.FullName; } } } } //Daten über die Methode SetData setzen, weil der AppDomainInitializer-Delegate //kein Rückgabewert hat. Mit GetData können die Daten wieder abgeholt werden //http://msdn.microsoft.com/en-us/library/37z40s1c.aspx appDomain.SetData(_GetDataPluginTypes, pluginType); }
In der Methode GetInterfaceTypes welche unsere CallBack Funktion ist, durchsuchen wir die Assembly nach dem Typ der unser Plugin-Interface implementiert. Der Vorgang ist ähnlich diesem. Denn gefunden Typen wollen wir zurückgeben damit wir eine Instanz vom Plugin erstellen können. Weil der AppDomainInitializer-Delegate kein Rückgabewert besitzt müssen wir mit der Methode AppDomain.SetData und GetData arbeiten. Mit diesen Methoden kann man Daten zwischen AppDomains transportieren.
Bei diesen Methoden muss man folgendes beachten: Angenommen wir würden das Objekt typeInterface mit SetData in unserer Plugin AppDomain verschicken und in der Default AppDomain wieder mit GetData abholen, hätte das folgenden Effekt. Der Typ typeInterface ist in der Default AppDomain nicht bekannt, da die Plugin-Assembly nur in der Plugin-AppDomain geladen ist. Die Plugin-Assembly wird also auch in die Default AppDomain geladen.
Nach dem die AppDomain erstellt und die GetInterfaceTypes Methode ausgeführt wurde können wir den Plugin-Typ mit AppDomain.GetData() abholen.
Jetzt wo wir wissen welcher Typ unser Plugin implementiert, erstellen wir mit CreateInstanceFromAndUnwrap die Plugin-Instanz. Die Instanz speichern wir in die generische Eigenschaft die logischerweise Instance heißt.
_Instance = this._appDom.CreateInstanceFromAndUnwrap(_AssemblyPath, pPluginType) as T;
Noch bevor die Instanz erstellt wird, haben wir von der AppDomain das UnhandledException Event angemeldet.
_appDom.UnhandledException += new UnhandledExceptionEventHandler(AppDom_UnhandledException);
Diese tritt auf, wenn eine Ausnahme in der AppDomain nicht abgefangen wurde. Dieses Event ist dafür gedacht, dass man den Fehler mitloggen kann. Nach dem das Event gefeuert wurde, wird das Programm normalerweise beendet.
Da wir aber bei unserem Programm die Abwärtskompatibilität aktiviert haben, wird das Programm weiter ausgeführt. Wenn eine Exception in einer AppDomain bzw. in einem Plugin auftritt, werden wir dieses Plugin entfernen, in dem wir die AppDomain entladen in dem es sich befindet. Die Default AppDomain kann nicht entladen werden.
Wir werden in der Funktion AppDom_UnhandledException eine Key setzen damit wir im Pluginmanager wissen, welche AppDomain wir entladen müssen. (Wir machen also fast das gleiche wir hier, nur einfacher.)
Über die UnhandledExceptionEventArgs können wir auf das Exception Objekt zugreifen. In dieses Tragen wir über
System.AppDomain appDomain = sender as System.AppDomain; Exception exception = e.ExceptionObject as Exception; exception.Data.Add("PluginDomain", this);
das Plugin ein, das die Exception ausgelöst hat. Der PluginManager hat ebenfalls von alle PluginDomains das UnhandledException Event registriert.
Er führt darin einige Prüfungsroutinen aus, um auch sicher zu stellen dass die Exception auch von einem unserer Plugins geworfen wurde.
Anschließend versucht er sie zu entladen.
if (appDomain.Equals(plugindomain.AppDomain) && plugindomain.AppDomain.IsDefaultAppDomain().Equals(false)) { new Thread(delegate() { try { Console.WriteLine("Unloading the offending application domain..."); AppDomain.Unload(appDomain); appDomain = null; plugindomain = null; } catch (System.Threading.ThreadAbortException abortexception) { //(AppDomain..::.Unload beendet Threads mit Thread..::.Abort //http://msdn.microsoft.com/de-de/library/cyayh29d.aspx appDomain = null; plugindomain = null; System.Console.WriteLine(abortexception); } catch (CannotUnloadAppDomainException unloadexception) { System.Console.WriteLine(unloadexception); } catch (Exception ex) { Console.WriteLine(ex.ToString()); } finally { OnPluginChangedEvent(new PluginManager.PluginChangedArgs(plugindomain, ChangedAction.Remove)); } Console.WriteLine("Unloaded the offending application domain..."); }).Start();
Beim Entladen gilt folgendes zu beachten.
Wurde eine AppDomain entladen, sind alle darin enthaltenen Typen nicht mehr verwendbar. Ein Zugriff auf einen entladenen Typ würde eine AppDomainUnloadedException auslösen. Die entladene AppDomain gibt alle geladenen Assemblies wieder frei.
Ist in einer AppDomain ein oder noch mehrere Threads aktiv wird eine ThreadAbortException geworfen. Das bedeudet, dass beim „unloaden“ einer AppDomain eine ThreadAbortException auftreten kann. Mehr Informationen siehe dazu in der MSDN. Nachdem entladen der AppDomain macht sich der GC auf, um die Ressourcen von der AppDomain frei zu geben.
Am besten ihr schaut euch noch die Demo an. In der Demo ist ein Plugin vorhanden, das via Timer eine ArgumentNullException wirft. Die Domain wird anschließend entladen. Die aktivierte Abwärtskompatibilität verhindert, dass die Host-Anwendung abstürzt. Über eine Rückmeldung freue ich mich.
Schlüsselwörter: Plugins, AppDomain, C#, plugins host crash, UnhandledException
zuletzt geändert: 01. September 2010 10:08
Link zu diesem Artikel: (in die Zwischenablage)
C# Anwendung Plugin fähig machen
In diesem Beispiel sieht man wie man ein einfaches Plugin-System realisiert. Demonstriert wird das ganze anhand eines „Mathematikprogramms“.
/// <summary> /// Definiert das "Aussehen" unseres Plugins /// </summary> public interface IPlugin { /// <summary> /// Name des Plugins /// </summary> String Name { get; set; } /// <summary> /// Beschreibung des Plugins /// </summary> String Description { get; set; } /// <summary> /// Berechnet die Eingabe /// </summary> /// <param name="pInput">Eingabe die ausgewertet werden soll.</param> /// <returns>Gibt das berechnete Ergebnis zurück</returns> String Calc(String pInput); }
Das bedeutet, dass jeder, der unser Interface implementiert zwei Properties "Name" und "Description" definiert und zu guter Letzt eine Funktion "Calc".
Im Namen speichern wir den Funktionsnamen der Mathematischen Funktion. Als z.B. „sqrt“. In der Funktion Calc berechnen wir den Übergabewert pInput und geben das Ergebnis als String zurück. Natürlich wäre eine andere Mathematische Funktion wie z.B. Modulo interessanter zu implementieren.Als nächstes gilt es die Mathematik Klasse MathCore zu erstellen, welche die Plugins lädt und erstellt.
Als erstes wird die Funktion LoadPlugins aufgerufen. Dieser übergeben wir den Dateipfad in dem sich die Plugins befinden.
Als nächstes gilt es die Mathematik Klasse MathCore zu erstellen, welche die Plugins lädt und erstellt.
Als erstes wird die Funktion LoadPlugins aufgerufen. Dieser übergeben wir den Dateipfad in dem sich die Plugins befinden.
public void LoadPlugins(String pPluginPath) { String[] files = Directory.GetFiles(pPluginPath); foreach (String file in files) { FileInfo fileInfo = new FileInfo(file); if (fileInfo.Extension.Equals(".dll")) { Dictionary<string, object> dictionary = GetModul(file, typeof(IPlugin.IPlugin)); foreach (var a in dictionary) { Plugins.Add(a.Key,a.Value); } } } }
private static Dictionary<string, object> GetModul(string pFileName, Type pTypeInterface) { //Hier speichern wir unsere Plugins Dictionary<string, object> interfaceinstances = new Dictionary<string, object>(); //Loads an assembly given its file name or path. Assembly assembly = Assembly.LoadFrom(pFileName); // http://msdn.microsoft.com/de-de/library/t0cs7xez.aspx // Assembly Eigenschaften checken foreach (Type type in assembly.GetTypes()) { if (type.IsPublic) // Ruft einen Wert ab, der angibt, ob der Type als öffentlich deklariert ist. { if (!type.IsAbstract) //nur Assemblys verwenden die nicht Abstrakt sind { // Sucht die Schnittstelle mit dem angegebenen Namen. Type typeInterface = type.GetInterface(pTypeInterface.ToString(), true); //Make sure the interface we want to use actually exists if (typeInterface != null) { try { object activedInstance = Activator.CreateInstance(type); if (activedInstance != null) { interfaceinstances.Add(type.Name, activedInstance); } } catch (Exception exception) { System.Diagnostics.Debug.WriteLine(exception); } } typeInterface = null; } } } assembly = null; return interfaceinstances; }
Der Funktion wird der Pfad der Assembly übergeben, die nach Plugins durchsucht werden soll. Zusätzlich muss man den Typ des Plugins übergeben.
Wir laden die Assembly mit Assembly.LoadFrom() damit wir Informationen aus ihr lesen können (Stichwort Reflection). Mit der Funktion GetTypes() erhalten wir alle Typen (Klassen), die in der Assembly gespeichert sind. Als nächstes führen wir ein paar Routineprüfungen durch wie z.B. ob der Typ public ist und nicht abstrakt.
Danach „suchen“ oder besser gesagt holen wir mit der Methode GetInterface() das Plugin-Interface falls der gerade iterierende Typ das Plugin implementiert hat. Falls kein Interface implementiert wurde ist der Rückgabewert NULL. Deshalb wird als nächstes überprüft ob der Rückgabewert ungleich NULL ist. Haben wir einen Rückgabewert erhalten, erstellen wir mit Activator.CreateInstance eine Instanz. Diese speichern wir dann in unser Dictionary.
So werden die Plugins Schritt für Schritt in unsere Anwendung geladen. Wurden alle Dateien auf Plugins überprüft, kann man mit der Funktion public string Calc(string pInput) Mathematikfunktionen aufrufen bzw. ausführen.
Ruft man _MathCore.Calc("sqrt(3)") auf, wird in der Funktion Calc der Input String interpretiert damit man weiß, welche Mathematikfunktion aufgerufen wird. In diesem Fall wäre das „sqrt“. Anschließend wird nach dem passenden Plugin mit der Funktion GetPlugin gesucht. Wurde es gefunden, wird der Input String an das Plugin weitergereicht und die Funktion Calc vom Plugin aufgerufen.
Wurde kein Plugin gefunden, wird "Funktion nicht gefunden" zurückgegeben.
Wie gesagt handelt es sich hierbei um ein sehr einfaches Plugin-System. Das Beispiel verwendet die Plugins zwar nur in einer Konsolen Applikation, trotzdem sollte es kein Problem sein, das Ganze in WPF oder WinForms umzusetzen.
Bei einem ausgereiften Plugin-System gibt es Features wie z.B., dass die Plugins auf bestimmte Teile des Host System zugreifen können (z.b auf ein Control), dass Plugins wiederum Plugins enthalten können oder, dass die Plugins in einer Art Sandbox (Stichwort AppDomain ) gehostet werden damit kein schädlicher Code ausgeführt werden kann usw. usf.
Seit .NET 2< wurden eine Reihe neuer Techniken mitgeliefert die das Erstellen von Plugins vereinfachen soll.
Anbei ein paar Links:
http://www.mycsharp.de/wbb2/thread.php?threadid=34472
http://www.codeproject.com/KB/cs/pluginsincsharp.aspx
Beispiel Projekt herunterladen
Schlüsselwörter: C#, Plugin, Modul
zuletzt geändert: 24. August 2010 16:52
Link zu diesem Artikel: (in die Zwischenablage)
CKeditor: Events abfangen
Ich bin gerade dabei, die Modifikationen für den FCKeditor auf den CKeditor umzuschreiben. Dazu zählen: interne Verlinkungen, Links zum Galeriemodul, Links zu Blogeinträgen (vom Blogmodul).
Das Ziel ist es, alles umzusetzen, ohne den Originalquelltext zu ändern. Dabei ergibt sich in manchen Situationen ein Problem:
Wie soll man z.B.: das onOk Ereignis modifizieren? Es gibt keinerlei Möglichkeit den Quelltext über Javascript zu ändern.
Man kann aber vor der eigentlichen Funktion eine eigene Funktion einfügen die die Ausnahmesituation behandelt:
Pseudocode:
Ereignis onOk: Wenn Ausnahmesituation -> führe eigene Funktion aus, andernfalls: führe native Funktion aus!
{
/* CKeditor specific */
CKEDITOR.on( 'dialogDefinition', function( ev )
{
var dialogName = ev.data.name;
var dialogDefinition = ev.data.definition;
if ( dialogName == 'link' )
{
/* in Events hineinhängen */
var linkType = infoTab.get('linkType');
var onChangeFunction = linkType.onChange;
/* alte Funktion speichern */
linkType.onChange = function()
{
if(eigneSituation)
eigeneSituationBehandeln();
else
onChangeFunction.apply(this);
}
}
}
}
Schlüsselwörter: ckeditor fckeditor event-hooking event hook
zuletzt geändert: 08. November 2010 17:27
Link zu diesem Artikel: (in die Zwischenablage)
(c) 2011 | Impressum |
| Empfehlenswerte Blog Einträgebaska.jpg)

