error LNK2001: unresolved external symbol _IID_IDirectDraw2 - c++

I work with piece of legacy code which uses direct draw and I'm in rather embarrassing situation.
Not long ago I've updated my system and had to adapt to the new situation (loading ddraw.dll) and everything worked fine.
Today I explored another legacy solution which also uses classes (files) I've changed, but I'm stuck with above mentioned linking error. I've checked and compared project properties and they seam fine.
This is code for directX initialization, "troublesome" code is bold.
typedef int (__stdcall *DirectDrawCreateFunc)(GUID FAR* a ,LPDIRECTDRAW FAR* b, IUnknown FAR* c);
/* init_directx:
* Low-level DirectDraw initialization routine.
*/
int CDCUtils::init_directx(HWND allegro_wnd)
{
LPDIRECTDRAW directdraw1;
HRESULT hr;
LPVOID temp;
HINSTANCE ddraw = LoadLibrary("%WINDIR%\system32\ddraw.dll");
if(ddraw== NULL)
{
return -1;
}
_ddrawLib =ddraw;
DirectDrawCreateFunc ddFunc = (DirectDrawCreateFunc)GetProcAddress(ddraw,"DirectDrawCreate");
if(ddFunc)
{
/* first we have to set up the DirectDraw1 interface... */
hr = ddFunc(NULL, &directdraw1, NULL);
if (FAILED(hr))
return -1;
}
///* first we have to set up the DirectDraw1 interface... */
//hr = DirectDrawCreate(NULL, &directdraw1, NULL);
//if (FAILED(hr))
// return -1;
//...then query the DirectDraw2 interface
//This is the only place where IID_IDirectDraw2 is mentioned in entire solution
hr=directdraw1->QueryInterface(IID_IDirectDraw2, &temp);
if (FAILED(hr))
return -1;
_directdraw = (LPDIRECTDRAW2)temp;
directdraw1->Release();
/* set the default cooperation level */
hr = IDirectDraw2_SetCooperativeLevel(_directdraw, allegro_wnd, DDSCL_NORMAL);
if (FAILED(hr))
return -1;
/* get capabilities */
_ddcaps.dwSize = sizeof(_ddcaps);
hr = IDirectDraw2_GetCaps(_directdraw, &_ddcaps, NULL);
if (FAILED(hr)) {
TRACE("Can't get driver caps\n");
return -1;
}
_dxHwnd=allegro_wnd;
return 0;
}
Any ideas?
Why it works in one solution and not in this one?
Oh linker I loathe thee.

Did you add dxguid.lib to your project's linker inputs?

Make sure you add Dxguid.lib in your project.

Related

LNK2019 with a sample from MSDN

Tried to copy the code from the .cpp file in Common File Dialog Sample got some LNK2019. It seems like a problem with the linking of 3 functions.
Here are the errors:
Severity Code Description Project File Line Suppression State
Error LNK2019 unresolved external symbol __imp_TaskDialog referenced in function "public: class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > __cdecl CDialogEventHandler::ChooseFromFolder(void)" (?ChooseFromFolder#CDialogEventHandler##QEAA?AV?$basic_string#DU?$char_traits#D#std##V?$allocator#D#2##std##XZ) Build-A-Font C:\Users\nadav\Desktop\SFML\Build-A-Font\Build-A-Font\FileDialog.obj 1
Error LNK2019 unresolved external symbol __imp_PSGetPropertyDescriptionListFromString referenced in function "public: virtual long __cdecl CDialogEventHandler::OnTypeChange(struct IFileDialog *)" (?OnTypeChange#CDialogEventHandler##UEAAJPEAUIFileDialog###Z) Build-A-Font C:\Users\nadav\Desktop\SFML\Build-A-Font\Build-A-Font\FileDialog.obj 1
Error LNK2019 unresolved external symbol QISearch referenced in function "public: virtual long __cdecl CDialogEventHandler::QueryInterface(struct _GUID const &,void * *)" (?QueryInterface#CDialogEventHandler##UEAAJAEBU_GUID##PEAPEAX#Z) Build-A-Font C:\Users\nadav\Desktop\SFML\Build-A-Font\Build-A-Font\FileDialog.obj 1
That is their code after some modifications for my needs:
#pragma once
#define STRICT_TYPED_ITEMIDS
#include <shlobj.h>
#include <shlwapi.h>
#include <string>
#include <sstream>
#pragma comment(linker, "\"/manifestdependency:type='Win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
// Added for changing the entry point
#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
const COMDLG_FILTERSPEC c_rgSaveTypes[] =
{
{L"Word Document (*.doc; *.docx)", L"*.doc;*.docx"},
{L"Powerpoint Presentation (*.ppt; *.pptx)", L"*.ppt;*.pptx"},
{L"Web Page (*.htm; *.html)", L"*.htm;*.html"},
{L"Text Document (*.txt)", L"*.txt"},
{L"All Documents (*.*)", L"*.*"}
};
// Indices of file types
#define INDEX_WORDDOC 1
#define INDEX_PRPNTPR 2
#define INDEX_WEBPAGE 3
#define INDEX_TEXTDOC 4
// Controls
#define CONTROL_GROUP 2000
#define CONTROL_RADIOBUTTONLIST 2
#define CONTROL_RADIOBUTTON1 1
#define CONTROL_RADIOBUTTON2 2 // It is OK for this to have the same ID as CONTROL_RADIOBUTTONLIST,
// because it is a child control under CONTROL_RADIOBUTTONLIST
// IDs for the Task Dialog Buttons
#define IDC_BASICFILEOPEN 100
/* File Dialog Event Handler *****************************************************************************************************/
class CDialogEventHandler : public IFileDialogEvents,
public IFileDialogControlEvents
{
public:
// IUnknown methods
IFACEMETHODIMP QueryInterface(REFIID riid, void** ppv)
{
static const QITAB qit[] = {
QITABENT(CDialogEventHandler, IFileDialogEvents),
QITABENT(CDialogEventHandler, IFileDialogControlEvents),
{ 0 },
#pragma warning(suppress:4838)
};
return QISearch(this, qit, riid, ppv);
}
IFACEMETHODIMP_(ULONG) AddRef()
{
return InterlockedIncrement(&_cRef);
}
IFACEMETHODIMP_(ULONG) Release()
{
long cRef = InterlockedDecrement(&_cRef);
if (!cRef)
delete this;
return cRef;
}
// IFileDialogEvents methods
IFACEMETHODIMP OnFileOk(IFileDialog*) { return S_OK; };
IFACEMETHODIMP OnFolderChange(IFileDialog*) { return S_OK; };
IFACEMETHODIMP OnFolderChanging(IFileDialog*, IShellItem*) { return S_OK; };
IFACEMETHODIMP OnHelp(IFileDialog*) { return S_OK; };
IFACEMETHODIMP OnSelectionChange(IFileDialog*) { return S_OK; };
IFACEMETHODIMP OnShareViolation(IFileDialog*, IShellItem*, FDE_SHAREVIOLATION_RESPONSE*) { return S_OK; };
IFACEMETHODIMP OnOverwrite(IFileDialog*, IShellItem*, FDE_OVERWRITE_RESPONSE*) { return S_OK; };
// This method gets called when the file-type is changed (combo-box selection changes).
// For sample sake, let's react to this event by changing the properties show.
IFACEMETHODIMP OnTypeChange(IFileDialog* pfd)
{
IFileSaveDialog* pfsd;
HRESULT hr = pfd->QueryInterface(&pfsd);
if (SUCCEEDED(hr))
{
UINT uIndex;
hr = pfsd->GetFileTypeIndex(&uIndex); // index of current file-type
if (SUCCEEDED(hr))
{
IPropertyDescriptionList* pdl = NULL;
switch (uIndex)
{
case INDEX_WORDDOC:
// When .doc is selected, let's ask for some arbitrary property, say Title.
hr = PSGetPropertyDescriptionListFromString(L"prop:System.Title", IID_PPV_ARGS(&pdl));
if (SUCCEEDED(hr))
{
// FALSE as second param == do not show default properties.
hr = pfsd->SetCollectedProperties(pdl, FALSE);
pdl->Release();
}
break;
case INDEX_WEBPAGE:
// When .html is selected, let's ask for some other arbitrary property, say Keywords.
hr = PSGetPropertyDescriptionListFromString(L"prop:System.Keywords", IID_PPV_ARGS(&pdl));
if (SUCCEEDED(hr))
{
// FALSE as second param == do not show default properties.
hr = pfsd->SetCollectedProperties(pdl, FALSE);
pdl->Release();
}
break;
case INDEX_TEXTDOC:
// When .txt is selected, let's ask for some other arbitrary property, say Author.
hr = PSGetPropertyDescriptionListFromString(L"prop:System.Author", IID_PPV_ARGS(&pdl));
if (SUCCEEDED(hr))
{
// TRUE as second param == show default properties as well, but show Author property first in list.
hr = pfsd->SetCollectedProperties(pdl, TRUE);
pdl->Release();
}
break;
}
}
pfsd->Release();
}
return hr;
};
// IFileDialogControlEvents methods
// This method gets called when an dialog control item selection happens (radio-button selection. etc).
// For sample sake, let's react to this event by changing the dialog title.
IFACEMETHODIMP OnItemSelected(IFileDialogCustomize* pfdc, DWORD dwIDCtl, DWORD dwIDItem)
{
IFileDialog* pfd = NULL;
HRESULT hr = pfdc->QueryInterface(&pfd);
if (SUCCEEDED(hr))
{
if (dwIDCtl == CONTROL_RADIOBUTTONLIST)
{
switch (dwIDItem)
{
case CONTROL_RADIOBUTTON1:
hr = pfd->SetTitle(L"Longhorn Dialog");
break;
case CONTROL_RADIOBUTTON2:
hr = pfd->SetTitle(L"Vista Dialog");
break;
}
}
pfd->Release();
}
return hr;
};
IFACEMETHODIMP OnButtonClicked(IFileDialogCustomize*, DWORD) { return S_OK; };
IFACEMETHODIMP OnCheckButtonToggled(IFileDialogCustomize*, DWORD, BOOL) { return S_OK; };
IFACEMETHODIMP OnControlActivating(IFileDialogCustomize*, DWORD) { return S_OK; };
CDialogEventHandler() : _cRef(1) { };
private:
~CDialogEventHandler() { };
long _cRef;
};
// Instance creation helper
HRESULT CDialogEventHandler_CreateInstance(REFIID riid, void** ppv)
{
*ppv = NULL;
CDialogEventHandler* pDialogEventHandler = new (std::nothrow) CDialogEventHandler();
HRESULT hr = pDialogEventHandler ? S_OK : E_OUTOFMEMORY;
if (SUCCEEDED(hr))
{
hr = pDialogEventHandler->QueryInterface(riid, ppv);
pDialogEventHandler->Release();
}
return hr;
}
// This code snippet demonstrates how to work with the common file dialog interface
std::string BasicFileOpen()
{
// CoCreate the File Open Dialog object.
IFileDialog* pfd = NULL;
HRESULT hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pfd));
if (SUCCEEDED(hr))
{
// Create an event handling object, and hook it up to the dialog.
IFileDialogEvents* pfde = NULL;
hr = CDialogEventHandler_CreateInstance(IID_PPV_ARGS(&pfde));
if (SUCCEEDED(hr))
{
// Hook up the event handler.
DWORD dwCookie;
hr = pfd->Advise(pfde, &dwCookie);
if (SUCCEEDED(hr))
{
// Set the options on the dialog.
DWORD dwFlags;
// Before setting, always get the options first in order not to override existing options.
hr = pfd->GetOptions(&dwFlags);
if (SUCCEEDED(hr))
{
// In this case, get shell items only for file system items.
hr = pfd->SetOptions(dwFlags | FOS_FORCEFILESYSTEM);
if (SUCCEEDED(hr))
{
// Set the file types to display only. Notice that, this is a 1-based array.
hr = pfd->SetFileTypes(ARRAYSIZE(c_rgSaveTypes), c_rgSaveTypes);
if (SUCCEEDED(hr))
{
// Set the selected file type index to Word Docs for this example.
hr = pfd->SetFileTypeIndex(INDEX_WORDDOC);
if (SUCCEEDED(hr))
{
// Set the default extension to be ".doc" file.
hr = pfd->SetDefaultExtension(L"doc");
if (SUCCEEDED(hr))
{
// Show the dialog
hr = pfd->Show(NULL);
if (SUCCEEDED(hr))
{
// Obtain the result, once the user clicks the 'Open' button.
// The result is an IShellItem object.
IShellItem* psiResult;
hr = pfd->GetResult(&psiResult);
if (SUCCEEDED(hr))
{
// We are just going to print out the name of the file for sample sake.
PWSTR pszFilePath = NULL;
hr = psiResult->GetDisplayName(SIGDN_FILESYSPATH, &pszFilePath);
if (SUCCEEDED(hr))
{
TaskDialog(NULL,
NULL,
L"CommonFileDialogApp",
pszFilePath,
NULL,
TDCBF_OK_BUTTON,
TD_INFORMATION_ICON,
NULL);
CoTaskMemFree(pszFilePath);
}
psiResult->Release();
}
}
}
}
}
}
}
// Unhook the event handler.
pfd->Unadvise(dwCookie);
}
pfde->Release();
}
pfd->Release();
}
TCHAR filepath[1024];
if (hr == S_OK)
{
std::stringstream pff;
pff << filepath;
return pff.str();
}
return "";
}
I changed their code in the original files I downloaded from the github and it worked just fine. I tried to copy it to another project and it just won't work (the LNK2019 errors)
I thought I'd provide a sort of meta-answer to this: how did Jerry know what libraries you need to link with? And, as ever, the answer lies in the documentation.
First of all, let's take a look at those linker errors (I've cut them down a bit for clarity, using templates usually tends to lead to verbose / hard to read error messages):
Unresolved symbol __imp_TaskDialog referenced in function <irrelevant>
Unresolved symbol __imp_PSGetPropertyDescriptionListFromString referenced in function <irrelevant>
Unresolved symbol QISearch referenced in function <irrelevant>
First up, you can ignore the __imp_ bit. This just tells you that the function is imported from a DLL. So that leaves us with the following unresolved references:
TaskDialog
PSGetPropertyDescriptionListFromString
QISearch
So, time to go Googling.
The documentation for TaskDialog is here, and if you scroll down to the 'requirements' section at the bottom of the page you will see:
Library Comctl32.lib
So that's nailed that one (note that capitalisation doesn't matter here, I don't know why Microsoft document this stuff in such a weird way).
In a similar vein, we can easily discover that PSGetPropertyDescriptionListFromString is in Propsys.lib, and QISearch is in Shlwapi.lib. End of story.
I hope that shows you how it's done. Every Windows developer needs to understand how to do this and how to find and read the (extensive) documentation that Microsoft provide in general.

CoCreateInstance creates empty IBaseFilter for CLSID_WavDest

I am trying to record .wav by using directshow framework in C++ Visual Studio 2010 project. I am following WAV file section of this guide: https://msdn.microsoft.com/en-us/library/windows/desktop/dd375005(v=vs.85).aspx
I have built WavDest.dll, added it to registry, found it in the registry, it can be added as filter in Graphedit. I had unresolved external symbol error for _CLSID_WavDest but had fixed it by including InitGuid.h in my StdAfx.h and by linking the WavDest.lib.
Now I get no errors, program doesn't crash, but I get 0 byte wav file.
Section of code:
res = AddFilterByCLSID(dshow_dev->m_pGraph, CLSID_WavDest, (IBaseFilter **)&dshow_dev->m_pWaveDest, L"WavDest");
res = AddFilterByCLSID(dshow_dev->m_pGraph, CLSID_FileWriter, (IBaseFilter **)&dshow_dev->m_pWaveWriter, L"File Writer");
res = dshow_dev->m_pWaveWriter->QueryInterface(IID_IFileSinkFilter, (void**)&dshow_dev->m_pFileSink);
res = dshow_dev->m_pFileSink->SetFileName(L"D:\\test.wav", NULL);
res = ConnectFilters(dshow_dev->m_pGraph, dshow_dev->m_pCaptureSourceAudio, dshow_dev->m_pWaveDest);
res = ConnectFilters(dshow_dev->m_pGraph, dshow_dev->m_pWaveDest, dshow_dev->m_pWaveWriter);
AddFilterByCLSID for CLSID_WavDest returns S_OK but dshow_dev->m_pWaveDest has following values: -
[CWavDestFilter] {m_cbWavData=0x00000000 m_cbHeader=0x00000000 } CWavDestFilter
.
Therefore, ConnectFilters for m_pWaveDest returns E_Fail and no audio is recorded.
I have tried this with both Debug and Release versions of WavDest.dll registered (first Debug, then unreg Debug and reg Release).
I have checked everything other in code, graph (dshow_dev->m_pGraph) runs fine for video preview and writing AVI file (with audio).
I am sure that that I did something wrong with WavDest integration but I don't know what.
Any help is appreciated.
It was my mistake after all. I have replaced
assert(pResult != NULL);
in this function
// Match a pin by pin direction and connection state.
HRESULT MatchPin(IPin *pPin, PIN_DIRECTION direction, BOOL bShouldBeConnected, BOOL *pResult)
{
assert(pResult != NULL);
BOOL bMatch = FALSE;
BOOL bIsConnected = FALSE;
HRESULT hr = IsPinConnected(pPin, &bIsConnected);
if (SUCCEEDED(hr))
{
if (bIsConnected == bShouldBeConnected)
{
hr = IsPinDirection(pPin, direction, &bMatch);
}
}
if (SUCCEEDED(hr))
{
*pResult = bMatch;
}
return hr;
}
with
if(pResult == NULL);
{
HRESULT hr = E_FAIL;
return hr;
}

Problems accessing a COM interface in C++

What I want to do is access a COM interface and then call the "Open" method of that interface.
I have a sample code in Visual Basic which works fine, but I need to write it in C++ and I can't seem to get it to work.
First of all, this is the working VB code:
Dim CANapeApplication As CANAPELib.Application
CANapeApplication = CreateObject("CANape.Application")
Call CANapeApplication.Open("C:\Users\Public\Documents\Vector\CANape\12\Project", 0)
CANape.Application is the ProgID which selects the interface I need.
After reading some docs at msdn.microsoft.com and this question, I wrote this code:
void ErrorDescription(HRESULT hr); //Function to output a readable hr error
int InitCOM();
int OpenCANape();
// Declarations of variables used.
HRESULT hresult;
void **canApeAppPtr;
IDispatch *pdisp;
CLSID ClassID;
DISPID FAR dispid;
UINT nArgErr;
OLECHAR FAR* canApeWorkingDirectory = L"C:\\Users\\Public\\Documents\\Vector\\CANape\\12\\Project";
int main(){
// Instantiate CANape COM interface
if (InitCOM() != 0) {
std::cout << "init error";
return 1;
}
// Open CANape
if (OpenCANape() != 0) {
std::cout << "Failed to open CANape Project" << std::endl;
return 1;
}
CoUninitialize();
return 0;
}
void ErrorDescription(HRESULT hr) {
if(FACILITY_WINDOWS == HRESULT_FACILITY(hr))
hr = HRESULT_CODE(hr);
TCHAR* szErrMsg;
if(FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
NULL, hr, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&szErrMsg, 0, NULL) != 0)
{
_tprintf(TEXT("%s"), szErrMsg);
LocalFree(szErrMsg);
} else
_tprintf( TEXT("[Could not find a description for error # %#x.]\n"), hr);
}
int InitCOM() {
// Initialize OLE DLLs.
hresult = OleInitialize(NULL);
if (!SUCCEEDED(hresult)) {
ErrorDescription(hresult);
return 1;
}
// Get CLSID from ProgID
//hresult = CLSIDFromProgID(OLESTR("CANape.Application"), &ClassID);
hresult = CLSIDFromProgID(OLESTR("CanapeCom.CanapeCom"), &ClassID);
if (!SUCCEEDED(hresult)) {
ErrorDescription(hresult);
return 1;
}
// OLE function CoCreateInstance starts application using GUID/CLSID
hresult = CoCreateInstance(ClassID, NULL, CLSCTX_LOCAL_SERVER,
IID_IDispatch, (void **)&pdisp);
if (!SUCCEEDED(hresult)) {
ErrorDescription(hresult);
return 1;
}
// Call QueryInterface to see if object supports IDispatch
hresult = pdisp->QueryInterface(IID_IDispatch, (void **)&pdisp);
if (!SUCCEEDED(hresult)) {
ErrorDescription(hresult);
return 1;
}
std::cout << "success" << std::endl;
return 0;
}
int OpenCANape() {
//Method name
OLECHAR *szMember = L"Open";
// Retrieve the dispatch identifier for the Open method
// Use defaults where possible
DISPID idFileExists;
hresult = pdisp->GetIDsOfNames(
IID_NULL,
&szMember,
1,
LOCALE_SYSTEM_DEFAULT,
&idFileExists);
if (!SUCCEEDED(hresult)) {
std::cout << "GetIDsOfNames: ";
ErrorDescription(hresult);
return 1;
}
unsigned int puArgErr = 0;
VARIANT VarResult;
VariantInit(&VarResult);
DISPPARAMS pParams;
memset(&pParams, 0, sizeof(DISPPARAMS));
pParams.cArgs = 2;
VARIANT Arguments[2];
VariantInit(&Arguments[0]);
pParams.rgvarg = Arguments;
pParams.cNamedArgs = 0;
pParams.rgvarg[0].vt = VT_BSTR;
pParams.rgvarg[0].bstrVal = SysAllocString(canApeWorkingDirectory);
pParams.rgvarg[1].vt = VT_INT;
pParams.rgvarg[1].intVal = 0; // debug mode
// Invoke the method. Use defaults where possible.
hresult = pdisp->Invoke(
dispid,
IID_NULL,
LOCALE_SYSTEM_DEFAULT,
DISPATCH_METHOD,
&pParams,
&VarResult,
NULL,
&puArgErr
);
SysFreeString(pParams.rgvarg[0].bstrVal);
if (!SUCCEEDED(hresult)) {
ErrorDescription(hresult);
return 1;
}
return 0;
}
There are several problems with this.
Using the ClassID received from CLSIDFromProgID as the first parameter of CoCreateInstance does not work, it returns the error: class not registered
If i use the ProgID CanapeCom.CanapeCom (I found it by looking in the Registry), CoCreateInstance works. However, when I use pdisp->GetIDsOfNames I get the error message: Unkown name. Which I think means that the method was not found. That seems logical because I've used a different ProgID, but I just can't figure out how to get to the interface I'm looking for.
I have also tried to use the resulting CLSID from CLSIDFromProgID(OLESTR("CANape.Application"), &ClassID); as the 4th argument of CoCreateInstance but that resulted in a "No such interface supported" error.
Do I need the dll file of the software? In the VB example the dll file is used to get the interface and then create a new object using the ProgID. I'm not sure if I need to do the same in C++ or how this should work.
I'm really stuck here and hope that someone can help me.
Thanks for your comments.
I've fixed the problem, although the solution is kind of embarrassing...
In my defense, I'm still a student and new to this kind of stuff.
I've used the Process Monitor to check what happens when I execute the VB script.
I saw that the CLSID used there is the ID returned by CLSIDFromProgID(OLESTR("CANape.Application"), &ClassID);, which meant that this had to be the right one and the problem had to be somewhere else. I've looked again at the CoCreateInstance and then took a look at the other parameters. Turns out that the context CLSCTX_LOCAL_SERVER was wrong, it has to be CLSCTX_INPROC_SERVER. I don't know why I've set it to local_server in the first place or why I've never questioned it. I wrote that part of the code a few days ago and then focused too much on the CLSID and IID rather than on the other parameters.
I've also taken the first comment from Alex into account and created a tlb file.
This is a simplified version of the code that works:
#import "CANape.tlb"
int _tmain(int argc, _TCHAR* argv[])
{
_bstr_t path = "C:\\Users\\Public\\Documents\\Vector\\CANape\\12\\Project";
CLSID idbpnt;
CoInitialize(NULL);
HRESULT hr = CLSIDFromProgID (L"CANape.Application", &idbpnt);
CANAPELib::IApplication *app;
hr = CoCreateInstance(idbpnt,NULL,CLSCTX_INPROC_SERVER,__uuidof(CANAPELib::IApplication),(LPVOID*)&app );
app->Open(path,0);
CoUninitialize();
return 0;
}

Why COM doesn't work in a new thread?

My problems started after converting my VS2003 project to VS2008. Solution contains 3 projects. Projects are DLL's. There were A LOT of compilation errors, then some linker errors... Well, I fought them off. Now it just simply doesn't work ;)
So, one of this DLL's is suppoused to communicate with Word by COM.
Word::_ApplicationPtr d_pApp;
Word::_DocumentPtr d_pDoc;
void MSWord2003::init()
{
free();
HRESULT hr;
CLSID clsid;
CLSIDFromProgID(L"Word.Application", &clsid);
// Get an interface to the running instance, if any..
IUnknown *pUnk;
hr = GetActiveObject(clsid, NULL, (IUnknown**)&pUnk);
if(hr!=S_OK)
throw MSWord::MSWordException("Nie znaleziono działającej aplikacji MSWord.");
IDispatch* d_pDispApp;
hr = pUnk->QueryInterface(IID_IDispatch, (void**)&d_pDispApp);
if(hr!=S_OK)
throw MSWord::MSWordException("Nie udało się połączyć z aplikacją MSWord.");
pUnk->Release();
pUnk = 0;
d_pApp = d_pDispApp;
d_pDoc = d_pApp->ActiveDocument;
d_pDispApp->AddRef();
d_currIdx = -1;
paragraphsCount = d_pDoc->GetParagraphs()->Count;
footnotesCount = d_pDoc->GetFootnotes()->Count;
endnotesCount = d_pDoc->GetEndnotes()->Count;
}
void MSWord2003::free()
{
if(d_pApp!=0)
{
d_pApp->Release();
d_pApp=0;
}
}
This code works on VS2003 (and different machine, I don't have VS2003 on my computer) while in VS2008 it works only if it is called by main thread.
When called by a new thread (wich is initialized by CoInitialize) d_pApp is not initialized properly - its ptr shows 0.
While debugging I reached code in comip.h:
template<typename _InterfacePtr> HRESULT _QueryInterface(_InterfacePtr p) throw()
{
HRESULT hr;
// Can't QI NULL
//
if (p != NULL) {
// Query for this interface
//
Interface* pInterface;
hr = p->QueryInterface(GetIID(), reinterpret_cast<void**>(&pInterface));
// Save the interface without AddRef()ing.
//
Attach(SUCCEEDED(hr)? pInterface: NULL);
}
else {
operator=(static_cast<Interface*>(NULL));
hr = E_NOINTERFACE;
}
return hr;
}
In a new thread, QueryInterface returns E_NOINTERFACE, although GetIID() returns the same thing for both threads. And that is where I got stuck - I have no idea, what causes this behaviour...
IMO you should initialize COM not with CoInitialize, but with CoInitializeEx, specifying COINIT_MULTITHREADED. Otherwise you'll have separate single-threaded COM apartment for every thread.

How to globally mute and unmute sound in Vista and 7, and to get a mute state?

I'm using the old good Mixer API right now, but it does not work as expected on Windows Vista & 7 in the normal, not in XP compatibility mode. It mutes the sound for the current app only, but I need a global (hardware) mute. How to rearch the goal? Is there any way to code this w/o COM interfaces and strange calls, in pure C/C++?
The audio stack was significantly rewritten for Vista. Per-application volume and mute control was indeed one of the new features. Strange calls will be required to use the IAudioEndpointVolume interface.
I recently dealt with this same issue. We have a Windows application that uses the sound system for alarms. We cannot abide the user muting the sound system inadvertently. Here is how I was able to use the interface suggested above to address this issue:
During initialization I added a function to initialize a member of type IAudioEndpointVolume. It was a bit tricky and the help wasn't as helpful as it could be. Here's how to do it:
/****************************************************************************
** Initialize the Audio Endpoint (Only for post XP systems)
****************************************************************************/
void CMuteWatchdog::InitAudioEndPoint(void)
{
HRESULT hr;
IMMDeviceEnumerator * pDevEnum;
IMMDevice * pDev;
const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator);
const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator);
hr = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL,
CLSCTX_ALL, IID_IMMDeviceEnumerator,
(void**)&pDevEnum);
m_pIaudEndPt = NULL;
if(hr == S_OK)
{
hr = pDevEnum->GetDefaultAudioEndpoint(eRender, eConsole, &pDev);
if(hr == S_OK)
{
DWORD dwClsCtx;
const IID iidAEV = __uuidof(IAudioEndpointVolume);
dwClsCtx = 0;
hr = pDev->Activate(iidAEV, dwClsCtx, NULL, (void**) &m_pIaudEndPt);
if(hr == S_OK)
{
// Everything is groovy.
}
else
{
m_pIaudEndPt = NULL; // Might mean it's running on XP or something. Don't use.
}
pDev->Release();
}
pDevEnum->Release();
}
}
...
About once per second I added a simple call to the following:
////////////////////////////////////////////////////////////////////////
// Watchdog function for mute.
void CMuteWatchdog::GuardMute(void)
{
if(m_pIaudEndPt)
{
BOOL bMute;
HRESULT hr;
bMute = FALSE;
hr = m_pIaudEndPt->GetMute(&bMute);
if(hr == S_OK)
{
if(bMute)
{
m_pIaudEndPt->SetMute(FALSE, NULL);
}
}
}
}
Finally, when the program exits just remember to release the allocated resource.
////////////////////////////////////////////////////////////////////////
// De-initialize the watchdog
void CMuteWatchdog::OnClose(void)
{
if(m_pIaudEndPt)
{
m_pIaudEndPt->Release();
m_pIaudEndPt = NULL;
}
}