Media Center Automation: Difference between revisions

From wiki.jriver.com
Jump to navigation Jump to search
No edit summary
No edit summary
Line 59: Line 59:
* [[MJServiceAutomation]] - interface for working with a single service
* [[MJServiceAutomation]] - interface for working with a single service
* [[MJCDDVDAutomation]] - interface for working with CD/DVDs
* [[MJCDDVDAutomation]] - interface for working with CD/DVDs

==Event Handling==
MediaCenter version 11.1 and higher supports an event interface for automation clients. An event is fired upon certain actions with three string parameters:
FireMJEvent(string1, string2, string3)

The first parameter is the event type and currently there is just one event type, namely "MJEvent type: MCCommand"
The second parameter identifies the MCC command. See the list below.
The third parameter is optional and may contain information specific to the command.

Here are the commands currently sent to the event handler:
#"MCC: NOTIFY_TRACK_CHANGE"
#"MCC: NOTIFY_PLAYLIST_ADDED"
#"MCC: NOTIFY_PLAYLIST_INFO_CHANGED"
#"MCC: NOTIFY_PLAYLIST_FILES_CHANGED"
#"MCC: NOTIFY_PLAYLIST_REMOVED"
#"MCC: NOTIFY_PLAYLIST_COLLECTION_CHANGED"
#"MCC: NOTIFY_PLAYLIST_PROPERTIES_CHANGED"
The track change event is fired whenever a new track starts to play.

===Handling the events from Visual Basic===
To add a handler in VB.NET for events coming from Media Center:

1. Add a reference to Media Center's type library ("Media Center 12.tlb"). In VB.NET use the Projects/Add Reference option, select the "COM" tab, then select "MediaCenter" from the list.

2. Add the "Imports MediaCenter" statement to your code.

3. Declare your MC automation variable to handle events, like this:
Dim WithEvents MC As MCAutomation

4. Set the MC variable using either GetObject or CreateObject as described elsewhere in this document.

5. Declare a handler function for the event like this:
Private Sub MJEvent(ByVal s1 As String, ByVal s2 As String, ByVal s3 As String) Handles MC.FireMJEvent
MsgBox(s1 + " " + s2)
End Sub

6. That's it. Events fired from MediaCenter should come to your function.

===Handling the events from Visual C++ with MFC===
In your C++ MFC application, you should have a class which is derived from CCmdTarget (or from CWnd or CDialog which derive from CCmdTarget). This class will sink the events from MC's automation object. In the sample code below, this class is called "CMyClass", substitute your class's name for CMyClass.
In the header file of the class, add the following import using the correct tlb path for your system. Also add the afxctl.h include file if you get warnings of undefined AfxConnectionAdvise and AfxConnectionUnadvise.

#import "C:\\Program Files\\J River\\Media Center 12\\Media Center 12.tlb" no_namespace, named_guids
#include "afxctl.h"

Then within the header file's class declaration for CMyClass, add this code:
<pre>
DECLARE_DISPATCH_MAP()
DECLARE_INTERFACE_MAP()
afx_msg void MJEvent(LPCTSTR strType, LPCTSTR strParam1, LPCTSTR strParam2);
IMJAutomationPtr m_pMJ;
DWORD m_SinkID;
</pre>

Now in the cpp file for the class, add these lines which connect your handler function to the correct entry in the dispatch map:
<pre>
BEGIN_DISPATCH_MAP(CMyClass, CCmdTarget)
DISP_FUNCTION_ID(CMyClass,"MJEvent",1,MJEvent,VT_EMPTY,VTS_BSTR VTS_BSTR VTS_BSTR)
END_DISPATCH_MAP()

BEGIN_INTERFACE_MAP(CMyClass, CCmdTarget)
INTERFACE_PART(CMyClass, DIID_IMJAutomationEvents, Dispatch)
END_INTERFACE_MAP()
</pre>

Now add the following code to the constructor or wherever you do initialization:
<pre>
CoInitialize(NULL);
EnableAutomation();
HRESULT hr = m_pMJ.GetActiveObject (L"MediaJukebox Application");
if (hr != S_OK)
m_pMJ.CreateInstance(L"MediaJukebox Application");

// Get a pointer to the sink IDispatch interface
LPUNKNOWN pUnknownSink = GetIDispatch(FALSE);

// Connect the event source (MJ automation) to the sink
AfxConnectionAdvise(m_pMJ, DIID_IMJAutomationEvents, pUnknownSink, FALSE, &m_SinkID);
</pre>

Add the function body which receives the events:

<pre>
// MJAutomationSink message handler
void CMyClass::MJEvent(LPCTSTR strType, LPCTSTR strParam1, LPCTSTR strParam2)
{
// handle events here...
}
</pre>

And finally, add this code to the destructor or wherever you do final cleanup. The release for m_pMJ has to be called prior to CoUninitialize.
<pre>
LPUNKNOWN pUnknownSink = GetIDispatch(FALSE);
AfxConnectionUnadvise(m_pMJ, DIID_IMJAutomationEvents, pUnknownSink, FALSE, m_SinkID);
m_pMJ.Release();
CoUninitialize();
</pre>

Revision as of 20:48, 26 February 2007

Media Center exports much of its power and functionality through COM automation interfaces. This makes it easy to create your own plug-ins for Media Center that integrate tightly with its interface. Since automation interfaces are based on COM, these plug-ins can be written in almost any language. (C++, VB, Delphi, etc.). Also, the interfaces can be accessed in Metamorphis skins and Display plug-ins (see documentation for the plug-ins). Prior to build 9.1.238, the only way to access the automation interfaces was to write a plug-in for MC because the interfaces were not accessible by objects which were outside Media Center's process. Now, any application, including VBS and Java script, can start MC or just run it to query information about MC's database.

The functions in this document are for Media Center 11.1. Most will work with earlier versions, but not all.

Initialization

Media Center can be initialized by in-proc or out-of-proc objects.

Out-of-proc initialization (C++):

#import "Media Jukebox.tlb" no_namespace, named_guids

void GetMJAutomation()
{
    IMJAutomationPtr pMJ;

    HRESULT hr = pMJ.GetActiveObject (L"MediaJukebox Application");
    if (hr != S_OK)
        pMJ.CreateInstance(L"MediaJukebox Application");
}

Out-of-proc initialization (VB):

Private Sub Form_Load()
' First try to get an already running object
On Error Resume Next
Set myobj = GetObject(, "MediaJukebox Application")

If Err.Number = 429 Then
    'Then, create a new object
    Set myobj = CreateObject("MediaJukebox Application")

End Sub

NOTE: If Media Center was created as out-of-proc object, the main window of the program will be invisible. To show the window use ShowProgram function from MJAutomation interface.

For information on how to get access to MJAutomation interface from in-proc plug-ins see plug-in SDK.

Automation Objects

Event Handling

MediaCenter version 11.1 and higher supports an event interface for automation clients. An event is fired upon certain actions with three string parameters: FireMJEvent(string1, string2, string3)

The first parameter is the event type and currently there is just one event type, namely "MJEvent type: MCCommand" The second parameter identifies the MCC command. See the list below. The third parameter is optional and may contain information specific to the command.

Here are the commands currently sent to the event handler:

  1. "MCC: NOTIFY_TRACK_CHANGE"
  2. "MCC: NOTIFY_PLAYLIST_ADDED"
  3. "MCC: NOTIFY_PLAYLIST_INFO_CHANGED"
  4. "MCC: NOTIFY_PLAYLIST_FILES_CHANGED"
  5. "MCC: NOTIFY_PLAYLIST_REMOVED"
  6. "MCC: NOTIFY_PLAYLIST_COLLECTION_CHANGED"
  7. "MCC: NOTIFY_PLAYLIST_PROPERTIES_CHANGED"

The track change event is fired whenever a new track starts to play.

Handling the events from Visual Basic

To add a handler in VB.NET for events coming from Media Center:

1. Add a reference to Media Center's type library ("Media Center 12.tlb"). In VB.NET use the Projects/Add Reference option, select the "COM" tab, then select "MediaCenter" from the list.

2. Add the "Imports MediaCenter" statement to your code.

3. Declare your MC automation variable to handle events, like this:

  Dim WithEvents MC As MCAutomation

4. Set the MC variable using either GetObject or CreateObject as described elsewhere in this document.

5. Declare a handler function for the event like this:

   Private Sub MJEvent(ByVal s1 As String, ByVal s2 As String, ByVal s3 As String) Handles MC.FireMJEvent
       MsgBox(s1 + "   " + s2)
   End Sub

6. That's it. Events fired from MediaCenter should come to your function.

Handling the events from Visual C++ with MFC

In your C++ MFC application, you should have a class which is derived from CCmdTarget (or from CWnd or CDialog which derive from CCmdTarget). This class will sink the events from MC's automation object. In the sample code below, this class is called "CMyClass", substitute your class's name for CMyClass. In the header file of the class, add the following import using the correct tlb path for your system. Also add the afxctl.h include file if you get warnings of undefined AfxConnectionAdvise and AfxConnectionUnadvise.

 #import "C:\\Program Files\\J River\\Media Center 12\\Media Center 12.tlb" no_namespace, named_guids
 #include "afxctl.h"

Then within the header file's class declaration for CMyClass, add this code:

  DECLARE_DISPATCH_MAP()
  DECLARE_INTERFACE_MAP()
  afx_msg void MJEvent(LPCTSTR strType, LPCTSTR strParam1, LPCTSTR strParam2);
  IMJAutomationPtr m_pMJ;
  DWORD m_SinkID;

Now in the cpp file for the class, add these lines which connect your handler function to the correct entry in the dispatch map:

  BEGIN_DISPATCH_MAP(CMyClass, CCmdTarget)
    DISP_FUNCTION_ID(CMyClass,"MJEvent",1,MJEvent,VT_EMPTY,VTS_BSTR VTS_BSTR VTS_BSTR)
  END_DISPATCH_MAP()

  BEGIN_INTERFACE_MAP(CMyClass, CCmdTarget)
    INTERFACE_PART(CMyClass, DIID_IMJAutomationEvents, Dispatch)
  END_INTERFACE_MAP()

Now add the following code to the constructor or wherever you do initialization:

  CoInitialize(NULL);
  EnableAutomation();
  HRESULT hr = m_pMJ.GetActiveObject (L"MediaJukebox Application");
  if (hr != S_OK)
    m_pMJ.CreateInstance(L"MediaJukebox Application");

  // Get a pointer to the sink IDispatch interface
  LPUNKNOWN pUnknownSink = GetIDispatch(FALSE);

  // Connect the event source (MJ automation) to the sink
  AfxConnectionAdvise(m_pMJ, DIID_IMJAutomationEvents, pUnknownSink, FALSE, &m_SinkID);

Add the function body which receives the events:

  // MJAutomationSink message handler
  void CMyClass::MJEvent(LPCTSTR strType, LPCTSTR strParam1, LPCTSTR strParam2)
  {
    // handle events here...
  }

And finally, add this code to the destructor or wherever you do final cleanup. The release for m_pMJ has to be called prior to CoUninitialize.

  LPUNKNOWN pUnknownSink = GetIDispatch(FALSE);
  AfxConnectionUnadvise(m_pMJ, DIID_IMJAutomationEvents, pUnknownSink, FALSE, m_SinkID);
  m_pMJ.Release();
  CoUninitialize();