Browse Source

more generic google implementation

Scrublord1336 6 years ago
parent
commit
7b231f277e

+ 1 - 0
CHANGELOG.md

@@ -1,6 +1,7 @@
 ### 2.7.0
  * Additional installation instructions for standalone installation
  * Fixed a bug with NGUI that caused those texts not to be translated
+ * Improved fix for gtrans (23.07.2018)
 
 ### 2.6.0
  * Fix for current issue with gtrans (23.07.2018)

+ 10 - 1
src/XUnity.AutoTranslator.Plugin.Core/Web/AutoTranslateClient.cs

@@ -42,7 +42,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Web
          }
       }
 
-      public static bool HasAvailableClients => _runningTranslations < _maxConcurrency;
+      public static bool HasAvailableClients => !_endpoint.IsSettingUp() && _runningTranslations < _maxConcurrency;
 
       public static bool Fallback()
       {
@@ -51,6 +51,15 @@ namespace XUnity.AutoTranslator.Plugin.Core.Web
 
       public static IEnumerator TranslateByWWW( string untranslated, string from, string to, Action<string> success, Action failure )
       {
+         // allow self configuration of cache parameters by an endpoint
+         var yieldable = _endpoint.StartSetup();
+         if( yieldable != null )
+         {
+            yield return yieldable;
+            _endpoint.EndSetup( yieldable );
+         }
+
+
          var url = _endpoint.GetServiceUrl( untranslated, from, to );
          var headers = new Dictionary<string, string>();
          _endpoint.ApplyHeaders( headers );

+ 95 - 3
src/XUnity.AutoTranslator.Plugin.Core/Web/GoogleTranslateEndpoint.cs

@@ -3,7 +3,9 @@ using System.Collections.Generic;
 using System.Globalization;
 using System.IO;
 using System.Net;
+using System.Reflection;
 using System.Text;
+using Harmony;
 using SimpleJSON;
 using UnityEngine;
 using XUnity.AutoTranslator.Plugin.Core.Configuration;
@@ -14,13 +16,22 @@ namespace XUnity.AutoTranslator.Plugin.Core.Web
 {
    public class GoogleTranslateEndpoint : KnownEndpoint
    {
+      private static readonly ConstructorInfo WwwConstructor = Constants.Types.WWW.GetConstructor( new[] { typeof( string ), typeof( byte[] ), typeof( Dictionary<string, string> ) } );
+
       //private static readonly string CertificateIssuer = "CN=Google Internet Authority G3, O=Google Trust Services, C=US";
       private static ServicePoint ServicePoint;
       private static readonly string HttpServicePointTemplateUrl = "http://translate.googleapis.com/translate_a/single?client=t&dt=t&sl={0}&tl={1}&ie=UTF-8&oe=UTF-8&tk={2}&q={3}";
       private static readonly string HttpsServicePointTemplateUrl = "https://translate.googleapis.com/translate_a/single?client=t&dt=t&sl={0}&tl={1}&ie=UTF-8&oe=UTF-8&tk={2}&q={3}";
       private static readonly string FallbackHttpServicePointTemplateUrl = "http://translate.googleapis.com/translate_a/single?client=gtx&sl={0}&tl={1}&dt=t&q={2}";
       private static readonly string FallbackHttpsServicePointTemplateUrl = "https://translate.googleapis.com/translate_a/single?client=gtx&sl={0}&tl={1}&dt=t&q={2}";
-      private static bool _hasFallenBack = false;
+      private static readonly string HttpsTranslateUserSite = "https://translate.google.com";
+      private static readonly string HttpTranslateUserSite = "http://translate.google.com";
+
+      private bool _hasFallenBack = false;
+      private bool _isSettingUp = false;
+      private bool _hasSetup = false;
+      private int m = 425635;
+      private int s = 1953544246;
 
       public GoogleTranslateEndpoint()
          : base( KnownEndpointNames.GoogleTranslate )
@@ -41,6 +52,89 @@ namespace XUnity.AutoTranslator.Plugin.Core.Web
          headers[ HttpRequestHeader.AcceptCharset ] = "UTF-8";
       }
 
+      public override bool IsSettingUp()
+      {
+         return _isSettingUp;
+      }
+
+      public override object StartSetup()
+      {
+         if( !_isSettingUp && !_hasSetup )
+         {
+            _isSettingUp = true;
+
+            var headers = new Dictionary<string, string>();
+            ApplyHeaders( headers );
+            object www = WwwConstructor.Invoke( new object[] { Settings.EnableSSL ? HttpsTranslateUserSite : HttpTranslateUserSite, null, headers } );
+
+            try
+            {
+               return www;
+            }
+            catch( Exception e )
+            {
+               _hasSetup = true;
+               _isSettingUp = false;
+
+               Console.WriteLine( "[XUnity.AutoTranslator][ERROR]: An error occurred while setting up GoogleTranslate TKK (Start)." + Environment.NewLine + e.ToString() );
+
+               return null;
+            }
+         }
+         return null;
+      }
+
+      public override void EndSetup( object www )
+      {
+         string error = null;
+         try
+         {
+            error = (string)AccessTools.Property( Constants.Types.WWW, "error" ).GetValue( www, null );
+         }
+         catch( Exception e )
+         {
+            error = e.ToString();
+         }
+
+         if( error == null )
+         {
+            try
+            {
+               var html = (string)AccessTools.Property( Constants.Types.WWW, "text" ).GetValue( www, null );
+
+               const string lookup = "TKK=eval('";
+               var lookupIndex = html.IndexOf( lookup ) + lookup.Length;
+               var openClamIndex = html.IndexOf( '{', lookupIndex );
+               var closeClamIndex = html.IndexOf( '}', openClamIndex );
+               var endStringEval = html.IndexOf( '\'', closeClamIndex );
+               var script = html.Substring( lookupIndex, endStringEval - lookupIndex );
+               var decodedScript = script.Replace( "\\x3d", "=" ).Replace( "\\x27", "'" ).Replace( "function", "function FuncName" );
+
+               using( ScriptEngine engine = new ScriptEngine( "{16d51579-a30b-4c8b-a276-0ff4dc41e755}" ) )
+               {
+                  ParsedScript parsed = engine.Parse( decodedScript );
+                  var result = (string)parsed.CallMethod( "FuncName" );
+
+                  var parts = result.Split( '.' );
+                  m = int.Parse( parts[ 0 ] );
+                  s = int.Parse( parts[ 1 ] );
+               }
+            }
+            catch( Exception e )
+            {
+               error = e.ToString();
+            }
+         }
+
+         _hasSetup = true;
+         _isSettingUp = false;
+
+         if( error != null )
+         {
+            Console.WriteLine( "[XUnity.AutoTranslator][ERROR]: An error occurred while setting up GoogleTranslate TKK (End)." + Environment.NewLine + error );
+         }
+      }
+
       // TKK Approach stolen from Translation Aggregator r190, all credits to Sinflower
 
       private long Vi( long r, string o )
@@ -58,8 +152,6 @@ namespace XUnity.AutoTranslator.Plugin.Core.Web
 
       private string Tk( string r )
       {
-         long m = 425635;
-         long s = 1953544246;
          List<long> S = new List<long>();
 
          for( var v = 0 ; v < r.Length ; v++ )

+ 15 - 0
src/XUnity.AutoTranslator.Plugin.Core/Web/KnownEndpoint.cs

@@ -34,5 +34,20 @@ namespace XUnity.AutoTranslator.Plugin.Core.Web
       {
          return 1;
       }
+
+      public virtual bool IsSettingUp()
+      {
+         return false;
+      }
+
+      public virtual object StartSetup()
+      {
+         return null;
+      }
+
+      public virtual void EndSetup( object obj )
+      {
+
+      }
    }
 }

+ 738 - 0
src/XUnity.AutoTranslator.Plugin.Core/Web/ScriptEngine.cs

@@ -0,0 +1,738 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using System.Runtime.Serialization;
+using System.Text;
+using System.Threading;
+
+namespace XUnity.AutoTranslator.Plugin.Core.Web
+{/// <summary>
+ /// Represents a Windows Script Engine such as JScript, VBScript, etc.
+ /// </summary>
+   public sealed class ScriptEngine : IDisposable
+   {
+      /// <summary>
+      /// The name of the function used for simple evaluation.
+      /// </summary>
+      public const string MethodName = "EvalMethod";
+
+      /// <summary>
+      /// The default scripting language name.
+      /// </summary>
+      public const string DefaultLanguage = JavaScriptLanguage;
+
+      /// <summary>
+      /// The JavaScript or jscript scripting language name.
+      /// </summary>
+      public const string JavaScriptLanguage = "javascript";
+
+      /// <summary>
+      /// The javascript or jscript scripting language name.
+      /// </summary>
+      public const string VBScriptLanguage = "vbscript";
+
+      /// <summary>
+      /// The chakra javascript engine CLSID. The value is {16d51579-a30b-4c8b-a276-0ff4dc41e755}.
+      /// </summary>
+      public const string ChakraClsid = "{16d51579-a30b-4c8b-a276-0ff4dc41e755}";
+
+      private IActiveScript _engine;
+      private IActiveScriptParse32 _parse32;
+      private IActiveScriptParse64 _parse64;
+      internal ScriptSite Site;
+      private Version _version;
+      private string _name;
+
+      [Guid( "BB1A2AE1-A4F9-11cf-8F20-00805F2CD064" ), InterfaceType( ComInterfaceType.InterfaceIsIUnknown )]
+      private interface IActiveScript
+      {
+         [PreserveSig]
+         int SetScriptSite( IActiveScriptSite pass );
+         [PreserveSig]
+         int GetScriptSite( Guid riid, out IntPtr site );
+         [PreserveSig]
+         int SetScriptState( ScriptState state );
+         [PreserveSig]
+         int GetScriptState( out ScriptState scriptState );
+         [PreserveSig]
+         int Close();
+         [PreserveSig]
+         int AddNamedItem( string name, ScriptItem flags );
+         [PreserveSig]
+         int AddTypeLib( Guid typeLib, uint major, uint minor, uint flags );
+         [PreserveSig]
+         int GetScriptDispatch( string itemName, out IntPtr dispatch );
+         [PreserveSig]
+         int GetCurrentScriptThreadID( out uint thread );
+         [PreserveSig]
+         int GetScriptThreadID( uint win32ThreadId, out uint thread );
+         [PreserveSig]
+         int GetScriptThreadState( uint thread, out ScriptThreadState state );
+         [PreserveSig]
+         int InterruptScriptThread( uint thread, out System.Runtime.InteropServices.ComTypes.EXCEPINFO exceptionInfo, uint flags );
+         [PreserveSig]
+         int Clone( out IActiveScript script );
+      }
+
+      [Guid( "4954E0D0-FBC7-11D1-8410-006008C3FBFC" ), InterfaceType( ComInterfaceType.InterfaceIsIUnknown )]
+      private interface IActiveScriptProperty
+      {
+         [PreserveSig]
+         int GetProperty( int dwProperty, IntPtr pvarIndex, out object pvarValue );
+         [PreserveSig]
+         int SetProperty( int dwProperty, IntPtr pvarIndex, ref object pvarValue );
+      }
+
+      [Guid( "DB01A1E3-A42B-11cf-8F20-00805F2CD064" ), InterfaceType( ComInterfaceType.InterfaceIsIUnknown )]
+      private interface IActiveScriptSite
+      {
+         [PreserveSig]
+         int GetLCID( out int lcid );
+         [PreserveSig]
+         int GetItemInfo( string name, ScriptInfo returnMask, out IntPtr item, IntPtr typeInfo );
+         [PreserveSig]
+         int GetDocVersionString( out string version );
+         [PreserveSig]
+         int OnScriptTerminate( object result, System.Runtime.InteropServices.ComTypes.EXCEPINFO exceptionInfo );
+         [PreserveSig]
+         int OnStateChange( ScriptState scriptState );
+         [PreserveSig]
+         int OnScriptError( IActiveScriptError scriptError );
+         [PreserveSig]
+         int OnEnterScript();
+         [PreserveSig]
+         int OnLeaveScript();
+      }
+
+      [Guid( "EAE1BA61-A4ED-11cf-8F20-00805F2CD064" ), InterfaceType( ComInterfaceType.InterfaceIsIUnknown )]
+      private interface IActiveScriptError
+      {
+         [PreserveSig]
+         int GetExceptionInfo( out System.Runtime.InteropServices.ComTypes.EXCEPINFO exceptionInfo );
+         [PreserveSig]
+         int GetSourcePosition( out uint sourceContext, out int lineNumber, out int characterPosition );
+         [PreserveSig]
+         int GetSourceLineText( out string sourceLine );
+      }
+
+      [Guid( "BB1A2AE2-A4F9-11cf-8F20-00805F2CD064" ), InterfaceType( ComInterfaceType.InterfaceIsIUnknown )]
+      private interface IActiveScriptParse32
+      {
+         [PreserveSig]
+         int InitNew();
+         [PreserveSig]
+         int AddScriptlet( string defaultName, string code, string itemName, string subItemName, string eventName, string delimiter, IntPtr sourceContextCookie, uint startingLineNumber, ScriptText flags, out string name, out System.Runtime.InteropServices.ComTypes.EXCEPINFO exceptionInfo );
+         [PreserveSig]
+         int ParseScriptText( string code, string itemName, IntPtr context, string delimiter, int sourceContextCookie, uint startingLineNumber, ScriptText flags, out object result, out System.Runtime.InteropServices.ComTypes.EXCEPINFO exceptionInfo );
+      }
+
+      [Guid( "C7EF7658-E1EE-480E-97EA-D52CB4D76D17" ), InterfaceType( ComInterfaceType.InterfaceIsIUnknown )]
+      private interface IActiveScriptParse64
+      {
+         [PreserveSig]
+         int InitNew();
+         [PreserveSig]
+         int AddScriptlet( string defaultName, string code, string itemName, string subItemName, string eventName, string delimiter, IntPtr sourceContextCookie, uint startingLineNumber, ScriptText flags, out string name, out System.Runtime.InteropServices.ComTypes.EXCEPINFO exceptionInfo );
+         [PreserveSig]
+         int ParseScriptText( string code, string itemName, IntPtr context, string delimiter, long sourceContextCookie, uint startingLineNumber, ScriptText flags, out object result, out System.Runtime.InteropServices.ComTypes.EXCEPINFO exceptionInfo );
+      }
+
+      [Flags]
+      private enum ScriptText
+      {
+         None = 0,
+         //DelayExecution = 1,
+         //IsVisible = 2,
+         IsExpression = 32,
+         IsPersistent = 64,
+         //HostManageSource = 128
+      }
+
+      [Flags]
+      private enum ScriptInfo
+      {
+         //None = 0,
+         //IUnknown = 1,
+         ITypeInfo = 2
+      }
+
+      [Flags]
+      private enum ScriptItem
+      {
+         //None = 0,
+         IsVisible = 2,
+         IsSource = 4,
+         //GlobalMembers = 8,
+         //IsPersistent = 64,
+         //CodeOnly = 512,
+         //NoCode = 1024
+      }
+
+      private enum ScriptThreadState
+      {
+         //NotInScript = 0,
+         //Running = 1
+      }
+
+      private enum ScriptState
+      {
+         Uninitialized = 0,
+         Started = 1,
+         Connected = 2,
+         Disconnected = 3,
+         Closed = 4,
+         Initialized = 5
+      }
+
+      private const int TYPE_E_ELEMENTNOTFOUND = unchecked((int)( 0x8002802B ));
+      private const int E_NOTIMPL = -2147467263;
+
+      public static bool TryParseGuid( string s, out Guid guid )
+      {
+         try
+         {
+            guid = new Guid( s );
+            return true;
+         }
+         catch
+         {
+            guid = Guid.Empty;
+            return false;
+         }
+      }
+
+      /// <summary>
+      /// Determines if a script engine with the input name exists.
+      /// </summary>
+      /// <param name="language">The language.</param>
+      /// <returns>true if the engine exists; false otherwise.</returns>
+      public static Version GetVersion( string language )
+      {
+         if( language == null )
+            throw new ArgumentNullException( "language" );
+
+         Type engine;
+         Guid clsid;
+         if( TryParseGuid( language, out clsid ) )
+         {
+            engine = Type.GetTypeFromCLSID( clsid, false );
+         }
+         else
+         {
+            engine = Type.GetTypeFromProgID( language, false );
+         }
+         if( engine == null )
+            return null;
+
+         IActiveScript scriptEngine = Activator.CreateInstance( engine ) as IActiveScript;
+         if( scriptEngine == null )
+            return null;
+
+         IActiveScriptProperty scriptProperty = scriptEngine as IActiveScriptProperty;
+         if( scriptProperty == null )
+            return new Version( 1, 0, 0, 0 );
+
+         int major = GetProperty( scriptProperty, SCRIPTPROP_MAJORVERSION, 0 );
+         int minor = GetProperty( scriptProperty, SCRIPTPROP_MINORVERSION, 0 );
+         int revision = GetProperty( scriptProperty, SCRIPTPROP_BUILDNUMBER, 0 );
+         Version version = new Version( major, minor, Environment.OSVersion.Version.Build, revision );
+         Marshal.ReleaseComObject( scriptProperty );
+         Marshal.ReleaseComObject( scriptEngine );
+         return version;
+      }
+
+      private static T GetProperty<T>( IActiveScriptProperty prop, int index, T defaultValue )
+      {
+         object value;
+         if( prop.GetProperty( index, IntPtr.Zero, out value ) != 0 )
+            return defaultValue;
+
+         try
+         {
+            return (T)Convert.ChangeType( value, typeof( T ) );
+         }
+         catch
+         {
+            return defaultValue;
+         }
+      }
+
+      /// <summary> 
+      /// Initializes a new instance of the <see cref="ScriptEngine"/> class. 
+      /// </summary> 
+      /// <param name="language">The scripting language. Standard Windows Script engines names are 'jscript' or 'vbscript'.</param> 
+      public ScriptEngine( string language )
+      {
+         if( language == null )
+            throw new ArgumentNullException( "language" );
+
+         Type engine;
+         Guid clsid;
+         if( TryParseGuid( language, out clsid ) )
+         {
+            engine = Type.GetTypeFromCLSID( clsid, true );
+         }
+         else
+         {
+            engine = Type.GetTypeFromProgID( language, true );
+         }
+         _engine = Activator.CreateInstance( engine ) as IActiveScript;
+         if( _engine == null )
+            throw new ArgumentException( language + " is not an Windows Script Engine", "language" );
+
+         Site = new ScriptSite();
+         _engine.SetScriptSite( Site );
+
+         // support 32-bit & 64-bit process 
+         if( IntPtr.Size == 4 )
+         {
+            _parse32 = (IActiveScriptParse32)_engine;
+            _parse32.InitNew();
+         }
+         else
+         {
+            _parse64 = (IActiveScriptParse64)_engine;
+            _parse64.InitNew();
+         }
+      }
+
+      private const int SCRIPTPROP_NAME = 0x00000000;
+      private const int SCRIPTPROP_MAJORVERSION = 0x00000001;
+      private const int SCRIPTPROP_MINORVERSION = 0x00000002;
+      private const int SCRIPTPROP_BUILDNUMBER = 0x00000003;
+
+      /// <summary>
+      /// Gets the engine version.
+      /// </summary>
+      /// <value>
+      /// The version.
+      /// </value>
+      public Version Version
+      {
+         get
+         {
+            if( _version == null )
+            {
+               int major = GetProperty( SCRIPTPROP_MAJORVERSION, 0 );
+               int minor = GetProperty( SCRIPTPROP_MINORVERSION, 0 );
+               int revision = GetProperty( SCRIPTPROP_BUILDNUMBER, 0 );
+               _version = new Version( major, minor, Environment.OSVersion.Version.Build, revision );
+            }
+            return _version;
+         }
+      }
+
+      /// <summary>
+      /// Gets the engine name.
+      /// </summary>
+      /// <value>
+      /// The name.
+      /// </value>
+      public string Name
+      {
+         get
+         {
+            if( _name == null )
+            {
+               _name = GetProperty( SCRIPTPROP_NAME, string.Empty );
+            }
+            return _name;
+         }
+      }
+
+      /// <summary>
+      /// Gets a script engine property.
+      /// </summary>
+      /// <typeparam name="T">The expected property type.</typeparam>
+      /// <param name="index">The property index.</param>
+      /// <param name="defaultValue">The default value if not found.</param>
+      /// <returns>The value of the property or the default value.</returns>
+      public T GetProperty<T>( int index, T defaultValue )
+      {
+         object value;
+         if( !TryGetProperty( index, out value ) )
+            return defaultValue;
+
+         try
+         {
+            return (T)Convert.ChangeType( value, typeof( T ) );
+         }
+         catch
+         {
+            return defaultValue;
+         }
+      }
+
+      /// <summary>
+      /// Gets a script engine property.
+      /// </summary>
+      /// <param name="index">The property index.</param>
+      /// <param name="value">The value.</param>
+      /// <returns>true if the property was successfully got; false otherwise.</returns>
+      public bool TryGetProperty( int index, out object value )
+      {
+         value = null;
+         IActiveScriptProperty property = _engine as IActiveScriptProperty;
+         if( property == null )
+            return false;
+
+         return property.GetProperty( index, IntPtr.Zero, out value ) == 0;
+      }
+
+      /// <summary>
+      /// Sets a script engine property.
+      /// </summary>
+      /// <param name="index">The property index.</param>
+      /// <param name="value">The value.</param>
+      /// <returns>true if the property was successfully set; false otherwise.</returns>
+      public bool SetProperty( int index, object value )
+      {
+         IActiveScriptProperty property = _engine as IActiveScriptProperty;
+         if( property == null )
+            return false;
+
+         return property.SetProperty( index, IntPtr.Zero, ref value ) == 0;
+      }
+
+      /// <summary> 
+      /// Adds the name of a root-level item to the scripting engine's name space. 
+      /// </summary> 
+      /// <param name="name">The name. May not be null.</param> 
+      /// <param name="value">The value. It must be a ComVisible object.</param> 
+      public void SetNamedItem( string name, object value )
+      {
+         if( name == null )
+            throw new ArgumentNullException( "name" );
+
+         _engine.AddNamedItem( name, ScriptItem.IsVisible | ScriptItem.IsSource );
+         Site.NamedItems[ name ] = value;
+      }
+
+      internal class ScriptSite : IActiveScriptSite
+      {
+         internal ScriptException LastException;
+         internal Dictionary<string, object> NamedItems = new Dictionary<string, object>();
+
+         int IActiveScriptSite.GetLCID( out int lcid )
+         {
+            lcid = Thread.CurrentThread.CurrentCulture.LCID;
+            return 0;
+         }
+
+         int IActiveScriptSite.GetItemInfo( string name, ScriptInfo returnMask, out IntPtr item, IntPtr typeInfo )
+         {
+            item = IntPtr.Zero;
+            if( ( returnMask & ScriptInfo.ITypeInfo ) == ScriptInfo.ITypeInfo )
+               return E_NOTIMPL;
+
+            object value;
+            if( !NamedItems.TryGetValue( name, out value ) )
+               return TYPE_E_ELEMENTNOTFOUND;
+
+            item = Marshal.GetIUnknownForObject( value );
+            return 0;
+         }
+
+         int IActiveScriptSite.GetDocVersionString( out string version )
+         {
+            version = null;
+            return 0;
+         }
+
+         int IActiveScriptSite.OnScriptTerminate( object result, System.Runtime.InteropServices.ComTypes.EXCEPINFO exceptionInfo )
+         {
+            return 0;
+         }
+
+         int IActiveScriptSite.OnStateChange( ScriptState scriptState )
+         {
+            return 0;
+         }
+
+         int IActiveScriptSite.OnScriptError( IActiveScriptError scriptError )
+         {
+            string sourceLine = null;
+            try
+            {
+               scriptError.GetSourceLineText( out sourceLine );
+            }
+            catch
+            {
+               // happens sometimes... 
+            }
+            uint sourceContext;
+            int lineNumber;
+            int characterPosition;
+            scriptError.GetSourcePosition( out sourceContext, out lineNumber, out characterPosition );
+            lineNumber++;
+            characterPosition++;
+            System.Runtime.InteropServices.ComTypes.EXCEPINFO exceptionInfo;
+            scriptError.GetExceptionInfo( out exceptionInfo );
+
+            string message;
+            if( !string.IsNullOrEmpty( sourceLine ) )
+            {
+               message = "Script exception: {1}. Error number {0} (0x{0:X8}): {2} at line {3}, column {4}. Source line: '{5}'.";
+            }
+            else
+            {
+               message = "Script exception: {1}. Error number {0} (0x{0:X8}): {2} at line {3}, column {4}.";
+            }
+            LastException = new ScriptException( string.Format( message, exceptionInfo.scode, exceptionInfo.bstrSource, exceptionInfo.bstrDescription, lineNumber, characterPosition, sourceLine ) );
+            LastException.Column = characterPosition;
+            LastException.Description = exceptionInfo.bstrDescription;
+            LastException.Line = lineNumber;
+            LastException.Number = exceptionInfo.scode;
+            LastException.Text = sourceLine;
+            return 0;
+         }
+
+         int IActiveScriptSite.OnEnterScript()
+         {
+            LastException = null;
+            return 0;
+         }
+
+         int IActiveScriptSite.OnLeaveScript()
+         {
+            return 0;
+         }
+      }
+
+      /// <summary> 
+      /// Evaluates an expression using the specified language. 
+      /// </summary> 
+      /// <param name="language">The language.</param> 
+      /// <param name="expression">The expression. May not be null.</param> 
+      /// <returns>The result of the evaluation.</returns> 
+      public static object Eval( string language, string expression )
+      {
+         return Eval( language, expression, null );
+      }
+
+      /// <summary> 
+      /// Evaluates an expression using the specified language, with an optional array of named items. 
+      /// </summary> 
+      /// <param name="language">The language.</param> 
+      /// <param name="expression">The expression. May not be null.</param> 
+      /// <param name="namedItems">The named items array.</param> 
+      /// <returns>The result of the evaluation.</returns> 
+      public static object Eval( string language, string expression, params KeyValuePair<string, object>[] namedItems )
+      {
+         if( language == null )
+            throw new ArgumentNullException( "language" );
+
+         if( expression == null )
+            throw new ArgumentNullException( "expression" );
+
+         using( ScriptEngine engine = new ScriptEngine( language ) )
+         {
+            if( namedItems != null )
+            {
+               foreach( KeyValuePair<string, object> kvp in namedItems )
+               {
+                  engine.SetNamedItem( kvp.Key, kvp.Value );
+               }
+            }
+            return engine.Eval( expression );
+         }
+      }
+
+      /// <summary> 
+      /// Evaluates an expression. 
+      /// </summary> 
+      /// <param name="expression">The expression. May not be null.</param> 
+      /// <returns>The result of the evaluation.</returns> 
+      public object Eval( string expression )
+      {
+         if( expression == null )
+            throw new ArgumentNullException( "expression" );
+
+         return Parse( expression, true );
+      }
+
+      /// <summary> 
+      /// Parses the specified text and returns an object that can be used for evaluation. 
+      /// </summary> 
+      /// <param name="text">The text to parse.</param> 
+      /// <returns>An instance of the ParsedScript class.</returns> 
+      public ParsedScript Parse( string text )
+      {
+         if( text == null )
+            throw new ArgumentNullException( "text" );
+
+         return (ParsedScript)Parse( text, false );
+      }
+
+      private object Parse( string text, bool expression )
+      {
+         const string varName = "x___";
+         object result;
+
+         _engine.SetScriptState( ScriptState.Connected );
+
+         ScriptText flags = ScriptText.None;
+         if( expression )
+         {
+            flags |= ScriptText.IsExpression;
+         }
+
+         try
+         {
+            // immediate expression computation seems to work only for 64-bit 
+            // so hack something for 32-bit... 
+            System.Runtime.InteropServices.ComTypes.EXCEPINFO exceptionInfo;
+            if( _parse32 != null )
+            {
+               if( expression )
+               {
+                  // should work for jscript & vbscript at least... 
+                  text = varName + "=" + text;
+               }
+               _parse32.ParseScriptText( text, null, IntPtr.Zero, null, 0, 0, flags, out result, out exceptionInfo );
+            }
+            else
+            {
+               _parse64.ParseScriptText( text, null, IntPtr.Zero, null, 0, 0, flags, out result, out exceptionInfo );
+            }
+         }
+         catch
+         {
+            if( Site.LastException != null )
+               throw Site.LastException;
+
+            throw;
+         }
+
+         IntPtr dispatch;
+         if( expression )
+         {
+            // continue  our 32-bit hack... 
+            if( _parse32 != null )
+            {
+               _engine.GetScriptDispatch( null, out dispatch );
+               object dp = Marshal.GetObjectForIUnknown( dispatch );
+               try
+               {
+                  return dp.GetType().InvokeMember( varName, BindingFlags.GetProperty, null, dp, null );
+               }
+               catch
+               {
+                  if( Site.LastException != null )
+                     throw Site.LastException;
+
+                  throw;
+               }
+            }
+            return result;
+         }
+
+         _engine.GetScriptDispatch( null, out dispatch );
+         ParsedScript parsed = new ParsedScript( this, dispatch );
+         return parsed;
+      }
+
+      /// <summary>
+      /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
+      /// </summary>
+      public void Dispose()
+      {
+         if( _parse32 != null )
+         {
+            Marshal.ReleaseComObject( _parse32 );
+            _parse32 = null;
+         }
+
+         if( _parse64 != null )
+         {
+            Marshal.ReleaseComObject( _parse64 );
+            _parse64 = null;
+         }
+
+         if( _engine != null )
+         {
+            Marshal.ReleaseComObject( _engine );
+            _engine = null;
+         }
+      }
+   }
+
+   public sealed class ParsedScript : IDisposable
+   {
+      private object _dispatch;
+      private readonly ScriptEngine _engine;
+
+      internal ParsedScript( ScriptEngine engine, IntPtr dispatch )
+      {
+         _engine = engine;
+         _dispatch = Marshal.GetObjectForIUnknown( dispatch );
+      }
+
+      public object CallMethod( string methodName, params object[] arguments )
+      {
+         if( _dispatch == null )
+            throw new InvalidOperationException();
+
+         if( methodName == null )
+            throw new ArgumentNullException( "methodName" );
+
+         try
+         {
+            return _dispatch.GetType().InvokeMember( methodName, BindingFlags.InvokeMethod, null, _dispatch, arguments );
+         }
+         catch
+         {
+            if( _engine.Site.LastException != null )
+               throw _engine.Site.LastException;
+
+            throw;
+         }
+      }
+
+      void IDisposable.Dispose()
+      {
+         if( _dispatch != null )
+         {
+            Marshal.ReleaseComObject( _dispatch );
+            _dispatch = null;
+         }
+      }
+   }
+
+   [Serializable]
+   public class ScriptException : Exception
+   {
+      public ScriptException()
+          : base( "Script Exception" )
+      {
+      }
+
+      public ScriptException( string message )
+          : base( message )
+      {
+      }
+
+      public ScriptException( Exception innerException )
+          : base( null, innerException )
+      {
+      }
+
+      public ScriptException( string message, Exception innerException )
+          : base( message, innerException )
+      {
+      }
+
+      protected ScriptException( SerializationInfo info, StreamingContext context )
+          : base( info, context )
+      {
+      }
+
+      public string Description { get; internal set; }
+      public int Line { get; internal set; }
+      public int Column { get; internal set; }
+      public int Number { get; internal set; }
+      public string Text { get; internal set; }
+   }
+}