Friday, September 23, 2005

How I got FreeTextBox 3.0 working without aspnet_client

[11/22/05 Update: FreeTextBox 3.1.1 requires fewer fixes, and an updated blog on the process is here].

FreeTextBox is a (free) rich text editor control for ASP.NET 1.1 and 2.0. It is pure JavaScript, supports Mozilla, IE, and other popular browsers, and works great (almost). You can add it to your web designer toolbox and just drop it onto a web page and you have rich text editing for free. It's incredibly easy, and the other has done a great job. With the release of FreeTextBox 3.0, the requirement for a very hefty aspnet_client subdirectory was replaced with an http handler that can fetch all of the control's dependencies (gifs, jpgs, js, etc.) straight out of the control's assembly. That means deployment is as easy as copying a single FreeTextBox.dll file into the Bin directory of your ASP.NET web application and you are ready to launch. Swell... except it doesn't work. Not quite.

To get the FreeTextBox control to pull its dependencies out of the assembly rather than external files, its installation instructions claim that with ASP.NET 1.1 you have to add an HTTP handler to your web app's Web.config file, and that with ASP.NET 2.0, you don't. You actually have to modify Web.config for both versions of ASP.NET. Just follow the instructions that they give for ASP.NET 1.1 for modifying Web.config, and your ASP.NET 2.0 app will be on its way to using just the .dll. In my next blog post, I'll talk about enhancing their instructions so your FreeTextBox.dll assembly doesn't even have to be in your Bin directory.

The way FreeTextBox 3.0 comes from their download page, it has a few bugs that get in the way of completely eliminating dependencies on aspnet_client (at least up to version 3.0.6, the current version as of the time of this writing). I'll enumerate them here, and how to get around them. But first, let me prescribe the code you use to try to get it working for yourself, and then I'll list the gotchas that you'll likely run into, and how to solve them.

For the purpose of this blog, I will be assuming that you are following along using ASP.NET 2.0. However, very nearly all (if not all) of the steps would be the same for ASP.NET 1.1.

  1. Modify your Web.config file so that it includes this snippet between the <system.web> tags:
    <httpHandlers>
     <add verb="GET" path="FtbWebResource.axd" type="FreeTextBoxControls.AssemblyResourceHandler, FreeTextBox"/>
    </httpHandlers>
  2. Now open up an empty .aspx page. Add this line just below the <%@ Page ... %>line:
    <%@ Register Assembly="FreeTextBox" Namespace="FreeTextBoxControls" TagPrefix="FTB" %>
    Now add this tag where you want the rich text box to appear:
    <ftb:freetextbox id="FreeTextBox1" supportfolder="~/FtbWebResource.axd" focus="true" runat="Server"/>
    Notice that you must explicitly set these attributes as given. A few hiccups may/will happen, and are listed below, if you don't. For now, just do it. :)
  3. Copy the FreeTextBox.dll assembly that you downloaded into your web application's Bin directory. If you are using ASP.NET 2.0, grab the copy of the .dll that resides in the "bin\Framework 2.0 Beta 2" subdirectory of the downloaded zip.
  4. Start your web application and browse to the .aspx you just created.

At this point, you will either see a working FreeTextBox, or (and much more likely) you will run into one or more of the issues below. Next to each issue is its fix.

  • FreeTextBox improperly caches certain HTML attributes for the life of the web application. If you change attributes in a tag (like SupportFolder or any of the ...Location= attributes), you will typically need to restart your web site/server in order for the changes to actually get noticed. The easiest way to do this is to re-save your Web.config file. You don't need to make any changes to Web.config. Just save it to get ASP.NET to dump the web app and reload it, effectively dumping FTB's cache.
  • Failed to map the path '/aspnet_client/FreeTextBox/Languages/en-US.xml'.
    This comes up when you haven't set the SupportFolder attribute to point to your .axd handler. Be sure to work from the snippet I include above, with emphasis on the SupportFolder="~/FtbWebResource.axd" attribute. And remember to re-save Web.config so that the new attribute will be noticed.
  • FreeTextBox has not been correctly installed. To install FreeTextBox either:
    1. add a reference to FtbWebResource.axd in web.config:
      <system.web>
       <httphandlers>
        <add verb="GET" path="FtbWebResource.axd" type="FreeTextBoxControls.AssemblyResourceHandler, FreeTextBox"/>
       </httphandlers>
      </system.web>
    2. Save the FreeTextBox image and javascript files to a location on your website and set up FreeTextBox as follows
      <ftb:freetextbox id="FreeTextBox1" supportfolder="ftbfileslocation" javascriptlocation="ExternalFile" toolbarimageslocation="ExternalFile" buttonimageslocation="ExternalFile" runat="server"/>

    This is the most fatal flaw in FreeTextBox, and it's the one that ultimately trips most people into giving up. The error lies in FreeTextBoxControls.Resources.JavaScript.FTB-Utility.js, one of the javascript files that are embedded in FreeTextBox.dll. This file exists in the aspnet_client directory (that we're not using), and oddly it's not the same file. And the embedded file has two typos in it that keep the browsers from parsing it, resulting in this error. The fix is not too difficult, honestly, but it's the pits that it has to be done. Keep in mind to, that this fix probably can't be redistributed because of copyright laws. So fix it for yourself, and anyone else who wants this fix should download the control themselves, and then come here to patch it themselves. Here's the fix:

    1. Copy your FreeTextBox.dll from your web app's Bin directory to some temporary and clean directory. For this example, I'll assume c:\temp\ftb. When you download FreeTextBox, 3 versions of the FreeTextBox.dll are included (for some reason). One is for .NET 1.0, another for 2.0, and another one that isn't labeled. Work with the one in the 2.0 directory. At least that is what I did, so I know it works.
    2. You'll need the .NET Framework SDK for this step, but that comes with Visual Studio, to access it from the command prompt, navigate through your Start menu to your Visual Studio 200x group, and then deeper into the "Visual Studio Tools" group. Launch "Visual Studio 200x Command Prompt". This opens a command prompt with the PATH variable set so you can use the SDK commands easily.
    3. Change to the temporary directory you created with FreeTextBox.dll in it.
      cd \temp\ftp
    4. Decompile the FreeTextBox.dll assembly. We need to fix the embedded .js file within it. This is how you do it:
      ildasm /out=FreeTextBox.il FreeTextBox.dll
      You should now have hundreds of files in your little temporary directory. These files all make up what you will eventually recompile into your patched version of FreeTextBox.dll.
    5. Keep your command window open. You'll use it later.
    6. Using your favorite JavaScript editor (Notepad does just fine), fix two lines in FreeTextBoxControls.Resources.JavaScript.FTB-Utility.js:
      old line 14: for(var i = 0; i < arguments.length; i++){
      new line 14: for(var i = 0; i < arguments.length; i++){
      old line 35: for(i = listEvents.length - 1; i < = 0; i = i - 1){
      new line 35: for(i = listEvents.length - 1; i <= 0; i = i - 1){
      These changes are to change an HTML representation for the less-than sign to the sign itself, and to remove the space between <= that shouldn't be there.
    7. It's a really good idea to increment the version number of the assembly you're building. Otherwise, .NET thinks that your assembly is identical to the old one, and won't trash the old one in its cache with your new one, and the bug won't go away. Modify 1 line in FreeTextBox.il to increment the version number:
      old line 2251: .ver 3:0:6600:6
      new line 2251: .ver 3:0:6601:6
    8. One optional change can be made in FreeTextBox.il while you're there. It's a shame that the SupportFolder="~/FtbWebResource.axd" attribute has to be manually added to every FreeTextBox tag you drop into a web page. Change these few lines in your .il file to fix the default value so you don't have to: 35305, 18049, 17992. Don't mess with the IL hex code--just the string. Below is an example of the change.
      old line 35305: IL_001d: ldstr "/aspnet_client/FreeTextBox/"
      new line 35305: IL_001d: ldstr "~/FtbWebResource.axd"
    9. In the command window you used to decompile, recompile the FreeTextBox.dll like this:
      ilasm /dll FreeTextBox.il /output=FreeTextBox.dll /quiet /key=yourkey.snk
      You'll need to have your own strong name key. You can generate one with sn.exe -k yourkey.snk
    10. Copy your new FreeTextBox.dll assembly to your web app's Bin directory. This change to Bin should automatically cause ASP.NET to restart the web app, so just refreshing your browser should now eliminate the error that was displayed initially. Yay!
    11. One note: if you've already been using the GAC to store FreeTextBox, you'll need to add your new assembly to the GAC and adjust your Web.config and .aspx files to point to your own version of FreeTextBox before you'll see the fix. If you didn't follow that, don't worry about it. Read my next blog when it is published.

    So that's it. That should get it working for you. Please post a comment below either way. Let me know if this has helped, or if there are other bugs/issues you've run into, including their fix, if you have it. I'm not an expert on FreeTextBox, but seeing as the author of it doesn't seem to be responding to any forum messages and isn't updating his installation wiki to be correct, let's make this blog useful for everyone!

Wednesday, September 14, 2005

Someone responded to my recent post...

Someone responded to my recent post, and made some good, and some poor comments. This is in response.

"Uhh… install it and choose KDE as the default session the next time you see a login screen. Not really hard. To install KDE is just as easy - open up a terminal and type “sudo apt-get install kde”."

Maybe that works in Debian, but I use some less-well-known *cough* distros: SuSE 9.3 Pro and Gentoo, and installing KDE after Gnome, or vice versa, has RARELY added the new screen manager to my login screen. It has occurred, but as a rule, I have to reinstall the whole distro and select both in order to get both to show up on the menu. Am I a moron after all? Hmmmm... I think the distro is misbehaving. But that's my point. It's not (always) as easy as it is for the developers. If it works for the developers and most users, that apparently doesn't mean it works for everybody.

"Press on the little update notification on your panel and have all of your apps updated (not just the OS itself). Now that’s incredibly hard, you are right."

I've seen that icon on some distros, yes. But not all recent ones make it that easy. However, I'll submit that some distros make it as easy as Windows does, and yes, having it work for (some/most) apps plus the OS rather than just the OS is a huge plus.

"About your other drivel. You are aware of sudo and acls, aren’t you?

And you are aware that you can share folders on modern linux distros by simply clicking, just like you described for windows?"

Oh please. Did you even read that paragraph, or did you just skim it? I know you can share with Linux. I mention ACLs myself in that blog. Duh. Someone's not reading. (I'm only being blunt because you take no pains to avoid it. I can hear you now "well you started it" Ha ha. You'd be half-right. Actually I point at the original article poster). Are ACLs easy in Linux? I've not seen a single distro that makes it so. Are they pervasive throughout the OS and apps? No. Can the average Linux user, with their window manager, or even at the prompt (AVERAGE user, I say) share one file with one friend, without sudo privileges? I don't know that it's even possible, but I'd love it if you could SHOW me that it's so easy your mom could do it.

"To sum it up, your blog entry is probably one of the dumbest things I have read recently. Not one good point, only senseless drivel."

You only addressed 4 or 5 of the many points I made. Are you out of breath? Some of your retorts have merit. The biggest one is the update process. But to make poor return arguments about window managers not updating the login menu properly, or ACLs that are inaccessible to most people, that just shows either your obsession of Linux or how clueless you are that not everyone knows the inner workings of Linux like you do, and to wrap up by saying my blog was "one of the dumbest things [you] have read recently" sounds pathetic.

Finally, to qualify what I mean by "the average user" or "most users". Have you ever walked into a regular business, or home, and seen the way most users work? They pull up Excel and know about 5 commands: open, save, bold, italics, and center. I'm exaggerating slightly here, but if you DON'T know what I'm talking about then you've been in your system admin den too long and need to get out into the light.

Thursday, September 08, 2005

Mono on embedded Linux

I am working with a TS-5500 embedded system for the first time, equipped with a PCMCIA slot, an Ethernet port, and a CompactFlash slot, which is used for a 64MB flash drive. We downloaded an image of embedded Linux on the card. This was part of a class to write an AI, self-navigating car in the C language to run on Linux. But being a .NET/Mono and C# fan myself, I wanted to try my hand at building Mono for embedded linux so I could write the AI code in C#.

The "hard drive" flash card only has 64MB, as I mentioned. The embedded linux system did not come with compiling tools either. So all development for this system had to be done in a development OS environment, which when chroot'd into allows one to build programs that can then be freely copied onto the embedded system that can run. Mono would have to be built on a traditional Linux system, inside that development OS area, and installed to some special location that I could then use to copy the installation directory to the embedded device. I crossed my fingers that it would be small enough to fit on the CF drive -- or I'd be buying a bigger one.

I downloaded the sources to Mono 1.1.8.3 and read the README file. I have built Mono several times before on other varieties of Linux, but because embedded linux has so many stripped out features, I was curious about the dependency list. I was pleased to see only two dependencies, both easy to get: glib and pkg-config. The other nice part about the dependencies, at least as it sounded from the README file, is that they were only required to build mono, not necessarily to run mono, which would help keep the size down on the embedded system. One other dependency was required for me, that wasn't listed, because the embedded system didn't have /dev/random, which was required to build
mono. I didn't know how to add /dev/random to this distribution of Linux, so instead I downloaded the alternative, egd. I'll discuss each of these dependencies and their gotchas next.

Each dependency's source code had to be downloaded as a tarball, moved into the development OS directory and extracted with tar xvzf some.tar.tgz, all from outside the development OS environment, meaning without chroot'ing to it. Then chroot'ing into the environment, I built each package with the standard ./configure && make install.

glib would not build because of a gettext dependency. After Googling on the text of the compile error, I found that gettext isn't actually required by glib any more, so its detection within its configure script could be safely disabled. Opening glib's configure script in vi, I searched for "You must have either have gettext support" (double "have" intended), and then modified the "if test" line above it from: if test "$gt_cv_have_gettext" != "yes" ; then to read: if test "$gt_cv_have_gettext" = "XXX" ; then, which essentially disabled the check. glib built and installed without any more trouble.

Once pkg-config and glib were installed, I found building mono still failed, due to /dev/random being missing. I had a /dev/random in my native Linux, which happens to be Gentoo, but inside my chroot'd environment from which I was building mono, no /dev/random was provided. After Googling on the compile error text, I found that last year the mono group wrote a random generator substitute that tied into egd, which I later found out was an entropy generator, good for random numbers. Google Linux helped me find the sources to egd. egd requires Perl, which fortunately
the embedded system already had. Although egd claims to not require an install, I did so anyway, which added SHA support to my system, which seemed to be necessary. Running egd.pl /tmp/entropy after that started the entropy-generating daemon, and set the socket to be at /tmp/entropy. Finally, an environment variable had to be set so that mono's install could find egd as the substitute random number generator: export MONO_EGD_SOCKET=/tmp/entropy.

Finally I could build mono. I set the prefix directory (the directory to install to) to /src/dest, which was empty at that point, so that I could copy /src/dest into / of the embedded system and effectively install mono. Time would tell if that worked.

./configure --prefix=/src/dest
make install

I ran into this error "error while loading shared libraries: libgthread-2.0.so.0" during build. The solution was to set the LD_LIBRARY_PATH environment variable so that the mono compiler could find its dependencies.

I also ran into this error "Unhandled Exception: System.Security.Cryptography.CryptographicException: Couldn't access random source.", which is how I found out that /dev/random is needed, and how I eventually came to the workaround discussed earlier in this blog.

Another error I ran into was that after chroot'ing into the development OS, the /proc filesystem was unreachable from the inside of the development OS. A /proc directory existed, but was empty. So solve this, I had to mount the /proc directory inside the development OS before chroot'ing into the it: sudo mount -t proc none /home/mech/ts5500devOS/proc

So after all these steps, make and make install succeeded. Hurray!

I quickly switched to /src/dest and ran a du -hcs to find out how much space mono wanted to take for install. 86MB. Rats. That's way too big. So I sorted through the directory, removing large libraries and executables from the mono installed directory that I didn't think I'd be using on the embedded system. I got it to 11MB, and my Hello World app still runs on it (though I removed the compiler).

All comments are welcome. Let me know if you found this useful!