tags:

views:

95

answers:

4

I have created an interface which I am inheriting from another (COM) interface:

public interface IDTSComponentMetaData : IDTSComponentMetaData90 { }

That's all it does.

The reason behind this is, depending on which version of SQL Server I am working with, the base may be IDTSComponentMetaData90 (2005) or IDTSComponentMetaData100 (2008). Rather than conditionally compile every reference to IDTSComponentMetaData90 / IDTSComponentMetaData100 in code, I'd like to use the version-neutral interface which will simply wrap the proper real interface.

The problem is that SSIS passes me an object to the native interface at one key point, and I need to cast it to my wrapper interface:

#if SQL2005
public void Initialize(IDTSComponentMetaData90 c,IServiceProvider s) {
#elif SQL2008
public void Initialize(IDTSComopnentMetaData100 c,IServiceProvider s) {
#endif
  m_ComponentMetaData = (IDTSComponentMetaData) c;
  m_ServiceProvider = s;
}

This compiles with no problem, but at run time, I get an 'Unable to cast COM object of type 'System.__ComObject' to interface type 'MyNameSpace.IDTSComponentMetaData'. This operation failed because the QueryInterface call on the COM component for the interface with IID '{483E01E7-001C-35E4-Ac9f-4B0C1B81E409}' failed due to the following error: No such interface supported (Exception from HRESULT: 0x80004002 (E_NOINTERFACE)).

Is what I am doing totally wrong?

+5  A: 

Any reason you don't simply alias the interface depending on SQL Server version?

#if SQL2005
using IDTSComponentMetaData = IDTSComponentMetaData90;
#elif SQL2008
using IDTSComponentMetaData = IDTSComopnentMetaData100;
#endif
dtb
Because I'm really a C++ programmer and never knew using could be used in that way. I'm going to try it right now, if it works, you're saving me a lot of pain!
Marc Bernier
That will work nicely, except that you can't specify that centrally, you must specify it at the top of each file where you want to use your interface alias.
Lasse V. Karlsen
This looks like it's working! Too bad C# doesn't have the #include directive because Lasse is correct about the repetition, and there's some 14 .cs files with 20 or so substitutions that are needed.But this is still loads better than the alternative, which was to maintain 2 sets of source code, one for 2005 another for 2008.I guess CRTL+C is going to be my new best friend for the next few hours...
Marc Bernier
A: 

COM interface names map to interface IDs (IIDs); this error is telling you that the object your working with doesn't support IDTSComponentMetaData90.

I would say that you're best option here is to explicitly define IDTSComponentMetaData instead of deriving it from IDTSComponentMetaData90. You can then create 2 different classes that implement the interface by forward method calls to the COM interface that is appropriate for the SQL Server version.

Paul Keister
+1  A: 

Unfortunately, what you're trying to do won't work.

The problem is that the underlying object does not in fact implement your interface. It only implements the specific versioned interface, the one you're trying to get away from.

So when you cast your object to that interface, it will fail.

The best you can do in C# is by aliasing the interface, but unfortunately this has to be done in the top of every .cs file that needs to use this interface.

Any chance you could create a wrapper object that implemented your interface instead? Then your own object would implement the new interface, and pass each call back to the relevant method/property on the underlying object. In that object you can do all sorts of #if's and similar to handle the differences, but hiding the differences from the rest of your program.

Or, you could create two classes, both implementing your new interface, and use an IoC container or similar to ask for "that object which implements this interface", without knowing which of the two you're actually talking to.

Lasse V. Karlsen
I was actually going down this route, but @dtb's solution is actually going to be a bit easier to implement, as I'd have to write about 14 wrapper classes.
Marc Bernier
Does his way work? If it does, It'd save me a heap of similar problems, please let us know?
Lasse V. Karlsen
Yes, quite well. I had to add 30 or so 'using' lines to the top of a bunch of .cs files, but I compiled and run both versions without a hitch.
Marc Bernier
A: 

I believe the name of the COM interface doesn't really matter, so creating your own COM interfaces might also work (I can't test it though).

#if SQL2005

[ComImport]
[Guid("XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IDTSComponentMetaData
{
    // all members of IDTSComponentMetaData90
}

#elif SQL2008

[ComImport]
[Guid("YYYYYYYY-YYYY-YYYY-YYYY-YYYYYYYYYYYY")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IDTSComponentMetaData
{
    // all members of IDTSComponentMetaData100
}

#endif
dtb