Interop with ASIO driver COM object

T

Technics

Ok I will be as clearer as I can (sorry for english/technical
mistakes)

I would like to write an audio application that supports ASIO drivers.
I downloaded the ASIO sdk from Stainberg and I read the docs and
viewed the code provided. The sdk retrieve the varius ASIO drivers
available from windows registry. The various drivers are COM objects
wich must be loaded dinamically after retrieving the CLSID from the
registry.
In C++ this is done by the following statement:

rc = CoCreateInstance(lpdrv->clsid,0,CLSCTX_INPROC_SERVER,lpdrv-
clsid,asiodrv);

As you can notice the interface CLSID is the same as object's CLSID.
asiodrv type is IASIO:

interface IASIO : public IUnknown
{

virtual ASIOBool init(void *sysHandle) = 0;
virtual void getDriverName(char *name) = 0;
....
virtual ASIOError future(long selector,void *opt) = 0;
virtual ASIOError outputReady() = 0;
};

In C# i tried to do the following:

Type ASIODriverObjectType = Type.GetTypeFromCLSID(drvlist[dID].clsid,
true);
drvlist[dID].asiodrv =
(IASIO)Activator.CreateInstance(ASIODriverObjectType);

where IASIO is the following:

[InterfaceType(ComInterfaceType.InterfaceIsDual)]
[Guid("a91eaba1-cf4c-11d3-b96a-00a0c9c7b61a")]
public interface IASIO
{
[return: MarshalAs(UnmanagedType.Bool)]
bool init([In,Out] ref IntPtr sysHandle);

void getDriverName([Out, MarshalAs(UnmanagedType.LPStr)] out
string name);
....
}

1. Is there a way to specify the Guid attribute for the interface
during runtime (or not specify it at all)? Infact if I don't specify
the attribute, I get an E_NOINTERFACE error during conversion into
IASIO at the CreateInstence line.

2. When I call init function from the asiodrv object i created I get
an AccessViolationException can anybody tell me why this happens? Is
my way of doing things correct?

Thank you!
 
N

Nicholas Paldino [.NET/C# MVP]

The only way you will be able to use this in .NET would be to define the
interface over and over again, with a new name/namespace and the different
ids.

The reason for this is because the guys who wrote all of this completely
screwed it up when they declared the way to create the instances of this
interface.

COM declares that the GUID for the interface is immutable, and COM
interop relies on this when performing casts to COM interfaces. Because
they decided that you should re-declare the interface and give it a new IID
(which is the same as the CLSID, two horrible decisions on their part), the
COM interop layer is not going to know what to do.

My recommendation is to declare the interface once in unmanaged code,
and then implement it on a stub class. That class would have another
interface which declares a method instance telling it which CLSID it is
proxying. When you create the object, you would get this other interface,
and pass the CLSID to the method to tell it which class to create, then, you
would funnel the calls from your proxy object to the interface declaration
in your file (which will have the same vtable structure so it should work).

Hope this helps.


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Technics said:
Ok I will be as clearer as I can (sorry for english/technical
mistakes)

I would like to write an audio application that supports ASIO drivers.
I downloaded the ASIO sdk from Stainberg and I read the docs and
viewed the code provided. The sdk retrieve the varius ASIO drivers
available from windows registry. The various drivers are COM objects
wich must be loaded dinamically after retrieving the CLSID from the
registry.
In C++ this is done by the following statement:

rc = CoCreateInstance(lpdrv->clsid,0,CLSCTX_INPROC_SERVER,lpdrv-
clsid,asiodrv);

As you can notice the interface CLSID is the same as object's CLSID.
asiodrv type is IASIO:

interface IASIO : public IUnknown
{

virtual ASIOBool init(void *sysHandle) = 0;
virtual void getDriverName(char *name) = 0;
...
virtual ASIOError future(long selector,void *opt) = 0;
virtual ASIOError outputReady() = 0;
};

In C# i tried to do the following:

Type ASIODriverObjectType = Type.GetTypeFromCLSID(drvlist[dID].clsid,
true);
drvlist[dID].asiodrv =
(IASIO)Activator.CreateInstance(ASIODriverObjectType);

where IASIO is the following:

[InterfaceType(ComInterfaceType.InterfaceIsDual)]
[Guid("a91eaba1-cf4c-11d3-b96a-00a0c9c7b61a")]
public interface IASIO
{
[return: MarshalAs(UnmanagedType.Bool)]
bool init([In,Out] ref IntPtr sysHandle);

void getDriverName([Out, MarshalAs(UnmanagedType.LPStr)] out
string name);
...
}

1. Is there a way to specify the Guid attribute for the interface
during runtime (or not specify it at all)? Infact if I don't specify
the attribute, I get an E_NOINTERFACE error during conversion into
IASIO at the CreateInstence line.

2. When I call init function from the asiodrv object i created I get
an AccessViolationException can anybody tell me why this happens? Is
my way of doing things correct?

Thank you!
 
T

Technics

Thank you a lot for help!

However a little code example would clarify.

1. What happens when I cast the object into the interface ? When the
runtime system watches for Guid attribute of the instance to determine
how to query COM for the right interface?

2. I viewed at the Disassembly window to see why I get the exception
and I see that the exception comes when the program get to "call"
instruction. Is maybe a matter of how the interface is declared ? And
is there a way to create the exact interface layout in memory without
using unmanaged code?
 
N

Nicholas Paldino [.NET/C# MVP]

Technics,

This is what you need to do. What you are going to see is pseudo-code
which you will have to replicate in C++.

First, you have to define an interface that will have a constant IID.
This interface will be the IASIO interface. However, you are going to
export it from your COM library:

// Define an IID for this interface which will be exported from your proxy
and be constant.
// This is what is going to be used in .NET.
interface IConstantAsio : public IUnknown
{

virtual ASIOBool init(void *sysHandle) = 0;
virtual void getDriverName(char *name) = 0;
....
};

Define another COM interface which will go on the proxy object as well:

interface IInitializeAsioProxy
{
virtual void Initialize(CLSID ProxyClsid) = 0;
}

Then you define your COM class (I don't recommend using the "C" in the
AsioProxy program id), like so:

class CAsioProxy : IConstantAsio, IInitializeAsioProxy
{
private:
IConstantAsio *m_proxy;

// IInitializeAsioProxy implementation.
HRESULT IInitializeAsioProxy::Initialize(CLSID ProxyClsid)
{
// Call CoCreateInstance here for the proxy.
return CoCreateInstance(ProxyClsid, 0, CLSCTX_INPROC_SERVER,
ProxyClsid, &m_proxy);
}

// IConstantAsio implementation.
void IConstantAsio::getDriverName(char *name)
{
// Call the proxy.
m_proxy->getDriverName(name);
}

}

Then, you use this in C#, and make sure you cast to the
InitializeAsioProxy first to pass the appropriate CLSID.

It occurs to me that this STILL might not work, because the IASIO
interface is not a valid COM interface. ALL methods on a COM interface must
return HRESULT, and it seems that none of them do, so you might have to do
additional work there in defining another PROPER COM interface and using
that internally on the proxy, and creating a different interface that the
proxy exports.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top