2

I've written a driver using the Ascom LocalServer template, and it works just fine so far. But I want to add an extended interface for use by a "private" app. I first added support for the IConform interface as a model for how to do this and that was also straightforward. But when I then added my own private interface - IbwExtTelescope - using the same type of "decorations" I saw on the IConform interface, the COM object can't be cast to (IbwExtTelescope). Here's a snippet of the code:

namespace ASCOM.bwAstroPhysics
    {
    [ComVisible(true)]
    [Guid("21497dbb-2674-4287-bff5-6baf439738a0")]
    public interface IbwExtTelescope
        {
        void SendBacklashCommand(int iCmdInx, int iSecs);
        }
    }

So here’s the test code:

        Object oCom;

        Object oType;

        ASCOM.bwAstroPhysics.IbwExtTelescope oExtScope;
        IConform oConform;

        oType = Type.GetTypeFromProgID("ASCOM.bwAstroPhysics.Telescope");
        oCom = Activator.CreateInstance(oType);
        oConform = (IConform)oCom;                           // This works correctly

And one line later, this fails:

        oExtScope = (ASCOM.bwAstroPhysics.IbwExtTelescope) oCom;    // and this breaks

The error returned says: Unable to cast COM object of type 'System.__ComObject' to interface type 'ASCOM.bwAstroPhysics.IbwExtTelescope'. This operation failed because the QueryInterface call on the COM component for the interface with IID '{328956A7-C95F-3EE5-9744-A189AA38DBDD}' failed due to the following error: No such interface supported (Exception from HRESULT: 0x80004002 (E_NOINTERFACE)).

Here's what I have determined with debugging: 1) My private interface is registered - if I use the debugger inside the LocalServer.cs and Class.cs code, I can force a call on MarshalGetComInterface on my interface, and it is found. 2) I can cruise around in the Registry and find the GUID in the error message and things look right, at least superficially.

Some other info that might be relevant: 1) This is being done on my XP SP3 development system, Ascom 5.5 installed and running fine, everything up to date. I am working with Visual C# 2008 using VS Express. 2) I have not created or run an installer for my driver - so I don't know if there's something magic that would happen there.

It seems like IConform got some magical treatment done to it that I can't replicate - but I'm just guessing because I'm not very knowledgeable about COM. But since what I'm trying to do is likely to be a common desire, I thought maybe you would help me out. By the way, the driver templates work really well - I've been able to spend 99% of my time just thinking about the telescope mount and how to control it properly. This is the first time I've had to think much about COM or Windows weirdness. Nice job!

Thanks.

Bruce W.

flag
+1 for lots of detail and well formatted. – Tim Long Jan 26 at 6:36

5 Answers

2

Since I've finally gotten this to work, I thought I'd close out the thread with a recap of how to add a private interface to your telescope driver when you're working within the framework of the 'LocalServer' template.

1) Define your interface in a separate project (DLL) within your VS solution. Set the project properties to make it 'signed' (strongly named) and decorate it with COM attributes like:

[ComVisible(true)]
[Guid("<put a GUID here>")]
public interface IMyPrivateAPI

2) Implement your new interface in your telescope driver - your class definition will look something like this:

public class Telescope : ReferenceCountedObjectBase, ITelescope, IMyPrivateAPI

3) Add references to your interface dll in the telescope project, the "global" project, and any application project that will use the private interface. You can avoid this by putting your dll in the Global Assembly Cache, but I chose not to do this. By definition, my API is private and will be used by only the one application I wrote, so I saw no reason to put it in the GAC.

4) Once everything builds correctly, register your interface by running RegAsm:

RegAsm IMyPrivateAPI.dll /codebase /tlb:MyPrivateAPI.tlb

The /tlb parameter is mandatory even if you have no use for the type library - without this parameter, RegAsm won't register your interface at all.

5) In order to use the new interface, you will typically do an Activator.CreateInstance () on your telescope type, then cast that returned reference to (IMyPrivateAPI).

All of this rigamarole is necessary when your are implementing your driver as an out-of-proc COM service (LocalServer). If you're just doing a simple in-proc DLL style driver, you can use standard dot-net syntax to access your private API - you won't need to register it in the COM world.

link|flag
1

There are a number of things that could be causing this.

First, ensure everything is strong-named (signed with a strong-naming key). Please refer to my blog article 'What's in a Strong Name' for a discussion of why this is advisable.

Next, where is your private interface defined? It must be visible to both assemblies at runtime and also to COM. The best way to guarantee this in a way that enables COM to find it too, is to spin it out into a seperate assembly (project) and then either - install that assembly in the Global Assembly Cache, or - register your interface assembly using RegAsm with the /codebase option

The IConform interface is installed in the GAC, which might explain why that interface works and yours doesn't.

A useful tool for troubleshooting this sort of thing is FusLogVw.exe (Fusion Log Viewer, part of the .NET Framework SDK). FusLogVw lets you see detailed trace output of where Fusion is searching for your assemblies.

link|flag
Thanks, Tim, this is a big help. I think you are right that isolating the interface in its own assembly is the key to making headway. I hope to get back to this in the next week, and I'll update this thread with the outcome. – Bruce Waddington Jan 29 at 4:54
Just to close this out, Tim - it turns out that RegAsm won't register the interface unless I also specify the /tlb option. This drove me nuts for the better part of a week :-). Anyway, thanks for the help. – Bruce Waddington Feb 10 at 16:11
1

The IConform interface doesn't have any special treatment beyond what is visible inthe source tree. It is however installed by the 5.5 updater which does a couple of things:

  • Copies the IConform assembly to a publicly visible location in the Common Files area.
  • Loads the assembly into the Global Assembly Cache
  • Registers the assembly with RegAsm
  • Registers a type library with RegAsm /TLB

As Tim suggested IConform is in an assembly of its own and is signed to create a strong name, whch is mandatory to get the assembly into the GAC.

link|flag
Thanks, Peter, this seems very clear. I hope to get back to this in the next week or so, and I'll update the message thread with the outcome. – Bruce Waddington Jan 29 at 4:55
0

Are you aware that there is an extra space in the line that fails? Right before "oCom;"

You did not previously declare (or define, whichever is correct) oExtScope as you did with oConform.

You entered the whole row in the second one, whereas the first only had the interface name.

link|flag
Thanks Arlen, I know you're trying to help - but the problem has nothing to do with C# syntax. – Bruce Waddington Jan 24 at 4:34
0

Have you added the extension interface to the Telescope class? Like this:

public class Telescope : ITelescope, IConform, IExtension

I've just tried it and it works but removing the IExtension causes a compile error as you describe.

link|flag
Yes, the Telescope class declaration looks like this: public class Telescope : ReferenceCountedObjectBase, ITelescope, IConform, IbwExtTelescope This is not a compiler error message I'm getting - it is a runtime issue. Also, it works fine with a DLL implementation - the problem occurs with the out-of-proc COM service implementation. The runtime cast to my extended interface forces the COM machinery to do a 'QueryInterfaces' call, and that is failing. Thanks. – Bruce Waddington Jan 24 at 17:08

Your Answer

Not the answer you're looking for? Browse other questions tagged or ask your own question.