Announcement

Collapse
No announcement yet.

How to create a C++ COM object for StreetDeck

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • How to create a C++ COM object for StreetDeck

    The StreetDeck development enviornment will let you load any standard COM object into it using VB Scripts CreateObject function.

    Alternatively, you may prefer to create a COM addin that StreetDeck can use directly and does not require you to use the scripting engine at all. See Building a .Net Addin for StreetDeck for more information.

    This tutorial will show you how to use Microsoft Visual Studio 2003 to create a COM object that can be loaded by the StreetDeck scripting engine.

    Once created and registered, you can simply load the COM object in StreetDeck by calling CreateObject with the registered name of the object you created and immediatly call methods in it.

    For this tutorial, we will be writing the object in C++ using ATL. You can write this object using a variety of different methods, but for the purposes of this tutorial, we will only be using ATL which greatly simplfies reference counting and other mundane tasks required without it.

    Creating a New Project
    1. Create a new ATL Project in Visual Studio
    Click image for larger version

Name:	NewProject.JPG
Views:	1
Size:	40.8 KB
ID:	2298818



    2. Specify the application settings for the project
    Click image for larger version

Name:	ApplicationSettings.JPG
Views:	1
Size:	34.8 KB
ID:	2298819



    3. Switch to the class view, then right click on "SDCOMObject" and select Add->Class, then select ATL COM+ 1.0 Component.
    Click image for larger version

Name:	COM+.JPG
Views:	1
Size:	37.4 KB
ID:	2298820



    4. Call the object "ExampleObject"
    Click image for larger version

Name:	ExampleObject.JPG
Views:	1
Size:	45.0 KB
ID:	2298821



    5. Click finish and the class will be generate for you.


    6. Make sure you can see the class view then, then...
    • Right click on your IExampleObject interface.
    • Choose Add->Add Method...
    • Call the method "MsgBox"
    • Add a BSTR input parameter called "Description"
    • Add a second BSTR input parameter called "Caption"
    • Click Finish to have the wizard generate the function stub for you
    Click image for larger version

Name:	AddMethod.JPG
Views:	1
Size:	36.4 KB
ID:	2298822



    7. Implement the MsgBox method in ExampleObject.cpp by adding the following code which will show a windows message box (BTW, this is a bad example since you should NEVER show a windows dialog in a real project made for use with StreetDeck Its only being used here for ease of understanding)
    Code:
    STDMETHODIMP CExampleObject::MsgBox(BSTR Description, BSTR Caption)
    {
    	MessageBox(NULL, CString(Description), CString(Caption), MB_OK);
    
    	return S_OK;
    }

    8. Notice the use of CString in the previous code, to use this class, we should include #include <atlstr.h> in our stdafx.h file.



    9. Your object is now complete and ready for testing, compile the project.


    10. Before starting up StreetDeck, we should first get the ProgId of our function that will be used in the CreateObject function called from the development enviornment. To find this id, look in the ExampleObject.rgs file for a line called VersionIndependantProgID, it should look something like this:
    Code:
    VersionIndependentProgID = s 'SDCOMObject.ExampleObject'
    SDCOMObject.ExampleObject is the ProgId to use in the call to CreateObject
    StreetDeck.com Developer (I am Chuck)
    Get StreetDeck at http://www.streetdeck.com
    The Official StreetDeck Forums have moved, please visit us at http://www.streetdeck.com/forum for official support for Streetdeck.

  • #2
    Using a COM Object from StreetDeck

    1. Startup the StreetDeck development enviornment by going click the shortcut at "Programs | StreetDeck | Tools | Development Enviornment"

    2. Create a new script project by going to "File | New | Script Project"

    3. Create a new scripted module by going to "File | New | Scripted Module". Call it HelloWorld

    4. Goto the toolbox, then double click on "button" and click ok to add it to the module.

    5. Switch to design mode "Panels | Design Mode"

    6. Double click on the new button, it should add a new OnMouseClick event.

    7. At the top of the script editor, select the "Module" component, then choose the OnFirstEnter event to add the event to the script. Now find the OnDestroy event and choose it to add that event to the script.

    8. At the very top of the script, in the global scope, add the following line:
    Code:
    Dim oCOMObject
    9. In the OnFirstEnter event, add the following code to instantiate the COM object the first time the module is viewed (Notice the ProgId from our C++ com object is used):
    Code:
    Set oCOMObject = CreateObject("SDCOMObject.ExampleObject")

    10. In the OnMouseClick event add the following code to call the 1 method we implemented in our COM object:
    Code:
    oCOMObject.MsgBox "Hello World!", "StreetDeck Rules!"
    11. In the OnDestroy event add the following code:
    Code:
    Set oCOMObject = Nothing
    12. You are done, the module should now work and call the MsgBox method of our COM object when you click the button. To test it, goto "Script Project | Start Project"



    Below is the code in its entirety for the HelloWorld.vbs module:
    Code:
    Dim oCOMObject
    
    '*******************************************************************************
    'Button_OnMouseClick
    'DESC: event OnMouseClick - Called when the left mouse button is pressed and released within the panel
    '*******************************************************************************
    Function Button_OnMouseClick(x, y)
    	oCOMObject.MsgBox "Hello World!", "StreetDeck Rules!"
    End Function
    
    
    '*******************************************************************************
    'Module_OnFirstEnter
    'DESC: Called the first time the module is shown
    '*******************************************************************************
    Function Module_OnFirstEnter()
    	Set oCOMObject = CreateObject("SDCOMObject.ExampleObject")	
    End Function
    
    '*******************************************************************************
    'Module_OnDestroy
    'DESC: event OnDestroy - Called when the panel is destroyed
    '*******************************************************************************
    Function Module_OnDestroy()
    	Set oCOMObject = Nothing
    End Function
    StreetDeck.com Developer (I am Chuck)
    Get StreetDeck at http://www.streetdeck.com
    The Official StreetDeck Forums have moved, please visit us at http://www.streetdeck.com/forum for official support for Streetdeck.

    Comment


    • #3
      Installing your DigitalMod and COM object on another machine

      Currently, there is limited support for automatically registering a COM object on another machine when the DigitalMod is installed. To distribute your DigitalMod with COM object after making it, you should make sure the COM dll you are using is in the path containing your skin ("Script Project | Browse Project Folder"), then goto "Script Project | Package for Distribution" to create an SDZ package that can be imported by other for StreetDeck.

      After that you must give explicit instructions for the user to run "regsvr32 <Name of your COM file>.dll" to register the COM object before importing the SDZ package.

      Automatic registration of COM object is possible in version 1.0.5.5 of StreetDeck and later. To use this feature, you should create a COM sub folder in your DigitalMod's sub directory. Any dll files found in this sub directory will have regsvr32.exe run on it to register it after the sdz package has been extracted to the user's script directory. i.e. Place all your COM objects in "My Documents\StreetDeck\Scripts\<Your DigitalMod GUID>\COM" to have them register automatically with packaging.
      StreetDeck.com Developer (I am Chuck)
      Get StreetDeck at http://www.streetdeck.com
      The Official StreetDeck Forums have moved, please visit us at http://www.streetdeck.com/forum for official support for Streetdeck.

      Comment


      • #4
        Sending Events From your COM Object

        There is currently no direct way to send event calls to VBScript. You can, however, manually trigger events from your COM object by using the GetRef method of VBScript which basically gives you a function pointer, but you have to jump through a bunch of hoops to get it to your COM object and be able to call it.

        If you call GetRef from VBScript, it will try to look up the name of the sub you put in as a parameter and check that it is a valid sub procedure defined in your script it will then create a new IDispatch object which has a default method that points to the sub procedure you passed in. Once you have this object, all you have to do is get it to your COM object and store it so every time you need to fire an event you just have to Invoke the default method of the object.

        To add event calling capability to our previous example
        1. Make sure you can see the class view
        • Right click on your IExampleObject interface
        • Choose Add->Add Method
        • Call the method "SetEventObject"
        • Add a IDispatch* parameter called pDispatch
        • Click Finish to have the wizard generate the function stub for you

        Click image for larger version

Name:	Adding Events.JPG
Views:	1
Size:	36.1 KB
ID:	2257260


        2. Add a private CComPtr<IDispatch> member variable to the CExampleObject. This will store our object interface that we recieve from GetRef in VBScript that points to the sub procedure we want to call when the event fires.
        Code:
        CComPtr<IDispatch> m_pEventObject;

        3. Implement the SetEventMethod to set the event object pointer to reference the passed in IDispatch parameter.
        Code:
        STDMETHODIMP CExampleObject::SetEventMethod(IDispatch* pDispatch)
        {
        	m_pEventObject = pDispatch;
        
        	return S_OK;
        }

        4. To call the event from someplace in the COM object, simply call the Invoke method of the IDispatch event object for the default method. For our example, we will change the message box to a MB_YESNO message box and call our event after we recieve a response from the user. The event will have a single integer argument with the result from the message box.

        Code:
        STDMETHODIMP CExampleObject::MsgBox(BSTR Description, BSTR Caption)
        {
        	int iRetVal = MessageBox(NULL, CString(Description), CString(Caption), MB_YESNO);
        
        	if (m_pEventObject.p != NULL)
        	{
        		VARIANT var;
        		DISPPARAMS dp;
        
        		// Allocate memory for the rgvarg member of the DISPPARAMS struct.
        		dp.rgvarg = new VARIANTARG[1];
        
        		dp.rgvarg[0] = CComVariant((long)iRetVal);
        		// set cArgs to the total number of arguments passed.
        		dp.cArgs = 1;
        		dp.cNamedArgs = 0;
        		HRESULT hr = m_pEventObject->Invoke(0, IID_NULL, LOCALE_USER_DEFAULT,
        					DISPATCH_METHOD,&dp, &var, NULL, NULL);
        		// Delete the memory we allocated above for dp.rgvarg.
        		delete[] dp.rgvarg;
        	}
        
        	return S_OK;
        }

        5. Compile the code and then startup the StreetDeck Development Enviornment and add the following code to the script after the CreateObject function to register the sub as an event with our COM object.
        Code:
        	Set oCOMObject = CreateObject("SDCOMObject.ExampleObject")
        	oCOMObject.SetEventMethod GetRef("TestEvent")

        6. Add the following sub procedure to the script that will be called whenever our event is fired.
        Code:
        Sub TestEvent(iRetVal)
        	Module.MsgBox "Return Value = " & iRetVal, "TestEvent Fired!"
        End Sub
        StreetDeck.com Developer (I am Chuck)
        Get StreetDeck at http://www.streetdeck.com
        The Official StreetDeck Forums have moved, please visit us at http://www.streetdeck.com/forum for official support for Streetdeck.

        Comment


        • #5
          Unable to create object

          Chuck,

          I seem to have difficutly creating an object using the 'CreateObject' function. I was able to create and register my .NET COM component and it was successfully registered (confirmed with regedit) but an error is thrown when trying to instantiate it. I listed the C# and VBScript code for completing these tasks and hopefully you can verify if I have something set wrong.

          C# Code:

          using System;
          using System.Runtime.InteropServices; // necessary library to use COM attributes

          namespace STIC
          {
          [GuidAttribute("EFDD602A-685A-4e60-BEEB-6D380011EA32")]
          /// <summary>
          /// Class used to adjust the height of the vehicle based upon the user selection
          /// </summary>
          public class HeightCommands
          {
          public HeightCommands()
          {
          }

          /// <summary>
          /// Method used to adjust the rear of the vehicle to the pre-set height(s)
          /// </summary>
          [DispId(1)]
          public static void AdjustRear()
          {
          Globals.KillCurrentCommandAndWait();
          Globals.CurrentCommand = new RearSetCommand();
          }

          /// <summary>
          /// Method used to adjust the front of the vehicle to the pre-set height(s)
          /// </summary>
          [DispId(2)]
          public static void AdjustFront()
          {
          Globals.KillCurrentCommandAndWait();
          Globals.CurrentCommand = new FrontSetCommand();
          }

          /// <summary>
          /// Method used to adjust the vehicle to the pre-set heights
          /// </summary>
          [DispId(3)]
          public static void AdjustAll()
          {
          Globals.KillCurrentCommandAndWait();
          Globals.CurrentCommand = new AllSetCommand();
          }

          VBScript Code:

          Dim obj
          '************************************************* ******************************
          'Control_OnMouseClick
          'DESC: event OnMouseClick - Called when the left mouse button is pressed and released within the panel
          '************************************************* ******************************
          Function Control_OnMouseClick(x, y)
          Set obj = CreateObject(STIC.HeightCommands)
          End Function

          Error Thrown:

          Object Required: 'STIC'
          STIC

          Comment


          • #6
            You should put "STIC.HeightCommands" in quotes
            StreetDeck.com Developer (I am Chuck)
            Get StreetDeck at http://www.streetdeck.com
            The Official StreetDeck Forums have moved, please visit us at http://www.streetdeck.com/forum for official support for Streetdeck.

            Comment


            • #7
              Thanks for reviewing the code. It always seems to be the simplest mistakes.
              STIC

              Comment


              • #8
                Subscribing to .NET Events

                ************************************************** *******
                Then you should just have to call it in vbscript with
                Set oCOMObject = CreateObject("SDCOMObject.ExampleObject")
                Set oCOMObject.OnEvent = GetRef("SDCOMObject_OnEvent")
                ************************************************** *******
                Chuck, after reviewing the article you provided in regards to exposing .NET events to COM clients, I've been unsuccessful referencing them. Here are the steps and code I implemented into the .NET event(s) in my component to expose them:

                1. Created an interface that contained the method names and signatures of
                my .NET events.
                2. Used the InterfaceType attribute to mark the interface as an IDispatch
                interface.
                3. Used the ComSourceInterfaces attribute on my .NET object to define the
                interface as the "source" interface for COM events.

                Code in C#:
                namespace STIC
                {
                [Guid("33577CD0-785C-4a35-A969-48C7F69985EF")]
                [InterfaceType(ComInterfaceType.InterfaceIsIDispatc h)]
                public interface IAirRideEvents
                {
                [DispId(1)]
                void PositionChanged();
                }

                [ComSourceInterfaces(typeof(IAirRideEvents))]
                public class AirBag
                {
                public delegate void PositionChangedEventHandler();
                public event PositionChangedEventHandler PositionChanged;
                }
                }

                VBScript Code:

                Set pos = CreateObject("STIC.AirBag")
                Set pos.PositionChanged = GetRef("STIC_PositionChanged")

                The error thrown is, "Object does not support this method or property". Am I calling the GetRef function wrong in the script to subscribe to the event?

                -Brian
                STIC

                Comment


                • #9
                  I can't figure out how to receive com events either..

                  Comment


                  • #10
                    Code to add events to the example have been added above
                    StreetDeck.com Developer (I am Chuck)
                    Get StreetDeck at http://www.streetdeck.com
                    The Official StreetDeck Forums have moved, please visit us at http://www.streetdeck.com/forum for official support for Streetdeck.

                    Comment


                    • #11
                      rats! doesn't this means I have to create my own DLL as a wrapper for itunes? oh well. its probably easier this way anyway in the long run.

                      Thanks for adding the example god_of_cpu.

                      Comment


                      • #12
                        Originally posted by pogitalonx View Post
                        rats! doesn't this means I have to create my own DLL as a wrapper for itunes? oh well. its probably easier this way anyway in the long run.

                        Thanks for adding the example god_of_cpu.
                        Yes, at this point it means you have to do special event handling. There will probably be more standard event handling at some point in time, but this is a quick and dirty way that will let you do it now.

                        We should have built in iTunes support soon though, I think the code was already checked in, but it hasn't been tested yet. In any case, it will be a part of StreetDeck in the near future.
                        StreetDeck.com Developer (I am Chuck)
                        Get StreetDeck at http://www.streetdeck.com
                        The Official StreetDeck Forums have moved, please visit us at http://www.streetdeck.com/forum for official support for Streetdeck.

                        Comment


                        • #13
                          ok thanks for the heads up. i guess i'll leave my digital mod as is for those that want some simple music playing asap. only took an hour or two today anyway.

                          Comment


                          • #14
                            vbscript failure:

                            Set obj2 = CreateObject("test.HeightCommands")

                            This was the error (with the guid preceeding it): -2147024894

                            This is what I got after successfully registering my dll - it ran fine in windows cscript??

                            Function Button_OnMouseDown(x, y)
                            Dim obj2
                            Set obj2 = CreateObject("test.HeightCommands")
                            obj2.Test GetRef("tester")
                            End Function
                            View my current SD Projects:

                            Comment


                            • #15
                              Seeing as how finding a C++ COM tutorial is literally impossible could you, God_Of_Cpu give us/me the code for coding a com object in C++(Ie, DevC++) or a more recent(Like Visual Studio Express) version of the current tutorial?


                              PLEASE!!

                              Comment

                              Working...
                              X