COMy, IDispatch i nieprawidłowy wskaźnik

0

Po konwersji z VS2003 na VS2008 (pozdrowienia dla stałych czytelników :P)...
Dobra, będę szczera - ten błąd możliwe, że występował również przed konwersją. Nawet teraz, wygląda na to, że występuje tylko na moim komputerze.

Oto problematyczny kod:

	HRESULT hr;

	CLSID clsid;
	CLSIDFromProgID(L"Word.Application", &clsid);  

	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();
	d_pApp = d_pDispApp;
	d_pDoc = d_pApp->ActiveDocument;    //<---- tu wywala z komunikatem: Nieprawidłowy wskaźnik

Jak widać, wszystkie hr po drodze są S_OK. Wskaźnik jest mi zwracany przez QueryInterface, więc nie wiem skąd nagle jest nieprawidłowy...

0

Głupie pytanie, ale leciałaś debuggerem? d_pApp jest jakiego typu?

0

tak może nie koniecznie związane z tym błędem ale jeśli pUnk jest interfejsem COMowym to czy nie powinnaś zamiast Release() zrobić mu pUnk = NULL? Tak tylko pytam bo jakoś c++ do mnie nie przemawia a w Delphi tak by to wyglądało :)

0

d_pApp jest jakiego typu?

To IMO najważniejsze pytanie, bo IDispatch samo w sobie jest bardzo generycznym interfejsem, który pozwala na wywoływanie metod na zasadzie .Invoke("metoda", "parametr"). d_pApp musi być obiektem jakiejś klasy, która wszystkie metody wywołuje w ten sposób i ma konstruktor konwertujący z IDispatch.

0
aurel napisał(a)
d_pDoc = d_pApp->ActiveDocument;

Masz taki operator zdefiniowany? Może zwyczajnie brakuje nawiasów: ActiveDocument() ?

0

@stfu, pod debuggerem to widzę tylko, że na jakąś pamięć to wskazuje, ciężko powiedzieć, czy na odpowiednią ;)

d_pApp jest typu Word::_ApplicationPtr
Zawarte jest w nim property ActiveDocument:

__declspec(property(get=GetActiveDocument))
_DocumentPtr ActiveDocument;

@sapero, "term does not evaluate to a function taking 0 arguments".

0

A para CoInitialize i CoUninitialize jest?

1

Wcześniej by błąd wywaliło, gdyby nie było COM zainicjowane.

Spróbuj jeszcze raz wyeksportować nagłówki cpplusowe z wordowskiego type library. Jeżeli są importowane przez zwykłe #import to skasuj wynikowe pliki tlh i tli.

Ten kod u mnie działa (VS 2010):

#include <Windows.h>

#import "C:\Program Files\Common Files\Microsoft Shared\OFFICE14\MSO.DLL" rename("DocumentProperties", "WordDocumentProperties"), rename("RGB", "WordRGB")
#import "C:\Program Files (x86)\Common Files\Microsoft Shared\VBA\VBA6\VBE6EXT.OLB"
#import "C:\Program Files\Microsoft Office\Office14\MSWORD.OLB" rename("ExitWindows", "WordExitWindows"), rename("FindText", "WordFindText")

int main()
{
	CoInitialize(NULL); 
	
	HRESULT hr;
 
	CLSID clsid;
	CLSIDFromProgID(L"Word.Application", &clsid);  
 
	IUnknown *pUnk;
	hr = GetActiveObject(clsid, NULL, (IUnknown**)&pUnk);
 
	IDispatch* d_pDispApp;
	hr = pUnk->QueryInterface(IID_IDispatch, (void**)&d_pDispApp);
 
	pUnk->Release();

	Word::_ApplicationPtr d_pApp = d_pDispApp;
	Word::_DocumentPtr d_pDoc = d_pApp->ActiveDocument;

	MessageBox(NULL, d_pDoc->Name, L"Nazwa dokumentu", NULL);

	return 0;
}
0

Wcześniej by błąd wywaliło, gdyby nie było COM zainicjowane.

Różnie z tym bywa. Kiedyś pisałem coś z IShellFolder, wszystko niby działało, tylko IQueryInfo::GetInfoTip zwracało jakieś bzdury. Jak się okazało, powodem był brak wywołania CoInitialize.

0

@Rev, wielki dzięki za sprawdzenie - skłoniło mnie to do dalszych sprawdzeń. Okazało się, że za pierwszym razem kod przechodzi przez ten punkt. Za drugim razem natomiast już się wywala.
Przytoczę trochę więcej kodu:

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;
	}
}

Najpierw pomyślałam sobie - a po co to inicjalizować dwa razy? Wykomentowałam drugie wywołanie, ale okazało się to bez sensu - rzecz dzieje się w drugim wątku, stąd drugie wywołanie. Bez niego obiekt jest pusty (ptr=0).

Tak więc wygląda na to, że coś się dzieje pomiędzy jednym a drugim wywołaniem, co uniemożliwia korzystanie z d_pApp. No cóż, to chyba moja zabawa na dziś...

//update w trakcie pisania
Właśnie pod debuggerem widzę, tuż po linijsce d_pApp = d_pDispApp, że:
d_pDisApp = 0x0b2d845c
d_pApp = 0x00000000

...jak? o.O
Nie zauważył, że ma być równy d_pDisApp.....?

0

Z dalszych badań doszłam do tego, że problem powoduje fakt, że:

hr = p->QueryInterface(GetIID(), reinterpret_cast<void**>(&pInterface));

zwraca E_NOINTERFACE przy drugim wywołaniu.

p to _InterfacePtr i dla drugiego wywołania jest różne niż dla pierwszego.
GetIID() zwraca to samo dla obu wywołań.

0

Ok, z tego co wyczytałam w Internecie, prawdopodobne jest, że mój błąd jest spowodowany przez fakt, że wywołuję wątek w STA, podczas gdy wątek główny jest w MTA. Nie jestem do tego 100% przekonana, bo akurat w miejscach gdzie się wywala, wcale nie ma takiej potrzeby, by nowy wątek komunikował się z innymi wątkami tego procesu. Fakt, że ma się komunikować z Wordem...
*
Nie, to powyższe to jakaś papka. Nie rozumiem problemu dobrze :/ To proces, któremu robimy QueryInterface powinien być w tym samym apartamencie (tak po polsku się mówi?) co ten, z którego wywołujemy. Nie powinno to dotyczyć obiektów OLE, bo one mają własny, wbudowany marshalling....
Fakty są takie - wątek główny może komunikować się z Wordem. Nowy wątek, niezależnie od tego, czy użyję CoInitialize, czy CoInitializeEx(NULL, COINIT_MULTITHREADED) - nie znajduje interfejsu.
*

Zamieniłam w funkcji wątku CoInitialize na CoInitializeEx(NULL, COINIT_MULTITHREADED) - ale nie zmieniło to nic.

Porównałam właściwości projektu w wersji nowej (nie działającej) i starej pod VS2003. Jest jedna być może znacząca różnica - w VS2003 projekt ma Runtime Library Multi-threaded Debug DLL, w VS2008 Multi-threaded Debug. Projekt jest dllką. Kiedy mu zmienię pod VS2008 na Multi-threaded Debug DLL przestanie się kompilować, ponieważ jest w solucji jeden projekt, który kompiluje się tylko z ustawieniem Multi-threaded Debug.
Nie potrafię powiedzieć, czy to ma wpływ na obecny problem. Nie miałam jeszcze takich dzikich problemów z wątkami, ale może to dlatego, że nie pracowałam z nimi na cudzym, starym kodzie...

                         * 

STA - All OLE controls by necessity must live in a STA. STA means that your COM-object must be always manipulated on the UI thread and cannot be passed to other threads (much like any UI element in MFC). However, your program can still have many threads.

i jeszcze jeden edit:
Doprowadziłam projekt do kompilacji w Multi-threaded Debug DLL, mimo to nic się nie zmieniło. Pod VS2003 (i innej maszynie) obie wersje działają prawidłowo.

1 użytkowników online, w tym zalogowanych: 0, gości: 1