External MUClient text window

Posted by Poromenos on Mon 15 Mar 2004 01:49 PM — 57 posts, 186,897 views.

Greece #0
OK, this is the thing... Right now we have billions of people shouting "WE WANT AN EXTERNAL WINDOW", so I figured, how hard can it be... I started to code it in VB, and it works nicely, you can create it, set it to topmost or not and add text. The rest is easy to add, but the problem is that I suck with RTF textboxes (I haven't used any), and I don't know how you go about adding color and stuff... So, if anyone would like to help me with the window thing, or have any ideas, please post here... I'll post the sources eventually, but right now it's like 10 lines of code :P Anyway, I just want to see the response it has, I'll get back to coding it now...
Greece #1
It's more or less working now, you can move it, resize it, print text in color and different fonts and sizes, set it to always on top or not, activate it, etc. I'll work on it a bit more and post it, when i find out how :p
Greece #2
It is now possible to have transparent and/or "click-through" windows, meaning that the click goes to the window behind that one. I still haven't found of an acceptable way to activate the windows (not in code, just normally), since they do not appear in the taskbar. Idealy, they would be added to MUClient's "Windows" menu, but I don't know if that can be done...
Greece #3
Oh, the transparency thing only works on XP/2k. Turns out it's just 2 API calls, I'm sure MUClient could be made to support that very easily...
Australia Forum Administrator #4
Quote:

... and I don't know how you go about adding color and stuff...


The way MUSHclient does it is to draw each style as a separate operation. You can do that quite easily in your program, as it exposes the style runs, and the text to be drawn, as part of GetStyleInfo.

Thus you keep track of the X-Y coordinates on the window, start drawing (by going back a screenfull of lines, or by starting at the bottom and working up), draw each line from left to right, updating the X coordinate for each style run, and then updating the Y coordinate for each line.
Greece #5
No, I was referring to the Rich Text box control, it has specific commands to do that... Anyway, it's working now, I just uploaded it to http://www.poromenos.org/MUClientWindow.exe. Download it, run it, and paste this alias:

<aliases>
  <alias
   match="DoDemo"
   enabled="y"
   send_to="12"
   ignore_case="y"
   sequence="100"
  >
  <send>
Dim WindowObject
WindowObject = Empty
Set WindowObject = CreateObject ("MUClientWindow.TextBox")
WindowObject.Topmost = True
WindowObject.SetTitle "Groovy MUClient Window"
WindowObject.SetPosition 300, 500, 12000, 3000
WindowObject.Opacity = 0.7
WindowObject.Transparent = True
WindowObject.Locked = True
WindowObject.BackgroundColor = vbBlack
WindowObject.Note "The ", "Times New Roman", 11, vbWhite
WindowObject.Note "quick ", "Times New Roman", 12, vbGreen
WindowObject.Note "fox ", "Times New Roman", 13, vbRed
WindowObject.Note "jumped ", "Times New Roman", 14, vbBlue
WindowObject.Note "over ", "Times New Roman", 15, vbYellow
WindowObject.Note "some ", "Times New Roman", 16, vbMagenta
WindowObject.Note "meaningless ", "Times New Roman", 17, vbCyan
WindowObject.Note "animal. " &amp; vbCrLf, "Times New Roman", 18, vbWhite
WindowObject.Note "This is just a demo."  &amp; vbCrLf, "Lucida Console", 8, vbRed
WindowObject.Note "Post stuff at the forum." &amp; vbCrLf, , , vbWhite
Dim WindowObject2
WindowObject2 = Empty
Set WindowObject2 = CreateObject ("MUClientWindow.TextBox")
WindowObject2.Note "Oh!" &amp; vbCrLf
WindowObject2.Note "You can have as many of these as you like (I think)"
</send>
  </alias>
</aliases>

Then, type DoDemo to see it working. Tell me what you think.
USA #6
Hmm. For those of us with Pre-2000/XP systems, do not use (delete) these options:

WindowObject.Opacity = 0.7
WindowObject.Transparent = True

The first one will fail and cause the script to terminate early. The second option will work, but since this floating window doesn't periodically update itself (under 98 at least, no idea about the others), unless you send text to it, the background will bleed through and completely obscure the window and anything in it.

A more reasonable thing would be to have your program check to see if it is running under 2000 pr XP and if not, then simply ignore these options completely. I.e., the user can supply them, but they won't do anything.
USA #7
Ok... A few issues here.

First.. There is no way to clear the existing window contents, at least from the example you give.

Second and far more critical, when Mushclient executes a reload/recompile of a script, manually or otherwise, the script engine does not release existing instances of the control. If a reload automatically triggered a special "Release" sub that you could add to kill all instances of created objects this wouldn't be a big issue. Once Mushclient itself completely closes, then memory is freed, but I jumped to 90%+ system resources used in no time as a result of reloading my main script file while I fiddled with positioning the window and adding code to correctly capture the lines I needed. This is bad news...

However, it is the same situation I ran into with my fireworks gadget, so moving all the related code to a plugin would work, if I didn't need to make changes to it periodically and plugins didn't seem to occationally do the same thing to me. I get te feeling that the plugin/script in such case is released before the system has time to release the object.

I have no idea how to work around this frankly... Even if you make the program 'serve' up windows, so that it worked like a database and was 'multiple use, single instance', you still get extra references to the object that the script engine keeps, but can't use when the script reloads. The only real solution is to make sure these are released 'before' Mushclient reinstalls a plugin or reloads the script. I don't know if Nick can solve that problem somehow, but it means that all such script created objects are dangerous if not killed 'before' you reload the script. Closing the window does nothing.

You will notice that my fireworks program has the close button disabled. It 'cannot' kill the running instance, but only closed the form and gives you a false sense of security. I figured it was safer to disble the button, than let people think it is no longer running. Until the reference itself is released but the script, you can't *close* such a window for real.

The only interrum solution I can think of is the 'multiple use, single instance' trick. It doesn't actuall *fix* the problem, but you would do this instead:

WindowObject.NewWindow "My Window"
...
WindowObject.Note "You can have as many of these as you like (I think)"

This will work exactly the same, since internally you would keep track of *which* open window the script object is using. This should therefor only take the minimum resources needed to establish a link to the object and initially create the child window of your main program. Currently, each time the script reloads, and automatically opens a window, it creates a completely new window and the old one stays sitting in memory unused and swallowing up resources. We really need a better way to handle objects that doesn't have this problem imho...
Greece #8
I see... Regarding the transparency problem, I read that it just wouldn't work in systems other than XP/2k... I'll do something about it... As for the way to clear the text, there is a WindowObject.Text property, that returns/sets the entire text, so you can do WindowObject.Text = "". I forgot to add that to the demo. Finally, about releasing objects, making new windows with a command would be too complicated both for me and the user :/ I would have to make form and control arrays, and the user would have to pass the number of the window with each operation... If I'm not mistaken though, plugins call OnPluginClose even when they reload, so they could be made to set the object to nothing until I find a better way.
Now that I think of it, why doesn't MUClient free the objects? Isn't the old scripting space released? That would cause the objects to be freed, unless I am (or Microsoft is :p) missing something.
Greece #9
It will now ignore transparency if the OS is less than 2k...
USA #10
Hmm. Not as complicated as all that. I would suggest:

Public Function NewWindow (Name as String) As Boolean
  dim NewWin as MyBaseWindow
  dim TempWin as MyBaseWindow
  'I have no idea if the code between ***** is accurate.
  'I haven't used this type of control array before, so am
  'going by memory of what I think it was.
  '*****
  For Each TempWin In Me.Controls
    If TempWin.Name = Name then
      NewWindow = False
      Exit Function
    end if
  Next
  NewWin.Name = Name
  Me.Controls.Add NewWin
  '*****
  NewWin = Nothing
  NewWindow = True
End Sub

Then make the 'Note' command:

Public Function Note (Name as String, Text as String, Font As String, Size as Integer, Color as Integer) As Boolean

Then the user can direct text to the window they want and internally it would do:

Dim TempWin As MyBaseWindow
For Each TempWin In Me.Controls
  If TempWin.Name = Name then
    TempWin.Note Text, Font, Size, Color
    Note = True
    Exit Function
  end if
Next
Note = False


You would just move the existing window design and code into a seperate class you can make copies of, then add code like above to each of the current calls (making them functions too so the user can test to make sure the window exists). The basic window you already have doesn't change, you just use For Each to hunt down the specific window the user wants to send text to. This isn't really that much more complicated and the 'main' form can exist as a task tray item or even a hidden service. Since closing Mushclient will kill all connections to it, you don't need to worry about it being visible to the user.

Hmm. It does mean you have to make things like the .text also be accessed by name, with other things, but that is no big deal really.

----

As for Mushclient and plugins.. I think actually the problem was in my main script file, so plugins may correctly release the objects through OnPluginClose. However, I have:

sub Close_World
  set winamp = Nothing
end sub

In my main script and either this is not called when you reload the script or the previous instance of the script is killed before the system has time to actually release the object. In any case, it never releases the WinAmp COM control I was using if I do a reload. The issue I had may also have just been something to do with the odd nature of the WinAmp control. It is both ActiveX and a normal DLL, so closing winamp would *hang* the WinAmp program and occationally Mushclient. Releasing the object worked fine though, as long as you did that *first*, then closed WinAmp.

In any case, it may be possible to solve some of the odd quirks my just adding a delay to the Close function, so the script cannot exit until the system has had a chance to actually release the objects.

Amended on Tue 16 Mar 2004 10:17 PM by Shadowfyr
USA #11
In fact, with this configuration you can actually make the close button on the window do something and add a WindowObject.Close <Name> type command, either of which will delete the object from the control array.
Greece #12
I don't see how that is better than setting the variable to nothing, since that takes a line of code too, and the user could do a =nothing instead of a .close. The close button currently hides the window, and you can show it again with .Visible = True, without losing the contents... My point is that if you are going to free the windows anyway, you can free them with either way, either by having an array of objects, or by having one object with a .CloseAll function...
Greece #13
Also, I think that For ... Each In Me.Controls just enumerates the controls on a form, not the forms in a project, I'm not sure though.
USA #14
Well, since the forms are children of the main form, they should be in the control area, either of the main form (though maybe that is only MDI, or in a seperate control array.

As for the difference between Close and = Nothing... 'Nothing' in this case won't work. You are not destorying your connection to the control, just killing one window. Example, your method does this to create two windows:

set Obj1 = createobject("MUClientWindow.TextBox")
set Obj2 = createobject("MUClientWindow.TextBox")

Mine does it this way:

set Obj = createobject("MUClientWindow.Server")
Obj.NewWindow("Window1")
Obj.NewWindow("Window2")

This is a 'major' difference, since if something causes the original script instance to die, like a reload and the object isn't released you can do this:

a = Obj.Exists("Window1")
if not a then
  Obj.NewWindow("Window1")
end if

More than one script, plugin or even world can talk to the same window through a link to the service, which could be an advantage in some cases. But more important, as long as you use the same name each time, the only extra resources taken up by a script reload or other nasty issue that fails to release the object is what provides the 'link'. With your current design, every time one fails to be released it takes up the memory needed for that link + the memory needed for a brand new window. After 6-7 reloads you are looking at literally MBs of unreleased memory that is taken up by inactive and inaccessable processes.

It may be possible to find a better solution, but imho this could significantly *limit* the damage caused each time someone messes up and doesn't properly release the object in WorldClosed and OnPluginClose, or some glitch prevents these functions from working, as happened to me.

Your version works fine, I am just suggesting a 'safer' way to do it while Nick works out why such objects fail to automatically release when the script is killed and reloaded anyway.
Amended on Wed 17 Mar 2004 07:18 AM by Shadowfyr
Greece #15
Yes, I see what you mean... By =nothing, I meant that if you're going to close the windows anyway (in your implementation), you might as well set the object to nothing (i.e. you don't gain anything in lines of code)... I just don't think that it's important enough to rewrite the entire program (i don't know anyone using it yet :p), but if you want to try it, I can post the sources somewhere...
USA #16
Nah.. If I can work out a few technical issues (like the stupid lack of an API or DLL that supports telling a form to enter design mode), I will be designing my own that does work like that anyway. The reason is to save resources and because I want to be able to host any extra stuff like color converters or things in the service as well. That and being able to use design mode to edit the window layout and set which calls it should make or text clicking a button should send, etc.

I have found a dll that comes with VB6.0 that provides a way to read the info for an object, so I can make calls to it and maybe respond to its events, even if not pre-coded into the program, but I am not sure. VB tends to screw you with respect to doing anything actually "dynamic". But the inability to get a form to enter design mode is a pain. I could code a class in C++ to do it, in theory and have that in a DLL, but I suck at C++. As in, I haven't a clue how to create such a class and make it a usable DLL. :( I am not even sure if it would work on a VB form if I could...
USA #17
DL'd the MUClientWindow.exe

Copied and pasted the alias...

Get this:

Error number: -2146827949
Event: Execution of line 3 column 1
Description: Unknown runtime error: 'CreateObject'
Line in error:

Called by: Immediate execution
USA #18
The problem David is that COM objects must be 'registered' before they become available. This can usually be done by simply running the EXE once. VB programs have code built in that automatically registers them for use, in theory. Sometimes it won't work, in which case you can try again or try going into a dos windows, CD to the directory you saved the MUClientWindow.exe program in and type:

Regsvr32 MUClientWindow.exe

--------

A better option for those making plugins for these sorts of things is to use something like the following in your code:

dim lcount
do 
  on error resume
  set hndl = creatobject("*object_name*")
  on error goto 0
  if typename(hndl) <> "*type*" then
    set WshShell = WScript.CreateObject("WScript.Shell")
    WshShell.exec("Regsvr32 c:\program files\mushclient\worlds\plugins\*your_program.exe*")
    exit loop
  end if
  lcount = lcount + 1
loop until typename(hndl) = "*type*" or lcount > 1
if if typename(hndl) <> "*type*" then
  note "Error here...."
  exit sub
end if

Where *type* is whatever the typename() function normally returns for your object, *object_name* is the object you create, like 'MUClientWindow.TextBox' and *your_program* is the exe or dll containing it. This should, if the directory and everything is right, automatically register the object, even if not currently available. An even better method is to use some sort of installer to register it for you, but people like Poromenos are apparently a bit lazy. ;)
Amended on Fri 23 Apr 2004 04:29 AM by Shadowfyr
USA #19
Ok, I did run the MUClient window first...

Going into cmd prompt and typing that didn't work.

As for that code you put in down there, Not too sure where that goes...
USA #20
The code was a suggestion for people making such things, not you, that is why I seperated it with ------.

I am not sure what the problem is though, since it should be registered and thus usable.. I have no idea what the error actually means, neither does the web apparently. :( I haven't a clue what is wrong.
Australia Forum Administrator #21
Regsvr32.exe is a program that may not be installed by default. Have a hunt for it on your hard disk. The code for that is about 15 lines. You should be able to find a copy on the web or your install disks somewhere.
Australia Forum Administrator #22
However I think it is for DLLs. The exe you should be able to just run to self-register.
Greece #23
Yes, I think the executable self-registers when run... And i'm not lazy! Well, maybe a little :p
USA #24
I still get that error, When I open up my task manager it shows that the windows are supposed to be there, but there is no window
Australia Forum Administrator #25
Can't help beyond that. This is why I don't like using VB.
USA #26
Oh this seems like a great concept but I don't understand. I run the program and nothing happens :) I get the two windows when I used 'dodemo' alias, but never any input going to or coming from the mud.
Don't worry about it :)
USA #27
You have to set up a trigger/alias to send text to it. Trigger if its mud text, alias if you want to send something to it manually. His DoDemo is just an example of things that can be done.

Poromenos, can you post the source somewhere? or send to me? Id like to fiddle.

And also, I couldnt seen to click close on the transparent window. Couldnt move it or anything. Had to kill it via task manager. Im sure closing MC wouldve worked too. But, point being couldnt "x" out of it like the non transparent one.
Amended on Tue 01 Jun 2004 08:14 AM by Flannel
Russia #28
Yes, yes - post the source. I don't use VB myself but I am constantly poking at trying to find a (correctly) working solution to this in Python and seeing how it's done in VB might cause some insights.
Greece #29
Sure, I'll clean it up a bit, add comments and post it. The reason you couldn't move the transparent window is because it was transparent :p The click goes to the window below it. If you just want it to be transparent but not click-through, you need to use the "opacity" feature. Transparency and opacity are entirely different... The window can be transparent while being 100% opaque... Maybe i should change the name on that one...
Greece #30
OK, so much for reading comments and cleaning up :p The code is actually pretty legible, and it should be very easy to add new functions. Enjoy.
Greece #31
In case you're wondering, yes, it would be better if I posted the actual URL too :P
http://storage.poromenos.org/window.zip
Australia Forum Administrator #32
You may want to comment on this:

http://www.gammon.com.au/forum/?bbsubject_id=4286
Greece #33
New URL for this:
http://poromenos.fateback.com/MUClientWindow.exe
Poland #34
How did you do colored text in this external window?

Would you be so kind to poste the code of that function Sub Note() in your VB ?
#35
I've tried your window, I couldn't get it working.
Greece #36
Quote:

I've tried your window, I couldn't get it working.

Did you run the .exe first? What happened?

Note is:

Public Sub Note(strText As Variant, Optional strFontName As String, Optional intFontSize As Integer, Optional varColor As Variant)
    frmMain.rtbText.SelStart = Len(frmMain.rtbText.Text)
    frmMain.rtbText.SelLength = 0
    frmMain.rtbText.SelText = strText
    frmMain.rtbText.SelStart = Len(frmMain.rtbText.Text) - Len(strText)
    frmMain.rtbText.SelLength = Len(strText)
    If Not IsMissing(strFontName) Then frmMain.rtbText.SelFontName = strFontName
    If Not IsMissing(intFontSize) Then frmMain.rtbText.SelFontSize = intFontSize
    If Not IsMissing(varColor) Then frmMain.rtbText.SelColor = varColor
    frmMain.rtbText.SelLength = 0
End Sub
Poland #37
Not working for me:( I run the MUClientWindow.exe, then I run MC, and then your Demo alias, and what I get is:
Error number: -2147024894
Event: Execution of line 3 column 1
Description:
Line in error:

Called by: Immediate execution

The line in error is the CreateObject one.
I've tried running the .exe from the cmd.exe, straight from windows, before running MC, and while running MC.. and I also tried registering it, which didnt work (and shouldnt).
The MUClientWindow.exe doesnt appear in processes or in apps in the Task Manager.

Help plz?:)
Greece #38
Hmm, that is very odd. You have the vb6 runtimes installed? Although it should produce an error if you don't. I downloaded the file, ran it and ran
Set WindowObject = CreateObject ("MUClientWindow.TextBox")
in MC and a window popped up. This has been reported again, but I don't think it's a problem with the program itself... Which OS are you running?
Poland #39
Win XP, not SP1/2.. hmm, I'm not 100% sure about vb runtimes, how can I check?
Poland #40
Oh, I checked my event logs, and found something.. it's not in English tho, so I'll give you my best translation:P

Cannot run DCOM server: {1F32B04F-9C49-491F-98AF-0C5B61895360}. Error:
&#8222;File not found. &#8221;
occured during the following command:
D:\Documents and Settings\Maciek\Pulpit\MUClientWindow.exe -Embedding

For more info, visit http://go.microsoft.com/fwlink/events.asp

Gonna check the M$ thing now.. but doubt it will be helpful

/Edit
Yep, nothing on their site:( Currently there's no info concerning this issue blah blah blah:(
Amended on Mon 20 Dec 2004 09:45 PM by Scypio
Greece #41
Try copying it to the D:\ root. Maybe it has problems with spaces.
Poland #42
Ok, guess what. The funny thing about that error message was that the file was NOT in the D:\Documents and Settings\Maciek\Pulpit\ (which is just the desktop), but in another dir on D:\. So what I did was copy it to the desktop and run. It did run and is showing itself on the Task manager now, and there are no events in the event log. However, I still get an error when I CreateObject, only this time it's different:
Error number: -2146827949
Event: Execution of line 3 column 1
Description: Nieznany b³±d czasu wykonywania.: 'CreateObject'
Line in error:

Called by: Immediate execution

Desc translated by me: Unknown runtime error -- not very helpful:(

/Edit

I just googled the error number, and all I found was:
http://news2.componentone.com/cgi-bin/dnewsweb.exe?cmd=article&group=Component1.public.query&item=600&utag=
Amended on Mon 20 Dec 2004 09:54 PM by Scypio
Greece #43
Hmm, it shouldn't appear in the task manager before a CreateObject, it should just register itself and wait. Try closing it from the task manager, moving it to d:\, running it and doing a CreateObject?
Poland #44
Hmm. So I kept it on the desktop. I shut it using the task manager. I opened MC, and ran the alias line by line. After CreateObject, I got the aforementioned error. However, MUClientWindow.exe appeared in task manager. But WindowObject is unreachable - when I try to access its properties (WindowObject.Topmost=True) it says that it needs an object: WindowObject.

/Edit
And, using CreateObject, I can create more of the instances of MUClientWindow.exe appearing in the task manager.. but cannot create the actual scripting object:(
Gonna get back to this tommorrow after work, gnight:) And thanks for quick answers:)
Amended on Mon 20 Dec 2004 10:04 PM by Scypio
Australia Forum Administrator #45
These issues, which I also had with my own VB toolbar, are why I went to write a Windows one in C.

The VB runtimes, the Registry, inexplicable but (sometimes) silent errors, are all very frustrating.
#46
Hey, I went and downloaded your MUClientWindow.exe and tried to run it, but nothing pops up. I get the pointer-loading Icon and then it stops, and nothing shows up. What do I need to do? I'm running 98Se, if thats any help.
USA #47
(If I remember correctly) Running it doesn't create a window, try going through the steps outlined on page 1, and you should be set.
#48
"Download it, run it, and paste this alias:"

Step 1 - Complete
Step 2 - Complete
Step 3 - Where am I pasting this alias?
USA #49
In the alias menu (thats the one you go to and see all the aliases in the grid) click 'paste' and the alias will be pasted into the world (obviously it has to be on the clipboard).
#50
Looks like I get a totally different error than everyone else:

Error number: -2146827859
Event: Execution of line 4 column 1
Description: ActiveX component can't create object: 'MUClientWindow.TextBox'
Line in error:

Called by: Immediate execution

Any help with this one?
Greece #51
Hmm, are you sure the executable is in a directory where the OS can find it? Place it in the Windows directory, to be safe.
#52
Well, gave it a shot and put the executable in the Windows directory, but still no luck to get it running. Same error as the last one.
#53
Yeah, I tried your window too, I couldn't get it to work either.

*ammended* I've given up on the window thing untill Nick gets it working. I'll just survive with what I got for now.

*ammended again* Yeah, like before, I still can't get it running, Anways, yeah. Kind of a bummer. Yeah. Didn't realize I'd already tried it in the past.
Amended on Fri 15 Apr 2005 07:30 AM by David Berthiaume
Greece #54
That's very odd, it's very straightforward to run, you just unzip it, run it, run mushclient, and paste the alias in... Let me try again, hopefully it'll error out :p
Sweden #55
I got an error too. Can't comprehend it, so I'll just paste it for you to review.
Error Number: -2146827949
Event: Execution of line 4 column 1
Description: Unknown runtime error: 'CreateObject'
             Line in error: 
Called by: Immediate execution
USA #56
Hmm, Are you an administrator on your computer?
Or are you running a limited account?

No idea if it would even make a difference. But since the code is good, it's got to be some setting or something of the like.