Von Windows 7 aus via ODBC auf eine benannte SQL-Server-2000-Instanz zugreifen

Heute ist Legacy-Tag!

Issue

Für ein Projekt müssen wir von Microsoft Office Access 2010 auf einen SQL Server 2000 via ODBC zugreifen.

Die Herausforderung ist, dass der SQL Server 2000 als benannte Instanz („Named instance“) auf dem Server installiert ist (Windows 2008 R2). Also so was wie „MEINSERVER\SQL2000“, anstatt nur „MEINSERVER“.

Lokal auf dem Server kann ich wunderbar auf den SQL Server 2000 zugreifen, alleine via Netzwerk von einem PC mit Windows 7 ging es nicht. Es kam immer wieder die Meldung, dass keine Verbindung hergestellt werden konnte.

Lösung

Die Lösung war dann im Microsoft-Knowledge-Base-Artikel „How to connect to SQL Server by using an earlier version of SQL Server“ zu finden:

Vom Prinzip via cliconfg.exe-Programm auf dem Client einen Alias zu IP-Adresse und Port des SQL Server 2000 machen und dann über den Alias zugreifen.

Zitate aus der Lösung:

Server:

Determine the TCP/IP port number of the instance of SQL Server. To do this, use one of the following methods, depending on which version of SQL Server that you are running.

  1. On the server that is running SQL Server 2000, start the Server Network Utility. To do this, click Start, click All Programs, click Microsoft SQL Server, and then click Server Network Utility.
  2. Click the General tab, and then select the instance that you want from the Instances list.
  3. Click TCP/IP, and then click Properties. The TCP/IP port number for this instance is shown. Note this number so that you can use it later.

Client:

Configure the server alias on the client computer. To do this, use one of the following methods, depending on your version of SQL Server.

  1. Start the Client Network Utility. To do this, click Start, click Run, type cliconfg.exe, and then press Enter.
  2. On the General tab, verify that TCP/IP appears in the list under Enabled protocols by order.
  3. Click the Alias tab, and then click Add.
  4. Under Network libraries, select TCP/IP.
  5. In the Server name text box, type the IP address of the server that is running SQL Server 2000.
    Note The IP address that you type here is the one that is configured to use the TCP/IP port number.
  6. Click to clear the Dynamically determine port check box, and then type the port number of the instance of SQL Server 2000 in the Port number text box.
  7. Type a name in the Server alias text box, and then click OK.

Dann ging die ODBC-Verbindung („Verbindungstest“ war erfolgreich).

Merlin

Ein treuer Freund musste diese Woche von uns gehen. Merlin ist tot.

Merlin

Du hast uns allen, Mensch und Hund, viel Spaß und Freude bereitet und es sogar bis zum Programmnamen und eigenem Verb geschafft.

Vielen Dank für die schönen Erlebnisse mit Dir, ob beim Geocaching, beim Spielen mit den Hunden, beim Fondue im Büro oder bei vielen anderen Gelegenheiten. Ich werde Dich stets in guter Erinnerung behalten!

Wir sind sehr traurig.

Texte in JavaScript-Dateien in ASP.NET übersetzen

Der Artikel „Localize text in JavaScript files in ASP.NET“ hat eine hervorragende Lösung um Texte in JavaScript-Dateien (und auch in CSS-Dateien) zu übersetzen.

Das klappt sowohl mit ASP.NET-Website-Projekten, als auch mit ASP.NET-Web-Applikations-Projekten (also dem kompilierten).

Bis auf einen Fall, der mir heute passiert ist. Da kam immer eine Exception, dass er die Ressourcen für die aktuelle Culture nicht findet.

Die Lösung sah dann so aus, dass ich eine Funktion geändert habe.

Alt:

public static string TranslateScript(string text)
{
	if (string.IsNullOrEmpty(text))
	{
		return text;
	}
	else
	{
		var matches = Regex.Matches(text);
        var manager = new ResourceManager(typeof(Resources.Resources));

		foreach (Match match in matches)
		{
			var value = match.Groups[1].Value;

            var obj = manager.GetObject(value);
            if (obj != null)
			{
				text = text.Replace(match.Value, CleanText(obj.ToString()));
			}
		}

		return text;
	}
}

Neu:

public static string TranslateScript(string text)
{
	if (string.IsNullOrEmpty(text))
	{
		return text;
	}
	else
	{
		var matches = Regex.Matches(text);

		foreach (Match match in matches)
		{
			var value = match.Groups[1].Value;

			var obj = HttpContext.GetGlobalResourceObject(@"Resources", value);
            if (obj != null)
			{
				text = text.Replace(match.Value, CleanText(obj.ToString()));
			}
		}

		return text;
	}
}

D.h. ich habe den Zugriff über eine Klasse in einen Zugriff über „GetGlobalResourceObject“ umgewandelt, der erst zur Laufzeit versucht das aufzulösen.

Bandbreite und Paketumlaufzeit

Das hier ist sicher jetzt nicht-wissenschaftlich und ggf. auch in Teilen falsch/ungenau. Ich möchte das trotzdem mal für mich festhalten:

Eine Internetanbindung (z.B. zu einem Server) hat zwei Schlüssel-Parameter, die sie als „schnell“ erscheinen lassen:

Anhand der Bandbreite ist quasi definiert, wie viele Daten in welcher Zeiteinheit übertragen werden können. Die Paketumlaufzeit beschreibt, wie lange es dauert, bis die Daten zum Server übertragen werden und eine Antwort wieder beim ursprünglichen Absender ist.

Aus meiner Sicht sind beide Werte wichtig um die Qualität einer Internetverbindung bewerten zu können; eine zwar hohe Bandbreite gepaart jedoch mit einer lange Paketumlaufzeit („Lag„) ergibt wiederum ein unbefriedigendes Ergebnis, wenn ich z.B. via Remote Desktop auf einen Rechner zugreife, also viele kleine Pakete hin und hergehen.

Bubble Mouse Move events from child controls up the hierarchy in Windows Forms

Windows Forms (or „WinForms“ for short) does not know the concept of event bubbling (also called „event propagation“ sometimes). To solve this in terms of command routing, I’ve written some small classes earlier.

To bubble up events from child controls to parent controls (or the form itself), the idea is to hook into the child control creation and hook up for those specific events and manually forward them.

Based on this idea and with the help of a forum answer on MSDN, I’ve written a small class that you can attach to a control and get all child control MouseMove events. The class looks like:

public sealed class MouseEventBubbler
{
    private readonly Control _attachTo;

    public MouseEventBubbler(Control attachTo)
    {
        _attachTo = attachTo;

        _attachTo.MouseMove += _attachTo_MouseMove;

        _attachTo.ControlAdded += _attachTo_ControlAdded;
        _attachTo.ControlRemoved += _attachTo_ControlRemoved;

        foreach (Control control in _attachTo.Controls)
        {
            AttachToControl(control);
        }
    }

    public void _attachTo_MouseMove(object sender, MouseEventArgs e)
    {
        OnMouseMove(e);
    }

    public event MouseEventHandler MouseMove;

    private void _attachTo_ControlAdded(object sender, ControlEventArgs e)
    {
        AttachToControl(e.Control);
    }

    private void _attachTo_ControlRemoved(object sender, ControlEventArgs e)
    {
        DetachFromControl(e.Control);
    }

    private void AttachToControl(Control c)
    {
        c.MouseMove += Child_MouseMove;
        c.ControlAdded += Child_ControlAdded;
        c.ControlRemoved += Child_ControlRemoved;
        AttachToChildren(c);
    }

    private void AttachToChildren(Control parent)
    {
        foreach (Control child in parent.Controls)
        {
            AttachToControl(child);
        }
    }

    private void DetachFromControl(Control c)
    {
        DetachFromChildren(c);
        c.MouseMove -= Child_MouseMove;
        c.ControlAdded -= Child_ControlAdded;
        c.ControlRemoved -= Child_ControlRemoved;
    }

    private void DetachFromChildren(Control parent)
    {
        foreach (Control child in parent.Controls)
        {
            DetachFromControl(child);
        }
    }

    private void Child_ControlAdded(object sender, ControlEventArgs e)
    {
        AttachToControl(e.Control);
    }

    private void Child_ControlRemoved(object sender, ControlEventArgs e)
    {
        DetachFromControl(e.Control);
    }

    private void Child_MouseMove(object sender, MouseEventArgs e)
    {
        var pt = e.Location;
        var child = (Control)sender;
        do
        {
            pt.Offset(child.Left, child.Top);
            child = child.Parent;
        }
        while (child != _attachTo);

        var newArgs = new MouseEventArgs(e.Button, e.Clicks, pt.X, pt.Y, e.Delta);
        OnMouseMove(newArgs);
    }

    private void OnMouseMove(MouseEventArgs newArgs)
    {
        var h = MouseMove;
        if (h != null)
        {
            h(this, newArgs);
        }
    }
}

I’ve also saved it as a PasteBin.

The class can be adjusted to match other events than the MouseMove event, if required.

Dateigrößenangaben in C# menschenlesbar formatieren

Um aus Byte-Angaben (z.B. von Dateigrößen) menschenlesbare Werte (z.B. „12.5GB“) zu machen, sowie den anderen Weg, habe ich mir ein bisschen Code geschrieben bzw. von Stack Overflow zusammen gesucht:

public static class SizeTranslationHelper
{
    /// <summary>
    /// Converts bytes to human-readable, e.g. "12.5GB".
    /// </summary>
    public static string MakeLazy(ulong byteCount)
    {
        string[] suf = { @"B", @"KB", @"MB", @"GB", @"TB", @"PB", @"EB" }; //Longs run out around EB
        if (byteCount == 0)
            return "0" + suf[0];
        var bytes = (ulong)Math.Abs((decimal)byteCount);
        var place = Convert.ToInt32(Math.Floor(Math.Log(bytes, 1024ul)));
        var num = Math.Round(bytes / Math.Pow(1024ul, place), 1);
        return (Math.Sign((decimal)byteCount) * num).ToString(CultureInfo.InvariantCulture) + suf[place];
    }

    /// <summary>
    /// Converts human-readable, e.g. "12.5GB", to bytes.
    /// </summary>
    public static ulong TranslateLazySize(string sizeLazy)
    {
        sizeLazy = sizeLazy.ToLowerInvariant();

        ulong result = 0;
        if (tryParse(ref result, sizeLazy, @"b", 1ul))
        {
            return result;
        }
        else if (tryParse(ref result, sizeLazy, @"kb", 1024ul))
        {
            return result;
        }
        else if (tryParse(ref result, sizeLazy, @"mb", 1024ul * 1024ul))
        {
            return result;
        }
        else if (tryParse(ref result, sizeLazy, @"gb", 1024ul * 1024ul * 1024ul))
        {
            return result;
        }
        else if (tryParse(ref result, sizeLazy, @"tb", 1024ul * 1024ul * 1024ul * 1024ul))
        {
            return result;
        }
        else if (tryParse(ref result, sizeLazy, @"pb", 1024ul * 1024ul * 1024ul * 1024ul * 1024ul))
        {
            return result;
        }
        else if (tryParse(ref result, sizeLazy, @"eb", 1024ul * 1024ul * 1024ul * 1024ul * 1024ul * 1024ul))
        {
            return result;
        }
        else
        {
            decimal r;
            if (decimal.TryParse(sizeLazy, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out r))
            {
                return (ulong)r;
            }
            else
            {
                throw new Exception(string.Format(@"Cannot parse '{0}' to number.", sizeLazy));
            }
        }
    }

    private static bool tryParse(ref ulong result, string sizeLazy, string suffix, ulong factor)
    {
        decimal r;
        if (
            sizeLazy.EndsWith(suffix) &&
            decimal.TryParse(sizeLazy.Substring(0, sizeLazy.Length - suffix.Length), NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out r))
        {
            result = (ulong)(r * factor);
            return true;
        }
        else
        {
            return false;
        }
    }
}

Hier noch der Pastebin-Eintrag dazu.

Magnus aus Crailsheim

Kennt von Euch noch jemand die Sendung „Dr. Music“, so in den 80er-Jahren auf damals noch SDR 3?

Das war eine Sendung, die Abends/Nachts lief und bei der Hörer anrufen konnten und allerlei (in meinen damaligen Augen und Ohren) sauschwere Frage rund um das Thema Musik beantworten konnten. Wenn die Antwort richtig war, ging’s zur nächsten, schwierigeren Frage, bis entweder der Anrufer aussteigen wollte und dann einen Haufen Plattengutscheine und richtig wertvolles Zeugs gewonnen hatte, oder er eine Frage falsch beantwortete und alles verlor (glaube ich).

Na auf jeden Fall hat dort ganz oft ein und derselbe Typ angerufen und der wusste immer ganz viel Antworten auf die vielen schweren Fragen. Und der hieß eben Magnus und hat immer aus Crailsheim angerufen.

Das blieb mir noch in Erinnerung. Schöne Zeit damals, auch wenn’s kein Internet gab, zumindest kein öffentliches.

Meine Rezension zum Buch „Leichtmatrosen“ von Tom Liehr

Nachdem mir der Autor sogar persönlich einen Kommentar zu meiner Ankündigung geschrieben hat, versuche ich mich hier mal in einer kurzen Rezension vom Buch „Leichtmatrosen“ von Tom Liehr.

Getreu dem Motto „Wer im Glashaus sitzt kann sehr wohl mit Steinen werfen“, bin ich mir bewusst, dass meine Rezension selbst von einem Grundschüler mit Legasthenie deutlich an intellektuellem Niveau übertroffen werden kann.

Meine Rezension

Gleich zu Beginn: Mir hat das Buch sehr gefallen.

Wie bei den anderen Bücher vom Autor, empfinde ich auch bei diesem Buch, dass es so schön dahin plätschert und als externer Beobachter eine Geschichte beschreibt, unaufgeregt und entspannt. Das macht es für mich sehr angenehm zu lesen.

Subjektiv habe ich den Eindruck, dass die Sprache und Formulierungen hier nochmals deutlich besser und virtuoser sind als in den vorherigen Büchern. Auch das finde ich sehr gut.

Bis zur Mitte des Buches dachte ich ständig „Wann geht’s endlich los mit der Story/Handlung?“ und habe dann verstanden, dass das bereits die Handlung war. Das hat mich ein bisschen enttäuscht und irgendwie auch nicht. Ich konnte so herrlich entspannen und Spaß beim Lesen haben.

Weiter hinten wurde es dann spannender und gegen Ende ein schönes „Happy End“, was ich als sehr angenehm empfand, zumal ich ja selber als frischer Vater mich ein bisschen wieder gefunden habe. Toll finde ich die Einleitungen und den Epilog, in dem der Autor erneut „menschlich“ rüber kommt, wenn er Hintergründe zum Buch erzählt.

Mir gefällt auch, dass das ganze Buch so geschrieben ist, dass ich mich sofort drin hineinfinden konnte, der Autor also quasi so schreibt, wie ich denke. Ich habe da auch schon ganz andere Bücher lesen müssen, die ich dann entnervt wieder weggelegt hatte, weil es mir zu kompliziert war.

Und toll war nicht zuletzt, dass diesmal auf die verschiedenen Vor- und Rückblenden (fast?) komplett verzichtet wurde.

Mein Fazit

Buch kaufen, Autor huldigen und anbeten, auf dass das nächste Buch von ihm ASAP kommt.