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).
Related
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...
}
}
}
I'm trying to convert the C++ COM code for TaskScheduler to Rust and am stuck with the VARIANT argument of ITaskService::Connect:
extern crate winapi;
use winapi::{
ctypes::c_void,
shared::{
guiddef::{GUID, REFCLSID, REFIID},
ntdef::{HRESULT, NULL},
rpcdce::{RPC_C_AUTHN_LEVEL_PKT_PRIVACY, RPC_C_IMP_LEVEL_IMPERSONATE},
winerror::FAILED,
wtypes::VARENUM,
wtypesbase::CLSCTX_INPROC_SERVER,
},
um::{
combaseapi::{CoCreateInstance, CoInitializeEx, CoInitializeSecurity, CoUninitialize},
oaidl,
objbase::COINIT_MULTITHREADED,
taskschd::{ITaskService, TaskScheduler},
},
Class, Interface,
}; // 0.3.7
fn main() {
// Create an instance of the Task Service.
let mut p_service: *mut ITaskService = unsafe { std::mem::zeroed() };
let hr = unsafe {
CoCreateInstance(
&TaskScheduler::uuidof(),
std::ptr::null_mut(),
CLSCTX_INPROC_SERVER,
&ITaskService::uuidof(),
&mut p_service as *mut *mut ITaskService as *mut *mut c_void,
)
};
// [...]
// // Connect to the task service.
// let hr = pService->Connect(_variant_t(), _variant_t(),
// _variant_t(), _variant_t());
let hr = unsafe {
(&mut *p_service).Connect(
oaidl::VARIANT { /* VT_EMPTY HERE */ },
oaidl::VARIANT { /* VT_EMPTY HERE */ },
oaidl::VARIANT { /* VT_EMPTY HERE */ },
oaidl::VARIANT { /* VT_EMPTY HERE */ },
)
};
}
The docs say:
_variant_t( ) Constructs an empty _variant_t object, VT_EMPTY.
I found no mention on how to use the variant as stated in the README:
Use std::mem::zeroed() to create an instance of the union, and then assign the value you want using one of the variant methods.
When checking the docs for "variant", there's no _variant_t fn on the list, so not sure what it is called in Rust
Errors
field `0` of struct `winapi::um::oaidl::VARIANT_n1` is private
field `0` is private
for
oaidl::VARIANT {
n1: oaidl::VARIANT_n1 {
0: [0, 0, 0]
}
}
Initializing unions is explained in winapi's README:
How do I create an instance of a union?
Use std::mem::zeroed() to create an instance of the union, and then
assign the value you want using one of the variant methods.
For instance, for VARIANT_n1, the methods are n2, n2_mut, decVal and decVal_mut. You will naturally need to use a _mut method to assign a value.
Note that, starting with winapi 0.3.7, you can also use Default::default() to obtain a zeroed union instead of using the unsafe std::mem::zeroed(). You need to enable the impl-default feature on the winapi crate for the Default trait to be implemented.
You can construct a variant with
use winapi::um::oaidl::VARIANT;
fn doStuff() {
unsafe {
let mut variant: VARIANT = std::mem::zeroed();
// set values here
}
}
variant.n1.n2().vt contains the type (e.g. winapi::shared::wtypes::VT_BOOL or winapi::shared::wtypes::VT_EMPTY, msdn describes the possible values here), the variant.n1.n2().n3 union contains the value.
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.
When I try to process incoming emails (whithin NewMailEx method) I got an exception if I call the GetItemFromId method (an access violation). Doesn't Namespace.GetItemFromId method require storeId as a second parameter? what value should I prvide as a store Id in case I'm not interested in which Inbox the event was raised. In other words I want only to retreive informations about the received email regardless which account (inbox) it belongs to.
CMailItem m_mailItem = NULL;
CApplication l_application;
l_application.CreateDispatch("Outlook.Application");
CNameSpace l_namespace = l_application.GetNamespace(_T("MAPI"));
CString ItemId((pDispParams->rgvarg)->bstrVal);
m_mailItem = l_namespace.GetItemFromID(ItemId, /*whatIdToProvide*/??);
I tried to pass an emty value but this didn't work.
Here is the GetItemFromId definition (automatically generated)
LPDISPATCH GetItemFromID(LPCTSTR EntryIDItem, const VARIANT& EntryIDStore)
{
LPDISPATCH result;
static BYTE parms[] = VTS_BSTR VTS_VARIANT;
InvokeHelper(0x2109, DISPATCH_METHOD, VT_DISPATCH, (void*)&result, parms,EntryIDItem, &EntryIDStore);
return result;
}
For all optional parameters, you need to pass a variant of type VT_ERROR with the value of DISP_E_PARAMNOTFOUND.
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.