I have a task, that includes grabbing some text that lie inside a third party ActiveX component that's embedded inside Internet Explorer webpage using "OBJECT id=S_DetectCom codeBase=xxxx.cab ..."
i have the .cab file having the activeX dlls(4 dlls)
i'm not familiar with OLE and activeX, but doing some research and trials , i could get an IDispatch to ActiveX object by the following steps :-
1- get handle to IE tab having class "Internet Explorer_Server"
2- get IHTMLDocument2 using ObjectFromLresult
3- get IHTMLElementCollection from IHTMLDocument2
4- get IDispatch from IHTMLElementCollection with element of name "S_DetectCom"
what lies in mind, is getting the typelib from DLLs to know the methods,... and their parameters
by try and error find which dll in the cab is embedded into that web page
right now i'm stuck, as i don't know how to reach Invoke to call methods from IDispatch i got
i'd appreciate if somebody could help me with ideas and implementation below is the code part that gets IDispatch from hwnd
int msg;
DWORD lRes = NULL ;
IHTMLDocument2 *pDoc= NULL;
IHTMLElementCollection *pElement = NULL ;
IHTMLInputTextElement *ppvInput ;
IDispatch *ppvDisp;
ITypeInfo *TypeInfo = NULL ;
VARIANT ObjName ;
_variant_t index = NULL;
HRESULT hr;
LRESULT lr ;
UINT cntTypeInf= NULL ;
long pItems = NULL;
BSTR pszOptText[200];
OleInitialize(NULL);
msg = RegisterWindowMessage(L"WM_HTML_GETOBJECT");
lr = SendMessageTimeout(hwnd, msg, 0, 0, SMTO_ABORTIFHUNG, 1000, &lRes);
hr = ObjectFromLresult((LRESULT)lRes, IID_IHTMLDocument2, 0, (void**)&pDoc);
hr = pDoc->get_all( &pElement );
BSTR BStrObjName = _com_util::ConvertStringToBSTR((const char *)"S_DetectCom");
ObjName.vt = VT_BSTR ;
ObjName.bstrVal = BStrObjName ;
hr = pElement->item( ObjName , index , &ppvDisp );
if (hr == S_OK && ppvDisp)
{
hr = ppvDisp->GetTypeInfoCount(&cntTypeInf);
hr = ppvDisp->GetTypeInfo(NULL , NULL , &TypeInfo);
// to do here
}
If you are using WM_HTML_GETOBJECT to get the HTML document you are definitely not an expected caller, since you are out of the current thread. The ActiveX is probably not thread-safe.
Anyway, if you can get a proxy plugin to execute in the IE's Tab thread (probably by installing a BHO), here are the steps to access the ActiveX's properties and methods. It is for accessing an Adobe Flash ActiveX but you can change file name in the import statement to import interfaces from the ocx file.
If I were you instead of trying to use COM directly I'd write a FireBreath plugin, which would then work on firefox, etc as well as IE (it implements an ActiveX Control as well as a NPAPI plugin).
That will do all of the IDispatch stuff for you, and then you can tie into the lower level classes (look at IDispatchAPI) to get the direct COM handle for the element after you grab it and do a queryinterface for the interface you need.
If nothing else you could use the IDispatchAPI class as an example of how to access IDispatch methods.
Related
Recently I've been looking at how the IDispatch works. Below is an example of how an automation client may call a method of an automation server object, which implements IDispatch:
HRESULT hresult;
IDispatch * pdisp = (IDispatch *)NULL;
DISPID dispid;
OLECHAR * szMember = "color";
// Code that sets a pointer to the dispatch (pdisp) is omitted.
hresult = pdisp->GetIDsOfNames(
IID_NULL,
&szMember,
1, LOCALE_SYSTEM_DEFAULT,
&dispid);
pdisp->Invoke(
dispid,
...
)
When implementing a server, this suggests to me that you could implement GetIDOfNames as:
HRESULT GetIDsOfNames(
REFIID riid,
LPOLESTR *rgszNames,
UINT cNames,
LCID lcid,
DISPID *rgDispId
){
rgDispId = 10;
return S_OK;
};
In this way we tell the client, essentially, that all method calls are okay, and all method calls will call method with DISPID==10.
myObject->a();
myObject->ab();
myObject->abc();
myObject->abcd();
//^^ Are all valid and will all call DispID 10.
So my question is, Can we somehow store the name that was called, such that our DispID 10 method, will know which method is being called?
Note: The ultimate goal of this is to build a COM server which has the ability to be a COM server for other COM clients, which might lack the capability/knowledge of being COM servers themselves.
We are hosting a 3rd party web page in a WPF WebBrowser control. The 3rd party web site uses a VBScript GetObject(, "Prog.Id") call to get access to an already instantiated object.
I am aware this is a silly idea so please don't just answer telling me that. We need to integrate with this site and they are not in a position to change their VBScript approach.
Using PInvoke I can call RegisterActiveObject and GetActiveObject from within my WPF app code.
[DllImport("oleaut32.dll")]
public static extern int RegisterActiveObject([MarshalAs(UnmanagedType.IUnknown)] object punk, ref Guid rclsid, uint dwFlags, out int pdwRegister);
[DllImport("oleaut32.dll", CharSet = CharSet.None, ExactSpelling = false)]
public static extern void GetActiveObject(ref Guid rclsid, IntPtr pvReserved, [MarshalAs(UnmanagedType.IUnknown)] out Object ppunk);
If I use GetObject in the WPF code after RegisterActiveObject I get the previously instantiated version but the VBScript running in the WebBrowser control cannot see the running instance
This is the code to Register an instance with the ROT
int regResult = RegisterActiveObject(gatewayObject, ref gatewayClsIdGuid, 0, out this.objectRegisterValue);
The VBScript GetObject call that is not seeing the registered instance is
If (existing = True) Then
MsgBox("Getting not creating")
Set objTest = GetObject(, progId)
Else
Set objTest = CreateObject(progId)
End if
On Error Goto 0
If (objTest Is Nothing) Then
MsgBox("Unable to get object")
Else
MsgBox("Got the object successfully")
End If
Why is the GetObject(, progId) call returning Nothing?
we are using the OLE object to call the internet explorer, how do i open them in maximized state by default.
Below are the code
ioleInternetExplorer = create oleobject
ioleInternetExplorer.connecttonewobject( "InternetExplorer.Application" )
You might try:
ioleInternetExplorer.fullscreen = TRUE
I have not tested this. A resource for IE methods and properties can be found here
Try this.
CONSTANT Integer WM_SYSCOMMAND = 274
CONSTANT UInt SC_MAXIMIZE = 61488
//
Send(ioleInternetExplorer.HWND, WM_SYSCOMMAND, SC_MAXIMIZE, 0)
I am developing a COM surrogate object in C, it will be used by my applications to call the UAC elevation dialog for certain actions that require administrative rights.
The plan is to make this it export a function that takes a pointer to a function with a variable number of arguments and executes it in a different context. This way, an application can use this object to perform some actions with admin rights, all they need to do is use that object and pass it a pointer to the function that has to be executed with said rights.
This works partially, calling CoCreateInstance goes fine, the function pointer is passed and my function is executed.
However, when I create an instance of this object using the COM Elevation Moniker archive, and Microsoft's sample code for CoCreateInstanceAsAdmin, problems occur.
Here is the code:
HRESULT CoCreateInstanceAsAdmin(HWND hwnd, REFCLSID rclsid, REFIID riid, __out void ** ppv)
{
// Manual implementation of CreateInstanceAsAdmin
CComPtr<IBindCtx> BindCtx;
HRESULT hr = CreateBindCtx(0,&BindCtx);
BIND_OPTS3 bo;
memset(&bo, 0, sizeof(bo));
bo.cbStruct = sizeof(bo);
bo.grfMode = STGM_READWRITE;
bo.hwnd = hwnd;
bo.dwClassContext = CLSCTX_LOCAL_SERVER;
hr = BindCtx->SetBindOptions(&bo);
if (SUCCEEDED(hr))
{
// Use the passed in CLSID to help create the COM elevation moniker string
CComPtr<IMoniker> Moniker;
WCHAR wszCLSID[50];
WCHAR wszMonikerName[300];
StringFromGUID2(rclsid,wszCLSID,sizeof(wszCLSID) / sizeof(wszCLSID[0]));
//Elevation:Administrator!new
hr = StringCchPrintfW(wszMonikerName, sizeof(wszMonikerName)/sizeof(wszMonikerName[0]), L"Elevation:Administrator!new:%s", wszCLSID);
if (SUCCEEDED(hr))
{
// Create the COM elevation moniker
ULONG ulEaten = 0;
ULONG ulLen = (ULONG)wcslen(wszMonikerName);
LPBC pBindCtx = BindCtx.p;
hr = MkParseDisplayName(pBindCtx,wszMonikerName,&ulEaten,&Moniker);
if (SUCCEEDED(hr) && ulEaten == ulLen)
{
// Use passed in reference to IID to bind to the object
IDispatch * pv = NULL;
hr = Moniker->BindToObject(pBindCtx,NULL,riid,ppv);
}
}
}
return hr;
}
Calling CoCreateInstanceAsAdmin fails with "Class not registered".
The object is registered by creating the following registry keys (here's the body of the REG file)
[HKEY_CLASSES_ROOT\COMsurrogate]
#="COMsurrogate Class"
[HKEY_CLASSES_ROOT\COMsurrogate\CurVer]
#="COMsurrogate.1"
[HKEY_CLASSES_ROOT\COMsurrogate\CLSID]
#="{686B6F70-06AE-4dfd-8C26-4564684D9F9F}"
[HKEY_CLASSES_ROOT\CLSID\{686B6F70-06AE-4dfd-8C26-4564684D9F9F}]
#="COMsurrogate Class"
"LocalizedString"="#C:\\Windows\\system32\\COMsurrogate.dll,-101"
"DllSurrogate"=""
[HKEY_CLASSES_ROOT\CLSID\{686B6F70-06AE-4dfd-8C26-4564684D9F9F}\ProgID]
#="COMsurrogate.1"
[HKEY_CLASSES_ROOT\CLSID\{686B6F70-06AE-4dfd-8C26-4564684D9F9F}\VersionIndependentProgID]
#="COMsurrogate"
[HKEY_CLASSES_ROOT\CLSID\{686B6F70-06AE-4dfd-8C26-4564684D9F9F}\InprocServer32]
#="#C:\\windows\system32\COMsurrogate.dll"
"ThreadingModel"="Apartment"
[HKEY_CLASSES_ROOT\CLSID\{686B6F70-06AE-4dfd-8C26-4564684D9F9F}\NotInsertable]
[HKEY_CLASSES_ROOT\CLSID\{686B6F70-06AE-4dfd-8C26-4564684D9F9F}\Programmable]
I suppose that some registry entries are missing - that's the conclusion I reach when reading the error message. However, this list of registry keys was compiled after exploring the documentation on MSDN and other sites - so I am pretty certain that nothing was missed.
Among the things I've tried to solve this is to implement it via ATL (such that registration is automated). That works, but the problem is that I can't pass a funtion pointer to the MIDL generated function prototype.
I tried to pass it using the VARIANT type:
v.vt = VT_PTR;
void (*myptr)(void);
myptr = &DoTheStuff;
v.byref = myptr;
hr = theElevated->CoTaskExecuter(0, v);
as result I get "Invalid argument type".
Could someone shed some light on the subject? Perhaps what I am trying to achieve is not possible by design?
I believe the issues you are having is by design and that the intent of window's security improvements were to help avoid potential security risks.
Microsoft doesn't really want you to elevate your privileges if it can stop you from doing so. Executing arbitrary functions as a privileged user shouldn't be easy in any way if Windows is even a decently secured system. You might could try impersonating a different user using tokens and getting better access that way, but even then it would be a stretch. If I remember right, user impersonations won't even guarantee that you'll get full access. The best solution in this case is just to use the super user account and properly request the correct privileges.
I'm trying to print the contents of a WPF WebBrowser control so that no print dialog is shown, but am having no luck.
I have tried the following and am sure it did work:
PrintDialog printDialog = new PrintDialog();
printDialog.PrintDocument(((IDocumentPaginatorSource)browser.Document).DocumentPaginator, "My App");
but for some reason, I'm now getting the following exception:
Unable to cast COM object of type 'mshtml.HTMLDocumentClass' to interface type 'System.Windows.Documents.IDocumentPaginatorSource'. This operation failed because the QueryInterface call on the COM component for the interface with IID '{2C0C27DF-282F-3225-ADCD-CEC68F890EEB}' failed due to the following error: No such interface supported (Exception from HRESULT: 0x80004002 (E_NOINTERFACE)).
The only thing I can think has changed on my PC is that I have installed IE8 since I last tried this, but would that really break it?
// Send the Print command to WebBrowser. For this code to work: add the Microsoft.mshtml .NET reference
mshtml.IHTMLDocument2 doc = WebBrowser1.Document as mshtml.IHTMLDocument2;
doc.execCommand("Print", true, null);
Source:
http://social.msdn.microsoft.com/Forums/en/wpf/thread/63ae5345-b2ca-45a9-9113-0ddc43e9925b
For silent printing without dialogs, use this code:
private void PrintCurrentPage()
{
// document must be loaded for this to work
IOleServiceProvider sp = WebBrowser1.Document as IOleServiceProvider;
if (sp != null)
{
Guid IID_IWebBrowserApp = new Guid("0002DF05-0000-0000-C000-000000000046");
Guid IID_IWebBrowser2 = new Guid("D30C1661-CDAF-11d0-8A3E-00C04FC9E26E");
const int OLECMDID_PRINT = 6;
const int OLECMDEXECOPT_DONTPROMPTUSER = 2;
dynamic wb; // should be of IWebBrowser2 type
sp.QueryService(IID_IWebBrowserApp, IID_IWebBrowser2, out wb);
if (wb != null)
{
// this will send to the default printer
wb.ExecWB(OLECMDID_PRINT, OLECMDEXECOPT_DONTPROMPTUSER, null, null);
}
}
}
[ComImport, Guid("6D5140C1-7436-11CE-8034-00AA006009FA"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
private interface IOleServiceProvider
{
[PreserveSig]
int QueryService([MarshalAs(UnmanagedType.LPStruct)] Guid guidService, [MarshalAs(UnmanagedType.LPStruct)] Guid riid, [MarshalAs(UnmanagedType.IDispatch)] out object ppvObject);
}
WebBrowser silent printing
To avoid taking a dependency on MSHTML you can simply do:
browser.InvokeScript("execScript", new object[] { "window.print();", "JavaScript" });
I devised the above solution when I was having trouble with a WPF project that was migrated to .Net Core 3 and was using Tony's answer that involves MSHTML reference. Referencing MSHTML in .Net Core is not very straightforward (see this github issue)