January 2004 - Posts

Mit einem Tag Verzögerung ein kurzes Resumee der Produktion von dotnet.tv Folge 3:

Gestern sind wir mit dem Schnitt endgültig fertig geworden. Vorgestern hatten wir nochmal 10 Stunden im Café Mozart gesessen, um das Video komplett zusammenzusetzen. Dazu kamen dann noch Vor-/Abspann und die Outtakes am Ende.

Das Resultat sind 43min Video. Geplant waren 35min laut meiner Zeitmessung, die ich per Stoppuhr nach Fertigstellung des Drehbuchs einmal mache; dann lese ich alle Texte laut vor mich hin und versuche die Geschwindigkeit zu haben, wie beim Drehen. Das Vorlesen ist aber natürlich nie exakt und eigentlich immer zu schnell. Dass also das Video 20% länger ist, ist normal und durchaus erwartet.

Gestern haben wir die geschnittene Fassung nocheinmal mit frischem Blick durchgeschaut. Nach einer Mütze voll Schlaf ist man wieder kritischer. So haben wir denn auch noch etwas die Lautstärke in einigen Einstellungen angepasst und hier und da gekürzt und sogar zwei Einstellungen gestrichen.

An zwei Stellen hatten sich auch noch echte Fehler eingeschlichen. Einstellungen waren beim Schnitt zerteilt oder überlagert worden. Puh, da waren wir froh, darauf nochmal gestoßen zu sein. Die Lehre für uns: Man muss sich wirklich höllisch konzentrieren beim Zusammensetzen eines Video und immer genau darauf achten, wo in der Reihenfolge der Takes man nun den nächsten einsetzt: am Ende, zwischen zwei Takes oder in einen Take...

Das Endergebnis gefällt uns ganz gut. Bis gestern hatten wir die vielen Szenen ja nie komplett in der richtigen Reihenfolge gesehen. Insofern war uns nicht klar, ob wirklich alles zusammenpassen und Sinn machen würde. Aber ich denke, es ist doch gelungen. Im Grunde ist die Folge schon so geworden, wie ich es mir vorgestellt habe. Es gibt natürlich viel zu verbessern an Technik oder Sprache oder Schauspielkunst; aber für die Kürze der Zeit, das begrenzte Budget und unser kleines Team ist das Resultat erfreulich.

Jetzt kommt es nur noch darauf an, dass Sie als Zuschauer auch Spaß daran haben. Ich bin gespannt auf das Feedback... Die gerade gedrehte Folge 3 wird auf der Heft-CD der dotnetpro 3/04 im März erscheinen und einen Monat später im Internet bei MSDN Online zu sehen sein.

Und jetzt muss ich schon langsam an die Planung der nächsten Folge denken, die wir ab dem 9.2. drehen wollen.

Posted by Ralf Westphal | 2 comment(s)
Filed under:

After a couple of meetings with clients still employing VB3 programms, I come to the conclusion, there is need for a "VB3 to .NET" migration tool. There´s still a lot (!) VB3 software running out there - and developers see no way to move it anywhere. Jumping onto the VB6 bandwaggon is not really the thing to do anymore - and an alternative is not in sight.

Microsoft hasn´t been able to provide a 100% migration path for VB6 to VB.NET due to several reasons - one of them being VB6' object orientation.

With VB3 I don´t see that many hurdles in the way of a real (!) migration tool. It could either be a VB3 to VB.NET/C# converter. This then would be a one way street resulting in a new codebase to be maintained with VS.NET.

Or, I guess, translating VB3 directly to IL or even translating P-Code to IL would be possible. That way, the sources could still be maintained using the old tools.

I don´t know yet, which way to go. But I´ll start soon after some tests. (VBX integration is still kinda mindboggling :-)

Posted by Ralf Westphal | 5 comment(s)
Filed under:

Die äußerlich aufregenden Tage des Drehens sind vorbei. Heute haben wir mit dem Schneiden von dotnet.tv Folge 3 begonnen. Weniger Aufregung bedeutet das jedoch nicht. Fast im Gegenteil! Denn jetzt stoßen wir wieder in für uns unbekanntes Gebiet vor.

Da ist zum einen ein neues Schnittprogramm. Bisher hatten wir mit einem "Kinderprogramm" gearbeitet (iMovie), jetzt ist ein Profitool dran: Final Cut Pro. Ohje, damit kann man wirklich alles machen - wenn man es kann. Die Zahl der Optionen ist schier unermesslich. Und selbst einfachste Dinge - gleichzeitige Modifikation von Bild und Tonspuren - geraten zu einer Suchaktion in den Menüs. Dazu kommen die auch zu erkundenden Macken von Software, die es doch tatsächlich sogar bei Mac-Software noch gibt.


Auch ein Mac hat seine unerwarteten Momente

Zusätzlich ist das Rohmaterial auf Digitalkassetten mehr als doppelt so umfangreich wie bei Folge 2. Wir müssen also mehr sichten und uns bei der Zusammenstellung des Grobschnitts anders organisieren. Wenn man es nicht gelernt hat, dann bedarf das schon einigen Versuchs und Irrtums.

Außerdem haben wir ein paar Szenen, die im Drehbuch unterspezifiziert sind. (Das gibt es also nicht nur bei Software.) Dafür haben wir dann bei Dreh Einstellungen aus verschiedenen Blickwinkeln gedreht, ohne zu wissen, was wir am Ende wie beim Schnitt verwenden. Solche Szenen zusammen zu stellen kostet deutlich mehr Zeit, als klar gegliedertes, chronologisch gedrehtes Material zusammenzusetzen.

Nichtsdestotrotz macht der Schnitt Spaß - selbst wenn die 70 GB Platte ächzt. Zwischendurch haben wir uns auch mit deftigen Leckereien aufgemuntert,


Anwärter auf den Preis für die weltgrößten Schnitzel Wiener Art

und am Ende - mit nur 11 geschnittenen Szenen (von 80) - den Tag gemütlich ausklingen lassen.


Abendstimmung im Café Mozart in München

Morgen geht es für ein paar Stunden weiter. Am Donnerstag steht dann der Löwenanteil zur Verarbeitung an. Puh...

Posted by Ralf Westphal | 3 comment(s)
Filed under:

Manchmal geht auch etwas schief. Heute zum Beispiel: nur noch magere 9 Szenen standen auf dem Zettel. Und alle sollten an einer Location gedreht werden, nämlich am Bildschirm.

Aber mit einem lockeren Drehtag von 2-3 Stunden ist es dann doch nichts geworden. Bei genauerem Hinsehen stellte sich nämlich heraus, dass einige Szenen in mehrere Teile zerlegt werden mussten, um sie vom Text her handhabbar zu machen. (Denn die Wahrscheinlichkeit von Fehlern beim Ablesen steigt anscheinend überproportional mit der Textlänge.) Außerdem kam mir plötzlich in den Kopf, einige Codesequenzen durch Animationen (achten Sie auf den verwaschenen Schemen im Bild!) zu ersetzen.

Und so war Zusatzaufwand vorprogrammiert. Am Ende waren dann zwar alle Szenen im Kasten, aber das Chaos herrschte auf dem Set, dass Sebastian wegen seiner Wartezeiten kurzerhand zu einem Notbüro für sich umfunktioniert hatte. Wireless LAN sei dank, kam er nämlich mit seinem Mac Notebook auch am Drehort ins Internet und konnte Emails in den Drehpausen, in denen ich noch Beispiele programmiert habe, beantworten.

Zum "Einsaugen" des Filmmaterials - ich darf gar nicht dran denken, wieviele Digitalkassetten wir diesmal vollgemacht haben - oder zum Schneiden sind wir heute natürlich nicht mehr gekommen. Das steht morgen wieder ins Haus. Puh... Im Café Mozart freut man sich bestimmt wieder auf uns :-)

Posted by Ralf Westphal | with no comments
Filed under:

Auch ein Filmteam braucht mal ein wenig Ruhe. So hatten wir uns für heute nur 9 kleinere Szenen zum Drehen ausgesucht. Nach zweimal 2 Stunden an unterschiedlichen Drehorten war alles im Kasten.

Unserem Streben nach Anschaulichkeit in der Darstellung

war es dabei geschuldet, dass die Drehorte jeweils Küchen waren.

Das erforderte auch eine aufwändigere Maske - durchaus inspiriert von einem großen internationalen Spielfilm, der ebenfalls in der 3. Folge vor einiger Zeit in die Kinos kam:

Leider können wir allerdings nicht auf ein ähnlich großes Budget zurückgreifen. So mussten wir für den Special Effect ein wenig improvisieren:

Bei der Anpassung eines anderen Körperteils stellte sich ein Erfolg jedoch auch durch beherztes Zupacken von Clarissa, einer unserer heutigen Nebenrollenschauspielerinnen, am Ende nicht ein. Und ich persönlich würde sagen: zum Glück.

Dennoch hieß es gegen 15 Uhr: Ende gut, alles gut. Alle Szenen, die nicht direkt vom Bildschirm abgenommen werden müssen, sind im Kasten. Feierlaune war denn auch völlig angebracht.

Posted by Ralf Westphal | with no comments
Filed under:

Geschafft. Der zweite Drehtag mit wieder 12 Stunden Arbeitszeit liegt hinter uns. Dass wir das Pensum in der Zeit wirklich hinter uns bringen können würden, hätte ich gestern eigentlich nicht gedacht. Es waren noch Szenen von gestern übrig, die wir nicht hatten drehen können, weil in der Lobby des Hilton immer wieder zuviel los war. Zusammen mit den für heute geplanten Szenen ergaben sich eine Zahl von 36 Einstellungen mit z.T. mehreren Abschnitten - gegenüber 28 am ersten Drehtag.

Um 8:00 Uhr morgens war aber das ganze Team pünktlich am Drehort im Hotel und wir konnten gegen 9:00 Uhr mit den ersten Einstellungen in der am Wochenende gewöhnlich unbevölkerten Lobby beginnen.

Michaela, unsere weibliche Hauptdarstellerin für heute, ließ sich da dann auch gleich einmal von mir die Koffer tragen. (dotnet.tv will ja etwas für die Höflichkeitskultur zwischen Männern und Frauen tun :-)

Anschließend habe ich mich dann wieder unter das Hotelpersonal gemischt...

Am Nachmittag waren wir dann durch mit den Szenen im öffentlichen Bereich und bezogen die Präsidenten Suite. Als ersten Eindruck von der Geräumigkeit dieser Zimmerflucht hier nur ein Blick ins größte ihrer Bäder - das wohl ca. 40qm groß ist:

Trotz der Geräumigkeit der Suite hatten wir aber natürlich kein Problem, sie mit unseren kleinen Team schnell mit Beschlag zu belegen. Der Konferenzraum sah durch unsere Requisiten, Koffer usw. im Nuh aus wie ein Schlachtfeld_

Die räumliche Trennung des Drehbereiches

bot dann auch willkommene Gelegenheit, sich zwischendurch für´s Rollenstudium zurückzuziehen:

Und wer nicht glauben wollte, dass sich das lohnen würde, wurde schnell bei einigen Szenen eines Besseren belehrt:

(Was nicht bedeutet, dass auch alle Einstellungen es am Ende auch in die finale Version schaffen werden. Denn manches mag interessant gedacht gewesen sein - aber am Ende z.B. aus pädagogischen Gründen doch ungeeignet für eine manchmal junge Leserschaft von dotnetpro und MSDN Online sein.)

Aber nicht nur von den Schauspielern verlangte das Drehbuch einiges ab:

So war es am Ende nicht verwunderlich, dass "Aufbaupräparate" zum Einsatz kommen mussten, um ein Durchhalten bis zur letzten Einstellung zu gewährleisten.

Für Sonntag ist der Drehbeginn auf 10:30 Uhr festgesetzt. Hoffentlich können alle die Zeit bis dahin zur Regeneration nutzen.

Posted by Ralf Westphal | with no comments
Filed under:

Es ist wieder soweit: Die Dreharbeiten zur nächsten Folge von dotnet.tv haben begonnen. Und ich bin sehr erleichtert: sie gestalten sich in mancherlei Hinsicht leichter als gedacht. Aber der Reihe nach:

Das Thema diesmal lautet: COM+ oder .NET Enterprise Services. Es geht also um den Windows Application Server. Das heißt, diesmal dreht sich alles um eine große Infrastrukturumgebung für Softwarekomponenten. Aus diesem Grund haben wir uns auch als Drehort ein Hotel gewünscht - und dieser Wunsch ist in Erfüllung gegangen. Mit dem Hilton Park Hotel in München haben wir eine sehr hübsche Location gefunden, die uns überaus freundlich aufgenommen hat.

Die Idee hinter einem Hotel als Drehort ist, das Thema "Infrastruktur" innerhalb einer Infrastruktur zu präsentieren. Statt Erklärungen dazu in einem nichtssagenden Studio abzugeben, scheint es uns attraktiver, mit jeder Einstellung das Thema schon durch das Ambiente zu transportieren. Ich bin gespannt, ob das am Ende auch so funktioniert. Denn nichts war ja schon beim deutschen Autorenfilm schlimmer, als dass sich der Autor und Regisseur in einer wunderbaren Idee verfangen hat, die beim Zuschauer dann aber nicht ankommt. Na, wir sind gespannt...

Mühe haben wir zumindest in das Drehbuch gesteckt. So sind denn auch knapp 80 Szenen herausgekommen - 30 mehr als bei Folge 2 -, die wohl am Ende zu 100 Schnitten führen werden. Ein Aufwand, der deutlich größer ist als beim letzten Mal. Im Augenblick sind wir aber mal optimistisch, dass wir mit etwas geänderter Schnitttechnik zeitlich dabei nicht aus dem Ruder laufen. Auch hier sind wir gespannt... Die Balance zwischen technischem Aufwand und guter Didaktik und verständlichen technischen Erklärungen zum Thema behalten wir hoffentlich.

Wenn dann aber am Ende gar nichts geholfen hat, dann reißen hoffentlich unsere "Wunderwaffen" die Produktion noch 'raus: Dieses Mal haben wir auch nicht bei den Komparsen gespart. Dynamische Männer

und hübsche Frauen bevölkern Szene um Szene... :-)

Um den Erfolg des Konzepts zu sichern, durften wir natürlich nicht bei der Technik und Vorbereitung nicht sparen. Sebastian hat sich um alles gekümmert. Funkmikrofone, Teleprompter und Nervennahrung sollen uns die Dreharbeiten erleichtern.

Aber nicht nur uns...

Auch unsere "guten Fee" Katrin vom Hilton Park, uns als Begleitung durch dick&dünn zugeordnet und ohne deren Hilfe so manches sich nicht hätte realisieren lassen, muss sich zwischendurch stärken.

Bei knapp 12 Stunden Drehzeit heute ist das auch wirklich nötig - wie das Verhalten einiger Teammitglieder zeigt:

Letztlich siegte dann aber doch immer wieder der Drang, die Arbeiten im 12. Stock mit Blick über die Stadt fortzusetzen:

 

Posted by Ralf Westphal | with no comments
Filed under:

In my previous posting ("Check for logged on user on Windows machine") I showed a WMI-based method to check, if a certain user was logged on. Andreas Häber then suggested in a comment, to use the Win32 API function NetUserEnum instead. Since WMI always sounds to me like a heavyweight technology, I liked the idea to resort to "simple" Win32 API calls to solve the problem. Below you find the result of my attempt wrapped in a VB.NET module.

Usage:

Call GetUsersLastLogOn() to retrieve an array of all users of the machine running the code. The array contains UserLastLogOn objects providing a username and the date/time of the user´s last logon (lastLoggedOn).

Dim users() As LogOnInfo.UserLastLogOn
users = LogOnInfo.GetUsersLastLogOn()
For Each user As LogOnInfo.UserLastLogOn In users
    Console.WriteLine(user.username & ", " & user.lastLoggedOn)
Next

To solve the problem of a Windows service checking for logged on users you need to compare the logon time of the users to a timestamp (e.g. the start time of the Windows service process (call Process.GetCurrentProcess.StartTime())).

However, although a user might have logged on after the Windows service started, you never know, if he/she still is logged on. The last_logoff information in the USER_INFO_2 structure is not used by Windows.

That´s the main reason why I find this solution inferior to the one using WMI. Plus, it cost much more effort to code it (compiling information, translating the USER_INFO_2 structure to VB.NET, finding a way to convert UTC seconds to DateTime (easy, but eluded me for quite some time)).

How does it work:

NetUserEnum returns a list of USER_INFO_2 structures containing information about users of a certain machine. Passing nothing as the first parameter to NetUserEnum requests users from the local machine, passing 2 as the second parameter requests the info to be returned as USER_INFO_2 structures.

GetUsersLastLogOn() iterates over all USER_INFO_2 structures, extracts the user name and the last_logon time expressed in UTC seconds since 1/1/1970. The time is converted to a DateTime structure with a simple calculation: add the seconds to midnight of 1/1/1970. The extracted information is wrapped in UserLastLogOn objects and returned as an array.

Limitation:

NetUserEnum returns several USER_INFO_2 structures with each call. Calling it once on my system thus is enough. On machines with far more user accounts, though, it might be necessary to call the function several times. However, I did not test that scenario.

Code:

Imports System.Management
Imports System.Runtime.InteropServices

Module LogOnInfo
    <StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Unicode)> _
    Friend Structure USER_INFO_2
        Public name As String
        Public password As String
        Public password_age As Integer
        Public priv As Integer
        Public home_dir As String
        Public comment As String
        Public flags As Integer
        Public script_path As String
        Public auth_flags As Integer
        Public full_name As String
        Public usr_comment As String
        Public param As String
        Public workstations As String
        Public last_logon As Integer
        Public last_logoff As Integer
        Public acct_expires As Integer
        Public max_storage As Integer
        Public units_per_week As Integer
        Public logon_hours As Byte
        Public bad_pw_count As Integer
        Public num_logons As Integer
        Public logon_server As String
        Public country_code As Integer
        Public code_page As Integer
    End Structure


    Public Declare Function NetUserEnum Lib "Netapi32.dll" ( _
        <MarshalAs(UnmanagedType.LPWStr)> ByVal servername As String, _
        ByVal level As Integer, _
        ByVal filter As Integer, _
        ByRef bufptr As IntPtr, _
        ByVal prefmaxlen As Integer, _
        ByRef entriesread As Integer, _
        ByRef totalentries As Integer, _
        ByRef resume_handle As Integer) As Integer

    Public Declare Function NetApiBufferFree Lib "Netapi32.dll" (ByVal buffer As IntPtr) As Integer


    Public Class UserLastLogOn
        Private _username As String
        Private _lastLoggedOn As DateTime

        Friend Sub New(ByVal ui As USER_INFO_2)
            _username = ui.name
            _lastLoggedOn = #1/1/1970#.AddSeconds(ui.last_logon)
        End Sub

        Public ReadOnly Property username() As String
            Get
                Return _username
            End Get
        End Property

        Public ReadOnly Property lastLoggedOn() As DateTime
            Get
                Return _lastLoggedOn
            End Get
        End Property
    End Class

 

    Public Function GetUsersLastLogOn() As UserLastLogOn()
        Dim users As New ArrayList

        Dim entriesRead, totalEntries, hResume As Integer
        Dim bufPtr As IntPtr

        NetUserEnum(Nothing, 2, 2, bufPtr, -1, entriesRead, totalEntries, hResume)
        If entriesRead > 0 Then
            Dim iter As IntPtr = bufPtr
            For i As Integer = 0 To entriesRead - 1
                users.Add(New UserLastLogOn(Marshal.PtrToStructure(iter, GetType(USER_INFO_2))))
                iter = New IntPtr(iter.ToInt32 + Marshal.SizeOf(GetType(USER_INFO_2)))
            Next
        End If
        NetApiBufferFree(bufPtr)

        Return users.ToArray(GetType(UserLastLogOn))
    End Function


    Public Function IsUserLoggedOn(ByVal userName As String) As Boolean
        Dim mc As New ManagementClass("Win32_Process")
        Dim moc As ManagementObjectCollection = mc.GetInstances
        Dim mo As ManagementObject
        For Each mo In moc
            Dim p As New ROOT.CIMV2.Process(mo)

            Dim processDomain, processUser As String
            p.GetOwner(processDomain, processUser)
            If processUser = userName Then
                Return True
            End If
        Next
    End Function
End Module

Resources:

-Documentation of NetUserEnum Win32 API function
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/netmgmt/netmgmt/netuserenum.asp

-Documentation of USER_INFO_2 structure:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/netmgmt/netmgmt/user_info_2_str.asp

Motivated by a posting in a user group I tried to find the answer to the question: How can a Windows Service find out, if a user has already logged onto the machine it is running on?

I googled for a while and it seems, there is no simple function like "IsUserLoggedOn" in the Win32 API or in the .NET Fx base class library. But I stumbled across a hint which pointed into the direction of WMI as a solution.

So here is my VB.NET version of the function IsUserLoggedOn(). It gets a list of all processes on a machine from WMI and asks each one for the name of the user it is running under. So if a Windows Service is checking for a certain user, e.g. John Doe, if he has already logged on, it just needs to call the function periodically. (If instead it wants to check if any user has logged on, it needs to see, if processes exist running under a non-system account.)

Imports System.Management

Module Module1
    Sub Main()
        Console.WriteLine(IsUserLoggedOn("John Doe"))
    End Sub


    Private Function IsUserLoggedOn(ByVal userName As String) As Boolean
        Dim mc As New ManagementClass("Win32_Process")
        Dim moc As ManagementObjectCollection = mc.GetInstances
        Dim mo As ManagementObject
        For Each mo In moc
            Dim p As New ROOT.CIMV2.Process(mo)

            Dim processDomain, processUser As String
            p.GetOwner(processDomain, processUser)
            If processUser = userName Then
                Return True
            End If
        Next
    End Function
End Module

For this code to work you need to reference the WMI library assembly System.Management in your project. Then download the WMI Server Explorer Add-In for VS.NET 2003 (see resources below) and create a managed class (ROOT.CIMV2.Process) for the Processes node in the Management Classes branch of the server explorer. Include the above function IsUserLoggedOn() and you´re set.

Resources:

-A introductory article on WMI:
http://msdn.microsoft.com/msdnmag/issues/02/05/WMIMan/default.aspx

-Download link for WMI Server Explorer Add-In for VS.NET 2003
http://www.microsoft.com/downloads/details.aspx?familyid=62d91a63-1253-4ea6-8599-68fb3ef77de1&displaylang=en

 

More Posts