Glengamoi (Forum) · AspHeute · .NET Heute (RSS-Suche) · AspxFiles (Wiki) · .NET Blogs
ASP German Homepage Homepage
 

Liste

.NET 2.0 (1)
.NET Allgemein (16)
.NET Fu (5)
ADO.NET (11)
Aprilscherz (3)
ASP Grundlagen (44)
ASP Tricks (83)
ASP.NET (44)
ASPIntranet.de (5)
C# (28)
Datenbank (44)
Dokumentation (4)
IIS 6.0 (1)
Komponenten (29)
Optimierung (10)
Server (21)
Sicherheit (34)
Tee Off (6)
VB.NET (6)
WAP (8)
Web Services (11)
XML (9)

RSS 2.0 - Die neuesten fünf Artikel auf AspHeute.com


 

Suchen





 

English Articles
Chinese Articles
Unsere Autoren
 
Link zu AspHeute
Impressum
Werben
Anfragen

Dateityp-Ermittlung in Managed C++

Geschrieben von: Christoph Wille
Kategorie: .NET Allgemein

Im gestrigen Artikel Vorsicht Falle: Dateien, die keine sind habe ich gezeigt, wie man mit Hilfe einer in C++ geschriebenen COM+ Komponente herausfinden kann, ob es sich bei der Datei die man verwenden möchte um eine "echte" oder "nur" um Devices und Named Pipes handelt. Heute sehen wir uns die Thematik in .NET an, und machen gleich ein paar Aufwärmübungen in Managed C++.

Bevor wir zum harten Kern des Themas kommen, eine erfreuliche Nachricht vorweg: unter .NET liefert die File.Exists Methode nur dann True, wenn es sich um eine Datei auf einem Laufwerk handelt - COM Ports und andere Devices liefern False. Die Verwendung demonstriert das folgende Beispiel (fileexists.aspx):

<%@ Page Language="C#" %>
<%@ Import Namespace="System.IO" %>
<script language="C#" runat="server">
void Page_Load(Object sender, EventArgs e) 
{
  bool bResult = File.Exists("c:\\COM1");
  idOutput.Text = bResult.ToString();
}
</script> 
<html> 
<body> 
	<asp:label id="idOutput" runat="server"/>
</body> 
</html>

Damit haben wir schon die halbe Miete - den zuverlässigen Check, ob auf eine Datei lesend zugegriffen werden kann. Will man eine neue Datei erstellen, dann hilft File.Exists keineswegs - weil die Datei sowohl gültig als auch ungültig ein False retourliefern wird. Hier müssen wir uns etwas anderes ausdenken.

Plan B - Dateityp ermitteln

Man könnte die Komponente aus dem gestrigen Artikel Vorsicht Falle: Dateien, die keine sind ja ohne weiteres nach Anleitung des Artikels Verwenden von COM Komponenten in ASP.NET mittels eines RTCW (Runtime Callable Wrapper) einbinden. Die Sache hat einen Nachteil - man verliert für eine Komponente mit so geringer Funktionalität unsinnigerweise viel Performance.

Ergo werden wir die Komponente .NET-tauglich machen. Und was wäre besser geeignet als Managed C++, wenn der Compiler dafür sogar gratis im .NET SDK mit dabei ist? Gestern konnten nur die Visual Studio 6 Besitzer die Komponente selbst kompilieren, heute kann es jeder der das SDK am Rechner hat!

Die Komponente

Aus Spaß an der Freude habe ich mich mit Managed C++ anhand dieses Projekts richtig zu beschäftigen begonnen. Im Endeffekt hat das Umschreiben und Einlernen in Managed C++ weniger als eine Stunde benötigt, und die Komponente ist sogar deutlich einfacher zu verwenden als zuvor. Beginnen wir in der Datei secenhance.cpp (übrigens die einzige für das gesamte Komponentenprojekt!) ganz oben:

#include "windows.h"

#using <mscorlib.dll>
using namespace System;
using namespace System::Runtime::InteropServices;

namespace SecurityEnhance
{
__value public enum FileType {undefined=-1, unknown=0, disk=1, character=2, pipe=3};

Wenn man flüchtig genug hinsieht, sähe das beinahe aus wie C# Code. Allerdings komme ich um das #include für die Windowsfunktionen nicht herum, und ich muß mich explizit darum kümmern, daß der Compiler die MetaData von .NET erhält - mittels #using Statements. Ach ja, noch ein kleiner Unterschied - anstatt des Punkts werden zwei Doppelpunkte eingesetzt (bei System.Runtime.InteropServices).

In der unmanaged C++ Komponente habe ich die (Datei)Typen mittels in #define Statements deklarierten Konstanten zurückgeliefert, jetzt habe ich das auf eine enum umgebaut. Das coole an der Sache ist, daß wenn ich den Namespace SecurityEnhance in eine Anwendung importiere, ist die Enum dort genauso verwendbar - keine const Statements mehr! Und typsicher ist es so nebenbei auch noch.

Sehen wir uns nun die Klasse FileUtilities an:

__gc public class FileUtilities
{
public:
   FileUtilities(){}
     
public:
   FileType GetFileType(System::String *sFileName, System::Int32 *Win32ErrorCode)
   {
     wchar_t __nogc* pszFileName = static_cast<wchar_t*>
                         (Marshal::StringToHGlobalUni(sFileName).ToPointer());
     FileType theType = FileType::undefined;
     *Win32ErrorCode = 0;

     // open the file for generic reading
     HANDLE hFile = ::CreateFileW(pszFileName, 
                         GENERIC_READ,
                         FILE_SHARE_READ | FILE_SHARE_WRITE, 
                         NULL, 
                         OPEN_EXISTING, 
                         0, 
                         NULL );

     if (INVALID_HANDLE_VALUE == hFile)
     {
          DWORD dwError = ::GetLastError();
          *Win32ErrorCode = (long)dwError;
     }
     else
     {
          DWORD dwFileType = ::GetFileType(hFile);
          switch (dwFileType)
          {
          case FILE_TYPE_UNKNOWN: theType = FileType::unknown; break;
          case FILE_TYPE_DISK: theType = FileType::disk; break;
          case FILE_TYPE_CHAR: theType = FileType::character; break;
          case FILE_TYPE_PIPE: theType = FileType::pipe; break;
          default: theType = FileType::undefined;
          }
          ::CloseHandle(hFile);
     }
     Marshal::FreeHGlobal(pszFileName);
     return theType;
   }
};

Alle Schlüsselwörter in Managed C++ die für .NET neu eingeführt wurden, beginnen mit zwei Unterstrichen. Ein __value aus der Enumdefinition bedeutet, daß es sich um einen Value-Type handelt. Das __gc hingegen zeigt an, daß es sich um eine Garbage-Collected Klasse handelt. Somit ergibt sich irgendwie zwingend, daß Variablen die mit __nogc verziert sind, nicht von .NET verwaltet werden.

Eine solche ist pszFileName. Diese brauche ich, weil ich mit der String Klasse aus .NET nicht direkt in das WIN32 API hineingehen kann - zuerst muß ich den String auf einen "normalen" Unicode String konvertieren lassen, wobei mir die Marshal Klasse aus System.Runtime.InteropServices unter die Arme greift.

Nach dieser Konvertierung verwende ich im Prinzip den identen Sourcecode von gestern. Nur der Rückgabewert ist diesmal eine typsichere Enum, und der WIN32 Fehlercode kommt als per Rerferenz übergebene Integer Variable zurück.

Kompiliert wird mittels cl.exe, ich habe entsprechende Batchdateien zur einfachen Verwendung mitgegeben. Übrigens: die erzeugte Assembly kann man genauso in ildasm.exe betrachten wie C# Anwendungen auch:

Einsatz der Komponente

Da wir eine .NET Assembly vor uns haben, darin eine richtige .NET Klasse plus Enumeration vorhanden ist, stellt sich eine kleine Konsolenanwendung sehr einfach dar (filetype.cs):

// Kompilieren mit "csc filetype.cs /r:secenhance.dll"
using System;
using SecurityEnhance;

public class MainClass
{
public static void Main()
{
  FileUtilities fu = new FileUtilities();
  int Win32ErrorCode = 0;
  FileType f = fu.GetFileType("c:\\COM1", ref Win32ErrorCode);
  Console.WriteLine(f.ToString() + " " + Win32ErrorCode.ToString());
}
}

Und schon wissen wir, daß obwohl File.Exists uns ein False liefert, es sich um eine Datei vom Typ 3, also Character beziehungsweise Device handelt. Und auf sowas greift man natürlich nicht zu! Weder lesend, noch schreibend.

Der Vollständigkeit halber nun noch das obige Beispiel als ASP.NET Script (filetype.aspx). Die Assembly muß man wie immer ins bin Verzeichnis kopieren:

<%@ Page Language="C#" %>
<%@ Import Namespace="SecurityEnhance" %>
<script language="C#" runat="server">
void Page_Load(Object sender, EventArgs e) 
{
  FileUtilities fu = new FileUtilities();
  int Win32ErrorCode = 0;
  FileType f = fu.GetFileType("c:\\COM1", ref Win32ErrorCode);
  idOutput.Text = f.ToString() + " " + Win32ErrorCode.ToString();
}
</script> 
<html> 
<body> 
	<asp:label id="idOutput" runat="server"/>
</body> 
</html>

Schlußbemerkung

In bestimmten Fällen kann Managed C++ zum Retter in der Not werden - vor allem dann, wenn man eine bestehende Applikation oder Teile davon .NET-tauglich machen möchte. Mit C# hätte ich sicher deutlich länger gebraucht, den C++ Code zu portieren.

Download des Codes

Klicken Sie hier, um den Download zu starten.

Verwandte Artikel

Ein Touch-Utility in C#
Verwenden von COM Komponenten in ASP.NET
Vorsicht Falle: Dateien, die keine sind

Wenn Sie jetzt Fragen haben...

Wenn Sie Fragen rund um die in diesem Artikel vorgestellte Technologie haben, dann schauen Sie einfach bei uns in den Community Foren der deutschen .NET Community vorbei. Die Teilnehmer helfen Ihnen gerne, wenn Sie sich zur im Artikel vorgestellten Technologie weiterbilden möchten.

Eine weitere sehr hilfreiche Resource ist das deutsche ASP.NET Wiki, das als zentrale Anlaufstelle für Tips, Tricks, Know How und alles Nützliche was man in seinem Alltag als (ASP).NET-Entwickler so braucht und entdeckt gedacht ist.

Haben Sie Fragen die sich direkt auf den Inhalt des Artikels beziehen, dann schreiben Sie dem Autor! Unsere Autoren freuen sich über Feedback zu ihren Artikeln. Ein einfacher Klick auf die Autor kontaktieren Schaltfläche (weiter unten) und schon haben Sie ein für diesen Artikel personalisiertes Anfrageformular.

 

Und zu guter Letzt möchten wir Sie bitten, den Artikel zu bewerten. Damit helfen Sie uns, die Qualität der Artikel zu verbessern - und anderen Lesern bei der Auswahl der Artikel, die sie lesen sollten.

Bewerten Sie diesen Artikel
 Sehr gut   Nicht genügend  
   1  2  3  4  5  
 

  
   Für Ausdruck optimierte Seite

©2000-2006 AspHeute.com
Alle Rechte vorbehalten. Der Inhalt dieser Seiten ist urheberrechtlich geschützt.
Eine Übernahme von Texten (auch nur auszugsweise) oder Graphiken bedarf unserer schriftlichen Zustimmung.