How to dynamically linking sapi.dll and making it work? - c++

I would like to dynamically bind into windows sapi.dll
and then call some code needed to 'speak' some text
on windows - some code like this below
(this snippet is copied from gamedev.net article by desmond lang)
I have no experience in using dll's especialy com. Could someone
tell me how this code should look like to work for my? much tnx
// The voice interface pointer
IspVoice* Voice = NULL;
// Initialize COM
CoInitialize ( NULL );
// Create the voice instance
CoCreateInstance ( CLSID_SpVoice, NULL, CLSCTX_ALL, IID_ISpVoice, (void**)&Voice );
// Our text to be spoken
WCHAR* TextBuffer = "Hello World";
// Use our voice interface to speak the contents of the buffer
Voice -> Speak ( TextBuffer, SPF_DEFAULT, NULL );

Related

Marshalling D3D11Device and D3D11DeviceContext objects

This article says:
The ID3D11DeviceContext methods (except for those that exist on
ID3D11DeviceChild) are not free-threaded, that is, they require single
threading. Only one thread may safely be calling any of its methods
(Draw, Copy, Map, etc.) at a time.
I was wondering if I can make COM do the ID3D11DeviceContext synchronization for me.
Let's say I do this (in simplified code):
CoInitializeEx( NULL, COINIT_APARTMENTTHREADED );
// Create D3D11Device
CComPtr<ID3D11DeviceContext> pD3D11DeviceContext;
pD3D11Device->GetImmediateContext(&pD3D11DeviceContext);
Then I either marshall it:
IStream* pStreamD3D11DeviceContext = NULL;
CComPtr<IUnknown> pUnknownD3D11DeviceContext;
pD3D11DeviceContext->QueryInterface(IID_PPV_ARGS(&pUnknownD3D11DeviceContext));
::CoMarshalInterThreadInterfaceInStream( __uuidof(ID3D11DeviceContext), pUnknownD3D11DeviceContext, &pStreamD3D11DeviceContext );
Or us a GIT table:
CComPtr<IGlobalInterfaceTable> pIGlobalInterfaceTable;
::CoCreateInstance( CLSID_StdGlobalInterfaceTable, NULL, CLSCTX_INPROC_SERVER, IID_IGlobalInterfaceTable, (void **)&pIGlobalInterfaceTable );
CComPtr<IUnknown> pUnknownD3D11DeviceContext;
DWORD dwCookieD3D11DeviceContext = 0;
pD3D11DeviceContext->QueryInterface(IID_PPV_ARGS(&pUnknownD3D11DeviceContext));
pIGlobalInterfaceTable->RegisterInterfaceInGlobal( pUnknownD3D11DeviceContext, __uuidof(ID3D11DeviceContext), &dwCookieD3D11DeviceContext );
Unfortunately this doesn't seem to work.
CoMarshalInterThreadInterfaceInStream returns REGDB_E_IIDNOTREG (0x80040155, Interface not registered) and pStreamD3D11DeviceContext remains NULL.
GIT method goes one step further. I get the cookie, but when I try to use it on another MTA thread, the GetInterfaceFromGlobal returns E_INVALIDARG.
CComPtr<ID3D11DeviceContext> pD3D11DeviceContext;
hresult = pIGlobalInterfaceTable->GetInterfaceFromGlobal( dwCookieD3D11DeviceContext, __uuidof(ID3D11DeviceContext), (void**)&pD3D11DeviceContext );
The params for GetInterfaceFromGlobal seem okay, I tested getting the pointer back on the original thread and it worked.
The D3D11Device and D3D11DeviceContext appear to be unmarshallable. Obviously I have neither proxy DLL nor a typelib for the d3d11.
Am I missing anything?
Thank you.

IAxWinHostWindow CreateControl returns E_NOINTERFACE when trying to host WMP within a CAxWindow

I am trying to embed a WMP control inside my Win32 App.
I have followed the example code here: Hosting the Windows Media Player Control in a Windows Application
However when I step through this example, the line:
hr = spHost->CreateControl(CComBSTR(_T("{6BF52A52-394A-11d3-B153-00C04F79FAA6}")), m_wndView, 0);
returns E_NOINTERFACE;
The line:
hr = m_wndView.QueryHost(&spHost);
succeeds and seems to populate spHost correctly.
Here's the code:
CAxWindow m_wndView;
CComPtr<IObjectWithSite> spHostObject;
CComPtr<IAxWinHostWindow> spHost;
CComBSTR classID = __uuidof(WindowsMediaPlayer);
RECT rcClient = { 0, 0, 560, 335 };
m_wndView.Create(m_hWnd, rcClient, NULL, WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN, WS_EX_CLIENTEDGE);
hr = m_wndView.QueryHost(&spHost);
if( SUCCEEDED(hr) )
{
hr = spHost->CreateControl(classID, m_wndView, 0); //E_NOINTERFACE
}
hr = m_wndView.QueryControl(&m_spPlayer); //E_FAIL
Can anybody give me any idea as to why this happens?
Thanks.
Edit: The line that actually fails is this one inside the ActivateAx() function:
hr = m_spOleObject->DoVerb(OLEIVERB_INPLACEACTIVATE, NULL, spClientSite, 0, m_hWnd, &m_rcPos);
If anyone could shed any more light on this that would be great.
Edit2: Just noticed I get this spammed in my output window whilst i'm stepping through this code:
"An outgoing call cannot be made since the application is dispatching an input-synchronous call"
Any Ideas?
The code is right and works as is, and as suggested on MSDN page.
An problem exactly as described might come up if you your UI window is in MTA apartment, that is the thread you are running on was initialized with CoInitializeEx(NULL, COINIT_MULTITHREADED).
What is happening then, your WMP is instantiated on a side STA thread and its interface marshaled into your thread/apartment. Not every required interface can be transferred this way, so once you keep on initializing it some of the mandatory interfaces is missing...
WMP is using "Apartment" threading model, use it on STA threads.

How to share COM objects between 2 processes?

I want Application1.exe to instantiate an instance of its Item class.
I want Application2.exe to call GetPrice() on this object.
I have followed steps 1-7 on the following website:
http://www.codeguru.com/Cpp/COM-Tech/activex/tutorials/article.php/c5567/
This is what I have so far.
Application1's main looks like this:
CoInitialize( NULL );
DWORD dwRegister;
ItemFactory *pFactory = new ItemFactory;
CoRegisterClassObject( CLSID_Item, pFactory, CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE, &dwRegister );
_getch();
return 0;
Application2's main looks like this:
CoInitialize( NULL );
CoGetClassObject( CLSID_Item, CLSCTX_LOCAL_SERVER, NULL, IID_IItem, (LPVOID *)&pFactory );
My Issue (hopefully my only issue) is that I have no idea how to associate my Item class (or its interface, IItem) with CLSID_Item; this is just some random GUID I defined in another file. I've tried
CoRegisterPSClsid( IID_IItem, CLSID_Item );
After this line, I tried
Item *pItem;
CoCreateInstance( CLSID_Item, NULL, CLSCTX_LOCAL_SERVER, IID_IItem, (LPVOID *)&pItem );
I get an E_NOINTERFACE error.
Should I be creating a factory with CoCreateInstance? Ugh, so confused...
In order to use COM across process or thread boundraries, you must tell COM about your interfaces so it can marshal your function arguments/return values between processes. The easiest way to do this is to use an interface predefined in the system, such as IDispatch, but if you want to use your own, you must either register a proxy/stub DLL, or a type library. If you don't do this, then calls to QueryInterface for your custom interface across COM domains will fail with E_NOINTERFACE, as you are seeing.

What is the "Shell Namespace" way to create a new folder?

Obviously this is trivial to do with win32 api - CreateDirectory(). But I'm trying to host an IShellView, and would like to do this the most shell-oriented way. I would have thought that there would be a createobject or createfolder or some such from an IShellFolder. But neither IShellView nor IShellFolder nor even IFolderView seem to have anything quite like this.
Is there a Shell-programming way to create a new folder? Or do I need to create a folder using a pathname, the old-fashioned way?
If I have to do it via CreateDirectory(), then my next question might be: any ideas as to how to get the IShellView / IFolderView to actually see this new object and display it to the user?
Motivation: Creating my own File Dialog replacement and I want to provide the "new folder" toolbar icon functionality of the standard XP-style file dialog.
EDIT: I went ahead and created something that basically works, using CreateDirectory. However, I'm still hoping that there's a better way to do this, but so that you can see how that works, and to offer better ideas as to solve this issue better:
PidlUtils::Pidl pidl(m_folder);
CFilename folderName(GetDisplayNameOf(pidl), "New Folder");
for (int i = 2; folderName.Exists(); ++i)
folderName.SetFullName(FString("New Folder (%d)", i));
if (!CPathname::Create(folderName, false))
throw CContextException("Unable to create a new folder here: ");
// get the PIDL for the newly created folder
PidlUtils::Pidl pidlNew;
#ifdef UNICODE
const wchar_t * wszName = folderName.c_str();
#else
wchar_t wszName[MAX_PATH];
MultiByteToWideChar(CP_ACP, 0, folderName.GetFullName(), -1, wszName, MAX_PATH);
#endif
m_hresult = m_folder->ParseDisplayName(NULL, NULL, wszName, NULL, pidlNew, NULL);
if (FAILED(m_hresult))
throw CLabeledException(FString("Unable to get the PIDL for the new folder: 0x%X", m_hresult));
// upgrade our interface so we can select & rename it
CComQIPtr<IShellView2> sv2(m_shell_view);
if (!sv2)
throw CLabeledException("Unable to obtain the IShellView2 we need to rename the newly created folder.");
// force it to see thew new folder
sv2->Refresh();
// select the new folder, and begin the rename process
m_hresult = sv2->SelectAndPositionItem(pidlNew, SVSI_EDIT|SVSI_DESELECTOTHERS|SVSI_ENSUREVISIBLE|SVSI_POSITIONITEM, NULL);
if (FAILED(m_hresult))
throw CLabeledException(FString("Unable to select and position the new folder item: 0x%X", m_hresult));
Yes, you can get IContextMenu and look for sub menus, but why bother, just call SHChangeNotify after you call CreateDirectory
Shell folders usually implement the IStorage interface, so this is pretty simple. For example, the following creates a folder named "abcd" on the desktop:
CComPtr<IShellFolder> pDesktop;
HRESULT hr = SHGetDesktopFolder(&pDesktop);
if (FAILED(hr)) return;
CComQIPtr<IStorage> pStorage(pDesktop);
if (!pStorage) return;
CComPtr<IStorage> dummy;
hr = pStorage->CreateStorage(L"abcd", STGM_FAILIFTHERE, 0, 0, &dummy);
The Win32 API function CreateDirectory seems to be the "correct way". At least, I can find nothing better - and the answers here don't really shed any light on a better way to do it.

How to prevent crashing if com dll isnt registered

From some old c++ code im trying to use a com dll, it works fine when the dll is registered, but it crahses if the dll isnt registered.
// Initialize COM.
HRESULT hr = CoInitialize(NULL);
IGetTestPtr ptest(__uuidof(tester));
"Use method from the dll"
// Uninitialize COM.
CoUninitialize();
Is it anyway to check if the dll has been registered, before calling IGetTestPtr ptest(__uuidof(tester))?
Or what is the correct way to prevent the crash?
Calling CreateInstance on your object will return an HRESULT that can be tested for success:
IGetTestPtr p = null;
HRESULT hRes = p.CreateInstance( __uuidof(tester) );
bool bSuccess = SUCCEEDED(hRes);
This assumes you've created an interface wrapper around your type library using Visual Studio, where COM Smart Pointers are used in the interface (this gives you the CreateInstance method).
If the DLL is registered, there is a record in HKCR/CLSID/{uuidof(tester)} (curly brackets do matter).
Actually, if it's not, then CoCreateInstance will return an error. Check for this error before using the pointer.
If the COM class is not registered then CoCreateInstance will return REGDB_E_CLASSNOTREG. You should check for general success or failure by using the SUCCEEDED() or FAILED() macros. You also need to create the object properly - see MDSN for an introduction to COM. For example:
HRESULT hr = CoInitialize(NULL);
if (SUCCEEDED(hr))
{
IGetTestPtr ptr = NULL;
hr = CoCreateInstance(CLSID_MyObjectNULL, CLSCTX_INPROC_SERVER, IID_IMyInterface, ptr)
if (SUCCEEDED(hr))
{
Do something with your COM object
...
// Don't forget to release your interface pointer or the object will leak
ptr->Release();
}
hr = CoUninitialize();
}
return hr;
If I recall correctly this "#import" styled COM framework throws exceptions. Add a try-catch block around the code. Google tells me the exceptions are of type _com_error.
Unless you twiddle the params on #import to prevent this, all failing COM calls are going to throw exceptions. You're going to have to turn this off (#import "raw" interfaces I think does it - look at the #import docs) or get real familiar with these exceptions.