Lazarus

Programming => Operating Systems => Windows => Topic started by: dbannon on February 02, 2018, 08:44:41 am

Title: [solved] load hunspell.dll
Post by: dbannon on February 02, 2018, 08:44:41 am
Hi Folks, needing to do some spell checking and going over the forum and looking at whats likely to be installed already, I choose hunspell. If nothing else, hunspell is used by LibreOffice and it would be nice to avoid further install demands. Several code snippets out there ...

I found getting it to work under Linux easy but cannot get the call to LoadLibrary() to work under Windows. Perhaps, as much as anything, because I don't know what the DLL is really called or where it is. The code snipits all seem to just say LoadLibrary(Pchar('hunspell.dll')) with no path (have they put the dll in working directory ?). I do not seem to have a dll by that name on my windows box and it does have a working hunspell spell checker in LibreOffice.

I can even find the LibreOffice's  dictionary files.

Can anyone suggest how I find (and load) the hunspell.dll ?

If you help me, I promise not to say anything nasty about windows for at least a week !

Davo 
Title: Re: load hunspell.dll
Post by: taazz on February 02, 2018, 08:47:24 am
hunspell is not part of windows you need to either find the dll (openoffice might install a version) or build it from source your self.after that placing the dll in the same directory as your exe should be enough to allow loadlibrary to work.
Title: Re: load hunspell.dll
Post by: Mike.Cornflake on February 02, 2018, 09:06:19 am
Just had a look in my LibreOffice portable apps folder - no sign of hunspell.dll.

Oh, there is a copy shipped with CuteMarkEdPortable though.  DependancyWalker lists two other DLLs (libgcc_s_dw2-1.dll, libstdc++-6.dll), both in the same folder.  Should be able to copy those three into the same folder as your exe...

https://portableapps.com/apps/office/cutemarked-portable
Title: Re: load hunspell.dll
Post by: rvk on February 02, 2018, 10:20:52 am
hunspell.dll is statically linked in Libre/Open-Office.

See the list at https://en.wikipedia.org/wiki/Hunspell

You could also try the downloads at http://www.crawler-lib.net/nhunspell
It has a Hunspellx86.dll and Hunspellx64.dll (besides the NHunspell.dll)

(There is also a wrapper for NHunspell for Delphi 2007+ floating around)
Title: Re: load hunspell.dll
Post by: dbannon on February 02, 2018, 11:53:20 am
Quote
hunspell.dll is statically linked in Libre/Open-Office.
OK, that explains why I cannot find it.

Thanks guys, unfortunately, neither suggestion worked, they do not load.

And, even more unfortunate, LoadLibrary() is in the RTL and apparently I cannot debug into there. So, I cannot find out even if its trying to open it and failing or still, somehow, not looking in the right place (I have tried an explicit path). Sigh...

Might try opening some other random dll. Obviously I won't get any further than opening it but thats further than I have got so far.....

I am quite sure I am not the first person to try and load a library into a Lazarus app......

D
Title: Re: load hunspell.dll
Post by: balazsszekely on February 02, 2018, 11:59:10 am
@dbannon
Put the dlls in the same directory with your exe.  To test if a particular library can be loaded:
Code: Pascal  [Select][+][-]
  1. procedure TForm1.Button1Click(Sender: TObject);
  2. var
  3.   LibHandle: TLibHandle;
  4. begin
  5.   LibHandle := LoadLibrary('FullPathToYourLibrary');
  6.   if LibHandle <> NilHandle then
  7.   begin
  8.      Showmessage('ok');
  9.   end;
  10. end;

If you know the name of the function, you can load it dynamically as well.
Title: Re: load hunspell.dll
Post by: rvk on February 02, 2018, 12:01:04 pm
Thanks guys, unfortunately, neither suggestion worked, they do not load.
Are you sure.
I just tried this and it worked for both hunspell and nhunspell DLLs.

Code: Pascal  [Select][+][-]
  1. procedure TForm1.Button1Click(Sender: TObject);
  2. var
  3.   Handle1: TLibHandle;
  4. begin
  5.  
  6.   Handle1 := LoadLibrary('Hunspellx86.dll');
  7.   if Handle <> 0 then
  8.   begin
  9.     Showmessage('Library Hunspellx86.dll loaded');
  10.     FreeLibrary(Handle1);
  11.   end
  12.   else
  13.     Showmessage('Error');
  14.  
  15.   Handle1 := LoadLibrary('NHunspell.dll');
  16.   if Handle <> 0 then
  17.   begin
  18.     Showmessage('Library NHunspell.dll loaded');
  19.     FreeLibrary(Handle1);
  20.   end
  21.   else
  22.     Showmessage('Error');
  23.  
  24. end;

Make sure you load the correct bitness dll. So in 32bit appllication load x86 and in 64bit application load x64.
Title: Re: load hunspell.dll
Post by: dbannon on February 02, 2018, 01:05:44 pm
OK, so I wrote  long answer saying Yes I'm sure and was about to post the code I am using. Then I realised it was in fact loading, since I started using the Hunspellx64.dll

Its just that the function then goes on to do things like

Code: Pascal  [Select][+][-]
  1. Hunspell_create := THunspell_create(GetProcAddress(HunLibHandle, 'Hunspell_create'));

And they are all failing, and the function returns false.

Thanks folks, tomorrow I'll try and workout why GetProcAddress() fails. Sort of sounds like those functions are not actually in the DLL. I think I'm going to learn a lot more about Windows DLL than I want to...

At least now I can LoadLibrary ....
Title: Re: load hunspell.dll
Post by: taazz on February 02, 2018, 01:25:11 pm
OK, so I wrote  long answer saying Yes I'm sure and was about to post the code I am using. Then I realised it was in fact loading, since I started using the Hunspellx64.dll

Its just that the function then goes on to do things like

Code: Pascal  [Select][+][-]
  1. Hunspell_create := THunspell_create(GetProcAddress(HunLibHandle, 'Hunspell_create'));

And they are all failing, and the function returns false.

Thanks folks, tomorrow I'll try and workout why GetProcAddress() fails. Sort of sounds like those functions are not actually in the DLL. I think I'm going to learn a lot more about Windows DLL than I want to...

At least now I can LoadLibrary ....
1)there are a number of open source projects that bind with (n)hunspell dlls for delphi already try using one of them as base.
2) use a dependency walker application for windows to see which functions are exported from your dll.
3) make sure that both libraries (linux/windows) use the same compiler (and version) to avoid problems with the exported names, c/C++ is notoriously problematic with name mangling.
Title: Re: load hunspell.dll
Post by: rvk on February 02, 2018, 01:35:04 pm
Thanks folks, tomorrow I'll try and workout why GetProcAddress() fails. Sort of sounds like those functions are not actually in the DLL. I think I'm going to learn a lot more about Windows DLL than I want to...
I see that the Hunspellx64.dll has different exported functions (see left on image).

I downloaded the thunspell.zip from Torry (THunSpell v.1.13 (https://torry.net/authorsmore.php?id=7163) which you can port B.T.W. or use as example).
That one does have hunspell_create (see right on image).

So you might need to compile the 64bit version yourself or find another one other than the nhunspell one, with the correct functions.
Title: Re: load hunspell.dll
Post by: dbannon on February 03, 2018, 12:56:55 pm
OK, just to be clear.

My problem is that I can get my hands on three different hunspell.dll files. The one from portable apps and the one from Torry are both 32 bit and unusable with my 64 bit FPC/Lazarus install on Windows. The 64bit one from NHunspell uses totally different names for functions (not just different capitalisation) - don't know why. Or how to use it.

To prove the above, I took my windows code back to my Linux box where I have some cross compilers setup, built a 32bit windows version of my test app, took the binary back to the Windows box and it did exactly what it should do there with the Torry dll (did not test the portableapps one).

The solution is that I need to build a 64bit windows DLL. And make it available too. I'll put all of this one a wiki page when I get it all working reliably on all three platforms.

Thanks folks for your help, this forum really is something special !

PS - DependancyWalker - the one I found is 12 years old and gave me all sorts of errors. Is that what people expect ? http://www.dependencywalker.com/

David
Title: Re: load hunspell.dll
Post by: taazz on February 03, 2018, 01:22:19 pm
PS - DependancyWalker - the one I found is 12 years old and gave me all sorts of errors. Is that what people expect ? http://www.dependencywalker.com/ (http://www.dependencywalker.com/)
that's the one I'm using with out any problems. There are 3 versions keep away of the ia64 there is no hardware to support it any longer. for optimal operation you need to much the bitness of the loaded executable to the bitness of the walker ee use the x86 to inspect 32bit applications and the x64 for 64bit. I never tested it under wine on linux though.
Title: Re: load hunspell.dll
Post by: wp on February 03, 2018, 01:33:21 pm
Looks that it does not work on Win10 where even the analysis of notepad.exe results in an eternal hourglass.
Title: Re: load hunspell.dll
Post by: taazz on February 03, 2018, 01:35:38 pm
Looks that it does not work on Win10 where even the analysis of notepad.exe results in an eternal hourglass.
here are a couple alternatives https://alternativeto.net/software/dependency-walker/ (https://alternativeto.net/software/dependency-walker/)  it lists the one rvk used on his screen shots too.
Title: Re: load hunspell.dll
Post by: dbannon on February 05, 2018, 07:23:02 am
Taazz said
Quote
keep away of the ia64 there is no hardware to support it
What ?  I can remember the HP rep assuring me the future of (my sort of) computing was definitely Itanium. Made me shut down my Alpha Cluster....

Jokes aside, Dependency Walker does work for me, shows me what I wanted to know such as names of methods. But it also shows a lot of errors, and, even with a 64bit DLL on a 64bit system and a 64bit Dependency Walker seems to want to link in a whole lot of 32bit libraries. And thats even if I look at an internal windows10 DLL.

But it did show me some of what I needed to know. Exception being whether the DLL is 32bit or 64bit. 

I'll have a look at suggested alternatives.

Thanks
Title: Re: load hunspell.dll
Post by: dbannon on February 09, 2018, 07:11:03 am
For benefit of anyone following ...
A 64bit version of Hunspell (specifically the DLL) can be built on Windows, you have to install MSYS2, thats easy (but a disk eater) and then build with gcc.

The resulting DLL seems to work OK when tested using hunspell's own command line tool and it loads fine when called with our FPC LoadLibrary() but crashes somewhere (on a malloc) internally when I call the hunspell_create() function.

Further research is indicated .....
Title: Re: load hunspell.dll
Post by: rvk on February 09, 2018, 11:40:08 am
Running the code below didn't crash for me. I didn't even need the .aff and .dic files yet.

I did need the libgcc_s_seh-1.dll, libstdc++-6.dll and libwinpthread-1.dll to do LoadLibrary but I didn't compile my libhunspell-1.3-0.dll with -static-libgcc and -static-libstdc++ yet.

Code: Pascal  [Select][+][-]
  1. uses Windows, dynlibs;
  2.  
  3. type
  4.   THunspell_create = function(const affpath, dpath: PAnsiChar): Pointer; cdecl;
  5.  
  6. var
  7.   Hunspell_create: THunspell_create;
  8.  
  9. procedure TForm1.Button1Click(Sender: TObject);
  10. const
  11.   LibName = 'libhunspell-1.3-0.dll';
  12. var
  13.   HunLibHandle: TLibHandle;
  14.   fHunHandle: Pointer;
  15.   Aff, Dic: TFileName;
  16. begin
  17.   HunLibHandle := LoadLibrary(LibName);
  18.   if HunLibHandle = dynlibs.NilHandle then
  19.   begin
  20.     ShowMessage('Error ' + GetLastError.ToString + ' ' + ExtractFilePath(Application.ExeName) + LibName);
  21.     exit;
  22.   end;
  23.  
  24.   Hunspell_create := THunspell_create(GetProcAddress(HunLibHandle, 'Hunspell_create'));
  25.   if not Assigned(Hunspell_create) then exit;
  26.  
  27.   Aff := ExtractFilePath(Application.ExeName) + 'en_US.aff';
  28.   Dic := ExtractFilePath(Application.ExeName) + 'en_US.dic';
  29.   fHunHandle := Hunspell_create(pointer(Aff), pointer(Dic));
  30.  
  31.   ShowMessage('success');
  32.  
  33.   FreeLibrary(HunLibHandle);
  34.  
  35. end;
Title: Re: load hunspell.dll
Post by: dbannon on February 10, 2018, 09:21:12 am
Thanks RVK, I just tried your specific code in case I had something in mine (which is basically the same) out of wack.

But it still segV when I try and execute the line -

fHunHandle := Hunspell_create(pointer(Aff), pointer(Dic));

Now, I note you are using v1.3  of Hunspell, I have v1.6. Are you using one built some time ago ?  Should I go back and get an earlier release and try that ?

I also notice you don't set a path to the library, is that because its properly installed on your system ?  Mine is not, I've just compiled it and copied it out of the MSYS2 tree into my lazaras project working directory (along with the other dependancy DLLs). Perhaps thats not a viable way to work ?

(I did warn you I don't 'do' Windows ...)

David
Title: Re: load hunspell.dll
Post by: taazz on February 10, 2018, 10:25:26 am
I also notice you don't set a path to the library, is that because its properly installed on your system ? 
On windows the loader will search for the dll specified, on the application's directory, the system folders (system/system32/syswow64) and in the system wide path. In that order you only need to place the dll in one of those locations and it will be loaded.
Title: Re: load hunspell.dll
Post by: dbannon on February 10, 2018, 11:17:11 am
Thanks taazz, the DLL is being loaded OK. I have it in the (lazarus's) app worKing directory and set an explicit path to it. There is a three plus set process -

But I am worried that Windows does not like me calling a function in a DLL thats not "registered" - is that the word ?

I'm reluctant to go ahead and run the hunspell installer as I'm not sure I can reverse the install and its my only windows test platform. Might have to bite the bullet....
Title: Re: load hunspell.dll
Post by: taazz on February 10, 2018, 11:22:51 am
But I am worried that Windows does not like me calling a function in a DLL thats not "registered" - is that the word ?
only for activeX dlls/controls for simple dlls it should work.
I'm reluctant to go ahead and run the hunspell installer as I'm not sure I can reverse the install and its my only windows test platform. Might have to bite the bullet....
use a virtual machine with windows installed you can froze it to a specific point in time and undo everything when the vm shuts down or backup the files to an external drive and replace them when you need to undo the changes.
Title: Re: load hunspell.dll
Post by: rvk on February 10, 2018, 12:37:29 pm
I'm reluctant to go ahead and run the hunspell installer as I'm not sure I can reverse the install and its my only windows test platform. Might have to bite the bullet....
What is the hunspell installer?
In the steps for Linux (https://github.com/hunspell/hunspell), which is the second part you follow after installing msys2 and it's dependencies, you would run make install. But I don't think that installs anything in Windows. Just in the virtual environment of msys2.

I did build 1.3.0 because that was the one supplied with THunspell and it had a direct download link.
I'm now building the latest "-master" release..... building... building... done.

Just tried the resulting libhunspell-1.6-0.dll with the code snippet I gave earlier and I get "success" too.

What were the exact steps you took?
I did:
-----
Download and run Msys2

$ pacman -S base-devel mingw-w64-x86_64-toolchain mingw-w64-x86_64-libtool mingw-w64-x86_64-boost
$ exit

in Windows extract hunspell-master (https://github.com/hunspell/hunspell/archive/master.zip) to your home directory of Msys2. For me that was D:\msys64\home\Rik
Run Msys2 again (you are already in your home directory, see ls -ltr)

$ cd hunspell-master
$ autoreconf -vfi
$ ./configure
$ make
$ exit

Now libhunspell-1.6-0.dll is created in D:\msys64\home\Rik\hunspell-master\src\hunspell\.libs
-----
Now put the libhunspell-1.6-0.dll in your program directory and the call to Hunspell_create() should not crash.
I had to copy libgcc_s_seh-1.dll, libstdc++-6.dll and libwinpthread-1.dll too because I didn't statically link them but that should be possible to do.

(My example with dlls is temporarily on my dropbox (https://www.dropbox.com/sh/qj4bkxqynexcjfb/AACU7SQJcEjebhbNjL3crATAa?dl=0) for the moment. You can use the download button at the top right if you want it.)

Title: Re: load hunspell.dll
Post by: dbannon on February 10, 2018, 11:43:02 pm
Thanks rvk, very grateful for you help here. But its getting a bit silly isn't it ?

Only thing I did differently during the MSYS2 stage was install gcc in MSYS2. It was installed during the MSYS2 install and that did surprise me a bit.

I assume your build of MSYS2 did also not get you a working gcc ?  Do you have gcc or some other c++ compiler already on the windows box ?  That could be an explanation for my problem ?
I'll grab your tarball rvk and see that that reveals.

No, I have not run make install at the end of the hunspell build process. My code is almost identical to yours now, except where I set a path to load the dll from.

taazz, I don't use windows in a vm because of licensing issues, I don't understand how I can get it to work without purchasing a license for that VM and then a whole lot of fuss every time I want to bring up a new VM.  I routinely use VMs for testing my app on various Linux distros ...
Title: Re: load hunspell.dll
Post by: dbannon on February 11, 2018, 12:24:40 am
OK, can confirm that rvk's kit works perfectly.

If I plug in the hunspell and friends dlls that I made it all falls apart. Its a MSYS2 / hunspell  issue of some sort.

Could be related to fact that my install of MSYS2 did not arrive with a compiler ? Maybe I need to repeat that process.....

I also note that my dlls have names like -
Code: Pascal  [Select][+][-]
  1.  msys-hunspell-1.6-0.dll
and rvk's are like -
Code: Pascal  [Select][+][-]
  1. libhunspell-1.6-0.dll

So there might be a clue there perhaps ?  Clearly been build differently. Anyway, rvk, if you could, I'd like to know what c++ compiler you use when you run 'make' please ?

David
Title: Re: load hunspell.dll
Post by: rvk on February 11, 2018, 12:28:33 am
I assume your build of MSYS2 did also not get you a working gcc ?  Do you have gcc or some other c++ compiler already on the windows box ?  That could be an explanation for my problem ?
No, I don't have gcc installed on Windows. As I understand it msys2 is a complete separate virtual Linux/Cygwin environment so nothing is taken from Windows.

Gcc is also not installed as default in msys2 but the first command (that pacman line) installs base-devel and I think that includes the gcc compiler. I did need to exit msys2 and enter it again before I could run gcc.

Setting the path in Windows is not needed if you include the dlls in the .exe directory.

So the steps I gave should also work on a complete clean Windows installation to compile hunspell. And the gcc compiler came from the pacman base-devel line.

From where did you copy your compiler hunspell.dll? (From what directory in your msys2 install?)
Title: Re: load hunspell.dll
Post by: dbannon on February 12, 2018, 09:29:09 am
OK, so further testing proves everything works as I expected it would if I use the DLLs made by RVK.  Its quite clear that I did something different when I built mine and thats not a Lazarus issue, its purely a MSYS2 / Hunspell / Me issue and I'll sort it out at some stage.

RVK, to save me some immediate time, do you mind if I continue to use the DLLs you made for my (completely open source) project, tomboy-ng Notes ? https://github.com/tomboy-notes/tomboy-ng ?  Credit to "rvk on Lazarus Forum" ?

In either case, much thanks for your help !

I will mark this issue as solved....
Title: Re: load hunspell.dll
Post by: rvk on February 12, 2018, 09:55:22 am
No problem. You might want to include a libhunspell.txt file with the license or link to hunspell itself.

Something like:
Quote
libhunspell.dll was compiled with MSYS2 from the original source from
https://github.com/hunspell/hunspell

You must distribute this file with your program.

As it is now the dll also needs libstdc++-6.dll and other files. I've read that if I compile with -static-libgcc and -static-libstdc++ they would be statically linked in (so you wouldn't need them separately). I'll try that later today (or when I get some extra time).
Title: Re: load hunspell.dll
Post by: rvk on February 12, 2018, 12:48:24 pm
David, I couldn't easily statically link in the g++ libraries in MSYS2/MinGW64.
The whole configure/Makefiles were focused on linking them shared.

I finally took Visual Studio and set it to cross-compile to x64 with release_dll and it gave me a new (64 bit) libhunspell.dll of just 479KB. I tried to use it with LoadLibrary and Hunspell_create() and that worked without error (AND without dependencies).

Can you check if that libhunspell.dll does what it needs to do?
(temp directory on dropbox (https://www.dropbox.com/sh/qj4bkxqynexcjfb/AACU7SQJcEjebhbNjL3crATAa?dl=0))

Quote
libhunspell.dll was compiled from the original source from
https://github.com/hunspell/hunspell
with Microsoft Visual Studio Community 2015.

You must distribute this file with your program.
Title: Re: load hunspell.dll
Post by: dbannon on February 13, 2018, 01:08:37 pm
Right rvk, indeed that works !

And I thank you in no uncertain terms !

Now, here is what I will do, please yell and shout if it does not sound right. Two parts.

I'll bundle the DLL and license file into the zip file when I release my next Windows binary,  I will also put the DLL and license file in a special one off zip file  for just that release of tomboy-ng.

I'll write up what I have learnt about hunspell onto a Lazarus Wiki page (done a few of those already). It will cover Linux, Windows and Mac and I'll have a link from there back to the zip file (on github) with just the hunspell stuff.   

I'll also mention "rvk from Lazarus Forum" (?) in the license file. I'll call it license_hunspell.txt

I can see that DLL being used by other projects, I'm happy with that, assume you are too ?

Does that all sound OK ?

David
Title: Re: load hunspell.dll
Post by: rvk on February 13, 2018, 01:22:31 pm
Does that all sound OK ?
Hi David,

Yes, that all sound fine.

As extra you might want to mention, in the license file, that is is the 64-bit version of libhunspell.dll. People searching for it will see right away it's that version.
Title: Re: load hunspell.dll
Post by: dbannon on February 20, 2018, 11:55:58 am
For anyone watching with great excitement (or any excitement at all), I've made a reasonable start on that wiki page about using hunspell from a Lazarus App.

http://wiki.freepascal.org/spelling

If no one objects in the next day I'll find somewhere to link it from ...

Many thanks for the help I've had on this topic. I'll mark it solved.

David
Title: Re: [solved] load hunspell.dll
Post by: Thaddy on February 20, 2018, 12:05:10 pm
A couple of remarks:
- Hunspell does not rely on Lazarus
- None of the examples in the wiki are compileable without some knowledge about creating a project that can actually compile your code.
- Try to add a simple command-line project or a full, small, Lazarus project to remedy that.

Examples should compile. Yours do, but not for a beginner or intermediate. You have to guess a lot.

Keep up the good work, though.
Title: Re: [solved] load hunspell.dll
Post by: dbannon on February 20, 2018, 12:23:52 pm
Yes Thaddy, I am aware of what you are saying. My original idea was to post a zip file of my test project, I think that would be a useful thing. But the wiki does not allow me to do that and I'm uncomfortable putting something on my private website that others may run, it (my website) is not IMHO sufficiently secure.

Posting pas, lfm and lpi files as text to be screen scrapped is just not on. I considered a command line demo, but my rough draft rapidly became far too long and was quite unrealistic, anyone using this unit will almost certainly be using a GUI ...

So, best of a bad set I'm afraid.

Quote
- Hunspell does not rely on Lazarus

Not sure what you mean by that. Its true but what do you mean in this context ?

But thanks for your comments and encouragement. I very much doubt I'll ever be a good enough coder to contribute much that way, but I can write docs and do think Lazarus needs better docs !

David
Title: Re: [solved] load hunspell.dll
Post by: rvk on February 20, 2018, 12:27:43 pm
Yes, a complete example (including binary for 32 and 64 bit) with installable package and example would be best.
Something like this: https://github.com/cutec-chris/hunspell

It could be hosted as separate project at https://github.com/tomboy-notes/lazhunspell or something.

As you can see from other wiki pages, there is always a download section. Pointing it to a github repository is easiest. The user can download it, install the .lpk package and try the example. In that you don't need to add complete sources on the wiki-page. Just the parts you want to explain.

Also, if tomboy-ng is for 32 and 64 bit compilation, maybe renaming libhunspell.dll to libhunspell32.dll and libhunspell64.dll would be easier. Inexperienced users don't need to worry because the source will either load the 32.dll or the 64.dll. Otherwise they need to rename and delete one.
Title: Re: [solved] load hunspell.dll
Post by: dbannon on February 21, 2018, 12:56:59 am
Hmm, wish I had found Chris's site before I put all that work in.

Its not mentioned anywhere on the Wiki, the forum has a couple of mentions to the cutec-chris but not hunspell project therein. Overall, cutec-chris seems a huge collection of forks, most way out of date ??

On the other hand, Chris's code, or who ever's code it is, has the usual "not one word of documentation". Not even a README.md.  I really value good example code but its not enough !   

<rant>
I get really frustrated to see so much effort put in (in particular into Lazarus) by such skilled people and but they cannot take the time to add a single line that says what a function does. Look at LazUFT8, LazFileUtils for example. I though the blue CRT gun had failed on my screen, then remembered I was using a LED flat screen .....
</rant>

I'll link the wiki spelling page to Chris's site, if people want a shrink wrapped demo, they can take his, if they want to understand the bigger picture, they can read mine !

David
TinyPortal © 2005-2018