<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-1592996183125788768</id><updated>2011-08-18T12:45:55.117+01:00</updated><category term='C#'/><category term='Python'/><category term='Scala'/><category term='proxy'/><category term='tools'/><category term='fault'/><category term='Ruby'/><category term='erlang'/><category term='FTP'/><category term='PalmOS'/><category term='C/C++'/><category term='.Net'/><category term='F#'/><category term='mono'/><category term='FOX'/><category term='JavaScript'/><category term='CTClib'/><category term='Java'/><category term='concurrency'/><category term='crypto'/><category term='Silverlight'/><category term='web design'/><category term='GUI'/><category term='PLua'/><title type='text'>Tinesware</title><subtitle type='html'>My hobbyist coding updates and releases as the mysterious "Mr. Tines"</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default?start-index=101&amp;max-results=100'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>154</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-7296721843601750688</id><published>2010-11-19T20:31:00.003Z</published><updated>2010-11-19T20:48:50.777Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='tools'/><category scheme='http://www.blogger.com/atom/ns#' term='F#'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>Infrastructure Helper 1.3</title><content type='html'>&lt;p&gt;&lt;a href="http://code.google.com/p/tinesware-tools/downloads/detail?name=Infrastructure.1.3.7z"&gt;Mainly a bug-fix release&lt;/a&gt;.  The &lt;a href="http://code.google.com/p/tinesware-tools/issues/detail?id=11"&gt;big bug&lt;/a&gt; being that the release distro contained the release build of &lt;code&gt;Tinesware.Recorder.dll&lt;/code&gt; which had in-lined the use of coverage file location property so that the overwrite of the value was having no effect.&lt;/p&gt;

&lt;p&gt;Also fixed &lt;a href="http://code.google.com/p/tinesware-tools/issues/detail?id=9"&gt;the usage output&lt;/a&gt;, and &lt;a href="http://code.google.com/p/tinesware-tools/issues/detail?id=8"&gt;the case of a read-only strong-naming key&lt;/a&gt;, and.&lt;/p&gt;

&lt;p&gt;The new release also includes some more interoperation with PartCover (using resource &lt;a href="http://code.google.com/p/tinesware-tools/source/browse/trunk/Infrastructure/Scripts/Partcover.xsl"&gt;/trunk/Infrastructure/Scripts/Partcover.xsl&lt;/a&gt; to perform the conversion to NCover style output), and an FxCop rule to enforce proper re-throwing of exceptions (so as not to lose stack information); and &lt;a href="http://code.google.com/p/tinesware-tools/issues/detail?id=6"&gt;adds exemption reasons to the XSLT generated reports&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;PartCover work is still on-going.  It is working albeit with some inefficiencies  with determining which overloaded method coverage refers to when overloads are encountered, but the big bug was the reason for a new release.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-7296721843601750688?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/7296721843601750688/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=7296721843601750688' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/7296721843601750688'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/7296721843601750688'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2010/11/infrastructure-helper-13.html' title='Infrastructure Helper 1.3'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-7733596008136223224</id><published>2010-06-06T11:23:00.004+01:00</published><updated>2010-07-13T22:17:02.135+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='tools'/><category scheme='http://www.blogger.com/atom/ns#' term='F#'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>Infrastructure Helper 1.2</title><content type='html'>&lt;p&gt;The &lt;a href="http://code.google.com/p/tinesware-tools/downloads/detail?name=Infrastructure.1.2.7z&amp;can=2&amp;q="&gt;new release&lt;/a&gt; includes&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A coverage tool that works by rewriting IL (so should be compatible with both the 2.0 and 4.0 .net runtimes) based on a &lt;a href="http://code.google.com/p/dot-net-coverage/"&gt;slightly less feature-rich C# tool&lt;/a&gt;; this is not nCover, though it generates compatible output, and so I've not attempted to make it look like nCover at the command line.&lt;/li&gt;
&lt;li&gt;A class-level coverage exemption attribute to allow exemptions to be applied to functions that cannot just be attributed as they stand -- a typical case would be a function that is defined as the first or last expression inside an F# function, and any funs it contains; and also preventing the coverage reporting from throwing when encountering these functions.&lt;/li&gt;
&lt;li&gt;At the source code level, now uses F# 2.0.0.0 for .net 3.5; and allows tool locations to be specified via environment variables.&lt;/li&gt;
&lt;li&gt;Includes Mono.Cecil 0.9.2, Mono.Options, and some other Mono tools for use in the coverage instrumentation.&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;The new coverage tool excludes all compiler-generated lines, so may report slightly different results to the old nCover 1.5.8; however the differences will be minor where they exist.&lt;/p&gt;
&lt;p&gt;Looking ahead, I see the following likely features for the post 1.2 development:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;extended XSLT to collate reports of the reasons given for code being excluded&lt;/li&gt;
&lt;li&gt;an nCoverExplorer like tool that distinguishes exemptions from coverage&lt;/li&gt;
&lt;li&gt;More code comments -- maybe even proper help files&lt;/li&gt;
&lt;li&gt;And of course bug fixing and refactoring as needed.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;PartCover/ nCover 1.5.8 support is dead; but if I ever find a FOSS .net 4 coverage tool that uses the profiling APIs I would consider using that.&lt;/p&gt;
&lt;p&gt;&lt;span style="font-weight:bold;"&gt;Later (13-Jul-10) :&lt;/span&gt; the current updated version of PartCover does meet this criterion, so I shall be working to use its output format as well.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-7733596008136223224?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/7733596008136223224/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=7733596008136223224' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/7733596008136223224'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/7733596008136223224'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2010/06/infrastructure-helper-12.html' title='Infrastructure Helper 1.2'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-2845198669845791767</id><published>2010-03-28T23:19:00.001+01:00</published><updated>2010-03-28T23:22:52.004+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>EasyWPF 0.3.93</title><content type='html'>&lt;p&gt;Having been made aware of the Python &lt;a href="http://easygui.sourceforge.net/"&gt;EasyGui&lt;/a&gt; project while reviewing "&lt;cite&gt;&lt;a href="http://www.amazon.co.uk/Hello-World-Computer-Programming-Beginners/dp/1933988495/"&gt;Hello World&lt;/a&gt;&lt;/cite&gt;", I thought that maybe there should be an equivalent version for IronPython.  So here is one (with just a minimal amount of fudging in places):&lt;/p&gt;
&lt;pre&gt;"""
@version: 0.3.93(2010-Mar-28)
@note:
EasyGui provides an easy-to-use interface for simple GUI interaction
with a user.  It does not require the programmer to know anything about
tkinter, frames, widgets, callbacks or lambda.  All GUI interactions are
invoked by simple function calls that return results.

EasyWPF is an API-compatible implementation for IronPython written against 
the Windows Presentation Foundation

Note that EasyWPF requires .net release 3.0 or greater.

EasyGui (http://easygui.sourceforge.net)
is licensed under the Creative Commons Attribution 2.0 License
http://creativecommons.org/licenses/by/2.0/

You are free to copy, distribute, and display the work, and to make derivative 
works (including translations). If you do, you must give the original author 
(http://easygui.sourceforge.net) credit. The author specifically permits (and 
encourages) teachers to post, reproduce, and distribute some or all of this 
material for use in their classes or by their students.

EasyWPF is licensed under the same terms.
"""
egversion = __doc__.split()[1]

__all__ = ['ynbox'
    , 'ccbox'
    , 'boolbox'
    , 'indexbox'
    , 'msgbox'
    , 'buttonbox'
    , 'integerbox'
    , 'multenterbox'
    , 'enterbox'
    , 'exceptionbox'
    , 'choicebox'
    , 'codebox'
    , 'textbox'
    , 'diropenbox'
    , 'fileopenbox'
    , 'filesavebox'
    , 'passwordbox'
    , 'multpasswordbox'
    , 'multchoicebox'
    , 'abouteasywpf'
    , 'egversion'
    , 'egdemo'
    , 'EgStore'
    ]

import sys
#-------------------------------------------------------------------
# Console I/O helpers
#-------------------------------------------------------------------

def write(*args):
    args = [str(arg) for arg in args]
    args = " ".join(args)
    sys.stdout.write(args)
    
def writeln(*args):
    write(*args)
    sys.stdout.write("\n")
    
def dq(s):
    return '"%s"' % s

#--------------------------------------------------
# check python version and take appropriate action
#--------------------------------------------------

if not ('subversion' in dir(sys)) or not (sys.subversion[0] == 'IronPython'):
    stars = "*"*75  
    writeln("""\n\n\n""" + stars + """
You are running Python version: 
\t\t"""+sys.version+"""
You must be using IronPython 2.6 or later to use EasyWPF.
Terminating.
""" + stars + """\n\n\n""")
    sys.exit(0)  

import os, pickle,traceback

import  clr
clr.AddReference('PresentationFramework')
clr.AddReference('PresentationCore')
clr.AddReference('System.Xml')
clr.AddReference('System.Windows.Forms')
from System import String, StringComparison, Console, Environment, Uri, Char
from System.Xml import XmlTextReader
from System.IO import StringReader, StringWriter, FileInfo, Path, BinaryReader, File
import System.Windows
from System.Windows import Thickness
from System.Windows.Markup import XamlReader
import System.Windows.Forms
import System.Windows.Controls
from Microsoft.Win32 import OpenFileDialog, SaveFileDialog
from System.Windows.Media import FontFamily

Vista = Environment.OSVersion.Version.Major &amp;gt;= 6

#-------------------------------------------------------------------
# Vista glass support
#-------------------------------------------------------------------

if Vista:
  from ctypes import *
  from System.Windows.Interop import WindowInteropHelper, HwndSource
  from System import IntPtr
  from System.Windows.Media import Brushes, Colors, Brush
  dwmapi = windll.dwmapi
  class __MARGINS(Structure):
    _fields_ = [("Left", c_int), ("Right", c_int), ("Top", c_int), ("Bottom", c_int)]
    
  def ExtendGlassFrame(window, margin):
    val = c_int()
    result = dwmapi.DwmIsCompositionEnabled(byref(val))
    success = result == 0 and not (val == 0)
    if not success:
      return False
      
    hwnd = WindowInteropHelper(window).Handle
    if (hwnd == IntPtr.Zero):
      return False
        
    window.Background = Brushes.Transparent
    HwndSource.FromHwnd(hwnd).CompositionTarget.BackgroundColor = Colors.Transparent
    margins = __MARGINS(int(margin.Left), 
          int(margin.Right), 
          int(margin.Top), 
          int(margin.Bottom))

    result = dwmapi.DwmExtendFrameIntoClientArea(hwnd, byref(margins))
    return result == 0
    
  class __GlassHelper:
    def __init__(self, w, t):
      self.window = w
      self.thickness = t
    
    def ApplyGlass(self):
      val = ExtendGlassFrame(self.window, self.thickness)
      hwnd = WindowInteropHelper(self.window).Handle
      HwndSource.FromHwnd(hwnd).AddHook(self.__wndproc)
    
    def __wndproc(self, hwnd, msg, wparam, lparam, handled):
      if msg == 0x031E: ## composition changed
        handled.Value = True ## set ref argument
        ExtendGlassFrame(self.window, self.thickness)
      return IntPtr.Zero
else:
  from System.Windows import SystemColors
    
#-------------------------------------------------------------------
# Xaml support
#-------------------------------------------------------------------

def LoadXaml(xaml):
    sr = StringReader(xaml)
    xr = XmlTextReader(sr)
    return XamlReader.Load(xr)
    
from System.IO.Compression import GZipStream, CompressionMode
from System.IO import MemoryStream
from System.Windows.Media.Imaging import BitmapImage, BitmapFrame

#-------------------------------------------------------------------
# unpacking my icon from the string
#-------------------------------------------------------------------

__icon64 = ('H4sIAAAAAAAEAO29B2AcSZYlJi9tynt/SvVK1+B0oQiAYBMk2JBAEOzBiM3mkuwdaUcjKasqgcplVmV'+
'dZhZAzO2dvPfee++999577733ujudTif33/8/XGZkAWz2zkrayZ4hgKrIHz9+fB8/In6NX+PX/DV+rV/'+
'jN/vNfrNf49f4NX6dX2Pr1/w1fo1fSL/xn7/Gb/drzH/tX+PXeEGfbeEz+n/6a6D9r4Mvf40/6Ndwz2+'+
'm/5fnD5Iv/6A/SH7gP3xE//xD/9A/RJ/83/y/X+P//r/lB/7DR/i78wi831R+oR//E/9NP39T+YB//k+'+
'/xm/6P/2m/xMA0b//EwPiFvLz/8Z7/ze+05//94bn1/jAZ+vX8On028mHv/ZA4/d+/q//6//6P//P//P'+
'GZvT8l//lf/k3/81/89/wN/wNf9af9Wf9jX/j3/gX/oV/If0in/wD/8A/8C/8C//CP/lP/pP/+D/+j//'+
'L//K/bF/57/67/+7P+/P+vD/uj/vj/ol/4p/4a//av/YP/8P/8D/jz/gz/qQ/6U/6Y/6YP4Y+/Fv+lr+'+
'Feve7+J/+p//pT/1T/9Q/5A/5Q/6UP+VP+cv/8r/8L/vL/rL/+D/+j/+Zf+afoV/+yr/yr6RX/uf/+X/'+
'22//z//w/T/3+w//wP/wP/oP/4L/2r/1rf/Ff/BdT+z/9T//T/9a/9W/9q/6qv+rv+Xv+nn/v3/v3/rf'+
'/7X+z7QnUv/qv/qv0yy/7Zb+MkL++vv7P//P//Jf8kl/yl/6lf+l//V//1//Vf/Vf/Uv/0r/0b//b/7Z'+
'gRf/+U//UP/Xv//v/vrz7B//BfzDBpzaE/F/xV/wV8uEv/+W//G/6m/4mIgK9Tu2JJoS59EgjpSHQh3/'+
'H3/F3/NP/9D9tcaCWf9ff9XcRYvT7v/vv/rvU46/8lb9ScCPq0S/0yb/z7/w7tv3QQyT9V/6Vf4V++Tf'+
'+jX+D8Pwf/of/YXN7mhciEf3yH/6H/+Ef+Uf+kX/1X/1Xb2hMiP11f91fRzS0n/yv/+v/Otz81/hf/pf'+
'/hUbUmZ0f8gNJ+xchbSQbf9Cvr/+n3/8k0jW/3c6v8Wv8Bge/xq/xR/8ev8av8at+F/n///wf0v//b3k'+
'PUvr/APisn8C2BAAA')
from System import Convert

__icoFile = Path.ChangeExtension(Path.GetTempFileName(), '.ico')
__iconzip = Convert.FromBase64String(__icon64)

def __GetIcon():
  mem = MemoryStream(__iconzip)
  unzip = GZipStream(mem, CompressionMode.Decompress, False)
  image = BitmapImage()
  image.BeginInit()
  image.StreamSource = unzip
  image.EndInit()
  return image

def __WriteIcon():
  mem = MemoryStream(__iconzip)
  unzip = GZipStream(mem, CompressionMode.Decompress, False)
  reader = BinaryReader(unzip)
  data = reader.ReadBytes(1206)
  reader.Close()
  unzip.Close()
  File.WriteAllBytes(__icoFile, data)

__WriteIcon()

#-------------------------------------------------------------------
# Making minimal hand-drawn title bar
#-------------------------------------------------------------------

def __FurnishWindow(title):
    boxRoot.Title = title
    boxRoot.FindName('title').Content = title
    boxRoot.FindName('icon').Source = __GetIcon()
    
    uri = Uri(__icoFile)
    boxRoot.Icon = BitmapFrame.Create(uri)
    
    boxRoot.MouseLeftButtonDown  += lambda s,e : boxRoot.DragMove()
      
    if Vista:
      boxRoot.AllowsTransparency = False
      boxRoot.Background = Brushes.Transparent
      boxRoot.FindName('title').Background = Brushes.Transparent
      thickness = Thickness(0.0, float(boxRoot.FindName('title').Height), 0.0, 0.0)
      helper = __GlassHelper(boxRoot, thickness)
      boxRoot.SourceInitialized += lambda s,e : helper.ApplyGlass()
    else:
      def activeColor():
        boxRoot.FindName('title').Background = SystemColors.ActiveBorderBrush  
        boxRoot.FindName('banner').Background = SystemColors.ActiveBorderBrush  
        boxRoot.FindName('title').Foreground = SystemColors.ActiveCaptionTextBrush
      def inactiveColor():
        boxRoot.FindName('title').Background = SystemColors.InactiveBorderBrush  
        boxRoot.FindName('banner').Background = SystemColors.InactiveBorderBrush  
        boxRoot.FindName('title').Foreground = SystemColors.InactiveCaptionTextBrush
      
      boxRoot.Activated += lambda s,e: activeColor()
      boxRoot.Deactivated += lambda s,e: inactiveColor()
      activeColor()    

#-------------------------------------------------------------------
# repeated code in EasyGUI -- for compatibility, still limit to GIF support
#-------------------------------------------------------------------

def __ValidateImage(image, msg):
    if image:
        image = os.path.normpath(image)
        junk,ext = os.path.splitext(image)
        if ext.lower() == ".gif":
            if os.path.exists(image):
                pass
            else:
                msg += ImageErrorMsg % (image, "Image file not found.")
                image = None
        else:
            msg += ImageErrorMsg % (image, "Image file is not a .gif file.")
            image = None
    return (image, msg)


# Initialize some global variables that will be reset later
__choiceboxMultipleSelect = None
__widgetTexts = None
__replyButtonText = None
__choiceboxResults = None
__firstWidget = None
__enterboxText = None
__enterboxDefaultText=""
__multenterboxText = ""
choiceboxChoices = None
choiceboxWidget = None
entryWidget = None
boxRoot = None
ImageErrorMsg = (
    "\n\n---------------------------------------------\n"
    "Error: %s\n%s")
root = None    
#-------------------------------------------------------------------
# various boxes built on top of the basic buttonbox
#-----------------------------------------------------------------------

#-----------------------------------------------------------------------
# ynbox
#-----------------------------------------------------------------------
def ynbox(msg="Shall I continue?"
    , title=" "
    , choices=("Yes", "No")
    , image=None
    ):
    """
    Display a msgbox with choices of Yes and No.

    The default is "Yes".

    The returned value is calculated this way::
        if the first choice ("Yes") is chosen, or if the dialog is cancelled:
            return 1
        else:
            return 0

    If invoked without a msg argument, displays a generic request for a confirmation
    that the user wishes to continue.  So it can be used this way::
        if ynbox(): pass # continue
        else: sys.exit(0)  # exit the program

    @arg msg: the msg to be displayed.
    @arg title: the window title
    @arg choices: a list or tuple of the choices to be displayed
    """
    return boolbox(msg, title, choices, image=image)


#-----------------------------------------------------------------------
# ccbox
#-----------------------------------------------------------------------
def ccbox(msg="Shall I continue?"
    , title=" "
    , choices=("Continue", "Cancel")
    , image=None
    ):
    """
    Display a msgbox with choices of Continue and Cancel.

    The default is "Continue".

    The returned value is calculated this way::
        if the first choice ("Continue") is chosen, or if the dialog is cancelled:
            return 1
        else:
            return 0

    If invoked without a msg argument, displays a generic request for a confirmation
    that the user wishes to continue.  So it can be used this way::

        if ccbox():
            pass # continue
        else:
            sys.exit(0)  # exit the program

    @arg msg: the msg to be displayed.
    @arg title: the window title
    @arg choices: a list or tuple of the choices to be displayed
    """
    return boolbox(msg, title, choices, image=image)


#-----------------------------------------------------------------------
# boolbox
#-----------------------------------------------------------------------
def boolbox(msg="Shall I continue?"
    , title=" "
    , choices=("Yes","No")
    , image=None
    ):
    """
    Display a boolean msgbox.

    The default is the first choice.

    The returned value is calculated this way::
        if the first choice is chosen, or if the dialog is cancelled:
            returns 1
        else:
            returns 0
    """
    reply = buttonbox(msg=msg, choices=choices, title=title, image=image)
    if reply == choices[0]: return 1
    else: return 0


#-----------------------------------------------------------------------
# indexbox
#-----------------------------------------------------------------------
def indexbox(msg="Shall I continue?"
    , title=" "
    , choices=("Yes","No")
    , image=None
    ):
    """
    Display a buttonbox with the specified choices.
    Return the index of the choice selected.
    """
    reply = buttonbox(msg=msg, choices=choices, title=title, image=image)
    index = -1
    for choice in choices:
        index = index + 1
        if reply == choice: return index
    raise AssertionError(
        "There is a program logic error in the EasyGui code for indexbox.")


#-----------------------------------------------------------------------
# msgbox
#-----------------------------------------------------------------------
def msgbox(msg="(Your message goes here)", title=" ", ok_button="OK",image=None,root=None):
    """
    Display a messagebox
    """
    if type(ok_button) != type("OK"):
        raise AssertionError("The 'ok_button' argument to msgbox must be a string.")

    return buttonbox(msg=msg, title=title, choices=[ok_button], image=image,root=root)

#-------------------------------------------------------------------
# buttonbox
#-------------------------------------------------------------------
def buttonbox(msg="",title=" "
    ,choices=("Button1", "Button2", "Button3")
    , image=None
    , root=None
    ):
    """
    Display a msg, a title, and a set of buttons.
    The buttons are defined by the members of the choices list.
    Return the text of the button that the user selected.

    @arg msg: the msg to be displayed.
    @arg title: the window title
    @arg choices: a list or tuple of the choices to be displayed
    """
    global boxRoot, __replyButtonText, __widgetTexts, buttonsFrame
    
    # Initialize __replyButtonText to the first choice.
    # This is what will be used if the window is closed by the close button.
    __replyButtonText = choices[0]
    
    xaml = '''&amp;lt;Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window2" SizeToContent="WidthAndHeight" MinWidth="400" MinHeight="150"  WindowStyle="None" AllowsTransparency="True"&amp;gt;
    &amp;lt;Grid&amp;gt;
        &amp;lt;Grid Name="banner"&amp;gt;
            &amp;lt;Image Name="icon" Stretch="Fill" HorizontalAlignment="Left" VerticalAlignment="Top" Width="20" Height="20" Margin="4,4,0,0" /&amp;gt;
            &amp;lt;Label Height="28" Name="title" VerticalAlignment="Top" Background="WhiteSmoke" Margin="28,0,0,0"&amp;gt;Label&amp;lt;/Label&amp;gt;
        &amp;lt;/Grid&amp;gt;
        &amp;lt;DockPanel Margin="0,28,0,0" Name="backdrop"&amp;gt;
          &amp;lt;StackPanel Name="buttons" DockPanel.Dock="Bottom" HorizontalAlignment="Center" Orientation="Horizontal"&amp;gt;
            &amp;lt;!-- Button Height="23" Name="button1" Width="75" Margin="2"&amp;gt;Button&amp;lt;/Button --&amp;gt;
          &amp;lt;/StackPanel&amp;gt;

          &amp;lt;StackPanel  Name="body" VerticalAlignment="Top"&amp;gt;
            &amp;lt;TextBlock Name="message" VerticalAlignment="Top" Margin="5" /&amp;gt;
            &amp;lt;Image Name="image" Stretch="None" /&amp;gt;
          &amp;lt;/StackPanel&amp;gt;
        &amp;lt;/DockPanel&amp;gt;
    &amp;lt;/Grid&amp;gt;
&amp;lt;/Window&amp;gt;'''
    boxRoot = LoadXaml(xaml)
    if root:
      boxRoot.Owner = root
      boxRoot.Top = root.Top
      boxRoot.Left = root.Left
      root.Visibility = System.Windows.Visibility.Hidden
    
    __FurnishWindow(title)
    
    if Vista:
      brush = Brushes.White
    else:
      brush = SystemColors.WindowBrush
    
    for name in ('body', 'buttons', 'backdrop'):
       boxRoot.FindName(name).Background = brush

    (image, msg) = __ValidateImage(image, msg)
    boxRoot.FindName('message').Text = msg

    if image:
      bi = BitmapImage()
      bi.BeginInit()
      image = FileInfo(image).FullName
      bi.UriSource = Uri(image)
      bi.EndInit()
      
      pane = boxRoot.FindName('image')
      pane.BeginInit()
      pane.Source = bi
      pane.EndInit()
    else:
      boxRoot.FindName('image').Visibility = System.Windows.Visibility.Collapsed
      
    if len(choices) &amp;lt; 1:
      choices = ('Ok')
      
    first = None
    for choice in choices:
      x = System.Windows.Controls.Button()
      x.Content = choice
      x.Click += __buttonEvent
      x.MinHeight=23 
      x.MinWidth=75 
      x.Margin= Thickness(2)
      x.HorizontalAlignment = System.Windows.HorizontalAlignment.Center
      boxRoot.FindName('buttons').Children.Add(x)
      if not first:
        first = x
        
    first.Focus()
    boxRoot.ShowDialog()
    
    if root:
      root.Visibility = System.Windows.Visibility.Visible
    return __replyButtonText

#-------------------------------------------------------------------
# integerbox
#-------------------------------------------------------------------
def integerbox(msg=""
    , title=" "
    , default=""
    , argLowerBound=0
    , argUpperBound=99
    , image = None
    , root  = None
    ):
    """
    Show a box in which a user can enter an integer.

    In addition to arguments for msg and title, this function accepts
    integer arguments for default_value, lowerbound, and upperbound.

    The default_value argument may be None.

    When the user enters some text, the text is checked to verify
    that it can be converted to an integer between the lowerbound and upperbound.

    If it can be, the integer (not the text) is returned.

    If it cannot, then an error msg is displayed, and the integerbox is
    redisplayed.

    If the user cancels the operation, None is returned.
    """
    if default != "":
        if type(default) != type(1):
            raise AssertionError(
                "integerbox received a non-integer value for "
                + "default of " + dq(str(default)) , "Error")

    if type(argLowerBound) != type(1):
        raise AssertionError(
            "integerbox received a non-integer value for "
            + "argLowerBound of " + dq(str(argLowerBound)) , "Error")

    if type(argUpperBound) != type(1):
        raise AssertionError(
            "integerbox received a non-integer value for "
            + "argUpperBound of " + dq(str(argUpperBound)) , "Error")

    if msg == "":
        msg = ("Enter an integer between " + str(argLowerBound)
            + " and "
            + str(argUpperBound)
            )

    while 1:
        reply = enterbox(msg, title, str(default), image=image, root=root)
        if reply == None: return None

        try:
            reply = int(reply)
        except:
            msgbox ("The value that you entered:\n\t%s\nis not an integer." % dq(str(reply))
                    , "Error")
            continue

        if reply &amp;lt; argLowerBound:
            msgbox ("The value that you entered is less than the lower bound of "
                + str(argLowerBound) + ".", "Error")
            continue

        if reply &amp;gt; argUpperBound:
            msgbox ("The value that you entered is greater than the upper bound of "
                + str(argUpperBound) + ".", "Error")
            continue

        # reply has passed all validation checks.
        # It is an integer between the specified bounds.
        return reply

#-------------------------------------------------------------------
# multenterbox
#-------------------------------------------------------------------
def multenterbox(msg="Fill in values for the fields."
    , title=" "
    , fields=()
    , values=()
    ):
    r"""
    Show screen with multiple data entry fields.
    
    If there are fewer values than names, the list of values is padded with
    empty strings until the number of values is the same as the number of names.
    
    If there are more values than names, the list of values
    is truncated so that there are as many values as names.
    
    Returns a list of the values of the fields,
    or None if the user cancels the operation.
    
    Here is some example code, that shows how values returned from
    multenterbox can be checked for validity before they are accepted::
        ----------------------------------------------------------------------
        msg = "Enter your personal information"
        title = "Credit Card Application"
        fieldNames = ["Name","Street Address","City","State","ZipCode"]
        fieldValues = []  # we start with blanks for the values
        fieldValues = multenterbox(msg,title, fieldNames)
    
        # make sure that none of the fields was left blank
        while 1:
            if fieldValues == None: break
            errmsg = ""
            for i in range(len(fieldNames)):
                if fieldValues[i].strip() == "":
                    errmsg += ('"%s" is a required field.\n\n' % fieldNames[i])
            if errmsg == "":
                break # no problems found
            fieldValues = multenterbox(errmsg, title, fieldNames, fieldValues)
        
        writeln("Reply was: %s" % str(fieldValues))
        ----------------------------------------------------------------------
    
    @arg msg: the msg to be displayed.
    @arg title: the window title
    @arg fields: a list of fieldnames.
    @arg values:  a list of field values
    """
    return __multfillablebox(msg,title,fields,values,None)


#-----------------------------------------------------------------------
# multpasswordbox
#-----------------------------------------------------------------------
def multpasswordbox(msg="Fill in values for the fields."
    , title=" "
    , fields=tuple()
    ,values=tuple()
    ):
    r"""
    Same interface as multenterbox.  But in multpassword box,
    the last of the fields is assumed to be a password, and
    is masked with asterisks.

    Example
    =======

    Here is some example code, that shows how values returned from
    multpasswordbox can be checked for validity before they are accepted::
        msg = "Enter logon information"
        title = "Demo of multpasswordbox"
        fieldNames = ["Server ID", "User ID", "Password"]
        fieldValues = []  # we start with blanks for the values
        fieldValues = multpasswordbox(msg,title, fieldNames)

        # make sure that none of the fields was left blank
        while 1:
            if fieldValues == None: break
            errmsg = ""
            for i in range(len(fieldNames)):
                if fieldValues[i].strip() == "":
                    errmsg = errmsg + ('"%s" is a required field.\n\n' % fieldNames[i])
                if errmsg == "": break # no problems found
            fieldValues = multpasswordbox(errmsg, title, fieldNames, fieldValues)
        
        writeln("Reply was: %s" % str(fieldValues))
    """
    return __multfillablebox(msg,title,fields,values,"*")

#-----------------------------------------------------------------------
# __multfillablebox
#-----------------------------------------------------------------------
def __multfillablebox(msg="Fill in values for the fields."
    , title=" "
    , fields=()
    , values=()
    , mask = None
    ):
    global boxRoot, __multenterboxText, __multenterboxDefaultText, cancelButton, entryWidget, okButton

    choices = ["OK", "Cancel"]
    if len(fields) == 0: return None

    fields = list(fields[:])  # convert possible tuples to a list
    values = list(values[:])  # convert possible tuples to a list

    if   len(values) == len(fields): pass
    elif len(values) &amp;gt;  len(fields):
        fields = fields[0:len(values)]
    else:
        while len(values) &amp;lt; len(fields):
            values.append("")
            
    xaml = '''&amp;lt;Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window2" SizeToContent="WidthAndHeight" MinWidth="400" MinHeight="150"  WindowStyle="None" AllowsTransparency="True"&amp;gt;
    &amp;lt;Grid&amp;gt;
        &amp;lt;Grid Name="banner"&amp;gt;
            &amp;lt;Image Name="icon" Stretch="Fill" HorizontalAlignment="Left" VerticalAlignment="Top" Width="20" Height="20" Margin="4,4,0,0" /&amp;gt;
            &amp;lt;Label Height="28" Name="title" VerticalAlignment="Top" Background="WhiteSmoke" Margin="28,0,0,0"&amp;gt;Label&amp;lt;/Label&amp;gt;
        &amp;lt;/Grid&amp;gt;
        &amp;lt;DockPanel Margin="0,28,0,0" Name="backdrop"&amp;gt;
          &amp;lt;StackPanel Name="buttons" DockPanel.Dock="Bottom" HorizontalAlignment="Center" Orientation="Horizontal"&amp;gt;
          &amp;lt;/StackPanel&amp;gt;
          &amp;lt;TextBlock Name="message" DockPanel.Dock="Top" VerticalAlignment="Top" Margin="5" /&amp;gt;
          &amp;lt;DockPanel Name="entries"&amp;gt;
            &amp;lt;StackPanel Name="fields" DockPanel.Dock="Left" &amp;gt;
            &amp;lt;/StackPanel&amp;gt;
            &amp;lt;StackPanel Name="values" &amp;gt;
            &amp;lt;/StackPanel&amp;gt;            
          &amp;lt;/DockPanel&amp;gt;
        &amp;lt;/DockPanel&amp;gt;
    &amp;lt;/Grid&amp;gt;
&amp;lt;/Window&amp;gt;'''
    boxRoot = LoadXaml(xaml)
    if root:
      boxRoot.Owner = root
      boxRoot.Top = root.Top
      boxRoot.Left = root.Left
      root.Visibility = System.Windows.Visibility.Hidden
    
    __FurnishWindow(title)
    
    if Vista:
      brush = Brushes.White
    else:
      brush = SystemColors.WindowBrush
    
    #for name in ('backdrop',):
    #   boxRoot.FindName(name).Background = brush
    boxRoot.FindName('backdrop').Background = brush
    boxRoot.FindName('message').Text = msg
      
    buttons = []
    for choice in ("Ok", "Cancel"):
      x = System.Windows.Controls.Button()
      x.Content = choice
      x.Height=23 
      x.Width=75 
      x.Margin= Thickness(2)
      boxRoot.FindName('buttons').Children.Add(x)
      buttons.append(x)

    buttons[0].Click += lambda s, e: __multenterboxGetText(mask)
    buttons[0].IsDefault = True
    buttons[1].Click += lambda s, e: __multenterboxCancel()
    buttons[1].IsCancel = True

    global entryWidgets
    entryWidgets = []
    labelWidgets = []
    first = None
    
    lastWidgetIndex = len(fields) - 1
    left = boxRoot.FindName('fields')
    right = boxRoot.FindName('values')

    for widgetIndex in range(len(fields)):
        argFieldName  = fields[widgetIndex]
        argFieldValue = values[widgetIndex]
        
        label = System.Windows.Controls.Label()
        label.Content = argFieldName
        left.Children.Add(label)
        labelWidgets.append(label)
        label.Margin = Thickness(2)
        
        if mask and (widgetIndex == lastWidgetIndex):
            entry = System.Windows.Controls.PasswordBox()
            entry.PasswordChar = mask
            entry.Password = argFieldValue
        else:
            entry = System.Windows.Controls.TextBox()
            entry.Text = argFieldValue
            
        if not first:
            first = entry
            
        entry.Margin = Thickness(2)
        right.Children.Add(entry)
        entryWidgets.append(entry)
        

    # ------------------- time for action! -----------------
    first.Focus()    # put the focus on the entryWidget
    
    boxRoot.SizeChanged += lambda s,e : __multienteradjust(entryWidgets, labelWidgets)

    boxRoot.ShowDialog()
    if root:
      root.Visibility = System.Windows.Visibility.Visible
    return __multenterboxText
    
#-----------------------------------------------------------------------
# __multienteradjust -- fiddle to get the heights to match up
#-----------------------------------------------------------------------
def __multienteradjust(entryWidgets, labelWidgets):
  h = max (entryWidgets[0].ActualHeight, labelWidgets[0].ActualHeight)
  for e in entryWidgets:
    e.Height = h
    e.MinHeight = h
  for l in labelWidgets:
    l.Height = h
    l.MinHeight = h 
 
#-----------------------------------------------------------------------
# __multenterboxGetText
#-----------------------------------------------------------------------
def __multenterboxGetText(mask):
    global __multenterboxText

    __multenterboxText = []
    for entryWidget in entryWidgets[:-1]:
        __multenterboxText.append(entryWidget.Text)
        
    entry = entryWidgets[-1]
    __multenterboxText.append(entry.Password if mask else entry.Text)       
    __finish()


def __multenterboxCancel():
    global __multenterboxText
    __multenterboxText = None
    __finish()


#-------------------------------------------------------------------
# enterbox
#-------------------------------------------------------------------
def enterbox(msg="Enter something."
    , title=" "
    , default=""
    , strip=True
    , image=None
    , root=None
    ):
    """
    Show a box in which a user can enter some text.

    You may optionally specify some default text, which will appear in the
    enterbox when it is displayed.

    Returns the text that the user entered, or None if he cancels the operation.

    By default, enterbox strips its result (i.e. removes leading and trailing
    whitespace).  (If you want it not to strip, use keyword argument: strip=False.)
    This makes it easier to test the results of the call::

        reply = enterbox(....)
        if reply:
            ...
        else:
            ...
    """
    result = __fillablebox(msg, title, default=default, mask=None,image=image,root=root)
    if result and strip:
        result = result.strip()
    return result


def passwordbox(msg="Enter your password."
    , title=" "
    , default=""
    , image=None
    , root=None
    ):
    """
    Show a box in which a user can enter a password.
    The text is masked with asterisks, so the password is not displayed.
    Returns the text that the user entered, or None if he cancels the operation.
    """
    return __fillablebox(msg, title, default, mask="*",image=image,root=root)


def __fillablebox(msg
    , title=""
    , default=""
    , mask=None
    , image=None
    , root=None
    ):
    """
    Show a box in which a user can enter some text.
    You may optionally specify some default text, which will appear in the
    enterbox when it is displayed.
    Returns the text that the user entered, or None if he cancels the operation.
    """

    global boxRoot, __enterboxText, __enterboxDefaultText
    global cancelButton, entryWidget, okButton

    if title == None: title == ""
    if default == None: default = ""
    __enterboxDefaultText = default
    __enterboxText        = __enterboxDefaultText

    xaml = '''&amp;lt;Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window2" SizeToContent="WidthAndHeight" MinWidth="400" MinHeight="150"  WindowStyle="None" AllowsTransparency="True"&amp;gt;
    &amp;lt;Grid&amp;gt;
        &amp;lt;Grid Name="banner"&amp;gt;
            &amp;lt;Image Name="icon" Stretch="Fill" HorizontalAlignment="Left" VerticalAlignment="Top" Width="20" Height="20" Margin="4,4,0,0" /&amp;gt;
            &amp;lt;Label Height="28" Name="title" VerticalAlignment="Top" Background="WhiteSmoke" Margin="28,0,0,0"&amp;gt;Label&amp;lt;/Label&amp;gt;
        &amp;lt;/Grid&amp;gt;
        &amp;lt;DockPanel Margin="0,28,0,0" Name="backdrop"&amp;gt;
          &amp;lt;StackPanel Name="buttons" DockPanel.Dock="Bottom" HorizontalAlignment="Center" Orientation="Horizontal"&amp;gt;
            &amp;lt;!-- Button Height="23" Name="button1" Width="75" Margin="2"&amp;gt;Button&amp;lt;/Button --&amp;gt;
          &amp;lt;/StackPanel&amp;gt;

          &amp;lt;StackPanel  Name="body" VerticalAlignment="Top"&amp;gt;
            &amp;lt;TextBlock Name="message" VerticalAlignment="Top" Margin="5" /&amp;gt;
            &amp;lt;Image Name="image" Stretch="None" /&amp;gt;
          &amp;lt;/StackPanel&amp;gt;
        &amp;lt;/DockPanel&amp;gt;
    &amp;lt;/Grid&amp;gt;
&amp;lt;/Window&amp;gt;'''
    boxRoot = LoadXaml(xaml)
    if root:
      boxRoot.Owner = root
      boxRoot.Top = root.Top
      boxRoot.Left = root.Left
      root.Visibility = System.Windows.Visibility.Hidden
      
    __FurnishWindow(title)
    
    if Vista:
      brush = Brushes.White
    else:
      brush = SystemColors.WindowBrush
    
    for name in ('body', 'buttons', 'backdrop'):
       boxRoot.FindName(name).Background = brush

    (image, msg) = __ValidateImage(image, msg)
    boxRoot.FindName('message').Text = msg
    
    if image:
      bi = BitmapImage()
      bi.BeginInit()
      image = FileInfo(image).FullName
      bi.UriSource = Uri(image)
      bi.EndInit()
      
      pane = boxRoot.FindName('image')
      pane.BeginInit()
      pane.Source = bi
      pane.EndInit()
    else:
      boxRoot.FindName('image').Visibility = System.Windows.Visibility.Collapsed

    if mask:
      entry = System.Windows.Controls.PasswordBox()
      entry.PasswordChar = mask
      entry.Password = __enterboxDefaultText
    else:
      entry = System.Windows.Controls.TextBox()
      entry.Text = __enterboxDefaultText
      
    entry.Height = 22
    entry.Margin = Thickness(5)
    boxRoot.FindName('body').Children.Add(entry)
    entry.Focus()
    
    buttons = []
    for choice in ("Ok", "Cancel"):
      x = System.Windows.Controls.Button()
      x.Content = choice
      x.Height=23 
      x.Width=75 
      x.Margin= Thickness(2)
      boxRoot.FindName('buttons').Children.Add(x)
      buttons.append(x)

    buttons[0].Click += lambda s, e: __enterboxGetText(entry, mask)
    buttons[0].IsDefault = True
    buttons[1].Click += lambda s, e: __enterboxCancel()
    buttons[1].IsCancel = True

    boxRoot.ShowDialog()
    if root:
      root.Visibility = System.Windows.Visibility.Visible
    return __enterboxText

def __finish():
    global boxRoot
    boxRoot.Hide()
    boxRoot.Close()
    boxRoot = None  

def __enterboxGetText(entry, mask):
    global __enterboxText
    __enterboxText = entry.Password if mask else entry.Text   
    __finish()


def __enterboxCancel():
    global __enterboxText
    __enterboxText = None
    __finish()

#-------------------------------------------------------------------
# multchoicebox
#-------------------------------------------------------------------
def multchoicebox(msg="Pick as many items as you like."
    , title=" "
    , choices=()
    , **kwargs
    ):
    """
    Present the user with a list of choices.
    allow him to select multiple items and return them in a list.
    if the user doesn't choose anything from the list, return the empty list.
    return None if he cancelled selection.

    @arg msg: the msg to be displayed.
    @arg title: the window title
    @arg choices: a list or tuple of the choices to be displayed
    """
    if len(choices) == 0: choices = ["Program logic error - no choices were specified."]

    global __choiceboxMultipleSelect
    __choiceboxMultipleSelect = 1
    return __choicebox(msg, title, choices)


#-----------------------------------------------------------------------
# choicebox
#-----------------------------------------------------------------------
def choicebox(msg="Pick something."
    , title=" "
    , choices=()
    , buttons=()
    ):
    """
    Present the user with a list of choices.
    return the choice that he selects.
    return None if he cancels the selection selection.

    @arg msg: the msg to be displayed.
    @arg title: the window title
    @arg choices: a list or tuple of the choices to be displayed
    """
    if len(choices) == 0: choices = ["Program logic error - no choices were specified."]

    global __choiceboxMultipleSelect
    __choiceboxMultipleSelect = 0
    return __choicebox(msg,title,choices,buttons)


#-----------------------------------------------------------------------
# __choicebox
#-----------------------------------------------------------------------
def __choicebox(msg
    , title
    , choices
    ,  buttons=()
    ):
    """
    internal routine to support choicebox() and multchoicebox()
    """
    global boxRoot, __choiceboxResults, choiceboxWidget, defaultText
    global choiceboxChoices
    #-------------------------------------------------------------------
    # If choices is a tuple, we make it a list so we can sort it.
    # If choices is already a list, we make a new list, so that when
    # we sort the choices, we don't affect the list object that we
    # were given.
    #-------------------------------------------------------------------
    choices = list(choices[:])
    if len(choices) == 0:
        choices = ["Program logic error - no choices were specified."]
    defaultButtons = ["OK", "Cancel"]

    # make sure all choices are strings
    choices = [x.ToString() for x in choices]

    if buttons:
        if type(buttons) == type("abc"): # user sent a string
            choiceboxButtons = [buttons]
        else: # we assume user sent in a list or tuple of strings
            choiceboxButtons = [x.ToString() for x in buttons]
    else:
        choiceboxButtons = defaultButtons

    if title == None: title = ""

    # Initialize __choiceboxResults
    # This is the value that will be returned if the user clicks the close icon
    __choiceboxResults = None

    xaml = '''&amp;lt;Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    Title="Window1" SizeToContent="WidthAndHeight" MinWidth="400" MinHeight="150" WindowStyle="None" AllowsTransparency="True"&amp;gt;
    &amp;lt;Grid&amp;gt;
        &amp;lt;Grid Name="banner"&amp;gt;
            &amp;lt;Image Name="icon" Stretch="Fill" HorizontalAlignment="Left" VerticalAlignment="Top" Width="20" Height="20" Margin="4,4,0,0" /&amp;gt;
            &amp;lt;Label Height="28" Name="title" VerticalAlignment="Top" Background="WhiteSmoke" Margin="28,0,0,0"&amp;gt;Label&amp;lt;/Label&amp;gt;
        &amp;lt;/Grid&amp;gt;
        &amp;lt;DockPanel Margin="0,28,0,0" Name='backdrop'&amp;gt;
          &amp;lt;ListBox Name="listBox" DockPanel.Dock="Bottom" MaxHeight="300" /&amp;gt;
        
          &amp;lt;StackPanel Name="stackPanel" VerticalAlignment="Top" HorizontalAlignment="Right" DockPanel.Dock="Right"  MinHeight="100" &amp;gt;
            &amp;lt;Button MinHeight="23" Name="ok" MinWidth="73" Margin="1"&amp;gt;OK&amp;lt;/Button&amp;gt;
            &amp;lt;Button MinHeight="23" Name="cancel" MinWidth="73" Margin="1"&amp;gt;Cancel&amp;lt;/Button&amp;gt;
            &amp;lt;Button MinHeight="23" Name="select" MinWidth="73" Margin="1"&amp;gt;Select All&amp;lt;/Button&amp;gt;
            &amp;lt;Button Height="23" Name="clear" MinWidth="73" Margin="1"&amp;gt;Clear All&amp;lt;/Button&amp;gt;
          &amp;lt;/StackPanel&amp;gt;
          &amp;lt;TextBlock Name="message" VerticalAlignment="Top" Text="message"  Padding="5" TextWrapping="Wrap" MinHeight="100" /&amp;gt;
        &amp;lt;/DockPanel&amp;gt;
    &amp;lt;/Grid&amp;gt;
&amp;lt;/Window&amp;gt;'''
    boxRoot = LoadXaml(xaml)

    boxRoot.FindName('message').Text = msg

    if Vista:
      brush = Brushes.White
    else:
      brush = SystemColors.WindowBrush
      
    for control in ('message', 'stackPanel', 'backdrop'):
      boxRoot.FindName(control).Background = brush

    __FurnishWindow(title)
      
    #---------------------------------------------------
    # sort the choices
    # eliminate duplicates
    # put the choices into the choiceboxWidget
    #---------------------------------------------------
    choices = set(choices)
    choices = [x for x in choices]
    choices.sort(cmp = lambda s1, s2 : String.Compare(s1, s2, StringComparison.CurrentCultureIgnoreCase))
    
    listBox = boxRoot.FindName('listBox')
    listBox.ItemsSource = choices
    choiceboxWidget = listBox
    choiceboxChoices = choices

    if __choiceboxMultipleSelect:
        listBox.SelectionMode = System.Windows.Controls.SelectionMode.Extended
    
    if len(choices) == 0:
      boxRoot.FindName('ok').Visibility = System.Windows.Visibility.Hidden
      listBox.MouseDoubleClick += __choiceboxCancel
    else:
      listBox.MouseDoubleClick += __choiceboxGetChoice
      listBox.SelectedItem = choices[0]
      listBox.Focus()
      
    if len(choices) == 0 or not __choiceboxMultipleSelect:
      boxRoot.FindName('select').Visibility = System.Windows.Visibility.Hidden
      boxRoot.FindName('clear').Visibility = System.Windows.Visibility.Hidden
    else:
      boxRoot.FindName('select').Click += __choiceboxSelectAll
      boxRoot.FindName('clear').Click += __choiceboxClearAll
      
    boxRoot.FindName('cancel').Click += __choiceboxCancel
    boxRoot.FindName('cancel').IsCancel = True
    boxRoot.FindName('ok').Click += __choiceboxGetChoice
    boxRoot.FindName('ok').IsDefault = True
    
    boxRoot.KeyUp += KeyboardListener
    
    # --- run it! -----
    boxRoot.ShowDialog()
    return __choiceboxResults

def __choiceboxGetChoice(source, event):
    global boxRoot, __choiceboxResults, choiceboxWidget
    if __choiceboxMultipleSelect:
        __choiceboxResults = [x for x in choiceboxWidget.SelectedItems]
    else:
        __choiceboxResults = choiceboxWidget.SelectedItem
    __finish()

def __choiceboxSelectAll(source, event):
    global choiceboxWidget, choiceboxChoices
    choiceboxWidget.SelectAll()

def __choiceboxClearAll(source, event):
    global choiceboxWidget, choiceboxChoices
    choiceboxWidget.SelectedIndex = -1

def __choiceboxCancel(source, event):
    global boxRoot, __choiceboxResults
    __choiceboxResults = None
    __finish()


def KeyboardListener(source, event):
    global choiceboxChoices, choiceboxWidget
    key = event.Key.ToString()
    if len(key) &amp;lt;= 1:
        if not Char.IsControl(key):
            # Find the key in the list.
            # before we clear the list, remember the selected member
            try:
                start_n = choiceboxWidget.SelectedIndex
            except IndexError:
                start_n = -1

            ## clear the selection.
            choiceboxWidget.SelectedIndex = -1

            ## start from previous selection +1
            for n in range(start_n+1, len(choiceboxChoices)):
                item = choiceboxChoices[n]
                if item[0].lower() == key.lower():
                    choiceboxWidget.SelectedIndex = n
                    choiceboxWidget.ScrollIntoView(item)
                    return
            else:
                # has not found it so loop from top
                for n in range(len(choiceboxChoices)):
                    item = choiceboxChoices[n]
                    if item[0].lower() == key.lower():
                        choiceboxWidget.SelectedIndex = n
                        choiceboxWidget.ScrollIntoView(item)
                        return

                # nothing matched -- we'll look for the next logical choice
                for n in range(len(choiceboxChoices)):
                    item = choiceboxChoices[n]
                    if item[0].lower() &amp;gt; key.lower():
                        if n &amp;gt; 0:
                            choiceboxWidget.SelectedIndex = (n-1)
                        else:
                            choiceboxWidget.SelectedIndex = 0
                        choiceboxWidget.ScrollIntoView(item)
                        return

                # still no match (nothing was greater than the key)
                # we set the selection to the first item in the list
                lastIndex = len(choiceboxChoices)-1
                choiceboxWidget.SelectedIndex = lastIndex
                item = choiceboxChoices[lastIndex]
                choiceboxWidget.ScrollIntoView(item)
                return

#-----------------------------------------------------------------------
# exception_format
#-----------------------------------------------------------------------
def exception_format():
    """
    Convert exception info into a string suitable for display.
    """
    return "".join(traceback.format_exception(
           sys.exc_info()[0]
        ,  sys.exc_info()[1]
        ,  sys.exc_info()[2]
        ))

#-----------------------------------------------------------------------
# exceptionbox
#-----------------------------------------------------------------------
def exceptionbox(msg=None, title=None):
    """
    Display a box that gives information about
    an exception that has just been raised.

    The caller may optionally pass in a title for the window, or a
    msg to accompany the error information.

    Note that you do not need to (and cannot) pass an exception object
    as an argument.  The latest exception will automatically be used.
    """
    if title == None: title = "Error Report"
    if msg == None:
        msg = "An error (exception) has occurred in the program."

    codebox(msg, title, exception_format())

#-------------------------------------------------------------------
# codebox
#-------------------------------------------------------------------

def codebox(msg=""
    , title=" "
    , text=""
    ):
    """
    Display some text in a monospaced font, with no line wrapping.
    This function is suitable for displaying code and text that is
    formatted using spaces.

    The text parameter should be a string, or a list or tuple of lines to be
    displayed in the textbox.
    """
    textbox(msg, title, text, codebox=1 )

#-------------------------------------------------------------------
# textbox
#-------------------------------------------------------------------
def textbox(msg=""
    , title=" "
    , text=""
    , codebox=0
    ):
    """
    Display some text in a proportional font with line wrapping at word breaks.
    This function is suitable for displaying general written text.

    The text parameter should be a string, or a list or tuple of lines to be
    displayed in the textbox.
    """

    if msg == None: msg = ""
    if title == None: title = ""

    global boxRoot, __replyButtonText, __widgetTexts, buttonsFrame
    global rootWindowPosition

    xaml = '''&amp;lt;Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    Title="Window1" SizeToContent="WidthAndHeight" MinWidth="400" MinHeight="150" WindowStyle="None" AllowsTransparency="True"&amp;gt;
    &amp;lt;Grid&amp;gt;
        &amp;lt;Grid Name="banner"&amp;gt;
            &amp;lt;Image Name="icon" Stretch="Fill" HorizontalAlignment="Left" VerticalAlignment="Top" Width="20" Height="20" Margin="4,4,0,0" /&amp;gt;
            &amp;lt;Label Height="28" Name="title" VerticalAlignment="Top" Background="WhiteSmoke" Margin="28,0,0,0"&amp;gt;Label&amp;lt;/Label&amp;gt;
        &amp;lt;/Grid&amp;gt;
        &amp;lt;DockPanel Margin="0,28,0,0" Name='backdrop'&amp;gt;
          &amp;lt;ScrollViewer DockPanel.Dock="Bottom" MaxHeight="300" &amp;gt;
            &amp;lt;TextBlock Name="content" /&amp;gt;
          &amp;lt;/ScrollViewer&amp;gt;        
          &amp;lt;StackPanel Name="stackPanel" VerticalAlignment="Top" HorizontalAlignment="Right" DockPanel.Dock="Right"  MinHeight="100" &amp;gt;
            &amp;lt;Button MinHeight="23" Name="ok" MinWidth="73" Margin="1"&amp;gt;OK&amp;lt;/Button&amp;gt;
          &amp;lt;/StackPanel&amp;gt;
          &amp;lt;TextBlock Name="message" VerticalAlignment="Top" Text="message"  Padding="5" TextWrapping="Wrap" MinHeight="100" /&amp;gt;
        &amp;lt;/DockPanel&amp;gt;
    &amp;lt;/Grid&amp;gt;
&amp;lt;/Window&amp;gt;'''
    boxRoot = LoadXaml(xaml)

    boxRoot.FindName('message').Text = msg

    __FurnishWindow(title)
    content = boxRoot.FindName('content')
    
    if Vista:
      boxRoot.FindName('message').Background = Brushes.White
      boxRoot.FindName('stackPanel').Background = Brushes.White
      boxRoot.FindName('backdrop').Background = Brushes.White
      content.Background = Brushes.White
      content.Foreground = Brushes.Black
    else:
      boxRoot.FindName('message').Background = SystemColors.WindowBrush
      boxRoot.FindName('stackPanel').Background = SystemColors.WindowBrush
      boxRoot.FindName('backdrop').Background = SystemColors.WindowBrush
      content.Background = SystemColors.WindowBrush
      content.Background = SystemColors.WindowTextBrush
      
    # put a textbox in the top frame

    if codebox:
      content.TextWrapping = System.Windows.TextWrapping.NoWrap
      content.FontFamily = FontFamily('GlobalMonospace.CompositeFont')
    else:
      content.TextWrapping = System.Windows.TextWrapping.WrapWithOverflow
      content.FontFamily = FontFamily('GlobalSanSerif.CompositeFont')

    okButton = boxRoot.FindName('ok')
    okButton.Click += __textboxOK
    okButton.IsDefault = True
    okButton.IsCancel = True

    # ----------------- the action begins ----------------------------------------
    try:
        # load the text into the textbox
        if type(text) == type("abc"): pass
        else:
            try:
                text = "".join(text)  # convert a list or a tuple to a string
            except:
                msgbox("Exception when trying to convert "+ str(type(text)) + " to text in textbox")
                sys.exit(16)

        ##print "setting text"
        ##print text
        content.Text = text
    except:
        msgbox("Exception when trying to load the textbox.")
        sys.exit(16)

    try:
        okButton.Focus()
    except:
        msgbox("Exception when trying to put focus on okButton.")
        sys.exit(16)

    boxRoot.ShowDialog()
    __finish()
    return __replyButtonText

#-------------------------------------------------------------------
# __textboxOK
#-------------------------------------------------------------------
def __textboxOK(source, event):
    global boxRoot
    boxRoot.Hide()



#-------------------------------------------------------------------
# diropenbox
#-------------------------------------------------------------------
def diropenbox(msg=None
    , title=None
    , default=None
    ):
    """
    A dialog to get a directory name.
    Note that the msg argument, if specified, is ignored.

    Returns the name of a directory, or None if user chose to cancel.

    If the "default" argument specifies a directory name, and that
    directory exists, then the dialog box will start with that directory.
    """
    
    dialog = System.Windows.Forms.FolderBrowserDialog()
    
    dialog.Description=getFileDialogTitle(msg,title) 
    if default:
        dialog.SelectedPath = FileInfo(default).FullName
    
    state = dialog.ShowDialog()
    f = dialog.SelectedPath if state == System.Windows.Forms.DialogResult.OK else None
      
    if not f: return None
    return os.path.normpath(f)



#-------------------------------------------------------------------
# getFileDialogTitle
#-------------------------------------------------------------------
def getFileDialogTitle(msg
    , title
    ):
    if msg and title: return "%s - %s" % (title,msg)
    if msg and not title: return str(msg)
    if title and not msg: return str(title)
    return None # no message and no title

#-------------------------------------------------------------------
# class FileTypeObject for use with fileopenbox
#-------------------------------------------------------------------
class FileTypeObject:
    def __init__(self,filemask):
        if len(filemask) == 0:
            raise AssertionError('Filetype argument is empty.')
            
        self.masks = []
        
        if type(filemask) == type("abc"):  # a string
            self.initializeFromString(filemask)
            
        elif type(filemask) == type([]): # a list
            if len(filemask) &amp;lt; 2:
                raise AssertionError('Invalid filemask.\n'
                +'List contains less than 2 members: "%s"' % filemask)
            else:
                self.name  = filemask[-1]
                self.masks = list(filemask[:-1] )
        else:
            raise AssertionError('Invalid filemask: "%s"' % filemask)

    def __eq__(self,other):
        if self.name == other.name: return True
        return False
    
    def add(self,other):
        for mask in other.masks:
            if mask in self.masks: pass
            else: self.masks.append(mask)

    def toTuple(self):
        return (self.name,tuple(self.masks))
        
    def isAll(self):
        if self.name == "All files": return True
        return False
        
    def initializeFromString(self, filemask):
        # remove everything except the extension from the filemask
        self.ext = os.path.splitext(filemask)[1]
        if self.ext == "" : self.ext = ".*"
        if self.ext == ".": self.ext = ".*"
        self.name = self.getName()
        self.masks = ["*" + self.ext]
        
    def getName(self):
        e = self.ext
        if e == ".*"  : return "All files"
        if e == ".txt": return "Text files"
        if e == ".py" : return "Python files"
        if e == ".pyc" : return "Python files"
        if e == ".xls": return "Excel files"
        if e.startswith("."): 
            return e[1:].upper() + " files"
        return e.upper() + " files"


#-------------------------------------------------------------------
# fileopenbox
#-------------------------------------------------------------------
def fileopenbox(msg=None
    , title=None
    , default="*"
    , filetypes=None
    ):
    """
    A dialog to get a file name.
    
    About the "default" argument
    ============================
        The "default" argument specifies a filepath that (normally)
        contains one or more wildcards.
        fileopenbox will display only files that match the default filepath.
        If omitted, defaults to "*" (all files in the current directory).
    
        WINDOWS EXAMPLE::
            ...default="c:/myjunk/*.py"  
        will open in directory c:\myjunk\ and show all Python files.

        WINDOWS EXAMPLE::
            ...default="c:/myjunk/test*.py"  
        will open in directory c:\myjunk\ and show all Python files
        whose names begin with "test".
        
        
        Note that on Windows, fileopenbox automatically changes the path
        separator to the Windows path separator (backslash).

    About the "filetypes" argument
    ==============================
        If specified, it should contain a list of items,
        where each item is either::
            - a string containing a filemask          # e.g. "*.txt"        
            - a list of strings, where all of the strings except the last one
                are filemasks (each beginning with "*.",
                such as "*.txt" for text files, "*.py" for Python files, etc.).
                and the last string contains a filetype description
            
        EXAMPLE::
            filetypes = ["*.css", ["*.htm", "*.html", "HTML files"]  ]
        
    NOTE THAT
    =========

        If the filetypes list does not contain ("All files","*"),
        it will be added.

        If the filetypes list does not contain a filemask that includes
        the extension of the "default" argument, it will be added.
        For example, if     default="*abc.py"
        and no filetypes argument was specified, then
        "*.py" will automatically be added to the filetypes argument.

    @rtype: string or None
    @return: the name of a file, or None if user chose to cancel

    @arg msg: the msg to be displayed.
    @arg title: the window title
    @arg default: filepath with wildcards
    @arg filetypes: filemasks that a user can choose, e.g. "*.txt"
    """
    dialog = OpenFileDialog()
    ## filter = Image Files(*.BMP;*.JPG;*.GIF)|*.BMP;*.JPG;*.GIF|All files (*.*)|*.*

    initialbase, initialfile, initialdir, filetypes = fileboxSetup(default,filetypes)
    dialog.InitialDirectory = initialdir
    
    filtering = [ '%s (%s) | %s' % (x[0], ';'.join(x[1]), ';'.join(x[1])) for x in filetypes]
    dialog.Filter = '|'.join(filtering)

    #------------------------------------------------------------
    # if initialfile contains no wildcards; we don't want an
    # initial file. It won't be used anyway.
    # Also: if initialbase is simply "*", we don't want an
    # initialfile; it is not doing any useful work.
    #------------------------------------------------------------
    if (initialfile.find("*") &amp;lt; 0) and (initialfile.find("?") &amp;lt; 0):
        initialfile = None
    elif initialbase == "*":
        initialfile = None
    else:
        initialfile = FileInfo(Path.Combine(initialdir, initialfile)).FullName
    dialog.FileName = initialfile
    dialog.Title = getFileDialogTitle(msg,title)

    f = None
    if dialog.ShowDialog():
      f = dialog.FileName

    if not f: return None
    return os.path.normpath(f)

#-------------------------------------------------------------------
# filesavebox
#-------------------------------------------------------------------
def filesavebox(msg=None
    , title=None
    , default=""
    , filetypes=None
    ):
    """
    A file to get the name of a file to save.
    Returns the name of a file, or None if user chose to cancel.

    The "default" argument should contain a filename (i.e. the
    current name of the file to be saved).  It may also be empty,
    or contain a filemask that includes wildcards.
    
    The "filetypes" argument works like the "filetypes" argument to
    fileopenbox.
    """
    dialog = SaveFileDialog()


    initialbase, initialfile, initialdir, filetypes = fileboxSetup(default,filetypes)
    dialog.InitialDirectory = initialdir
    
    filtering = [ '%s (%s) | %s' % (x[0], ';'.join(x[1]), ';'.join(x[1])) for x in filetypes]
    dialog.Filter = '|'.join(filtering)

    initialfile = FileInfo(Path.Combine(initialdir, initialfile)).FullName
    dialog.FileName = initialfile
    dialog.Title = getFileDialogTitle(msg,title)
    
    f = None
    if dialog.ShowDialog():
      f = dialog.FileName
    if not f: return None
    return os.path.normpath(f)


#-------------------------------------------------------------------
#
# fileboxSetup
#
#-------------------------------------------------------------------
def fileboxSetup(default,filetypes):    
    if not default: default = os.path.join(".","*")
    initialdir, initialfile = os.path.split(default)
    if not initialdir : initialdir  = "."
    if not initialfile: initialfile = "*"
    initialbase, initialext = os.path.splitext(initialfile)
    initialFileTypeObject = FileTypeObject(initialfile)
    
    allFileTypeObject = FileTypeObject("*")
    ALL_filetypes_was_specified = False  

    if not filetypes: filetypes= []
    filetypeObjects = []

    for filemask in filetypes:
        fto = FileTypeObject(filemask)
        
        if fto.isAll(): 
            ALL_filetypes_was_specified = True # remember this
            
        if fto == initialFileTypeObject:
            initialFileTypeObject.add(fto) # add fto to initialFileTypeObject
        else:
            filetypeObjects.append(fto)

    #------------------------------------------------------------------
    # make sure that the list of filetypes includes the ALL FILES type.
    #------------------------------------------------------------------
    if ALL_filetypes_was_specified: 
        pass
    elif allFileTypeObject == initialFileTypeObject:
        pass
    else:
        filetypeObjects.insert(0,allFileTypeObject)
    #------------------------------------------------------------------
    # Make sure that the list includes the initialFileTypeObject
    # in the position in the list that will make it the default.
    # This changed between Python version 2.5 and 2.6
    #------------------------------------------------------------------ 
    if len(filetypeObjects) == 0:
        filetypeObjects.append(initialFileTypeObject)

    if initialFileTypeObject in (filetypeObjects[0], filetypeObjects[-1]):
        pass
    else:
      filetypeObjects.append(initialFileTypeObject)   
        
    filetypes = [fto.toTuple() for fto in filetypeObjects]

    return initialbase, initialfile, initialdir, filetypes

#-------------------------------------------------------------------
# utility routines
#-------------------------------------------------------------------
# These routines are used by several other functions in the EasyGui module.

def __buttonEvent(s, e):
    """
    Handle an event that is generated by a person clicking a button.
    """
    global  boxRoot, __widgetTexts, __replyButtonText
    __replyButtonText = s.Content
    __finish()

#-----------------------------------------------------------------------
#
#     class EgStore
#
#-----------------------------------------------------------------------
class EgStore:
    r"""
A class to support persistent storage.

You can use EgStore to support the storage and retrieval
of user settings for an EasyGui application.


# Example A
#-----------------------------------------------------------------------
# define a class named Settings as a subclass of EgStore
#-----------------------------------------------------------------------
class Settings(EgStore):

    def __init__(self, filename):  # filename is required
        #-------------------------------------------------
        # Specify default/initial values for variables that
        # this particular application wants to remember.
        #-------------------------------------------------
        self.userId = ""
        self.targetServer = ""

        #-------------------------------------------------
        # For subclasses of EgStore, these must be
        # the last two statements in  __init__
        #-------------------------------------------------
        self.filename = filename  # this is required
        self.restore()            # restore values from the storage file if possible



# Example B
#-----------------------------------------------------------------------
# create settings, a persistent Settings object
#-----------------------------------------------------------------------
settingsFile = "myApp_settings.txt"
settings = Settings(settingsFile)

user    = "obama_barak"
server  = "whitehouse1"
settings.userId = user
settings.targetServer = server
settings.store()    # persist the settings

# run code that gets a new value for userId, and persist the settings
user    = "biden_joe"
settings.userId = user
settings.store()


# Example C
#-----------------------------------------------------------------------
# recover the Settings instance, change an attribute, and store it again.
#-----------------------------------------------------------------------
settings = Settings(settingsFile)
settings.userId = "vanrossum_g"
settings.store()

"""
    def __init__(self, filename):  # obtaining filename is required
        raise NotImplementedError()

    def restore(self):
        """
        Set the values of whatever attributes are recoverable
        from the pickle file.

        Populate the attributes (the __dict__) of the EgStore object
        from     the attributes (the __dict__) of the pickled object.

        If the pickled object has attributes that have been initialized
        in the EgStore object, then those attributes of the EgStore object
        will be replaced by the values of the corresponding attributes
        in the pickled object.

        If the pickled object is missing some attributes that have
        been initialized in the EgStore object, then those attributes
        of the EgStore object will retain the values that they were
        initialized with.

        If the pickled object has some attributes that were not
        initialized in the EgStore object, then those attributes
        will be ignored.

        IN SUMMARY:

        After the recover() operation, the EgStore object will have all,
        and only, the attributes that it had when it was initialized.

        Where possible, those attributes will have values recovered
        from the pickled object.
        """
        if not os.path.exists(self.filename): return self
        if not os.path.isfile(self.filename): return self

        try:
            f = open(self.filename)
            unpickledObject = pickle.load(f)
            f.close()

            for key in list(self.__dict__.keys()):
                default = self.__dict__[key]
                self.__dict__[key] = unpickledObject.__dict__.get(key,default)
        except:
            pass

        return self

    def store(self):
        """
        Save the attributes of the EgStore object to a pickle file.
        Note that if the directory for the pickle file does not already exist,
        the store operation will fail.
        """
        f = open(self.filename, "w")
        pickle.dump(self, f)
        f.close()


    def kill(self):
        """
        Delete my persistent file (i.e. pickle file), if it exists.
        """
        if os.path.isfile(self.filename):
            os.remove(self.filename)
        return

    def __str__(self):
        """
        return my contents as a string in an easy-to-read format.
        """
        # find the length of the longest attribute name
        longest_key_length = 0
        keys = []
        for key in self.__dict__.keys():
            keys.append(key)
            longest_key_length = max(longest_key_length, len(key))

        keys.sort()  # sort the attribute names
        lines = []
        for key in keys:
            value = self.__dict__[key]
            key = key.ljust(longest_key_length)
            lines.append("%s : %s\n" % (key,repr(value))  )
        return "".join(lines)  # return a string showing the attributes




#-----------------------------------------------------------------------
#
# test/demo easygui
#
#-----------------------------------------------------------------------
def egdemo():
    """
    Run the EasyGui demo.
    """
    # clear the console
    writeln("\n" * 100)
    
    # ============================= define a code snippet =========================
    code_snippet = ("dafsdfa dasflkj pp[oadsij asdfp;ij asdfpjkop asdfpok asdfpok asdfpok"*3) +"\n"+\
"""# here is some dummy Python code
for someItem in myListOfStuff:
    do something(someItem)
    do something()
    do something()
    if somethingElse(someItem):
        doSomethingEvenMoreInteresting()

"""*16
    #======================== end of code snippet ==============================

    #================================= some text ===========================
    text_snippet = ((\
"""It was the best of times, and it was the worst of times.  The rich ate cake, and the poor had cake recommended to them, but wished only for enough cash to buy bread.  The time was ripe for revolution! """ \
*5)+"\n\n")*10

    #===========================end of text ================================

    intro_message = '''
Pick the kind of box that you wish to demo.
 * IronPython version %s
 * EasyWPF version %s''' % (sys.version, egversion)
 
    #========================================== END DEMONSTRATION DATA


    while 1: # do forever
        choices = [
            "msgbox",
            "buttonbox",
            "buttonbox(image) -- a buttonbox that displays an image",
            "choicebox",
            "multchoicebox",
            "textbox",
            "ynbox",
            "ccbox",
            "enterbox",
            "enterbox(image) -- an enterbox that displays an image",
            "exceptionbox",
            "codebox",
            "integerbox",
            "boolbox",
            "indexbox",
            "filesavebox",
            "fileopenbox",
            "passwordbox",
            "multenterbox",
            "multpasswordbox",
            "diropenbox",
            "About EasyGui",
            " Help"
            ]
        choice = choicebox(msg=intro_message
            , title="EasyWPF " + egversion
            , choices=choices)

        if not choice: return

        reply = choice.split()

        if   reply[0] == "msgbox":
            reply = msgbox("short msg", "This is a long title")
            writeln("Reply was: %s" % repr(reply))

        elif reply[0] == "About":
            reply = abouteasywpf()

        elif reply[0] == "Help":
            _demo_help()

        elif reply[0] == "buttonbox":
            reply = buttonbox()
            writeln("Reply was: %s" % repr(reply))

            title = "Demo of Buttonbox with many, many buttons!"
            msg = "This buttonbox shows what happens when you specify too many buttons."
            reply = buttonbox(msg=msg, title=title, choices=choices)
            writeln("Reply was: %s" % repr(reply))

        elif reply[0] == "buttonbox(image)":
            _demo_buttonbox_with_image()

        elif reply[0] == "boolbox":
            reply = boolbox()
            writeln("Reply was: %s" % repr(reply))

        elif reply[0] == "enterbox":
            image = "python_and_check_logo.gif"
            message = "Enter the name of your best friend."\
                      "\n(Result will be stripped.)"
            reply = enterbox(message, "Love!", "     Suzy Smith     ")
            writeln("Reply was: %s" % repr(reply))

            message = "Enter the name of your best friend."\
                      "\n(Result will NOT be stripped.)"
            reply = enterbox(message, "Love!", "     Suzy Smith     ",strip=False)
            writeln("Reply was: %s" % repr(reply))

            reply = enterbox("Enter the name of your worst enemy:", "Hate!")
            writeln("Reply was: %s" % repr(reply))

        elif reply[0] == "enterbox(image)":
            image = "python_and_check_logo.gif"
            message = "What kind of snake is this?"
            reply = enterbox(message, "Quiz",image=image)
            writeln("Reply was: %s" % repr(reply))

        elif reply[0] == "exceptionbox":
            try:
                thisWillCauseADivideByZeroException = 1/0
            except:
                exceptionbox()
            
        elif reply[0] == "integerbox":
            reply = integerbox(
                "Enter a number between 3 and 333",
                "Demo: integerbox WITH a default value",
                222, 3, 333)
            writeln("Reply was: %s" % repr(reply))

            reply = integerbox(
                "Enter a number between 0 and 99",
                "Demo: integerbox WITHOUT a default value"
                )
            writeln("Reply was: %s" % repr(reply))

        elif reply[0] == "diropenbox" : _demo_diropenbox()
        elif reply[0] == "fileopenbox": _demo_fileopenbox()
        elif reply[0] == "filesavebox": _demo_filesavebox()

        elif reply[0] == "indexbox":
            title = reply[0]
            msg   =  "Demo of " + reply[0]
            choices = ["Choice1", "Choice2", "Choice3", "Choice4"]
            reply = indexbox(msg, title, choices)
            writeln("Reply was: %s" % repr(reply))

        elif reply[0] == "passwordbox":
            reply = passwordbox("Demo of password box WITHOUT default"
                + "\n\nEnter your secret password", "Member Logon")
            writeln("Reply was: %s" % str(reply))

            reply = passwordbox("Demo of password box WITH default"
                + "\n\nEnter your secret password", "Member Logon", "alfie")
            writeln("Reply was: %s" % str(reply))

        elif reply[0] == "multenterbox":
            msg = "Enter your personal information"
            title = "Credit Card Application"
            fieldNames = ["Name","Street Address","City","State","ZipCode"]
            fieldValues = []  # we start with blanks for the values
            fieldValues = multenterbox(msg,title, fieldNames)

            # make sure that none of the fields was left blank
            while 1:
                if fieldValues == None: break
                errmsg = ""
                for i in range(len(fieldNames)):
                    if fieldValues[i].strip() == "":
                        errmsg = errmsg + ('"%s" is a required field.\n\n' % fieldNames[i])
                if errmsg == "": break # no problems found
                fieldValues = multenterbox(errmsg, title, fieldNames, fieldValues)

            writeln("Reply was: %s" % str(fieldValues))

        elif reply[0] == "multpasswordbox":
            msg = "Enter logon information"
            title = "Demo of multpasswordbox"
            fieldNames = ["Server ID", "User ID", "Password"]
            fieldValues = []  # we start with blanks for the values
            fieldValues = multpasswordbox(msg,title, fieldNames)

            # make sure that none of the fields was left blank
            while 1:
                if fieldValues == None: break
                errmsg = ""
                for i in range(len(fieldNames)):
                    if fieldValues[i].strip() == "":
                        errmsg = errmsg + ('"%s" is a required field.\n\n' % fieldNames[i])
                if errmsg == "": break # no problems found
                fieldValues = multpasswordbox(errmsg, title, fieldNames, fieldValues)

            writeln("Reply was: %s" % str(fieldValues))

        elif reply[0] == "ynbox":
            title = "Demo of ynbox"
            msg = "Were you expecting the Spanish Inquisition?"
            reply = ynbox(msg, title)
            writeln("Reply was: %s" % repr(reply))
            if reply:
                msgbox("NOBODY expects the Spanish Inquisition!", "Wrong!")

        elif reply[0] == "ccbox":
            title = "Demo of ccbox"
            msg = "Fix EasyGUI bug by initializing mag"
            reply = ccbox(msg,title)
            writeln("Reply was: %s" % repr(reply))

        elif reply[0] == "choicebox":
            title = "Demo of choicebox"
            longchoice = "This is an example of a very long option which you may or may not wish to choose."*2
            listChoices = ["nnn", "ddd", "eee", "fff", "aaa", longchoice
                    , "aaa", "bbb", "ccc", "ggg", "hhh", "iii", "jjj", "kkk", "LLL", "mmm" , "nnn", "ooo", "ppp", "qqq", "rrr", "sss", "ttt", "uuu", "vvv"]

            msg = "Pick something. " + ("A wrapable sentence of text ?! "*30) + "\nA separate line of text."*6
            reply = choicebox(msg=msg, choices=listChoices)
            writeln("Reply was: %s" % repr(reply))

            msg = "Pick something. "
            reply = choicebox(msg=msg, title=title, choices=listChoices)
            writeln("Reply was: %s" % repr(reply))

            msg = "Pick something. "
            reply = choicebox(msg="The list of choices is empty!", choices=[])
            writeln("Reply was: %s" % repr(reply))

        elif reply[0] == "multchoicebox":
            listChoices = ["aaa", "bbb", "ccc", "ggg", "hhh", "iii", "jjj", "kkk"
                , "LLL", "mmm" , "nnn", "ooo", "ppp", "qqq"
                , "rrr", "sss", "ttt", "uuu", "vvv"]

            msg = "Pick as many choices as you wish."
            reply = multchoicebox(msg,"Demo of multchoicebox", listChoices)
            writeln("Reply was: %s" % repr(reply))

        elif reply[0] == "textbox":
            title = "Demo of textbox"
            msg = "Here is some sample text. " * 16
            reply = textbox(msg, "Text Sample", text_snippet)
            writeln("Reply was: %s" % repr(reply))

        elif reply[0] == "codebox":
            msg = "Here is some sample code. " * 16
            reply = codebox(msg, "Code Sample", code_snippet)
            writeln("Reply was: %s" % repr(reply))

        else:
            msgbox("Choice\n\n" + choice + "\n\nis not recognized", "Program Logic Error")
            return



def _demo_buttonbox_with_image():
    image = "python_and_check_logo.gif"

    msg   = "Pretty nice, huh!"
    reply=msgbox(msg,image=image, ok_button="Wow!")
    writeln("Reply was: %s" % repr(reply))

    msg   = "Do you like this picture?"
    choices = ["Yes","No","No opinion"]

    reply=buttonbox(msg,image=image,choices=choices)
    writeln("Reply was: %s" % repr(reply))

    image = os.path.normpath("python_and_check_logo.png")
    reply=buttonbox(msg,image=image, choices=choices)
    writeln("Reply was: %s" % repr(reply))

    image = os.path.normpath("zzzzz.gif")
    reply=buttonbox(msg,image=image, choices=choices)
    writeln("Reply was: %s" % repr(reply))


def _demo_help():
    from StringIO import StringIO
    savedStdout = sys.stdout    # save the sys.stdout file object
    sys.stdout = capturedOutput = StringIO()
    help("EasyWPF")
    sys.stdout = savedStdout   # restore the sys.stdout file object
    codebox("EasyWPF Help",text=capturedOutput.getvalue())

def _demo_filesavebox():    
    filename = "myNewFile.txt"
    title = "File SaveAs"
    msg ="Save file as:"

    f = filesavebox(msg,title,default=filename)
    writeln("You chose to save file: %s" % f)

def _demo_diropenbox(): 
    title = "Demo of diropenbox"
    msg = "Pick the directory that you wish to open."
    d = diropenbox(msg, title)
    writeln("You chose directory...: %s" % d)

    d = diropenbox(msg, title,default="./")
    writeln("You chose directory...: %s" % d)
    
    d = diropenbox(msg, title,default="c:/")
    writeln("You chose directory...: %s" % d)
    
    
def _demo_fileopenbox():        
    msg  = "Python files"
    title = "Open files"
    default="*.py"
    f = fileopenbox(msg,title,default=default)
    writeln("You chose to open file: %s" % f)

    default="./*.gif"
    filetypes = ["*.jpg",["*.zip","*.tgs","*.gz", "Archive files"],["*.htm", "*.html","HTML files"]]
    f = fileopenbox(msg,title,default=default,filetypes=filetypes)
    writeln("You chose to open file: %s" % f)
    
    """#deadcode -- testing ----------------------------------------
    f = fileopenbox(None,None,default=default)
    writeln("You chose to open file: %s" % f)

    f = fileopenbox(None,title,default=default)
    writeln("You chose to open file: %s" % f)

    f = fileopenbox(msg,None,default=default)
    writeln("You chose to open file: %s" % f)

    f = fileopenbox(default=default)
    writeln("You chose to open file: %s" % f)

    f = fileopenbox(default=None)
    writeln("You chose to open file: %s" % f)
    #----------------------------------------------------deadcode """


def _dummy():
    pass

EASYWPF_ABOUT_INFORMATION = '''
========================================================================
0.3.93(2010-Mar-28)
========================================================================

ENHANCEMENTS
------------------------------------------------------

 * Make the WNDPROC callback for handling the Composition Changed message
   actually set the "handled" parameter

========================================================================
0.2.93(2010-Mar-08)
========================================================================

ENHANCEMENTS
------------------------------------------------------

 * Complete port to apparent parity with EasyGui 0.93

========================================================================
0.1.93(2010-Mar-07)
========================================================================

ENHANCEMENTS
------------------------------------------------------

 * First partial implementation against WPF of EasyGui 0.93
'''

def abouteasywpf():
    """
    shows the easywpf revision history
    """
    codebox("About EasyWPF\n"+egversion,"EasyWPF",EASYWPF_ABOUT_INFORMATION)
    return None



if __name__ == '__main__':
    if len(sys.argv) == 1:
        egdemo()
    else:
        # test the new root feature 
        xaml = '''&amp;lt;Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window3" Height="300" Width="300"&amp;gt;
    &amp;lt;Grid&amp;gt;
        &amp;lt;TextBox Name="textBox1" /&amp;gt;
    &amp;lt;/Grid&amp;gt;
&amp;lt;/Window&amp;gt;'''        
        
        root = LoadXaml(xaml)
        root.FindName('textBox1').Text = """This is a test of a main WPF window in which we will place an easygui msgbox.
                It will be an interesting experiment.\n\n"""
        root.Show()
        msgbox("this is a test of passing in boxRoot", root=root)
        msgbox("this is a second test of passing in boxRoot", root=root)
        
        reply = enterbox("Enter something", root=root)
        writeln("You wrote:", reply)

        reply = enterbox("Enter something else", root=root)
        writeln("You wrote:", reply)
        root.Hide()
        root.Close()
        root = None 
&lt;/pre&gt;
&lt;p&gt;Plain unformatted code for easy cutting and pasting.&lt;/p&gt;
&lt;p&gt;Save this as &lt;code&gt;EasyWPF.py&lt;/code&gt;, and use&lt;/p&gt;
&lt;pre class="brush: python"&gt;from EasyWPF import *&lt;/pre&gt;
&lt;p&gt;in place of &lt;/p&gt;
&lt;pre class="brush: python"&gt;from easygui import *&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-2845198669845791767?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/2845198669845791767/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=2845198669845791767' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/2845198669845791767'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/2845198669845791767'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2010/03/easywpf-0393.html' title='EasyWPF 0.3.93'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-1297268289758696881</id><published>2010-03-08T20:59:00.005Z</published><updated>2010-03-28T23:21:49.807+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>EasyWPF 0.2.93</title><content type='html'>&lt;p&gt;&lt;a href="http://tinesware.blogspot.com/2010/03/easywpf-0393.html"&gt;See next post&lt;/a&gt;.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-1297268289758696881?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/1297268289758696881/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=1297268289758696881' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/1297268289758696881'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/1297268289758696881'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2010/03/work-in-progress-easywpf.html' title='EasyWPF 0.2.93'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-5033117113588580784</id><published>2010-02-14T19:45:00.002Z</published><updated>2010-02-14T21:14:45.728Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='tools'/><category scheme='http://www.blogger.com/atom/ns#' term='F#'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>Infrastructure Helper 1.1</title><content type='html'>&lt;p&gt;Mainly a build process update to &lt;a href="http://tinesware.blogspot.com/2009/11/infrastructure-helper-10.html"&gt;v1.0&lt;/a&gt;, but with minor tweaks for the February 2010 CTP of F# (version 1.9.9.9).&lt;/p&gt;
&lt;p&gt;The Infrastructure project should now build inside VS 2008 shell with the F# extension -- all the C# compilation needed to test that language's lambdas, and to show that even with the coverage annotation, setting the release build as default can still let a C# assembly be deployed without dragging F# along -- are done as &lt;code&gt;BeforeBuild&lt;/code&gt; targets invoking the CSC task.&lt;/p&gt;
&lt;p&gt;The manual steps of checking FxCop output are similarly automated as &lt;code&gt;AfterBuild&lt;/code&gt; targets, though none of these are yet abstracted into a shared .targets file.&lt;/p&gt;
&lt;p&gt;Source is now &lt;a href="http://code.google.com/p/tinesware-tools/"&gt;hosted at Google Code.&lt;/a&gt;, with &lt;a href="http://code.google.com/p/tinesware-tools/downloads/list"&gt;a first release available&lt;/a&gt; -- 7-zip archive containing PGP signature and nested archive.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-5033117113588580784?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/5033117113588580784/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=5033117113588580784' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/5033117113588580784'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/5033117113588580784'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2010/02/infrastructure-helper-11.html' title='Infrastructure Helper 1.1'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-4192287375347533285</id><published>2010-01-26T20:52:00.004Z</published><updated>2010-07-13T22:13:43.870+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='tools'/><category scheme='http://www.blogger.com/atom/ns#' term='F#'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>Adopting PartCover</title><content type='html'>&lt;p&gt;&lt;span style="font-weight:bold;"&gt;NOTE:&lt;/span&gt; &lt;a href="http://stevegilham.blogspot.com/2010/07/building-partcover-4-on-vista.html"&gt;the current version of PartCover (for .net 4) as of 13-Jul-10 appears to resolve this issue&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Starting to think about &lt;a href="http://tinesware.blogspot.com/2009/11/infrastructure-helper-10.html"&gt;Infrastructure Helper&lt;/a&gt; next release, I've started by running up PartCover to try that out; and noting the necessary transformations, like for a post-build step execution&lt;/p&gt;
&lt;pre class="brush: csharp"&gt;%NCOVER% %NUNIT% $(TargetFileName) /noshadow //reg //a "Tinesware.Infrastructure;Tinesware.InfrastructureTests;Tinesware.Rules;Tinesware.TestData;CSharpTests"&lt;/pre&gt;
&lt;p&gt;becoming&lt;/p&gt;
&lt;pre class="brush: csharp"&gt;%PCOVER% --target "%NUNIT%"  --target-work-dir $(TargetDir) --target-args "$(TargetFileName) /noshadow" --include [Tinesware.*]* --include [CSharpTests]* --output $(TargetDir)PCoverage.xml&lt;/pre&gt;
&lt;p&gt;where the &lt;code&gt;%THING%&lt;/code&gt; symbols point to the relevant executables.&lt;/p&gt;
&lt;p&gt;Next step is to unpick and start consuming the PartCover xml; and also to see how flaky it actually is when running over F# code.  I'm not immediately optimistic, as when running coverage over the operational tests, I get&lt;/p&gt;
&lt;pre class="brush: csharp"&gt;[01591] [05764] GetClassIDInfo failed for .NewRight
[01591] [05764] GetClassIDInfo failed for ..ctor
[01591] [05764] GetClassIDInfo failed for ..ctor
[01591] [05764] GetClassIDInfo failed for .get_Item
[01607] [05764] GetClassIDInfo failed for ..ctor
[01607] [05764] GetClassIDInfo failed for .Invoke
[01622] [05764] GetClassIDInfo failed for ..ctor
[01622] [05764] GetClassIDInfo failed for .Invoke
System.InvalidProgramException: Common Language Runtime detected an invalid program.
   at Tinesware.Rules.Assist.Local.LineRange(XElement node)
   at Tinesware.Rules.Assist.Local.MatchNode @ 234.Invoke(a candidate) in Infrastructure\TineswareRules\Assist.fs:line 235
...&lt;/pre&gt;
&lt;p&gt;which is some Partcover noise from instrumenting the F# code; and then a barf in code that unpicks NCover format data (and which worked just fine under NCover earlier in the same post-build operation).&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-4192287375347533285?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/4192287375347533285/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=4192287375347533285' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/4192287375347533285'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/4192287375347533285'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2010/01/adopting-partcover.html' title='Adopting PartCover'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-6310350420681450063</id><published>2009-11-22T20:48:00.005Z</published><updated>2009-11-22T23:24:23.038Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='tools'/><category scheme='http://www.blogger.com/atom/ns#' term='F#'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>Infrastructure Helper 1.0</title><content type='html'>&lt;p&gt;After a lot of refactoring, some bug-fixing, and some more catching up with F#, the project has got to the point where I feel it deserves a 1.0 label.&lt;/p&gt;
&lt;p&gt;Code and built binaries as usual from the Mediafire drop.&lt;/p&gt;

&lt;div style="width:80%; margin: 0 auto; border:1px solid silver"&gt;&lt;h3&gt;Legend&lt;/h3&gt;

    &lt;table width="30%"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td width="50%"&gt;Uncovered code&lt;/td&gt;&lt;td width="50%" style="background:red;"&gt;&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td width="50%"&gt;User exempted&lt;/td&gt;&lt;td width="50%" style="background:orange;"&gt;&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td width="50%"&gt;Generated code&lt;/td&gt;&lt;td width="50%" style="background:yellow;"&gt;&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td width="50%"&gt;Statically analyzed&lt;/td&gt;&lt;td width="50%" style="background:silver;"&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;
    &lt;td width="50%"&gt;Covered code&lt;/td&gt;&lt;td width="50%" style="background:#90ee90;"&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
    
    
    &lt;hr&gt;
    
    &lt;H2&gt;Modules summary&lt;/H2&gt;
    
    &lt;table width="90%"&gt;
    &lt;tr&gt;&lt;td width="30%"&gt;&lt;div&gt;CSharpTests&lt;/div&gt;&lt;/td&gt;
    &lt;td width="70%"&gt;&lt;table width="100%" style="font-size: 9px; font-weight: bold; padding-top: 1px; padding-bottom: 1px; padding-left: 3px; padding-right: 3px;"&gt;
    &lt;tr&gt;&lt;td style="background:#90ee90;" width="100%"&gt;100%&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/td&gt;&lt;/tr&gt;

    &lt;tr&gt;&lt;td width="30%"&gt;&lt;div&gt;Tinesware.Infrastructure&lt;/div&gt;&lt;/td&gt;
    &lt;td width="70%"&gt;&lt;table width="100%" style="font-size: 9px; font-weight: bold; padding-top: 1px; padding-bottom: 1px; padding-left: 3px; padding-right: 3px;"&gt;
    &lt;tr&gt;&lt;td style="background:orange;" width="10.53%"&gt;10.53%&lt;/td&gt;
    &lt;td style="background:silver;" width="10.53%"&gt;10.53%&lt;/td&gt;
    &lt;td style="background:#90ee90;" width="78.95%"&gt;78.95%&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/td&gt;&lt;/tr&gt;

    &lt;tr&gt;&lt;td width="30%"&gt;&lt;div&gt;Tinesware.InfrastructureTests&lt;/div&gt;&lt;/td&gt;
    &lt;td width="70%"&gt;&lt;table width="100%" style="font-size: 9px; font-weight: bold; padding-top: 1px; padding-bottom: 1px; padding-left: 3px; padding-right: 3px;"&gt;
    &lt;tr&gt;&lt;td style="background:orange;" width="3.57%"&gt;3.57%&lt;/td&gt;
    &lt;td style="background:yellow;" width="7.14%"&gt;7.14%&lt;/td&gt;
    &lt;td style="background:silver;" width="25%"&gt;25%&lt;/td&gt;
    &lt;td style="background:#90ee90;" width="64.29%"&gt;64.29%&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td width="30%"&gt;&lt;div&gt;Tinesware.Rules&lt;/div&gt;&lt;/td&gt;
    &lt;td width="70%"&gt;&lt;table width="100%" style="font-size: 9px; font-weight: bold; padding-top: 1px; padding-bottom: 1px; padding-left: 3px; padding-right: 3px;"&gt;
    &lt;tr&gt;&lt;td style="background:orange;" width="9.31%"&gt;9.31%&lt;/td&gt;
    &lt;td style="background:yellow;" width="2.09%"&gt;2.09%&lt;/td&gt;
    &lt;td style="background:silver;" width="0.48%"&gt;0.48%&lt;/td&gt;
    &lt;td style="background:#90ee90;" width="88.12%"&gt;88.12%&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td width="30%"&gt;&lt;div&gt;Tinesware.TestData&lt;/div&gt;&lt;/td&gt;
    &lt;td width="70%"&gt;&lt;table width="100%" style="font-size: 9px; font-weight: bold; padding-top: 1px; padding-bottom: 1px; padding-left: 3px; padding-right: 3px;"&gt;
    &lt;tr&gt;&lt;td style="background:red;" width="3.23%"&gt;3.23%&lt;/td&gt;
    &lt;td style="background:orange;" width="24.19%"&gt;24.19%&lt;/td&gt;
    &lt;td style="background:yellow;" width="12.9%"&gt;12.9%&lt;/td&gt;
    &lt;td style="background:silver;" width="62.9%"&gt;62.9%&lt;/td&gt;
    &lt;td style="background:#90ee90;" width="3.23%"&gt;3.23%&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;No new features since 0.9, but fixes for analysing F# Sum and Record types, code marked with the &lt;code&gt;[GeneratedCode]&lt;/code&gt; attribute, and the changed naming convention for the generated classes that represent F# &lt;code&gt;fun&lt;/code&gt;s in the October CTP.&lt;/p&gt;
&lt;p&gt;Looking ahead, I see the following likely features for the post 1.0 development:&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;Support for PartCover report files&lt;/li&gt;
&lt;li&gt;Some integration with NDepend (a suppress-message style attribute and modified CQL rules)&lt;/li&gt;
&lt;li&gt;extended XSLT to collate reports of the reasons given for code being excluded&lt;/li&gt; 
&lt;li&gt;Support for more F# language features as necessary (I haven't tried computation expressions at all yet, and expect there might be some more wrinkles under the covers there),&lt;/li&gt;
&lt;li&gt;And of course bug fixing and refactoring as needed.&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;If anyone else out there does decide to use this, I don't promise prompt turn around with bug reports, but I do welcome them.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-6310350420681450063?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/6310350420681450063/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=6310350420681450063' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/6310350420681450063'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/6310350420681450063'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2009/11/infrastructure-helper-10.html' title='Infrastructure Helper 1.0'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-4556194599696113003</id><published>2009-11-03T21:29:00.003Z</published><updated>2009-11-03T21:33:16.042Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='tools'/><category scheme='http://www.blogger.com/atom/ns#' term='F#'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>Infrastructure Helper  v0.9</title><content type='html'>&lt;p&gt;Around life being rather hectic here, some progress on fun things -- Version 0.9 of the FxCop coverage analyser now uploaded to Mediafire.&lt;/p&gt;
&lt;p&gt;This adopts the October CTP, and makes a number of changes in the static coverage analysis as a result of the changes in code generation.  It also much improves the estimation of which range of lines an FxCop &lt;code&gt;Method&lt;/code&gt; covers, and adds a couple of simple programming-by-contract methods for C# coding.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-4556194599696113003?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/4556194599696113003/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=4556194599696113003' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/4556194599696113003'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/4556194599696113003'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2009/11/infrastructure-helper-v09.html' title='Infrastructure Helper  v0.9'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-7561125728465616801</id><published>2009-10-02T16:58:00.002+01:00</published><updated>2009-10-02T17:05:51.590+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='tools'/><category scheme='http://www.blogger.com/atom/ns#' term='F#'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>A little more help</title><content type='html'>&lt;p&gt;Version 0.8 of the FxCop coverage analyser now uploaded to Mediafire&lt;/p&gt;
&lt;p&gt;Changes since 0.7 include&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;Code in C# lambdas is added to the coverage point count of the enclosing method&lt;/li&gt;
&lt;li&gt;more trivial methods are statically analysed above and beyond simple field-backed properties.  These include
&lt;ul&gt;&lt;li&gt;Empty methods&lt;/li&gt;
&lt;li&gt;methods that return a constant or a field&lt;/li&gt;
&lt;li&gt;methods that contain just the one statement to 
&lt;ul&gt;&lt;li&gt;&lt;code&gt;failwith&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;raise&lt;/code&gt;&lt;/li&gt;&lt;li&gt;or simply &lt;code&gt;throw&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt; an exeception&lt;/li&gt;&lt;/ul&gt;&lt;/ul&gt;
&lt;p&gt;As before I assume that such analyses are run against debug builds, and consequently cater for the oddities that sometimes arise in that build target type.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-7561125728465616801?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/7561125728465616801/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=7561125728465616801' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/7561125728465616801'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/7561125728465616801'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2009/10/little-more-help.html' title='A little more help'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-8659976033829289968</id><published>2009-09-16T07:35:00.005+01:00</published><updated>2009-09-16T09:09:56.093+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='tools'/><category scheme='http://www.blogger.com/atom/ns#' term='F#'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>A little help for .net (emphasis F#) development.</title><content type='html'>&lt;p&gt;One of the things that is always an annoyance when trying to get good unit test coverage of your code -- especially in F# -- is the amount of red that can come from compiler generated code, as well as things like trivial properties that just expose a field.  Do you really want to write great swathes of unit tests like&lt;/p&gt;
&lt;pre name="code" class="c#"&gt;[Test]
public void TrivialPointsPropertyIsTrival()
{
  var subject = new MyObj();
  int nonDefaultValue = 23;
  subject.Points = nonDefaultValue;
  Assert.That(subject.Points, Is.EqualTo(nonDefaultValue), "What goes in should come out");
}&lt;/pre&gt;
&lt;p&gt;rather than ones that exercise real logic?&lt;/p&gt;
&lt;p&gt;Of course not.  So I've been putting together a little project in F#, mainly for use with F# (thought there are some nods to C# code), to take raw coverage data in NCover format and separate sheep from goats.&lt;/p&gt;
&lt;p&gt;It works as an FxCop rule, analysing a &lt;code&gt;coverage.xml&lt;/code&gt; file in conjunction with the code, and producing both warnings about code that is not covered, and a revised coverage file that categorises uncovered code.  As an adjunct, the bundle includes a method-level attribute &lt;code&gt;CoverageExemption&lt;/code&gt;, to allow the developer to declare some part of a method as uncovered and why, like:&lt;/p&gt;
&lt;pre name="code" class="f#"&gt;  [&amp;lt; Test &amp;gt;]
  [&amp;lt; SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Unit tests cannot be static") &amp;gt;]
  [&amp;lt; CoverageExemption( Points = 1, Justification = "None branch required for warning-free compilation") &amp;gt;] 
  member self.ExpandFileWithSensibleWorks() =
    let result = CoverageExemptionTest.MakeOption (ExpandFile CoverageExemptionTest.Resolver "dummy.txt")
    match result with
    | Some x -&gt; Assert.That( x.EndsWith("\\dummy.txt", StringComparison.OrdinalIgnoreCase), Is.True, "should be a valid file name")
    | None -&gt; Assert.Fail()
&lt;/pre&gt;
&lt;p&gt;where we declare -- for code review purposes -- why we cannot get coverage of that final pattern when building with the expected &lt;code&gt;/W4 /WX&lt;/code&gt; restrictions.&lt;/p&gt;
&lt;p&gt;The sort of report we get from this (after applying a modified form of the XSLT file that processes it for the browser) looks like:&lt;/p&gt;
&lt;div style="width:80%; margin: 0 auto; border:1px solid silver"&gt;&lt;h3&gt;Legend&lt;/h3&gt;

    &lt;table width="30%"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td width="50%"&gt;Uncovered code&lt;/td&gt;&lt;td width="50%" style="background:red;"&gt;&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td width="50%"&gt;User exempted&lt;/td&gt;&lt;td width="50%" style="background:orange;"&gt;&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td width="50%"&gt;Generated code&lt;/td&gt;&lt;td width="50%" style="background:yellow;"&gt;&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td width="50%"&gt;Statically analyzed&lt;/td&gt;&lt;td width="50%" style="background:silver;"&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;
    &lt;td width="50%"&gt;Covered code&lt;/td&gt;&lt;td width="50%" style="background:#90ee90;"&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
    
    
    &lt;hr&gt;
    
    &lt;H2&gt;Modules summary&lt;/H2&gt;
    
    &lt;table width="90%"&gt;
    &lt;tr&gt;&lt;td width="30%"&gt;&lt;div&gt;Tinesware.Infrastructure&lt;/div&gt;&lt;/td&gt;
    &lt;td width="70%"&gt;&lt;table width="100%" style="font-size: 9px; font-weight: bold; padding-top: 1px; padding-bottom: 1px; padding-left: 3px; padding-right: 3px;"&gt;
    &lt;tr&gt;&lt;td style="background:silver;" width="25%"&gt;25%&lt;/td&gt;
    &lt;td style="background:#90ee90;" width="75%"&gt;75%&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
    &lt;table width="90%"&gt;&lt;tr&gt;&lt;td width="30%"&gt;&lt;div&gt;Tinesware.InfrastructureTests&lt;/div&gt;&lt;/td&gt;
    &lt;td width="70%"&gt;&lt;table width="100%" style="font-size: 9px; font-weight: bold; padding-top: 1px; padding-bottom: 1px; padding-left: 3px; padding-right: 3px;"&gt;
    &lt;tr&gt;&lt;td style="background:orange;" width="4.1666666666666661%"&gt;4.17%&lt;/td&gt;
    &lt;td style="background:yellow;" width="8.3333333333333321%"&gt;8.33%&lt;/td&gt;
    &lt;td style="background:silver;" width="8.3333333333333321%"&gt;8.33%&lt;/td&gt;
    &lt;td style="background:#90ee90;" width="79.166666666666657%"&gt;79.17%&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
    &lt;table width="90%"&gt;&lt;tr&gt;&lt;td width="30%"&gt;&lt;div&gt;Tinesware.Rules&lt;/div&gt;&lt;/td&gt;
    &lt;td width="70%"&gt;&lt;table width="100%" style="font-size: 9px; font-weight: bold; padding-top: 1px; padding-bottom: 1px; padding-left: 3px; padding-right: 3px;"&gt;
    &lt;tr&gt;&lt;td style="background:orange;" width="14.661654135338345%"&gt;14.66%&lt;/td&gt;
    &lt;td style="background:yellow;" width="2.8195488721804511%"&gt;2.82%&lt;/td&gt;
    &lt;td style="background:#90ee90;" width="82.5187969924812%"&gt;82.52%&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
    &lt;table width="90%"&gt;&lt;tr&gt;&lt;td width="30%"&gt;&lt;div&gt;Tinesware.TestData&lt;/div&gt;&lt;/td&gt;
    &lt;td width="70%"&gt;&lt;table width="100%" style="font-size: 9px; font-weight: bold; padding-top: 1px; padding-bottom: 1px; padding-left: 3px; padding-right: 3px;"&gt;
    &lt;tr&gt;&lt;td style="background:red;" width="26.666666666666668%"&gt;26.67%&lt;/td&gt;
    &lt;td style="background:orange;" width="20%"&gt;20%&lt;/td&gt;
    &lt;td style="background:yellow;" width="6.666666666666667%"&gt;6.67%&lt;/td&gt;
    &lt;td style="background:silver;" width="40%"&gt;40%&lt;/td&gt;
    &lt;td style="background:#90ee90;" width="6.666666666666667%"&gt;6.67%&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;etc., where code is split up into&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;Orange -- code marked by the user with &lt;code&gt;CoverageExemption&lt;/code&gt; as uncoverable, i.e. the bits where review should concentrate&lt;/li&gt;
&lt;li&gt;Yellow -- compiler generated code; this is currently either types beginning with "&amp;lt;", marked with the &lt;code&gt;CompilerGenerated&lt;/code&gt; or &lt;code&gt;GeneratedCode&lt;/code&gt; attributes, automatic code from algebraic types, which have &lt;code&gt;CompilationMapping()&lt;/code&gt; with either the &lt;code&gt;SourceConstructFlags.NonpublicRepresentation&lt;/code&gt; or &lt;code&gt;SourceConstructFlags.UnionCase&lt;/code&gt; flags, funs, and C# lambdas (code-points for funs are coalesced with the containing function, so are not totally exempted at this point; C# lambdas needs some work -- the process is heuristic based on observed generated type names)&lt;/li&gt;
&lt;li&gt;Silver -- simple code (such as properties that map onto a field, or trivial constructors, typically generated implicitly, that simply invoke another constructor) whose stereotypical structure can be matched against known patterns within FxCop.&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;The &lt;code&gt;CoverageExemption&lt;/code&gt; attribute in the Tinesware.Infrastructure assembly is itself marked with &lt;code&gt;[&amp;lt; System.Diagnostics.Conditional("CODE_ANALYSIS") &amp;gt;]&lt;/code&gt;; this assumes that you build debug builds for analysis with that value set -- omitting it in the release build to avoid adding this declarative clutter to what you finally publish.  This also means that you can use it in C# code without having to also require the F# libraries to be present to use the application.&lt;/p&gt;
&lt;p&gt;Source and binaries for what I've labelled the 0.7 version is &lt;a href="http://www.mediafire.com/mr_tines"&gt;on Mediafire&lt;/a&gt;  in the WorksInProgress folder; the source uses, but does not include FxCop 1.36, NCover 1.5.8 and NUnit 2.5.2.9222 as support.  In the binaries drop, &lt;code&gt;CoverageExemption&lt;/code&gt; attribute is in &lt;code&gt;Tinesware.Infrastructure.dll&lt;/code&gt;, the FxCop rule (along with the initial five-finger exercise of porting the much travelled "FxCop suppressions must have a justification" rule) in &lt;code&gt;Tinesware.Rules.dll&lt;/code&gt;, and the modified XSLT file is included too.&lt;/p&gt;
&lt;p&gt;The code could still be polished a lot further, but it's now at least at a useful minimum of functionality.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-8659976033829289968?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/8659976033829289968/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=8659976033829289968' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/8659976033829289968'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/8659976033829289968'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2009/09/little-help-for-net-emphasis-f.html' title='A little help for .net (emphasis F#) development.'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-7873536817432312642</id><published>2009-07-24T20:43:00.005+01:00</published><updated>2009-07-24T21:06:58.795+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C/C++'/><category scheme='http://www.blogger.com/atom/ns#' term='concurrency'/><title type='text'>The lock-free queue revisited</title><content type='html'>&lt;p&gt;&lt;a href="http://tinesware.blogspot.com/2009/07/simple-lockfree-queue-c-windows.html"&gt;The previous entry&lt;/a&gt; gave the queue itself, and didn't worry about locks that might or might not be taken out by the ancillary code.  To avoid allocations and deallocations of queue nodes in the steady state, get rid of the STL &lt;code&gt;list&lt;/code&gt; and keep them on a stack -- &lt;/p&gt;
&lt;pre name="code" class="cpp"&gt;/*
            DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
                    Version 2, December 2004

 Copyright (C) 2004 Sam Hocevar
  14 rue de Plaisance, 75014 Paris, France
 Everyone is permitted to copy and distribute verbatim or modified
 copies of this license document, and changing it is allowed as long
 as the name is changed.

            DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. You just DO WHAT THE FUCK YOU WANT TO. */

/* This program is free software. It comes without any warranty, to
 * the extent permitted by applicable law. You can redistribute it
 * and/or modify it under the terms of the Do What The Fuck You Want
 * To Public License, Version 2, as published by Sam Hocevar. See
 * http://sam.zoy.org/wtfpl/COPYING for more details. */ 

#pragma once

// suppress 64-bit ready warning about casting on the win32 platform (only)
#pragma warning(disable:4311)
#pragma warning(disable:4312)

// After Sutter in Dr. Dobbs -- http://ddj.com/cpp/210604448?pgno=2
#include &amp;lt;list&amp;gt;
#include "Windows.h"

template &amp;lt;typename T&amp;gt;
class SimpleQueue {
public:
    typedef T* Tptr;                                // should really be a smart_pointer&amp;lt;T&amp;gt;
    typedef const T * Tcptr;

private:
    SimpleQueue(const SimpleQueue &amp;amp;);               // Not copyable
     SimpleQueue &amp;amp; operator= (const SimpleQueue &amp;amp;); // Not assignable

  struct Node {
     Node( Tptr val ) : value(val), next(NULL) { }
    Node() : next(NULL) { value = NULL; }
    Tptr value;
    Node* next;
  };

  Node * freeStack;             // for producer only
  Node* first;                  // for producer only
  Node *divider, *last;         // shared -- Use explicit atomic compares only

  // Allocator/Deallocator for nodes -- 
  // only used in the producer thread
  // OR in the destructor.
  Node * Get(Tptr val)
  {
     if(freeStack)
     {
        // Clean because of Release
        Node * next = freeStack;
        freeStack = next-&amp;gt;next;
        next-&amp;gt;value = val;
        return next;
     }

     // clean by construction
     return new Node(val);     
  }

  // Avoids costly free() while running
  void Release(Node * node)
  {
        // reset the node to clean before shelving it
        node-&amp;gt;value = NULL;
        node-&amp;gt;next = freeStack;
        freeStack = node;
  }


public:
    SimpleQueue() : freeStack(NULL) {
    first = divider = last = Get(NULL);                         // add dummy separator
  }

  ~SimpleQueue() {
    while( first != NULL ) {               // release the list
      Node* tmp = first;
      first = tmp-&amp;gt;next;
      delete tmp;
    }

     // Require -- Producer thread calls this or is dead
     while(freeStack)
     {
          delete Get(NULL);
     }
  }

  // Produce is called on the producer thread only:
  void Produce( Tptr t ) {
    last-&amp;gt;next = Get(t);                            // add the new item
    InterlockedExchangePointer(&amp;amp;last, last-&amp;gt;next);  // publish it : atomic last = last-&amp;gt;next;


    // Burn the consumed part of the queue
    for( PVOID looper = first;                     // non-null; pointer read is atomic; atomic while( first != divider )
           InterlockedCompareExchangePointer(&amp;amp;looper, NULL, divider), looper;
           looper = first)
     {
      Node* tmp = first;
      first = first-&amp;gt;next;

      Release(tmp);
    }
  }

  // Consume is called on the consumer thread only:
  bool Consume( Tptr &amp;amp; result ) {

     PVOID choice = divider;                                  // non-null; pointer read is atomic
     InterlockedCompareExchangePointer(&amp;amp;choice, NULL, last);

     if(choice)
     {
        if(!divider || ! divider-&amp;gt;next || ! divider-&amp;gt;next-&amp;gt;value) 
            printf("help!");

        result = divider-&amp;gt;next-&amp;gt;value;                        // C: copy it back
        choice = divider;
        InterlockedExchangePointer(&amp;amp;divider, divider-&amp;gt;next);  // D: publish that we took it : atomic divider = divider-&amp;gt;next;
        reinterpret_cast&amp;lt;Node*&amp;gt;(choice)-&amp;gt;next = NULL;
        return true;                                          // and report success
    }

    return false;                                             // else report empty
  }
};

#pragma warning(default:4312)
#pragma warning(default:4311)
&lt;/pre&gt;
&lt;p&gt;Next add a recycling class to avoid having to keep allocating and reallocating payload objects.  C++ sucks as a functional language (without all the heavyweight Boost machinery), so import the functions we need via static polymorphic constraints on templated types.&lt;/p&gt;
&lt;pre name="code" class="cpp"&gt;/*
            DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
                    Version 2, December 2004

 Copyright (C) 2004 Sam Hocevar
  14 rue de Plaisance, 75014 Paris, France
 Everyone is permitted to copy and distribute verbatim or modified
 copies of this license document, and changing it is allowed as long
 as the name is changed.

            DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. You just DO WHAT THE FUCK YOU WANT TO. */

/* This program is free software. It comes without any warranty, to
 * the extent permitted by applicable law. You can redistribute it
 * and/or modify it under the terms of the Do What The Fuck You Want
 * To Public License, Version 2, as published by Sam Hocevar. See
 * http://sam.zoy.org/wtfpl/COPYING for more details. */ 
#pragma once

#include "SimpleQueue.h"

template &amp;lt;typename T, typename TSource, typename TReceiver&amp;gt;
class ConveyorBelt {
    SimpleQueue&amp;lt;T&amp;gt; forward;
    SimpleQueue&amp;lt;T&amp;gt; reverse;

public:
    

    ConveyorBelt() {}
    ~ConveyorBelt() 
    {
        SimpleQueue&amp;lt;T&amp;gt;::Tptr t;
        while(forward.Consume(t))
        {
            delete t;
        }
        while(reverse.Consume(t))
        {
            delete t;
        }
    }

    void Accept(TSource &amp;amp; source)
    {
        SimpleQueue&amp;lt;T&amp;gt;::Tptr t;
        if(!reverse.Consume(t))
        {
            t = new T();
        }
        source.Set(t);
        forward.Produce(t);
    }

    bool Display(TReceiver &amp;amp; sink)
    {
        SimpleQueue&amp;lt;T&amp;gt;::Tptr t;
        if(!forward.Consume(t))
            return false;

        SimpleQueue&amp;lt;T&amp;gt;::Tcptr tconst = t;
        sink.Use(tconst);
        t-&amp;gt;Reset();


        reverse.Produce(t);
        return true;
    }
};
&lt;/pre&gt;
&lt;p&gt;All &lt;code&gt;T&lt;/code&gt; records are owned by the &lt;code&gt;ConveyorBelt&lt;/code&gt;, and we just loan them out to the &lt;code&gt;TSource&lt;/code&gt; and &lt;code&gt;TReceiver&lt;/code&gt; objects to write into or read from.  Once read, they are recycled back to the producing thread.&lt;/p&gt;
&lt;p&gt;A test harness looks like this -- &lt;/p&gt;
&lt;pre name="code" class="cpp"&gt;/*
            DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
                    Version 2, December 2004

 Copyright (C) 2004 Sam Hocevar
  14 rue de Plaisance, 75014 Paris, France
 Everyone is permitted to copy and distribute verbatim or modified
 copies of this license document, and changing it is allowed as long
 as the name is changed.

            DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. You just DO WHAT THE FUCK YOU WANT TO. */

/* This program is free software. It comes without any warranty, to
 * the extent permitted by applicable law. You can redistribute it
 * and/or modify it under the terms of the Do What The Fuck You Want
 * To Public License, Version 2, as published by Sam Hocevar. See
 * http://sam.zoy.org/wtfpl/COPYING for more details. */ 

#include "stdafx.h"
#include "SimpleQueue.h"
#include "ConveyorBelt.h"

using namespace System;
using namespace System::Threading;


class MarkedBuffer {
public:
    size_t start;
    size_t end;
    char data[16384];

    MarkedBuffer() : start(0), end(0) 
    { Reset(); }

    void Reset()
    { memset(data, 0, sizeof(data)); }
};

class Endpoints {
public:
    size_t source;
    size_t sink;

    Endpoints() : source(0), sink(0) {}
    void Set(MarkedBuffer * t)
    {
        t-&amp;gt;start = source;
    }
    void Use(MarkedBuffer const * t)
    {
        sink = t-&amp;gt;start;
    }
};


ref class Work
{
public:

   static ConveyorBelt&amp;lt;MarkedBuffer, Endpoints, Endpoints&amp;gt; * q2;
   static Endpoints * e;

   static void Produce2()
   {
      Console::WriteLine( "Producer thread procedure." );
       for(int i=0; i&amp;lt;32768; ++i)
       {
            e-&amp;gt;source = i;
            q2-&amp;gt;Accept(*e);
            if(0 == (i%100)) Thread::Sleep(200);
            else Thread::Sleep(0);
       }
      Console::WriteLine( "Producer thread finishhd." );
   }

   static void Consume2()
   {
      Console::WriteLine( "Consumer thread procedure." );
       int latest = -1;
       while(latest &amp;lt; 32767)
       {
           if(q2-&amp;gt;Display(*e))
            {
                 if(e-&amp;gt;sink != size_t(latest+1))
                 {
      Console::WriteLine( "Consumer thread procedure -- failure." );
                 }
                 latest = int(e-&amp;gt;sink);
            }
       }
      Console::WriteLine( "Consumer thread finished." );
   }
};


int main(array&amp;lt;System::String ^&amp;gt; ^)
{

     {
          ConveyorBelt&amp;lt;MarkedBuffer, Endpoints, Endpoints&amp;gt; queue;
          Endpoints e;

          Work::q2 = &amp;amp;queue;
          Work::e = &amp;amp;e;

          ThreadStart ^ producerDelegate = gcnew ThreadStart( &amp;amp;Work::Produce2 );
          Thread ^ producerThread = gcnew Thread( producerDelegate );
          ThreadStart ^ consumerDelegate = gcnew ThreadStart( &amp;amp;Work::Consume2 );
          Thread ^ consumerThread = gcnew Thread( consumerDelegate );


          producerThread-&amp;gt;Start();
          consumerThread-&amp;gt;Start();

          producerThread-&amp;gt;Join();
          consumerThread-&amp;gt;Join();
     }

      Console::WriteLine( "Completed." );

    return 0;
}
&lt;/pre&gt;
&lt;p&gt;Where the &lt;code&gt;MarkedBuffer&lt;/code&gt; type is representative of the uses I have made of such a queue inside a proxying application -- a packet read from the network into the data array or composed for writing to the network, with start and end offsets as marked; the data being conveyed between threads service client facing and server facing sockets.&lt;/p&gt;
&lt;p&gt;Using the &lt;code&gt;ConveyorBelt&lt;/code&gt; to carry data, allocations will take place until a steady state is reached, and may infrequently happen as spikes load the queue; all deallocations are deferred to the destruction of the system.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-7873536817432312642?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/7873536817432312642/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=7873536817432312642' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/7873536817432312642'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/7873536817432312642'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2009/07/lock-free-queue-revisited.html' title='The lock-free queue revisited'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-8002969770914091044</id><published>2009-07-22T21:20:00.007+01:00</published><updated>2009-07-23T07:29:47.273+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C/C++'/><category scheme='http://www.blogger.com/atom/ns#' term='concurrency'/><title type='text'>A simple LockFree Queue (C++, Windows)</title><content type='html'>&lt;p&gt;Prompted by some discussion on &lt;a href="http://www.stackoverflow.com/"&gt;StackOverflow&lt;/a&gt; today...&lt;/p&gt;
&lt;p&gt;This is the simple single-producer, single-consumer version of the Lock-Free Queue &lt;a href="http://ddj.com/cpp/210604448"&gt;as published by Herb Sutter in DDJ&lt;/a&gt;, but adapted to replace the C++0x style &lt;code&gt;atomic&amp;lt;Node*&amp;gt;&lt;/code&gt; with Win32 API calls to &lt;code&gt;InterlockedExchangePointer&lt;/code&gt; and &lt;code&gt;InterlockedCompareExchangePointer&lt;/code&gt;.  Converting the &lt;a href="http://www.ddj.com/architect/211601363"&gt;multiple producers/consumers case&lt;/a&gt; is left as a trivial exercise for the reader.&lt;/p&gt;
&lt;p&gt;So is doing the free-list manually, as a stack of &lt;code&gt;Node&lt;/code&gt;s rather than bringing in a gratuitous &lt;code&gt;std::list&lt;/code&gt;.&lt;/p&gt;

&lt;pre name="code" class="cpp"&gt;/*
            DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
                    Version 2, December 2004

 Copyright (C) 2004 Sam Hocevar
  14 rue de Plaisance, 75014 Paris, France
 Everyone is permitted to copy and distribute verbatim or modified
 copies of this license document, and changing it is allowed as long
 as the name is changed.

            DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. You just DO WHAT THE FUCK YOU WANT TO. */

/* This program is free software. It comes without any warranty, to
 * the extent permitted by applicable law. You can redistribute it
 * and/or modify it under the terms of the Do What The Fuck You Want
 * To Public License, Version 2, as published by Sam Hocevar. See
 * http://sam.zoy.org/wtfpl/COPYING for more details. */ 

#pragma once

// suppress 64-bit ready warning about casting on the win32 platform (only)
#pragma warning(disable:4311)
#pragma warning(disable:4312)

// After Sutter in Dr. Dobbs -- http://ddj.com/cpp/210604448?pgno=2
#include &amp;lt;list&amp;gt;
#include "Windows.h"

template &amp;lt;typename T&amp;gt;
class SimpleQueue {

    typedef T* Tptr;                                // should really be a smart_pointer&amp;lt;T&amp;gt;

private:
    SimpleQueue(const SimpleQueue &amp;amp;);               // Not copyable
     SimpleQueue &amp;amp; operator= (const SimpleQueue &amp;amp;); // Not assignable

  struct Node {
     Node( Tptr val ) : value(val), next(NULL) { }
    Node() : next(NULL) { value = NULL; }
    Tptr value;
    Node* next;
  };

  std::list&amp;lt;Node *&amp;gt; freeList;   // for producer only
  Node* first;                  // for producer only
  Node *divider, *last;         // shared -- Use explicit atomic compares only

  // Allocator/Deallocator for nodes -- 
  // only used in the producer thread
  // OR in the destructor.
  Node * Get(Tptr val)
  {
     if(!freeList.empty())
     {
        // Clean because of Release
        Node * next = freeList.front();
        freeList.pop_front();
        next-&amp;gt;value = val;
        return next;
     }

     // clean by construction
     return new Node(val);     
  }

  // Avoids costly free() while running
  void Release(Node * node)
  {
        // reset the node to clean before shelving it
        node-&amp;gt;value = NULL;
        node-&amp;gt;next = NULL;
        freeList.push_back(node);
  }


public:
  SimpleQueue() {
    first = divider = last = Get(NULL);                         // add dummy separator
  }

  ~SimpleQueue() {
    while( first != NULL ) {               // release the list
      Node* tmp = first;
      first = tmp-&amp;gt;next;
      delete tmp;
    }

     // Require -- Producer thread calls this or is dead
     while(!freeList.empty())
     {
          delete Get(NULL);
     }
  }

  // Produce is called on the producer thread only:
  void Produce( Tptr t ) {
    last-&amp;gt;next = Get(t);                            // add the new item
    InterlockedExchangePointer(&amp;amp;last, last-&amp;gt;next);  // publish it

    // Burn the consumed part of the queue
    for( PVOID looper = first;                     // non-null; pointer read is atomic
           InterlockedCompareExchangePointer(&amp;amp;looper, NULL, divider), looper;
           looper = first)
     {
      Node* tmp = first;
      first = first-&amp;gt;next;
      Release(tmp);
    }
  }

  // Consume is called on the consumer thread only:
  bool Consume( Tptr &amp;amp; result ) {

     PVOID choice = divider;                                  // non-null; pointer read is atomic
     InterlockedCompareExchangePointer(&amp;amp;choice, NULL, last);

     if(choice)
     {
        result = divider-&amp;gt;next-&amp;gt;value;                        // C: copy it back
        choice = divider;

        InterlockedExchangePointer(&amp;amp;divider, divider-&amp;gt;next);  // D: publish that we took it
        reinterpret_cast&amp;lt;Node*&amp;gt;(choice)-&amp;gt;next = NULL;
        return true;                                          // and report success
    }

    return false;                                             // else report empty
  }
};

#pragma warning(default:4312)
#pragma warning(default:4311)

&lt;/pre&gt;
&lt;p&gt;And a simple test harness using C++/CLI to make the threading simpler to write&lt;/p&gt;
&lt;pre name="code" class="cpp"&gt;/*
            DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
                    Version 2, December 2004

 Copyright (C) 2004 Sam Hocevar
  14 rue de Plaisance, 75014 Paris, France
 Everyone is permitted to copy and distribute verbatim or modified
 copies of this license document, and changing it is allowed as long
 as the name is changed.

            DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. You just DO WHAT THE FUCK YOU WANT TO. */

/* This program is free software. It comes without any warranty, to
 * the extent permitted by applicable law. You can redistribute it
 * and/or modify it under the terms of the Do What The Fuck You Want
 * To Public License, Version 2, as published by Sam Hocevar. See
 * http://sam.zoy.org/wtfpl/COPYING for more details. */ 

#include "stdafx.h"
#include "SimpleQueue.h"

using namespace System;
using namespace System::Threading;
ref class Work
{
public:
   static void Produce()
   {
      Console::WriteLine( "Producer thread procedure." );
       for(int i=0; i&amp;lt;32768; ++i)
       {
            int * data = reinterpret_cast&amp;lt;int*&amp;gt;(malloc(sizeof(int)));
            *data = i;
            q-&amp;gt;Produce(data);
            if(0 == (i%1000)) Thread::Sleep(20);
       }
      Console::WriteLine( "Producer thread finished." );
   }

   static void Consume()
   {
      Console::WriteLine( "Consumer thread procedure." );
       int latest = -1;
       while(latest &amp;lt; 32767)
       {
            int * data;
            if(q-&amp;gt;Consume(data))
            {
                 if(data[0] != latest+1)
                 {
      Console::WriteLine( "Consumer thread procedure -- failure." );
                 }
                 latest = data[0];
                 free(data);
            }
       }
      Console::WriteLine( "Consumer thread finished." );
   }

   static SimpleQueue&amp;lt;int&amp;gt; * q;

};


int main(array&amp;lt;System::String ^&amp;gt; ^)
{
     {
          SimpleQueue&amp;lt;int&amp;gt; queue;
          Work::q = &amp;amp;queue;

          ThreadStart ^ producerDelegate = gcnew ThreadStart( &amp;amp;Work::Produce );
          Thread ^ producerThread = gcnew Thread( producerDelegate );
          ThreadStart ^ consumerDelegate = gcnew ThreadStart( &amp;amp;Work::Consume );
          Thread ^ consumerThread = gcnew Thread( consumerDelegate );


          producerThread-&amp;gt;Start();
          consumerThread-&amp;gt;Start();
          producerThread-&amp;gt;Join();
          consumerThread-&amp;gt;Join();
     }

      Console::WriteLine( "Completed." );

    return 0;
}
&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-8002969770914091044?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/8002969770914091044/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=8002969770914091044' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/8002969770914091044'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/8002969770914091044'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2009/07/simple-lockfree-queue-c-windows.html' title='A simple LockFree Queue (C++, Windows)'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-2955752224161285855</id><published>2009-05-26T18:46:00.004+01:00</published><updated>2009-05-26T19:00:28.501+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Scala'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>The Jython/Scala stack revisited</title><content type='html'>&lt;p&gt;After &lt;a href="http://tinesware.blogspot.com/2008/09/scaling-tower-of-babel-ii.html"&gt;last year's experiments&lt;/a&gt;, another refinement in the process of getting the two languages to talk, this time from inside NetBeans.&lt;/p&gt;
&lt;p&gt;Last year, doing everything from the command line, I got across the language bridge by adding the scala jar to the &lt;span style="font-variant:small-caps;"&gt;classpath&lt;/span&gt;.  Inside NetBeans, you get a little way along the road by adding the scala-library jar and the jars from the scala layer of the stack to the project Python Path.  This will let you import types, and see that traits have the expected members.  Classes, however, appear as &lt;code&gt;None&lt;/code&gt; valued.&lt;/p&gt;
&lt;p&gt;For development purposes, you can, instead of adding the jars to the &lt;span style="font-variant:small-caps;"&gt;classpath&lt;/span&gt;, add them to &lt;code&gt;sys.path&lt;/code&gt; instead, like:&lt;/p&gt;
&lt;pre name="code" class="python"&gt;sys.path.append(r'C:\Users\Tines\.netbeans\6.5\scala\scala-2.7.3.final\lib\scala-library.jar')
sys.path.append(r'C:\Users\Tines\Documents\FTPStack\FTPlib\dist\FTPLib.jar')&lt;/pre&gt;
&lt;p&gt;For a real app, rather than hard-coding, these file paths could be supplied as arguments.&lt;/p&gt;
&lt;p&gt;Scala companion objects, with names ending in $ don't show up at all in the namespaces when imported into Jython, so anything static-like will have to be accessed through a throwaway class instance.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-2955752224161285855?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/2955752224161285855/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=2955752224161285855' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/2955752224161285855'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/2955752224161285855'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2009/05/jythonscala-stack-revisited.html' title='The Jython/Scala stack revisited'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-983387615631141127</id><published>2009-05-11T19:51:00.004+01:00</published><updated>2009-05-11T19:57:05.912+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Scala'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>Scala is not C#</title><content type='html'>&lt;p&gt;Took a while to debug what was going wrong here&lt;/p&gt;
&lt;pre name="code" class="scala"&gt;        val tmp = toUnsigned(leftSide.digits(i)) * toUnsigned(rightSide.digits(j)))
           +  toUnsigned(da(k)) + carry&lt;/pre&gt;
&lt;p&gt;because it compiles, silently discarding the fact that this is two statements, the second with no immediately apparent side-effects.&lt;/p&gt;
&lt;p&gt;It should be more like&lt;/p&gt;
&lt;pre name="code" class="scala"&gt;        val tmp = (
          (toUnsigned(leftSide.digits(i)) * toUnsigned(rightSide.digits(j))) +
          toUnsigned(da(k)) + carry)&lt;/pre&gt;
&lt;p&gt;where the parentheses and the trailing operators give a belt-and-braces approach to delineating the statement boundaries.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-983387615631141127?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/983387615631141127/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=983387615631141127' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/983387615631141127'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/983387615631141127'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2009/05/scala-is-not-c.html' title='Scala is not C#'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-7573383569279116516</id><published>2009-05-09T22:27:00.008+01:00</published><updated>2009-05-09T22:56:55.449+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Scala'/><category scheme='http://www.blogger.com/atom/ns#' term='erlang'/><title type='text'>Integrating Scala with Erlang -- Initial steps</title><content type='html'>&lt;p&gt;So, I've started a parallel implementation of jinterface 1.5 in Scala, the idea being&lt;/p&gt;
&lt;ol&gt;&lt;li&gt;To teach myself Scala by doing&lt;/li&gt;
&lt;li&gt;To experiment with the actor-based concurrency as a counterparty to the Erlang model&lt;/li&gt;
&lt;li&gt;To experiment with Scala as a dual VM technology&lt;/li&gt;&lt;/ol&gt;
&lt;p&gt;So far, just the term-representation classes, and the start of the Input/Decode and Output/Encode part; and some unit tests, only for Atom so far.&lt;/p&gt;
&lt;p&gt;I am impressed at how much I've been able to do without bringing in platform types that I will need to abstract out.  What was memory-based I/O in Java or C# becomes Seq[Byte] or Queue[Byte] as appropriate and basing all exceptions off the shared name Exception root class gets rid of any dependence on concrete subclasses.  It's only BigInt that is a known problem so far, where I'll need to factor out a platform abstraction later. Of course abstracting ZLib support is a task lurking in the near future, as well.&lt;/p&gt;
&lt;p&gt;The first noticeable difference from trying to tidy the C#2 port of an older Java version is that the functional style and rich library support make a lot of previously long-winded support methods significantly more expressive.  Contrast the Erlang list (of integer values) to String &lt;/p&gt;
&lt;pre name="code" class="csharp"&gt;public System.String StringValue
        {
            get
            {
                char[] body = new char[Arity];
                ReadOnlyCollection&amp;lt;IObject&amp;gt; e = Elements;
                for (int i = 0; i &amp;lt; Arity; ++i)
                {
                    Integer v = e[i] as Integer;
                    if (null == v) body[i] = '?';
                    else
                    {
                        System.Diagnostics.Debug.Assert(v != null &amp;&amp; v.Value != null);
                        char? c = v.Value.ToChar();
                        if (null == c) c = '?';
                        body[i] = (char)c;
                    }
                }
                return new string(body);
            }
        }
// and elsewhere
        public char? ToChar()
        {
            try
            {
                return (char)System.UInt16.Parse(this.ToString(), System.Globalization.NumberStyles.Integer, System.Globalization.CultureInfo.InvariantCulture);
            }
            catch (OverflowException)
            {
                return null;
            }
        }
&lt;/pre&gt;
&lt;p&gt;with&lt;/p&gt;
&lt;pre name="code" class="scala"&gt;def AsString =
  {
    (Elements map List.AsChar).mkString
  }
// and elsewhere
  def AsChar(obj : IObject) =
    obj match
    {
      case c : Integer if c.Value &amp;lt; (Integer.Zero + Char.MinValue.toInt) =&gt; '?'
      case c : Integer if c.Value &amp;gt;= (Integer.Zero + Char.MaxValue.toInt) =&gt; '?'
      case c : Integer =&gt; c.Value.intValue.toChar
      case _ =&gt; '?'
    }&lt;/pre&gt;
&lt;p&gt;Yes, using C#3's functional extensions, that too could be simplified; this is really the Java equivalent of doing that.&lt;/p&gt;
&lt;p&gt;Code drop on the MediaFire page.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-7573383569279116516?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/7573383569279116516/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=7573383569279116516' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/7573383569279116516'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/7573383569279116516'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2009/05/integrating-scala-with-erlang-initial.html' title='Integrating Scala with Erlang -- Initial steps'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-7374423324370059276</id><published>2009-04-27T21:43:00.008+01:00</published><updated>2009-05-04T23:05:56.708+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='erlang'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>ZLIB and .Net</title><content type='html'>&lt;p&gt;A recurring problem in several of the projects I have in the pipeline is the matter of handling ZLIB.  Java, through &lt;code&gt;java.util.zip&lt;/code&gt; offers ZLIB compression with a 32k byte window (but no means of tuning the window) with the &lt;code&gt;DeflaterOutputStream&lt;/code&gt;.  The .Net framework doesn't offer direct ZLIB at all, but provides naked Deflate via &lt;code&gt;System.IO.Compression.DeflateStream&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;That gives us enough to be able to reflate the output of a ZLIB deflation, since a ZLIB is a 2 byte header, a deflate section and finally a 4 byte checksum:

&lt;pre name="code" class="py"&gt;import clr    
clr.AddReference( 'vjslib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' )
from java.io import *
from java.util.zip import *
from System.IO import *
from System.IO.Compression import *
from System import Array, Byte, SByte, Int64
from System.Text import Encoding

def readFileToSByteArray(name):
  # open a file via Java I/O
  raw = RandomAccessFile(name, 'r')
  # print "File size in bytes = %d "  % (raw.length())

  # read into signed byte array
  dim = Array[Int64]([raw.length()])
  helper = Array[SByte]([0])
  buffer = Array.CreateInstance(helper[0].GetType(), dim)
  raw.readFully(buffer)
  raw.close()
  return buffer
  
def sbyteArrayToUbyteArray(buffer, offset=0, length=-1):
  if length &amp;lt; 0:
    length = buffer.Length
 
  dim = Array[Int64]([length])
  helper = Array[Byte]([0])
  ubuffer = Array.CreateInstance(helper[0].GetType(), dim)
  
  # copy into unsigned byte array
  for i in range(0,length):
    ubuffer[i] = buffer[i+offset] &amp; 0xff
  return ubuffer
  
def jdeflate(buffer):
  sink0 = ByteArrayOutputStream()
  sink = DeflaterOutputStream(sink0)
  sink.write(buffer, 0, buffer.Length)
  sink.close()
  sink0.close()
  return sink0.toByteArray()
  
def update_adler(adler, buffer):
  s1 = adler &amp; 0xffff
  s2 = (adler &amp;gt;&amp;gt; 16) &amp; 0xffff
  BASE = 65521
  for n in range(0,buffer.Length):
          s1 = (s1 + buffer[n]) % BASE
          s2 = (s2 + s1)     % BASE
  return (s2 &amp;lt;&amp;lt; 16) + s1
  
def ninflate(buffer):
  mem = MemoryStream(buffer)
  inflate = DeflateStream(mem, CompressionMode.Decompress)

  mem = MemoryStream()
  while True:
    x = inflate.ReadByte()
    if x &amp;lt; 0:
      break
    mem.WriteByte(x)
 
  inflate.Close()
  mem.Close()
  return mem.ToArray()
  
 
##==========================================

# select a file
name = r'..\..\sidereal.py'

# make signed, unsigend buffers and string
buffer = readFileToSByteArray(name)
print "Constructed buffer size %d" % (buffer.Length)
ubuffer = sbyteArrayToUbyteArray(buffer)
instring = Encoding.Default.GetString(ubuffer)

# compute the Adler32 checksum
adler = update_adler(1, ubuffer)
print "Adler32 = %d" % (adler)
 
# deflate in Java
deflated = jdeflate(buffer)

# check the ZLIB header -- expect 120, 156 actually (120,-100)
first = (deflated[0] &amp; 0xff)
second = (deflated[1] &amp; 0xff)
print "header value EXPECTED 120:156 ACTUAL %d:%d" % (first, second)

i = deflated.Length-4
x = ((deflated[i]&amp;0xff) &amp;lt;&amp;lt; 24) | ((deflated[i+1]&amp;0xff) &amp;lt;&amp;lt; 16) | ((deflated[i+2]&amp;0xff) &amp;lt;&amp;lt; 8) | (deflated[i+3]&amp;0xff)
print "Java Adler32 = %d" % (x)

# discard header and Adler32 tail
newbuffer = sbyteArrayToUbyteArray(deflated, 2, deflated.Length-6)
print "ZLIB length %d" % (deflated.Length)
print "deflate section length %d" % (newbuffer.Length)

# reflate
out = ninflate(newbuffer)

# compare with input
print "reflated buffer length : %d" % (out.Length)
outstring = Encoding.Default.GetString(out)
same = outstring.Equals(instring)
print "Output == Input ?? %s" %(str(same))&lt;/pre&gt;

&lt;p&gt;That works fine; but the converse, taking a .Net deflate and adding the appropriate top and tail took a bit of getting to work.&lt;/p&gt;
&lt;p&gt;First pitfall -- the &lt;code&gt;InflaterInputStream&lt;/code&gt; has to be read in chunks as large as feasible, rather than byte at a time, so as not to throw a premature &lt;code&gt;EOFException&lt;/code&gt;.  That overcome, I got a result of the right length, but differing in the final few characters for the file under test, which I resolved by doing a belt-and-braces closing of the deflate operation.  The un-refactored code I currently have continues from the above as:&lt;/p&gt;

&lt;pre name="code" class="py"&gt;# now the other way around
sink0 = MemoryStream()
sink = DeflateStream(sink0, CompressionMode.Compress)
sink.Write(ubuffer, 0, ubuffer.Length)
## overkill the flush and close here
sink.Flush()
sink.Close()
sink0.Flush()
sink0.Close()

deflated = sink0.ToArray()
print "deflate section length %d" % (deflated.Length)

#now inflate
dim = Array[Int64]([deflated.Length+6])
jbuffer = Array.CreateInstance(buffer[0].GetType(), dim)

jbuffer[0] = 120 # 0111 0100 = 120 : 32 kbit window, Deflate
jbuffer[1] = -100 # 10 0 ????? = 128 + x : default compress, no dict, checksum

def sbyte(x):
  y = x &amp; 0xff
  if y &amp;gt; 127:
    return y - 256
  return y
 
for i  in range(0,deflated.Length):
    jbuffer[i+2] = sbyte(deflated[i])
 
i = deflated.Length+2
jbuffer[i] = sbyte(adler&amp;gt;&amp;gt;24) 
jbuffer[i+1] = sbyte(adler&amp;gt;&amp;gt;16) 
jbuffer[i+2] = sbyte(adler&amp;gt;&amp;gt;8)
jbuffer[i+3] = sbyte( adler )
 
 
source0 = ByteArrayInputStream(jbuffer)
source = InflaterInputStream(source0)

dim = Array[Int64]([ubuffer.Length])
helper = Array[SByte]([0])
xbuffer = Array.CreateInstance(helper[0].GetType(), dim)

## take big bites
offset = 0
try:
 while True:
  x = source.read(xbuffer, offset, xbuffer.Length-offset)
  if x &amp;lt; 0:
    break
  offset += x
except EOFException:
  pass

reflated = sbyteArrayToUbyteArray(xbuffer)

print "reflated length is %d" % (reflated.Length)
outstring = Encoding.Default.GetString(reflated)

same = outstring.Equals(instring)
print same&lt;/pre&gt;
&lt;p&gt;This should allow me to simplify the baggage accumulated for the C#/Erlang bridge, which currently uses a second-generation port of the original 'C' ZLIB for this sort of interoperation.&lt;/p&gt;
&lt;p&gt;Later &amp;#8212; I have &lt;a href="http://www.mediafire.com/download.php?hcgm2nkmgre"&gt;updated the C#/Erlang bridge&lt;/a&gt; in the light of this.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-7374423324370059276?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/7374423324370059276/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=7374423324370059276' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/7374423324370059276'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/7374423324370059276'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2009/04/zlib-and-net.html' title='ZLIB and .Net'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-261252259417137484</id><published>2009-04-26T10:05:00.005+01:00</published><updated>2009-04-26T12:17:54.376+01:00</updated><title type='text'>End of an era</title><content type='html'>&lt;p&gt;After &lt;a href="http://help.yahoo.com/l/us/yahoo/geocities/geocities-05.html"&gt;the Yahoo! announcement&lt;/a&gt;, time to find another home for &lt;a href="http://uk.geocities.com/mr_tines/code.html" title="get them while they last"&gt;files I've stashed there&lt;/a&gt;, and I've gone for MediaFire -- link at the right, except for those on my Demon site&lt;/p&gt;

&lt;ul&gt;
&lt;li id="pyFtp"&gt;&lt;span style="font-weight:bold;"&gt;pyFtp.zip&lt;/span&gt; : IronPython FTP client rework &amp;mdash; far from complete, just a repository&lt;/li&gt;

  &lt;li&gt;&lt;a href="http://www.windsong.demon.co.uk/CTCJava2beta2.7z"&gt;CTCJava2beta2.7z&lt;/a&gt; &amp;mdash; 7zip
    archive&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://www.windsong.demon.co.uk/ctcjlib.2.3.2181.36359.zip"&gt;ctcjlib.2.3.2181.36359.zip&lt;/a&gt;
    &amp;mdash; last native DLL build&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://www.windsong.demon.co.uk/ctclib_231.zip"&gt;ctclib_231.zip&lt;/a&gt; &amp;mdash; last source
    code drop&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://www.windsong.demon.co.uk/crypt.zip"&gt;crypt.zip&lt;/a&gt; &amp;mdash; 
A complete replacement for the old crypt.zip, with source and pre built .jar and .dll (angerona.algorithms version 1.0.2915.29952), unit tests, and a .Net build of junit 3.8.2 -- includes:&lt;br /&gt;

&lt;ul&gt;&lt;li&gt;JZlib (inflate/deflate)&lt;/li&gt;&lt;li&gt;Blowfish, CAST5, DES (including s3' and key-dependent variants), IDEA, Safer, Square, TEA, ThreeWay, TripleDES (encryption)&lt;/li&gt;&lt;li&gt;BlockCypherHash, Haval, MD5, RIPEM160, SHA0, SHA1 (message digests); and&lt;/li&gt;&lt;li&gt;Bignum (mutable multiple precision integers).&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;Requires the next item if using the .Net version:&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style="font-weight:bold;"&gt;vjredist2nd.zip&lt;/span&gt; &amp;mdash; J# redistributables 2nd edition for .Net frameworks 2.0 and 3.0 (for use with Java/Ruby JVM/CLR hybrid code, digital extraction permitting.)&lt;/li&gt;
  &lt;li&gt;&lt;span style="font-weight:bold;"&gt;util.zip&lt;/span&gt; &amp;mdash; source for ancient Java
    utilities (1.0/1.1 timescale)&lt;/li&gt;
  &lt;li&gt;&lt;span style="font-weight:bold;"&gt;util_jar.zip&lt;/span&gt; &amp;mdash;JAR file of the above in a
  .Zip&lt;/li&gt;
  &lt;li&gt;&lt;span style="font-weight:bold;"&gt;utildocs.zip&lt;/span&gt; &amp;mdash; old-school
  Javadocs&lt;/li&gt;

&lt;li&gt;&lt;a href="http://www.windsong.demon.co.uk/angerona.redcoffee.zip"&gt;angerona.redcoffee.zip&lt;/a&gt; -- Current drop of &lt;a href="http://tinesware.blogspot.com/2007/11/smelling-coffee.html"&gt;JVM/CLR hybrid library&lt;/a&gt;.  Archive is 546,797 bytes bytes; MD5 
e235de70 113f0147 b1809ec0 df7823bc &amp; SHA-1 4d421c00 d4b3b45d be0244e3 649b19bd 04923a94.&lt;/li&gt;
&lt;li&gt;&lt;span style="font-weight:bold;"&gt;GWT 2.2 - 1&lt;/span&gt; -- A build of the old, old, &lt;span style="font-weight:bold;"&gt;gwt2_2.zip&lt;/span&gt; Gadget Windowing Toolkit (an LGPL'd Swing for Java 1.0 and 1.1, javadocs and such in the latter archive) for Java 5 and J# 2.0 (though it needs an auxiliary C#-based library).  Archive is 257,055 bytes; MD5 
7335e89a 171560f3 003f225e d97a3b3c &amp; SHA-1 3069e971 e75dfbf9 44324637 bf0d730b 97d6f2c9.&lt;/li&gt;
&lt;li id="jinterface14"&gt;&lt;span style="font-weight:bold;"&gt;jinterface.zip&lt;/span&gt; &amp;#8212; (MD5 : edf5f0e2 61c4282e 1a937d6d a34e5c79) Project files for building the Erlang jinterface 1.4 code under J#; plus the built .dll (MD5 : edf5f0e2 61c4282e 1a937d6d a34e5c79) version 1.4.3030.33797.  &lt;strong&gt;Needs the VJ# 2.0 distributables&lt;/strong&gt;&lt;/li&gt;
&lt;li id="erlang"&gt;&lt;span style="font-weight:bold;"&gt;erlang.zip&lt;/span&gt; &amp;#8212; (MD5 : d5baedd9 90157eb9 a217e51a bb5d2f12) -- My fork of the &lt;a href="http://sourceforge.net/cvs/?group_id=73688"&gt;Jungerl version of jinterface for C#&lt;/a&gt;, plus the jinterface 1.4 code and the above extensions for J# (code only), plus some initial unit tests for the Otp.Net fork.  The tests only so far deal with some of the utilities and the wire types used for decoding data encoded e.g. by jinterface, and converting back again, but are essentially sufficient for those. This now includes compression as well, with both the Erlang code and the Zlib port needed for compression having been scrubbed with FxCop. &lt;strong&gt;Needs the VJ# 2.0 distributables to run the tests, as well as NUnit 2.5 and Rhino Mock 3.4 as of 08-08-08.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-261252259417137484?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/261252259417137484/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=261252259417137484' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/261252259417137484'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/261252259417137484'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2009/04/end-of-era.html' title='End of an era'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-984776776959356856</id><published>2009-03-22T20:14:00.003Z</published><updated>2009-03-22T20:21:56.838Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Silverlight'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Belatedly, more IronPython + Silverlight</title><content type='html'>&lt;p&gt;Domestic happenings and the demands of the day-job have kept me away from the codeface for playtime for a while; but at last, &lt;a href="http://tinesware.blogspot.com/2008/12/more-silverlight-clock.html"&gt;following up from last year&lt;/a&gt;, a SilverLight2/IronPython2 orrery-clock.  Back-ported from F#, I confess, hence the possibly less than Pythonic idioms at times.&lt;/p&gt;
&lt;iframe src="http://www.stevegilham.com/silverlight/astroclock.html"
style="width:800px;height:600px;border:1px solid silver;" type="text/html"&gt;
    &lt;p&gt;&lt;a href="http://www.stevegilham.com/silverlight/astroclock.html"&gt;Silverlight example.&lt;/a&gt;&lt;/p&gt;&lt;/iframe&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-984776776959356856?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/984776776959356856/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=984776776959356856' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/984776776959356856'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/984776776959356856'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2009/03/belatedly-more-ironpython-silverlight.html' title='Belatedly, more IronPython + Silverlight'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-2133505774639391707</id><published>2009-01-11T20:48:00.008Z</published><updated>2009-01-12T21:57:53.206Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Scala'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>Python swinging both ways</title><content type='html'>&lt;p&gt;Following up to &lt;a href="http://tinesware.blogspot.com/2008/11/python-swings-ruby-still-doesnt.html"&gt;the last post I made&lt;/a&gt; in my quest for a cross-VM (JVM &lt;em&gt;and&lt;/em&gt; CLR), single code-base, polyglot language stack, which doesn't involve any more Java than can be helped.  Especially when there is Scala to play with, a language which a reading of &lt;a href="http://www.artima.com/shop/programming_in_scala"&gt;the stairway book&lt;/a&gt; shows is very nifty indeed.&lt;/p&gt;
&lt;p&gt;To summarise where we are, and what we have discovered to date --&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;Python can call Scala and Java happily&lt;/li&gt;
&lt;li&gt;Java calling into Python is funky and platform dependent; doubly so for Scala&lt;/li&gt;
&lt;li&gt;Scala cannot both call into java.* classes and compile on .net&lt;/li&gt;
&lt;li&gt;Low-level Scala pre-defs differ in the Byte type (signed in Java/J#/Scala-JVM, unsigned in Scala-msil)&lt;/li&gt;
&lt;li&gt;Ruby's predilection for Pascal-case namespaces (fudged in JRuby to match Java's lower-case convention) would require a lot of plumbing around in C# to mate IronRuby to J# or Scala-msil (as the rest of .net uses Pascal-casing).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And then there is this weekend's discovery. &lt;/p&gt;
&lt;p&gt;As J# is a barely JDK1.2 implementation at best, I can't use the latest Java splash-screen feature, and have to fall back on older implementations.  &lt;a href="http://www.randelshofer.ch/oop/javasplash/javasplash.html"&gt;I have one to hand&lt;/a&gt;, back from c. JDK 1.4 days.  It uses &lt;code&gt;synchronized()&lt;/code&gt;, &lt;code&gt;Object.wait()&lt;/code&gt; and &lt;code&gt;Object.notifyAll()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The first is easy enough to deal with when wanting to put as much as possible of this GUI-driving code into Python -- define an interface to hold a code block&lt;/p&gt;
&lt;pre name="code" class="java"&gt;public interface ISynchronizedAction
{
  void performAction();
}&lt;/pre&gt;
&lt;p&gt;and then have a class which knows about the object to synchronize on, and can execute the block in a synchronized scope&lt;/p&gt;
&lt;pre name="code" class="java"&gt;public class Synchronized {
  private Object lock;

  public Synchronized(Object lock) {this.lock = lock;}

  public void Lock(ISynchronizedAction action)
  {
    synchronized(lock) {action.performAction();}
  }
}&lt;/pre&gt;
&lt;p&gt;Except in IronPython, I also discover by dumping the &lt;code&gt;dir()&lt;/code&gt; of an object sub-classing &lt;code&gt;java.awt.Window&lt;/code&gt; -- inheritance being needed for the splash-screen window class rather than composition, in order to hook the update/repaint APIs -- that neither &lt;code&gt;Object.wait()&lt;/code&gt; nor &lt;code&gt;Object.notifyAll()&lt;/code&gt; are present to be called.  Fortunately the object I want these methods on is the same one I want to synchronize on, the splash-screen window itself, so I was able to just extend &lt;code&gt;Synchronized&lt;/code&gt; with&lt;/p&gt;
&lt;pre name="code" class="java"&gt;  public void delegateWait() throws InterruptedException
  {
    lock.wait();
  }

  public void delegateNotifyAll()
  {
    lock.notifyAll();
  }&lt;/pre&gt;
&lt;p&gt;and make the there-and-back-again calls as required.&lt;/p&gt;
&lt;p&gt;Then I just had to work around the older dialect's lack of support for .png &lt;del&gt;(by using a .gif)&lt;/del&gt; by using the handy Java-1.1 compatible &lt;a href="http://code.google.com/p/javapng/"&gt;javapng-1.3.0 library&lt;/a&gt; to actually paint the image into the splash screen.&lt;/p&gt;
&lt;p&gt;As usual, Jython needs the helper .jar file in the &lt;code&gt;CLASSPATH&lt;/code&gt;, while IronPython can reference the equivalent .dll from code by assembly file name.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-2133505774639391707?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/2133505774639391707/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=2133505774639391707' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/2133505774639391707'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/2133505774639391707'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2009/01/python-swinging-both-ways.html' title='Python swinging both ways'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-3430679598604312294</id><published>2009-01-10T19:37:00.005Z</published><updated>2009-01-10T19:44:49.479Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Scala'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>Towards Scala/.net 2.0</title><content type='html'>&lt;p&gt;A propos of &lt;a href="http://stevegilham.blogspot.com/2008/12/nice-idea-while-it-lasted.html"&gt;my last bit of banging head against a wall...&lt;/a&gt; Oh, and these snippets are definitely offered under &lt;a href="http://sam.zoy.org/wtfpl/"&gt;the WTFPL&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The main things that the scala-msil compilation balks at when looking at .Net assemblies post framework 1.x are generics; and things marked as &lt;code&gt;var&lt;/code&gt;. Looking at the source in &lt;a href="http://lampsvn.epfl.ch/svn-repos/scala/msil/trunk/src/"&gt;http://lampsvn.epfl.ch/svn-repos/scala/msil/trunk/src/&lt;/a&gt;, the first changes to make are, first in &lt;code&gt;msil/util/Signature.java&lt;/code&gt;, add the extra byte code values&lt;/p&gt;
&lt;pre name="code" class="java"&gt;    public static final int ELEMENT_TYPE_VAR = 0x13;     // a class type variable VAR
    /***/
    public static final int  ELEMENT_TYPE_GENERICINST    = 0x15;     // GENERICINST    ...&lt;/pre&gt;
&lt;p&gt;and then start to do something about parsing them in &lt;code&gt;msil/PEFile.java&lt;/code&gt;, in method &lt;code&gt;PEFile.Sig.decodeType0()&lt;/code&gt;&lt;/p&gt;
&lt;pre name="code" class="java"&gt;        case ELEMENT_TYPE_GENERICINST:
        // Followed by &amp;lt;type&amp;gt; token
        //ELEMENT_TYPE_GENERICINST &amp;lt;an mdTypeDef metadata token&amp;gt; &amp;lt;argument Count&amp;gt; &amp;lt;arg1&amp;gt; ... &amp;lt;argN&amp;gt;
        {
            // base type
            int tmp = readByte();
            if((tmp != ELEMENT_TYPE_VALUETYPE) &amp;&amp; (tmp != ELEMENT_TYPE_CLASS))
                throw new RuntimeException("*1*&amp;gt;&amp;gt; "+ byte2hex(tmp) +
        "@" + pos() + " in " + this);   
            int id = decodeInt();
            type = pemodule.getTypeDefOrRef(id);
            if (type == null)   throw new RuntimeException("**&amp;gt;&amp;gt; "+ byte2hex(desc) +
        "@" + pos() + " in " + this);
            // number of type arguments
            int num = decodeInt();
            System.out.println("num = "+num);
            for(int i=0;i&amp;lt;num;++i)
            {
                Type t = decodeType0();
                //TODO: use this information
            }
        }
        break;

        case ELEMENT_TYPE_VAR:
        {
            int index = decodeInt();
            System.out.println("var of type "+index);
            // TODO: actually look up what the index means 
            return Type.GetType("System.Object");
        }&lt;/pre&gt;
&lt;p&gt;This is necessary but not sufficient -- this still leaves problems with types involving templated subtypes or interfaces, for which the first TODO is probably meaningful.  As a consequence we're still a long way from a "hello .net 2.0 world!" example yet; let alone a rebuilt &lt;code&gt;scala-compiler.jar&lt;/code&gt;, alas.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-3430679598604312294?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/3430679598604312294/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=3430679598604312294' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/3430679598604312294'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/3430679598604312294'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2009/01/towards-scalanet-20.html' title='Towards Scala/.net 2.0'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-2884032861625833984</id><published>2008-12-16T20:35:00.002Z</published><updated>2008-12-16T20:45:57.725Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='Silverlight'/><title type='text'>More Silverlight Clock</title><content type='html'>&lt;p&gt;I could, I suppose, have implemented &lt;a href="http://www.stevegilham.com/silverlight/astroclock.html"&gt;this project&lt;/a&gt; in JavaScript -- that language has all the tools to get the current URL query string and set a permalink to a custom URL, one that wouldn't need clicking, but could be right-clicked; and save the self-inflicted DLR download hit.&lt;/p&gt;
&lt;p&gt;If I wanted to, I could put the permalink URL out to an element in the DOM with Silverlight, too, which negates one perceived advantage.  And using Silverlight gives me something extra -- multithreading.&lt;/p&gt;
&lt;p&gt;Yes, there are JavaScript clocks, like &lt;a href="http://rainbow.arch.scriptmania.com/scripts/mouse_clock.html"&gt;this annoying mouse-following one&lt;/a&gt;, but they have to fake such things because they are running full-time in the UI thread.  By using a &lt;code&gt;BackgroundWorker&lt;/code&gt; to prompt a redraw at intervals (and, later, perform updates on the positions of celestial bodies), with Silverlight, I can offload everything but the painting from the UI thread.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-2884032861625833984?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/2884032861625833984/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=2884032861625833984' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/2884032861625833984'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/2884032861625833984'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2008/12/more-silverlight-clock.html' title='More Silverlight Clock'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-2359118481180851862</id><published>2008-12-14T17:30:00.007Z</published><updated>2008-12-14T20:38:36.469Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Silverlight'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Persisting user settings in Silverlight</title><content type='html'>&lt;p&gt;Many years ago, the first GUI application I wrote (in X with Motif) was a little orrery-cum-clock thing, with the clock face doubling as a view of the sky, showing the classical planets.  Some time in the intermediate past, I thought about doing this as a Java applet, but it never got anywhere, because out on the web, it would have to be settable to the user's geographic location, and I never figured a good way to persist that data between page visits.&lt;/p&gt;
&lt;p&gt;Having done the &lt;a href="http://stevegilham.blogspot.com/2008/12/ironpython-20-rtw-silverlight-20.html"&gt;basic tinkering with IronPython and Silverlight to get the two working together&lt;/a&gt;, I thought about resurrecting the project.  And the first thing to solve is the settings persistence.&lt;/p&gt;
&lt;p&gt;But now, I have an easy API to get at the current URL and such via &lt;code&gt;System.Windows.Browser.HtmlPage.Document&lt;/code&gt; -- so I can read the values from its query string; and set the values in a hyperlink for bookmarking purposes, thus (app.py):&lt;/p&gt;
&lt;pre name="code" class="py"&gt;from System.Windows import Application
from System.Windows.Controls import Canvas
import System
import System.Windows.Browser

xaml = Application.Current.LoadRootVisual(Canvas(), "astroclock.xaml")
xaml.hyperlink.NavigateUri = System.Windows.Browser.HtmlPage.Document.DocumentUri
thisPage = System.Windows.Browser.HtmlPage.Document.DocumentUri.GetComponents(
  System.UriComponents.SchemeAndServer | System.UriComponents.Path,
  System.UriFormat.SafeUnescaped)
query = System.Windows.Browser.HtmlPage.Document.QueryString
  
try:
  xaml.slider1.Value = float(query['lat'])
except:
  pass
try:
  xaml.slider2.Value = float(query['long'])
except:
  pass
 
def latValueChanged(s, e):
  v = xaml.slider1.Value
  if v &amp;gt; 0:
 xaml.label1.Text = "Latitude %.2fN" % (v)
  elif v &amp;lt; 0:
 xaml.label1.Text = "Latitude %.2fS" % (-v)
  else: 
 xaml.label1.Text = "Latitude 0"
 xaml.hyperlink.NavigateUri = System.Uri("%s?lat=%f&amp;long=%f" % (thisPage, v, xaml.slider2.Value))

xaml.slider1.ValueChanged += latValueChanged
latValueChanged(None, None)

def longValueChanged(s, e):
  v = xaml.slider2.Value
  if v &amp;gt; 0:
 xaml.label2.Text = "Longitude %.2fE" % (v)
  elif v &amp;lt; 0:
 xaml.label2.Text = "Longitude %.2fW" % (-v)
  else:
 xaml.label2.Text = "Longitude 0"
 xaml.hyperlink.NavigateUri = System.Uri("%s?lat=%f&amp;long=%f" % (thisPage, xaml.slider1.Value, v))
 
xaml.slider2.ValueChanged += longValueChanged
longValueChanged(None, None)&lt;/pre&gt;
&lt;p&gt;driving some UI described by (astroclock.xaml)&lt;/p&gt;
&lt;pre name="code" class="xml"&gt;&amp;lt;Canvas 
  x:Class="System.Windows.Controls.Canvas"
  xmlns="http://schemas.microsoft.com/client/2007"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  x:Name="parentCanvas"&amp;gt;
    &amp;lt;Grid&amp;gt;
        &amp;lt;Canvas Name="canvas1" Margin="0,0,0,129" Background="Black" Width="480" Height="240"/&amp;gt;
        &amp;lt;Slider Height="22" Margin="128,0,0,93" Name="slider1" VerticalAlignment="Bottom" Maximum="90" Minimum="-90" Value="52" LargeChange="10" SmallChange="1" Width="360"/&amp;gt;
        &amp;lt;TextBlock Height="28" Margin="0,0,0,87" Name="label1" VerticalAlignment="Bottom" HorizontalAlignment="Left" Width="120"&amp;gt;Latitude&amp;lt;/TextBlock&amp;gt;
        &amp;lt;TextBlock Height="28" HorizontalAlignment="Left" Margin="0,0,0,62" Name="label2" VerticalAlignment="Bottom" Width="120"&amp;gt;Longitude&amp;lt;/TextBlock&amp;gt;
        &amp;lt;Slider Height="22" Margin="128,0,0,65" Name="slider2" VerticalAlignment="Bottom" Maximum="180" Minimum="-180" LargeChange="10" SmallChange="1" Width="360"/&amp;gt;
        &amp;lt;HyperlinkButton Height="28" HorizontalAlignment="Left" Margin="0,0,0,17" Name="hyperlink" VerticalAlignment="Bottom" Width="87" NavigateUri="astroclock.html" Content="Permalink" /&amp;gt;
    &amp;lt;/Grid&amp;gt;
&amp;lt;/Canvas&amp;gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href="http://www.stevegilham.com/silverlight/astroclock.html"&gt;Evolving implementation here&lt;/a&gt;.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-2359118481180851862?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/2359118481180851862/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=2359118481180851862' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/2359118481180851862'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/2359118481180851862'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2008/12/persisting-user-settings-in-silverlight.html' title='Persisting user settings in Silverlight'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-7582084971780789657</id><published>2008-12-02T21:54:00.008Z</published><updated>2008-12-04T20:35:19.492Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='web design'/><title type='text'>Syntax highlighting</title><content type='html'>&lt;p&gt;Following on from &lt;a href="http://www.hanselman.com/blog/BestCodeSyntaxHighlighterForSnippetsInYourBlog.aspx"&gt;Scott Hanselman's post&lt;/a&gt; about the &lt;a href="http://code.google.com/p/syntaxhighlighter/"&gt;SyntaxHighlighter script&lt;/a&gt;, here's the secret sauce I needed to get it to work.&lt;/p&gt;
&lt;p&gt;The separate hosting is the easy bit -- the trick was getting the code I needed to run the repainting into a window.onload, and to keep Blogger from mutilating the code by use of a CDATA section.&lt;p&gt;
&lt;p&gt;Et voil&amp;agrave;!&lt;/p&gt;

&lt;pre name="code" class="xml"&gt;&amp;lt;link href='.../SyntaxHighlighter.css' rel='stylesheet' type='text/css'/&amp;gt;
&amp;lt;script src='.../shCore.js' type='text/javascript'/&amp;gt;
&amp;lt;script src='.../shBrushPython.js' type='text/javascript'/&amp;gt;
&amp;lt;script src='.../shBrushRuby.js' type='text/javascript'/&amp;gt;
...
&amp;lt;script type='text/javascript'&amp;gt;//&amp;lt;![CDATA[
window.onload = function () {
    dp.SyntaxHighlighter.ClipboardSwf = ".../clipboard.swf";
    dp.SyntaxHighlighter.BloggerMode();
    dp.SyntaxHighlighter.HighlightAll("code",true,false,false,1,false);
}
//]]&amp;gt;&amp;lt;/script&amp;gt;&lt;/pre&gt;
&lt;p&gt;There is also &lt;a href="http://www.chrononaut.org/showyourwork/?p=31"&gt;a Scala brush&lt;/a&gt; for this -- but &lt;a href="http://stevegilham.blogspot.com/2008/12/syntax-highlight-brush-for-f.html"&gt;F#&lt;/a&gt; or &lt;a href="http://stevegilham.blogspot.com/2008/12/syntax-highlight-brush-for-erlang.html"&gt;Erlang&lt;/a&gt; will need to be hand-cranked first.&lt;/p&gt;
&lt;p&gt;Later: hand cranking done &amp; linked.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-7582084971780789657?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/7582084971780789657/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=7582084971780789657' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/7582084971780789657'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/7582084971780789657'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2008/12/syntax-highlighting.html' title='Syntax highlighting'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-7305104396442532729</id><published>2008-11-29T19:23:00.014Z</published><updated>2009-01-12T10:59:57.565Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Python Swings; Ruby still doesn't</title><content type='html'>&lt;p&gt;Here's a proof-of-concept of running a Swing UI from Python on the CLR and the JVM, &lt;a href="http://tinesware.blogspot.com/2008/01/pushing-at-open-door.html"&gt;following up on earlier experiments&lt;/a&gt; (which I had forgotten about).&lt;/p&gt;
&lt;pre name="code" class="python"&gt;try:  
  # IronPython must load the J# redistributable libraries  
  # Using the FullName of the assemblies is probably overkill  
  import clr  
  clr.AddReference( 'vjslib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' )  
  clr.AddReference( 'vjssupuilib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' )  
except ImportError:  
  pass  
     
import javax.swing  
import java.awt.event  
import java.lang  
  
class Exit(java.awt.event.WindowAdapter):  
  def windowClosing(self, evt):  
    evt.getWindow().setVisible(False)
    evt.getWindow().dispose()
  def windowClosed(self, evt):  
    java.lang.System.exit(0) 
    
# With the 'import's above, we can now refer to things that are part of the  
# standard Java platform via their full paths.  
frame = javax.swing.JFrame("Window") # Creating a Java JFrame.  
label = javax.swing.JLabel("Hello")  
     
# We can transparently call Java methods on Java objects, just as if they were defined in Python.  
frame.getContentPane().add(label)  # Invoking the Java method 'getContentPane'.  
frame.addWindowListener(Exit()) # Need to do this for IronPython/J# which is barely JDK 1.2 level  
frame.pack()  
frame.setVisible(True)&lt;/pre&gt;
&lt;p&gt;which works happily with &lt;code&gt;java -jar jython-complete.jar HelloSwing.py&lt;/code&gt; using Jython 2.5&amp;beta;0 and with &lt;code&gt;ipy.exe HelloSwing.py&lt;/code&gt; for IronPython2.0RC2 as well.&lt;/p&gt;
&lt;p&gt;I was briefly conflicted earlier this week when I saw that IronRuby had reached &amp;alpha;2, and wondered about a JRuby/IronRuby UI, but the nigh-equivalent&lt;/p&gt;
&lt;pre name="code" class="ruby"&gt;begin  
  require 'vjslib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'  
  require 'vjssupuilib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'  
rescue LoadError  
  # This is the 'magical Java require line'.  
  require 'java'  
end    
        
class Exit &amp;lt; java.awt.event.WindowAdapter
  def windowClosing evt
    evt.getWindow.setVisible(false)
    evt.getWindow.dispose # make Closed
  end
  def windowClosed evt
    java.lang.System.exit 0 
  end
end

# With the 'require' above, we can now refer to things that are part of the  
# standard Java platform via their full paths.  
frame = javax.swing.JFrame.new("Window") # Creating a Java JFrame.  
label = javax.swing.JLabel.new("Hello")  
     
# We can transparently call Java methods on Java objects, just as if they were defined in Ruby.  
frame.getContentPane.add(label)  # Invoking the Java method 'getContentPane'.  
frame.addWindowListener(Exit.new()) # Need to do this for IronRuby/J# which is barely JDK 1.2 level  
frame.pack  
frame.setVisible(true)  &lt;/pre&gt;
&lt;p&gt;which works on JRuby 1.1.5, fails in IronRuby &amp;alpha;2 when trying to load Swing classes, and I've not found a dodge that will get me at even class metadata for the Swing files.  Ruby's casing rules may make it impossible for IronRuby to recognise the lower-case Java namespaces (&lt;a href="http://tinesware.blogspot.com/2007/12/j-and-rubynet-string-and-baling-wire.html"&gt;I had fun with that earlier with Ruby.Net&lt;/a&gt;).
&lt;p&gt;LATER: I could do something VM dependent that takes the class name and argument list for construction in {Iron|J}Ruby.  What is more tricky is inheritance like&lt;/p&gt;
&lt;pre name="code" class="ruby"&gt;class Exit &amp;lt; java.awt.event.WindowAdapter&lt;/pre&gt;
&lt;p&gt;which is a major use-case.  I wonder if there might be a metaprogramming technique I can use for this...&lt;/p&gt;
&lt;p&gt;LATER YET: The problem is that there is no exposed mechanism to wrap a &lt;code&gt;System.Type&lt;/code&gt; as a Ruby &lt;code&gt;Class&lt;/code&gt;; I could manually subclass everything that is not &lt;code&gt;final&lt;/code&gt; that I want in a C# assembly with a &lt;code&gt;My.JavaNamespace&lt;/code&gt; and emulate the way that JRuby maps namespace &lt;code&gt;Java::JavaAwtEvent&lt;/code&gt; to a value named &lt;code&gt;java.awt.event&lt;/code&gt;, but the nearest to automation I can see is to &lt;code&gt;Reflection.Emit&lt;/code&gt; a whole ton of stuff.&lt;/p&gt;
&lt;p&gt;So, for the moment, consider it a dead end.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-7305104396442532729?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/7305104396442532729/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=7305104396442532729' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/7305104396442532729'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/7305104396442532729'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2008/11/python-swings-ruby-still-doesnt.html' title='Python Swings; Ruby still doesn&apos;t'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-6068448238594245731</id><published>2008-10-19T22:52:00.003+01:00</published><updated>2008-12-02T22:09:39.894Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Scala'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>Sign of the times -- Impedence mismatch of Scala and J#</title><content type='html'>&lt;p&gt;Following up on the &lt;a href="http://tinesware.blogspot.com/2008/09/scaling-tower-of-babel-ii.html"&gt;Scala/.Net experiments&lt;/a&gt;, I took the interfaces for hashes and cyphers from my Java port; and replaced them in the Java world with a set of Scala traits, like:&lt;/p&gt;
&lt;pre name="code" class="scala"&gt;trait MDA
{
    def update(data:Array[byte], offset:int, length:int): unit

    def update(data:byte) : unit

    def digest() : Array[byte] 
}&lt;/pre&gt;
&lt;p&gt;and it builds happily (though NetBeans 6.5 nightly complains it can't find the interface definition from the library in the code view).&lt;/p&gt;
&lt;p&gt;Over to Visual Studio, with a suitable script to build the equivalent .dll -- and the implementing classes in J# fail to build because the &lt;code&gt;Array[byte]&lt;/code&gt; maps to &lt;code&gt;ubyte[]&lt;/code&gt; while a J# &lt;code&gt;byte[]&lt;/code&gt; is an &lt;code&gt;sbyte[]&lt;/code&gt;, and so the J# compilation fails to match the signature of the Scala.Net trait -- and there's no Java &lt;code&gt;ubyte&lt;/code&gt; type I can force everything to.  And as byte arrays -- or at least bytes -- are the fundamental building block for streams, it looks like all the Java code has to get the elbow.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-6068448238594245731?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/6068448238594245731/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=6068448238594245731' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/6068448238594245731'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/6068448238594245731'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2008/10/sign-of-times-impedence-mismatch-of.html' title='Sign of the times -- Impedence mismatch of Scala and J#'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-1293052154865115954</id><published>2008-09-25T18:57:00.004+01:00</published><updated>2008-09-25T21:00:45.070+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Scala'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>Why am I doing this sort of thing?</title><content type='html'>&lt;p&gt;&lt;a href="http://ironpython-urls.blogspot.com/2008/09/assorted-snippets.html"&gt;Michael Foord (aka Fuzzyman) has wondered, while linking me&lt;/a&gt;, about why one would want to talk to Scala from IronPython.&lt;/p&gt;
&lt;p&gt;Partly, it is the intellectual challenge of that particular sub-problem.  But the broader goal is to see how far it is possible, in this polyglot age, to build a language stack -- &lt;a href="http://olabini.com/blog/2008/01/language-explorations/"&gt;along the lines the Ola Bini mused about at the start of the year&lt;/a&gt; -- that one could drop onto the JVM or .Net using &lt;span style="font-style:italic;"&gt;essentially the same code base&lt;/span&gt;, give or take a thin C# layer to provide compatible APIs in .Net to those needed in Java but not present in J#.&lt;/p&gt;
&lt;p&gt;While the &lt;a href="http://www.fandev.org/"&gt;Fan language&lt;/a&gt; is developed to be exactly so code-compatible across the JVM and CLR, it appears to be resolutely monoglot, with the platform hidden behind an abstraction layer.&lt;/p&gt;
&lt;p&gt;The ulterior motive is to take a codebase that I already have in Java/J#, the first stages of a reimplementation in a VM targeted language, of &lt;a href="http://www.geocities.com/mr_tines/ctclib.html"&gt;my old CTClib project&lt;/a&gt; -- and be able to complete the reimplementation/upgrade to &lt;a href="http://www.ietf.org/rfc/rfc2440.txt"&gt;RFC2440&lt;/a&gt; spec, in a platform neutral way, and without having to write any more Java, even if the IronPython/J#+Swing interface will be rather less Pythonic than I could manage if I were just doing it in Jython.&lt;p&gt;
&lt;p&gt;And why Scala as well? For any new static layer code, especially where being explicit about the types involved, such as around the crypto, will make things clearer.&lt;/p&gt;

&lt;p&gt;One correction, though, to the remarks : ATM, I'm using the scala-net compiler, which is part of the Scala bazaar, and seems (from its failure modes when the J# libraries are in the loop) to be a different generator to that in IKVM.  Not being IKVM means I'm also spared having to drag in the GNU Classpath library, where the server stuff has priority, and desktop UI support is still primitive -- its AWT is less functional, in many important respects, than Java 1.0 was.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-1293052154865115954?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/1293052154865115954/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=1293052154865115954' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/1293052154865115954'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/1293052154865115954'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2008/09/why-am-i-doing-this-sort-of-thing.html' title='Why am I doing this sort of thing?'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-2426418475324119456</id><published>2008-09-24T15:38:00.009+01:00</published><updated>2008-12-02T22:10:45.585Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Scala'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Scaling the tower of Babel II</title><content type='html'>&lt;p&gt;Finally, a little digital extraction.  Scala from IronPython, works as expected.  In file operator.scala:&lt;/p&gt;
&lt;pre name="code" class="scala"&gt;package com_ravnaandtines.operator

trait Process {
 def op(x:Int, y:Int) : Int
}

class Multiply extends Process {
  def op(x:Int, y:Int) : Int =
   x * y
}

class Perform69 {
 def sixByNine(x: Process) =
  x.op(6,9)
}&lt;/pre&gt;
&lt;p&gt;Set up as in the previous post, and build using &lt;code&gt;%SCALA_HOME%\bin\scalac-net.bat operator.scala&lt;/code&gt; to get &lt;code&gt;operator.msil&lt;/code&gt; -- &lt;strong&gt;no assembly is generated at this point&lt;/strong&gt;.  Edit the .msil file as required to point at the appropriate versions of the runtime (see previous post).  Then &lt;code&gt;ilasm .\Operator.msil /dll /output=operator.dll&lt;/code&gt;.  Now fire up IronPython:&lt;/p&gt;
&lt;pre name="code" class="python"&gt;&amp;gt;&amp;gt;&amp;gt; import clr
&amp;gt;&amp;gt;&amp;gt; clr.AddReference("operator.dll")
&amp;gt;&amp;gt;&amp;gt; from com_ravnaandtines.operator import *
&amp;gt;&amp;gt;&amp;gt; dir()
['Multiply', 'Perform69', 'Process', '__builtins__', '__doc__', '__name__', 'clr']
&amp;gt;&amp;gt;&amp;gt; m = Multiply()
&amp;gt;&amp;gt;&amp;gt; p69 = Perform69()
&amp;gt;&amp;gt;&amp;gt; p69.sixByNine(m)
54
&amp;gt;&amp;gt;&amp;gt; class Adder(Process):
...   def op(self,a,b):
...     return a+b
...
&amp;gt;&amp;gt;&amp;gt; add = Adder()
&amp;gt;&amp;gt;&amp;gt; p69.sixByNine(add)
15
&amp;gt;&amp;gt;&amp;gt;&lt;/pre&gt;
&lt;p&gt;So long as we get our interfaces/traits from Scala, all is fine.&lt;/p&gt;
&lt;p&gt;In order to do the same in the JVM world, I need to have done &lt;code&gt;set CLASSPATH=%CLASSPATH%;C:\scala-2.7.1.final\lib\scala-library.jar&lt;/code&gt;; and then I can do exactly as above, but without the first two lines with &lt;code&gt;clr&lt;/code&gt;.  Those I can put in a &lt;code&gt;try/except&lt;/code&gt;, for common coding.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; -- it is neither necessary nor sufficient to put the Scala library jar in &lt;code&gt;java.class.path&lt;/code&gt; or &lt;code&gt;python.path&lt;/code&gt; properties.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-2426418475324119456?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/2426418475324119456/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=2426418475324119456' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/2426418475324119456'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/2426418475324119456'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2008/09/scaling-tower-of-babel-ii.html' title='Scaling the tower of Babel II'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-746867782368023189</id><published>2008-08-23T20:04:00.011+01:00</published><updated>2008-12-03T13:53:20.487Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Scala'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>Scaling the tower of Babel</title><content type='html'>&lt;p&gt;&lt;a href="http://tinesware.blogspot.com/2008/02/under-my-nose-all-time.html"&gt;A few months ago&lt;/a&gt;, I was playing around with seeing how I could adapt an existing Java code base so that I could finish doing the work in better languages -- and have the code work on both the JVM and the CLR.&lt;/p&gt;
&lt;p&gt;It seems that frustration will inevitably follow any attempt to incorporate any J# into the .Net versions of the stack (both IKVM and the Scala-net compilers bork when they see another Java in the stack) -- even though you can call Jython into Scala into Java on the JVM, this Tower of Babel cannot stand on the CLR with the J# runtime in the picture -- Java e.g. a Swing-based GUI using the J# extensions, has to stand off to one side.&lt;/p&gt;
&lt;p&gt;That said, the Scala on .Net "hello world" with Scala 2.7.1 is comparatively painless.&lt;/p&gt;
&lt;ol&gt;&lt;li&gt;Install the scala-msil package (&lt;code&gt;sbaz install scala-msil&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Create file &lt;code&gt;Hello.scala&lt;/code&gt; as&lt;br /&gt;
&lt;pre name="code" class="scala"&gt;import System.Console

object test extends Application {
  Console.WriteLine("Hello world!")
}&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;In a Visual Studio command prompt, issue &lt;code&gt;%SCALA_HOME%\bin\scalac-net.bat hello.scala&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Copy &lt;code&gt;%SCALA_HOME%\lib\*.dll&lt;/code&gt; to the current directory&lt;/li&gt;
&lt;li&gt;Run the resulting &lt;code&gt;test.exe&lt;/code&gt;&lt;/li&gt;&lt;/ol&gt;
&lt;p&gt;This uses the 1.0 version of &lt;code&gt;mscorlib.dll&lt;/code&gt;; as a post-processing step you may wish to re-base against the .Net 2.0 version -- for this, you need to rebuild &lt;code&gt;predef.dll&lt;/code&gt; and &lt;code&gt;scalaruntime.dll&lt;/code&gt; using the same technique as needed to &lt;a href="http://rant.blackapache.net/2008/07/09/net-fu-signing-an-unsigned-assembly-without-delay-signing/"&gt;strong-name them after the fact&lt;/a&gt;; only this time you want to reset the &lt;code&gt;mscorlib&lt;/code&gt; reference from&lt;bre /&gt;
&lt;pre name="code" class="csharp"&gt;.assembly extern mscorlib
{
  .publickeytoken = (B7 7A 5C 56 19 34 E0 89 )                         // .z\V.4..
  .ver 1:0:5000:0
}&lt;/pre&gt;&lt;bre /&gt;
to
&lt;pre name="code" class="csharp"&gt;.assembly extern mscorlib
{
  .publickeytoken = (B7 7A 5C 56 19 34 E0 89 )                         // .z\V.4..
  .ver 2:0:0:0
}&lt;/pre&gt;&lt;br /&gt;
as well as doing any strong-naming you may desire.&lt;/p&gt;
&lt;p&gt;To set the assembly name, add &lt;code&gt;-Xassem [assembly name]&lt;/code&gt; to the compile command, to generate &lt;code&gt;hello.exe&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To control the type of the generated assembly (&lt;code&gt;.dll&lt;/code&gt; rather than &lt;code&gt;.exe&lt;/code&gt;), I've not found a way other than taking the generated &lt;code&gt;[assembly name].msil&lt;/code&gt; and &lt;code&gt;ilasm&lt;/code&gt;ing it manually.  In particular trying to skip generating the unwanted &lt;code&gt;.exe&lt;/code&gt; will mean figuring why using &lt;code&gt;scalac.bat -target:msil&lt;/code&gt; always gives&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;error: fatal error: class scala.runtime.BoxedBooleanArray not found.&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;whatever I do with &lt;code&gt;-Xassem-path&lt;/code&gt; or tweaking environment variables.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-746867782368023189?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/746867782368023189/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=746867782368023189' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/746867782368023189'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/746867782368023189'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2008/08/scaling-tower-of-bable.html' title='Scaling the tower of Babel'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-567113609070771699</id><published>2008-08-08T20:54:00.003+01:00</published><updated>2008-12-03T13:54:33.570Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='erlang'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>Integrating .Net with Erlang, part 8</title><content type='html'>&lt;p&gt;Gratuitous update on 08/08/08 with a small increment of unit testing -- now using &lt;a href="http://ayende.com/projects/rhino-mocks.aspx"&gt;Rhino Mocks 3.4&lt;/a&gt; as well as home-grown ones, and &lt;a href="http://www.nunit.com/"&gt;NUnit 2.5 (alpha 3)&lt;/a&gt; for the neat &lt;pre name="code" class="csharp"&gt;Assert.That( [delegate expression], Throws.Exception&amp;lt;[type]&amp;gt;())&lt;/pre&gt; syntax.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://uk.geocities.com/mr_tines/code.html#erlang"&gt;Drop here, as usual&lt;/a&gt;.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-567113609070771699?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/567113609070771699/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=567113609070771699' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/567113609070771699'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/567113609070771699'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2008/08/integrating-net-with-erlang-part-8.html' title='Integrating .Net with Erlang, part 8'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-1300974801697157110</id><published>2008-06-07T11:03:00.007+01:00</published><updated>2008-12-03T14:45:14.201Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='erlang'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Erlang and Python</title><content type='html'>&lt;p&gt;Following the &lt;a href="http://tinesware.blogspot.com/2008/06/erlang-with-python-and-ruby.html#c3362561284353957479"&gt;suggestion that I try TwOtp&lt;/a&gt;, I've tried the "kicking the tyres" with that library.&lt;/p&gt;
&lt;p&gt;Adapting the sample code to work against my own local node seemed easy enough:&lt;/p&gt;
&lt;pre name="code" class="py"&gt;from twisted.internet import reactor
from erlang import OneShotPortMapperFactory, buildNodeName

def gotConnection(inst):
    return inst.factory.callRemote(inst, "file", "get_cwd"
        ).addCallback(gotResult)

def gotResult(resp):
    print "Got response", resp
    reactor.stop()

def eb(error):
    print "Got error", error
    reactor.stop()

epmd = OneShotPortMapperFactory('client@chloe.ravnaandtines.com', "cookie")
epmd.connectToNode("servernode@chloe.ravnaandtines.com").addCallback(gotConnection).addErrback(eb)
reactor.run()&lt;/pre&gt;
&lt;p&gt;which involves rather more baggage than using IronPython against my tidying of the C# port of jinterface, for just a "hello world!", given that the jinterface-derived "hello world!" actually does more work (passing arguments, testing results):&lt;/p&gt;
&lt;pre name="code" class="py"&gt;import clr
clr.AddReference("OtpErlang.dll")
from System import Array

from Tinesware.Otp import *
import Tinesware.Otp.Erlang as Erlang

cNode = Self("clientnode@chloe.ravnaandtines.com", "cookie")
sNode = Peer("servernode@chloe.ravnaandtines.com")
connection = cNode.Connect(sNode)

args = Array[Erlang.IObject]([Erlang.Integer(1), Erlang.Integer(2)])

connection.SendRpc("mathserver", "add", args)
sum = connection.ReceiveRpc()

if  sum.Value.ToInt32() != 3:
  print "Assertion failed, returned = " + sum.Value
else:
  print "OK!"&lt;/pre&gt;
&lt;p&gt;And, like py_interface, I find that this doesn't "just work" either:&lt;/p&gt;
&lt;pre class="code"&gt;C:\Documents and Settings\Steve\My Documents\code\babel\erlang&gt;python twotphw.py

Got error [Failure instance: Traceback (failure with no frames): &amp;lt;class 'erlang.epmd.NodeNotFound'&amp;gt;: 1]&lt;/pre&gt;
&lt;p&gt;which will require a similar amount of rolling sleeves up and getting hands dirty to resolve.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-1300974801697157110?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/1300974801697157110/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=1300974801697157110' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/1300974801697157110'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/1300974801697157110'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2008/06/erlang-and-python.html' title='Erlang and Python'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-2774087059484606428</id><published>2008-06-06T21:19:00.006+01:00</published><updated>2008-12-03T14:44:09.058Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='erlang'/><category scheme='http://www.blogger.com/atom/ns#' term='Ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Erlang with Python and Ruby</title><content type='html'>&lt;p&gt;Having played a bit with &lt;a href="http://stevegilham.blogspot.com/2008/05/erlang-is-interesting.html"&gt;Erlang for real&lt;/a&gt;, in the sense of something non-trivial, even if not a complete product, I find I'm in the mood for more.  But while it comes with some UI widgets, it's something more for headless use, and some client(s) in other languages doing the display.&lt;/p&gt;
&lt;p&gt;To date, I can easily do Swing or WinForms as the UI toolkit -- but what about wxPython or FXRuby?&lt;/p&gt;
&lt;p&gt;There are a number of bridges that I've found, but the closest to jinterface seem to be:&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.lysator.liu.se/~tab/erlang/py_interface/"&gt;py_interface&lt;/a&gt; for Python (about 2 years old)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://code.google.com/p/a2800276/source/browse/trunk/ruby/erlang/lib"&gt;Un-named code drop on Google&lt;/a&gt; for Ruby (about 1 year old)&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;Neither of them have "just worked" for me in the way that the Java and C# versions have done against the simple mathserver example.  Some of it might be down to my not figuring how the differences in the APIs are meant to work.&lt;/p&gt;
&lt;p&gt;For py_interface, I had to comment out the line &lt;pre name="code" class="py"&gt;[__import__(item) for item in __all__]&lt;/pre&gt; from &lt;code&gt;init.py&lt;/code&gt; to get imports to work at all; and even then, the simple &lt;/p&gt;
&lt;pre name="code" class="py"&gt;from otp import *

def Handler(Result):
    print Result

cNode = erl_node.ErlNode("clientnode@chloe.ravnaandtines.com", erl_opts.ErlNodeOpts(cookie="cookie"))
print cNode
print cNode.Publish()
mBox = cNode.CreateMBox()
print mBox
mBox.SendRPC("servernode@chloe.ravnaandtines.com", 
    "mathserver", "add", [1, 2], Handler)
raw_input("wait")&lt;/pre&gt;
&lt;p&gt;just sits there; while the Ruby code&lt;/p&gt;
&lt;pre name="code" class="ruby"&gt;require 'erlang_node'
 
s_node = Erlang::Node.new("servernode@chloe.ravnaandtines.com")
c_node = Erlang::LocalNode.new("clientnode@chloe.ravnaandtines.com", "cookie")
connection = Erlang::Connection.new(s_node, c_node)
connection.sendRPC("mathserver", "add", [1,2])
sum = connection.receiveRPC()
puts "Fail" unless sum == 3&lt;/pre&gt; 
&lt;p&gt;throws a different exception every time I run it, from some place or other inside the connection initializer.&lt;/p&gt;
&lt;p&gt;So they both need deeper inspection than this simple kicking of the tyres, maybe modifying for R12 nodes, maybe just reverse engineering the kinks out of the driver programs.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-2774087059484606428?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/2774087059484606428/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=2774087059484606428' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/2774087059484606428'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/2774087059484606428'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2008/06/erlang-with-python-and-ruby.html' title='Erlang with Python and Ruby'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-851865537344502613</id><published>2008-05-27T18:24:00.005+01:00</published><updated>2008-05-27T18:37:05.547+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='erlang'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>Integrating .Net with Erlang, part 7</title><content type='html'>&lt;p&gt;&lt;a href="http://uk.geocities.com/mr_tines/code.html#erlang"&gt;Another update&lt;/a&gt;, after work during the holiday weekend.&lt;/p&gt;
&lt;p&gt;By using a lot of spare cycles, I found a test case for the division code not previously exercised:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;9659964175213286916418497243082984930780677040655778513759952405592597918851617939992802011783376153721017703858175 / 3248063261698017931917474008545947342556579729240422363527 = 2974068974926080862795323621370295193198999568425344221053&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Also made the astoundingly obvious discovery that I needed the real Erlang port mapper d&amp;aelig;mon running, so put a unit test in which spawns an Erlang process to contact.  It works fine in NUnit-gui, but fails every time in the build time coverage plus test cycle, so is labelled as &lt;code&gt;[Explicit]&lt;/code&gt;.  I need to to write at least one test that handles the call in the other direction, but this is getting close to as far as can easily be done in unit test form.&lt;/p&gt;
&lt;p&gt;And it's getting close to my usual summer break from coding in the evenings, so this is probably close to being "it" for a while.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-851865537344502613?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/851865537344502613/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=851865537344502613' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/851865537344502613'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/851865537344502613'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2008/05/integrating-net-with-erlang-part-7.html' title='Integrating .Net with Erlang, part 7'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-1544096380773345961</id><published>2008-05-20T19:46:00.005+01:00</published><updated>2008-05-20T21:44:40.694+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='erlang'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>Integrating .Net with Erlang, part 6</title><content type='html'>&lt;p&gt;Time for &lt;a href="http://uk.geocities.com/mr_tines/code.html#erlang"&gt;a major drop of the code&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;It's been tidied up to remove most of the deprecation and to start unifying the comments as .Net XML.  The &lt;a href="http://www.codeplex.com/biginteger"&gt;&lt;code&gt;BigInteger&lt;/code&gt; class&lt;/a&gt; has had &lt;a href="http://tinesware.blogspot.com/2008/04/integrating-net-with-erlang-part-5.html"&gt;a number of bugs&lt;/a&gt; found and fixed by unit testing (initialization, big left shifts, hex-string), and been made into a &lt;code&gt;struct&lt;/code&gt; to reflect its value nature.&lt;/p&gt;
&lt;p&gt;Pretty much all (say, 80%) of the stuff that can be tested statically in isolation has been, and the code has been revised with dependency injection and interfaces to do that (including a bug-fixed, back ported (to C# 2.0) version of &lt;code&gt;&lt;a href="http://www.paulstovell.com/blog/nullobjectfor"&gt;NullObject.For&amp;lt;T&amp;gt;&lt;/a&gt;&lt;/code&gt;).  Further testing will require some more extensive test set-up, such as spinning up a Java peer in a separate thread and talking TCP with it.&lt;/p&gt;
&lt;p&gt;Code coverage is currently just over 60% for the Otp library, just over 70% for the ZLib code, for the static testing; beyond upgrading the wire types and the constants, the upgrade to 1.4 spec from 1.2 is not really begun.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-1544096380773345961?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/1544096380773345961/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=1544096380773345961' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/1544096380773345961'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/1544096380773345961'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2008/05/integrating-net-with-erlang-part-6.html' title='Integrating .Net with Erlang, part 6'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-5030665377146247670</id><published>2008-04-23T20:30:00.011+01:00</published><updated>2008-12-03T14:42:30.921Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='erlang'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>Integrating .Net with Erlang, part 5</title><content type='html'>&lt;p&gt;I have a first drop that has all the C# input code written, and a start of some unit tests for interoperability with the J# build of jinterface 1.4.  Nowhere near enough tests are written yet&lt;span style="text-decoration:line-through;"&gt;, and a couple of those that have been (encoding a 64-bit integer in J# and unpacking in C#) don't yet work&lt;/span&gt;.  But at least it's a start.&lt;/p&gt;
&lt;p&gt;Still to do are the full gamut of integer types, and the complex ones -- lists, tuples, ports and such, then replicate for writing by C# and reading in J#.&lt;/p&gt;
&lt;p&gt;One fix to the tests was for my code -- I needed to take the surprising little-endian wire format for an extended integer, and reverse it to fit into the BigInteger class I got &lt;a href="http://codeplex.com/biginteger"&gt;from Codeplex&lt;/a&gt;; the second was a fix to that class:&lt;/p&gt;
&lt;pre name="code" class="csharp"&gt;
381c381,384
&lt;    
---
&gt; 
&gt;             // BUGFIX -- use an accumulator rather than |=ing into
&gt;             // m_digits[m_digits.DataUsed], which will be zero
&gt;             DType accumulator = 0;
384c387,389
&lt;     m_digits[m_digits.DataUsed] |= (DType)(array[offset + leftOver - i] &lt;&lt; ((i - 1) * 8));
---
&gt;                 DType digit = array[offset + leftOver - i];
&gt;                 digit = (digit &lt;&lt; ((i - 1) * 8));
&gt;                 accumulator |= digit;
385a391
&gt;             m_digits[m_digits.DataUsed] = accumulator;
&lt;/pre&gt;
&lt;p&gt;Code drop for the curious &lt;a href="http://uk.geocities.com/mr_tines/code.html#erlang"&gt;at the usual site&lt;/a&gt;.  The project assumes you have NCover and NUnit around your system to run the tests and take coverage stats.&lt;/p&gt;
&lt;p&gt;Update: More tests, and a few more fixes, refactorings and extensions added 24-Apr.  Doubles now get encoded in the new style and integers that fall into the Int64 range are handled so the jinterface code can handle them.&lt;/p&gt;
&lt;p&gt;Update: 26-Apr -- Added an export to little-endian byte array for BigInteger and added unit tests for short and long BigInteger representations.&lt;/p&gt;
&lt;p&gt;Update: 27-Apr -- A first pass of Java type to C# and back for all the Java wire types, and a start of interface breaking changes to shut FxCop up a bit.  Still needs a lot of like-for-like based unit tests, and detailed code cleaning.&lt;/p&gt;
&lt;p&gt;Update: 28-Apr -- Unit tests about complete for all the wire types.  Now the fun really begins.&lt;/p&gt;
&lt;p&gt;Update: 4-May -- Unit tests extended to the input and output classes, including adding compression via a modified version of the ComponentAce port JZlib.  All code now clean after FxCop scrubbing.  Still needs all the node related classes being compared with the jinterface 1.4 versions and tested.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-5030665377146247670?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/5030665377146247670/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=5030665377146247670' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/5030665377146247670'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/5030665377146247670'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2008/04/integrating-net-with-erlang-part-5.html' title='Integrating .Net with Erlang, part 5'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-5987207146973042716</id><published>2008-04-22T23:01:00.004+01:00</published><updated>2008-12-03T14:41:33.864Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='erlang'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>Integrating .Net with Erlang, part 4</title><content type='html'>&lt;p&gt;So where are we going from here with the Otp.Net code?  First off, if we bring the codebase into .Net 2.0 from its '04-'06 vintage, and make all the data classes in the &lt;code&gt;Otp.Erlang&lt;/code&gt; namespace immutable -- returning &lt;code&gt;ReadOnlyConnection(of T)&lt;/code&gt;rather than arrays as needed -- we can get rid of the &lt;code&gt;ICloneable&lt;/code&gt; support.  That lets us make &lt;code&gt;Otp.Erlang.Object&lt;/code&gt; into an interface containing just the &lt;code&gt;encode&lt;/code&gt; method(dropping the &lt;code&gt;decode&lt;/code&gt; method, or making it an extension method as per C# 3.0).&lt;/p&gt;
&lt;p&gt;Then we can use some subset of the fields used in equality tests to do &lt;code&gt;GetHashCode&lt;/code&gt; properly, and replace arrays with growable collections.&lt;/p&gt;
&lt;p&gt;Next, fix the range check on &lt;code&gt;Double&lt;/code&gt;'s &lt;code&gt;floatValue&lt;/code&gt; method, and make it return a nullable value:&lt;/p&gt;
&lt;pre name="code" class="csharp"&gt;    public float? floatValue()
    {
        if (d &gt; Single.MaxValue || d &lt; Single.MinValue)
            return null;

        return (float) d;
    }&lt;/pre&gt;
&lt;p&gt;and rename &lt;code&gt;Long&lt;/code&gt; as &lt;code&gt;Integer&lt;/code&gt;, kill both their derived classes and make &lt;code&gt;Integer&lt;/code&gt; carry a &lt;code&gt;BigInteger&lt;/code&gt; to which we can delegate conversions returning nullables.&lt;/p&gt;
&lt;p&gt;That will mean some fixing up of input and output, but we need that anyway to handle new-style float values encoded as 8-byte big-endian blobs (and the change of tense means that this is where we move past what I've done so far).&lt;/p&gt;
&lt;p&gt;To come after that will be making &lt;code&gt;GenericQueue&lt;/code&gt; delegate to &lt;code&gt;Queue(of IObject)&lt;/code&gt;, and then a general scrub done in comparison with jinterface 1.4, before it will be sorta-releasable.&lt;/p&gt;
&lt;p&gt;At the moment it builds, but will not work, so no interim archive is yet publicly available.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-5987207146973042716?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/5987207146973042716/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=5987207146973042716' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/5987207146973042716'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/5987207146973042716'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2008/04/integrating-net-with-erlang-part-4.html' title='Integrating .Net with Erlang, part 4'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-6622284031181827246</id><published>2008-04-19T23:36:00.008+01:00</published><updated>2008-12-03T14:40:59.642Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='erlang'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>Integrating .Net with Erlang, part 3</title><content type='html'>&lt;p&gt;A kind anonymous &lt;a href="http://tinesware.blogspot.com/2008/04/integrating-net-with-erlang-part-1.html#comments"&gt;has pointed out&lt;/a&gt; that someone has already done the job of porting an earlier version (1.2.1.1) of the jinterface code, even though it has not made its way into the standard distro.  Via &lt;a href="http://weblogs.asp.net/nleghari/archive/2008/01/08/integrating-net-and-erlang-using-otp-net.aspx"&gt;this blog post&lt;/a&gt;, one eventually gets to the &lt;a href="http://sourceforge.net/cvs/?group_id=73688"&gt;sourceforge repository details&lt;/a&gt; for it.&lt;/p&gt;
&lt;p&gt;This actually does work with IronPython (after the trivial edits to load the appropriate assembly and the slightly different class names) and without the J# runtime being involved.&lt;/p&gt;
&lt;p&gt;I spotted the comment after spending a little while porting the 1.4 code.  Actually the underlying Java code is quite horrid -- to start with there are lots of methods with the same name as fields; and a lot of identically named fields shadowing ones elsewhere in the inheritance hierarchy; and methods and fields are used interchangeably.  Then there are blocks of cut-and-paste and tweak code; plenty of swallowing of exceptions and blurred responsibilities (where one object performs a series of manipulations on a member of another object).  And there there is this (where d is a double):&lt;/p&gt;
&lt;pre name="code" class="java"&gt;  public float floatValue()
    throws OtpErlangRangeException {
    float f = (float)d;

    if (f != d) {
      throw new OtpErlangRangeException("Value too large for float: " + d);
    }

    return f;
  }&lt;/pre&gt;
&lt;p&gt;which doesn't look too tested to me -- what's this &lt;span style="font-weight:bold;"&gt;(f != d)&lt;/span&gt;, in case you couldn't spot it.  This is carried over into otp.net, and, I think, shows how little floating point is used.&lt;/p&gt;
&lt;p&gt;I fixed up the name clashes and some of the blurred responsibility in the Java, then ran the conversion over; and by taking out the BigInteger stuff (not there in otp.net's Long class -- haven't checked the rest of these), compression, most of the floating point, got what was left to build, though it currently fails in the challenge/response handshake (where otp.net has replaced the roll-your-own MD5 with calls into System.Cryptography).&lt;/p&gt;
&lt;h4&gt;Where could we go from here?&lt;/h4&gt;
&lt;p&gt;Two main things need doing.  The otp.net code needs to be brought up to the 1.4 standard and make better use of .Net 2.0 e.g. for tracing; and it needs to be refactored to clean up inherited code smells.&lt;/p&gt;
&lt;p&gt;Ideally, some of the changes should be back-ported into jinterface, too.&lt;/p&gt;
&lt;p&gt;To date, this includes fallback to Java's file based location for cookie and a replacement for some of the non-ported tracing code (logging the remote endpoint).&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-6622284031181827246?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/6622284031181827246/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=6622284031181827246' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/6622284031181827246'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/6622284031181827246'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2008/04/integrating-net-with-erlang-part-3.html' title='Integrating .Net with Erlang, part 3'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-6797709832568492317</id><published>2008-04-18T20:39:00.007+01:00</published><updated>2008-12-03T14:38:49.896Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='erlang'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>Integrating .Net with Erlang, part 2</title><content type='html'>&lt;p&gt;So, we have pure J#.  Where do we go from here?&lt;/p&gt;
&lt;h4&gt;C#&lt;/h4&gt;
&lt;p&gt;The body of the driver class carries over unchanged but for the &lt;code&gt;Exception&lt;/code&gt; type.  The driver project has to itself reference &lt;code&gt;vjslib&lt;/code&gt;, as well as the &lt;code&gt;jinterface&lt;/code&gt; assembly but is otherwise a standard C# class-with-main, and "just works".&lt;/p&gt;
&lt;pre name="code" class="csharp"&gt;using System;
using com.ericsson.otp.erlang;

public class ClientNode
{
 
 [STAThread]
 public static void  Main(System.String[] _args)
 {
  
  OtpSelf cNode = new OtpSelf("clientnode", "cookie");
  OtpPeer sNode = new OtpPeer("servernode@chloe.ravnaandtines.com");
  OtpConnection connection = cNode.connect(sNode);
  
  OtpErlangObject[] args = new OtpErlangObject[]{new OtpErlangLong(1), new OtpErlangLong(2)};
  connection.sendRPC("mathserver", "add", args);
  OtpErlangLong sum = (OtpErlangLong) connection.receiveRPC();
  if (sum.intValue() != 3)
  {
   throw new System.SystemException("Assertion failed, returned = " + sum.intValue());
  }
  System.Console.Out.WriteLine("OK!");
 }
}&lt;/pre&gt;
&lt;p&gt;How do we divest ourselves of the dependency on the J# runtime?&lt;/p&gt;
&lt;p&gt;The Java Language Conversion Assistant generates C# code that doesn't compile, and would require a moderate amount of patching up for differences between superficially similar API classes.  It is by no means a one evening exercise.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;OtpErlang.jar&lt;/code&gt; similarly fails to be converted by the &lt;code&gt;jbImp&lt;/code&gt; tool, but IKVM converts it nicely; so at the penalty of having 27Mb worth of &lt;code&gt;IKVM.OpenJDK.ClassLibrary.dll&lt;/code&gt; and &lt;code&gt;IKVM.Runtime.dll&lt;/code&gt; instead of 3.7Mb of J#, driving the ~100kb dll, you can build the C# program and it passes this simple proof of concept.&lt;/p&gt;
&lt;h4&gt;IronPython and the DLR&lt;/h4&gt;
&lt;p&gt;Porting the code is easy enough&lt;/p&gt;
&lt;pre name="code" class="py"&gt;import clr
clr.AddReference("jinterface.dll")
from System import Array

from com.ericsson.otp.erlang import *

cNode = OtpSelf("clientnode", "cookie")
sNode = OtpPeer("servernode@chloe.ravnaandtines.com")
connection = cNode.connect(sNode);

args = Array[OtpErlangObject]([OtpErlangLong(1), OtpErlangLong(2)])
connection.sendRPC("mathserver", "add", args)
sum = connection.receiveRPC()
if (sum.intValue() != 3):
  print "Assertion failed, returned = " + sum.intValue()
else:
  print "OK!"&lt;/pre&gt;
&lt;p&gt;A direct conversion, the only slightly tricky bit being coercing the list to an Array.&lt;/p&gt;
&lt;p&gt;This works fine when calling into the J# built library, as expected.  IKVM needs the latest version -- before 0.36.0.11 &lt;span style="font-weight:bold;"&gt;(see comments)&lt;/span&gt;, though, is a different story.&lt;/p&gt;
&lt;pre class="code"&gt;
&amp;gt;\IronPython-2.0B1\ipy.exe Program.py
Traceback (most recent call last):
  File Snippets, line unknown, in Initialize
  File OtpErlang, line unknown, in connect
  File OtpErlang, line unknown, in .ctor
  File OtpErlang, line unknown, in .ctor
  File IKVM.OpenJDK.ClassLibrary, line unknown, in .ctor
  File IKVM.OpenJDK.ClassLibrary, line unknown, in init
  File IKVM.OpenJDK.ClassLibrary, line unknown, in getContext
  File IKVM.OpenJDK.ClassLibrary, line unknown, in getStackAccessControlContext
  File IKVM.Runtime, line unknown, in getStackAccessControlContext
  File IKVM.Runtime, line unknown, in GetProtectionDomainFromType
  File IKVM.Runtime, line unknown, in getProtectionDomain0
  File IKVM.Runtime, line unknown, in GetProtectionDomain
SystemError: The invoked member is not supported in a dynamic assembly.&lt;/pre&gt;
&lt;p&gt;Trace taken with 0.36.0.5, from last December.  Fortunately, that wasn't some fundamental architectural thing being run into.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-6797709832568492317?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/6797709832568492317/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=6797709832568492317' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/6797709832568492317'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/6797709832568492317'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2008/04/integrating-net-with-erlang-part-2.html' title='Integrating .Net with Erlang, part 2'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-8183465870009703686</id><published>2008-04-18T18:46:00.010+01:00</published><updated>2008-12-03T15:13:04.614Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='erlang'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>Integrating .Net with Erlang, part 1</title><content type='html'>&lt;style type="text/css"&gt;
.S2 {color: #800040;}
.S4 {font-weight: bold;color: #00007F;}
.S5 {font-weight: bold;color: #00007F;}
.S6 {font-weight: bold;color: #000000;}
.S7 {color: #000080;}
.S8 {color: #ff0000;}
.S10 {font-weight: bold;color: #000000;}
.S31 {color: #FFFF00;background: #FF0000;text-decoration: inherit;}
&lt;/style&gt;
&lt;p&gt;So, you can &lt;a href="http://www.theserverside.com/tt/articles/article.tss?l=IntegratingJavaandErlang"&gt;integrate Java with Erlang&lt;/a&gt;, using the jinterface code supplied as part of the standard Erlang distribution.  It's Java, so can we do this with .Net?&lt;/p&gt;
&lt;p&gt;Let's start with J#.  We set up the erlang source&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class="S6"&gt;-&lt;/span&gt;&lt;span class="S4"&gt;module&lt;/span&gt;&lt;span class="S6"&gt;(&lt;/span&gt;&lt;span class="S7"&gt;mathserver&lt;/span&gt;&lt;span class="S6"&gt;).&lt;/span&gt;
&lt;span class="S6"&gt;-&lt;/span&gt;&lt;span class="S4"&gt;export&lt;/span&gt;&lt;span class="S6"&gt;([&lt;/span&gt;&lt;span class="S8"&gt;start/0&lt;/span&gt;&lt;span class="S6"&gt;,&lt;/span&gt; &lt;span class="S8"&gt;add/2&lt;/span&gt;&lt;span class="S6"&gt;]).&lt;/span&gt;

&lt;span class="S7"&gt;start&lt;/span&gt;&lt;span class="S6"&gt;()&lt;/span&gt; &lt;span class="S6"&gt;-&amp;gt;&lt;/span&gt;
   &lt;span class="S2"&gt;Pid&lt;/span&gt; &lt;span class="S6"&gt;=&lt;/span&gt; &lt;span class="S7"&gt;spawn&lt;/span&gt;&lt;span class="S6"&gt;(&lt;/span&gt;&lt;span class="S4"&gt;fun&lt;/span&gt;&lt;span class="S6"&gt;()&lt;/span&gt; &lt;span class="S6"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="S7"&gt;loop&lt;/span&gt;&lt;span class="S6"&gt;()&lt;/span&gt; &lt;span class="S4"&gt;end&lt;/span&gt;&lt;span class="S6"&gt;),&lt;/span&gt;
   &lt;span class="S7"&gt;register&lt;/span&gt;&lt;span class="S6"&gt;(&lt;/span&gt;&lt;span class="S7"&gt;mathserver&lt;/span&gt;&lt;span class="S6"&gt;,&lt;/span&gt; &lt;span class="S2"&gt;Pid&lt;/span&gt;&lt;span class="S6"&gt;).&lt;/span&gt;

&lt;span class="S7"&gt;loop&lt;/span&gt;&lt;span class="S6"&gt;()&lt;/span&gt; &lt;span class="S6"&gt;-&amp;gt;&lt;/span&gt;
   &lt;span class="S4"&gt;receive&lt;/span&gt;
      &lt;span class="S6"&gt;{&lt;/span&gt;&lt;span class="S2"&gt;From&lt;/span&gt;&lt;span class="S6"&gt;,&lt;/span&gt; &lt;span class="S6"&gt;{&lt;/span&gt;&lt;span class="S7"&gt;add&lt;/span&gt;&lt;span class="S6"&gt;,&lt;/span&gt; &lt;span class="S2"&gt;First&lt;/span&gt;&lt;span class="S6"&gt;,&lt;/span&gt; &lt;span class="S2"&gt;Second&lt;/span&gt;&lt;span class="S6"&gt;}}&lt;/span&gt; &lt;span class="S6"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="S2"&gt;From&lt;/span&gt; &lt;span class="S6"&gt;!&lt;/span&gt; &lt;span class="S6"&gt;{&lt;/span&gt;&lt;span class="S7"&gt;mathserver&lt;/span&gt;&lt;span class="S6"&gt;,&lt;/span&gt; &lt;span class="S2"&gt;First&lt;/span&gt; &lt;span class="S6"&gt;+&lt;/span&gt; &lt;span class="S2"&gt;Second&lt;/span&gt;&lt;span class="S6"&gt;},&lt;/span&gt;
        &lt;span class="S7"&gt;loop&lt;/span&gt;&lt;span class="S6"&gt;()&lt;/span&gt;
   &lt;span class="S4"&gt;end&lt;/span&gt;&lt;span class="S6"&gt;.&lt;/span&gt;

&lt;span class="S7"&gt;add&lt;/span&gt;&lt;span class="S6"&gt;(&lt;/span&gt;&lt;span class="S2"&gt;First&lt;/span&gt;&lt;span class="S6"&gt;,&lt;/span&gt; &lt;span class="S2"&gt;Second&lt;/span&gt;&lt;span class="S6"&gt;)&lt;/span&gt; &lt;span class="S6"&gt;-&amp;gt;&lt;/span&gt;
   &lt;span class="S7"&gt;mathserver&lt;/span&gt; &lt;span class="S6"&gt;!&lt;/span&gt; &lt;span class="S6"&gt;{&lt;/span&gt;&lt;span class="S7"&gt;self&lt;/span&gt;&lt;span class="S6"&gt;(),&lt;/span&gt; &lt;span class="S6"&gt;{&lt;/span&gt;&lt;span class="S7"&gt;add&lt;/span&gt;&lt;span class="S6"&gt;,&lt;/span&gt; &lt;span class="S2"&gt;First&lt;/span&gt;&lt;span class="S6"&gt;,&lt;/span&gt; &lt;span class="S2"&gt;Second&lt;/span&gt;&lt;span class="S6"&gt;}},&lt;/span&gt;
   &lt;span class="S4"&gt;receive&lt;/span&gt;
      &lt;span class="S6"&gt;{&lt;/span&gt;&lt;span class="S7"&gt;mathserver&lt;/span&gt;&lt;span class="S6"&gt;,&lt;/span&gt; &lt;span class="S2"&gt;Reply&lt;/span&gt;&lt;span class="S6"&gt;}&lt;/span&gt; &lt;span class="S6"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="S2"&gt;Reply&lt;/span&gt;
   &lt;span class="S4"&gt;end&lt;/span&gt;&lt;span class="S31"&gt;.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and test it with&lt;/p&gt;
&lt;pre class="code"&gt;&amp;gt;erl -name servernode@chloe.ravnaandtines.com -setcookie cookie
Eshell V5.6  (abort with ^G)
(servernode@chloe.ravnaandtines.com)1&amp;gt; node().
'servernode@chloe.ravnaandtines.com'
(servernode@chloe.ravnaandtines.com)2&amp;gt; pwd().
jinterface          mathserver.erl
ok
(servernode@chloe.ravnaandtines.com)3&amp;gt; c(mathserver).
l{ok,mathserver}
(servernode@chloe.ravnaandtines.com)4&amp;gt; s().
jinterface          mathserver.beam     mathserver.erl
ok
(servernode@chloe.ravnaandtines.com)5&amp;gt; mathserver:start().
true
(servernode@chloe.ravnaandtines.com)6&amp;gt; mathserver:add(1,2).
3
(servernode@chloe.ravnaandtines.com)7&amp;gt;&lt;/pre&gt;
&lt;p&gt;Now fire up Visual Studio, and create a J# class library project.  Add existing items to &lt;span style="font-style: italic;"&gt;copy&lt;/span&gt; the jinterface code into the project, and build it.&lt;/p&gt;
&lt;pre class="code"&gt;Cannot find type 'java.lang.ref.WeakReference'&lt;/pre&gt;
&lt;p&gt;Well, that's not too bad.  Give it a stub, and build again:&lt;/p&gt;
&lt;pre name="code" class="java"&gt;package java.lang.ref;
import System.*;

public class WeakReference extends System.WeakReference
{
    public WeakReference()
    {
    }
}&lt;/pre&gt;

&lt;p&gt;gives us&lt;/p&gt;
&lt;pre class="code"&gt;error VJS1227: Cannot find constructor 'java.lang.ref.WeakReference(com.ericsson.otp.erlang.OtpMbox)'
error VJS1223: Cannot find method 'get()' in 'java.lang.ref.WeakReference'&lt;/pre&gt;
&lt;p&gt;So give it what it needs:&lt;/p&gt;
&lt;pre name="code" class="java"&gt;package java.lang.ref;
import System.*;

public class WeakReference extends System.WeakReference
{
    public WeakReference(Object o)
    {
        super(o);
    }
    public Object get()
    {
        try
        {
            return this.get_Target();
        }
        catch (InvalidOperationException ioe)
        {
            return null;
        }
    }
}&lt;/pre&gt;
&lt;p&gt;And it builds.  Now put in the driver program:&lt;/p&gt;
&lt;pre name="code" class="java"&gt;import com.ericsson.otp.erlang.*;

public class ClientNode
{

    public static void main(String[] _args) throws Exception
    {

        OtpSelf cNode = new OtpSelf("clientnode", "cookie");
        OtpPeer sNode = new OtpPeer("servernode"+"&amp;#64;"+"chloe.ravnaandtines.com");
        OtpConnection connection = cNode.connect(sNode);

        OtpErlangObject[] args = new OtpErlangObject[]{
            new OtpErlangLong(1), new OtpErlangLong(2)};
        connection.sendRPC("mathserver", "add", args);
        OtpErlangLong sum = (OtpErlangLong)connection.receiveRPC();
        if (sum.intValue() != 3)
        {
                throw new IllegalStateException("Assertion failed, returned = " + sum.intValue());
        }
        System.out.println("OK!");

    }

}&lt;/pre&gt;
&lt;p&gt;Now, run that and get:&lt;/p&gt;
&lt;pre class="code"&gt;OK!&lt;/pre&gt;
&lt;p&gt;So, we have first light!&lt;/p&gt;
&lt;p&gt;&lt;a href="http://uk.geocities.com/mr_tines/code.html#jinterface14"&gt;Get the goodies from here&lt;/a&gt;.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-8183465870009703686?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/8183465870009703686/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=8183465870009703686' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/8183465870009703686'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/8183465870009703686'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2008/04/integrating-net-with-erlang-part-1.html' title='Integrating .Net with Erlang, part 1'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-5811395054751172321</id><published>2008-04-08T20:48:00.003+01:00</published><updated>2008-04-08T20:57:15.229+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><title type='text'>Blast from the past</title><content type='html'>&lt;p&gt;I had need to play with &lt;code&gt;.ico&lt;/code&gt; files for the first time in ages the other day, and pulled out my old Java IcoImage utility to start with -- only to realise that it didn't handle modern 32-bit depth icons.&lt;/p&gt;
&lt;p&gt;So I  fixed it.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://uk.geocities.com/mr_tines/code.html"&gt;Latest versions here&lt;/a&gt;.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-5811395054751172321?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/5811395054751172321/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=5811395054751172321' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/5811395054751172321'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/5811395054751172321'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2008/04/blast-from-past.html' title='Blast from the past'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-8273188192495690462</id><published>2008-02-19T21:24:00.003Z</published><updated>2008-02-19T21:36:19.955Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='FTP'/><title type='text'>PassiveFTP 1.11</title><content type='html'>&lt;p&gt;&lt;a href="http://www.ravnaandtines.com/passiveftp-1_11.zip"&gt;Here&lt;/a&gt; or &lt;a href="http://www.geocities.com/mr_tines/windows.html"&gt;here&lt;/a&gt;. 

&lt;p&gt;PassiveFTP 1.11 &amp;#8212; build 1.11.2971.37821
&amp;#8212; mitigates the upload rate problem I've been having, by doing 
some unexpectedly effective tweaks to socket parameters.&lt;/p&gt;

&lt;p&gt;This came after having do do some throughput optimizations on XP at work for code that worked just dandy on Windows server OSs from NT4 up.  There it was just a case of upping the send buffer size.  Here, that was ineffective, but I did get a big boost from by sending only small packets and disabling Nagle coalescence.&lt;/p&gt;

&lt;p&gt;The new default upload rate is 128kb/s (1ms sleep between successful sends of 128-byte packets), which gave the best rate for me, up from an old and unreliable 2kb/s.&lt;/p&gt;

&lt;p&gt;This will of course feed back into the FePy version whenever I get the enthusiasm for doing all the plumbing.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-8273188192495690462?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/8273188192495690462/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=8273188192495690462' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/8273188192495690462'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/8273188192495690462'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2008/02/passiveftp-111.html' title='PassiveFTP 1.11'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-5023761846057651716</id><published>2008-02-04T19:37:00.000Z</published><updated>2008-02-04T20:16:46.816Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Scala'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>If only...</title><content type='html'>&lt;p&gt;Ultimately, the big splat from yesterday tracks down to calling the stub of&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
JbImp error: Couldn't find class 'java.lang.InheritableThreadLocal'
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;from the class constructor of Console$.  Now it would not be beyond the wit of man to emulate that new class in a library shim and leave everything else as I found it, but...  More annoying are the extensions to fundamental classes dating back to JDK 1.0 days, especially final ones like String&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
JbImp error: Unresolved Method 'public static java.lang.String java.lang.Character.toString(char)'
JbImp error: Unresolved Method 'public static java.lang.String[] java.lang.String.split(java.lang.String)'
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;where (absent some incantation to splice J#'s AWT into the namespace of an OpenJDK dll or similar), tackling it means modifying the Scala library code to retrofit.  Oh, yes, and even then there are some places where the Scala bytecode manages to throw jbimp for a wobbly.&lt;/p&gt;
&lt;p&gt;But if I keep to a fairly restricted subset (and for the moment I'd be interested in using it when writing a parser of OpenPGP), I might get away with a very small port of the runtime and library.&lt;/p&gt;
&lt;p&gt;Oh, well, like I said, hands on.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-5023761846057651716?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/5023761846057651716/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=5023761846057651716' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/5023761846057651716'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/5023761846057651716'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2008/02/if-only.html' title='If only...'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-7874963105026215410</id><published>2008-02-03T23:28:00.001Z</published><updated>2008-12-03T14:31:48.861Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Scala'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>Under my nose all the time</title><content type='html'>&lt;p&gt;Another of &lt;a href="http://tinesware.blogspot.com/2008/01/colour-me-slowpoke-swing-on-j.html"&gt;those hidden things about J#&lt;/a&gt; -- the &lt;a href="http://softwarecommunity.intel.com/articles/eng/3105.htm"&gt;JbImp tool&lt;/a&gt;, which takes .class files and spits out J# assemblies.  Just what I wanted, but was looking in the general freeware community for.&lt;/p&gt;
&lt;p&gt;Now the full scala-library.jar has a lot of post-J# Java API calls, but by cutting out a lot of the code I can get&lt;/p&gt;
&lt;pre class="code"&gt;&amp;gt;"\Program Files\Microsoft Visual Studio 8\SDK\v2.0\Bin\Jbimp.exe" /t:library /usestubrefs /out:scala-lib.dll /recurse *.class&lt;/pre&gt;
&lt;p&gt;to work on the remainder of it, and then&lt;/p&gt;
&lt;pre class="code"&gt;&amp;gt;"\Program Files\Microsoft Visual Studio 8\SDK\v2.0\Bin\Jbimp.exe" /t:library /usestubrefs /r:..\JavaLayer.dll ..\scala-lib.dll /out:SClass.dll /recurse *.class&lt;/pre&gt;
&lt;p&gt;And then build an IronPython project against it and get&amp;#8230;&lt;/p&gt;
&lt;pre class="code"&gt;&amp;gt;PythonLayer.exe
In class com_ravnaandtines.jlayer.JClass1.doSomething(), printing from Jython
11
In class com_ravnaandtines.jlayer.JClass1.doSomething(), printing Hello Babel!
Traceback (most recent call last):
  File scala-lib, line unknown, in .cctor
  File vjslib, line unknown, in checkAndThrowException
  File src\Program.py, line 24, in Initialize
  File , line 0, in NonDefaultNew##27
  File , line 0, in .ctor##29
  File SClass, line unknown, in .ctor
SystemError: The type initializer for 'scala.Console$' threw an exception.&lt;/pre&gt;
&lt;p&gt;which is progress, and most of &lt;a href="http://tinesware.blogspot.com/2008/01/tower-of-babel.html"&gt;what the JVM version does&lt;/a&gt;; but as this&lt;/p&gt;
&lt;pre name="code" class="java"&gt;
    private int len;
    private JClass1 in = new JClass1();

    public SClass(String token)
    {
        len = in().doSomething(token);
        Console$.MODULE$.println(BoxesRunTime.boxToInteger(len()));
    }&lt;/pre&gt;
&lt;p&gt;is the relevant part of the SClass code decompiled, the failure means that although it has executed some Scala derived code, it has fallen over at the first sniff of runtime.&lt;/p&gt;
&lt;p&gt;I hope that a more measured winnowing of the runtime will fix this.  But not tonight.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-7874963105026215410?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/7874963105026215410/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=7874963105026215410' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/7874963105026215410'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/7874963105026215410'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2008/02/under-my-nose-all-time.html' title='Under my nose all the time'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-1300669990530732263</id><published>2008-02-03T17:56:00.000Z</published><updated>2008-02-03T21:45:52.055Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Scala'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>No love for client-side Java</title><content type='html'>&lt;p&gt;It seemed a nice idea, having a deeper stack than just Python on Java that could build cross-VM apps from one code-base.  But alas&amp;#8230;&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;The only Java-on-.Net runtime I've found that has even half-way decent support for client-side apps (enough of AWT to support the GWT, or any flavour of Swing) is MSFT's J#, especially at 2.0&lt;/li&gt;
&lt;li&gt;The obvious cross-compilers (scala-net, if I could get it to work; IKVM) want to use anything else they can except the J# runtime, and don't offer the facility to &lt;span style="font-style:italic;"&gt;choose&lt;/span&gt; the one you want&lt;/li&gt;
&lt;li&gt;Meanwhile scala's bytecode is just plain beyond decompilation to Java by obvious decompilers&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;So, I'd have to write the tools myself if I want to do this.  It wouldn't even be a simple fork of IKVM to re-point references, and there are also its own special attributes decorating the classes.  It might be simpler to tweak the scala compiler, since the .Net-bit &lt;del&gt;is&lt;/del&gt;&lt;ins&gt;looked&lt;/ins&gt; smaller in that (and it would improve my hands-on with the language).&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Later:&lt;/p&gt;
&lt;p&gt;Building a replacement scalaruntime.dll based on the .Java files from the scala-library source jar (one tweak for the older Java dialect with doesn't have the JDK 1.4 &lt;code&gt;Character.toString()&lt;/code&gt;), plus a J# version of &lt;code&gt;scala.runtime.SymtabAttribute&lt;/code&gt; based on the C# one in SVN, easy.  Finding that the scala compiler source jar omits all the MSIL compiler files (from &lt;a href="https://lampsvn.epfl.ch/trac/scala/browser/msil/trunk"&gt;https://lampsvn.epfl.ch/trac/scala/browser/msil/trunk&lt;/a&gt;),
more of a pain.&lt;/p&gt;
&lt;p&gt;With a mscorlib 2.0 and my new runtime, and the predef.dll moved out of the way, I have to figure out why &lt;code&gt;System.String&lt;/code&gt; is causing a wobbly:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
java.lang.RuntimeException: PEModule.getTypeDefOrRef(): TypeSpec
        at ch.epfl.lamp.compiler.msil.PEModule.getTypeDefOrRef(PEModule.java:206)
        at ch.epfl.lamp.compiler.msil.PEType.loadInterfaces(PEType.java:309)
        at ch.epfl.lamp.compiler.msil.Type.initInterfaces(Type.java:944)
        at ch.epfl.lamp.compiler.msil.Type.getInterfaces(Type.java:907)
        at scala.tools.nsc.symtab.clr.TypeParser.parseClass(TypeParser.scala:88)
        at scala.tools.nsc.symtab.clr.TypeParser.parse(TypeParser.scala:58)
...
java.io.IOException: type 'System.String' is broken
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I'd probably have to rework a whole lot of stuff that shims a virtual Java library layer.  Which would have to start with identifying and encapsulating the shimming.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-1300669990530732263?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/1300669990530732263/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=1300669990530732263' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/1300669990530732263'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/1300669990530732263'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2008/02/no-love-for-client-side-java.html' title='No love for client-side Java'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-6679458920744767247</id><published>2008-02-01T18:24:00.000Z</published><updated>2008-02-03T18:21:37.624Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>One step forwards...</title><content type='html'>&lt;p&gt;Based on the &lt;a href="http://tinesware.blogspot.com/2008/01/confusion-of-babel.html#c6918416514725568669"&gt;helpful comment on my last post&lt;/a&gt;, I can run IKVM against 2.0 assemblies, but hit a different obstacle -- a critical failure linking against the J# runtime, leading to &lt;a href="http://sourceforge.net/tracker/index.php?func=detail&amp;aid=1884737&amp;group_id=69637&amp;atid=525264"&gt;a bug report&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;It seems to run OK auto-binding to the OpenJDK, but then I need to frob the reference somehow.  Perhaps it is back to the scala-net class loader issue...&lt;/p&gt;
&lt;p&gt;&lt;span style="font-style:italic;"&gt;Le sigh&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Later...&lt;/p&gt;
&lt;p&gt;I wondered.  We won't have much of Swing, but do we have enough of AWT to run the GWT?  So I converted TestGadget.jar and gwt221.jar and put all the images and &lt;code&gt;.dll&lt;/code&gt;s into the same folder and ran it and...&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt;TestGadget.exe
Exception during event dispatch:
cli.System.NotImplementedException: The method or operation is not implemented.
        at cli.ikvm.awt.NetComponentPeer.createImage(Unknown Source)
        at java.awt.Component.createImage(Component.java:2522)
        at dtai.gwt.Gadget.createImage(Gadget.java:2045)
        at dtai.gwt.ImageGadget.validateImage(ImageGadget.java:255)
        at dtai.gwt.ImageGadget.paintForeground(ImageGadget.java:283)
...&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and while much of the applet showed, the (re-)paint behaviour was terrible, and the applet could easily get into a state where it was spinning at almost 100% CPU.&lt;/p&gt;
&lt;p&gt;&lt;span style="font-style:italic;"&gt;Le sigh&amp;#178;&lt;/span&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-6679458920744767247?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/6679458920744767247/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=6679458920744767247' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/6679458920744767247'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/6679458920744767247'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2008/02/one-step-forwards.html' title='One step forwards...'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-4233077812289182746</id><published>2008-01-31T22:07:00.001Z</published><updated>2008-12-03T14:29:45.986Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Scala'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>Confusion of Babel</title><content type='html'>&lt;p&gt;I really would like to put a functional layer in the cross-VM stack, but...&lt;/p&gt;
&lt;p&gt;Ocamljava and F# might be code compatible, but the former has a cumbersome build and interop process (and doesn't seem to advertise call-from-Java); while Bigloo seems entirely silent on interop.&lt;/p&gt;
&lt;p&gt;Cross-compiling Scala class bytecodes to CLR doesn't seem to be a goer -- IKVM balks at referencing CLR 2.0 assemblies.  I &lt;span style="font-style:italic;"&gt;might&lt;/span&gt; be able decompile the classes and rebuild them via J# -- the hiccup there is that I would have to translate the runtime .jar as well -- my simple "Hello World!" level class decompiles to invoke &lt;code&gt;scala.runtime.BoxesRunTime&lt;/code&gt;, but that's not in any of the scalac-net assemblies.  On the up-side that would remove any nagging issues about CLR 2.0 compatibility, though; on the down-side, it would mean that I need a decompiler able to handle the generated code... Jode -- which I happened to have to hand -- balked at half the classes in the runtime; Jad seemed to have dealt with them all, though with a lot of &lt;code&gt;goto&lt;/code&gt; statements replacing things like jumps out of loops -- but leaves a lot of &lt;code&gt;JVM INSTR monitorenter;...JVM INSTR monitorexit;&lt;/code&gt; where a &lt;code&gt;synchronized{}&lt;/code&gt; block should be, and possibly other horrors.&lt;/p&gt;

&lt;p&gt;Meanwhile I took another prod at scalac-net, using the magic -Ydebug  flag:&lt;/p&gt;
&lt;pre class="code"&gt;
&gt;\scala-2.6.1-final\bin\scalac-net -d classes -Xassem-path JavaLayer.dll SClass.
scala  -Ydebug
[running phase parser on SClass.scala]
[running phase namer on SClass.scala]
java.lang.ClassCastException: scala.tools.nsc.symtab.Symbols$ClassSymbol cannot
be cast to scala.runtime.Nothing$
        at scala.tools.nsc.symtab.clr.TypeParser.parseClass(TypeParser.scala:77)

        at scala.tools.nsc.symtab.clr.TypeParser.parse(TypeParser.scala:58)
        at scala.tools.nsc.symtab.SymbolLoaders$MSILTypeLoader.doComplete(Symbol
Loaders.scala:263)
        at scala.tools.nsc.symtab.SymbolLoaders$SymbolLoader.complete(SymbolLoad
ers.scala:54)
...
java.io.IOException: type 'scala.Predef' is broken
(scala.tools.nsc.symtab.Symbols$ClassSymbol cannot be cast to scala.runtime.Nothing$)
...&lt;/pre&gt;
&lt;p&gt;Kudos to the scala implementors -- the line numbers match up down the stack, and show the culprit to be&lt;/p&gt;
&lt;pre name="code" class="java"&gt;
      unpickler.unpickle(symtab, 0, clazz.asInstanceOf, staticModule.asInstanceOf, typ.FullName);
&lt;/pre&gt;
&lt;p&gt;which looks like it's doing the right sort of thing; and there's nothing particularly odd about the &lt;code&gt;scala.Predef&lt;/code&gt; class in the assembly; and I get the same error trying to compile some of the examples in the distro...&lt;/p&gt;
&lt;p&gt;Next diversion, rebuilding scalac with more instrumentation, piling the tower higher.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-4233077812289182746?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/4233077812289182746/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=4233077812289182746' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/4233077812289182746'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/4233077812289182746'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2008/01/confusion-of-babel.html' title='Confusion of Babel'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-5670684493369236429</id><published>2008-01-30T22:09:00.001Z</published><updated>2008-12-03T14:28:24.532Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Scala'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>A tower of Babel</title><content type='html'>&lt;pre name="code" class="java"&gt;// JClass1.java
package com_ravnaandtines.jlayer;
public class JClass1
{
    public JClass1()
    {
    }
    public int doSomething(String arg)
    {
        System.out.println("In "+this.getClass().toString()+".doSomething(), printing "+arg);
        return arg.length();
    }
}&lt;/pre&gt;&lt;pre name="code" class="scala"&gt;//SClass.scala
package com_ravnaandtines.slayer

import com_ravnaandtines.jlayer

class SClass(token: String) {
 private val in = new com_ravnaandtines.jlayer.JClass1()
 private val len = in.doSomething(token)
 Console.println(len)
}&lt;/pre&gt;&lt;pre name="code" class="py"&gt;//Python.py
import com_ravnaandtines.jlayer.JClass1

direct = com_ravnaandtines.jlayer.JClass1()
val = direct.doSomething("from Jython")
print val

import com_ravnaandtines.slayer.SClass
x = com_ravnaandtines.slayer.SClass("Hello Babel!")
&lt;/pre&gt;
&lt;p&gt;Build the first into JavaLayer.jar and copy it alongside the second&lt;/p&gt;
&lt;pre class="code"&gt;
\scala-2.6.1-final\bin\scalac -classpath .JavaLayer.jar -d classes SClass.scala
cd classes
"\Program Files\Java\jdk1.6.0\bin\jar.exe" -cf ..\ScalaLayer.jar .
&lt;/pre&gt;
&lt;p&gt;Then run the last with the two jars and the scala runtime jar in your path&lt;/p&gt;
&lt;pre class="code"&gt;
In class com_ravnaandtines.jlayer.JClass1.doSomething(), printing from Jython
11
In class com_ravnaandtines.jlayer.JClass1.doSomething(), printing Hello Babel!
12
&lt;/pre&gt;
&lt;p&gt;I had hoped that I could do the same in .Net, but having built the J# JavaLayer.dll, I got&lt;/p&gt;
&lt;pre class="code"&gt;
&gt;\scala-2.6.1-final\bin\scalac-net -d classes -Xassem-path JavaLayer.dll SClass.
scala
error: error while loading Predef, type 'scala.Predef' is broken
(scala.tools.nsc.symtab.Symbols$ClassSymbol cannot be cast to scala.runtime.Noth
ing$)
one error found
&lt;/pre&gt;
&lt;p&gt;so haven't yet managed to make a Scala-derived DLL for IronPython to consume.  And that doesn't look like something I might obviously have done wrong.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-5670684493369236429?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/5670684493369236429/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=5670684493369236429' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/5670684493369236429'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/5670684493369236429'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2008/01/tower-of-babel.html' title='A tower of Babel'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-7139440688114339455</id><published>2008-01-23T22:49:00.000Z</published><updated>2008-01-23T23:02:03.039Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>Steps forwards, steps back.</title><content type='html'>&lt;p&gt;It would be nice to have the getX()/setX() map to a property, syntactically, in general use Jython as well, along the lines of &lt;a href="http://tinesware.blogspot.com/2008/01/making-ironpython-and-j-more-jythonic.html"&gt;the previous essay&lt;/a&gt;.  Alas, that simple "X indirects to getX()" method doesn't seem to work as is.  I wonder if I'm relying on things that are a more modern version than Jython yet supports.  I need to investigate more.&lt;/p&gt;
&lt;p&gt;Meanwhile, on another front, even using the invaluable vjsresgen tool, I have to have platform specific interludes to handle resources -- especially to convert from .png format to something that the back-version Java support understands.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-7139440688114339455?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/7139440688114339455/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=7139440688114339455' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/7139440688114339455'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/7139440688114339455'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2008/01/steps-forwards-steps-back.html' title='Steps forwards, steps back.'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-1681675514880533294</id><published>2008-01-23T15:49:00.000Z</published><updated>2008-01-23T16:23:40.956Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>Colour me slowpoke ... Swing on J#</title><content type='html'>&lt;p&gt;I'd not caught &lt;a href="http://msdn2.microsoft.com/en-gb/vjsharp/bb188695.aspx"&gt;this&lt;/a&gt; before:&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;The Microsoft Supplemental UI Library provides much of the functionality described in the Java 2 JFC Swing specification. It also adds support for much of the functionality found in the JDK 1.2 java.util package.&lt;/p&gt;
&lt;p&gt;&amp;#8230;&lt;/p&gt;
&lt;p&gt;Version 2.0 of the Supplemental UI Library is included as part of the &lt;a href="http://www.microsoft.com/downloads/details.aspx?familyid=F72C74B3-ED0E-4AF8-AE63-2F0E42501BE1&amp;displaylang=en"&gt;Visual J# Version 2.0 Redistributable Package&lt;/a&gt;. No additional download is required.&lt;/p&gt;
&lt;p&gt;&amp;#8230;&lt;/p&gt;
&lt;p&gt;To access the functionality in the Supplemental UI Library, simply add a reference to VJSSupUILib.dll in your project.&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;Betterer and betterer&amp;#8230;&lt;/p&gt;

&lt;p&gt;Of course this throws a little bit of a spanner in the works of the Jythonizer effort -- I might have subclassed &lt;code&gt;javax.swing.JFrame&lt;/code&gt;, but not the component that I get from calling its shiny new &lt;code&gt;contentPane&lt;/code&gt; attribute delegating to &lt;code&gt;getContentPane()&lt;/code&gt;.  Oh, well.  At least I might be able to get some cute tricks along the lines of some of the ideas in &lt;a href="http://www.oreilly.com/catalog/swinghks/"&gt;Swing &lt;/a&gt; &lt;a href="http://www.onjava.com/pub/a/onjava/2005/08/10/swinghacks.html"&gt;Hacks&lt;/a&gt; instead.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-1681675514880533294?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/1681675514880533294/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=1681675514880533294' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/1681675514880533294'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/1681675514880533294'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2008/01/colour-me-slowpoke-swing-on-j.html' title='Colour me slowpoke ... Swing on J#'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-1669172448659552047</id><published>2008-01-22T23:08:00.001Z</published><updated>2008-12-03T14:26:32.488Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>Making IronPython and J# more Jythonic -- Part 1</title><content type='html'>&lt;p&gt;For the Nine Unknown Men who might also be attempting this sort of thing.&lt;/p&gt; &lt;p&gt;Persistance pays&amp;#8230;&lt;/p&gt;
&lt;pre name="code" class="py"&gt;
import dtai.gwt.GadgetFrame

def my__getattr__(self, name):
  if name.startswith("__"):
    raise AttributeError(name)
  getter = "get"+name.capitalize()
  if getter in dir(self):
    tmp = getattr(self, getter)
    return tmp()
  iser = "is"+name.capitalize()
  if iser in dir(self):
    tmp = getattr(self, iser)
    return tmp()
  raise AttributeError(name)
        
class Local:    
  def getFred(self):
    return "Fred!"
  
Local.__getattr__ = my__getattr__
   
test = Local()   
print test.fred

class WrappedGadgetFrame(dtai.gwt.GadgetFrame):
  pass

WrappedGadgetFrame.__getattr__ = my__getattr__
dtai.gwt.GadgetFrame = WrappedGadgetFrame

window =  dtai.gwt.GadgetFrame()
print window.visible
&lt;/pre&gt;
&lt;p&gt;when executed, yields&lt;/p&gt;
&lt;pre class="code"&gt;
C:...&gt;PyInterop.exe
Fred!
False&lt;/pre&gt;
&lt;p&gt;which is the get() side of attributes done at the cost of a pass of subclassing.  The equivalent set() shouldn't be too difficult, but automating the subclassing will be the next challenge.&lt;/p&gt;
&lt;p&gt;Then adding the listener-based attributes; and finally, hiding it from Jython&amp;#8230;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-1669172448659552047?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/1669172448659552047/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=1669172448659552047' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/1669172448659552047'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/1669172448659552047'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2008/01/making-ironpython-and-j-more-jythonic.html' title='Making IronPython and J# more Jythonic -- Part 1'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-2581695345078912328</id><published>2008-01-21T21:09:00.000Z</published><updated>2008-01-22T23:29:45.699Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>How Jythonic can it get?</title><content type='html'>&lt;p&gt;Following up from &lt;a href="http://tinesware.blogspot.com/2008/01/pushing-at-open-door.html"&gt;my previous post&lt;/a&gt;, I've been thinking that it would be nice if I could make the code there a bit more Jythonic -- wrapping Java-style bean properties and event handlers transparently in the IronPython/J# world, where the inherent properties will be the Capitalized .Net ones, and we only have the .Net delegate-style handlers.&lt;/p&gt;
&lt;p&gt;I will need to experiment to see exactly how open the IronPython proxy classes are to extension, first.&lt;/p&gt;
&lt;p&gt;So many projects, so little free time.  And I really need to do more with F# and PowerShell, not to mention wanting to try writing a generic protocol-terminating proxy framework in Erlang, probably with SOCKS5 as an exemplar protocol.&lt;/p&gt;
&lt;p&gt;LATER&lt;/p&gt;
&lt;p&gt;As I suspected, setting &lt;code&gt;Local.__getattr__ = my__getattr__&lt;/code&gt; for a function of suitable signature works where Local is a pure IronPython class; but for an imported .Net class I get&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
TypeError: can't set '__getattr__' in dictproxy
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;even with the indirect approach of something like &lt;code&gt;dtai.gwt.GadgetFrame.__dict__["__getattr__"] = my__getattr__&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;So Java/J# idioms at the interface it may have to be.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-2581695345078912328?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/2581695345078912328/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=2581695345078912328' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/2581695345078912328'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/2581695345078912328'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2008/01/how-jythonic-can-it-get.html' title='How Jythonic can it get?'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-7474050843856599070</id><published>2008-01-16T22:35:00.003Z</published><updated>2008-12-03T14:25:01.868Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>Pushing at an open door…</title><content type='html'>&lt;p&gt;Compared with the travails required to shim Ruby.Net and JRuby onto a shared Java/J# code platform, doing the same for Jython (in NetBeans 5.0)/IronPython (using the DevStudio extension build for IronPython 1.1) was trivial.  In fact most of the work was re-gearing my mind to Python after thinking in Ruby for some weeks.&lt;/p&gt;

&lt;p&gt;Apart from the fact that that the IronPython build ignores any directory structure and smooshes all classes into the one assembly and considers that the package, which will impact on the broader code shape for any project, there is no shimming.  Just build your assembly with references to the appropriate external files and you don't even need all the clr.AddReference to do it by hand&lt;/p&gt;

&lt;p&gt;Doing the same as the &lt;a href="http://tinesware.blogspot.com/2007/12/j-and-rubynet-string-and-baling-wire.html"&gt;J# and Ruby.Net — string and baling wire&lt;/a&gt; example, in a project that references the appropriate assemblies just requires the same Python code as a Jython build with the equivalent .jar files on the classpath&lt;/p&gt;

&lt;pre name="code" class="py"&gt;
## pyinterop.py
import dtai.gwt.GadgetFrame
import java11.awt.event.WindowListener
import java.lang.System

window = dtai.gwt.GadgetFrame()
window.setSize (800, 600)
window.setTitle("Proof of Concept")

class Handler(java11.awt.event.WindowListener):
  def __init__(self, window):
    self.window = window

  def windowClosing(self, e):
    self.window.hide()

  def windowClosed(self, e):
    java.lang.System.exit(0)

  def windowActivated(self, e):
    pass

  def windowDeactivated(self, e):
    pass

  def windowDeiconified(self, e):
    pass

  def windowIconified(self, e):
    pass

  def windowOpened(self, e):
    pass

adapter = Handler(window)
window.addWindowListener(adapter)
window.show()
&lt;/pre&gt;
&lt;p&gt;It still works in either case, if I simply use the WindowAdapter class and just override the methods I need.  W00t!  Indeed, so far it almost seem &lt;span style="font-style:italic;"&gt;too&lt;/span&gt; easy!&lt;/p&gt;

&lt;p&gt;Now I just have to keep working around the &lt;a href="http://sourceforge.net/tracker/index.php?func=detail&amp;aid=1701729&amp;group_id=12867&amp;atid=112867"&gt;weird problems&lt;/a&gt; I ran into last time around this loop.  Keeping Java beneath everything this time around should help.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-7474050843856599070?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/7474050843856599070/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=7474050843856599070' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/7474050843856599070'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/7474050843856599070'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2008/01/pushing-at-open-door.html' title='Pushing at an open door&amp;#8230;'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-1092590718796349995</id><published>2008-01-15T20:25:00.000Z</published><updated>2008-01-15T20:53:26.182Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>Ruby.Net and Ruby.Net — foiled again</title><content type='html'>&lt;p&gt;A reboot (of the machine, and myself) later, I realised that the obscure "Wrong argument type String (Class expected)" was the result of a sleep-addled 'C'-drenched brain putting &lt;code&gt;include 'Wizard'&lt;/code&gt; where &lt;code&gt;require 'Wizard'&lt;/code&gt; was expected.&lt;/p&gt;
&lt;p&gt;Not so good with the memory issue.  After a couple of hours debugging and finally getting the code to interoperate, DevStudio was peaking at over 370Mb of memory, and still thrashing (on a 512Mb machine), while building a project with &lt; 1000 lines of Ruby.  NetBeans, by comparison has about 150Mb (if I have a lot of files open)in the IDE and and under 40Mb in the spawned application.&lt;/p&gt;
&lt;p&gt;I've put the &lt;a href="http://uk.geocities.com/mr_tines/code.html"&gt;Interop example code&lt;/a&gt; -- showing how to call from shared code JRuby/Ruby.Net to shared-code Java/J# libraries.  main.rb calls Wizard.rb which calls through the interop layer defined by .jsl files for Ruby.Net-&gt;J#, and Interfaces.rb plus the .java files for JRuby-&gt;Java -- on one of my sites, for those who are interested in taking this further.&lt;/p&gt;
&lt;p&gt;Until I upgrade my hardware to something that permits more than a toy Ruby.Net layer, though, I don't think I'll be taking the ideal of a shared code/multi-VM project further with Ruby.  Which sucks, because that has the best IDE support (I don't even know if the Jython add-in for NetBeans still works at 6.0).&lt;/p&gt;
&lt;p&gt;Still a little experimentation is needed with that, and with Scala, to get to an equivalent proof of concept stage.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-1092590718796349995?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/1092590718796349995/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=1092590718796349995' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/1092590718796349995'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/1092590718796349995'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2008/01/rubynet-and-rubynet-foiled-again.html' title='Ruby.Net and Ruby.Net — foiled again'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-6165336121504689820</id><published>2008-01-14T22:14:00.001Z</published><updated>2008-12-03T14:24:03.828Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>Ruby.Net and Ruby.Net — first steps</title><content type='html'>&lt;p&gt;Not a typo, alas.&lt;/p&gt;
&lt;p&gt;I was testing calling from a Ruby.Net .exe file into a Ruby.Net .dll, to see if partitioning like that would work; though not in such a simple setup as that : I was trying to test a Ruby implementation of a wizard dialog using the GWT... So I create a Testbed.Net.exe that refers to gwt_ruby.dll (which is built from a single source file Wizard.rb).&lt;/p&gt;
&lt;p&gt;That fails with a Key Not Found Exception, and looking at the Ruby.Net code, and ildasm-ing the assembly, I discern that it was looking for a gwt_ruby.rb file having been built into the project.  So I give it one to satisfy it&lt;/p&gt;

&lt;pre name="code" class="ruby"&gt;
require 'Wizard'
class Gwt_Ruby
end
&lt;/pre&gt;

&lt;p&gt;Later...&lt;/p&gt;
&lt;p&gt;The Ruby.Net build is being a voracious memory hog on my 3 year old machine with only 512Mb RAM -- even for only a few hundred lines of Ruby code each build and test is taking more than half an hour of thrashing madly at almost 0% CPU.  And I thought that NetBeans was memory hungry -- that I can work on in realtime, with the same code in JRuby.  I'm not sure that testing this is going to be practical, even without having to work around packaging quirks. &lt;/p&gt;

&lt;p&gt;It's almost enough to drive me to Scala!&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-6165336121504689820?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/6165336121504689820/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=6165336121504689820' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/6165336121504689820'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/6165336121504689820'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2008/01/rubynet-and-rubynet-first-steps.html' title='Ruby.Net and Ruby.Net — first steps'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-7180719804973390857</id><published>2007-12-28T21:30:00.000Z</published><updated>2007-12-28T21:37:24.928Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='CTClib'/><title type='text'>Phase 2 — “The best code is code someone else has already written and debugged.”</title><content type='html'>&lt;p&gt;So goes the old saying.&lt;/p&gt;
&lt;p&gt;And looking at the almost decade old Angerona project code, I see there's a lot of code that is written, was lightly debugged, probably works, and just needs refactoring.  After all, it was the lack of a ZLib that stopped that effort dead, not anything with the existing code.&lt;/p&gt;
&lt;p&gt;So, the next phase will be to put together an infrastructure library (jar/dll) from the useful bits, which should be enough to get to the point of being able to parse a key-ring or similar binary file to OpenPGP format.&lt;/p&gt;
&lt;p&gt;At that point, some weeks hence, it will make sense to put a first bit of GUI on top; and start filling in the gaps in a less clunky language than Java where possible.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-7180719804973390857?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/7180719804973390857/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=7180719804973390857' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/7180719804973390857'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/7180719804973390857'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2007/12/phase-2-best-code-is-code-someone-else.html' title='Phase 2 &amp;#8212; &amp;#8220;The best code is code someone else has already written and debugged.&amp;#8221;'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-5465084093566116804</id><published>2007-12-25T17:16:00.000Z</published><updated>2007-12-26T11:33:13.490Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='CTClib'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>angerona.algorithms 1.0.2915.29952</title><content type='html'>&lt;p&gt;&lt;a href="http://www.windsong.demon.co.uk/crypt.zip"&gt;Here.&lt;/a&gt;  and &lt;a href="http://www.stevegilham.com/crypt.zip"&gt;a back-up here&lt;/a&gt; if Demon's DNS flakes out.&lt;/p&gt;
&lt;p&gt;A complete replacement for the old crypt.zip, with source and pre built .jar and .dll, unit tests, and a .Net build of junit 3.8.2 -- includes:&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;JZlib (inflate/deflate)&lt;/li&gt;&lt;li&gt;Blowfish, CAST5, DES (including s3' and key-dependent variants), IDEA, Safer, Square, TEA, ThreeWay, TripleDES (encryption)&lt;/li&gt;&lt;li&gt;BlockCypherHash, Haval, MD5, RIPEM160, SHA0, SHA1 (message digests); and&lt;/li&gt;&lt;li&gt;Bignum (mutable multiple precision integers).&lt;/li&gt;&lt;/ul&gt;

&lt;p&gt;Needs the &lt;a href="http://uk.geocities.com/mr_tines/code/vjredist2nd.zip"&gt;J# redistributables&lt;/a&gt; for .Net use,&lt;/p&gt;

&lt;p&gt;Happy holidays!  and 100th post!&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-5465084093566116804?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/5465084093566116804/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=5465084093566116804' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/5465084093566116804'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/5465084093566116804'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2007/12/angeronaalgorithms-10291529952.html' title='angerona.algorithms 1.0.2915.29952'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-8781125593965373741</id><published>2007-12-20T20:01:00.001Z</published><updated>2008-12-03T14:23:12.514Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>J# and Ruby.Net — string and baling wire</title><content type='html'>&lt;p&gt;A proof of concept&lt;/p&gt;
&lt;pre name="code" class="java"&gt;
//Factory.java
public class Factory
{
    private Factory(){}

    public static Object Build(String classname) 
        throws InstantiationException, IllegalAccessException, 
        ClassNotFoundException
    {
        Class target = Class.forName(classname);
        return target.newInstance();
    }
}&lt;/pre&gt;&lt;pre name="code" class="java"&gt;//Utility.java
package Tinesware.Adapter;

public class Utility
{
    private Utility() { }

    public static void exit(int status)
    {
        java.lang.System.exit(status);
    }
}&lt;/pre&gt;&lt;pre name="code" class="java"&gt;//WindowAdapter.java
package Tinesware.Adapter;

public class WindowAdapter implements java11.awt.event.WindowListener  
{
    private final Ruby.Object payload;

    public WindowAdapter(Ruby.Object arg)
    {
        payload = arg;
    }

    public void windowActivated(java11.awt.event.WindowEvent e)
    {
    }

    public void windowClosed(java11.awt.event.WindowEvent e)
    {
        System.out.println("window Closed");
        Object[] args = {e};
        Ruby.Runtime.Eval.Call(payload, "windowClosed", args);
    }

    public void windowClosing(java11.awt.event.WindowEvent e)
    {
        System.out.println("window Closing");
        Object[] args = { e };
        Ruby.Runtime.Eval.Call(payload, "windowClosing", args);
    }

    public void windowDeactivated(java11.awt.event.WindowEvent e)
    {
    }

    public void windowDeiconified(java11.awt.event.WindowEvent e)
    {
    }

    public void windowIconified(java11.awt.event.WindowEvent e)
    {
    }

    public void windowOpened(java11.awt.event.WindowEvent e)
    {
    }
}&lt;/pre&gt;&lt;pre name="code" class="ruby"&gt;##Program.rb
require 'Tinesware.Adapter.dll'
require 'gwt221.dll'

window = Tinesware::Adapter::Factory.Build("dtai.gwt.GadgetFrame");
window.setSize (800, 600)
window.setTitle 'Proof of Concept'
window.show

class WindowHandler
  def initialize(w)
    @window = w
   end

  def windowClosing(e)
    @window.hide
  end
  def windowClosed(e)
    Tinesware::Adapter::Utility.exit 0
  end
end

adapter = Tinesware::Adapter::WindowAdapter.new(WindowHandler.new(window))
window.addWindowListener adapter
&lt;/pre&gt;
&lt;p&gt;It only works grace of &lt;code&gt;Ruby.Runtime.Eval.Call()&lt;/code&gt; being public, but that's all I need; that the event argument gets through is a bonus.&lt;/p&gt;
&lt;p&gt;It is a mild hack, it does involve repetitive boilerplate coding, but you only have to do it for a few interface classes.  And it will need some smoothing over on the Ruby side to make it look less awkward with code that's supposed to interoperate with Java as JRuby.&lt;/p&gt;
&lt;p&gt;Extra bonus -- if the &lt;code&gt;Call()&lt;/code&gt;'d method doesn't exist, it does nothing silently, so you only have to implement the parts of an interface you actually want.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-8781125593965373741?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/8781125593965373741/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=8781125593965373741' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/8781125593965373741'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/8781125593965373741'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2007/12/j-and-rubynet-string-and-baling-wire.html' title='J# and Ruby.Net &amp;#8212; string and baling wire'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-1345217658807320278</id><published>2007-12-17T20:07:00.001Z</published><updated>2008-12-03T14:22:06.367Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>J# and Ruby.Net — foiled again</title><content type='html'>&lt;p&gt;Carrying on from last post&amp;#8230;&lt;/p&gt;
&lt;pre name="code" class="csharp"&gt;// C#
        public static void typeTest(object arg)
        {
            System.Type wtf = arg.GetType();
            Console.WriteLine(wtf.ToString());
            Console.WriteLine("Base = "+wtf.BaseType.ToString());

            System.Type[] iface = wtf.GetInterfaces();
            Console.WriteLine("# interfaces = " + iface.Length);
            foreach (System.Type t in iface)
                Console.WriteLine(":: "+t.ToString())
        }&lt;/pre&gt;&lt;pre name="code" class="ruby"&gt;
##Ruby caller
q = Observer.new
JsharpToRuby::ClassDictionary.typeTest q
&lt;/pre&gt;
&lt;p&gt;yields disappointing results&lt;/p&gt;
&lt;pre&gt;Observer
Base = Ruby.Object
# interfaces = 0
&lt;/pre&gt;


&lt;p&gt;Oh dear! it doesn't seem to work the na&amp;iuml;ve way for proper interfaces&lt;/p&gt;
&lt;pre name="code" class="csharp"&gt;// C#
namespace JsharpToRuby
{
    public interface ITest
    {
        Boolean isPresent();
    }&lt;/pre&gt;&lt;pre name="code" class="ruby"&gt;##Ruby 
class EyeTest 
  include JsharpToRuby::ITest
  def isPresent
    1
  end
end
q = EyeTest.new
puts q.class
q.class.ancestors.each { |x| puts x }
JsharpToRuby::ClassDictionary.typeTest q
&lt;/pre&gt;
&lt;p&gt;yields&lt;/p&gt;
&lt;pre&gt;EyeTest
EyeTest
ITest
Object
Kernel
EyeTest
Base = Ruby.Object
# interfaces = 0
&lt;/pre&gt;
&lt;p&gt;This is of course the bit missed out in &lt;a href="http://rubydotnet.googlegroups.com/web/Ruby_DotNet_InteropTutorial.htm"&gt;the interop tutorial&lt;/a&gt;, alas; I think for the good and simple reason that it is not yet implemented (from a quick inspection of the code).&lt;/p&gt;
&lt;p&gt;*le sigh*&lt;/p&gt;
&lt;p&gt;Maybe not all is lost.  It would mean more hand-rolled adapter classes to wrap the objects and delegate by reflection.&lt;/p&gt;
&lt;p&gt;But later, definitely, later.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-1345217658807320278?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/1345217658807320278/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=1345217658807320278' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/1345217658807320278'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/1345217658807320278'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2007/12/j-and-rubynet-foiled-again.html' title='J# and Ruby.Net &amp;#8212; foiled again'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-2649338509436059517</id><published>2007-12-16T20:53:00.001Z</published><updated>2008-12-03T14:18:47.313Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>J# and Ruby.Net — further steps</title><content type='html'>&lt;p&gt;So what do we need Java classes for in a JRuby/Ruby.Net environment?&lt;/p&gt;
&lt;p&gt;Things that we have to &lt;code&gt;new&lt;/code&gt; because that defines system services in an unambiguous fashion &amp;#8212; I'm thinking encodings for I/O and GUI components here, as well as libraries already written that we don't want to port just yet (my algorithms library); and interfaces/abstract classes we want to make concrete (needing to be an ActionListener or similar).&lt;/p&gt;
&lt;p&gt;Concrete things are easy; abstractions are trickier, but not impossible.&lt;/p&gt;
&lt;pre name="code" class="csharp"&gt;//C# code to generate a Ruby class type
        public static Ruby.Class getThing()
        {
            object o = new java.awt.Panel();
            return Ruby.Class.CLASS_OF(o);
        }&lt;/pre&gt;&lt;pre name="code" class="ruby"&gt;
##Ruby code to use it
thing = JsharpToRuby::ClassDictionary.getThing

puts thing.to_s

puts thing.ancestors

panel = thing.new
puts panel.to_s&lt;/pre&gt;
&lt;p&gt;which generates&lt;/p&gt;
&lt;pre&gt;Panel
Container
Component
Serializable
MenuContainer
ImageObserver
Object
Object
Kernel
java.awt.Panel[panel1,0,0,0x0,invalid,layout=java.awt.FlowLayout]&lt;/pre&gt;
&lt;p&gt;which goes &lt;code&gt;&amp;#8230; java.lang.Object, Ruby Object, &amp;#8230;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;So now try&lt;/p&gt;
&lt;pre name="code" class="ruby"&gt;io = nil
thing.ancestors.each { |x| io = x if x.to_s.eql? "ImageObserver" }
MyImageObserver = io

class Observer 
  include MyImageObserver
  def imageUpdate(img, infoflags, x, y, width, height) 
    nil
   end
end

q = Observer.new
puts q.class.ancestors&lt;/pre&gt;
&lt;p&gt;to get&lt;/p&gt;
&lt;pre&gt;Observer
ImageObserver
Object
Kernel&lt;/pre&gt;
&lt;p&gt;which means the task is not immediately insuperable; but may need some hand-rolling of concrete classes for some of the interesting interfaces, rather than being something that can be totally read-only&amp;#8230;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-2649338509436059517?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/2649338509436059517/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=2649338509436059517' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/2649338509436059517'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/2649338509436059517'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2007/12/j-and-rubynet-further-steps.html' title='J# and Ruby.Net &amp;#8212; further steps'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-8417595726672396245</id><published>2007-12-16T10:44:00.001Z</published><updated>2008-12-03T14:17:24.751Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>J# and Ruby.Net — first steps</title><content type='html'>&lt;p&gt;This seems to work&lt;/p&gt;
&lt;pre name="code" class="csharp"&gt;
//C# assembly referencing the Ruby.NET.Runtime assembly
namespace Inspect
{
    public class Access
    {
        public static Ruby.Class getRubyPerson()
        {
            ClassLibrary2.nested.Person p = new ClassLibrary2.nested.Person("", 0);
            return Ruby.Class.CLASS_OF(p);
        }
    }
}&lt;/pre&gt;&lt;pre name="code" class="ruby"&gt;## Ruby 
x = Inspect::Access.getRubyPerson()
fred = x.new(name, age)
fred.print
&lt;/pre&gt;
&lt;p&gt;It's a pity that I need an object and cannot just send a &lt;code&gt;System.Type&lt;/code&gt; into &lt;code&gt;Interop.CLRClass.Load(type, null, false);&lt;/code&gt;, which this ends up doing, but that method is internal to the Runtime DLL.&lt;/p&gt;
&lt;p&gt;It should be possible, though, to have a &lt;code&gt;require_jsharp('assembly.dll')&lt;/code&gt; that does the tedious running around back and forth through the Ruby/C# boundary in an anutmated fashion, and creates appropriate proxies for lowercase package-names in a tree-like object.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-8417595726672396245?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/8417595726672396245/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=8417595726672396245' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/8417595726672396245'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/8417595726672396245'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2007/12/j-and-rubynet-first-steps.html' title='J# and Ruby.Net &amp;#8212; first steps'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-8351078976603661028</id><published>2007-12-13T00:34:00.001Z</published><updated>2008-12-03T14:16:17.294Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>J# and Ruby.Net — not all is rosy in the garden</title><content type='html'>&lt;p&gt;Ruby expects modules to be constants (capitalised).  Just like .Net namespace naming conventions dictate.&lt;/p&gt;
&lt;p&gt;Java, OTOH, uses lower-case package names which map directly to .Net namespaces, which cause Ruby.Net to bork when trying to instantiate classes.  In fact, this is a general issue &amp;#8212; it affects any .Net module names which are not capitalised, like  this C# code&lt;/p&gt;
&lt;pre name="code" class="csharp"&gt;namespace ClassLibrary2.nested
{
    public class Person
    {
        private String name;
        private int age;

        public Person(String name, int age)
        {
            this.name = name;
            this.age = age;
        }

        public void print()
        {
            Console.WriteLine("Hello " + name + " " + age);
        }
    }
}&lt;/pre&gt;
&lt;p&gt;which won't work when invoked by&lt;/p&gt;
&lt;pre name="code" class="ruby"&gt;require 'ClassLibrary2.dll'

name = 'Fred'
age = 42
fred = ClassLibrary2::nested::Person.new(name, age)
fred.print&lt;/pre&gt;

&lt;p&gt;But if you change &lt;code&gt;nested&lt;/code&gt; to &lt;code&gt;Nested&lt;/code&gt;, all is well.&lt;/p&gt;
&lt;p&gt;*le sigh* &amp;#8212; that means an adapter layer between Java-style names and Ruby/.Net ones.  At least that should be doable in Java/J#, even if it makes things more painful than I had hoped.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-8351078976603661028?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/8351078976603661028/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=8351078976603661028' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/8351078976603661028'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/8351078976603661028'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2007/12/j-and-rubynet-not-all-is-rosy-in-garden.html' title='J# and Ruby.Net &amp;#8212; not all is rosy in the garden'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-8383094207339417187</id><published>2007-12-09T22:48:00.002Z</published><updated>2008-12-03T14:15:00.442Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='CTClib'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>What's wrong with this code?</title><content type='html'>&lt;p&gt;Spent today getting about 2/3 coverage on the Bignums class, including rewriting the modularInverse function to complete in a finite time, based on the recursive method &lt;a href="http://en.wikipedia.org/wiki/Extended_Euclidean_algorithm"&gt;given in Wikipedia&lt;/a&gt; -- I can always redo it as iterative if there are problems, but for now I have achieved RSA.&lt;/p&gt;
&lt;p&gt;So everything was running fine in the JVM; go to .Net, and &amp;#8230; infinite loop trying to divide 40! by 20! (in a bit taken from the Ruby bignum unit tests.  Eventually, I tracked it down to this:&lt;/p&gt;
&lt;pre name="code" class="java"&gt;
    private static void s_m_mult_sub(
        int[] result, int ro,
        int[] multi, int mo,
        int scalar, int length)
    {
        long borrow = 0;
        int index = 0;

        /* Is basically practical =&gt; proceed */
        while(index &lt; length)
        {
            long product = multUnitxUnit(scalar, multi[mo+index]) + borrow;
            borrow = product &gt;&gt;&gt; 32;
            product &amp;= MASK;
            if(product &gt; (result[ro+index]&amp;MASK))
                ++borrow;
            result[ro+index++] -= (int)product;
        }
        result[ro+index] -= (int)(borrow&amp;MASK);
    }
&lt;/pre&gt;
&lt;p&gt;which Java  -- and the original 'C' from which this was taken -- runs "as you'd expect" but J# handles as if it were &lt;pre name="code" class="java"&gt;result[ro+index++] = result[ro+index++] - (int)product;&lt;/pre&gt; and increments index twice, as well as splattering other mayhem around the structure.&lt;/p&gt;
&lt;p&gt;Next up, getting JZlib tidied and turning its tests into JUnit form, and adding the PGP needed twiddles; then a first release to replace my old crypt.zip.&lt;/p&gt;
&lt;p&gt;The original 'C' code:&lt;/p&gt;
&lt;pre name="code" class="cpp"&gt;static void s_m_mult_sub(unit result[], unit multi[], unit scalar, unit length)
{
    register unit high, low1, low2;
    unit borrow = 0;
    unit index = 0;

    /* Is basically practical =&gt; proceed */
    while(index &lt; length)
    {
        multUnitxUnit(high, low1, scalar, multi[(size_t)index]);
        low2 = low1 + borrow;
        if(low2 &lt; low1) high++;
        if(low2 &gt; result[(size_t)index])
            borrow = high + 1;
        else
            borrow = high;
        result[(size_t)(index++)] -= low2;
    }
    assert(result[(size_t)index] &gt;= borrow);
    result[(size_t)index] -= borrow;
}&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-8383094207339417187?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/8383094207339417187/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=8383094207339417187' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/8383094207339417187'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/8383094207339417187'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2007/12/whats-wrong-with-this-code.html' title='What&apos;s wrong with this code?'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-1326878051062200116</id><published>2007-12-09T00:06:00.000Z</published><updated>2007-12-09T00:12:22.941Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='CTClib'/><category scheme='http://www.blogger.com/atom/ns#' term='crypto'/><title type='text'>Another step</title><content type='html'>&lt;table&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Package&lt;/td&gt;&lt;td&gt;com.ravnaandtines.crypt.mda&lt;/td&gt;&lt;td&gt;com.ravnaandtines.crypt.cea&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;# Classes&lt;/td&gt;&lt;td&gt;8&lt;/td&gt;&lt;td&gt;31&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Line Coverage&lt;/td&gt;&lt;td&gt;95% 
1051/1104&lt;/td&gt;&lt;td&gt;99% 
1641/1659&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Branch Coverage&lt;/td&gt;&lt;td&gt;81% 
170/210&lt;/td&gt;&lt;td&gt;95% 
399/422&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Complexity&lt;/td&gt;&lt;td&gt;2.369&lt;/td&gt;&lt;td&gt;2.146&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;That includes real test vectors for TEA (and fixing the implementation at last, so it's incompatible with what I used to have for Java, but is real for the first time -- problems with operator precedence and signed vs unsigned shift), as well as things like Safer not implementing the common interface and a few problems with non-zero array offsets and with triple encryption.&lt;/p&gt;
&lt;p&gt;Just need to get bignums and ZLib sorted out with tests, and it'll be time for a proper drop.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-1326878051062200116?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/1326878051062200116/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=1326878051062200116' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/1326878051062200116'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/1326878051062200116'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2007/12/another-step.html' title='Another step'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-2175450955435670900</id><published>2007-12-04T22:52:00.000Z</published><updated>2007-12-04T23:26:50.734Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='CTClib'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>Moore at work</title><content type='html'>&lt;p&gt;The &lt;a href="ftp://ftp.rfc-editor.org/in-notes/rfc2144.txt"&gt;CAST5 algorithm&lt;/a&gt; defines a maintenance test:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;B.2. Full Maintenance Test

    A maintenance test for CAST-128 has been defined to verify the
    correctness of implementations.  It is defined in pseudo-code as
    follows, where a and b are 128-bit vectors, aL and aR are the
    leftmost and rightmost halves of a, bL and bR are the leftmost and
    rightmost halves of b, and encrypt(d,k) is the encryption in ECB mode
    of block d under key k.

    Initial a = 01 23 45 67 12 34 56 78 23 45 67 89 34 56 78 9A (hex)
    Initial b = 01 23 45 67 12 34 56 78 23 45 67 89 34 56 78 9A (hex)
 
    do 1,000,000 times
    {
        aL = encrypt(aL,b)
        aR = encrypt(aR,b)
        bL = encrypt(bL,a)
        bR = encrypt(bR,a)
    }

 Verify a == EE A9 D0 A2 49 FD 3B A6 B3 43 6F B8 9D 6D CA 92 (hex)
 Verify b == B2 C9 5E B0 0C 31 AD 71 80 AC 05 B8 E8 3D 69 6E (hex)&lt;/pre&gt;&lt;/blockquote&gt;
&lt;p&gt;In my code I had a comment "takes about an hour to run", dating from about the end of 1998.  Trying today, I got&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;In shiny new NetBeans 6.0, run from a JUnit 3.8.2 unit test with cobertura coverage instrumentation: 885s&lt;/li&gt;
&lt;li&gt;With instrumentation disabled: 6s&lt;/li&gt;
&lt;li&gt;In VS 2005 as a post-build run of NCover 1.5.8 running the ported test as a debug build: 34s&lt;/li&gt;
&lt;li&gt;In VS 2005 as a post-build run of NCover 1.5.8 running the ported test as a release build: 17s&lt;/li&gt;
&lt;li&gt;In VS 2005 as a command-line run of the test as a release build (100 lines of output piped to a file, rather than the console): 6s&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So, you pay through the nose (~150 times slower) for your branch coverage instrumentation, as opposed to line coverage (~6 times).  And both JVM and CLR are seriously faster, even allowing for the maybe 20x faster processor, than what I was running a decade ago.&lt;/p&gt;
&lt;p&gt;Oh, yeah, today's unit test&amp;#8230;&lt;/p&gt;
&lt;p&gt;It passes.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-2175450955435670900?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/2175450955435670900/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=2175450955435670900' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/2175450955435670900'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/2175450955435670900'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2007/12/moore-at-work.html' title='Moore at work'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-6030674288067374227</id><published>2007-12-02T16:58:00.001Z</published><updated>2008-12-03T14:13:17.102Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='CTClib'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>Making a hash of it</title><content type='html'>&lt;p&gt;Wrote one (meta-)test today.  Find every class in com.ravnaandtines.crypt.mda, instantiate through the default constructor, and check that putting in a short sequence  of bytes one at a time, as an array or as an array-slice all gave the same results.&lt;/p&gt;
&lt;p&gt;On the up-side, this ups coverage:&lt;/p&gt;
&lt;table&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Package&lt;/td&gt;&lt;td&gt;com.ravnaandtines.crypt.mda&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;# Classes&lt;/td&gt;&lt;td&gt;8&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Line Coverage&lt;/td&gt;&lt;td&gt;95% 
1051/1104&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Branch Coverage&lt;/td&gt;&lt;td&gt;81% 
170/210&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Complexity&lt;/td&gt;&lt;td&gt;2.369&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;and found one bug, in BlockCypherHash, where partial blocks were being lost (I wasn't updating the count of pending bytes when I should).&lt;/p&gt;
&lt;p&gt;On the down side, while the test driver was easy to write for J#&lt;/p&gt;
&lt;pre name="code" class="java"&gt;
MD5 prototype = new MD5(); // an arbitrary class in the right assembly
System.Reflection.Assembly algorithms = prototype.GetType().get_Assembly();
System.Type[] types = algorithms.GetTypes();
System.Type mda = algorithms.GetType("com.ravnaandtines.crypt.mda.MDA");

for (int k = 0; k &lt; types.length; ++k )
{
 final System.Type underTest = types[k];
 if (!mda.IsAssignableFrom(underTest))
 {
  continue;
 }
&amp;#8230;&lt;/pre&gt;
&lt;p&gt;but for Java, it requires some magic to do similar blind enumeration of classes.  The following fudge (&lt;a href="http://www.javaworld.com/javaworld/javatips/jw-javatip113.html"&gt;based off this one&lt;/a&gt;, with the fix noted below) gets the all loaded classes in a named package (stuck with Java 1.1 for convenience when cross compiling)&lt;/p&gt;
&lt;pre name="code" class="java"&gt;
    public static java.util.Vector find(final String pckgname) {
        // Translate the package name into an absolute path
        String name = pckgname;
        if (!name.startsWith("/")) {
            name = "/" + name;
        }
        name = name.replace('.', '/');
        // Get a File object for the package
        final java.net.URL url = // get an instance of the current class to hook from
                new CryptTestUtils().getClass().getResource(name);

        // NOTE -- this url will put %20 for spaces in your path 
        // It needs to be cleared up before creating a File object
&lt;span style="font-weight:bold;"&gt;        final String dirpath = java.net.URLDecoder.decode(// 1.2 class easily stubbed for J#
                url.getFile()); &lt;/span&gt;

        final java.io.File directory = new java.io.File(dirpath);
        final java.util.Vector contents = new java.util.Vector();
        if (directory.exists()) {
            // Get the list of the files contained in the package
            final String[] files = directory.list();
            for (int i = 0; i &lt; files.length; i++) {

                // we are only interested in .class files
                if (files[i].endsWith(".class")) {
                    // removes the .class extension
                    final String classname = 
                            files[i].substring(0, files[i].length() - 6);
                    try {
                        // Try to create an instance of the object
                        final Object obj = 
                                Class.forName(pckgname + "." + classname).newInstance();
                        contents.add(obj.getClass()); // save off the class
                    } catch (ClassNotFoundException cnfex) { 
                    } catch (InstantiationException iex) { 
                    // We try to instantiate an interface
                    // or an object that does not have a 
                    // default constructor
                    } catch (IllegalAccessException iaex) { 
                    // The class is not public
                    }
                }
            }
        }
            return contents;
    }
&lt;/pre&gt;
&lt;p&gt;and then we do&lt;/p&gt;
&lt;pre name="code" class="java"&gt;
final java.util.Vector classes = 
                com.ravnaandtines.crypt.CryptTestUtils.find(
                "com.ravnaandtines.crypt.mda"
                );       

for (final java.util.Enumeration iter = classes.elements(); iter.hasMoreElements();)
{
      final Class underTest = (Class) iter.nextElement();

      final Object test = underTest.newInstance();
      if(!(test instanceof com.ravnaandtines.crypt.mda.MDA ))
      {
          continue;
      }
&amp;#8230;&lt;/pre&gt;
&lt;p&gt;So, that's all the hash algorithms done.  Next is the similarly mindless conversion of the crypto algorithm test vectors (and a similar meta-test).  Then turning the JZlib system tests into a set of unit tests, and porting something like the Ruby Bignum unit tests.  Maybe by the turn of the year, I shall actually have a platform based on existing work that can be taken forward.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-6030674288067374227?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/6030674288067374227/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=6030674288067374227' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/6030674288067374227'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/6030674288067374227'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2007/12/making-hash-of-it.html' title='Making a hash of it'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-7297422990376618590</id><published>2007-12-01T20:57:00.000Z</published><updated>2007-12-01T21:05:47.456Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><title type='text'>Tinkering away</title><content type='html'>&lt;p&gt;The standard test vectors for the various algorithms do a good job of shaking down the code for an implementation.&lt;/p&gt;
&lt;table&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Package&lt;/td&gt;&lt;td&gt;com.ravnaandtines.crypt.mda&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;# Classes&lt;/td&gt;&lt;td&gt;8&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Line Coverage&lt;/td&gt;&lt;td&gt;93% 
1024/1103&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Branch Coverage&lt;/td&gt;&lt;td&gt;76% 
159/210&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Complexity&lt;/td&gt;&lt;td&gt;2.369&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Coverage would be higher if there weren't a still few CTC-related entry-points, or code-paths like compensating for a broken Haval implementation in a PGP extension (&lt;a href="http://www.pgpi.org/products/misc/EBP/"&gt;EPB&lt;/a&gt;)&amp;#8230;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-7297422990376618590?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/7297422990376618590/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=7297422990376618590' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/7297422990376618590'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/7297422990376618590'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2007/12/tinkering-away.html' title='Tinkering away'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-1669019390670631613</id><published>2007-11-30T20:13:00.000Z</published><updated>2007-11-30T20:32:57.966Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='GUI'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>Life in the old dog yet…</title><content type='html'>&lt;p&gt;Contradicting my earlier assertion that &amp;#8220;&lt;a href="http://tinesware.blogspot.com/2007/11/shaking-some-dust-off.html"&gt;it's only when you want GUI that you have to get off the fence.&lt;/a&gt;&amp;#8221; &amp;#8212 I've built the Java 1.0-based &lt;a href="http://www.geocities.com/mr_tines/gwt2_2.zip" title="Old, original Java only version with the Javadocs"&gt;Gadget Windowing Toolkit&lt;/a&gt; against &lt;a href="http://uk.geocities.com/mr_tines/code/gwt221.zip" title="sparkling new build for .Net as well"&gt;JVM for Java 5 and (with just a little added C#) CLR&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;It might not be &amp;#8220;teh shiny&amp;#8221;, and won't give you mouse wheel events, but it's the fastest way to a common GUI toolkit for both VMs.&lt;/p&gt;
&lt;p&gt;Weaning it off the Java 1.0 event model that it uses inside a 1.1 emulator, and using Container instead of its own Gadget class (written because Container and Component had protected constructors at Java 1.0) would make it a lot leaner, and more robust, but it's not a simple matter of mechanically replacing the classes.  There is a lot of code dedicated to managing focus and graphics contexts &amp;#8212 including managing the masks and offsets &amp;#8212 that would have to be carefully moved across.&lt;/p&gt;
&lt;p&gt;And of course, there's only the one huge demo/test, rather than unit tests.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-1669019390670631613?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/1669019390670631613/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=1669019390670631613' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/1669019390670631613'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/1669019390670631613'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2007/11/life-in-old-dog-yet.html' title='Life in the old dog yet&amp;#8230;'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-8060988230478060462</id><published>2007-11-27T20:24:00.000Z</published><updated>2007-11-27T21:57:36.501Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='CTClib'/><category scheme='http://www.blogger.com/atom/ns#' term='Ruby'/><title type='text'>Smelling the coffee…</title><content type='html'>&lt;p&gt;&amp;#8230; before work goes crazy in the 4 weeks left to holidays &amp;#8212; a first drop of &lt;a href="http://www.windsong.demon.co.uk/angerona.redcoffee.zip"&gt;angerona.redcoffee.zip&lt;/a&gt;; what may (ha! ha!) become a hybrid Java/Ruby CTClib equivalent for both JVM and CLR.&lt;/p&gt;

&lt;p&gt;This contains code for various symmetric crypto algorithms, strong hashes, erasable big numbers and Zlib compression (&lt;a href="http://www.jcraft.com/jzlib/"&gt;based on JZlib 1.0.7&lt;/a&gt;); it contains some initial &lt;a href="http://www.junit.org/"&gt;JUnit&lt;/a&gt; tests aimed at JUnit 3.8.2 in &lt;a href="http://www.netbeans.org/"&gt;NetBeans 6.0&lt;/a&gt; (currently using RC1).  It also contains a project to build JUnit 3.8.2 (almost) as a J# project generating a command line executable (.exe also included).  The "almost" is because the &lt;code&gt;assertEquals()&lt;/code&gt; methods for &lt;code&gt;Double&lt;/code&gt; and &lt;code&gt;boolean&lt;/code&gt; have been taken from 3.8.1 to permit them to compile under J#.&lt;/p&gt;

&lt;p&gt;The NetBeans project has been amended to use &lt;a href="http://cobertura.sourceforge.net/"&gt;cobertura 1.9&lt;/a&gt; to perform coverage analysis as the unit tests are run -- assumed to be in &lt;code&gt;C:\cobertura-1.9&lt;/code&gt;, adjust &lt;code&gt;build.xml&lt;/code&gt; to fit your location; or use it as a prototype for other projects.  The VS 2005 solution builds the JUnit ~3.8.2 executable, the &lt;code&gt;angerona.algorithms.dll&lt;/code&gt; library, and the unit tests as three projects; and as a post-build step for the last runs the tests under NCover (assumed to be installed to directory &lt;code&gt;C:\Program Files\NCover&lt;/code&gt;) -- I'm using 1.5.4, having had some problems with 1.5.8 run over managed C++ code not contained in gc classes.&lt;/p&gt;

&lt;p&gt;Also included is an FxCop project; I shall be using that run manually, and &lt;a href="http://pmd.sourceforge.net/"&gt;PMD&lt;/a&gt; as a live plug-in while the code is being groomed; they will get incorporated into the build process later when the noise level has been considerably reduced.&lt;/p&gt;

&lt;p&gt;Note that the two build environments are set up to use the same source file structures, though J# has to be hand-held to point it at the files that NetBeans just picks up automagically.&lt;/p&gt;

&lt;p&gt;The only files in any sort of stable state at the moment, the proofs of concept, are &lt;code&gt;SHA0.java&lt;/code&gt; and &lt;code&gt;SHA1.java&lt;/code&gt;, which have unit tests based on the FIPS PUB 180-1 test vectors.  These tests all pass and give 100% coverage (including branch coverage) to the base SHA class and the two wrappers.  The first phase will be getting the state of no PMD warnings, no FxCop warnings (except where naming conventions are involved, and there, Java conventions set out by PMD will win if there is a conflict), unit tests with ideally 100% coverage; this will overlap with doing some refactoring.&lt;/p&gt;

&lt;p&gt;Then with a stable foundation, the plan will be to gradually migrate the rest of CTClib, primarily to Ruby, but probably with a few Java interface types to make the external interface more easily usable by other JVM or CLR projects.&lt;/p&gt;

&lt;p&gt;Archive is 257,055 bytes; MD5 7335e89a 171560f3 003f225e d97a3b3c &amp; SHA-1 4d421c00 d4b3b45d be0244e3 649b19bd 04923a94.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-8060988230478060462?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/8060988230478060462/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=8060988230478060462' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/8060988230478060462'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/8060988230478060462'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2007/11/smelling-coffee.html' title='Smelling the coffee&amp;#8230;'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-2437240217893725685</id><published>2007-11-26T10:06:00.000Z</published><updated>2007-11-26T10:16:57.902Z</updated><title type='text'>Shaking some dust off…</title><content type='html'>&lt;p&gt;After a long distraction by paying work, I'm starting to turn attention back to projects.  The IronPython FTP client still needs wiring up (and bug fixing), but a few developments in recent months -- &lt;a href="http://jruby.codehaus.org/"&gt;JRuby&lt;/a&gt;, and now &lt;a href="http://rubydotnet.googlegroups.com/web/Home.htm"&gt;Ruby.Net&lt;/a&gt; 0.9, have led me to turn my attention once again to taking my old PGP-emulator library, currently languishing as 'C' and a number of abortive ports, out of the 1970s.&lt;/p&gt;
&lt;p&gt;I have several key bits -- crypto algorithm implementations I rolled myself, and, now the JZlib port of the key ZLib component (for reasons best known to PRZ, the version in PGP 2.x uses an 8k window, and no standard APIs give you control over that so as to be able to emit compatible compression) -- in Java.  But now I could build the rest of the system in Ruby and build it to the CLR as well as the JVM.&lt;/p&gt;
&lt;p&gt;Then it's only when you want GUI that you have to get off the fence.&lt;/p&gt;
&lt;p&gt;Of course, uploading the library will mean I shall want to have completed my improved FTP client, so win-win,&amp;#8230; or something&lt;/p&gt;
&lt;p&gt;Meanwhile a long term-goal for self-development stuff at work will involve some F#&amp;#8230;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-2437240217893725685?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/2437240217893725685/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=2437240217893725685' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/2437240217893725685'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/2437240217893725685'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2007/11/shaking-some-dust-off.html' title='Shaking some dust off&amp;#8230;'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-8973360626509475430</id><published>2007-09-28T21:05:00.000+01:00</published><updated>2007-09-28T21:12:51.666+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='FTP'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>One small step…</title><content type='html'>&lt;p&gt;A first part of the refactoring of the file system representation for the local files in the FTP client, removing all use of module &lt;code&gt;os&lt;/code&gt; (to simply deployment by taking out all dependencies on FePy's extended library code), and encapsulating all references to the file data in a &lt;code&gt;LocalFile&lt;/code&gt; class.&lt;/p&gt;
&lt;p&gt;TODO list includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;saving settings&lt;/li&gt;
&lt;li&gt;hooking a remote file system class to the FTP client class&lt;/li&gt;
&lt;li&gt;linking the FTP client and any low-level error feedback to the UI&lt;/li&gt;
&lt;li&gt;Ensuring that file transfers but not directory listings get caught by the progress bar&lt;/li&gt;
&lt;li&gt;Handling reconnects intelligently, including recording current directory state on the remote site&lt;/li&gt;
&lt;li&gt;&amp;#8230;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href="http://uk.geocities.com/mr_tines/code.html#pyFtp"&gt;Drop here again&lt;/a&gt;.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-8973360626509475430?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/8973360626509475430/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=8973360626509475430' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/8973360626509475430'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/8973360626509475430'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2007/09/one-small-step.html' title='One small step&amp;#8230;'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-3796593950109645142</id><published>2007-09-16T20:23:00.000+01:00</published><updated>2007-09-16T20:25:31.653+01:00</updated><title type='text'>Coding formula</title><content type='html'>&lt;p&gt;Normal service has resumed -- i.e. hobby coding is light again, as I'm deep into the dev cycle on the current project at work, the weather has improved since the height of the summer so I'm out at weekends, except I've been being distracted by my teeth, and have just been moping.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-3796593950109645142?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/3796593950109645142/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=3796593950109645142' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/3796593950109645142'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/3796593950109645142'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2007/09/coding-formula.html' title='Coding formula'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-4891459168802906061</id><published>2007-08-28T20:17:00.002+01:00</published><updated>2008-12-03T14:09:24.923Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Ruby'/><title type='text'>10 Minute refactor</title><content type='html'>&lt;p&gt;After a long weekend in the garden...&lt;/p&gt;
&lt;pre name="code" class="ruby"&gt;
module Com_ravnaandtines
  module Zlib
    class Adler32
      # largest prime smaller than 65536
      @@ADLER_BASE = 65521
    
      def initialize
        @value = 1
      end
      
      def update(buffer)
        if not buffer
          return @value
        end
        ## build up the checksum
        low = @value &amp; 0xffff
        high = (@value &gt;&gt; 16) &amp; 0xffff
        buffer.each do |x| 
          low += (x.to_i &amp; 0xff)
          high += low
        end

        ## collapse into modular parts
        low %= @@ADLER_BASE
        high %= @@ADLER_BASE
        @value = (high &lt;&lt; 16) | low      
      end
      
      def reset
        @value = 1
      end
      
    end
  end
end
&lt;/pre&gt;
&lt;p&gt;and tweak the tests thus:&lt;/p&gt;
&lt;pre name="code" class="ruby"&gt;
  def basic_test_engine(seq, expected)
    ## string to array
    ## otherwise expect an each method to yield integers
    if seq.respond_to? :unpack
      seq = seq.unpack("C*")
    end
    adler = Com_ravnaandtines::Zlib::Adler32.new()
    a = adler.update(seq)
    assert_equal(expected, a)
  end

  def test_boundary
    basic_test_engine(nil, 1)  
  end
&lt;/pre&gt;
&lt;p&gt;That feels better.  No special cases, no leakage of state.  Looking at JZlib, most of the work will be in doing the encapsulation properly.&lt;/p&gt;
&lt;p&gt;Next up, though, when time and energy combine, more on the IronPython FTP client.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-4891459168802906061?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/4891459168802906061/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=4891459168802906061' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/4891459168802906061'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/4891459168802906061'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2007/08/10-minute-refactor.html' title='10 Minute refactor'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-2739505405753613597</id><published>2007-08-24T21:30:00.002+01:00</published><updated>2008-12-03T14:09:39.306Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='CTClib'/><category scheme='http://www.blogger.com/atom/ns#' term='Ruby'/><title type='text'>Selection Factors</title><content type='html'>&lt;p&gt;I had started porting CTClib to managed C++ to do an Iron&amp;lt;something&amp;gt; piecemeal conversion.  But at the moment IronPython doesn't have a good deployment story, IronRuby isn't all there; and I &lt;em&gt;still&lt;/em&gt; much prefer Java's UI model to WinForms or WPF&amp;#8230;  So what about JRuby -- familiar UI style and run from jar -- then?&lt;/p&gt;
&lt;p&gt;The stumbling block as always is PhilZ's choice of deflate with a 2^13 bit window (as opposed to Zlib's fixed 2^15 bit window size for Ruby or &lt;code&gt;java.util.zip&lt;/code&gt;) for compression.  This was where I paused my first Java port -- &lt;a href="http://www.jcraft.com/jzlib/"&gt;JZlib&lt;/a&gt; which could do the job has appeared since I last looked, around the turn of the century.  Even so, for sake of portability I've decided to bite the bullet, and do a minimal zlib/deflate implementation in Ruby for the purpose, to do something meaningful with the language, using JZlib as a guide.  Inflate can, of course, have the larger window (as in current CTClib-C builds), and use the built-in version, be it the C version from native Ruby or JRuby's &lt;code&gt;java.util.zip&lt;/code&gt; wrapper.&lt;/p&gt;
&lt;p&gt;So, the easy bit first -- Adler 32 checksum&amp;#8230;&lt;/p&gt;
&lt;pre name="code" class="ruby"&gt;
module Com_ravnaandtines
  module Zlib
    # largest prime smaller than 65536
    ADLER_BASE = 65521
    # Adler32 checksum : takes a seed (usually 1), and a byte sequence, 
    # returns 32-bit integer
    def adler32(adler, buffer)
      if not buffer
        return 1
      end
      
      ## string to array
      ## otherwise expect an each method to yield integers
      if buffer.respond_to? :unpack
        buffer = buffer.unpack("C*")
      end
      
      ## build up the checksum
      low = adler &amp;amp; 0xffff
      high = (adler &amp;gt;&amp;gt; 16) &amp; 0xffff
      buffer.each do |x| 
        low += (x.to_i &amp;amp; 0xff)
        high += low
      end

      ## collapse into modular parts
      low %= ADLER_BASE
      high %= ADLER_BASE
      (high &amp;lt;&amp;lt; 16) | low
    end
  end
end
&lt;/pre&gt;
&lt;p&gt;Test vectors for the unit tests had to be scavenged from the internet:&lt;/p&gt;
&lt;pre name="code" class="ruby"&gt;
require 'test/unit'
require 'tinesware_zlib'
include Com_ravnaandtines::Zlib

class AdlerTest &amp;lt; Test::Unit::TestCase

  def basic_test_engine(seq, expected)
    a = Com_ravnaandtines::Zlib.adler32(1, seq)
    assert_equal(expected, a)
  end

  def test_boundary
    a = Com_ravnaandtines::Zlib.adler32(0, nil)
    assert_equal(1, a)    
  end

  def test_simple_0
    basic_test_engine("Mark Adler", 0x13070394)
  end
  
  def test_simple_1
    basic_test_engine("\x00\x01\x02\x03", 0x000e0007)
  end
 
  def test_simple_2
    basic_test_engine("\x00\x01\x02\x03\x04\x05\x06\x07",  0x005c001d)
  end

  def test_simple_3
    basic_test_engine("\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", 0x02b80079)
  end

  def test_simple_4
    basic_test_engine("\x41\x41\x41\x41", 0x028e0105)
  end

  def test_simple_5
    basic_test_engine("\x42\x42\x42\x42\x42\x42\x42\x42", 0x09500211)
  end

  def test_simple_6
    basic_test_engine("\x43\x43\x43\x43\x43\x43\x43\x43\x43\x43\x43\x43\x43\x43\x43\x43", 0x23a80431)
  end

  class Vector #arrays filled with value = (byte) index
    def initialize(size)
      @size = size
    end
    def each
      index = 0
      while index &amp;lt; @size
        yield index &amp;amp; 0xff
        index += 1
      end
    end
  end

  def test_total
    index = 0
    results = [  486795068,
                1525910894,
                3543032800,
                2483946130,
                4150712693,
                3878123687,
                3650897945,
                1682829244,
                1842395054,
                 460416992,
                3287492690,
                 479453429,
                3960773095,
                2008242969,
                4130540683,
                1021367854,
                4065361952,
                2081116754,
                4033606837,
                1162071911 ]

    
    while index &amp;lt; 20
      size = 5*index + 1
      xx = Vector.new(1000*size)
      basic_test_engine(xx, results[index])
      index += 1
    end
  end

end
&lt;/pre&gt;
&lt;p&gt;That was one evening.  Now, how long will the rest of it take?&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-2739505405753613597?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/2739505405753613597/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=2739505405753613597' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/2739505405753613597'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/2739505405753613597'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2007/08/selection-factors.html' title='Selection Factors'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-3348681059253942582</id><published>2007-08-19T20:09:00.001+01:00</published><updated>2007-09-28T21:12:26.060+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='FTP'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>A wet weekend</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_Y0-w-d5dpBA/RsiVhZl9t0I/AAAAAAAAAaQ/q8rcoz5tbCA/s1600-h/screen-19-08.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://1.bp.blogspot.com/_Y0-w-d5dpBA/RsiVhZl9t0I/AAAAAAAAAaQ/q8rcoz5tbCA/s320/screen-19-08.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5100490979035428674" /&gt;&lt;/a&gt;

&lt;p&gt;The FTP client code is pretty much there, though I still need to write code to drain headers for the HTTP proxy case.  And then the rest is pretty much just plumbing it all together -- of which there will be a fair volume.&lt;/p&gt;
&lt;p&gt;Today's screen capture is how the UI will look during a transfer -- a float-up panel with a live throbber in front of the disabled directory views.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-3348681059253942582?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/3348681059253942582/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=3348681059253942582' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/3348681059253942582'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/3348681059253942582'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2007/08/wet-weekend.html' title='A wet weekend'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_Y0-w-d5dpBA/RsiVhZl9t0I/AAAAAAAAAaQ/q8rcoz5tbCA/s72-c/screen-19-08.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-1831239278522646049</id><published>2007-08-18T18:11:00.001+01:00</published><updated>2008-12-03T14:07:00.817Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='mono'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>After a break</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_Y0-w-d5dpBA/RscoqZl9tzI/AAAAAAAAAaI/ce7g733kPi4/s1600-h/screen-18-08.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://4.bp.blogspot.com/_Y0-w-d5dpBA/RscoqZl9tzI/AAAAAAAAAaI/ce7g733kPi4/s320/screen-18-08.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5100089811910113074" /&gt;&lt;/a&gt;
&lt;p&gt;Last week I was at &lt;a href="http://www.recombination.org.uk/"&gt;Recombination&lt;/a&gt; so nothing done then.&lt;/p&gt;
&lt;p&gt;Now I've started to wire up the logon dialog.  This included a bit of shuffling of the main UI, and preparing to split the DirectoryPanel into UI (repeated) and Directory Model classes (local and FTP); and starting the process of writing an FTP client against the .Net socket APIs.&lt;/p&gt;
&lt;p&gt;Writing the logon dialog exposed one Mono quirk.  The &lt;code&gt;UseSystemPasswordChar&lt;/code&gt; property on the text-box doesn't seem to do anything.  I resorted to &lt;code&gt;self.pwdTB.PasswordChar = u'\u2022'&lt;/code&gt; as a portable solution.&lt;/p&gt;
&lt;p&gt;Also, you can use the Data Protection API for more safely storing passwords:&lt;/p&gt;
&lt;pre name="code" class="py"&gt;
import clr
clr.AddReference("System.Security")
from System.Security.Cryptography import *
import System.Text
import System

&amp;#8230;

  def getSecureValue(self, section, key, default):
    raw = self.getValue(section, key, None)
    if raw== None:
      return default
    try:
      array = System.Convert.FromBase64String(raw)
      chars = ProtectedData.Unprotect(array,
        None, DataProtectionScope.CurrentUser)
      result = System.Text.Encoding.UTF8.GetString(chars)
      for i in range(chars.Length):
        chars[i] = 0
      return result
    except System.Exception, ex:
      print ex.ToString()
      return default

  def setSecureValue(self, section, key, value):
    bytes = System.Text.Encoding.UTF8.GetBytes(value)
    try:
      safed = ProtectedData.Protect(bytes,
        None, DataProtectionScope.CurrentUser)
      for i in range(bytes.Length):
        bytes[i] = 0
      string = System.Convert.ToBase64String(safed)
      for i in range(safed.Length):
        safed[i] = 0
      self.setValue(section, key, string)
    except System.Exception, ex:
      print ex.ToString()

&lt;/pre&gt;&lt;/code&gt;
&lt;p&gt;which stores the password as an encrypted Base64 blob -- and works with Mono on Win32 as well.  The weak point is the password kept in memory as a string (immutable) -- if you can use it as char array, you can wipe that when done.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-1831239278522646049?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/1831239278522646049/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=1831239278522646049' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/1831239278522646049'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/1831239278522646049'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2007/08/after-break.html' title='After a break'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_Y0-w-d5dpBA/RscoqZl9tzI/AAAAAAAAAaI/ce7g733kPi4/s72-c/screen-18-08.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-8128310049103621335</id><published>2007-08-07T19:41:00.000+01:00</published><updated>2007-08-19T20:13:47.697+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='FTP'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>Meets minimum</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_Y0-w-d5dpBA/Rri9IBb-RSI/AAAAAAAAAaA/QkiLklZx0NY/s1600-h/screen-07-08.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://1.bp.blogspot.com/_Y0-w-d5dpBA/Rri9IBb-RSI/AAAAAAAAAaA/QkiLklZx0NY/s320/screen-07-08.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5096030923892081954" /&gt;&lt;/a&gt;
&lt;p&gt;After a bit more fiddling with P/Invoke code, I have hooked into the Win32 Recycle or Delete APIs; though at the moment multiple file delete is done one file at a time.  But the directory pane is roughly meets-minimum standard.&lt;/p&gt;
&lt;p&gt;Next I can get to the object of the exercise, making an FTP connection and getting the remote directory information back to its own version of &lt;code&gt;DirectoryPane&lt;/code&gt;.  This will be an interesting combination of &lt;code&gt;BackgroundWorker&lt;/code&gt; and asynchronous I/O, with pauses built in so as to throttle the uplink to what my router is prepared to handle.&lt;/p&gt;
&lt;p&gt;And then I get to tweak it -- like switching to WPF if available -- and port it -- Mono on Debian working around the P/Invoke stuff.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-8128310049103621335?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/8128310049103621335/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=8128310049103621335' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/8128310049103621335'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/8128310049103621335'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2007/08/meets-minimum.html' title='Meets minimum'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_Y0-w-d5dpBA/Rri9IBb-RSI/AAAAAAAAAaA/QkiLklZx0NY/s72-c/screen-07-08.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-7359799311567273929</id><published>2007-08-05T16:38:00.001+01:00</published><updated>2007-08-07T19:50:05.723+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mono'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>Directory pane almost there</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_Y0-w-d5dpBA/RrXvLBb-RRI/AAAAAAAAAZ4/bfpUbtnl8zQ/s1600-h/screen-05-08.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://2.bp.blogspot.com/_Y0-w-d5dpBA/RrXvLBb-RRI/AAAAAAAAAZ4/bfpUbtnl8zQ/s320/screen-05-08.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5095241526082946322" /&gt;&lt;/a&gt;
&lt;p&gt;Some of the problems I was cursing Mono for turn out to be IronPython 1.x-isms -- mainly the insisting on having Images rather than Icons.  Mono has other peculiarities, like in how it actually lays out widgets (not that .Net is much better) at runtime, so for the moment I'm forcing the issue by trapping any size change to the directory pane so that it fits inside the viewport.&lt;/p&gt;
&lt;p&gt;Apart from wiring up the delete context menu, the directory pane is done.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-7359799311567273929?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/7359799311567273929/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=7359799311567273929' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/7359799311567273929'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/7359799311567273929'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2007/08/directory-pane-almost-there.html' title='Directory pane almost there'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_Y0-w-d5dpBA/RrXvLBb-RRI/AAAAAAAAAZ4/bfpUbtnl8zQ/s72-c/screen-05-08.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-5064522462772252732</id><published>2007-08-04T21:17:00.000+01:00</published><updated>2007-08-05T16:43:32.788+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mono'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>More Mono</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_Y0-w-d5dpBA/RrTfGhb-RQI/AAAAAAAAAZw/TNK6MoY5_E8/s1600-h/screen-04-08.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://3.bp.blogspot.com/_Y0-w-d5dpBA/RrTfGhb-RQI/AAAAAAAAAZw/TNK6MoY5_E8/s320/screen-04-08.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5094942381610779906" /&gt;&lt;/a&gt;
&lt;p&gt;A lot of work done for the benefit of Mono, redoing how I wrap files to use os.path rather than .Net directly; and rewriting the P/Invoke layer from scratch (and from &lt;a href="http://www.pinvoke.net/"&gt;pinvoke.net&lt;/a&gt;) --including finding that &lt;code&gt;Bitmap.FromHicon()&lt;/code&gt; is the simplest way of getting the transparent pixels roughly honoured as well as feeding that system what it wants.&lt;/p&gt;
&lt;p&gt;I said that the &lt;code&gt;DirectoryPane&lt;/code&gt; would be the worst of it, and it's doing its best to prove me right.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-5064522462772252732?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/5064522462772252732/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=5064522462772252732' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/5064522462772252732'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/5064522462772252732'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2007/08/more-mono.html' title='More Mono'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_Y0-w-d5dpBA/RrTfGhb-RQI/AAAAAAAAAZw/TNK6MoY5_E8/s72-c/screen-04-08.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-4154188309620667411</id><published>2007-07-30T21:19:00.000+01:00</published><updated>2007-08-04T21:23:35.615+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mono'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>Not quite so pretty</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_Y0-w-d5dpBA/Rq5ICBb-RPI/AAAAAAAAAZo/_KRVOdBxdmo/s1600-h/screen3.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://3.bp.blogspot.com/_Y0-w-d5dpBA/Rq5ICBb-RPI/AAAAAAAAAZo/_KRVOdBxdmo/s320/screen3.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5093087428185310450" /&gt;&lt;/a&gt;
&lt;p&gt;After remarkably little work, it runs under Mono 1.2.4 and IPCE-r6.  The main changes are that as Mono doesn't support &lt;code&gt;System.Management&lt;/code&gt; (and the extra work wasn't useful for the icon related info), I fell back to &lt;code&gt;Directory.GetLogicalDrives()&lt;/code&gt;, and had to do a bit of type tweaking (explicitly converting to &lt;code&gt;Bitmap&lt;/code&gt; to draw the icons).&lt;/p&gt;
&lt;p&gt;Everything else is non-visible : the lower left sector has been factored out into a separate class and modules have been rationalised, ahead of making a next step in functionality.&lt;/p&gt;
&lt;p&gt;First drop for the curious and for back-up distribution. &lt;span style="font-style:italic;"&gt;Now updated.&lt;/span&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-4154188309620667411?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/4154188309620667411/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=4154188309620667411' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/4154188309620667411'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/4154188309620667411'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2007/07/not-quite-so-pretty.html' title='Not quite so pretty'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_Y0-w-d5dpBA/Rq5ICBb-RPI/AAAAAAAAAZo/_KRVOdBxdmo/s72-c/screen3.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-7903791498129491818</id><published>2007-07-29T17:17:00.000+01:00</published><updated>2007-07-29T20:54:52.999+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>You move 16 tons...</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_Y0-w-d5dpBA/RqzAkxb-RNI/AAAAAAAAAZY/kVV_OY7ZYy8/s1600-h/screen2.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://4.bp.blogspot.com/_Y0-w-d5dpBA/RqzAkxb-RNI/AAAAAAAAAZY/kVV_OY7ZYy8/s320/screen2.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5092657016627676370" /&gt;&lt;/a&gt;
&lt;p&gt;The gaps in the Win32 API support in .Net are random and frustrating.  You can &lt;a href="http://stevegilham.blogspot.com/2007/07/logical-drive-enumeration-in-ironpython.html"&gt;get all the drive letters&lt;/a&gt; (and the "DriveType" or "MediaType" properties which together give the offset into Shell32.dll for the drive icon), but short of unpicking the PE format by hand, Google gives no way to get the icons without using P/Invoke.&lt;/p&gt;
&lt;p&gt;So with a combination of existing recipes for &lt;a href="http://www.vbaccelerator.com/home/NET/Code/Libraries/Shell_Projects/Getting_File_Icons_Using_The_Shell/article.asp"&gt;wrapping SHGetFileInfo in C#&lt;/a&gt;, and feeding it through &lt;a href="http://www.voidspace.org.uk/ironpython/dynamically_compiling.shtml#compiling-c"&gt;Fuzzyman's dynamic compiler utility&lt;/a&gt;, I have some approximately script-like behaviour for icon extraction -- as can be seen from the left-hand dropdown in the screenshot.&lt;/p&gt;
&lt;p&gt;I still need to add some extra hooks to use SHGetFileInfo to check if a file is executable or not, so I can get custom icons, and then refactor out a proper utility.  And there's still more work on the generic directory pane widget before it'll be something stand-alone (sorted file list, hooking up events); though I hope that should not require any more exploting of exotic APIs to achieve.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-7903791498129491818?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/7903791498129491818/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=7903791498129491818' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/7903791498129491818'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/7903791498129491818'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2007/07/you-move-16-tons.html' title='You move 16 tons...'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_Y0-w-d5dpBA/RqzAkxb-RNI/AAAAAAAAAZY/kVV_OY7ZYy8/s72-c/screen2.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-7512350863106870781</id><published>2007-07-28T19:18:00.001+01:00</published><updated>2007-07-28T19:21:42.049+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='FTP'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>Sneak Peek</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_Y0-w-d5dpBA/RquIhxb-RMI/AAAAAAAAAZQ/c3_4kq4NxXo/s1600-h/screen.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://3.bp.blogspot.com/_Y0-w-d5dpBA/RquIhxb-RMI/AAAAAAAAAZQ/c3_4kq4NxXo/s320/screen.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5092313917460202690" /&gt;&lt;/a&gt;
&lt;p&gt;I've starting recoding my FTP client in IronPython, mainly as a self-teaching exercise (but hoping it might work in Mono/IPCE too).  So far, this is the shell of the main UI, with a ribbon-style bar at the top.&lt;/p&gt;
&lt;p&gt;The biggest piece of work is likely to be the Directory Pane component that Fox just gave to me on a plate.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-7512350863106870781?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/7512350863106870781/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=7512350863106870781' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/7512350863106870781'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/7512350863106870781'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2007/07/sneak-peek.html' title='Sneak Peek'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_Y0-w-d5dpBA/RquIhxb-RMI/AAAAAAAAAZQ/c3_4kq4NxXo/s72-c/screen.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-5825355199486134703</id><published>2007-06-29T20:35:00.001+01:00</published><updated>2008-02-19T21:36:25.449Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='FTP'/><title type='text'>PassiveFTP 1.10</title><content type='html'>&lt;p&gt;Now available on the home server too.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-5825355199486134703?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/5825355199486134703/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=5825355199486134703' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/5825355199486134703'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/5825355199486134703'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2007/06/passiveftp-110_29.html' title='PassiveFTP 1.10'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-7684882289607228417</id><published>2007-06-27T22:46:00.000+01:00</published><updated>2007-06-27T22:48:31.657+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='FTP'/><category scheme='http://www.blogger.com/atom/ns#' term='C/C++'/><title type='text'>PassiveFTP 1.10</title><content type='html'>&lt;p&gt;As promised&amp;#8230;&lt;/p&gt;
&lt;p&gt;PassiveFTP 1.10 &amp;#8212; build 1.10.2734.38706
&amp;#8212; squeezes in an up-one-level button into the remote site list,
for FTP sites that manage to conceal their ".." directories.  Or for
people who'd prefer it anyway.&lt;/p&gt;
&lt;p&gt;From the GeoCities Windows page at the moment.  I need to do a physical rather than a remote logon to my home server before I can upload to that, so in a day or two.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-7684882289607228417?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/7684882289607228417/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=7684882289607228417' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/7684882289607228417'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/7684882289607228417'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2007/06/passiveftp-110.html' title='PassiveFTP 1.10'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-8174743848770520567</id><published>2007-06-26T21:38:00.001+01:00</published><updated>2007-06-26T21:39:34.819+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='FTP'/><category scheme='http://www.blogger.com/atom/ns#' term='C/C++'/><title type='text'>PassiveFTP -- new revision</title><content type='html'>&lt;p&gt;As the revised Demon homepages site doesn't provide ".." as a directory in the default &lt;code&gt;ls&lt;/code&gt; response, I shall have to add an explicit "go up one level" button to the server side.&lt;/p&gt;

&lt;p&gt;Expect version 1.10 soonish.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-8174743848770520567?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/8174743848770520567/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=8174743848770520567' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/8174743848770520567'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/8174743848770520567'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2007/06/passiveftp-new-revision.html' title='PassiveFTP -- new revision'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-3289983780935250879</id><published>2007-06-24T16:55:00.000+01:00</published><updated>2007-06-24T16:57:32.968+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='CTClib'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Current directions</title><content type='html'>&lt;p&gt;CTClib/Angerona is taking a strange turn.&lt;/p&gt;
&lt;p&gt;I want to use IronPython for .Net and Mono UI; but for the core library keep it as pure Python as feasible. But to get there, I'm porting via C++/CLI.&lt;/p&gt;
&lt;p&gt;I have most of it building (I have not ported I/O, or deflate as yet), and I'm separating out the test code into a more formal set of unit tests.&lt;/p&gt;
&lt;p&gt;The plan goes get it into "managed 'C'" with test coverage (side release 1); refactor to provide CLR interfaces at suitable places (side release 2); then pull code through the language barrier, one module at a time, to achieve a pyCTC library (library release 1), followed by a WinForms GUI on top. Or maybe Jython for the GUI.&lt;/p&gt;
&lt;p&gt;No promises, as usual.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-3289983780935250879?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/3289983780935250879/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=3289983780935250879' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/3289983780935250879'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/3289983780935250879'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2007/06/current-directions.html' title='Current directions'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-5989101285017849696</id><published>2007-04-27T16:50:00.001+01:00</published><updated>2008-12-03T14:05:13.768Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Drophash in Python dialects</title><content type='html'>&lt;h4&gt;Iron Python 1.1&lt;/h4&gt;
&lt;p&gt;Now for IronPython 1.1 — annoyingly the WebBrowser control doesn't accept normal DnD, nor does it let you paint it to a bitmap to blit on another control, so this one uses a ListView.&lt;/p&gt;
&lt;pre class="py" name="code"&gt;
import clr
clr.AddReferenceByPartialName("System.Windows.Forms")
clr.AddReferenceByPartialName("System.Drawing")

from System.Windows.Forms import *
from System.Drawing import *

from md5 import *
from sha import *

pane = None
hashes = [md5, sha]
nbsp = list(" ")


def getHash(file, hashOp, delimiter) :

    try :
        reader = open(file, 'rb')
    except :
        return ""
   
    try :
        digest = hashOp()
        while True :
            chunk = reader.read(1024)
            if not chunk :
                break
            digest.update(chunk)
    finally:
        reader.close()

    raw = digest.hexdigest()
    work = []
    i = 0
    while i &lt; len(raw) :
        work.append(raw[i])
        i += 1
        if 0 == (i%8) :
            work = work + delimiter

    return ''.join(work)

def dragOver(sender, evt):
  evt.Effect = DragDropEffects.Link
 
def dragDrop(sender, evt):
  global pane
  data = evt.Data
  data = data.GetData(DataFormats.FileDrop)
  num = pane.Items.Count
 
  for file in data:
    pane.Items.Add(file)
    for hashOp in hashes :
      anHash = getHash(file, hashOp, nbsp)
      pane.Items[num].SubItems.Add(anHash)
    num += 1
      
    


def decorate(frame):
    global pane
    frame.Text = "IronPython - Drophash"
    ## optional icon
    ## ico = Image.FromFile("drophash.ico")
    ## frame.Icon = Icon.FromHandle(ico.GetHicon());
    pane = ListView()
    pane.Dock = DockStyle.Fill;
    frame.Controls.Add(pane)
    pane.AllowDrop = True
    pane.DragOver += dragOver
    pane.DragEnter += dragOver
    pane.DragDrop += dragDrop
    pane.View = View.Details
    pane.Columns.Add("File name", 100, HorizontalAlignment.Left)
    pane.Columns.Add("MD5", 200, HorizontalAlignment.Left)
    pane.Columns.Add("SHA-1", 200, HorizontalAlignment.Left)


if __name__ == "__main__":
    frame = Form()
    decorate(frame)
    Application.Run(frame)
&lt;/pre&gt;

&lt;h4&gt;Jython 2.2&lt;/h4&gt;
&lt;p&gt;Jython next; but rather than coding the messy .ico to image transformation again, use a PNG icon (save as drophash.png) instead:—&lt;/p&gt;

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp3.blogger.com/_Y0-w-d5dpBA/Rn6TRo3NBRI/AAAAAAAAAP0/3xgANWUrgxc/s1600-h/drophash.png"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://bp3.blogger.com/_Y0-w-d5dpBA/Rn6TRo3NBRI/AAAAAAAAAP0/3xgANWUrgxc/s320/drophash.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5079659360956515602" /&gt;&lt;/a&gt;

&lt;pre class="py" name="code" style="clear: both;"&gt;
import sys
import os.path
sys.packageManager.makeJavaPackage("javax.swing", "JWindow", None)
sys.packageManager.makeJavaPackage("java.awt", "Window", None)
sys.packageManager.makeJavaPackage("java.awt.dnd", "DropTargetListener", None)
sys.packageManager.makeJavaPackage("java.awt.datatransfer", "DataFlavor", None)
import javax.swing
import java.lang
import java.awt.dnd
import java.awt.datatransfer

from md5 import *
from sha import *

pane = None
hashes = [md5, sha]
nbsp = list("&amp;nbsp;")
target = None

def getHash(file, hashOp, delimiter) :

  try :
    reader = open(file, 'rb')
  except :
    return ""
  
  try :
    digest = hashOp()

  while True :
    chunk = reader.read(1024)
    if not chunk :
      break

      digest.update(chunk)
  finally:
    reader.close()
        
  raw = digest.hexdigest()
  work = []
  i = 0
  while i &amp;lt; len(raw) :
    work.append(raw[i])
    i += 1
    if 0 == (i%8) :
      work = work + delimiter

  return ''.join(work)
  
def OnDropFiles(filenames) :
  global pane, nbsp, hashes
  pane.text += "&amp;lt;table border=1&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;File&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;MD5&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;SHA-1&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;"
  for file in filenames :
    pane.text += "&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;%s&amp;lt;/td&amp;gt;" % file

    for hashOp in hashes :
      anHash = getHash(str(file), hashOp, nbsp)
      pane.text += "&amp;lt;td&amp;gt;&amp;lt;pre&amp;gt;%s&amp;lt;/pre&amp;gt;&amp;lt;/td&amp;gt;" % anHash

  pane.text +="&amp;lt;/tr&amp;gt;"
  pane.text +="&amp;lt;/table&amp;gt;"

  

class dropHandler(java.awt.dnd.DropTargetAdapter):
  def __init__(self):
    java.awt.dnd.DropTargetAdapter.__init__(self)

  def drop(self, evt):
    if not evt.transferable.isDataFlavorSupported(java.awt.datatransfer.DataFlavor.javaFileListFlavor):
      return
    evt.acceptDrop(-1)
    names = evt.transferable.getTransferData(java.awt.datatransfer.DataFlavor.javaFileListFlavor)
    OnDropFiles(names)

def decorate(frame):
  global pane
  frame.setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE)
  frame.title = "Jython - Drophash"
  frame.layout = java.awt.BorderLayout()

  pane = javax.swing.JLabel()
  pane.border = javax.swing.BorderFactory.createEmptyBorder(5,5,5,5)
  frame.contentPane.add(javax.swing.JScrollPane(pane), java.awt.BorderLayout.CENTER)
  pane.text = "&amp;lt;html&amp;gt;"
  icon = java.awt.Toolkit.getDefaultToolkit().createImage("drophash.png")
  frame.setIconImage(icon)
  target = java.awt.dnd.DropTarget(pane, dropHandler())
  
  
  width = 800
  height = 600
  frame.setSize(width, height);
  screenDim = java.awt.Toolkit.getDefaultToolkit().getScreenSize();
  frame.setLocation(
        (screenDim.width - width) / 2,
        (screenDim.height - height) / 2
        )
  



if __name__ == "__main__":
  frame = javax.swing.JFrame()
  decorate(frame)
  frame.visible = True

Made with Cascading Style Sheets
&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-5989101285017849696?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/5989101285017849696/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=5989101285017849696' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/5989101285017849696'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/5989101285017849696'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2007/04/drophash-in-python-dialects.html' title='Drophash in Python dialects'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp3.blogger.com/_Y0-w-d5dpBA/Rn6TRo3NBRI/AAAAAAAAAP0/3xgANWUrgxc/s72-c/drophash.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-6460754938341411489</id><published>2007-03-03T16:40:00.001Z</published><updated>2008-12-03T14:04:13.227Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Drophash in Python</title><content type='html'>&lt;p&gt;First little Python project, a
    little utility to take the MD5 and SHA hashes of a bunch of files
    dragged and dropped onto it. Same function as but better presentation
    that a Win32 version (on the Windows page) I wrote back in
    late '00 (and which I have still been making regular use of for checking
    MD5s of file downloads).&lt;/p&gt;
&lt;h4&gt;WxPython UI&lt;/h4&gt;
&lt;p&gt;This one is neater than the Win32 version since I can use the nifty HTML window widget and place the results for each new batch of files in a table, with the files and hashes cross-indexed.

Simply cut, paste and save the text of the section below as drophash.py.&lt;/p&gt;

&lt;pre class="py" name="code"&gt;
#!/usr/bin/env python
# -*- coding: ISO-8859-1 -*-

#MD5 test suite:
#MD5 ("") = d41d8cd98f00b204e9800998ecf8427e
#MD5 ("a") = 0cc175b9c0f1b6a831c399e269772661
#MD5 ("abc") = 900150983cd24fb0d6963f7d28e17f72
#MD5 ("message digest") = f96b697d7cb7938d525a2f31aaf161d0
#MD5 ("abcdefghijklmnopqrstuvwxyz") = c3fcd3d76192e4007dfb496cca67e13b
#MD5 ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") = d174ab98d277d9f5a5611c2c9f419d9f
#MD5 ("12345678901234567890123456789012345678901234567890123456789012345678901234567890") = 57edf4a22be3c955ac49da2e2107b67a

#SHA test suite:
#SHA ("abc") = A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
#SHA ("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq") = 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
#SHA (A million repetitions of "a") = 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F


import wx
import wx.html as html
from md5 import *
from sha import *

def getHash(file, hashOp, delimiter) :

    try :
        reader = open(file, 'rb')
    except :
        return ""
    
    try :
        digest = hashOp()
        while True :
            chunk = reader.read(1024)
            if not chunk :
                break
            digest.update(chunk)
    finally:
        reader.close()
        
    raw = digest.hexdigest()
    work = []
    i = 0
    while i &lt; len(raw) :
        work.append(raw[i])
        i += 1
        if 0 == (i%8) :
            work = work + delimiter

    return ''.join(work)

class FileDropTarget(wx.FileDropTarget):
    def __init__(self, window):
        wx.FileDropTarget.__init__(self)
        self.window = window

    hashes = (md5, sha)
    nbsp = list("&amp;nbsp;")

    def OnDropFiles(self, x, y, filenames) :
        self.window.AppendToPage("&amp;lt;table border=1&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;File&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;MD5&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;SHA-1&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;")
        for file in filenames :
            self.window.AppendToPage("&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;%s&amp;lt;/td&amp;gt;" % file)

            for hashOp in self.hashes :
                anHash = getHash(file, hashOp, self.nbsp)
                self.window.AppendToPage("&amp;lt;td&amp;gt;&amp;lt;pre&amp;gt;%s&amp;lt;/pre&amp;gt;&amp;lt;/td&amp;gt;" % anHash)

            self.window.AppendToPage("&amp;lt;/tr&amp;gt;")
        self.window.AppendToPage("&amp;lt;/table&amp;gt;")

def decorate(frame):
    frame.SetTitle("wxPython - Drophash")
    ## optionally, an icon
    ## _icon = wx.EmptyIcon()
    ## _icon.CopyFromBitmap(wx.Bitmap("drophash.ico", wx.BITMAP_TYPE_ANY))
    ## frame.SetIcon(_icon)
    frame.style = wx.DEFAULT_FRAME_STYLE
    pane = html.HtmlWindow(frame, -1)
    dt = FileDropTarget(pane)
    pane.SetDropTarget(dt)


if __name__ == "__main__":
    theApp = wx.App(False)
    frame  = wx.Frame(None, -1)
    decorate(frame)
    theApp.SetTopWindow(frame)
    frame.Show()
    theApp.MainLoop()
&lt;/pre&gt;

&lt;h4&gt;Python + Tk UI&lt;/h4&gt;
&lt;p&gt;As an alternative, a pure Tk version. Not so fancy; you have to select the files you want (or put them on the command line). Just extract this section as tkhash.py and double click…&lt;/p&gt;
&lt;pre class="py" name="code"&gt;
#!/usr/bin/env python
# -*- coding: ISO-8859-1 -*-

#MD5 test suite:
#MD5 ("") = d41d8cd98f00b204e9800998ecf8427e
#MD5 ("a") = 0cc175b9c0f1b6a831c399e269772661
#MD5 ("abc") = 900150983cd24fb0d6963f7d28e17f72
#MD5 ("message digest") = f96b697d7cb7938d525a2f31aaf161d0
#MD5 ("abcdefghijklmnopqrstuvwxyz") = c3fcd3d76192e4007dfb496cca67e13b
#MD5 ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") = d174ab98d277d9f5a5611c2c9f419d9f
#MD5 ("12345678901234567890123456789012345678901234567890123456789012345678901234567890") = 57edf4a22be3c955ac49da2e2107b67a

#SHA test suite:
#SHA ("abc") = A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
#SHA ("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq") = 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
#SHA (A million repetitions of "a") = 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F

import Tkinter
import ScrolledText
import tkFileDialog
import sys
import tkFont

from md5 import *
from sha import *

hashes = {"MD5" : md5, "SHA" : sha}
nbsp = list(" ")

def getHash(file, hashOp, delimiter) :

    try :
        reader = open(file, 'rb')
    except :
        return ""
    
    try :
        digest = hashOp()
        while True :
            chunk = reader.read(1024)
            if not chunk :
                break
            digest.update(chunk)
    finally:
        reader.close()
        
    raw = digest.hexdigest()
    work = []
    i = 0
    while i &lt; len(raw) :
        work.append(raw[i])
        i += 1
        if 0 == (i%8) :
            work = work + delimiter

    return ''.join(work)


def doGetHashes() :
  filenames = tkFileDialog.askopenfilenames()
  updateCanvas(filenames)
  
def updateCanvas(filenames):
  textarea.config(state=Tkinter.NORMAL)
  for file in filenames :
    textarea.insert(Tkinter.END, ("%s\n" % file))

    for hashOp in hashes :
      anHash = getHash(file, hashes[hashOp], nbsp)
      textarea.insert(Tkinter.END, ("%s " % hashOp) )

      textarea.insert(Tkinter.END, ("%s\n" % anHash), 'mono' )

    textarea.insert(Tkinter.END, "-------------------------\n")
  textarea.tag_config('mono', font='courier')
  textarea.config(state=Tkinter.DISABLED)

if __name__ == "__main__":
  root = Tkinter.Tk()
  root.title("tkFileHasher")

  textarea = ScrolledText.ScrolledText()
  textarea.pack()
  textarea.config(state=Tkinter.DISABLED)
  Tkinter.Button(text="Select files to hash...", command=doGetHashes).pack()
  updateCanvas(sys.argv[1:])
  Tkinter.mainloop()
&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-6460754938341411489?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/6460754938341411489/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=6460754938341411489' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/6460754938341411489'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/6460754938341411489'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2007/03/drophash-in-python.html' title='Drophash in Python'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-3342269509716866581</id><published>2007-02-14T16:38:00.000Z</published><updated>2007-06-24T16:40:00.644+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><title type='text'>Fun with JavaScript</title><content type='html'>&lt;p&gt;&lt;a href="http://www.geocities.com/mr_tines/enkoderScript.html"
    title="E-mail address obfuscator"&gt;&amp;#8220;E&amp;#8221;-ddress encoder script 1.0&lt;/a&gt;
    &amp;#8212; a utility based on the the old Hiveware &amp;#8220;Enkoder&amp;#8221;, only this time
    done entirely in JavaScript. Because you can.&lt;/p&gt;
&lt;p&gt;
    Also the same thing for &lt;a href="http://www.geocities.com/mr_tines/acreteScript.html"
    title="Planetary system simulation"&gt;Acrete script 1.0&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;    This sort of thing doesn't need heavyweight stuff like a Java applet;
    though Acrete currently uses the &amp;lt;canvas&amp;gt; element which isn't
    supported in IE (though I'm investigating alternatives for that). And,
    besides, I might as well do something useful, rather than just playing,
    with dynamic languages like JavaScript, Ruby and Python. Java projects
    (at least, those not for work) have probably reached the end of the road
    as a result.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-3342269509716866581?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/3342269509716866581/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=3342269509716866581' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/3342269509716866581'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/3342269509716866581'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2007/02/fun-with-javascript.html' title='Fun with JavaScript'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-8277977594474433884</id><published>2006-10-30T16:37:00.000Z</published><updated>2007-06-24T16:38:32.555+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><title type='text'>Acrete update</title><content type='html'>&lt;p&gt;A small tweak to the previous entry;
    code included in the Jar file. &lt;a
    href="http://www.geocities.com/mr_tines/acrete.html"&gt;Inline demo and code download page&lt;/a&gt;. To run 
    &lt;pre class="code"&gt;java -jar acrete.jar&lt;/pre&gt;

  &lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-8277977594474433884?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/8277977594474433884/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=8277977594474433884' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/8277977594474433884'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/8277977594474433884'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2006/10/acrete-update.html' title='Acrete update'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-4520313693541194719</id><published>2006-10-25T16:36:00.000+01:00</published><updated>2007-06-24T16:37:20.887+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><title type='text'>Blast from the past</title><content type='html'>&lt;p&gt;A Java port of the
    re-implementation I did of Dole's Acrete program for growing solar
    systems that first appeared in &lt;cite&gt;White Dwarf&lt;/cite&gt; 57. The port is
    not elegant, and used a lot of &amp;#8220;here's one I prepared earlier&amp;#8221; for
    the animation rather than using Swing.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-4520313693541194719?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/4520313693541194719/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=4520313693541194719' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/4520313693541194719'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/4520313693541194719'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2006/10/blast-from-past.html' title='Blast from the past'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-1935254213412759189</id><published>2006-06-18T16:35:00.001+01:00</published><updated>2007-06-24T16:36:33.873+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='CTClib'/><category scheme='http://www.blogger.com/atom/ns#' term='C/C++'/><title type='text'>CTCJava 2.0 Beta state</title><content type='html'>&lt;p&gt;
    Meanwhile CTCJava 2.0 is (slowly) undergoing a massive scrubbing with the
    help of PMD, Cobertura, JUnit and MiniCppUnit, so declaring beta may have
    been a tad premature.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-1935254213412759189?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/1935254213412759189/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=1935254213412759189' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/1935254213412759189'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/1935254213412759189'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2006/06/ctcjava-20-beta-state.html' title='CTCJava 2.0 Beta state'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-8471741013283190031</id><published>2006-06-18T16:35:00.000+01:00</published><updated>2007-06-24T16:35:54.678+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='FTP'/><category scheme='http://www.blogger.com/atom/ns#' term='C/C++'/><title type='text'>PassiveFTP 1.9</title><content type='html'>&lt;p&gt;PassiveFTP 1.9 &amp;#8212; build
    1.9.2360.19246 &amp;#8212; is a minor usability tweak, moving status indications
    from dialogs to the main screen - progress at the lower-left, errors in
    the scrolling text at top right.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-8471741013283190031?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/8471741013283190031/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=8471741013283190031' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/8471741013283190031'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/8471741013283190031'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2006/06/passiveftp-19.html' title='PassiveFTP 1.9'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-232610558370346266</id><published>2006-01-22T16:34:00.000Z</published><updated>2007-06-24T16:34:59.734+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><title type='text'>CTCJava 2.0 Beta 2 - update</title><content type='html'>&lt;p&gt;Testing in advance of Beta 3 has shown that there are
    some problems with managing keyrings in key generation. This includes
    some problems with the handle to the JVM environment through multiple
    levels of calls Java-&amp;gt;native-&amp;gt;Java-&amp;gt;native...; and failing to
    pick up the keyring files once they have been updated. This will take
    more than simple refactoring or easy Swing UI tweaks.&lt;/p&gt;
    &lt;p&gt;
    If the bug turns out to be made more difficult to resolve because of the
    back and forth calling, I may end up pulling more of the OpenPGP
    functionality into the Java layer. I will certainly be making it more C++
    with a C interface where that simplifies matters for me.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-232610558370346266?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/232610558370346266/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=232610558370346266' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/232610558370346266'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/232610558370346266'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2006/01/ctcjava-20-beta-2-update.html' title='CTCJava 2.0 Beta 2 - update'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-3187257571694063765</id><published>2006-01-14T16:33:00.000Z</published><updated>2007-06-24T16:34:04.305+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='CTClib'/><title type='text'>CTCJava 2.0 Beta 2</title><content type='html'>&lt;li&gt;&lt;a     href="http://www.windsong.demon.co.uk/CTCJava2beta2.7z"&gt;Beta 2 of CTCJava
    2.0&lt;/a&gt; &amp;#8212; a simpler archive structure (archive = archive2 + signature;
    archive2 = jar+java source zip+dll 2.3.2205.36688+ctcjlib JNI layer
    source). The main change is that documents are shown in the main window,
    by selecting the current document from the tree control, though there are
    other minor UI changes and behind the scenes updates.&lt;/p&gt;
    &lt;p&gt;
    There will be at least one more interation with UI tweaks before I
    declare RC.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-3187257571694063765?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/3187257571694063765/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=3187257571694063765' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/3187257571694063765'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/3187257571694063765'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2006/01/ctcjava-20-beta-2.html' title='CTCJava 2.0 Beta 2'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-1577500138320634597</id><published>2006-01-01T16:32:00.000Z</published><updated>2007-06-24T16:33:07.170+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='CTClib'/><title type='text'>CTCJava 2.0 Beta 1</title><content type='html'>&lt;p&gt;This release is a &lt;a
    href="http://www.7-zip.org/"&gt;7-zip&lt;/a&gt; format archive, and uses Java 5
    (JRE 1.5) or later, rather than the Java 1.1 of the previous models. The
    main feature of this release is a replacement of the UI widgets with Java
    Swing components; and the start of a rework of the way in which multiple
    open files are managed.&lt;/p&gt;

&lt;p&gt;
    This release archive contains a 7-zip archive with detached signature;
    within that 4 other such archives, each containing a file and its
    detached signature. The files in the innermost archives, the ones with
    accompanying signature, are 
    &lt;ol&gt;
      &lt;li&gt;CTCJava.jar &amp;#8212; the compiled file including icons (Both the &amp;#8220;&lt;a
        href="http://sourceforge.net/projects/icon-collection"&gt;Dean's
        icons&lt;/a&gt;&amp;#8221; set used before, and the preferred &lt;a
        href="http://tango-project.org/Tango_Desktop_Project"&gt;Tango
        Project&lt;/a&gt; set.&lt;/li&gt;
      &lt;li&gt;java.zip &amp;#8212; the refactored source code&lt;/li&gt;

      &lt;li&gt;ctcjlib.dll &amp;#8212; version 2.3.2192.30638; modified from the previous
        version to take into account changes in the names of classes and
        methods in the Java code&lt;/li&gt;
      &lt;li&gt;ctcjlib-src.zip &amp;#8212; the modified source used to build the
      above.&lt;/li&gt;
    &lt;/ol&gt;
    &lt;p&gt;
    I shan't make promises about the likely schedule of further updates.
  &lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-1577500138320634597?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/1577500138320634597/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=1577500138320634597' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/1577500138320634597'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/1577500138320634597'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2006/01/ctcjava-20-beta-1.html' title='CTCJava 2.0 Beta 1'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-8248689334637880406</id><published>2005-12-21T16:29:00.000Z</published><updated>2007-06-24T23:32:48.236+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='CTClib'/><category scheme='http://www.blogger.com/atom/ns#' term='fault'/><title type='text'>CTCjlib -- IMPORTANT UPDATE!</title><content type='html'>&lt;p&gt;The previous CTCjlib
    update (2.3.2178.34472) was built off an experimental (and broken)
    code-base, the result of my having set the project aside some years (and
    computers) ago, and taking from the wrong repository.&lt;/p&gt;
    &lt;p&gt;
    I have now reverted the code-base to the last released archive from this
    site, and built &lt;a 
    href="http://uk.geocities.com/mr_tines/code.html"
    title="CTCJlib update 2.3.2181.36359"&gt;CTCJlib.dll v 2.3.2181.36359&lt;/a&gt;
    from that. As noted before, this incorporates the fix noted in the
    26-Jun-05 entry below, plus the altered source files, and with detached
    signatures in the archive too.&lt;/p&gt;
    &lt;p&gt;
    Please accept my apologies, and &amp;#8212; Enjoy!&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-8248689334637880406?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/8248689334637880406/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=8248689334637880406' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/8248689334637880406'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/8248689334637880406'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2005/12/ctcjlib-important-update.html' title='CTCjlib -- IMPORTANT UPDATE!'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-4261752515784630121</id><published>2005-12-10T16:27:00.000Z</published><updated>2007-06-24T16:29:17.515+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web design'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><title type='text'>Enkoder applet</title><content type='html'>&lt;a href="http://www.geocities.com/mr_tines/enkoder.html"
    title="E-mail address obfuscator"&gt;&amp;#8220;E&amp;#8221;-ddress encoder 1.0&lt;/a&gt; &amp;#8212; a
    utility based on the the old Hiveware &amp;#8220;Enkoder&amp;#8221;, only this one is a
    Java app/applet. Offered into the public domain.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-4261752515784630121?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/4261752515784630121/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=4261752515784630121' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/4261752515784630121'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/4261752515784630121'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2005/12/enkoder-applet.html' title='Enkoder applet'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-3972230600146628079</id><published>2005-10-29T16:26:00.000+01:00</published><updated>2007-06-24T16:27:33.923+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='FTP'/><category scheme='http://www.blogger.com/atom/ns#' term='C/C++'/><title type='text'>PassiveFTP 1.8</title><content type='html'>&lt;p&gt;PassiveFTP 1.8 &amp;#8212; build 1.8.2127.30224 &amp;#8212; minor bug-fix
    release &amp;#8212; only treats client-side executables as possibly having unique
    icons, so does not get icon creation failures when scanning client-side
    folders with vary many files. Also set default upload to dial-up
    compatible 2kb/s for best observed stability.&lt;/p&gt;
&lt;p&gt;From the windows page&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-3972230600146628079?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/3972230600146628079/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=3972230600146628079' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/3972230600146628079'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/3972230600146628079'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2005/10/passiveftp-18.html' title='PassiveFTP 1.8'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-6587849961303282194</id><published>2005-06-26T16:04:00.000+01:00</published><updated>2007-06-23T16:06:08.131+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='CTClib'/><category scheme='http://www.blogger.com/atom/ns#' term='fault'/><title type='text'>CTClib inflate issue</title><content type='html'>&lt;p&gt;The CTClib deflate implementation follows PGP 2.x in limiting the
    compression window to 8k; it also limits the decompression window
    similarly. Other implementations e.g. Bouncy Castle, do not heed this
    limitation. Andrew Paterson writes:&lt;/p&gt; 

    &lt;blockquote&gt;
      &lt;p&gt;Since my last e-mail, I did a quick experiment and I have
      established that, as you suggested, the 8K window size was the culprit.
      As it was easier to modify CTC, I changed the definition of WSIZE back
      to 32K (0x8000) in gzip.h then defined WSIZE as 8K (0x2000) in
      deflate.c before gzip.h was included. That did the trick!
      Encryption/signing (deflating) uses an 8K window size whilst decryption
      (inflating) uses a 32K window size. Problem solved and, as a bonus, the
      compatibility of CTC is actually increased.&lt;/p&gt;
 
      &lt;p&gt;Unfortunately, it does mean that I will have to rebuild/redeploy all
      of my software that uses CTC but I think I prefer that to using an
      uneasy hybrid of Sun JDK, hacked Bouncy Castle JCE and hacked GNU
      Classpath zip classes. There are also licence implications in that the
      Bouncy Castle crypto libraries are not under the GPL whereas the GNU
      Classpath classes obviously are.&lt;/p&gt;
    &lt;/blockquote&gt;

    &lt;p&gt;Look for an update build sometime.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-6587849961303282194?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/6587849961303282194/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=6587849961303282194' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/6587849961303282194'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/6587849961303282194'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2005/06/ctclib-inflate-issue.html' title='CTClib inflate issue'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-1746788011957774798</id><published>2005-05-09T16:02:00.000+01:00</published><updated>2007-06-23T16:04:03.958+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='FTP'/><title type='text'>PassiveFTP 1.7</title><content type='html'>&lt;p&gt;PassiveFTP version 1.7.1955.38236 &amp;#8212;
    allows the upload rate to be limited for fussy servers.&lt;/p&gt;
&lt;p&gt;From the Windows page, as usual.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-1746788011957774798?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/1746788011957774798/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=1746788011957774798' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/1746788011957774798'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/1746788011957774798'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2005/05/passiveftp-17.html' title='PassiveFTP 1.7'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1592996183125788768.post-5455041623911998921</id><published>2005-05-08T16:01:00.000+01:00</published><updated>2007-06-23T16:01:50.085+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='FTP'/><category scheme='http://www.blogger.com/atom/ns#' term='fault'/><title type='text'>PassiveFTP problem</title><content type='html'>&lt;p&gt;I have observed a flaw in the implementation of PassiveFTP. I
    intended to make the data channel socket non-blocking, but instead
    re-asserted that for the command channel. Also the host I usually FTP to
    has started to choke on uploads done at full ADSL rate, so I need to get
    the non-blocking behaviour fixed, so I can throttle the upload rate
    appropriately.&lt;/p&gt;

    &lt;p&gt;Look for an update in the next week or so.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1592996183125788768-5455041623911998921?l=tinesware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tinesware.blogspot.com/feeds/5455041623911998921/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1592996183125788768&amp;postID=5455041623911998921' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/5455041623911998921'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1592996183125788768/posts/default/5455041623911998921'/><link rel='alternate' type='text/html' href='http://tinesware.blogspot.com/2005/05/passiveftp-problem.html' title='PassiveFTP problem'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry></feed>
