Trouble With Handling COM Events

This forum is for questions / discussions regarding development of addons / tweaks for MediaMonkey for Windows 4.

Moderators: Gurus, Addon Administrators

SpiderMonkey
Posts: 18
Joined: Sun May 27, 2007 4:49 pm

Trouble With Handling COM Events

Post by SpiderMonkey »

I'm completely new to COM and I have some experience programming in Python, and for a personal project to improve familiarity in both, and to make something practical with MediaMonkey, I was wondering if someone could help me figure out how to handle events? I've figured out how to change basic stuff, and make automated nodes, but events are a head scratcher for me. Any help would be greatly appreciated.
Melloware
Posts: 339
Joined: Mon Aug 18, 2008 9:46 am
Location: Philadelphia, PA, US
Contact:

Re: Trouble With Handling COM Events

Post by Melloware »

What language are you using or are you just using VBScript?
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
Melloware Inc.
MonkeyTunes - DACP Server for MediaMonkey
Intelliremote - Take Back Control of your HTPC!
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
SpiderMonkey
Posts: 18
Joined: Sun May 27, 2007 4:49 pm

Re: Trouble With Handling COM Events

Post by SpiderMonkey »

I'm using Python as the programming/scripting language.
Melloware
Posts: 339
Joined: Mon Aug 18, 2008 9:46 am
Location: Philadelphia, PA, US
Contact:

Re: Trouble With Handling COM Events

Post by Melloware »

SpiderMonkey wrote:I'm using Python as the programming/scripting language.
Well the problem with Events from any "out-of-process" application like Python, .NET, Delphi etc is that they do not seem to register the events. IF you search for it in the forums you will see nothing but problems with the Events not working outside of an "in-process" call.

The only way I have seen it solved is in .NET by using a COM object and registering that COM object of your out of process app in MM itself.

See this thread: http://www.mediamonkey.com/forum/viewto ... nts#p69933
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
Melloware Inc.
MonkeyTunes - DACP Server for MediaMonkey
Intelliremote - Take Back Control of your HTPC!
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
SpiderMonkey
Posts: 18
Joined: Sun May 27, 2007 4:49 pm

Re: Trouble With Handling COM Events

Post by SpiderMonkey »

@Melloware: I wish I knew what to look for since I'm not familiar with .Net. Maybe you can explain it?

@Anyone with Python Experience/COM Handling:

This is the code I'm using to get the COM Object.

Code: Select all

import win32com.client

SDB = win32com.client.Dispatch("SongsDB.SDBApplication")
I can use some methods and get some information...

Code: Select all

>>> SDB.Player.isPlaying
True
>>>SDB.Player.Pause
...and I can pause the player and control it, etc.

Also, if I want to find out exactly what will fire when the external process searches for events, I can even find that out.

Code: Select all

>>> events = win32com.client.getevents("SongsDB.SDBApplication")
>>> events
<class win32com.gen_py.E602ED16-8EF9-4F08-B09F-6F6E8306C51Bx0x1x0.ISDBApplicationEvents at 0x02055FC0>
Basically, it knows what classes are going to execute when an event is called. But I don't know of a way to get the external process to tell when the class executes. After some online searches, I figured I could use a "while" loop but I wouldn't know what to check for.

I know the simple solution would just be to do everything in VBS. From using a helper script to launch the python file, to maybe having VBS handle the events and then make calls to the python script, but I wouldn't know how to start doing that too. Hmmm... I'll still think about it...
markstuartwalker
Posts: 931
Joined: Fri Jul 10, 2009 8:10 am

Re: Trouble With Handling COM Events

Post by markstuartwalker »

I'm sure that you can achieve what you need with Python but you're definitely going to be in the minority. I've used both VBScript and Delphi to interface to MM quite extensively now. I'd suggest learning VBS - it's actually simpler than Python and most tasks can be achieved with little more than Notepad to edit the files.

During my Delphi work I autogenerated an MM type library from the executable (fragment attached) which shows the interface exposed by MM through the COM interface.

Code: Select all

// *********************************************************************//
// Interface: ISDBApplication
// Flags:     (4416) Dual OleAutomation Dispatchable
// GUID:      {1FAF02F8-A7D3-41F1-9210-A3B12046F136}
// *********************************************************************//
  ISDBApplication = interface(IDispatch)
    ['{1FAF02F8-A7D3-41F1-9210-A3B12046F136}']
    function Get_VersionString: WideString; safecall;
    function Get_CurrentSongList: ISDBSongList; safecall;
    function Get_SelectedSongList: ISDBSongList; safecall;
    function Get_Player: ISDBPlayer; safecall;
    function Get_IniFile: ISDBIniFile; safecall;
    function Get_VersionHi: Integer; safecall;
    function Get_VersionLo: Integer; safecall;
    function Get_Database: ISDBDatabase; safecall;
    function MessageBox(const MessageText: WideString; MsgType: EnumMsgBox; Buttons: OleVariant): Integer; safecall;
    function Get_VersionRelease: Integer; safecall;
    function Get_Progress: ISDBProgress; safecall;
    function Get_CommonDialog: ISDBCommonDialog; safecall;
    function Get_AllVisibleSongList: ISDBSongList; safecall;
    function Localize(const Value: WideString): WideString; safecall;
    function toASCII(const Value: WideString): WideString; safecall;
    function Get_PlaylistByTitle(const Title: WideString): ISDBPlaylist; safecall;
    function Get_MainTree: ISDBTree; safecall;
    function Get_MainTracksWindow: ISDBTracksWindow; safecall;
    function Get_ShutdownAfterDisconnect: WordBool; safecall;
    procedure Set_ShutdownAfterDisconnect(Value: WordBool); safecall;
    function Get_IsRunning: WordBool; safecall;
    function Get_UI: ISDBUI; safecall;
    function Get_Registry: ISDBRegistry; safecall;
    function Get_Objects(const Name: WideString): IDispatch; safecall;
    procedure Set_Objects(const Name: WideString; const Value: IDispatch); safecall;
    function RegisterIcon(const Filename: WideString; IconType: Integer): Integer; safecall;
    function Get_NewSongData: ISDBSongData; safecall;
    function Get_Device: SDBDevice; safecall;
    function Get_ApplicationPath: WideString; safecall;
    function Get_MyMusicPath: WideString; safecall;
    function Get_Tools: ISDBTools; safecall;
    function Get_NewSongList: ISDBSongList; safecall;
    function Format(const Text: WideString; Param1: OleVariant; Param2: OleVariant; 
                    Param3: OleVariant): WideString; safecall;
    function LocalizedFormat(const Text: WideString; Param1: OleVariant; Param2: OleVariant; 
                             Param3: OleVariant): WideString; safecall;
    function SelectFolder(const Folder: WideString; const ShowText: WideString): WideString; safecall;
    function Get_TemporaryFolder: WideString; safecall;
    procedure ProcessMessages; safecall;
    function RegisterIconHandle(IconHandle: Integer; IconType: Integer): Integer; safecall;
    function CreateTimer(Interval: Integer): ISDBTimer; safecall;
    function Get_WebControl: IDispatch; safecall;
    function LocalizeGen(const LangFile: WideString; const Value: WideString): WideString; safecall;
    function Get_CursorType: Integer; safecall;
    procedure Set_CursorType(Value: Integer); safecall;
    function Get_NewStringList: ISDBStringList; safecall;
    procedure RefreshScriptItems; safecall;
    function IsKnownFileType(const Filename: WideString): WordBool; safecall;
    function Get_IconsPath: WideString; safecall;
    function Get_EqualizerPath: WideString; safecall;
    function Get_SkinsPath: WideString; safecall;
    function Get_PluginsPath: WideString; safecall;
    function Get_ScriptsPath: WideString; safecall;
    function Get_VersionBuild: Integer; safecall;
    function Get_ComServerUIActive: WordBool; safecall;
    procedure Set_ComServerUIActive(Value: WordBool); safecall;
    property VersionString: WideString read Get_VersionString;
    property CurrentSongList: ISDBSongList read Get_CurrentSongList;
    property SelectedSongList: ISDBSongList read Get_SelectedSongList;
    property Player: ISDBPlayer read Get_Player;
    property IniFile: ISDBIniFile read Get_IniFile;
    property VersionHi: Integer read Get_VersionHi;
    property VersionLo: Integer read Get_VersionLo;
    property Database: ISDBDatabase read Get_Database;
    property VersionRelease: Integer read Get_VersionRelease;
    property Progress: ISDBProgress read Get_Progress;
    property CommonDialog: ISDBCommonDialog read Get_CommonDialog;
    property AllVisibleSongList: ISDBSongList read Get_AllVisibleSongList;
    property PlaylistByTitle[const Title: WideString]: ISDBPlaylist read Get_PlaylistByTitle;
    property MainTree: ISDBTree read Get_MainTree;
    property MainTracksWindow: ISDBTracksWindow read Get_MainTracksWindow;
    property ShutdownAfterDisconnect: WordBool read Get_ShutdownAfterDisconnect write Set_ShutdownAfterDisconnect;
    property IsRunning: WordBool read Get_IsRunning;
    property UI: ISDBUI read Get_UI;
    property Registry: ISDBRegistry read Get_Registry;
    property Objects[const Name: WideString]: IDispatch read Get_Objects write Set_Objects;
    property NewSongData: ISDBSongData read Get_NewSongData;
    property Device: SDBDevice read Get_Device;
    property ApplicationPath: WideString read Get_ApplicationPath;
    property MyMusicPath: WideString read Get_MyMusicPath;
    property Tools: ISDBTools read Get_Tools;
    property NewSongList: ISDBSongList read Get_NewSongList;
    property TemporaryFolder: WideString read Get_TemporaryFolder;
    property WebControl: IDispatch read Get_WebControl;
    property CursorType: Integer read Get_CursorType write Set_CursorType;
    property NewStringList: ISDBStringList read Get_NewStringList;
    property IconsPath: WideString read Get_IconsPath;
    property EqualizerPath: WideString read Get_EqualizerPath;
    property SkinsPath: WideString read Get_SkinsPath;
    property PluginsPath: WideString read Get_PluginsPath;
    property ScriptsPath: WideString read Get_ScriptsPath;
    property VersionBuild: Integer read Get_VersionBuild;
    property ComServerUIActive: WordBool read Get_ComServerUIActive write Set_ComServerUIActive;
  end;

Of course this will not help with your solution but it does show the interface in a source-code manner.

Regards
Mark
Windows 7,8 / Ubuntu 13.10 / Mavericks 10.9 / iOS 7.1 / iTunes 11.1
iTunes plugin (d_itunes & itunes4) http://www.mediamonkey.com/forum/viewto ... =2&t=45713
Running MM under Mac OS X with Wine http://www.mediamonkey.com/forum/viewto ... =4&t=58507
Melloware
Posts: 339
Joined: Mon Aug 18, 2008 9:46 am
Location: Philadelphia, PA, US
Contact:

Re: Trouble With Handling COM Events

Post by Melloware »

I agree with Mark on this one. However even when using Delphi and COM I was never able to get the events to fire in my code.

The only way I was able to accomplish that is by using a combination of VBS to actually initialize my code with the SongsDB object and register the events.
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
Melloware Inc.
MonkeyTunes - DACP Server for MediaMonkey
Intelliremote - Take Back Control of your HTPC!
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
markstuartwalker
Posts: 931
Joined: Fri Jul 10, 2009 8:10 am

Re: Trouble With Handling COM Events

Post by markstuartwalker »

The device driver (Delphi) used events to trap the tree node expansion/contraction. I know the code works but I only modified it to my own needs so can't take all the credit.

Here is a few snippets ...

Code: Select all

procedure AddNodes( Node:Variant; Start:boolean);
var
  Tree, NewNode : Variant;
  FConnectionToken : Longint;
begin
  if Start then
  begin
    Tree := MMInterf.MainTree;

    if ShowLibraryNode then
    begin
      NewNode := Tree.CreateNode;
      NewNode.Caption := MMInterf.Localize( 'Library');
      NewNode.SortCriteria := -1000;
      NewNode.IconIndex := 19;   // Library icon
      NewNode.CustomNodeId := NODE_LIBRARY;
      if TreeEvents=nil then
        TreeEvents := TNodesEventSink.Create;
      InterfaceConnect( NewNode, DIID_ISDBTreeNodeEvents, TreeEvents, FConnectionToken);

      Tree.AddNode( Node, NewNode, 3);
      NewNode.HasChildren := True;

      NewNode := Tree.CreateNode;
      NewNode.Caption := MMInterf.Localize( 'Playlists');
      NewNode.SortCriteria := -1000;
      NewNode.IconIndex := 19;   // Library icon
      NewNode.CustomNodeId := NODE_PLAYLISTS;
      if TreeEvents=nil then
        TreeEvents := TNodesEventSink.Create;
      InterfaceConnect( NewNode, DIID_ISDBTreeNodeEvents, TreeEvents, FConnectionToken);

      Tree.AddNode( Node, NewNode, 3);
      NewNode.HasChildren := True;

    end;
  end;
end;

function TNodesEventSink.Invoke(DispID: Integer; const IID: TGUID;
  LocaleID: Integer; Flags: Word; var Params; VarResult, ExcepInfo,
  ArgErr: Pointer): HResult;
begin
  Result := S_OK;
  case DispID of
    201: OnNodeChildren( Variant(TDispParams(Params).rgvarg^[0]));
    202: OnNodeTracks( Variant(TDispParams(Params).rgvarg^[0]));
  end;
end;


Mark
Windows 7,8 / Ubuntu 13.10 / Mavericks 10.9 / iOS 7.1 / iTunes 11.1
iTunes plugin (d_itunes & itunes4) http://www.mediamonkey.com/forum/viewto ... =2&t=45713
Running MM under Mac OS X with Wine http://www.mediamonkey.com/forum/viewto ... =4&t=58507
SpiderMonkey
Posts: 18
Joined: Sun May 27, 2007 4:49 pm

Re: Trouble With Handling COM Events

Post by SpiderMonkey »

Mark,

is that your delphi script (snippet) for handling events? Mine going over it if it is?
mcow
Posts: 834
Joined: Sun Sep 21, 2008 9:35 pm
Location: Cupertino, California

Re: Trouble With Handling COM Events

Post by mcow »

I'm working on the same issue that spidermonkey was: I have an externally-started Python script that I want to respond to events from MediaMonkey.

I found an interesting bit of sample code, Python hooking into an event-generating COM object, at http://web.mit.edu/zacka/Public/wallcom ... /speech.py.
What I got from that code:
win32com.client.getevents() returns an event-handler class object, whose methods are the event handlers. That code implements a subclass that overrides the desired event handler method.
In order to process the events, there needs to be a loop (which you probably want in a background thread) calling pythoncom.PumpWaitingMessages(). In the example code, the subclassed event handler is instantiated with this interesting comment:

Code: Select all

     # Just creating a _ListenerCallback object makes events
     # fire till listener loses reference to its grammar object
     _ListenerCallback(context, listener, callback)
So I've tried a similar thing with MediaMonkey, but with no success yet. I can define and instantiate a subclass, but I haven't yet seen the event fire. It's not clear whether the script object's RegisterEvent() should be used in this case, and if it is, it's not clear which method name I should be passing to it.
Maybe the Script object is the one that's running the PumpWaitingMessages() loop in MediaMonkey.

If the script object is necessary, you can get to it using this:

Code: Select all

win32com.client.Dispatch('SongsDB.SDBScriptControl')
I guess that's a global object, not specific to a given script.

What would be great is for the Script object to support hooking directly into a subclass of the getevents() object.
Post Reply