I've seen a number of BHO samples and I try to implement this common scenario:
STDMETHODIMP CBhoImpl::SetSite(IUnknown* pSite)
{
if (NULL != pSite)
{
CComQIPtr<IWebBrowser2> webBrowser(pSite);
// webBrowser should hold a non-null pointer here
// but it holds a null pointer instead
//whatever
}
// whatever
}
SetSite() is invoked and the if branch is entered but QueryInterface() fails to retrieve IWebBrowser2.
I've seen a number of examples doing exactly the same.
What am I doing wrong?
Try querying the IUnknown parameter for IServiceProvider, and if successful then you can call its QueryService() method to get the IWebBrowser2, eg:
STDMETHODIMP CBhoImpl::SetSite(IUnknown* pSite)
{
if (pSite)
{
CComPtr<IWebBrowser2> webBrowser;
CComQIPtr<IServiceProvider> pServiceProvider(pSite);
if (pServiceProvider)
{
pServiceProvider->QueryService(SID_SWebBrowserApp, IID_PPV_ARGS(&webBrowser));
}
else
{
webBrowser = CComQIPtr<IWebBrowser2>(pSite);
}
if (webBrowser)
{
// use webBrowser as needed...
}
}
}
Related
I have generated a Visual MFC project with a CMultiDocTemplate.
New and File open is already implemented. But I want now to open a database and query the data.
Under CDocument::OnOpenDocument I could read that it would be possible to write an application for that. But I have no idea how to open a document with an attached view without open an file.
What I need is a function OnOpenDB(LPCTSTR tableName) which creates the document and the view but not trying to open a file and extent the table name.
Google did not help me. I could not find any useful documentation.
#Adrian,
is there maybe another solution?
Finally I want to use more than one template for different functions.
In your solution I am missing a possibility to send the table name to the doc for doing the database query and provide the data to the view.
Can I do it in override the template class?
In your code, is dynamic_cast<CMyDoc *> ... ceating the view?
So finally I would need a public function which allows me to send a request for a new document with the table name.
Is this possible?
Update:
I tried to override CMultiDocTemplate::OpenDocumentFile(...), but it does not work. It seem that this function is not virtual.
Therefore I created a OpenDocumentFile(...) inside the override of the CMultiDocTemplate
CDocument* CStreamAuswertungMDT::OpenDocumentFile(LPCTSTR lpszPathName, BOOL bAddToMRU, BOOL bMakeVisible)
{
CDocument* pDoc = CreateNewDocument();
if (pDoc == NULL)
{
TRACE(traceAppMsg, 0, "CDocTemplate::CreateNewDocument returned NULL.\n");
AfxMessageBox(AFX_IDP_FAILED_TO_CREATE_DOC);
return NULL;
}
ASSERT_VALID(pDoc);
BOOL bAutoDelete = pDoc->m_bAutoDelete;
pDoc->m_bAutoDelete = FALSE; // don't destroy if something goes wrong
CFrameWnd* pFrame = CreateNewFrame(pDoc, NULL);
pDoc->m_bAutoDelete = bAutoDelete;
if (pFrame == NULL)
{
AfxMessageBox(AFX_IDP_FAILED_TO_CREATE_DOC);
delete pDoc; // explicit delete on error
return NULL;
}
ASSERT_VALID(pFrame);
// avoid creating temporary compound file when starting up invisible
if (!bMakeVisible)
pDoc->m_bEmbedded = TRUE;
if (!pDoc->OnNewDocument())
{
// user has be alerted to what failed in OnNewDocument
TRACE(traceAppMsg, 0, "CDocument::OnNewDocument returned FALSE.\n");
pFrame->DestroyWindow();
return NULL;
}
// it worked, now bump untitled count
pDoc->SetTitle(lpszPathName);
pDoc->OnDocumentEvent(CDocument::onAfterOpenDocument);
InitialUpdateFrame(pFrame, pDoc);
return pDoc;
}
This is now working for me!
You have to be a bit careful but you can do this by creating a local (static) 'copy' of your CMultiDocTemplate object and then manually calling OpenDocumentFile() on that with a nullptr argument:
static CMultiDocTemplate MY_tmplate(IDR_MYDOC, RUNTIME_CLASS(CMyDoc), RUNTIME_CLASS(CMDIChild), RUNTIME_CLASS(CMyView));
void OnOpenDB(LPCTSTR tableName)
{
CMyDoc* pDoc = dynamic_cast<CMyDoc *>(MY_tmplate.OpenDocumentFile(nullptr));
//... do stuff with pDoc (using `tableName` to probe the database, maybe)
//
}
You will need to be careful when initializing any required data members and any limits, et cetera, in the pDoc object and its associated view(s).
You can set the document's filename/path at any stage, using the SetPathName() base class function; or you can just change the displayed title (that shown in the view's frame windows) with pDoc->SetTitle().
Alternatively, rather than using a local (copy) document template, you could add a member to your application class to run through its list of 'installed' templates, looking for a match to a given string identifier:
CMultiDocTemplate *MyApp::GetDocTemplate(CString name)
{
POSITION dtPos = GetFirstDocTemplatePosition(); CString dtName;
while (dtPos != nullptr) {
CMultiDocTemplate *mdTmp = dynamic_cast<CMultiDocTemplate *>(GetNextDocTemplate(dtPos));
if ((mdTmp == nullptr) || !mdTmp->GetDocString(dtName, CDocTemplate::docName)) break;
if (dtName == name) return mdTmp;
}
return nullptr;
}
Then, if that function returns a non-NULL pointer, use that to call OpenDocumentFile. For a list of the various string components you can use to match the relevant template, see the CDocTemplate documentation.
So I asked this question in a C# context and I have set bounty over there.
I have written an equivalent fragment of C++ code (to be housed within an ATL DLL project) to tap C++ developers experience as well.
IDispatch has a method called GetTypeInfo() with which one can acquire a pointer to ITypeInfo. ITypeInfo itself has a method called GetContainingTypeLib which gets the containing ITypeLib (as it says). This is useful when given a CoClass instance once can get to all the other CoClasses in a given DLL, very useful for reflection.
I had wondered if Excel VBA would co-operate in a similar fashion and return a ITypeLib for the containing VBA project and equally allow me to iterate over all the types found therein. So I wrote some test C# code and that is linked. Here I give equivalent C++ code to tap C++ experience.
The IDL...
[
object,
uuid(ddc4e135-49d6-49f8-ad57-ded4180095fd),
dual,
nonextensible,
pointer_default(unique)
]
interface ICoClassGetContainingTypeLib : IDispatch
{
HRESULT GetContainingTypeLib(IDispatch* vbaClass, IUnknown** iunkTypeLib);
};
[
uuid(bc8410a6-802a-4f91-a73b-c03179bb402b)
]
coclass CoClassGetContainingTypeLib
{
[default] interface ICoClassGetContainingTypeLib;
};
The cpp method implementation
STDMETHODIMP CCoClassGetContainingTypeLib::GetContainingTypeLib(IDispatch* vbaClass, IUnknown** iunkTypeLib)
{
CComPtr<IDispatch> itfDispatch(vbaClass);
CComPtr<ITypeInfo> pITypeInfo;
HRESULT hr = S_OK;
HRESULT hr2 = S_OK;
try
{
hr = itfDispatch->GetTypeInfo(0, 0, &pITypeInfo);
if (SUCCEEDED(hr))
{
UINT* idx(0);
CComQIPtr<ITypeLib> pTLib;
HRESULT hr2 = pITypeInfo->GetContainingTypeLib(&pTLib, idx);
return hr2; // throws exception Exception 0x800A88C1
// which ends u[p at 35009
//TODO If successful copy to ByRef param iunkTypeLib
}
}
catch (std::exception ex)
{
return E_FAIL;
}
return S_OK;
}
And the header declaration
STDMETHOD(GetContainingTypeLib)(IDispatch* vbaClass, IUnknown** iunkTypeLib);
And some client VBA
Sub TestGetContainingTypeLib()
Dim oTR As ToolsReferences '* could be ANY VBA class
Set oTR = New ToolsReferences '* could be ANY VBA class
'* instantiate my ATL project
Dim oTest As GetContainingTypeLibTestLib.CoClassGetContainingTypeLib
Set oTest = New GetContainingTypeLibTestLib.CoClassGetContainingTypeLib
Dim iunkTypeLib As IUnknown
Set iunkTypeLib = Nothing '* not strictly required
Call oTest.GetContainingTypeLib(oTR, iunkTypeLib)
End Sub
There is very little on the Internet to explain the exception number of 0x800A88C1 . I think this the Excel team saying no.
I am developing a BHO for Internet Explorer. In the BHO code, when I enumerate through properties of a JavaScript object, I need to detect whether property value is a JavaScript function.
I enumerate object properties, and get each property value as a VARIANT, like this:
CComQIPtr<IDispatchEx> pObj = /* ... get a reference to JavaScript object */
DISPID dispId = DISPID_STARTENUM;
for (; pObj && NOERROR == pObj->GetNextDispID(fdexEnumDefault, dispId, &dispId);)
{
DISPPARAMS dispParams = { 0 };
CComVariant value; // here I get property value
// invoke property getter
pObj->Invoke(dispId, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET, &dispParams, &value, NULL, NULL);
// determine value kind
switch (value.vt)
{
case VT_DISPATCH:
// both Object and Function get here...
}
}
Both for objects and functions, the VARTYPE vt equals VT_DISPATCH. I'm looking for a way to distinguish between the two.
Just a side note
In JavaScript, I can do:
typeof obj === "function"
or
Object.getPrototypeOf(obj).constructor.name === "function"
(though in IE, constructor.name is undefined).
In my MFC application i have a derived CDHtmlDialog class that opens a login screen and i need to get a url and a cookie from the server after a redirect.
I navigate to the url inside the OnInitDialog and catch the redirect inside OnNavigateComplete:
void CDMYHtmlDlg::OnNavigateComplete(LPDISPATCH pDisp, LPCTSTR szUrl)
{
BSTR *bstr = nullptr;
this->GetDHtmlDocument(&this->m_spHtmlDoc);
if (this->m_spHtmlDoc != nullptr)
this->m_spHtmlDoc->get_cookie(bstr);
}
but the GetDHtmlDocument is returning E_NOINTERFACE, should i implement him? if yes how do i get the IHTMLDocument2?.
So my question is why i can't get the document and is this the right way to get a cookie?
Thank's in advance and sorry for my bad English.
Finally figured it out.
Inside the CDHtmlDialog class the OnNavigateComplete method is responsible for assigning value to m_spHtmlDoc but since i overwritten the method no ones assign value to the document, not even the OnDocumentComplete so the simple solution is
void CDHtmlDlgPersonalizado::OnNavigateComplete(LPDISPATCH pDisp, LPCTSTR szUrl)
{
/*CALL THE PARENT METHOD*/
CDHtmlDialog::OnNavigateComplete(pDisp, szUrl);
/*Now GetDHtmlDocument will get the value from m_spHtmlDoc and assign to spHtmlDoc*/
IHTMLDocument2Ptr spHtmlDoc = nullptr;
this->GetDHtmlDocument(&spHtmlDoc);
if (spHtmlDoc != nullptr)
{
BSTR bstr = ::SysAllocString(L" ");
spHtmlDoc->get_cookie(&bstr);
}
}
I am currently trying to translate a few WIN32API functions to Java using JNI. One of those functions is RegisterClassEx. Usually, I think you would specify a different callback function for every window class you register, but since I'm translating the callback to Java as well this doesn't work.
So the current plan is to append a jobject (defined as _jobject*) to the window class and work with that in the callback. Problem is: you can only change data attached to a window class using a HWND. The MSDN documentation to the best of my knowledge doesn't specify a function that can modify a window class using only a window class ATOM or the name.
Thus my question: Is there a way to change a window class (using something like SetClassLongPtr), without having to use a valid HWND?
Java side (I'll eventually add a public function that does what I actually need done):
public class c_winjni implements i_jnisystem {
public interface i_wnd_proc {
public int wnd_proc(long il_hwnd, int im_message, long im_wparam, long im_lparam);
}
private class c_wndclassex {
public int im_style = 0;
public i_wnd_proc ds_wnd_proc = null;
public int im_cls_extra = 0;
public int im_wnd_extra = 0;
public long il_instance = 0L;
public long il_icon = 0L;
public long il_small_icon = 0L;
public long il_cursor = 0L;
public long il_background = 0L;
public String str_menu_name = null;
public String str_class_name = null;
}
private static native short registerClassEx(c_wndclassex ds_wcx, int[] imr_error);
}
C++ side:
LRESULT CALLBACK default_window_callback_proc(HWND ds_hwnd, UINT im_message, WPARAM im_w_param, LPARAM im_l_param) {
return DefWindowProc(ds_hwnd, im_message, im_w_param, im_l_param);
}
/*
* Class: c_winjni
* Method: registerClassEx
* Signature: (Lc_winjni/c_wndclassex;[I)S
*/
JNIEXPORT_EX jshort JNICALL Java_c_1winjni_registerClassEx
(JNIEnv *ads_env, jclass /*jds_class*/, jobject jds_wcx, jintArray jimr_error)
JNI_CPPEXCEPTION_TRAP_BEGIN {
c_jnienv jds_env(ads_env);
jint *aim_error = NULL;
if (jimr_error && jds_env.get_array_length(jimr_error) > 0) {
aim_error = jds_env.get_array_elements(jimr_error, NULL);
}
WNDCLASSEX ds_wcx;
ds_wcx.cbSize = sizeof(WNDCLASSEX);
ds_wcx.style = jds_env.get_int_field(jds_wcx, "im_style");
// Imagine I'm checking whether field ds_wnd_proc in object jds_wcx is null.
// If it is, use the default callback (as shown below).
// If it isn't, set ds_wcx.lpfnWndProc to some other callback that reads
// custom class data and calls a Java function of the object attached to the window class.
ds_wcx.lpfnWndProc = default_window_callback_proc;
ds_wcx.cbClsExtra = jds_env.get_int_field(jds_wcx, "im_cls_extra") + sizeof(LONG_PTR);
ds_wcx.cbWndExtra = jds_env.get_int_field(jds_wcx, "im_wnd_extra");
ds_wcx.hInstance = (HINSTANCE) jds_env.get_long_field(jds_wcx, "il_instance");
ds_wcx.hIcon = (HICON) jds_env.get_long_field(jds_wcx, "il_icon");
ds_wcx.hIconSm = (HICON) jds_env.get_long_field(jds_wcx, "il_small_icon");
ds_wcx.hCursor = (HCURSOR) jds_env.get_long_field(jds_wcx, "il_cursor");
ds_wcx.hbrBackground = (HBRUSH) jds_env.get_long_field(jds_wcx, "il_background");
ct_jstring<TCHAR, 256> str_menu_name(ads_env, (jstring) jds_env.get_string_field(jds_wcx, "str_menu_name"));
ds_wcx.lpszMenuName = str_menu_name.get_data();
ct_jstring<TCHAR, 256> str_class_name(ads_env, (jstring) jds_env.get_string_field(jds_wcx, "str_class_name"));
ds_wcx.lpszClassName = str_class_name.get_data();
jshort result = RegisterClassEx(&ds_wcx);
if (result == NULL && aim_error) {
*aim_error = GetLastError();
}
// commit changes and invalidate pointer
if (aim_error) {
jds_env.release_array_elements(jimr_error, aim_error, 0);
}
return result;
} JNI_CPPEXCEPTION_TRAP_END2(ads_env, 0)
The simple answer to your question is that it is only possible to modify the extra class memory using SetClassLongPtr. For which you need a valid window handle.
Interesting problem... A WNDCLASSEX is a class, and HWND is a handle to a window. You may think of the first as a OO class, the second as a "pointer" or "reference" an object (instance).
Yet to modify the class, you seem to have to go through an instance of that class... Weird :) I can imagine this is the most common scenario (having the HWND) (BTW, why you don't have a HWND?)
An idea: would it be acceptable to create a window (hiddend) using the ATOM, and use the returned HWND as a "reference" for SetClassLongPtr?
As to get a valid HWND for registered class ATOM, you can use the FindWindow function.
By passing NULL for the lpWindowName parameter you'll get a handle to the first window that matches the class. Of course you'll fail if there is no window instance present. But in this it's probably good to assume that the corresponding window class isn't registered yet.