Using The TAPI 3.0 MSP Base Classes
These steps illustrate what a developer needs to do to implement an MSP using ATL 2.1 and the MSP base classes. See TAPI 3.0 MSP Base Classes for a list of libraries and headers. The following material assumes that the developer has a working understanding of ATL and COM, and has experience implementing COM DLLs using ATL.
Complete sample code for a derived MSP is tentatively planned as part of the final release of the Windows 2000 documentation.
- Create an IDL file for your MSP. This file defines a CLSID for your MSP. You must declare your MSP's "coclass" as implementing the ITMSPAddress interface, and declare this interface as the default interface on your class object. For the definition of ITMSPAddress, you will need to import the file "msp.idl". You will want to include your MSP's "coclass" in a type library for your MSP. If your MSP supports any private (custom) interfaces, define them here and include them in your type library. The following is an example IDL file as described above, without any custom interfaces:
import "msp.idl";
[
uuid(4DDB6D35-3BC1-11d2-86F2-006008B0E5D2),
version(2.0),
helpstring("Wave MSP 2.0 Type Library")
]
library WAVEMSPLib
{
importlib("stdole32.tlb");
importlib("stdole2.tlb");
[
uuid(4DDB6D36-3BC1-11d2-86F2-006008B0E5D2),
helpstring("Wave MSP Class")
]
coclass WaveMSP
{
[default] interface ITMSPAddress;
};
};
- Modify your TSP to advertise the CLSID of your MSP when Tapi3.dll asks for it. To do this, make sure that (1) your TSP can negotiate TAPI_VERSION3_0 or higher in the TSPI function lineNegotiateAPIVersion, (2) your TSP's LINEDEVCAPS structure has the LINEDEVCAPFLAGS_MSP flag set in the dwDevCapFlags member, and (3) your TSP returns your MSP's CLSID in the TSPI function TSPI_lineMSPIdentify. This should be the same CLSID that is specified in your IDL file; for example, the second "uuid" line in the example IDL file in the previous step.
- Link your MSP DLL with Mspbase.lib from the SDK.
- Include Mspbase.h from the SDK for MSP base class definitions.
- Implement your DLL exports (DllMain, etc.). Microsoft Visual C++ will generate these for you. In DllMain, on DLL_PROCESS_ATTACH and DLL_PROCESS_DETACH, respectively, use the MSPLOGREGISTER and MSPLOGDEREGISTER macros to enable up logging features for your DLL. Specify the name of your DLL in the MSPLOGREGISTER call.
- Use the LOG macro, defined in Msplog.h, to output trace messages in the same fashion as the base classes. Define the MSPLOG preprocessor symbol to include logging in your DLL; leave it undefined to build a DLL that does not have logging.
- Derive a class from CMSPAddress which implements addresses for your MSP. Declare a global ATL object map which tells ATL to create an instance of your address class when asked to CoCreate on the CLSID you specified in your IDL file. Also, derive your address class from ATL 2.1's CComCoClass template, and include a DECLARE_REGISTRY_RESOURCEID declaration in your address class. Construct a corresponding resource script and header file, just as you would do for any other ATL COM DLL.
- Implement the required CMSPAddress overrides for your address class. For MSPAddressAddRef and MSPAddressRelease, simply call the provided helper function templates. For GetCallMediaTypes, simply return a DWORD bitmap with all of your MSP's supported TAPIMEDIAMODEs ORed together. For CreateMSPCall and ShutdownMSPCall, you should be able to just return E_NOTIMPL and compile and link your MSP at this point. At this stage you may want to verify that you can register and instantiate your MSP from TAPI 3.0 applications (but not successfully create calls).
- Derive a class from CMSPCallMultiGraph to implement your MSP's call objects. You may want to derive from CMSPCallBase instead of CMSPCallMultiGraph if the filter-graph-per-stream model does not fit your requirements, but this will make your job harder (as of this writing, all MSPs to date have derived their call objects directly from CMSPCallMultiGraph). In your address object, implement CreateMSPCall and ShutdownMSPCall to create and shutdown your specific type of call object using the provided helper function templates. In your call object, override CreateStreamObject to return E_NOTIMPL for now. Override MSPCallAddRef and MSPCallRelease in a manner identical to the corresponding address methods. Again, you should be able to compile and link your MSP; it should now be able to create and shut down calls (but the calls will not do any useful streaming).
- Derive a class from CMSPStream to implement your MSP's stream objects. In your call object, implement CreateStreamObject to create and initialize your stream object (typically by calling ATL 2.1's CreateInstance followed by ATL 2.1's _InternalQueryInterface for ITStream followed by calling Init on your stream object). lf you plan to support a fixed number of streams (this is common for MSPs which do not support modification of stream configurations by other endpoints on the call), override Init, CreateStream, and RemoveStream on your call object. (The call Init creates all of your streams initially, and CreateStream and RemoveStream simply return the appropriate TAPI error codes to prevent the application from creating or removing streams). Otherwise, override the call's Init method to create some initial default configuration of streams using the media types requested for the call. When creating any default stream objects in your call's Init method, simply use the InternalCreateStream helper method.
- Implement your stream object. The only required override is the get_Name method, which simply returns a friendly name for the stream. In addition, you will need to override several other methods. Exactly which methods to override depends on your implementation and when you decide to perform the various tasks involved in constructing and tearing down your filter graph. These tasks include creating the appropriate "transport" filters, codecs, etc., and inserting them and removing them from the filter graphs at the appropriate times. You will also have to use the ITTerminalControl interface on terminal objects to connect the selected terminals to your streams. You may want to override SelectTerminal and UnselectTerminal on your stream object to limit the terminal configurations that your streams will accept; limiting each stream to a single terminal will especially simplify construction of your filter graphs, but will sacrifice application functionality such as video preview. Depending on your implementation, you will place your graph construction, teardown, and terminal connection code in the StartStream, StopStream, PauseStream, Initialize, Shutdown, SelectTerminal, and UnselectTerminal methods, or in your own methods based on private TSP communication. Note that a stream with no terminals selected must still keep track of the desired graph state; a StartStream call followed by a SelectTerminal call on such a stream must result in commencement of streaming. You will have to override most of these methods to ensure that the correct construction, teardown, connection, and disconnection happens in each case depending on the state of the stream.
- Implement your TSP communication. Override CMSPAddress::ReceiveTSPAddressData and/or CMSPCallBase::ReceiveTSPCallData, and/or by calling PostEvent on your address object, or HandleStreamEvent on your call object (from either your call or stream objects).
- Use PostEvent on your address object, or HandleStreamEvent on your call object (from either your call or stream objects) to send call media events to the application via Tapi3.dll. You will typically do this on your stream object, in overridden methods including the ProcessGraphEvent, StopStream, StartStream, PauseStream, SelectTerminal, and UnselectTerminal methods, depending on how you implement your streams.
- Implement any desired private interfaces or substreams on your existing objects (address, call, and stream). Usually there are none. Note that when implementing your private interfaces you will have to specify the LIBID of your type library from your IDL file. This means that application programmers will have to use your MSP's type library when using your custom interfaces. The standard MSP interfaces, implemented in the MSP base classes, use Tapi3.dll's LIBID and are therefore accessible to all TAPI 3 applications.
- If you are implementing MSP-specific static or dynamic terminal objects or replacements for the default static terminals (this is not typical), you can use the provided terminal base classes to do so. You will have to override various methods on your address object to provide alternative or additional methods of creating terminal objects.
- Implement the IObjectSafety interface on your Address, Call, Stream and Terminal objects. If you want to use Dispatch Mapper to query for interfaces on your MSP objects, you will need to mark your objects as safe for scripting on these interfaces. You should do this by implementing the IObjectSafety interface on your object. Deriving from CMSPObjectSafetyImpl (a helper class provided in Msputils.h) and adding IObjectSafety to your class's ATL COM_MAP will make your objects safe for scripting on all the interfaces they expose. Note that using Dispatch Mapper on MSP objects could be implicit. MSP Address and MSP Call are aggregated by TAPI Address and TAPI Call objects. If Dispatch Mapper is used on the TAPI objects to query for the interfaces exposed by the aggregated MSP objects, the aggregated MSP objects will be queried for safety of the requested interfaces.