From your message, it was not clear to me whether you are accessing
the DLL by setting the reference (through TLBIMP path) or using the
C++ type header file for your COM class. If you follow the former way,
there is header file involved in the process. Can you please further
clarify the scenario.
Regards
Rao TRN
Dan - 30 Apr 2004 20:17 GMT
>From your message, it was not clear to me whether you are accessing
>the DLL by setting the reference (through TLBIMP path) or using the
>C++ type header file for your COM class. If you follow the former way,
>there is header file involved in the process. Can you please further
>clarify the scenario.
I can try! :) Really need to get a book on this stuff.
Based on some post on usenet or the web, in the solution explorer, I
went to references, Add Reference and selected the TAPI30.DLL. I'm
guessing that imports it via the first way you mentioned.
I tried to include <tapi3.h> as well, but that did not help. In my
very short code I have (stolen out of MSDN help or off the net)
During compilation, I get
Form1.cpp(17) : error C2065: 'TAPI' : undeclared identifier
Form1.cpp(17) : error C2146: syntax error : missing ';' before
identifier 'tapi'
Form1.cpp(17) : error C2065: 'tapi' : undeclared identifier
Form1.cpp(17) : error C3861: 'TAPI': identifier not found, even with
argument-dependent lookup
Form1.cpp(17) : error C2059: syntax error : ';'
Form1.cpp(18) : error C2228: left of '.Initialize' must have
class/struct/union type
type is ''unknown-type''
Form1.cpp(18) : error C3861: 'tapi': identifier not found, even with
argument-dependent lookup
though intellisense has no problem showing me the members of the TAPI
class, such as the tapi.Initialize() member. I guess I just need to
figure out what the compiler is looking for to know what the class is.
#include "stdafx.h"
#include "Form1.h"
#include <windows.h>
using namespace tapinet;
int APIENTRY _tWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
System::Threading::Thread::CurrentThread->ApartmentState =
System::Threading::ApartmentState::STA;
TAPI tapi=new(TAPI());
tapi.Initialize();
Application::Run(new Form1());
return 0;
}
> Sounds simple enough - problem is there seems to be WAY too much
> documentation and can't seem to find the right one.
I thought twice about replying. But I saw that you said you were desperate.
So, I'm gonna do it but be warned that this drips with personal opinion.
I have to tell you upfront that I am not a big fan of COM. It is not that I
don't appreciate what it does. It came into being, in my view, so that
components could be used by "developers" whose preferred language is not
_the_ one true language. <gd&r> My attitude to those folks is a lot like
Marie Antoinette's was to her subjects - let them use C++.
(Just by the way, .Net addresses COM's issues in a sensible way as all .Net
languages share the same object model so _ultimately_ we won't need a
different and strange component object model).
And just to make it interesting, there are is "raw" COM where everyone and
his brother has to consider what effect his usage of a component has on its
reference count and then the COM often used by C++ developers.
The link below describes the #import compiler directive which causes the C++
compiler to read a COM object's type library and then to create wrapper
classes so you can think in C++ terms rather than in COM terms. Using the
wrappers, for example, insures that when one of these objects is destroyed
its reference count is automatically decremented.
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vccore98/HTML/_
predir_the_.23.import_directive.asp
Not to be outdone, .Net provides Runtime Callable Wrappers (RCW) for the
same purpose - i.e. so that code in member functions of .Net classes can
make use of the services provided by COM objects. About a year or so ago,
Helen Warn, posted in the TAPI group that there was "some" problem with the
RCW's for TAPI3. I don't remember the issue but I do know that she created
her own wrappers for TAPI (v2?) and was encouraged to post them here:
http://www.gotdotnet.com/Community/UserSamples/Details.aspx?SampleGuid=5d893af6-
b340-49d8-9162-b90bf6932414
Finally, the view of lots of types (at least in my experience) who do much
TAPI is that the version 2 procedural API is a better choice than the COM
based stuff of 3.0.
I don't want this to sound negative - C++ rocks, TAPI2 rocks, NET rocks. COM
and TAPI3 well that's something else in my view.
However, if you are looking for a .Net-targeting Managed C++ sample that
uses TAPI3, and if you are willing to use the compiler's COM support then
take a look at the hack below.
It makes a call on a line - here the one with the name "56Kbps Internal
Modem" to the MoviePhone service - here (212) 777-3456.
Note that a proper TAPI application would look for a line which is capable
of carrying a call of the type in question - TAPIMEDIATYPE_AUDIO - and use
it instead of a hard-coded line. You'll need to use the name of whatever
device you have handy.
Regards,
Will
// Don't forget to link against UUID.LIB
#define _WIN32_WINNT 0x0400
#include <windows.h>
#import <tapi3.dll>
using namespace TAPI3Lib;
#using <mscorlib.dll>
using namespace System;
extern "C" const CLSID CLSID_TAPI;
void MakeTapiCall(_bstr_t &, _bstr_t &);
int main()
{
// Look ma, it really is .Net ...
Console::WriteLine(S"Hello World");
// ... but I can have COM too
CoInitializeEx(0, COINIT_MULTITHREADED);
// ... and use COM to call on TAPI 3
MakeTapiCall( _bstr_t("56Kbps Internal Modem"),
_bstr_t("2127773456") );
CoUninitialize();
return 0;
}
// Searches for a line and makes a call to the requested number on it
void MakeTapiCall(_bstr_t &bstrLine, _bstr_t &bstrNumber)
{
ULONG ul;
_bstr_t name;
HRESULT hr;
ITTAPIPtr pTapi;
ITAddressPtr pAddress;
IEnumAddressPtr pEnumAddress;
ITBasicCallControlPtr pCall;
// Initialize TAPI
hr = pTapi.CreateInstance(CLSID_TAPI);
hr = pTapi->Initialize();
// Look for the requested line
pEnumAddress = pTapi->EnumerateAddresses();
while ( pEnumAddress->Next(1, &pAddress, &ul) == S_OK )
{
name = pAddress->AddressName;
if ( name == bstrLine )
break;
pAddress.Release();
pAddress = 0;
}
// If the line was found, then make a call
if ( pAddress )
{
pCall = pAddress->CreateCall(bstrNumber,
LINEADDRESSTYPE_PHONENUMBER,
TAPIMEDIATYPE_AUDIO);
// Make the call
hr = pCall->Connect(true);
// Wait a while - do something sensible here
// while the call is ongoing
Sleep(10000);
// Hangup
hr = pCall->Disconnect(DC_NORMAL);
}
// Shutdown TAPI
hr = pTapi->Shutdown();
pTapi.Release();
}
Dan - 01 May 2004 02:39 GMT
>I don't want this to sound negative - C++ rocks, TAPI2 rocks, NET rocks. COM
>and TAPI3 well that's something else in my view.
I've been using TAPI2 and going to the COM version has been, well as
someone mentioned about MFC once - I feel like I'm fighting against
the compiler. I'm too much of a procedural type person for all these
macros and interfaces, etc... Guess I need to replace my pillow with
a book on COM and let osmosis take over.
>However, if you are looking for a .Net-targeting Managed C++ sample that
>uses TAPI3, and if you are willing to use the compiler's COM support then
>take a look at the hack below.
Thanks. I'll be glad to take a look at it, but I think my officemates
goal of having everything as .Net may not happen in this case - YIKES!
>Note that a proper TAPI application would look for a line which is capable
>of carrying a call of the type in question - TAPIMEDIATYPE_AUDIO - and use
>it instead of a hard-coded line. You'll need to use the name of whatever
>device you have handy.
Yes. Usually wehn just goofing around with the API before I get
going, I hardcode just enough to get it working in and then clean up.
Thanks again. I didn't think the some of the COM stuff would be
needed with .Net, but as you said, there appears to be issues with
TAPI3.
Dan - 01 May 2004 03:04 GMT
>#import <tapi3.dll>
>using namespace TAPI3Lib;
>extern "C" const CLSID CLSID_TAPI;
> hr = pTapi.CreateInstance(CLSID_TAPI);
> hr = pTapi->Initialize();
Those were the critical lines. Based on some other examples, I didn't
think the CreateInstance would still be needed.
I think I'm going to go back to the shallow end for a while.
Something simple like Unix network programming :)
Thanks for the help.
William DePalo [MVP VC++] - 02 May 2004 21:15 GMT
> >#import <tapi3.dll>
> >using namespace TAPI3Lib;
[quoted text clipped - 4 lines]
> Those were the critical lines. Based on some other examples, I didn't
> think the CreateInstance would still be needed.
If your samples didn't show a
#import ...
line then they didn't take advantage of the compiler's ability to read the
metadata and create C++ wrapper classes on the fly. IMO, using the wrappers
is less painful than taking COM straight-up with no chaser. :-)
You might want to take a look at the .tli and .tlh files to see what the
compiler did.
Regards,
Will