Mono, Ubuntu and Clipboard

Reading clipboard in .NET is fairly straightforward.

   var text = Clipboard.GetText();

Once you stumble into Mono under Linux, things tend to change. Clipboard works only inside your application, rest of system works on different plane altogether. In order to get text from system clipboard we need to have following code:

    Gtk.Clipboard clippy = Gtk.Clipboard.Get(Gdk.Atom.Intern("CLIPBOARD", false));
    var text = clippy.WaitForText();

And here comes the problem. These functions need reference to both gdk-sharp and gtk-sharp. Unfortunately this also means that you cannot run it without MONO runtime. You can forget running application under Microsoft’s .NET Framework. Even if we don’t use those functions our applications will fail upon startup since it cannot resolve all references.

I solved this issue by having separate .dll for clipboard operations on Linux but I haven’t created any reference to it. In order to determine clipboard content I use reflection to load it (only if we are on Mono):

private string GetClipboardText() {
    if (IsRunningOnMono) {				
        var fullExePath = new FileInfo(Assembly.GetExecutingAssembly().Location);
        var gtkAss = Assembly.LoadFile(Path.Combine(fullExePath.DirectoryName, "GtkClipboard.dll"));
        var assMethod = gtkAss.GetTypes()[0].GetMethod("GetGtkText");
        txtClipboard.Text = assMethod.Invoke(null, new object[] { }) as string;
    } else {
       txtClipboard.Text = Clipboard.GetText();
    }
}

private static bool IsRunningOnMono {
    get {
        return (Type.GetType("Mono.Runtime") != null);
    }
}

And what should we put in our .dll? It ends up that it is quite simple code:

public static class Clipboard {
    public static String GetGtkText() {
        Gtk.Clipboard clipboard = Gtk.Clipboard.Get(Gdk.Atom.Intern("CLIPBOARD", false));
        if (clipboard != null) {
            return clipboard.WaitForText();
        } else {
            return "";
        }
    }
}

P.S. You will notice here that reflection code loads first class that comes in array. Since I only have one class in that whole .dll this is not an issue. If you add more classes to that assembly take care that you select correct one.

Showing Form in Mono

When I develop application that should be usable on Linux I usually program it in C# with Visual Studio. From time to time I test it with Mono Migration Analyzer. End result is usually runnable under Linux after some further debugging. This time I had to create application that would be used primarily under Linux so I took different approach - MonoDevelop.

Most difficult thing to get used to after transfer from Visual Studio world is lack of Windows Forms designer; everything needs to be done by hand. Doing that I stumbled upon strange behavior. Take this simple code:

private void SomeButton_Click(object Sender, EventArgs e) {
    using (var frm = new SomeForm()) {
        frm.ShowDialog(this);
    }
}
...
internal class SomeForm : Form {
}

This code is dead simple - there is no chance of error. However this code also doesn’t work properly under Mono 2.6 (I haven’t checked other versions). Form will be shown first time button is clicked but not second time. What puzzled me the most is fact that I knew that that ShowDialog should work - other .NET application that I used under Linux worked perfectly.

First rule of bug-triage is to trim code as much as possible to determine which line is problematic one - there was nothing to trim here and I still had a bug. As it turned out, this bug shows it’s ugly head only if you don’t have any controls on Form. As soon as first control is added everything works as expected. I haven’t seen this behavior in my other applications since I never had an empty form to show.

It just shows that testing smallest possible working code is not always an answer.

Pointless Mails

I just got e-mail from Microsoft with my free 30-day pass for Windows Azure. It came in mail titled “Take Advantage of a Free 30-Day Windows Azure Pass”. Last year I wanted to get into but Croatia wasn’t among supported countries. From good sources I got that this year this will change. I took this offer as good sign.

Of course, when I came to registration site, Croatia was suspiciously missing from list. Another disappointment.

I am not that much disappointed at Microsoft as such since it is their business decision whether they want Croatian money or not. I am disappointed by assholes who sent such mails to everybody everywhere. Microsoft has all my data (and that does include country). How hard was it to filter out people not in “proper” countries?

White-on-White Spinner

Illustration

As far as Android goes, I like it’s light theme much better that black one. It is matter of preference really since both are equally functional.

First time I used Spinner control I noticed interesting result - I got white text on white background. While other controls used theme that was defined application wide Spinner forced white background on it’s drop-down. Black theme defines white text on black background, Spinner forces white background - white text on white background is their bastard offspring.

Solving this took a little bit of time and single line. It was easiest to change background color for drop-down just after it is inflated:

@Override
public View getDropDownView(int position, View convertView, ViewGroup parent) {				
    if (convertView == null) {			
        View view = _inflater.inflate(android.R.layout.simple_list_item_single_choice, parent, false);
        view.setBackgroundColor(Color.BLACK);
        ((TextView)view).setSingleLine();
        ((TextView)view).setEllipsize(TruncateAt.END);
        convertView = view;
    }
    return super.getDropDownView(position, convertView, parent);
}

P.S. It might be as well that I broke something to get Spinner behave this weirdly - I haven’t investigated much further after I got the result.

ConnectionString Loses Password Field

Illustration

One application used ConnectionString that read “Server=.\SQLEXPRESS; Database=MyDatabase; MultipleActiveResultSets=true; User ID=MyUser; Password=MyPassword; Connect Timeout=10;”. Yes, I know all dangers of transporting user id and password in this manner, but trust me, there was no other choice for this particular scenario. It worked for quite some time and I was satisfied with it.

As upgrade went, I needed one additional connection. I saw no problem with it, I did it plenty of times:

public SqlConnection Clone(SqlConnection original) {
    var clone = new SqlConnection()
    clone.ConnectionString = original.ConnectionString
    return clone;
}

However, in this particular scenario, all I got was “Login failed for user…” message. After some checking, I noticed one peculiar thing. My connection string, as far as SqlClient was concerned, was “Server=.\SQLEXPRESS; Database=MyDatabase; MultipleActiveResultSets=true; User ID=MyUser; Connect Timeout=10;”. My Password field was completely missing!

ConnectionString gets cleaning treatment and password just gets omitted. Solution comes in form of one additional parameter. If we add “Persist Security Info=true;” to out ConnectionString SqlConnection will give us enough rope to hang ourself and Password field will be preserved.