Archive for the ‘Softwareentwicklung’ Category

Prüfen auf Schreibrechte für eine Datei in C#

Saturday, February 28th, 2009

Hallo,

manchmal möchte man wissen, ob ein bestimmter Benutzer bestimmte Rechte (z.B. Schreibrechte) für eine bestimmte Datei besitzt. Wie sich herausstellt, ist diese Anforderung alles andere als leicht umzusetzen, weil dabei viele verschiedene Aspekte eine Rolle spielen. Die Vorgehensweise sieht prinzipiell so aus:

  • Abfragen der Access Control Listen für die Datei
  • Itererieren über alle Einträge und prüfen ob man für die Kennung oder die Gruppe des gewünschten Benutzers Einträge findet
  • Dabei die Präzedenz der Einträge beachten (explizite kommen vor vererbten Regeln, “Deny”-Regeln kommen for “Allow”-Regeln)

Den nicht unerheblichen Code hat dankenswerterweise Bruce Hatt in einem Artikel auf CodeProject.com veröffentlicht.

Man sollte bedenken, dass die Möglichkeit in eine Datei zu schreiben zusätzlich durch das “ReadOnly-flag” und die .NET Code Access Security eingeschränkt sein könnte. Beides wird in dieser Klasse nicht berücksichtigt.

Die Prüfung auf das ReadOnly-flag könnte man so schreiben:

FileAttributes attrs = File.GetAttributes(fileName);
 
if ((attrs & FileAttributes.ReadOnly) != 0)
{
    readOnly = true;
}

Und für die Prüfung im Rahmen der CAS könnte man folgenden Code verwenden:

FileIOPermission fp = new FileIOPermission(FileIOPermissionAccess.Write, fileName);
try
{
    fp.Demand();
}
 
catch (SecurityException)
{
    readOnly = true;
}

Wenn es nur um die Prüfung auf Schreibrechte geht, hört man oft den Vorschlag, einfach den Schreibvorgang auszuführen und ggf. die geworfene Exception zu fangen. Dieser Vorschlag ist aber nicht immer geeignet. In meinem Fall war es so, dass ich an einer Anwendung arbeite, in der Dateien editiert werden können. Falls Dateien nicht geschrieben werden können, soll dies visualisiert werden. Man möchte aber vermeiden, dazu bei jedem Start der Anwendung die zu editierenden Texte zu speichern, da damit der Timestamp verändert wäre, obwohl der Benutzer die Datei gar nicht geändert hat und auch nicht speichern wollte.

Viele Grüße,
Andreas

Technorati Tags: ,

Destruktor nicht gerufen in mixed mode C++ Anwendungen mit Visual C++ 2008

Monday, February 9th, 2009

Hallo,

in C++ gibt es ja das Resource Aquisition Is Initialization (RAII) pattern. Der Kern dieses Patterns besteht darin, dass ein Objekt bereits bei der Konstruktion die Ownership für eine Resource übernimmt und diese Resource bei seiner Zerstörung wieder frei gibt. Man wendet dieses pattern an, um Resourcen wie allokierten Speicher oder file handles sicher freizugeben. Dabei mancht man sich zu nutze, dass in C++ der Destruktor eines Objektes deterministisch aufgerufen wird, z. B. auch im Falle eine exception.

Dieses pattern ist sehr wichtig, um zuverlässige und robuste Software in C++ zu schreiben und ich nutze es regelmäßig. Nun hatte ich vor einiger Zeit in einem der Unittests in der Software, die ich für mein Unternehmen EmPowerTec schreibe, einen seltsamen Fehler in einem Unittest. Nach einer längeren Debugging-Session sah es so aus, als würden die Destruktoren für Objekte die in einer Methode auf dem Stack angelegt waren, beim Verlassen der Methode nicht mehr gerufen. Ein solches Verhalten führt mit Sicherheit zu Fehlern, wenn das RAII Pattern genutzt wird. Zunächst war ich darüber verblüfft, da ich mir nicht vorstellen konnte, dass ein derartig fundamentales Verhalten vom Microsoft C++ Compiler nicht richtig implementiert war. Tatsächlich war das aber der Fall, wie in folgendem Knowledge Base Eintrag beschrieben:

http://social.msdn.microsoft.com/forums/en-US/vclanguage/thread/8324a598-ad05-40e1-a271-6f64ce3b6008

Wie man dem Thread entnehmen kann, tritt das Problem auf, wenn man von einer C++/CLI Library eine native C++ library aufruft und diese mit inkompatiblen Einstellungen für das Exception handling übersetzt hat.

Und die Moral von der Geschicht,
Trau’ keinem Compiler nicht,

Unittests sollst Du machen,
Sonst hast Du nichts zu lachen

Viele Grüße,
Andreas

Technorati Tags: ,

C#/.NET ist populärer als Java

Wednesday, November 19th, 2008

zumindest wenn man den tags auf stackoverflow.com glaubt.

Erstaunt hat mich auch die Popularität von Python im Vergleich zu Perl.

Viele Grüße,
Andreas

stackoverflow.com: die beste online-Resource für Softwareentwickler?

Wednesday, September 24th, 2008

Hallo,

wer wie ich seinen Berufseinstieg in der Softwareentwicklung noch ohne Internet + Google gemacht hat, ist vielleicht immer noch dankbar wenn er nach 15 Minuten “googeln” eine Antwort auf ein Problem gefunden hat, das ihn früher vielleicht Tage gekostet hätte. Die meisten relevanten Treffer finden sich in Foren oder speziellen “Experten-sites” wie etwa Experts Exchange.

Nun tritt einen neue website an, um das teilen von Wissen zur Softwareentwicklung noch effizienter zu machen: stackoverflow.com.

Hinter der Seite steht unter anderem Joel Spolky, einer der einflussreichsten Köpfe in der IT-Szene.  Auf seiner Website erklärt er, warum stackoverflow.com die beste derartige Resource  sein soll.

In der Tat hat die site einige Alleinstellungsmerkmale, die sie zu einem Erfolg machen könnten:

  • Durch die Möglichkeit Antworten zu bewerten werden die Antworten nach Qualität sortiert
  • Durch die Möglichkeit antworten zu editieren, werden die Antworten immer besser
  • Die Möglichkeit “Reputation” zu gewinnen, fördert die Bereitschaft zu der site beizutragen
  • Antworten können kommentiert werden, ähnlich wie in einem blog
  • Last but not least: es ist keine Registrierung erforderlich!

Das Konzept scheint aufzugehen wie man etwa an enthusiastischen Diskussionen in Foren und an der bereits nach kurzer Zeit sehr hohen Zahl von Fragen sieht.

Es lohnt sich auf jeden Fall mal einen Blick auf stackoverflow.com zu werfen. In den FAQs kann man sich am schnellsten einen Überblick verschaffen.

Viele Grüße,
Andreas

Nette Silverlight Demo

Thursday, September 4th, 2008

Hallo,

Microsofts Silverlight stellt eine Plattform dar, mit der man “Rich Internet Applications” (RIAs) mit Schwerpunkt auf grafische Darstellung erstellen kann.

Wer sich für Silverlight interessiert findet unter http://foidoschess.tv/ eine nette Demonstration der Möglichkeiten von Silverlight. Dummerweise ist eine Registrierung erforderlich, ich habe hierfür eine Email-Adresse @mailinator.com verwendet.

Ausserdem muss man noch die Silverlight Runtime installieren, was aber schnell und unkompliziert geht.

Da ich öfter das Internet nutze um Schachpartien live zu verfolgen (z.B. soweit möglich bei dem im Oktober anstehenden Weltmeisterschaftskampf) kann ich den Mehrwert der Foidos-Plattform durchaus erkennen.

Viele Grüße,
Andreas

Technorati Tags: ,

Popularität des Microsoft Ribbon controls

Monday, August 18th, 2008

Hallo,

die aktuell in Entwicklung befindliche Software meines Unternehmens EmPowerTec verwendet das Microsoft Ribbon zur Steuerung der Anwendung. Während ich persönlich von der höheren Produktivität (und auch der optischen Attraktivität – “eye candy”) schon immer überzeugt war, gab es Anfangs doch viel Skepsis ob das Ribbon der richtige Ansatz ist. Teilweise bezog sich diese Kritik auch auf Microsoft’s Vorgehen, ein bereits vorhandenes GUI (in den Microsoft Office Anwendungen) zu ersetzen und auf den Versuch die Ideen als “intellectual property” zu patentieren um potentielle Wettbewerber zu behindern.

Inzwischen ist mein Eindruck dass für Neuentwicklungen das Ribbon-Konzept zum Standard geworden ist und eine breite Akzeptanz gefunden hat. Als Beispiel kann man sich diese Diskussion in dem Business Of Software forum auf joelonsoftware.com anschauen.

Hier gibt es weitere Beispiele für Anwendungen mit Ribbon controls.

Und hier noch ein Beispiel aus der erwähnten Anwendung:

Example for Ribbon control

Den eigentlichen Witz des Ribbons kann man so nicht ohne weiteres erkennen: je nach Kontext wird das passende “ribbon tab” automatisch aktiviert und macht so genau die relevante Funktionalität verfügbar.

Im obigen Beispiel wurde in ein editor window geklickt woraufhin das “Edit”-tab des Ribbons aktiviert wurde. Würde man in das treeview “Project files” im linken Bereich klicken, würde automatisch das “Project”-tab aktiviert werden.

Viele Grüße,
Andreas

Technorati Tags:

Erstellung von Parsern in C++ mit der boost::spirit library (Teil 2)

Friday, August 15th, 2008

Hallo,

nachdem ich im letzten Posting zu diesem Thema einige grundlegende Überlegungen zum Einsatz der boost::spirit C++ parser library beschrieben habe möchte ich heute den konkreten Einsatz in dem bereits beschriebenen Beispiel zeigen.

Für den beschriebenen Ansatz werden folgende Include-Direktiven benötigt:

//lint -save -elib(*)
#include <boost/spirit/core.hpp>
#include <boost/spirit/tree/tree_to_xml.hpp>
#include <boost/spirit/tree/parse_tree.hpp>
//lint -restore

Die spirit header files sind nicht “PC-Lint clean”, weshalb entsprechende warnings unterdrückt werden sollten. Da boost::spirit eine “header only” library ist, sind keine weiteren Eingabedaten für den linker erforderlich.

Hier sind einige typedefs, die die Nutzung der boost-Klassen erleichtern. Ich habe sie in einem “anonymous namespace” innerhalb des entsprechenden cpp-files definiert:

typedef char const*         iterator_t;
typedef tree_match<iterator_t> parse_tree_match_t;
typedef parse_tree_match_t::const_tree_iterator iter_t;
 
typedef pt_match_policy<iterator_t> match_policy_t;
typedef scanner_policies<skip_parser_iteration_policy<space_parser>, match_policy_t, action_policy> scanner_policy_t;
typedef scanner<iterator_t, scanner_policy_t> scanner_t;
typedef rule<scanner_t> rule_t;

Nun kann man den Parser selbst definieren. Dies ist z.B. bei kleineren Grammatiken die nicht oft aufgerufen werden direkt in der Methode die das parsen übernimmt, möglich.
Fangen wir mit den Regeln (nonterminals) an:

InstantiatedTypeParser::parse(char marker, const std::string&amp; instantiatedType)
{
rule_t path_sep;
rule_t identifier;
rule_t path;
rule_t single_type;
rule_t instantiated_type_list;i

und hier die eigentliche Grammatik, der EBNF-Form nicht unähnlich:

path_sep = token_node_d[(ch_p(':') >> ch_p(':'))]
           ;
 
identifier = token_node_d[((alpha_p | ch_p('_')) >> *(alnum_p | ch_p('_')))]
               ;
 
path =	identifier >> *(path_sep >> identifier)
          ;
 
single_type =	path     >> ch_p(m_startMarker)
				>> path
				>> ch_p(m_endMarker)
                    ;
 
instantiated_type_list =	single_type
					|
					path  >> ch_p(m_startMarker)
						>> instantiated_type_list
						>> ch_p(m_endMarker)
                                        ;

Hier der eigentliche Aufruf des parser:

const char* first = instantiatedType.c_str();
tree_parse_info<> info = pt_parse(first, instantiated_type_list, space_p);

Das zurückgegebene Objekt vom Type tree_parse_info gibt nun Auskunft darüber, ob die Eingabe komplett gelesen wurde (was bedeutet, dass sie syntaktisch korrekt ist) oder ob ein Fehler aufgetreten ist. Eine Fehlerbehandlung könnte z.B. so aussehen:

if (info.full)
{
    evaluate(path.id(), identifier.id(), result, info.trees.begin());
}
else
{
    std::ostringstream msg;
    msg << "Invalid syntax in instantiated type '" << instantiatedType
	  << "' at character position " << (info.stop - first) + 1
	  <<". Invalid character: '" << *info.stop
	  << "'.";
    throw std::runtime_error(msg.str());
}

Falls das parsen erfolgreich war, kann dem zurückgegeben tree_parse_info-Objekt der komplette parse tree entnommen und verarbeitet werden. Die Struktur und Beschreibung des parse trees ist ziemlich kompliziert und ich fand es hilfreich, mir von spirit ein xml-Dokument aus dem parse tree erzeugen zu lassen um ihn besser zu verstehen:

std::map
 rule_names;
rule_names[path_sep.id()] = "pathsep";
rule_names[path.id()] = "path";
rule_names[identifier.id()] = "identifier";
rule_names[single_type.id()] = "single_type";
rule_names[instantiated_type_list.id()] = "instantiated_type_list";
tree_to_xml(std::cout, info.trees, first, rule_names);

Dies macht es leichter eine Methode zu schreiben, die den parse tree auswertet. In unserem Beispiel sieht diese Methode so aus:

/**
 *	Processes the parse tree returned by the spirit tree parser.
 *	The parse tree is not intuitively to understand.
 *
 *	We use a tree parser instead of semantic actions because the grammar is
 *	ambiguous and semantic actions cannot easily be undone during
 *	backtracking.
 */
void evaluate(const parser_id&amp; parentId,
			  const parser_id&amp; childId,
			  et::ocl::InstantiatedTypeParser::TypeList&amp; result,
			  const iter_t&amp; node)
{
if (node->value.id() == parentId)
{
	std::ostringstream path;
	/*
	 *	Collect all child nodes of type childId.
	 *	These are the actual identifiers.
	 */
	for (iter_t child = node->children.begin();
	     child != node->children.end();
	     ++child)
	{
		if (child->value.id() == childId)
		{
			/*
			 *	Look at the xml parse tree to understand the
			 *	following code.
			 *	For unknown reasons, the parse tree contains
			 *	always two nested nodes for an identifier rule
			 *	and only the child node contains the actual
			 *	identifier.
			 */
			assert(child->children[0].value.id() == childId);
			std::string value(child->children[0].value.begin(),
					       child->children[0].value.end());
 
			if (path.str().size() > 0)
			{
				path << "::";
			}
			path << value;
		}
	}
 
	std::string p = path.str();
	et::base::trim(p);
	result.push_back(p);
}
 
for (iter_t child = node->children.begin();
      child != node->children.end();
      ++child)
{
	evaluate(parentId, childId, result, child);
}
}

Insgesamt würde ich sagen dass boost::spirit eine echte Alternative ist wenn man einen kleinen Parser in eine C++ Anwendung integrieren möchte. Bei komplexeren Grammatiken würde ich einem richtigen parser generator wie ANTLR den Vorzug geben.

Noch eine Anmerkung: wenn man das warning level in Visual Studio sinnvollerweise auf 4 setzt und ebenso sinnvollerweise die Option “Warnings as errors” aktiviert, verursacht die Spirit header files warnings die zu einem Abbruch des builds führen. Abhilfe schafft es, für die Dateien die diese header files einbinden, die Warnungen 4512, 4127 und 4996 zu deaktivieren.

Technorati Tags: , , ,


Viele Grüße,
Andreas

Erstellung von Parsern in C++ mit der boost::spirit library

Monday, July 21st, 2008

Hallo!

In der Softwareentwicklung steht man regelmäßig vor der Aufgabe, eine Eingabe nach gewissen Regeln zu verarbeiten. Dazu bedient man sich in einfachen Fällen regulärer Ausdrücke, die man in C++ z.B. mit der boost::regex library verarbeiten kann. Wenn die Struktur der Eingabe zu komplex ist, definiert man eine Grammatik und verwendet einen Parser Generator wie lex/yacc oder ANTLR (wobei man in diesem Fall vorsichtig sein muss. Für die aktuelle Version 3.0 ist zum Zeitpunkt der Veröffentlichung dieses postings immer noch kein brauchbares C++ target verfügbar).

Reguläre Ausdrücke sind allerdings schwer zu lesen und eignen sich schlecht für rekursive Strukturen.

Einen Parser Generator aufzusetzen und in ein C++-Projekt einzubinden ist wiederum etwas lästig. Insbesondere wenn man verschiedene Plattformen (etwa, Visual C++ und MinGW unter Windows und gcc unter Linux) unterstützt, wiederholt sich der Aufwand.

Mit der boost::spirit library gibt es noch einen Weg dazwischen, der genau diese Lücke füllt. Man kann mit richtigen Grammatiken arbeiten und braucht trotzdem kein externes tool einzubinden.

Da die Arbeit mit Spirit nicht ganz trivial ist, wollte ich meine Erfahrungen anhand eines kleinen Beispiels festhalten und weitergeben.

Die zu lösende Aufgabe bestand darin, einen String zu parsen, der einen “instantiierten Typ” – i.A. template oder generic genannt – verarbeitet und die verwendeten Typen als Liste zurückgibt. Dabei sollten die in den gängigen Programmiersprachen üblichen Regeln für identifier berücksichtigt werden und die Eingabe sollte wie sonst üblich formatfrei sein, d.h. white space sollte ignoriert werden.

Beispiele für gültige Eingaben wären dann:

Set(String)
Set(Bag(String))
Set ( String )
My_Container1(Integer)

Eine solche Aufgabe ist genau von der richtigen Komplexität für boost::spirit.

Falls sich jemand über die runden Klammern wundert (üblich sind spitze Klammern) – der Parser soll im Kontext der Object Constraint Language (OCL) verwendet werden und dort werden runde Klammern verwendet. Dies ist aber ein irrelevantes Detail.

Spirit ist auf möglichst flexible Verwendbarkeit ausgelegt, es gibt verschiedene alternative Verfahren und es werden intensiv “Policy-Klassen” in der Form von template-Parametern verwendet.
Dies macht die Verwendung nicht gerade leichter, da es sehr viele mögliche Kombinationen der Verfahren und Policies gibt. Deshalb soll dieses posting auch eine Art Kochbuch sein, dass typische Anforderungen abdeckt. Gängige Beispiele wie etwa hier verwenden “semantic actions”, die direkt beim match einer Regel ausgeführt werden. Dieses Vorgehen hat einen grossen Nachteil, der es für viele Fälle unbrauchbar macht: bei einer mehrdeutigen Grammatik, wie sie in der Praxis häufig vorkommen, versucht der Parser durch “probieren” eine zur Eingabe passende Regel zu finden. Falls dies nicht klappt wird der Versuch abgebrochen und nochmal von vorne begonnen (”backtracking“). Dummerweise werden semantic actions die mit einer Regel assoziiert sind bei jedem versuchten match aufgerufen. Es kann aber sein, dass diese Aufrufe gar nicht relevant sind, weil der Parser später erkennt dass die aktuelle Regel nicht matcht und ein backtracking vornimmt.

Spirit bietet die Möglichkeit einen “tree parser” zu verwenden, der dieses Problem nicht hat. Ein solcher tree parser liefert einen Syntax Baum der immer gleich aussieht und exakt der Eingabe entspricht, unabhängig davon wie oft ein backtracking auftrat. Davon abgesehen ist es generell eine gute Idee, die Grammatik von der semantischen Verarbeitung zu trennen, was mit einem tree parser implizit gegeben ist.
Zusammenfassend würde ich also bei der Verwendung von Spirit immer einen Tree Parser empfehlen.

Angesichts der schweren Zugänglichkeit von Spirit ist man für Beispiele immer dankbar, z.B. hier für die Verwendung eines tree parsers. Wenn man sich an diesem Beispiel anlehnt, wird man aber feststellen, dass der Parser white space nicht ignoriert, ein Verhalten dass im allgemeinen unerwünscht ist. Die Lösung besteht darin, die Zeile

typedef scanner_policies<iteration_policy, match_policy_t, action_policy> scanner_policy_t;

in

typedef scanner_policies<skip_parser_iteration_policy<space_parser>, match_policy_t, action_policy> scanner_policy_t;

zu ändern. Auch wenn man herausgefunden hat, dass der erste template Parameter, die “iteration policy”, für das ignorieren von white space zuständig ist, ist es noch ein weiter Weg bis man den korrekten Wert kennt den man für diese policy angeben muss.

Nachdem ich in diesem posting die wichtige Festlegung auf einen tree parser empfohlen habe und die Hürde des ungünstigen Beispiels eines parser der white space nicht ignoriert genommen habe, werde ich im nächsten posting den konkreten code und insbesondere die Definition der Grammatik zeigen.

Knifflig wird es dann wieder beim Auswerten des parse trees, was leider keine triviale Aufgabe ist.

Viele Grüße,
Andreas

Technorati Tags: , ,

Formatierter source code in Microsoft Word

Sunday, June 8th, 2008

Hallo,

bis vor kurzem war mir nicht bewusst, das beim Kopieren und Einfügen von source code zwischen Visual Studio und Microsoft Word die Formatierung erhalten bleibt.

Ich nehme an, Visual Studio legt den code (unter anderem) im RTF-Format im Clipboard ab.

Das ist eine ziemlich einfache Möglichkeit, Code-Beispiele in Word-Dokumente einzufügen.

Formatted source code in Microsoft Word

Dieses Verfahren funktioniert übrigens auch mit Eclipse.

Viele Grüße,
Andreas

Technorati Tags: ,

Visualisierung von Softwarearchitekturen mit Doxygen

Sunday, May 18th, 2008

Hallo,

in diesem posting geht es zwar um UML aber ich war erstaunt wie viele Kommentar sich auf Doxgyen als Werkzeug zu Visualisierung von Softwarearchitekturen bezogen.

Ich selbst habe mit Doxygen ebenfalls schon sehr gute Erfahrungen beim Verstehen fremden codes gemacht. Doxygen analysiert den code mit einem Minimum an Konfiguration, geht dabei “best effort” vor und produziert bei entsprechender Konfiguration eine Reihe hilfreicher Artefakte:

  • Collaboration graph: zeigt welche Klassen von einer Klasse benutzt werden, z.B. als member
  • Inheritance graph: zeigt von welchen Klassen eine gegebene Klasse abgeleitet ist aber auch welche Klassen von der gegebenen Klasse abgeleitet sind
  • Static call hierarchy graph: zeigt (rekursiv) an, welche Methoden in einer bestimmten Methode gerufen werden
  • References/referenced by: zeigt an welche anderen Methoden eine Methode benutzt und wo eine Methode benutzt wird (cross reference Dokumentation)
  • Include graph: illustriert die Schachtelung von include-Anweisungen in C/C++
  • Verlinkter source code: Doxygen erzeugt formatierten source code mit syntax highlighting. Methoden und Datentypen sind verlinkt so dass man mit einem Mausklick zu der Methode oder Definition des Datentyps springen kann.

Beim reverse engineering mit einem UML tool wird in der Regel nur das repository selbst erzeugt und ggf. ein großes Diagramm mit allen importierten Elementen, was nicht viel bringt. Graphen wie etwa die statische Aufrufhierchie einer Methode erzeugen diese tools überhaupt nicht.

Hier ist ein Beispiel, entnommen von der Doxygen-Dokumentation des Tahoe-Projektes.

In diesem Artikel ging es primär um die Reengineering-Fähigkeiten von Doxygen, der Fokus dieses tools ist aber eigentlich die Aufbereitung bon inline-Dokumentation die von den Autoren der Software erstellt wurde.

Highly recommended!

Viele Grüsse,
Andreas

Technorati Tags: ,