Getting active SDBApplication COM object

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

Moderator: Gurus

jimbo11883
Posts: 3
Joined: Thu Jun 21, 2007 7:07 am

Getting active SDBApplication COM object

Post by jimbo11883 » Thu Jun 21, 2007 7:20 am

I'm developing an application with a feature that has simple playback controls for MM, and shows the current song and playback time, etc.

The way my app works, is that it will check for the MM main window by using FindWindow() API call. If the window is found, then it will obtain a reference to MM's SDBApplication object by using the GetObject() function built into VB.Net. This is called late binding. The reason I am using late binding is because it seems that referencing the type library means that the end user would have to already have MM installed on their computer to use my app. My application does much, much more than just work with MM. It also can control Foobar2000, Winamp, WiMP, for example.

To make a long story short, I need to be able to access the SongsDB.SDBApplication COM Object so that I can get information and send commands to MM. I need to be able to do this by using GetObject(). This method works fine with foobar2000 and the COM server plugin for it.

So can you make a SDBApplication instance available system-wide for use with GetObject() by creating the instance and adding it to the COM ROT?

Thanks.

darchangel
Posts: 26
Joined: Wed Nov 30, 2005 11:23 pm

Post by darchangel » Thu Jun 21, 2007 7:44 am

I don't know how to do this with GetObject but it can be done with CreateObject: external scripts and applications

Right now I'm working on a .NET project very similar to yours. If you can figure out how to use a reference to MediaMonkey and not get a COM server warning when everything is shut down, please let me know. I can't figure out how to do so.

Also check out this thread. It may save you some headaches and you may be able to help with some of mine.

jimbo11883
Posts: 3
Joined: Thu Jun 21, 2007 7:07 am

Post by jimbo11883 » Thu Jun 21, 2007 8:00 am

I've already tried all that. I specifically want support for the GetObject() way of getting an existing SDBApplication object. I DO NOT want to create a new one, as it causes problems. The app needs to be able to run on systems where MM is not installed.

Steegy
Posts: 3452
Joined: Sat Nov 05, 2005 7:17 pm
Location: Belgium
Contact:

Post by Steegy » Thu Jun 21, 2007 9:39 am

Why not use the ease of early binding (ok, it's an extra dll...). When you don't find MediaMonkey using FindWindow(), then nothing has to happen.
When you do find the window, you can instantiate a new SDBApplication Class object.

Apart from this, do you think that using late binding with MediaMonkey would enable .NET to properly catch the MM events?
I DO NOT want to create a new one, as it causes problems.
What kind of problems are these?

I'm just curious, because just like darchangel, I want to learn how to decently interact with MM through .NET.
Extensions: ExternalTools, ExtractFields, SongPreviewer, LinkedTracks, CleanImport, and some other scripts (Need Help with Addons > List of All Scripts).

darchangel
Posts: 26
Joined: Wed Nov 30, 2005 11:23 pm

Post by darchangel » Thu Jun 21, 2007 10:29 am

@jimbo11883: Whatever you do/find with regard to this matter, PLEASE continue to report on it here. The MM+.NET knowledge here is thin. Aside from you, there's me, Steegy, and 1 or 2 other forum users who post about these issues. And some issues are still as of yet unsolved. If you figure out how to accomplish your above goal, it could help out some other pending issues sometimes discussed around here.

Steegy
Posts: 3452
Joined: Sat Nov 05, 2005 7:17 pm
Location: Belgium
Contact:

Post by Steegy » Thu Jun 21, 2007 11:22 am

Stax, are you still around?
Extensions: ExternalTools, ExtractFields, SongPreviewer, LinkedTracks, CleanImport, and some other scripts (Need Help with Addons > List of All Scripts).

jimbo11883
Posts: 3
Joined: Thu Jun 21, 2007 7:07 am

Post by jimbo11883 » Thu Jun 21, 2007 11:58 am

Steegy wrote:Why not use the ease of early binding (ok, it's an extra dll...). When you don't find MediaMonkey using FindWindow(), then nothing has to happen.
When you do find the window, you can instantiate a new SDBApplication Class object.

Apart from this, do you think that using late binding with MediaMonkey would enable .NET to properly catch the MM events?
I DO NOT want to create a new one, as it causes problems.
What kind of problems are these?

I'm just curious, because just like darchangel, I want to learn how to decently interact with MM through .NET.
I can't use early binding as it causes problems when the user doesn't have MM installed. As far as MM events go, I never used them before, so I don't know about problems with them. I don't want to use CreateObject() because that causes my app to freeze for some reason when MM is running before my app is.

mistresso
Posts: 67
Joined: Sun Feb 24, 2008 11:15 am
Location: New Haven
Contact:

Re: Getting active SDBApplication COM object

Post by mistresso » Fri Feb 13, 2009 9:12 pm

Old post somewhat, but I wonder if anyone has an answer on this? In my recent .Net experiments, I have the exact same problem. To wit, if MM is not running when I start my app, when it gets to the "SongsDB.SDBApplication SDB = new SongsDB.SDBApplication();" line it will make a call to startup MediaMonkey and exit back to the .Net app just fine. BUT... if MM is already up ... I get a COM error, specifically 80080005 - which is a permissions error.

This isn't yet another VISTA issue either - I am running on XP SP3, and have not accessed any web browser windows. This is a simple matter of just talking to the SongsDB COM object. I know the "easy workaround" is just to shut down MM before running the app (or throw in a kludge like ignoring the exception), but there should be an easy answer to this.

flashk
Posts: 188
Joined: Sun Dec 11, 2005 5:04 pm

Re: Getting active SDBApplication COM object

Post by flashk » Mon Feb 16, 2009 4:45 pm

mistresso wrote:Old post somewhat, but I wonder if anyone has an answer on this? In my recent .Net experiments, I have the exact same problem. To wit, if MM is not running when I start my app, when it gets to the "SongsDB.SDBApplication SDB = new SongsDB.SDBApplication();" line it will make a call to startup MediaMonkey and exit back to the .Net app just fine. BUT... if MM is already up ... I get a COM error, specifically 80080005 - which is a permissions error.

This isn't yet another VISTA issue either - I am running on XP SP3, and have not accessed any web browser windows. This is a simple matter of just talking to the SongsDB COM object. I know the "easy workaround" is just to shut down MM before running the app (or throw in a kludge like ignoring the exception), but there should be an easy answer to this.
I guess the easy answer is that this is not currently possible with MM. I've wanted the same behavior, but it seems that MM does not register itself with the global COM ROT (Running Object Table). As far as I know, that would be the only way to get a handle to the MM COM server without starting it up. Maybe one of the MM developers can shed some light on this issue?

Peke
Posts: 12472
Joined: Tue Jun 10, 2003 7:21 pm
Location: Serbia
Contact:

Re: Getting active SDBApplication COM object

Post by Peke » Mon Feb 16, 2009 7:53 pm

I'm re reading this and as I'm not .NET dev I wonder what may cause your issues. Are you closing COM object Correctly each time prior to creating new one?
FindWindow Fixes all problems
Skinned version = Findwindow('TFMainWindow','MediaMonkey');
NON-Skinned Version = Findwindow('TFMainWindow.UnicodeClass','MediaMonkey');
Best regards,
Pavle
MediaMonkey Team lead QA/Tech Support guru
Admin of Free MediaMonkey addon Site HappyMonkeying
Image
Image
How to add SCREENSHOTS to forum

mistresso
Posts: 67
Joined: Sun Feb 24, 2008 11:15 am
Location: New Haven
Contact:

Re: Getting active SDBApplication COM object

Post by mistresso » Sat Feb 21, 2009 12:42 am

FindWindow is actually old VB6 era code :-)

With .Net it seems you're supposed to now use the Interop libraries or Reflection libraries to get direct access to pointers and running assemblies. Or even Process.GetProcessesByName().

However, with this and other newer languages, the recommendation seems to be to stick to just calling "new SongsDB.SDBApplication" command rather than file handles, since it's supposed to return a reference to the MM COM object either way.

In my case the erratic behavior was caused by stupidly naming my test application MediaMonkey (which was the test project name/folder), thereby causing mass confusion when it came to differentiating between processes. So kids, don't try this at home.

As far as talking to MM without starting it up ... do you mean talking to the API without MM actually -running- in the background?

mayberg
Posts: 7
Joined: Wed Aug 26, 2009 1:28 pm

Re: Getting active SDBApplication COM object

Post by mayberg » Sun Aug 08, 2010 7:12 am

I have the same problem. If MediaMonkey is not running yet, I can create a new instance by calling "new SongsDB.SDBApplication();" and everything works fine. But if Media Monkey is already running, I cannot connect to the COM-Server. There seems to be no way to connect to a running instance.

Some C# code that demonstrates the problem:

Code: Select all

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;

namespace Test
{
    class Program
    {
        static void Main(string[] args)
        {
            ConnectToMediaMonkeyViaMarshalGetActiveObject();
            ConnectToMediaMonkeyViaNativeCall();
            ConnectOrCreateMediaMonkey();
        }

        public static void ConnectOrCreateMediaMonkey()
        {
            try
            {
                Console.Out.WriteLine("Creating MediaMonkey COM Appliation Object...");
                SongsDB.SDBApplication SDB = new SongsDB.SDBApplication();
                Console.Out.WriteLine("MediaMonkey COM Appliation Object created.");
            }
            catch (COMException ex)
            {
                Console.Out.WriteLine("Failed to create MediaMonkey COM Appliation Object.");
                Console.Out.WriteLine(ex.Message);
                Console.Out.WriteLine(ex.StackTrace);
            }

            Console.Out.WriteLine("Press any key to continue.");
            Console.ReadKey();
        }

        public static void ConnectToMediaMonkeyViaMarshalGetActiveObject()
        {
            try
            {
                Console.Out.WriteLine("Connecting to MediaMonkey COM Appliation Object via Marshal.GetActiveObject...");
                SongsDB.SDBApplication SDB = (SongsDB.SDBApplication)Marshal.GetActiveObject("SongsDB.SDBApplication");
                Console.Out.WriteLine("MediaMonkey COM Appliation Object connected.");
            }
            catch (COMException ex)
            {
                Console.Out.WriteLine("Failed to connect to MediaMonkey COM Appliation Object.");
                Console.Out.WriteLine(ex.Message);
                Console.Out.WriteLine(ex.StackTrace);
            }

            Console.Out.WriteLine("Press any key to continue.");
            Console.ReadKey();
        }

        public static void ConnectToMediaMonkeyViaNativeCall()
        {
            try
            {
                Console.Out.WriteLine("Connecting to MediaMonkey COM Appliation Object via native call...");
                Guid guid = Marshal.GenerateGuidForType(typeof(SongsDB.ISDBApplication));
                object obj;
                GetActiveObject(ref guid, IntPtr.Zero, out obj);
                SongsDB.SDBApplication SDB = (SongsDB.SDBApplication)obj;
                Console.Out.WriteLine("MediaMonkey COM Appliation Object connected.");
            }
            catch (COMException ex)
            {
                Console.Out.WriteLine("Failed to connect to MediaMonkey COM Appliation Object.");
                Console.Out.WriteLine(ex.Message);
                Console.Out.WriteLine(ex.StackTrace);
            }

            Console.Out.WriteLine("Press any key to continue.");
            Console.ReadKey();
        }

        [DllImport("oleaut32.dll", PreserveSig = false)]
        static extern void GetActiveObject(ref Guid rclsid, IntPtr pvReserved, [MarshalAs(UnmanagedType.IUnknown)] out Object ppunk);
    }
}
Results with the following output, if MM is already running:

Code: Select all

Connecting to MediaMonkey COM Appliation Object via Marshal.GetActiveObject...
Failed to connect to MediaMonkey COM Appliation Object.
Vorgang nicht verfgbar. (Exception from HRESULT: 0x800401E3 (MK_E_UNAVAILABLE))
   at System.Runtime.InteropServices.Marshal.GetActiveObject(Guid& rclsid, IntPtr reserved, Object& ppunk)
   at System.Runtime.InteropServices.Marshal.GetActiveObject(String progID)
   at CopyPlaylistsWithFiles.Program.ConnectToMediaMonkeyViaMarshalGetActiveObject() in D:\Benutzer\markus\documents\visual studio 2010\Projects\CopyPlaylistsWithFiles\CopyPlaylistsWithFiles\Program.cs:line 41
Press any key to continue.
Connecting to MediaMonkey COM Appliation Object via native call...
Failed to connect to MediaMonkey COM Appliation Object.
Vorgang nicht verfgbar. (Exception from HRESULT: 0x800401E3 (MK_E_UNAVAILABLE))
   at CopyPlaylistsWithFiles.Program.GetActiveObject(Guid& rclsid, IntPtr pvReserved, Object& ppunk)
   at CopyPlaylistsWithFiles.Program.ConnectToMediaMonkeyViaNativeCall() in D:\Benutzer\markus\documents\visual studio 2010\Projects\CopyPlaylistsWithFiles\CopyPlaylistsWithFiles\Program.cs:line 62
Press any key to continue.
Creating MediaMonkey COM Appliation Object...
Failed to create MediaMonkey COM Appliation Object.
Retrieving the COM class factory for component with CLSID {148F7BB6-4943-4C53-8E30-0F9115D30283} failed due to the following error: 80080005 Starten des Servers fehlgeschlagen (Exception from HRESULT: 0x80080005 (CO_E_SERVER_EXEC_FAILURE)).
   at System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean noCheck, Boolean& canBeCached, RuntimeMethodHandleInternal& ctor, Boolean& bNeedSecurityCheck)
   at System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache)
   at System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean skipVisibilityChecks, Boolean skipCheckThis, Boolean fillCache)
   at System.Activator.CreateInstance(Type type, Boolean nonPublic)
   at System.Activator.CreateInstance(Type type)
   at CopyPlaylistsWithFiles.Program.ConnectOrCreateMediaMonkey() in D:\Benutzer\markus\documents\visual studio 2010\Projects\CopyPlaylistsWithFiles\CopyPlaylistsWithFiles\Program.cs:line 22
Press any key to continue.
If it is not running the method ConnectOrCreateMediaMonkey starts MM.

mayberg
Posts: 7
Joined: Wed Aug 26, 2009 1:28 pm

Re: Getting active SDBApplication COM object

Post by mayberg » Tue Aug 10, 2010 4:28 pm

After some more tries, I reinstalled MM and then checked the "Start MediaMonkey after Setup" checkbox at the end of the setup. This seems to help as far as the the call

Code: Select all

SongsDB.SDBApplication SDB = new SongsDB.SDBApplication();
does work now. Another variant that does work identically is:

Code: Select all

SongsDB.SDBApplication SDB = (SongsDB.SDBApplication)Microsoft.VisualBasic.Interaction.GetObject("", "SongsDB.SDBApplication");
Both calls create the COM-Object, if it is not running.

The Wiki instructs to use:

Code: Select all

SDB.ShutdownAfterDisconnect = false;
which is the default behaviour. This causes trouble:
- Stop MediaMonkey
- Run a program that calls "new SongsDB.SDBApplication();". This will create the COM-Server.
- Run the program again. It will try to connect to the COM-Server without success:

Code: Select all

Connecting to MediaMonkey COM Appliation Object...
Failed to connect to MediaMonkey COM Appliation Object.
Retrieving the COM class factory for component with CLSID {148F7BB6-4943-4C53-8E
30-0F9115D30283} failed due to the following error: 80080005 Starten des Servers
 fehlgeschlagen (Exception from HRESULT: 0x80080005 (CO_E_SERVER_EXEC_FAILURE)).

   at System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOn
ly, Boolean noCheck, Boolean& canBeCached, RuntimeMethodHandleInternal& ctor, Bo
olean& bNeedSecurityCheck)
   at System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean skipChec
kThis, Boolean fillCache)
   at System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean s
kipVisibilityChecks, Boolean skipCheckThis, Boolean fillCache)
   at System.Activator.CreateInstance(Type type, Boolean nonPublic)
   at System.Activator.CreateInstance(Type type)
   at TestMediaMonkeyComConnect.ComTests.ConnectOrCreateMediaMonkey() in D:\Benu
tzer\markus\documents\visual studio 2010\Projects\CopyPlaylistsWithFiles\TestMed
iaMonkeyComConnect\ComTests.cs:line 24
If the MM-COM-Object is created by the code it behaves different as if it is created by the user.

Finally - the only variant that really works for me is:

Code: Select all

SongsDB.SDBApplication SDB = new SongsDB.SDBApplication();
SDB.ShutdownAfterDisconnect = true;

Steegy_

Re: Getting active SDBApplication COM object

Post by Steegy_ » Thu Aug 12, 2010 12:28 pm

For my WebSourcesTagger I used this code:

Code: Select all

        private static SongsDB.SDBApplicationClass sdb;
        private static SongsDB.SDBApplicationClass SDB
        {
            get {
                if (sdb == null)
                {
                    sdb = new SongsDB.SDBApplicationClass();

                    while (!sdb.IsRunning) { System.Threading.Thread.Sleep(500); }

                    sdb.ShutdownAfterDisconnect = false;
                }
                return sdb;
            }
        }
Som basically it connects to the MM COM object. If MM is already running, the property returns quickly. If MM still has to start, the property-get keeps waiting for 0.5s until MM's isRunning becomes True, i.e. it's running, at least enough for COM interaction to start.

While COM connection with MediaMonkey wasn't always 100% stable, most of the times it worked perfectly. It worked both if MM was or wasn't running before.

Cheers,
Steegy

mayberg
Posts: 7
Joined: Wed Aug 26, 2009 1:28 pm

Re: Getting active SDBApplication COM object

Post by mayberg » Thu Aug 12, 2010 1:08 pm

Steegy_ wrote:For my WebSourcesTagger I used this code:

Code: Select all

        private static SongsDB.SDBApplicationClass sdb;
        private static SongsDB.SDBApplicationClass SDB
        {
            get {
                if (sdb == null)
                {
                    sdb = new SongsDB.SDBApplicationClass();

                    while (!sdb.IsRunning) { System.Threading.Thread.Sleep(500); }

                    sdb.ShutdownAfterDisconnect = false;
                }
                return sdb;
            }
        }
Som basically it connects to the MM COM object. If MM is already running, the property returns quickly. If MM still has to start, the property-get keeps waiting for 0.5s until MM's isRunning becomes True, i.e. it's running, at least enough for COM interaction to start.

While COM connection with MediaMonkey wasn't always 100% stable, most of the times it worked perfectly. It worked both if MM was or wasn't running before.

Cheers,
Steegy
Hi Steegy,

thanks for the reply. Unfortunatly it doesn't really help. What happens on your system, when you start the program a second time, reusing the instance which was created on the first call? On my system I get an exception:

Code: Select all

Creating MediaMonkey COM Appliation Object...
Failed to create MediaMonkey COM Appliation Object.
Retrieving the COM class factory for component with CLSID {148F7BB6-4943-4C53-8E
30-0F9115D30283} failed due to the following error: 80080005 Starten des Servers
 fehlgeschlagen (Exception from HRESULT: 0x80080005 (CO_E_SERVER_EXEC_FAILURE)).

   at System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOn
ly, Boolean noCheck, Boolean& canBeCached, RuntimeMethodHandleInternal& ctor, Bo
olean& bNeedSecurityCheck)
   at System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean skipChec
kThis, Boolean fillCache)
   at System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean s
kipVisibilityChecks, Boolean skipCheckThis, Boolean fillCache)
   at System.Activator.CreateInstance(Type type, Boolean nonPublic)
   at System.Activator.CreateInstance(Type type)
   at TestMediaMonkeyComConnect.ComTests.ConnectOrCreateMediaMonkey() in D:\Benu
tzer\markus\documents\visual studio 2010\Projects\CopyPlaylistsWithFiles\TestMed
iaMonkeyComConnect\ComTests.cs:line 24
Press any key to continue.
- First start: Everything seems to be fine.
- Second start (reuse instance created on first start): Exception.

The curious thing is that when calling this "sdb = new SongsDB.SDBApplicationClass();" twice or more times during one program execution it works. Once the application is restarted the problem shows up.

Post Reply