Register forum user name Search FAQ

Gammon Forum

Notice: Any messages purporting to come from this site telling you that your password has expired, or that you need to verify your details, confirm your email, resolve issues, making threats, or asking for money, are spam. We do not email users with any such messages. If you have lost your password you can obtain a new one by using the password reset link.

Due to spam on this forum, all posts now need moderator approval.

 Entire forum ➜ MUSHclient ➜ Suggestions ➜ Tcl scripting

Tcl scripting

It is now over 60 days since the last post. This thread is closed.     Refresh page


Pages: 1 2  

Posted by Metaxy   (28 posts)  Bio
Date Fri 09 May 2003 06:38 PM (UTC)
Message
How about Tcl scripting for MUSHclient?

Tcl, the Tool Command Language, is the leading language for embedding into applications to automate tasks (precisely what MUclient scripting is). It is used by NBC for video scheduling, by Oracle and Sybase for testing, by NASA in the Mars Pathfinder probe, by AOL for their generated web content, and in countless applications that use scripting. It is thread-safe, has a very simple syntax, and has built-in functionality for disabling all "non-scripting" type functionality (like file manipulation, etc.) with one line of code. VMoo and TkMOO already have this functionality, to great success.

If you added an option to "trust" a script and allow it to use Tk (the accompaying graphics toolkit) and file i/o, you could even allow TkMOO plugins to run under MUclient. Hell, if you did this I'd personally write the adapter code to make it work - I've gotten authorization to write an adapter for his plugin code for my own client from the author, and presumably he'd be OK with one for MC too.

Here's the URL: http://tcl.activestate.com/
Top

Posted by Poromenos   Greece  (1,037 posts)  Bio
Date Reply #1 on Sat 10 May 2003 02:41 AM (UTC)
Message
I like new language support... Beats the heck out of all other clients... But does that mean that i'll have to learn TCL now too? :p

Vidi, Vici, Veni.
http://porocrom.poromenos.org/ Read it!
Top

Posted by Metaxy   (28 posts)  Bio
Date Reply #2 on Sat 10 May 2003 03:13 AM (UTC)
Message
Only if you want to :P
Top

Posted by Nick Gammon   Australia  (23,158 posts)  Bio   Forum Administrator
Date Reply #3 on Sat 10 May 2003 04:48 AM (UTC)
Message
I'll take a stab at it. :)

Can you help me learn enough Tcl to test it? For instance, how do I make a function that calls a COM object? Here is an example from Jscript:


function OnHelp (sName, sLine, wildcards)
  {
  world.Note (World.GetPluginInfo (World.GetPluginID, 3));
  }

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Nick Gammon   Australia  (23,158 posts)  Bio   Forum Administrator
Date Reply #4 on Sat 10 May 2003 04:49 AM (UTC)
Message
And array iteration? Like this from Python:


#
#  interate through array 
#

for cmd in world.GetInternalCommandsList: world.Note (cmd) 

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Metaxy   (28 posts)  Bio
Date Reply #5 on Sat 10 May 2003 06:09 AM (UTC)
Message
Well, if you're implementing the MUSHclient commands yourself, you would most likely render the various objects (world etc.) as namespaces.

[code]proc OnHelp {sName sLine wildcards} {
{
World::Note [World::GetPluginInfo [World.GetPluginID] 3]];
}[/code]

and

[code]
#
# interate through array
#
foreach cmd [World::GetInternalCommandsList] {World::Note $cmd}[/code]

However, if a built-in COM interface (rather than Tcl's very easy native embedding system) is necessary for whatever reason , you're going to need to use the Optcl library for COM at http://www.sys.uea.ac.uk/~fuzz/optcl/default.html.
Top

Posted by Metaxy   (28 posts)  Bio
Date Reply #6 on Sat 10 May 2003 06:13 AM (UTC)
Message
I'm sorry, I didn't check "forum codes." Just ignore the [code] and [/code] bits.
Top

Posted by Metaxy   (28 posts)  Bio
Date Reply #7 on Sat 10 May 2003 06:15 AM (UTC)
Message
Also, make note that the semicolon in Tcl is not only optional if you provide a newline but recommended to be excluded.
Top

Posted by Nick Gammon   Australia  (23,158 posts)  Bio   Forum Administrator
Date Reply #8 on Sat 10 May 2003 06:55 AM (UTC)
Message
You can edit your posts you know. :)

However if you are going to use square brackets and forum codes, you should really use MUSHclient's notepad to "escape forum codes" so that things like [b] don't get rendered as bold (that is, in the code).

I am having trouble getting it to register the script engine, which version of tcl do you recommend I install? I tried ActiveTcl 8.3.5.0 however it didn't register the script engine, and I am in the dark a bit about how to get it all working. For instance, trying to open the register.tcl file in Wish (should I be doing this?) gives messages about:

% 0x80029c4a {Error loading type library/DLL.}

Trying regsvr32 on the tclscript.dll gives messages about "the DllRegisterServer entry point was not found".

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Metaxy   (28 posts)  Bio
Date Reply #9 on Sat 10 May 2003 06:57 AM (UTC)
Message
Try ActiveTcl 8.4.whatever. It has many enhancements and is more stable.
Top

Posted by Nick Gammon   Australia  (23,158 posts)  Bio   Forum Administrator
Date Reply #10 on Sun 11 May 2003 01:13 AM (UTC)
Message
OK, I installed 8.4.2.0 which seemed to go OK, and registered the script engine, which appears in the Registry.

However when trying to initialise the script engine I now get an access violation.

This is exactly the same code that successfully opens VBscript, Jscript, Perlscript, and Python.

I'm not going to spend too much time on it now, unless you can point me to some place that says what I am supposed to do differently with Tcl compared to the others. The error itself seems to be in Tcom.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Metaxy   (28 posts)  Bio
Date Reply #11 on Tue 13 May 2003 03:36 AM (UTC)
Message
I'm talking on the newsgroup with one of the language developers.

"Now we need to see some code, to see what is being attempted, as well as specific errors, to see where the specific problem is."
Top

Posted by Nick Gammon   Australia  (23,158 posts)  Bio   Forum Administrator
Date Reply #12 on Tue 13 May 2003 11:42 AM (UTC)
Message
I can understand that, that is what I would say.

I'll make up a test case tomorrow.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Nick Gammon   Australia  (23,158 posts)  Bio   Forum Administrator
Date Reply #13 on Tue 13 May 2003 11:31 PM (UTC)
Message
I have done a minimal test file that, in a single file, demonstrates the problem. Copy between the lines and paste into (say) test.cpp and compile under Microsoft Visual C++.




#include <stdio.h>
#include <objbase.h>
#include <activscp.h>

//#define VBSCRIPT
//#define JSCRIPT
//#define PERLSCRIPT
//#define PYTHON
#define TCL

#ifdef VBSCRIPT
  #define LANGUAGE L"vbscript"
  #define TEST_CODE L"msgbox \"hello, world\", 0, \"test\" "
#endif   //  VBSCRIPT

#ifdef JSCRIPT
  #define LANGUAGE L"jscript"
  #define TEST_CODE L"WshShell = new ActiveXObject(\"WScript.Shell\");\n"  \
                    L"WshShell.Popup(\"Hello, world\", 0, \"test\");\n"
#endif  // JSCRIPT

#ifdef PERLSCRIPT
  #define LANGUAGE L"perlscript"
  #define TEST_CODE L"$WshShell = Win32::OLE->new(\"WScript.Shell\");\n"  \
                    L"$WshShell->Popup(\"Hello, world\", 0, \"test\");\n"
#endif  // PERLSCRIPT

#ifdef PYTHON
  #define LANGUAGE L"python"
  #define TEST_CODE L"import win32com.client\n" \
                    L"WshShell = win32com.client.Dispatch(\"WScript.Shell\")\n"  \
                    L"WshShell.Popup(\"Hello, world\", 0, \"test\")\n"
#endif // PYTHON

#ifdef TCL
  #define LANGUAGE L"tclscript"
  #define TEST_CODE L"a = 42\n"
#endif  // TCL

#define TRACE(arg) printf ("%s", arg)
#define TRACE1(arg, arg2) printf (arg, arg2)

class CActiveScriptSite :   public IActiveScriptSite,
                            public IActiveScriptSiteWindow {
    LONG m_cRef;
    IDispatch * m_pDispWorld;

public:
    CActiveScriptSite(IDispatch * pDispWorld) 
      : m_cRef(0), m_pDispWorld (pDispWorld)  
          { }

    ~CActiveScriptSite(void) 
      { 
      if (m_pDispWorld) 
        m_pDispWorld->Release (); 
      }

// IUnknown methods
    STDMETHODIMP QueryInterface(REFIID riid, void **ppv) 
      {
      TRACE ("CActiveScriptSite: QueryInterface\n");

      if (riid == IID_IUnknown||riid == IID_IActiveScriptSite)
        *ppv = (IActiveScriptSite*)this;

      else if (riid == IID_IActiveScriptSiteWindow)
        *ppv = (IActiveScriptSiteWindow*)this;
      else 
        return (*ppv = 0), E_OUTOFMEMORY;

      ((IUnknown*)*ppv)->AddRef();
      return S_OK;
      }
    
    STDMETHODIMP_(ULONG) AddRef()
      { 
      TRACE ("CActiveScriptSite: AddRef\n");
      return InterlockedIncrement(&m_cRef); 
      }
    
    STDMETHODIMP_(ULONG) Release() 
      {
      TRACE ("CActiveScriptSite: Release\n");
      if (InterlockedDecrement(&m_cRef))
        return m_cRef;
      delete this;
      return 0;
      }
    
    // IActiveScriptSite methods
    STDMETHODIMP GetItemInfo(LPCOLESTR pstrName, DWORD dwReturnMask,
      IUnknown **ppiunkItem, ITypeInfo **ppti) 
      {
      HRESULT hr = E_FAIL;

      if (dwReturnMask & SCRIPTINFO_IUNKNOWN)
        *ppiunkItem = 0;

      if (dwReturnMask & SCRIPTINFO_ITYPEINFO)
        *ppti = 0;

      return hr;
      }
    
    STDMETHODIMP OnScriptError(IActiveScriptError *pscripterror);
    
    STDMETHODIMP GetLCID(LCID *plcid)
      { 
      TRACE ("CActiveScriptSite: GetLCID\n");
      *plcid = 9; 
      return S_OK; 
      }
    
    STDMETHODIMP GetDocVersionString(BSTR *pbstrVersion) 
      { 
      TRACE ("CActiveScriptSite: GetDocVersionString\n");
      *pbstrVersion = SysAllocString(L"1.0"); 
      return S_OK; 
      }
    
    STDMETHODIMP OnScriptTerminate(const VARIANT *pvr, const EXCEPINFO *pei)
      { 
      TRACE ("CActiveScriptSite: OnScriptTerminate\n");
      return S_OK; 
      }
    
    STDMETHODIMP OnStateChange(SCRIPTSTATE ssScriptState)
      { 
      TRACE1 ("CActiveScriptSite: OnStateChange: %i\n", ssScriptState);
      return S_OK; 
      }
    
    STDMETHODIMP OnEnterScript(void)
      { 
      TRACE ("CActiveScriptSite: OnEnterScript\n");
      return S_OK; 
      }
    
    STDMETHODIMP OnLeaveScript(void) 
      { 
      TRACE ("CActiveScriptSite: OnLeaveScript\n");
      return S_OK; 
      }
    
    // IActiveScriptSiteWindow methods    
    STDMETHODIMP GetWindow(HWND *phwnd)
      { 
      TRACE ("CActiveScriptSite: GetWindow\n");
      *phwnd = GetDesktopWindow(); 
      return S_OK; 
      }
    
    STDMETHODIMP EnableModeless(BOOL)
      { 
      TRACE ("CActiveScriptSite: EnableModeless\n");
      return S_OK; 
      }
};

STDMETHODIMP CActiveScriptSite::OnScriptError(IActiveScriptError *pscripterror) 
  {
  DWORD dwCookie;
  LONG nChar;
  ULONG nLine;
  BSTR bstr = 0;
  EXCEPINFO ei; 
  ZeroMemory(&ei, sizeof(ei));

  TRACE ("CActiveScriptSite: OnScriptError\n");
  
  pscripterror->GetExceptionInfo(&ei);
  pscripterror->GetSourcePosition(&dwCookie, &nLine, &nChar);
  pscripterror->GetSourceLineText(&bstr);
    
  printf ("Error occurred at line %ld character %ld\n", nLine, nChar);
  printf ("Error source: %S\n", ei.bstrSource);
  printf ("Error description: %S\n", ei.bstrDescription);
  printf ("Error help file: %S\n", ei.bstrHelpFile);
  
  SysFreeString(bstr);
  SysFreeString(ei.bstrSource);
  SysFreeString(ei.bstrDescription);
  SysFreeString(ei.bstrHelpFile);
  
  return S_OK;
  }   // end of CActiveScriptSite::OnScriptError

bool ShowError (const HRESULT hr, const char * sMsg)
  {
DWORD status = 0;
char *formattedmsg;

  if (hr == S_OK)
    return false;

  
  if (!FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM |
                    FORMAT_MESSAGE_ALLOCATE_BUFFER | 
                    FORMAT_MESSAGE_IGNORE_INSERTS,
                    NULL, 
                    hr, 
                    LANG_NEUTRAL, 
                    (LPTSTR) &formattedmsg, 
                    0, 
                    NULL))
   printf ("<<Error %08X>>\n\nWhen doing: %s\n", hr, sMsg);
 else
  {
   printf ("Error %08X occurred when doing %s:\n\n%s\n", hr, sMsg,formattedmsg);
   LocalFree (formattedmsg);
  }

  return true;

  } // end of ShowError

int main (void)
  {

  HRESULT hr;

  CLSID clsid;

  IActiveScript       * pIActiveScript = NULL;        // script interface
  IActiveScriptParse  * pIActiveScriptParse = NULL;   // parser
  CActiveScriptSite   * pSite = NULL;                 // our local site (world object)
//  IDispatch           * ppDispatch = NULL;            // script engine dispatch pointer

  // initialise COM
  hr = CoInitialize (NULL);
  if (ShowError (hr, "CoInitialize"))
    return 1;

  // find class ID of scripting language
  hr = CLSIDFromProgID(LANGUAGE, &clsid);
  if (ShowError (hr, "CLSIDFromProgID"))
    return 1;

  // create an instance of the script engine
  TRACE ("Creating instance of script engine ...\n");
  hr = ::CoCreateInstance(clsid,  
          NULL, 
          CLSCTX_ALL,    
          IID_IActiveScript,
          reinterpret_cast<void**>(&pIActiveScript));
  if (ShowError (hr, "CoCreateInstance"))
    return 1;

  // get the script parser interface
  TRACE ("Getting script engine parse interface...\n");
  hr = pIActiveScript->QueryInterface(
          IID_IActiveScriptParse, 
          reinterpret_cast<void**>(&pIActiveScriptParse));
  if (ShowError (hr, "QueryInterface: IID_IActiveScriptParse"))
    return 1;

  // initialise it
  TRACE ("Initialise parse interface...\n");
  hr = pIActiveScriptParse->InitNew ();
  if (ShowError (hr, "InitNew"))
    return 1;

  // create host site object
  TRACE ("Create host site object...\n");
  pSite = new CActiveScriptSite (NULL);   // should be dispatch pointer
  pSite->AddRef ();

  TRACE ("Set site...\n");
  hr = pIActiveScript->SetScriptSite (pSite);
  if (ShowError (hr, "SetScriptSite" ))
    return 1;

  // start script engine
  TRACE ("Start engine...\n");
  hr = pIActiveScript->SetScriptState (SCRIPTSTATE_STARTED);
  if (ShowError (hr, "SetScriptState: SCRIPTSTATE_STARTED" ))
    return 1;

  // connect it
  TRACE ("Connect engine...\n");
  hr = pIActiveScript->SetScriptState (SCRIPTSTATE_CONNECTED);
  if (ShowError (hr, "SetScriptState: SCRIPTSTATE_CONNECTED" ))
    return 1;

  // execute our test script
  TRACE ("Test script...\n");
  EXCEPINFO ei; 
  hr = pIActiveScriptParse->ParseScriptText
                               (TEST_CODE, 0, 0, 0, 0, 0, 
                               SCRIPTTEXT_ISPERSISTENT |
                                SCRIPTTEXT_ISVISIBLE,
                               NULL, 
                               &ei);
  if (ShowError (hr, "ParseScriptText" ))
    return 1;

  TRACE ("Release everything...\n");

  // release engine

  pIActiveScript->SetScriptState(SCRIPTSTATE_DISCONNECTED);
  pIActiveScript->Close ();
  pIActiveScript->Release ();

  // release parser

  pIActiveScriptParse->Release ();

  // finished with site
  pSite->Release ();

  // show we got to the end
  printf ("Completed OK\n");
  return 0;
  } // end of main




By changing the defines (line 4 on) you can test different script engines. Uncomment #define PERLSCRIPT for instance, and comment out #define TCL.

In *every* other case (you can try them) the script engine initialises, and runs, displaying a message box. Here is the example from Perlscript, also from ActiveState:


Creating instance of script engine ...
Redirecting output to win32trace remote collector
Getting script engine parse interface...
Initialise parse interface...
Create host site object...
CActiveScriptSite: AddRef
Set site...
CActiveScriptSite: AddRef
CActiveScriptSite: QueryInterface
CActiveScriptSite: QueryInterface
CActiveScriptSite: GetLCID
CActiveScriptSite: OnStateChange: 5
Start engine...
CActiveScriptSite: OnStateChange: 1
Connect engine...
CActiveScriptSite: OnStateChange: 2
CActiveScriptSite: OnStateChange: 2
Test script...
CActiveScriptSite: OnEnterScript
CActiveScriptSite: OnLeaveScript
CActiveScriptSite: OnEnterScript
CActiveScriptSite: OnLeaveScript
Release everything...
CActiveScriptSite: OnStateChange: 3
CActiveScriptSite: OnStateChange: 5
CActiveScriptSite: OnStateChange: 4
CActiveScriptSite: Release
CActiveScriptSite: Release
Completed OK


However for TCL I get this:


Creating instance of script engine ...
Getting script engine parse interface...
Initialise parse interface...
Error 8000FFFF occurred when doing InitNew:

Catastrophic failure

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Metaxy   (28 posts)  Bio
Date Reply #14 on Wed 14 May 2003 01:13 AM (UTC)
Message
a = 42 is not valid Tcl code. Try replacing it with 'set a 42', which is how to express it in the language. If it still fails identically, tell me.
Top

The dates and times for posts above are shown in Universal Co-ordinated Time (UTC).

To show them in your local time you can join the forum, and then set the 'time correction' field in your profile to the number of hours difference between your location and UTC time.


71,947 views.

This is page 1, subject is 2 pages long: 1 2  [Next page]

It is now over 60 days since the last post. This thread is closed.     Refresh page

Go to topic:           Search the forum


[Go to top] top

Information and images on this site are licensed under the Creative Commons Attribution 3.0 Australia License unless stated otherwise.