Scripting Tips & Tricks
- 1 (MediaMonkey) Scripting in general
- 1.1 'Correctly' update song properties
- 1.2 See MM debug log via DebugView
- 1.3 See MM Scripting interface using OLEView
- 1.4 Detecting docking of a panel
- 1.5 Keep reference to Form/Panel
- 1.6 Event mechanisms
- 1.7 Internal mechanism of script loading
- 1.8 Changing the current tree node
- 1.9 Interaction with MediaMonkey from outside
- 2 Visual Basic Script
- 3 Database & SQL
(MediaMonkey) Scripting in general
'Correctly' update song properties
Using the SongData object, you can either update the database/library (UpdateDB) and/or update the file's tags (WriteTags). So there's no way to let the song changes be saved like the user selected in the Options panel (menu Tools > Options > Library > Tags & Playlists > "Update tags when editing properties"). However, the UpdateAll method for the SongList object does take into account this user-specified option. So you can create a new SongList object using SDB.NewSongList and add your SDBSongData object(s) to it. Then call the UpdateAll method on the songlist, and your song(s) will be saved in user-specified way.
See MM debug log via DebugView
You can download DebugView directly from Microsoft. If used with the debug version of MM, lots of useful messages will be displayed in DebugView. Also, any message set via ISDBTools::OutputDebugStringMM will also be added to that debug log.
See MM Scripting interface using OLEView
MS Visual Studio contains a tool called OLEView ("OLE Viewer" or "OLE/COM Object Viewer") (direct link), which can show the type library of the SongsDB object "MediaMonkey Library". It requires the Iviewers.dll, which is not included, but can be downloaded from here or other 3rd-party locations. There, you can see all supported properties and methods of the MediaMonkey scripting interface. This can be handy to find out new scripting methods in case that the scripting reference isn't up-to-date yet.
The way to get there: OleView > Type Libraries > MediaMonkey Library > (double click)
and then in the ITypeLib Viewer > SongsDB (MediaMonkey Library) > Interfaces.
Detecting docking of a panel
When you un(dock) a dockable panel, the OnResize event is triggered twice:
- Once while the panel is being (un)docked with DockedTo = 0
- Once after the panel has been (undocked): with DockedTo = -1 (panel undocked) or DockedTo = X (panel docked, X is the number that represents the docking position)
Keep reference to Form/Panel
When you create a form or panel, always add a reference to the SDB.Objects dictionary. If you don't add it, the form/panel will be gone immediately after you created it, with or without error message. Instead of using the SDB.Objects dictionary you can also store a reference as a global script variable, in most cases.
There are still 2 event mechanisms available:
- Using the (mostly deprecated) old event mechanism (using Control.OnClickFunc and Control.UseScript), your global variables aren't remembered in the event handlers (e.g. kind of like the script is reopened). For those scripts, you had to use ParentControl.ChildControl("control name") or save object references to SDB's Objects dictionary.
- Using the new event mechanism, your global variables are remembered so you can use their values in the event handlers. The Objects dictionary is now only necessary to communicate/store object references between different scripts.
So always use the new event mechanism where you can. Only for option panels (sheets) this is not yet possible. There you'll need to store values between the main program and the event handlers in SDB.Objects, in the INI or in the Registry.
Internal mechanism of script loading
Normally, you don't need to know much about internal handling of scripts by MediaMonkey, but sometimes it might be useful to know, when exactly scripts are loaded.
When some function of a script is about to be executed (e.g. OnStartup function of auto-scripts), the script is loaded to memory and executed as necessary. When the execution finishes, the script is again unloaded from memory. However, there is on exception - if you register some event (by calling RegisterEvent function), the script remains still loaded in memory, until all registered events are unregistered, then it's unloaded.
This is good to know when you plan some sharing of variables. While the script is loaded in memory, you can use its global variables, but if you need to persist some values or objects between script sessions, you have to use some other mechanism (ini files, Objects collection, ...).
Note that starting from MediaMonkey 22.214.171.1245, there is always at most one instance of script loaded in memory. In older versions, if a script registered some events and then e.g. Option sheet functions were called back by MediaMonkey, they were processed in another instance of the script. Now they are processed in the already loaded instance, which helps avoid some memory leaks, is easier to handle and is also faster to process.
Changing the current tree node
Changing the selected node from a script (using SDB.MainTree.CurrentNode = ...........) takes some time to happen, and it happens asynchronicly (the code execution doesn't wait until the node is really selected). For that reason, you can't use (get value of) SDB.MainTree.CurrentNode directly after you have assigned a new node to it (set value to), because changes won't have happened yet.
Interaction with MediaMonkey from outside
There are at least three ways to interact with MediaMonkey from an external program:
- Open the MediaMonkey.exe program with command line options (arguments).
- Using the same Windows Messages (SendMessage/PostMessage) as in Winamp (WM_USER and WM_COMMAND). MM partially emulates how WinAmp works (not 100%, but very close) and so it can use WinAmp plug-ins. The class name to communicate with is "Winamp v1.x".
- Using OLE Automation, which can be very easily used e.g. from VB Script.
Dim SDB : Set SDB = CreateObject("SongsDB.SDBApplication")
SongsDB.SDBApplicationClass SDB = new SongsDB.SDBApplicationClass();
- Be sure to set SDB.ShutdownAfterDisconnect to False if you opened the MediaMonkey program by calling the SDB object, and you don't want it to be closed when your external program exits (or if you disconnect the SDB object = COM link).
Visual Basic Script
Index in VBScript
Usually indexes start from 0 (e.g. for normal arrays). However, sometimes (e.g. for string positions or for ISDBCommonDialog::FilterIndex) they start from 1.
To maintain good readability of your scripting code, make sure to add comments where necessary, split large methods in smaller ones, use appropriate indentation (tabs or 2/3/4 spaces), and if necessary split long lines of code over several lines. To split a line of VBScript code (not text or numbers), add a space and an underscore, and continue on the next line. Don't forget the space before the underscore or it won't work.
MsgBox "This is quite a long text to display on one single line and for readability we better split it on multiple lines" MsgBox "This is quite a long text to display on one single line" & " and for readability we better split it on multiple lines" MsgBox "This is quite a long text to display on one single line" _ & " and for readability we better split it on multiple lines"
In the third example the code is more readable, but the result is still the same: a message box with one long line of text. To split this over multiple lines, we preferably use the constant vbNewLine. If necessary, you can also use vbCr (Unix-style) or vbCrLf(Windows-style). Using these constants is more clear than using respectively Chr(13) (Unix-style) or Chr(13) & Chr(10) (Windows-style).
MsgBox "This is quite a long text to display on one single line" & vbNewLine _ & " and for readability we better split it on multiple lines"
Using Flags in VBScript
The concept of Flags can be a difficult for novice programmers. Click the link for an explanation of how to use them in VBScript (or Visual Basic).
- Reference for scripting resources
- Microsoft Script Center
- VBScript User's Guide and Language Reference
- VBScript Script Repository
- W3Schools VBScript learning
- Microsoft TechNet scripting products and technologies
- VBSEdit (debugging, object and COM lookup, integrated help)
- Sapien PrimalScript
- Microsoft Script Debugger for Windows NT 4.0 and Later (debugging)
- EditPad (Pro)
Database & SQL
Starting from MM 3.0 transactions are often used when working with database. It can cause some problems to script authors in case they don't know some details. One example where you can face a problem is when you use ISDBSongData::UpdateDB method while you have some SQL query open. What happens in this case is, that MM wants to start a transaction, but there's still an SQL query open. This results either in an error message (in debug build) or possibly an apparent freeze of MM in the release build.
In order to prevent this problem, you can either:
- Put all these DB operations in an transaction, i.e. use ISDBDatabase::BeginTransaction and ISDBDatabase::Commit.
- Close already running SQL queries before you call things like ISDBSongData::UpdateDB.
Creating complex SQL queries
To easily create complex SQL queries, you can use the Query graphical user interface in Access interface, and show the SQL code when you are done. In any case, make sure that your SQL code is organized is such a way that it can be understood easily, e.g. by putting SELECT, INSERT, FROM, ORDER BY, ... on separate lines with appropriate indentation.
Avoid SQL date problems
One of the problems with SQL is that dates are often confused, which sometimes give programming bugs. The best way to circumvent this problem is by using the format #yyyy-mm-dd# for all dates in SQL (e.g. #2006-10-31# for 31th October 2006).