I made custom downloader of IE using IDownloader Interface and URLDownloadToFile method.
But URLDownloadToFile return 0x80004004 error code.
I wonder! what's wrong in source code?
reference : http://social.msdn.microsoft.com/Forums/en/iewebdevelopment/thread/3fbcea06-4e69-4504-82e0-f4786368a5e2
STDMETHODIMP CDownloader::Download(IMoniker* pmk,
IBindCtx* pbc,
DWORD dwBindVerb,
LONG grfBINDF,
BINDINFO* pBindInfo,
LPCOLESTR pszHeaders,
LPCOLESTR pszRedir,
UINT uiCP )
{
LPOLESTR sDisplayName;
pmk->GetDisplayName(NULL, NULL, &sDisplayName);
HRESULT hr = URLDownloadToFile(NULL, sDisplayName, L"C:\\downloaded.exe", 0, NULL);
return S_OK;
}
This is an error code for E_ABORT, so it looks like your download operation was aborted
From MSDN:
"The download operation can be canceled by returning E_ABORT from any callback"
but since your callback is NULL, it is probably caused by some other problem.
I solved it.
COM object on Internet explorer must create file in IE template folder. Because IE protected mode.
Related
I'm trying to rewrite some code that calls a local COM Server from C# to C++. The C# code works without issue. The key part is:
Guid lr_FactoryGuid = Guid.Parse("AE7CFA4B-985A-4F76-8CC6-2011649FC8A9");
Guid lr_FactoryClass = Guid.Parse("1CA0D073-4ABB-4D06-B318-BFFDE38E4903");
IntPtr lk_FactoryPtr = new IntPtr();
CoGetClassObject(
ref lr_FactoryClass,
4,
new IntPtr(),
ref lr_FactoryGuid,
out lk_FactoryPtr);
if (lk_FactoryPtr == IntPtr.Zero)
{
MessageBox.Show("lk_FactoryPtr == IntPtr.Zero");
return false;
}
I've tried to rewrite this into C++ and I can't get any further than here, the error is give as "No such interface supported":
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
CLSID clsid;
HRESULT hr = CLSIDFromString(L"{1CA0D073-4ABB-4D06-B318-BFFDE38E4903}", &clsid);
CLSID iid;
hr = CLSIDFromString(L"{AE7CFA4B-985A-4F76-8CC6-2011649FC8A9}", &iid);
void* pIFace;
hr = CoCreateInstance(clsid, NULL, CLSCTX_LOCAL_SERVER, iid, &pIFace);
if (!SUCCEEDED(hr))
{
_com_error err(hr);
LPCTSTR errMsg = err.ErrorMessage();
MessageBox(NULL, errMsg, L"SiteKiosk demo", MB_ICONEXCLAMATION | MB_OK);
}
There is a .tlb file that I used to generate the interop DLL for C# and to import into the C++, however it's currently commented out of the C++ in an attempt to keep the code smaller and I still get this error from CoCreateInstance.
The COM application I'm calling is a 32 bit app, so both my C# and C++ clients applications are also 32 bit. Both of the clients are Windows Console applications.
Is there anything else I need to set/do to get the C++ working?
The suggestion by Hans solved the problem, I used CoGetClassObject and the rest of the code then clicked into place.
char desktopPath[MAX_PATH];
HRESULT r = SHGetFolderPath(NULL, CSIDL_DESKTOP, NULL, 0, desktopPath);
if (r != S_OK) {
throw XArch(new XArchEvalWindows());
}
m_desktopPath = CString(desktopPath);
It is so weird. This piece of code works in VS2010 under both the release and debug modes. After I distribute it and run the application, I would get an error saying "The system cannot find the file specified". The more strange thing is my colleague runs the same application on his machine and it works.
In MSDN it says SHGetFolderPath is deprecated, so I tried to use SHGetKnownFolderPath. It is the same situation.
PWSTR desktopPathW = 0;
HRESULT hr = SHGetKnownFolderPath(FOLDERID_Desktop, 0, NULL, &desktopPathW);
if (!SUCCEEDED(hr)) {
throw XArch(new XArchEvalWindows());
}
CoTaskMemFree(static_cast<void*>(desktopPathW));
Any idea about what is going on? Or how am I suppose to debug this?
Thanks in advance.
Jerry
The comments explicitly state that S_OK is the only success result, and the error codes include S_FALSE, E_FAIL and E_INVALIDARG. You incorrectly assume that these 3 are the only error codes.
[Revised yet again for clarity]
I have a C++ program which interacts with a website. The site is IE-specific, and so is my program.
I'm connecting to the running instance of IE in an ordinary way (out of process -- see code). Once I get the IWebBrowser2, I have no problem getting the IHTMLDocument2 and interacting with the individual IHTMLElement objects, filling in fields and clicking buttons.
But if the web page has javascript that calls window.showModalDialog, I'm stuck: I need to interact with the HTML elements in the popup, just like the other pages; but I can't seem to get its IWebBrowser2.
The popup is always titled "Web Page Dialog", and is a window of type Internet Explorer_TridentDlgFrame containing an Internet Explorer_Server. But I can't get the IWebBrowser2 from the Internet Explorer_Server window the way I can when it's a normal IE instance.
I can get the IHTMLDocument2Ptr, but when I try to get the IWebBrowser2 I get an HRESULT of E_NOINTERFACE.
The code is pretty standard stuff, and works fine if it's a 'normal' IE window
IHTMLDocument2Ptr pDoc;
LRESULT lRes;
/* hWndChild is an instance of class "Internet Explorer_Server" */
UINT nMsg = ::RegisterWindowMessage( "WM_HTML_GETOBJECT" );
::SendMessageTimeout( hWndChild, nMsg, 0L, 0L, SMTO_ABORTIFHUNG, 1000,
(DWORD*)&lRes );
LPFNOBJECTFROMLRESULT pfObjectFromLresult =
(LPFNOBJECTFROMLRESULT)::GetProcAddress( hInst, "ObjectFromLresult" );
if ( pfObjectFromLresult != NULL )
{
HRESULT hr;
hr = (*pfObjectFromLresult)( lRes, IID_IHTMLDocument, 0, (void**)&pDoc );
if ( SUCCEEDED(hr) ) {
IServiceProvider *pService;
hr = pDoc->QueryInterface(IID_IServiceProvider, (void **) &pService);
if ( SUCCEEDED(hr) )
{
hr = pService->QueryService(SID_SWebBrowserApp,
IID_IWebBrowser2, (void **) &pBrowser);
// This is where the problem occurs:
// hr == E_NOINTERFACE
}
}
}
In case it matters, this is Vista and IE8. (I emphasize this because both of these introduced breaking changes in my codebase, which had worked fine with XP/IE7.)
Once again, my goal is to get each IHTMLElement and interact with it. I don't have access to the source code of the application which I'm automating.
I'm considering sending keystrokes blindly to the Internet Explorer_Server window, but would rather not.
Edited to add:
Someone suggested getting the child windows and sending them messages, but I'm pretty sure that doesn't work with Internet Explorer_Server; according to Spy++, there's aren't any child windows. (This is not IE-specific. Java applets don't seem to have child windows, either.)
Update
In the comments, Simon Maurer said the above code worked for him, and just to make sure there were no typos he very generously posted a complete standalone app on pastebin. When I used his code, it failed in the same way in the same place, and I realized he thought I wanted to connect to the underlying page, not the popup. So I've edited the text above to remove that ambiguity.
I don't know why you want to get the IServiceProvider or IWebBrowser2 if you just want the IHTMLElement's. You can get them by calling the IHTMLDocument's get_all() method.
This code snippet shows you how this works:
#include <Windows.h>
#include <mshtml.h>
#include <Exdisp.h>
#include <atlbase.h>
#include <SHLGUID.h>
#include <oleacc.h>
#include <comdef.h>
#include <tchar.h>
HRESULT EnumElements(HINSTANCE hOleAccInst, HWND child)
{
HRESULT hr;
UINT nMsg = ::RegisterWindowMessage(_T("WM_HTML_GETOBJECT"));
LRESULT lRes = 0;
::SendMessageTimeout(child, nMsg, 0L, 0L, SMTO_ABORTIFHUNG, 1000, (PDWORD)&lRes);
LPFNOBJECTFROMLRESULT pfObjectFromLresult = (LPFNOBJECTFROMLRESULT)::GetProcAddress(hOleAccInst, "ObjectFromLresult");
if (pfObjectFromLresult == NULL)
return S_FALSE;
CComPtr<IHTMLDocument2> spDoc;
hr = (*pfObjectFromLresult)(lRes, IID_IHTMLDocument2, 0, (void**)&spDoc);
if (FAILED(hr)) return hr;
CComPtr<IHTMLElementCollection> spElementCollection;
hr = spDoc->get_all(&spElementCollection);
if (FAILED(hr)) return hr;
CComBSTR url;
spDoc->get_URL(&url);
printf("URL: %ws\n", url);
long lElementCount;
hr = spElementCollection->get_length(&lElementCount);
if (FAILED(hr)) return hr;
printf("Number of elements: %d", lElementCount);
VARIANT vIndex; vIndex.vt = VT_I4;
VARIANT vSubIndex; vSubIndex.vt = VT_I4; vSubIndex.lVal = 0;
for (vIndex.lVal = 0; vIndex.lVal < lElementCount; vIndex.lVal++)
{
CComPtr<IDispatch> spDispatchElement;
if (FAILED(spElementCollection->item(vIndex, vSubIndex, &spDispatchElement)))
continue;
CComPtr<IHTMLElement> spElement;
if (FAILED(spDispatchElement->QueryInterface(IID_IHTMLElement, (void**)&spElement)))
continue;
CComBSTR tagName;
if (SUCCEEDED(spElement->get_tagName(&tagName)))
{
printf("%ws\n", tagName);
}
}
return S_OK;
}
int _tmain(int argc, _TCHAR* argv[])
{
::CoInitialize(NULL);
HINSTANCE hInst = ::LoadLibrary(_T("OLEACC.DLL"));
if (hInst != NULL)
{
HRESULT hr = EnumElements(hInst, (HWND)0x000F05E4); // Handle to Internet Explorer_Server determined with Spy++ :)
::FreeLibrary(hInst);
}
::CoUninitialize();
return 0;
}
Above code works on both: a normal window or a modal window, just pass the correct HWND to the SendMessageTimeout function.
WARNING I use a hard-coded HWND value in this example, if you want to test it you should start an IE instance and get the HWND of the Internet Explorer_Server window using Spy++.
I also advise you to use CComPtr to avoid memory leaks.
I am working on a browser automation framework, which automates Internet Explorer, as well as other browsers. I am running into an intermittent problem when attempting to launch IE. The framework launches IE using the IELaunchURL API if it is present, and uses CreateProcess if not. The code to obtain the IWebBrowser2 interface is as follows:
// hwndBrowser is obtained by calling ::EnumWindows() with a function that
// compares the process ID of the window handle to the known process ID of
// the IE instance.
CComPtr<IHTMLDocument2> document;
LRESULT result;
::SendMessageTimeout(hwndBrowser,
WM_HTML_GETOBJECT,
0L,
0L,
SMTO_ABORTIFHUNG,
1000,
(PDWORD_PTR)&result);
// oleacc_instance_handle is obtained from ::LoadLibrary("oleacc.dll")
LPFNOBJECTFROMLRESULT object_pointer = reinterpret_cast<LPFNOBJECTFROMLRESULT>(
::GetProcAddress(oleacc_instance_handle, "ObjectFromLresult"));
if (object_pointer != NULL) {
HRESULT hr = (*object_pointer)(result,
IID_IHTMLDocument2,
0,
reinterpret_cast<void **>(&document));
if (SUCCEEDED(hr)) {
CComPtr<IHTMLWindow2> window;
hr = document->get_parentWindow(&window);
if (SUCCEEDED(hr)) {
// http://support.microsoft.com/kb/257717
CComQIPtr<IServiceProvider> provider(window);
if (provider) {
CComPtr<IServiceProvider> child_provider;
hr = provider->QueryService(SID_STopLevelBrowser,
IID_IServiceProvider,
reinterpret_cast<void **>(&child_provider));
if (SUCCEEDED(hr)) {
IWebBrowser2* browser;
hr = child_provider->QueryService(SID_SWebBrowserApp,
IID_IWebBrowser2,
reinterpret_cast<void **>(&browser));
if (SUCCEEDED(hr)) {
// The IWebBrowser2 pointer is passed back to the caller.
// process_window_info->pBrowser = browser;
}
}
}
}
}
}
Now for the problem: It seems that we can always successfully retrieve the IHTMLDocument2 object. However, when we attempt to call get_parentWindow(), we sometimes receive a "class not registered" result (0x80040154 REGDB_E_CLASSNOTREG), most often when launching a new instance of IE after closing a previous one. We can get the IWebBrowser2 interface by omitting the call to get_parentWindow() and simply calling QueryService directly on the document, but we will receive this error further down the line when we try to manipulate parts of the document. What could cause the get_parentWindow() call to fail?
Note that the entirety of the code can be found in context here.
The problem was in the use of threading in the application. We were not properly waiting for a thread to terminate before launching a new instance of IE.
I am trying to pass a COM object from an ActiveX component to JScript. So far I have tried the following method of doing so:
STDMETHODIMP CHSNetwork::CreateIPPPacket(VARIANT ** ppv)
{
IIPPacket *iipp;
HRESULT hr = CoCreateInstance(CLSID_IPPacket, NULL, CLSCTX_ALL, IID_IIPPacket, (void **)&iipp);
if(SUCCEEDED(hr) && ppv)
{
CComVariant cvar((IUnknown *)iipp);
hr = cvar.Detach(*ppv);
}
return hr;
}
The following JScript causes the error Variable uses an Automation type not supported in JScript:
var hsn = new ActiveXObject("ZIENetwork.HSNetwork");
var ipp = hsn.CreateIPPPacket();
Any help would be greatly appreciated. Thanks.
Ah. I got it. I needed to pass a VARIANT * not a VARIANT **. I guess I still get confused by pointers-to-pointers as it relates to return values with COM.
Thus the correct code is:
STDMETHODIMP CHSNetwork::CreateIPPPacket(VARIANT * ppv)
{
// TODO: Add your implementation code here
IIPPacket *iipp;
HRESULT hr = CoCreateInstance(CLSID_IPPacket, NULL, CLSCTX_ALL, IID_IIPPacket, (void **)&iipp);
if(ppv)
{
CComVariant cvar((IUnknown *)iipp);
hr = cvar.Detach(ppv);
}
return hr;
}
Of course if you only ever pass back an interface you can pass back a IDispatch** or IUnknown**, you don't need the VARIANT, just a thought :)