Premise Problem with Minibroker and C# Windows Service

Motorola Premise

123

Senior Member
Here's a tough question for Damon and/or John!

What's the trick for making a Windows Service using Minibroker (in C#)? The SubscribeToProperty method works fine in a Windows Form application but fails in a Windows Service. The method takes three parameters:
  1. string PropertyName
  2. string NameOfCallbackFunction
  3. object ObjectContainingCallback
For example: MyCustomDevice.SubscribeToProperty("Play", "OnChangePlay", this);

The third parameter, ObjectContainingCallback, is usually set to "this" and works in a Windows Form. However, in a Windows Service, it throws the following exception:
This type has a ComVisible(false) parent in its hierarchy, therefore QueryInterface calls for IDispatch or class interfaces are disallowed.

I have added the following NameSpace:
using System.Runtime.InteropServices

and decorated all classes with:
[ComVisible(true)]

Again, this works in a Windows Form but fails in a Windows Service. What do I need to do to get it to work for a Service?
 
I haven't done anything in C#, but you may want to make sure the entire assembly is COM visible. In VS2008, right click the project and select properties. There's a button on the properties page that says Assembly Information. Click that and you should see a "Make Assembly COM VIsible" check box. Check it and you should be in business.

It took me days to figure that out... :lol:
 
... "Make Assembly COM VIsible" check box. Check it and you should be in business.
John,

Thanks for the quick reply. I've had that option enabled for all of my VS2008 Windows Form projects. It doesn't appear to do the trick for a Windows Service.

What's curious is that the Service has no problems with Minibroker's SubscribeToDiscovery method. In my mind, that indicates that the Service is able to communicate with the Minibroker DLL so some part is "ComVisible". However, unlike SubscribeToProperty, SubscribeToDiscovery doesn't need an ObjectContainingCallback argument and that's what throws the exception.

I've created a super-simple Forms app and a Service app with virtually identical code. The Forms app has no problem subscribing to a property ... the Service fails with the mentioned exception.
 
I haven't done anything in C#..
John,

If you can share any examples of a Minibroker Windows Service written in VB, it would be very helpful.

Here are two example that share virtually identical code except one is a Windows Form application (inherits from Form) and the other is a Service (inherits from ServiceBase). The Form works while the Service fails with the mentioned exception. For your convenience, I used DeveloperFusion's translator to convert the Service's C# code into VB.NET (no idea if it compiles).

The Form has two buttons, Start and Stop, and both examples simply speak the state of sys://Home/Light.

Windows Form (C#)
Code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using MINIBROKERLib;
using SpeechLib;
using System.Runtime.InteropServices;

namespace SYSForm
{
	[ComVisible(true)]
	public partial class SYSForm : Form
	{
		private ISYSMiniBroker m_mb = null;
		private IRemotePremiseObject m_sys = null;
		private IRemotePremiseObject m_device = null;
		private SpVoice m_speech = null;

		public SYSForm()
		{
			InitializeComponent();

			m_mb = new SYSMiniBrokerClass();
			m_mb.SetMethodTimeout(2000);
			m_speech = new SpVoice();
		}

		private void Start_Click(object sender, EventArgs e)
		{
			Debug.WriteLine("Started: " + this.Name);
			Connect();
		}

		private void Stop_Click(object sender, EventArgs e)
		{ Debug.WriteLine("Stopped: " + this.Name); }

		public void Connect()
		{
			try
			{
				m_sys = m_mb.Connect("ServerName", "", "");
				m_device = m_sys.GetObject("sys://Home/Light");
				if (m_device != null)
				{
					try
					{ m_device.SubscribeToProperty("PowerState", "OnPowerState", this); }
					catch (Exception ex)
					{ Debug.WriteLine("Failed to subscribe: " + ex.Message); }
				}
				else
					Debug.WriteLine("Cannot get Device.");
			}
			catch (Exception ex)
			{ Debug.WriteLine("Failed to connect: " + ex.Message); }
		}

		public virtual void OnPowerState(int subID, IRemotePremiseObject objectChanged, IRemotePremiseObject property, object newValue)
		{
			string Phrase;

			if ((bool) newValue == true)
				Phrase = "On.";
			else
				Phrase = "Off.";
			Debug.WriteLine("Speak: " + Phrase);
			m_speech.Speak(Phrase, SpeechVoiceSpeakFlags.SVSFlagsAsync);
		}
	}
}

Windows Service (VB.NET)
Code:
Imports System 
Imports System.Collections.Generic 
Imports System.ComponentModel 
Imports System.Data 
Imports System.Diagnostics 
Imports System.ServiceProcess 
Imports System.Text 
Imports MINIBROKERLib 
Imports SpeechLib 
Imports System.Runtime.InteropServices 


Namespace SYSService 
	<ComVisible(True)> _ 
	Public Partial Class SYSService 
		Inherits ServiceBase 
		Private m_mb As ISYSMiniBroker = Nothing 
		Private m_sys As IRemotePremiseObject = Nothing 
		Private m_device As IRemotePremiseObject = Nothing 
		Private m_speech As SpVoice = Nothing 
		
		Public Sub New() 
			InitializeComponent() 
			
			m_speech = New SpVoice() 
			m_mb = New SYSMiniBrokerClass() 
			m_mb.SetMethodTimeout(2000) 
		End Sub 
		
		Protected Overloads Overrides Sub OnStart(ByVal args As String()) 
			Debug.WriteLine("Started: " & Me.ServiceName) 
			Connect() 
		End Sub 
		
		Protected Overloads Overrides Sub OnStop() 
			Debug.WriteLine("Stopped: " & Me.ServiceName) 
		End Sub 
		
		Public Sub Connect() 
			Try 
				m_sys = m_mb.Connect("ServerName", "", "") 
				m_device = m_sys.GetObject("sys://Home/Light") 
				If m_device IsNot Nothing Then 
					Try 
						m_device.SubscribeToProperty("PowerState", "OnPowerState", Me) 
					Catch ex As Exception 
						Debug.WriteLine("Failed to subscribe: " & ex.Message) 
						' Throws an exception for SubscribeToProperty 
					End Try 
				Else 
					Debug.WriteLine("Cannot get Device.") 
				End If 
			Catch ex As Exception 
				Debug.WriteLine("Failed to connect: " & ex.Message) 
			End Try 
		End Sub 
		
		Public Overridable Sub OnPowerState(ByVal subID As Integer, ByVal objectChanged As IRemotePremiseObject, ByVal [property] As IRemotePremiseObject, ByVal newValue As Object) 
			Dim Phrase As String 
			
			If CBool(newValue) = True Then 
				Phrase = "On." 
			Else 
				Phrase = "Off." 
			End If 
			Debug.WriteLine("Speak: " & Phrase) 
			m_speech.Speak(Phrase, SpeechVoiceSpeakFlags.SVSFlagsAsync) 
		End Sub 
	End Class 
End Namespace
 
I've found a solution to the problem.

In the simple example above, it attempts to perform property subscriptions within the SYSService class (derived from ServiceBase). For whatever reason, the SubscribeToProperty method fails to work if "this" points to the SYSService object (i.e. the main program).

My solution simply changes the object pointed to by "this".

In the example below, I move the operation of property subscriptions to a separate class called SYSSubscriber. An instance of the class, called Subscriptions, is instantiated in SYSService. Now when SubscribeToProperty is called, "this" points to the Subscriptions object and not to SYSService.

Code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.ServiceProcess;
using System.Text;
using MINIBROKERLib;
using SpeechLib;
using System.Runtime.InteropServices;


namespace SYSService
{
	[ComVisible(true)]
	public partial class SYSService : ServiceBase
	{
		private ISYSMiniBroker m_mb = null;
		private IRemotePremiseObject m_sys = null;
		private IRemotePremiseObject m_device = null;
		private SYSSubscriber Subscriptions = null;

		public SYSService()
		{
			InitializeComponent();
			m_mb = new SYSMiniBrokerClass();
			m_mb.SetMethodTimeout(2000);
		}
		
		protected override void OnStart(string[] args)
		{
			Debug.WriteLine("Started: " + this.ServiceName );
			Connect();
		}

		protected override void OnStop()
		{ Debug.WriteLine("Stopped: " + this.ServiceName); }

		public void Connect()
		{
			try
			{
				m_sys = m_mb.Connect("ServerName", "", "");
				m_device = m_sys.GetObject("sys://Home/Light");
				if (m_device != null)
					Subscriptions = new SYSSubscriber(m_device); // Instantiate a new object that handles property subscriptions
				else
					Debug.WriteLine("Cannot get Device.");
			}
			catch (Exception ex)
			{ Debug.WriteLine("Failed to connect: " + ex.Message); }
		}
	}


	// This class handles property subscriptions.
	[ComVisible(true)]
	public class SYSSubscriber
	{
		public SpVoice m_speech = null;

		public SYSSubscriber(IRemotePremiseObject Device)
		{
			m_speech = new SpVoice();

			try
			{ Device.SubscribeToProperty("PowerState", "OnPowerState", this); }
			catch (Exception ex)
			{ Debug.WriteLine("Failed to subscribe: " + ex.Message); }
		}

		public virtual void OnPowerState(int subID, IRemotePremiseObject objectChanged, IRemotePremiseObject property, object newValue)
		{
			string Phrase;

			if ((bool)newValue == true)
				Phrase = "On.";
			else
				Phrase = "Off.";
			Debug.WriteLine("Speak: " + Phrase);
			m_speech.Speak(Phrase, SpeechVoiceSpeakFlags.SVSFlagsAsync);
		}
	}
}
 
Back
Top