Click here to Skip to main content
Click here to Skip to main content

Automated IE SaveAs MHTML

By , 4 Sep 2002
 

Introduction

The purpose of this article is to show how to automate the fully fledged Save As HTML feature from Internet Explorer, which is normally hidden to those using the Internet Explorer API. Saving the current document as MHTML format is just one of the options available, including:

  • Save As MHTML (whole web page, images, ... in a single file)
  • Save As Full HTML (additional folder for images, ...)
  • Save HTML code only
  • Save As Text

Saving Silently as HTML Using the Internet Explorer API

In fact, the ability to save the current web page for storage without showing a single dialog box is already available to everyone under C++, using the following code, with an important restriction:

LPDISPATCH lpDispatch = NULL;
IPersistFile *lpPersistFile = NULL;

// m_ctrl is an instance of the Web Browser control
lpDispatch = m_ctrl.get_Document();
lpDispatch->QueryInterface(IID_IPersistFile, (void**)&lpPersistFile);

lpPersistFile->Save(L"c:\\htmlpage.html",0);
lpPersistFile->Release();
lpDispatch->Release();

(caption for code above) Saving HTML code only, without dialog boxes

The restriction is that we are talking about the HTML code only, not the web page. Of course, what is interesting is to gain access to full HTML archives with images and so on.

Because there is no "public" or known way to ask for this feature without showing one or more dialog boxes from Internet Explorer, what we are going to do is hook the operating system to listen all window creations, including the dialog boxes. Then we'll ask Internet Explorer for the feature and override the file path from the dialog boxes without being seen. Finally, we'll mimic the user clicking on the Save button to validate the dialog box and unhook ourselves. That's done!

Hooking Internet Explorer to Save As HTML without popping the dialog boxes

This was the short workflow, but there are a few tricks to get along and this article is a unique opportunity to go into detail. By the way, the code is rooted by an article from MS about how to customize Internet Explorer Printing by hooking the Print dialog boxes; see here or here. In our app, we have our own Save As feature:

m_wbSaveAs.Config( CString("c:\\htmlpage.mhtml"), SAVETYPE_ARCHIVE );
m_wbSaveAs.SaveAs();

// where the second parameter is the type of HTML needed :
typedef enum _SaveType
{
    SAVETYPE_HTMLPAGE = 0,
    SAVETYPE_ARCHIVE,
    SAVETYPE_HTMLONLY,
    SAVETYPE_TXTONLY
} SaveType;

We start the SaveAs() implementation by installing the hook:

// prepare SaveAs Dialog hook
//
g_hHook = SetWindowsHookEx(WH_CBT, CbtProc, NULL, GetCurrentThreadId());
if (!g_hHook)
    return false;

// make SaveAs Dialog appear
//
// cmd = OLECMDID_SAVEAS (see ./include/docobj.h)
g_bSuccess = false;
g_pWebBrowserSaveAs = this;
HRESULT hr = m_pWebBrowser->ExecWB(OLECMDID_SAVEAS, 
    OLECMDEXECOPT_PROMPTUSER, NULL, NULL);

// remove hook
UnhookWindowsHookEx(g_hHook);
g_pWebBrowserSaveAs = NULL;
g_hHook = NULL;

The hook callback procedure is just hardcore code; see for yourself:

LRESULT CALLBACK CSaveAsWebbrowser::CbtProc(int nCode, 
    WPARAM wParam, LPARAM lParam) 
{  
    // the windows hook sees for each new window being created :
    // - HCBT_CREATEWND : when the window is about to be created
    //      we check out if it is a dialog box (classid = 0x00008002, 
    //      see Spy++)
    //      and we hide it, likely to be the IE SaveAs dialog
    // - HCBT_ACTIVATE : when the window itself gets activited
    //      we run a separate thread, and let IE do his own init steps in 
    //      the mean time
    switch (nCode)
    {
        case HCBT_CREATEWND:
        {
            HWND hWnd = (HWND)wParam;
            LPCBT_CREATEWND pcbt = (LPCBT_CREATEWND)lParam;
            LPCREATESTRUCT pcs = pcbt->lpcs;
            if ((DWORD)pcs->lpszClass == 0x00008002)
            {
                g_hWnd = hWnd;          // Get hwnd of SaveAs dialog
                pcs->x = -2 * pcs->cx;  // Move dialog off screen
            }
            break;
        }    
        case HCBT_ACTIVATE:
        {
            HWND hwnd = (HWND)wParam;
            if (hwnd == g_hWnd)
            {
                g_hWnd = NULL;
                g_bSuccess = true;

                if (g_pWebBrowserSaveAs->IsSaveAsEnabled())
                {
                    g_pWebBrowserSaveAs->SaveAsDisable();

                    CSaveAsThread *newthread = new CSaveAsThread();
                    newthread->SetKeyWnd(hwnd);
                    newthread->Config( g_pWebBrowserSaveAs->GetFilename(), 
                        g_pWebBrowserSaveAs->GetSaveAsType() );
                    newthread->StartThread();
                }
            }
            break;
        }
    }
    return CallNextHookEx(g_hHook, nCode, wParam, lParam); 
}

In our thread, we wait until the Internet Explorer Save As dialog is ready with filled data:

switch(    ::WaitForSingleObject( m_hComponentReadyEvent, m_WaitTime) )
{
     ...
     if ( ::IsWindowVisible(m_keyhwnd) )
     {
         bSignaled = TRUE;
         bContinue = FALSE;
     }

     MSG msg ;
     while( PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) )
     {
         if (msg.message == WM_QUIT)
         {
              bContinue = FALSE ;
              break ;
         }
         TranslateMessage(&msg);
         DispatchMessage(&msg);
     }
     ...
}

// relaunch our SaveAs class, but now everything is ready to play with
if (bSignaled)
{
    CSaveAsWebbrowser surrenderNow;
    surrenderNow.Config( GetFilename(), GetSaveAsType() );
    surrenderNow.UpdateSaveAs( m_keyhwnd );
}

// kill the thread, we don't care anymore about it
delete this;

We can now override the appropriate data:

void CSaveAsWebbrowser::UpdateSaveAs(HWND hwnd)
{
    // editbox : filepath (control id = 0x047c)
    // dropdown combo : filetypes (options=complete page;
    //     archive;html only;txt) (control id = 0x0470)
    // save button : control id = 0x0001
    // cancel button : control id = 0x0002


    // select right item in the combobox
    SendMessage(GetDlgItem(hwnd, 0x0470), CB_SETCURSEL, 
        (WPARAM) m_nSaveType, 0);
    SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(0x0470,CBN_CLOSEUP), 
        (LPARAM) GetDlgItem(hwnd, 0x0470));

    // set output filename
    SetWindowText(GetDlgItem(hwnd, 0x047c), m_szFilename);

    // Invoke Save button
    SendMessage(GetDlgItem(hwnd, 0x0001), BM_CLICK, 0, 0);  
}

In the code above, it is funny to remark that to select the kind of HTML we want (full HTML, archive, code only or text format), we not only select the adequate entry in the combo-box, we also send Internet Explorer a combo-box CloseUp notification. This is because that's what Internet Explorer has subscribed for to know we want this kind of HTML. This behavior is known by hints-and-trials.

Conclusion

This article describes a technique to gain access to the fully fledged Save As HTML feature exposed by Internet Explorer. I have never seen an article about this topic on the 'net, whereas it's easy to figure out that it is a compelling feature for developers building web applications. Files you may use from the source code provided are:

  • SaveAsWebBrowser.h, *.cpp: hook procedure; fill the dialog box data
  • SaveAsThread.h, *.cpp: auxiliary thread for synchronization with Internet Explorer

The application is just a simple MFC-based CHtmlView application embedding the web browser control.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Stephane Rodriguez.
France France
Member
Addicted to reverse engineering. At work, I am developing business intelligence software in a team of smart people (independent software vendor).
 
Need a fast Excel generation component? Try xlsgen.
 

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionNow have this working with IE 9, VS 2010 and C++ (no CLI) [modified]memberRene Pilon29 Jul '11 - 11:50 
When hooking the dialog in newer IE versions - things have changed - and trying to do it the original way this article outlines no longer works.
 
After 2 days of banging my head with the new "DirectUIHWND" - I finally managed to "force feed" the filename edit control and the combobox for the save as file type as follows:
 
<b>1.   Change the following enum:</b>
 
typedef enum _SaveType
{
     SAVETYPE_HTMLPAGE = 0,
     SAVETYPE_ARCHIVE <b>= 1</b>,
     SAVETYPE_HTMLONLY<b> = 2</b>,
     SAVETYPE_TXTONLY <b>= 3</b>
} SaveType;
 

2.   With newer versions of IE - you can no longer count on:
 
   HWND hSaveTypeComboWnd = GetDlgItem(hwnd, 0x0470);
 
Therefore:
 
<b>a) change the UpdateSaveAs method as follows:</b>
 
bool CSaveAsWebbrowser::UpdateSaveAs(HWND hwnd){  
 
     // editbox : filepath (control id = 0x047c)   NOTE (on older IEs)
     // dropdown combo : filetypes (options=complete page;archive;html only;txt) (control id = 0x0470)
     // save button : control id = 0x0001
     // cancel button : control id = 0x0002
 
   try {
 
      if( hwnd )
         m_bUpdateUI = TRUE;
 
        // select right item in the combobox
      HWND hSaveTypeComboWnd = GetDlgItem(hwnd, 0x0470);
      if( IsWindow(hSaveTypeComboWnd ) ){
         // Old Style
         SendMessage(hSaveTypeComboWnd, CB_SETCURSEL, (WPARAM) m_nSaveType, 0);
           SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(0x0470,CBN_CLOSEUP), (LPARAM) hSaveTypeComboWnd);
         HWND hFileNameEditCtrl = GetDlgItem(hwnd, 0x047c);
         if( !hFileNameEditCtrl || !IsWindow(hFileNameEditCtrl) ){
            hFileNameEditCtrl = GetDlgItem(hwnd, 0x03e9);           
         }
 
           // set output filename
         if( hFileNameEditCtrl && IsWindow(hFileNameEditCtrl) ){
            ExchangeEditText(hFileNameEditCtrl, (LPSTR)&m_szFilename);     
         }
      } else {
         HWND hWnd1 = FindWindowEx(hwnd,0,"DUIViewWndClassName","");
         if( hWnd1 ){
            HWND hWnd2 = FindWindowEx(hWnd1,0,"DirectUIHWND","");
            if( hWnd2 ){
               EnumChildWindows (hWnd2, FloatNotifySinkChildEnumProc, (LPARAM)this) ;     
            }
         }
      }
     
        if (m_bUpdateUI)
             SendMessage(GetDlgItem(hwnd, 0x0001), BM_CLICK, 0, 0);   // Invoke Save button
        else
             SendMessage(GetDlgItem(hwnd, 0x0002), BM_CLICK, 0, 0);   // Invoke Cancel button
     
   } catch(...){;}
 
   return true;
 
}
 

<b>b) add the following new callback function (for the child window enum)</b>
 
BOOL CALLBACK FloatNotifySinkChildEnumProc (HWND hwnd, LPARAM lParam){
    
     if (GetWindow (hwnd, GW_OWNER))            // Check for icon title
      return TRUE ;
   CSaveAsWebbrowser * saWb = (CSaveAsWebbrowser *) lParam;
   if( saWb ){
      // 1st, check to see if this window is of ComboBox class
      char szWndClassName[MAX_PATH];
      memset(&szWndClassName,0,MAX_PATH);
      GetClassNameA(hwnd,(LPSTR)&szWndClassName,MAX_PATH);     
      if( lstrcmp( (LPCSTR)&szWndClassName, "FloatNotifySink" ) == 0 ){     
 
         HWND hChildControl = (HWND) GetWindow(hwnd,GW_CHILD);
  
         if( hChildControl ){
            char szChildClassName[MAX_PATH];
            memset(&szChildClassName,0,MAX_PATH);
            GetClassName(hChildControl,(LPSTR)&szChildClassName,MAX_PATH);
            string_to_upper((LPSTR)&szChildClassName);
            if( lstrcmp("COMBOBOX",(LPSTR)&szChildClassName) == 0 ){
               // if it is a combobox - see if it has a "Edit" child winodw, if it does - it's the filename window, otherwise - it's the file type window.
               HWND hFileNameEditWnd = FindWindowEx(hChildControl, 0, "Edit", "");
               if( hFileNameEditWnd ){
                  saWb->AssignFileName(hFileNameEditWnd);
               } else {
                  /* Uncomment to peek at the current combobox's selection and selected text
                  int iSelected = 0 ;
                    iSelected = (int) SendMessage(hChildControl,CB_GETCURSEL,0,0);
                    if( iSelected != CB_ERR ){
                     int iBufLen   = SendMessage(hChildControl,CB_GETLBTEXTLEN,0,0);
                     int iMallocLen = (iBufLen * 2) + 2 ;
                           char * szBuffer = (char *) malloc( iMallocLen );            
                           if( szBuffer ){
                        memset(szBuffer,0, iMallocLen );
                        SendMessage(hChildControl,CB_GETLBTEXT,(WPARAM)iSelected,(LPARAM)(LPSTR)szBuffer);
                        free(szBuffer);
                     }                 
                  }
                  */
                  saWb->AssignFileType(hChildControl);
               }
            }
         }
      }
   }
   return TRUE ;
}
 
<b>c) and finally - add these 2 new methods to the class: </b>
 
void CSaveAsWebbrowser::AssignFileName(HWND hwnd){
   ExchangeEditText(hwnd, (LPSTR)&m_szFilename);
}
 
void CSaveAsWebbrowser::AssignFileType(HWND hwnd){
   SendMessage(hwnd, CB_SETCURSEL, (WPARAM) 1, 0);    
   SendMessage(GetParent(hwnd), WM_COMMAND, MAKEWPARAM(GetWindowLong(hwnd, GWL_ID),CBN_CLOSEUP), (LPARAM) hwnd);
}
 
Regards,
 
Rene Pilon
 

 
-- Modified Friday, July 29, 2011 6:09 PM
AnswerRe: Now have this working with IE 9, VS 2010 and C++ (no CLI)memberMember 81411146 Aug '11 - 15:26 
Thanks for the work!
 
Here are some notes and pitfalls I encountered while trying this out:
 
string_to_upper((LPSTR) &szChildClassName);
if(lstrcmp("COMBOBOX",(LPSTR) &szChildClassName) == 0){...}
string_to_upper() was unknown to my VS 2008 package, so I deleted that line and used the case insensitive version of lstrcmp() named lstrcmpi() in the following line.
 

void CSaveAsWebbrowser::AssignFileName(HWND hwnd){
   ExchangeEditText(hwnd, (LPSTR) &m_szFilename);
}
"ExchangeEditText()" expects type "CString &" as second argument, "m_szFilename" is already of type CString therefore the cast to "(LPSTR) &" is erroneous.
Same goes for
// set output filename
if( hFileNameEditCtrl && IsWindow(hFileNameEditCtrl) ){
   ExchangeEditText(hFileNameEditCtrl, (LPSTR) &m_szFilename);
in "CSaveAsWebbrowser::UpdateSaveAs()"
 

By mistake I defined the new callback function
BOOL CALLBACK FloatNotifySinkChildEnumProc()
as a member function of CSaveAsWebbrowser.
Problem you get is that it never resolves to the required type in the call to
EnumChildWindows(hWnd2, FloatNotifySinkChildEnumProc, (LPARAM)this);
Therefore "FloatNotifySinkChildEnumProc()" should be defined as global function.
When doing so, one should be aware though, that it perfoms calls to
"CSaveAsWebbrowser::AssignFileName()"
and
"CSaveAsWebbrowser::AssignFileType()"
hence these 2 new methods must be declared as public members of class CSaveAsWebbrowser.
 

Last thing:
In
void CSaveAsWebbrowser::AssignFileType(HWND hwnd){ 
   SendMessage(hwnd, CB_SETCURSEL, (WPARAM) 1, 0);     
   SendMessage(GetParent(hwnd), WM_COMMAND, MAKEWPARAM(GetWindowLong(hwnd, GWL_ID),CBN_CLOSEUP), (LPARAM) hwnd);
}
I don't see any access to the file type stored in "m_nSaveType".
The type does change from "complete web page" but it always saves as archive.
Am I missing something?
 
In this context I also noticed one major drawback of the presented solution:
As it looks, flicking through the child windows of the "Save As" dialog box using EnumChildWindows() always finds the edit control for the filename first and then the combo box for file type. This way IE will always dictate you its file extension no matter what you specify in "m_wbSaveAs.Config("filename","filetype")".
GeneralRe: Now have this working with IE 9, VS 2010 and C++ (no CLI)memberRene Pilon7 Aug '11 - 11:43 
Good stuff.
 
I posted the sample code to show how I managed to get it working before I modded it further for my needs.   You are correct about the CB_SETCURSEL - it's selecting the 2nd choice in the save type combobox - and this is very dependent in IE always having MHT as the 2nd option in the combobox.   As well - I should have put the enum value instead of the 1 directly.
 
Rene.
AnswerRe: Now have this working with IE 9, VS 2010 and C++ (no CLI)memberMember 81411147 Aug '11 - 7:28 
Having taken a second look at this I have now solved my requests to a satisfactory degree.
 
1. In
void CSaveAsWebbrowser::AssignFileType(HWND hwnd)
{ 
   SendMessage(hwnd, CB_SETCURSEL, (WPARAM) 1, 0);     
   SendMessage(GetParent(hwnd), WM_COMMAND, MAKEWPARAM(GetWindowLong(hwnd, GWL_ID),CBN_CLOSEUP), (LPARAM) hwnd);
}
The first SendMessage() should read
SendMessage(hwnd, CB_SETCURSEL, (WPARAM) m_nSaveType, 0);
if you want your website to be saved in a format other than an archive.
 
2. As regards the order of the selection of the controls the Save as dialog box turned out to be rather bitchy.
Say you want your website to be saved under the name "Source.txt" with save type HTMLONLY then the program would fill in "Source.txt" under filename and then select "HTML only" from the combobox, thus changing the filename to "Source.htm".
So I tried to reverse the order by storing the windows handle for the edit control in a variable and performing the selection of the save type first. The filename gets filled in correctly but the dialog box doesn't use it and reverts to the default name "yahoo.htm" ("yahoo.com" being the website of the example code).
Second approach: Performing the EnumChildWindows() function twice, first for the combobox (works) then for the edit control (didn't do anything). Resulting filename: "yahoo.htm"
Back to method 1: Filling in the edit control twice: "Source.txt" (first edit) -> "Source.htm" (after combobox selection) -> "Source.txt" (second edit) -> "Source.htm" (resulting filename).
 
Obviously the edit control needs something else before accepting the submitted string (like the combobox needing a CBN_CLOSEUP message) but I haven't figured out what this might be.
I did find out however, that performing a "Select All" (Ctrl+A) on the edit control does the trick.
 
Well, maybe not the most elegant solution by someone who knows what he is doing and what is going on in the internals of MS Windows, but here are the code changes:
 
Add the variable m_hFileNameEditWnd of type HWND as a public member to the class CSaveAsWebbrowser.
 
Add 3 lines to UpdateSaveAs()
bool CSaveAsWebbrowser::UpdateSaveAs(HWND hwnd)
{
  // editbox : filepath (control id = 0x047c)
  // dropdown combo : filetypes (options=complete page;archive;html only;txt) (control id = 0x0470)
  // save button : control id = 0x0001
  // cancel button : control id = 0x0002

  try
  {
    if(hwnd)
      m_bUpdateUI = TRUE;
 
    // select right item in the combobox
    HWND hSaveTypeComboWnd = GetDlgItem(hwnd, 0x0470);
    if(IsWindow(hSaveTypeComboWnd))
    {
      // Old Style
      SendMessage(hSaveTypeComboWnd, CB_SETCURSEL, (WPARAM) m_nSaveType, 0);
      SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(0x0470, CBN_CLOSEUP), (LPARAM) hSaveTypeComboWnd);
      HWND hFileNameEditCtrl = GetDlgItem(hwnd, 0x047c);
      if(!hFileNameEditCtrl || !IsWindow(hFileNameEditCtrl))
        hFileNameEditCtrl = GetDlgItem(hwnd, 0x03e9);
      // set output filename
      if(hFileNameEditCtrl && IsWindow(hFileNameEditCtrl))
        ExchangeEditText(hFileNameEditCtrl, m_szFilename);
    }
    else
    {
      HWND hWnd1 = FindWindowEx(hwnd,0,"DUIViewWndClassName","");
      if(hWnd1)
      {
        HWND hWnd2 = FindWindowEx(hWnd1,0,"DirectUIHWND","");
        if(hWnd2)
        {
          m_hFileNameEditWnd = NULL;
          EnumChildWindows(hWnd2, FloatNotifySinkChildEnumProc, (LPARAM) this);
          if(m_hFileNameEditWnd)
            AssignFileName(m_hFileNameEditWnd);
        }
      }
    }
    if (m_bUpdateUI)
      SendMessage(GetDlgItem(hwnd, 0x0001), BM_CLICK, 0, 0);   // Invoke Save button
    else
      SendMessage(GetDlgItem(hwnd, 0x0002), BM_CLICK, 0, 0);   // Invoke Cancel button
  }
  catch(...){;}
  return true;
}
Change 1 line of FloatNotifySinkChildEnumProc()
BOOL CALLBACK FloatNotifySinkChildEnumProc(HWND hwnd, LPARAM lParam)
{
  if (GetWindow(hwnd, GW_OWNER))            // Check for icon title
    return TRUE;
  CSaveAsWebbrowser *saWb = (CSaveAsWebbrowser *) lParam;
  if(saWb)
  {
    // 1st, check to see if this window is of ComboBox class
    char szWndClassName[MAX_PATH];
    memset(&szWndClassName, 0, MAX_PATH);
    GetClassNameA(hwnd, (LPSTR)&szWndClassName, MAX_PATH);
    if(lstrcmp((LPCSTR)&szWndClassName, "FloatNotifySink") == 0)
    {
      HWND hChildControl = (HWND) GetWindow(hwnd, GW_CHILD);
      if(hChildControl)
      {
        char szChildClassName[MAX_PATH];
        memset(&szChildClassName, 0, MAX_PATH);
        GetClassName(hChildControl, (LPSTR) &szChildClassName, MAX_PATH);
        if(lstrcmpi("COMBOBOX", (LPSTR)&szChildClassName) == 0)
        {
          // if it is a combobox - see if it has a "Edit" child window, if it does - it's the filename window, otherwise - it's the file type window
          HWND hFileNameEditWnd = FindWindowEx(hChildControl, 0, "Edit", "");
          if(hFileNameEditWnd)
            saWb->m_hFileNameEditWnd = hFileNameEditWnd;
          else
          {
            /* Uncomment to peek at the current combobox's selection and selected text
            int iSelected = 0;
            iSelected = (int) SendMessage(hChildControl, CB_GETCURSEL, 0, 0);
            if(iSelected != CB_ERR)
            {
              int iBufLen = SendMessage(hChildControl, CB_GETLBTEXTLEN, 0, 0);
              int iMallocLen = (iBufLen * 2) + 2;
              char *szBuffer = (char *) malloc(iMallocLen);
              if(szBuffer)
              {
                memset(szBuffer, 0, iMallocLen);
                SendMessage(hChildControl, CB_GETLBTEXT, (WPARAM)iSelected, (LPARAM)(LPSTR)szBuffer);
                free(szBuffer);
              }                  
            }*/
            saWb->AssignFileType(hChildControl);
          }
        }
      }
    }
  }
  return TRUE;
}
Finally to get AssignFileName() to send Ctrl+A:
void CSaveAsWebbrowser::AssignFileName(HWND hwnd)
{
	ExchangeEditText(hwnd, m_szFilename);
	SendMessage(hwnd, WM_KEYDOWN, 0x00000011, 0x001D0001); // 'CTRL'
	SendMessage(hwnd, WM_KEYDOWN, 0x00000041, 0x001E0001); // 'A'
	SendMessage(hwnd, WM_CHAR, 0x00000001, 0x001E0001);
	SendMessage(hwnd, WM_KEYUP, 0x00000041, 0xC01E0001);
	SendMessage(hwnd, WM_KEYUP, 0x00000011, 0xC01D0001);
}

QuestionAutomated IE SaveAs on C#memberjgiami240212 Jul '11 - 7:55 
I am navigating to a secure website and i need to click on a file to save it as and I am trying to interact with the dialog box to save as a certain file
 
Any help would be greatly appreciated
 
Thanks
GeneralHere is perfectly working C# codememberMadhava maydipalle19 Nov '09 - 4:05 
Thanks for all the people on this thread. here is working example..
 
Usage :
To use from IE plugin :
 
SHDocVw.IWebBrowser2 browser = GetCurrentBrowser();
MHTHelper mhtHelper = new MHTHelper();
bool bMhtFile = mhtHelper.SaveAs(browser, filePath, EnumBrowserFileSaveType.SAVETYPE_ARCHIVE);
 

 
#region ---- EnumBrowserFileSaveType ----
public enum EnumBrowserFileSaveType
{
SAVETYPE_HTMLPAGE = 0,
SAVETYPE_ARCHIVE,
SAVETYPE_HTMLONLY,
SAVETYPE_TXTONLY
}
#endregion
 
public class MHTHelper
{
#region ---- Constructor ----
public MHTHelper()
{
}
#endregion
#region ---- Private Attributes ----
private IWebBrowser2 webBrowser;
private string filePath;
private EnumBrowserFileSaveType saveType;
private WindowHookProc HookProcedure;
private int windowHook = 0;
private IntPtr hwndSaveAsDlg = (IntPtr)0;
private MHTHelper saveAsMht = null;

#endregion
#region ---- Public Attributes ----
public IWebBrowser2 WebBrowser
{
get { return webBrowser; }
set { webBrowser = value; }
}
 
public string FilePath
{
get { return filePath; }
set { filePath = value; }
}
public EnumBrowserFileSaveType SaveType
{
get { return saveType; }
set { saveType = value; }
}
#endregion
#region ---- SaveAs ----
/// <summary>
/// In this function we are automating the following functionality.
/// 1.Select 'File->Save AS' menu item on IE
/// 2.Take SaveAS dialog out of screen so that user cannot interact with it.(we cannot hide it, because of IE security policy.)
/// 3.Set the required file path to the save as dialog.
/// 4.Click on save button.
/// 5.IE automatically display the status bar to show the extraction process of currently displayed web page into mht file.
/// </summary>
/// <param name="webBrowser"></param>
/// <param name="pathFile"></param>
/// <param name="saveType"></param>
/// <returns></returns>
public bool SaveAs(IWebBrowser2 webBrowser, string pathFile, EnumBrowserFileSaveType saveType)
{
try
{
this.WebBrowser = webBrowser;
this.FilePath = pathFile;
this.SaveType = saveType;
//If no path is supplied or file already exists then it prompts for user action.
if (0 == pathFile.Length)
pathFile = "untitled";
if ((null == webBrowser) || (0 != windowHook))
return false;

this.HookProcedure = new WindowHookProc(this.SaveAsHookProc);
// prepare SaveAs dialog hook and activate it.
this.windowHook = SetWindowsHookEx(5 /*WH_CBT*/, HookProcedure, (IntPtr)0, AppDomain.GetCurrentThreadId());
if (this.windowHook == 0)
return false;
try
{
// This following code shows the save as dialog
this.saveAsMht = this;
object o = null;
webBrowser.ExecWB(SHDocVw.OLECMDID.OLECMDID_SAVEAS, SHDocVw.OLECMDEXECOPT.OLECMDEXECOPT_PROMPTUSER, ref o, ref o);
this.saveAsMht = null;
return true;
}
finally
{
//Here we took out our hook .
UnhookWindowsHookEx(this.windowHook);
this.windowHook =0;
}
}
catch (Exception ex)
{
ExceptionHandler.Display(ex);
return false;
}
}
 
#endregion
#region ---- SaveAsHookProc ----
/// <summary>
/// This proc is required to handle Window messages and do appropriate action for our requirements.
/// </summary>
/// <param name="nCode"></param>
/// <param name="wParam"></param>
/// <param name="lParam"></param>
/// <returns></returns>
public int SaveAsHookProc(int nCode, IntPtr wParam, IntPtr lParam)
{
switch (nCode)
{
case 3: // HCBT_CREATEWND
CBT_CREATEWND cw = (CBT_CREATEWND)Marshal.PtrToStructure(lParam, typeof(CBT_CREATEWND));
CREATESTRUCT cs = (CREATESTRUCT)Marshal.PtrToStructure(cw.lpcs, typeof(CREATESTRUCT));
if (cs.lpszClass == 0x00008002)
{
this.hwndSaveAsDlg = (IntPtr)wParam; // Get hwnd of SaveAs dialog
cs.x = -2 * cs.cx; // Move dialog off screen
}
break;
case 5: // HCBT_ACTIVATE
IntPtr hwnd = (IntPtr)wParam;
//Here we get Save as dialog handle
if (hwnd == this.hwndSaveAsDlg && this.hwndSaveAsDlg != (IntPtr)0)
{
//Prepare a thread to act as required messages source.Also set File path and its save type.
ThreadPressOk tpok = new ThreadPressOk(hwnd, this.saveAsMht.FilePath, this.saveAsMht.SaveType);
this.hwndSaveAsDlg = (IntPtr)0;
// Create a thread to execute the task, and then
// start the thread.
new Thread((new ThreadStart(tpok.ThreadProc))).Start();
}
break;
}
//This is required to pass control to next hook if exists on this process.
return CallNextHookEx(this.windowHook, nCode, wParam, lParam);
}
 
#endregion
#region ---- Win32APIs ----
//Import for SetWindowsHookEx.
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
public static extern int SetWindowsHookEx(int idHook, WindowHookProc lpfn, IntPtr hInstance, int threadId);
 
//Import for UnhookWindowsHookEx.
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
public static extern bool UnhookWindowsHookEx(int idHook);
 
//Import for CallNextHookEx.
//Use this function to pass the hook information to next hook procedure in chain.
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
public static extern int CallNextHookEx(int idHook, int nCode, IntPtr wParam, IntPtr lParam);
//This delegate is required to fetch our function to the thread.
public delegate int WindowHookProc(int nCode, IntPtr wParam, IntPtr lParam);
 
//These Win32 structures are requred to handle windows messages/state
[StructLayout(LayoutKind.Sequential)]
public struct CBT_CREATEWND
{
public IntPtr lpcs;
int hwndInsertAfter;
};
[StructLayout(LayoutKind.Sequential)]
public struct CREATESTRUCT
{
int lpCreateParams;
int hInstance;
int hMenu;
int hwndParent;
int cy;
public int cx;
int y;
public int x;
int style;
int lpszName;
public int lpszClass;
int dwExStyle;
}
#endregion
#region ---- Thread Requirements ----
//The following class is defined here because it is used in the above class only.
//It sends all required thread messages to IE's save as dialog.
class ThreadPressOk
{
public ThreadPressOk(IntPtr hwnd, string pathFile, EnumBrowserFileSaveType saveType)
{
this.hwndDialog = hwnd;
this.pathFile = pathFile;
this.saveType = saveType;
}
 
IntPtr hwndDialog;
string pathFile;
EnumBrowserFileSaveType saveType;
 
// Imports of the User32 DLL.
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr SendMessage(IntPtr hWnd, int msg, int wParam, int lParam);
 
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr GetDlgItem(IntPtr hWnd, int nIDDlgItem);
 
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern private bool SetWindowText(IntPtr hWnd, string lpString);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool IsWindowVisible(IntPtr hWnd);
 
// The thread procedure performs the message loop and place the data
public void ThreadProc()
{
//To avoid race condition, we are forcing this thread to wait until Saveas dialog is displayed.
while (!IsWindowVisible(hwndDialog))
{
Thread.Sleep(100);
Application.DoEvents();
}
Application.DoEvents();
//Get the handle to SaveType combo box on the save as dialog.
IntPtr typeB = GetDlgItem(hwndDialog, 0x0470);
//Get the handle to file path on the saveas dialog.
IntPtr nameB = GetDlgItem(hwndDialog, 0x047c);
//Get the handle to saveas on the saveas dialog.
IntPtr saveBtn = GetDlgItem(hwndDialog, 0x0001);

if (((IntPtr)0 != typeB) && ((IntPtr)0 != nameB) && ((IntPtr)0 != saveBtn) && IsWindowVisible(hwndDialog))
{
//select save type
SendMessage(typeB, 0x014E /*CB_SETCURSEL*/, (int)saveType, 0);
SendMessage(hwndDialog, 0x0111 /*WM_COMMAND*/, 0x80470/*MAKEWPARAM(0x0470, CBN_CLOSEUP)*/, (int)typeB);
// set save as filepath
SetWindowText(nameB, pathFile);
// Invoke Save button click.
SendMessage(saveBtn, 0x00F5 /*BM_CLICK*/, 0, 0);
}
// Clean up GUI - we have clicked save button.
//GC is going to do that cleanup job, so we are OK
Application.DoEvents();
//Terminate the thread.
return;
}
}
#endregion
}
 
Thanks
Maydipalle

GeneralProblem with code in Windows Vista and Windows 7memberNguyễn Đức Thiện24 Oct '09 - 18:29 
Hi to all, it been a long time since my last time I write/ask in the forum.
I have the problem I hope you can help me:
I’m using the guide:
http://www.codeproject.com/KB/shell/iesaveas.aspx
(Automated IE SaveAs MHTML)
I have the problem when I’m running the code in Windows Vista
In the method:
void CSaveAsWebbrowser::UpdateSaveAs(HWND hwnd)
{
// editbox : filepath (control id = 0x047c)
// dropdown combo : filetypes (options=complete page;
// archive;html only;txt) (control id = 0x0470)
// save button : control id = 0x0001
// cancel button : control id = 0x0002
 

// select right item in the combobox
SendMessage(GetDlgItem(hwnd, 0x0470), CB_SETCURSEL,
(WPARAM) m_nSaveType, 0);
SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(0x0470,CBN_CLOSEUP),
(LPARAM) GetDlgItem(hwnd, 0x0470));
 
// set output filename
SetWindowText(GetDlgItem(hwnd, 0x047c), m_szFilename);
 
// Invoke Save button
SendMessage(GetDlgItem(hwnd, 0x0001), BM_CLICK, 0, 0);
}
The problem is that we send the control id ‘0x047c’,(for the combo box and the output file), I noticed that the method GetDlgItem(hwnd, 0x047c), return 0, I’m thinking the problem is that the code id '0x047c' in Vista is not relevant, I found out that for NT, Windows 98 and 95 the combobox and output file has different ID ‘0x0480’, I was wandering I someone know where I can found the id’s for Vista, or someone have another idea why this is not working.
Thanks
GeneralThis can't run on windows 7memberNguyễn Đức Thiện24 Oct '09 - 6:09 
Hello!
I use this code to run success on Windows Xp (whith IE8) but can't exact on Windows 7 (with IE8).
Help me!
QuestionDo you know what would be the libraries use for C# ?memberBattosaiii10 Aug '09 - 6:31 
I would like to use custom dialog to save as webpages from internet explorer. It would be done in C#.
Do you know the libraries for C# ? I have been looking for libraries in c# but i cant find anything !
GeneralPages with big sizes are not being able to saved in IE explorer itselfmemberHMLGUY28 Jun '09 - 21:19 
Hi
 
I am using your application for programmatically saving file from IE
Automated IE SaveAs MHTML
Well only one small change I have made.
In your application
1)You create a separate browser control
2)Navigate it to web page
3)And then Save the file using
HRESULT hr = m_pWebBrowser->ExecWB(cmdid, OLECMDEXECOPT_PROMPTUSER, NULL, NULL);
 

I have included SaveAsThread.cpp and SaveAsWebBrowser.cpp in my application.
Only change I have made is in your SetWebBrowser method,
Instead of setting your application's browser control,
I get an instance of currently running IE(IE version 8) and set it in SetWebBrowser method
Then I have added one button on IE toolbar and on click of it I call

m_wbSaveAs.Config( CString(finalFilePath), SAVETYPE_ARCHIVE );
m_wbSaveAs.SaveAs()
 
where finalFilePath is path of file to be saved
 
Now when ever I surf lighter pages or pages with small size, I am able to Save pages and your program runs amazing.
But when ever i surf heavy pages for example home page of http://indiatimes.com my instance of IE gets hanged and page is not being able to saved.
 
Same heavy page when I surf in your application and in your web browser control instead of IE itself, application works absolutely fine and is able to save page.
Do you have any idea, why IE itself is not able to perform the same.
 
Any clues or help would be of great help
 
Thanks in advance
QuestionCan we use SetWindowsHookEx for hooking printer dialogs?memberMember 372884110 Dec '08 - 17:18 
hi
 
Can we in any way use SetWindowsHookEx in hooking printer dialogs using printHookProc()??plz tell me otherwise how to do so?if somebody can explain giving sample code that will be more helpful to me as i'm a newbie in this..
thanks and regards
Rm
Questioncan you use the hook for retriveing the address bar content?memberrerb26 Jul '07 - 9:47 
Is this possible? I am also a VB.net user but any info or sample would be very helpfull. Smile | :) thanks
QuestionExcellent! Can this be done in VB.Net?membersaab340b6 Apr '07 - 6:36 
I have spend days looking for this information. Now that I've finally found it, it is NOT in vb.net. Ever thought of converting it?
QuestionHow can I save HTML from IEmemberkallol kumar1 Aug '06 - 0:47 
Hello,

cool...Its a nice work.I have a problem of saving HTML.I developed a project which is load url into Internet Explorer(shell Executing iexplore.exe). But I cann't save that Url automatically. I want to save it without showing its saveAs Dialog.I am trying it with your project. But I cann't. Can you please give me any ideas.
 
Thanks
Kallol

GeneralSave as xls filememberxgnitesh15 Feb '06 - 0:15 
Hi,
First of all I would like to thank the author for the excellent article. This works perfectly for HTML pages.
Now I have a requirement where the URL link may point to a excel file or a pdf file or any other kind of binay file. When I use the program for excel file, the save as dialog box does popup but the BM_CLICK message does not work to invoke the save button. I noticed that this is because there is another kind of save as dialog that appears when we try to save the excel file (different from the one that appears when you try to do save as for HTML pages). I thought that the resource id for save button must have changed and tried 1 to 100 all ids not just 1. But no success. Then I tried to use Spy++ drag and drop feature to identify the save button window, but unfortunately even Spy++ does not identify this save button as an independent window.
 
Possible questions:
How can I indentify the right button resource id?
How can invoke the save button in this new kind of save as dialog?
 
Any help is appreciated.
 
Thanks & regards,
Nitesh
QuestionHow to know whether a printer is connected or notmembervijay kumar T17 Jan '06 - 7:50 
I have a problem with the printer. I want to know whether a printer is physically connected and then I want to issue a print command. If any one can help me I would be greatful.

AnswerRe: How to know whether a printer is connected or notmemberStephane Rodriguez.17 Jan '06 - 23:00 

What has this to do with the article?

GeneralRe: How to know whether a printer is connected or notmembervijay kumar T19 Jan '06 - 5:35 
Ofcourse it is not related but some one working with printers may respond.
GeneralProblem in Release versionmemberRoland Liu15 Jan '06 - 17:09 
Your code is very good in debug version, but in release version, I can't save any .mhtml file. This is a big bug, I hope you find why the Problem occur only in Release version.
 
Zjroland from http://www.outsourcexp.com
QuestionHow to save web page silently just from the url?memberTcpip20056 May '05 - 17:21 
I mean how to save a web page without navigate to the page just from the url ?
also the save as dialog box should not be shown.
 
Thank you sir
Generaldesktop flickering problem and realese problemmemberLibi123419 Mar '05 - 20:33 
Hi,
I used your code in order to save several pages in a loop.
The problem acuur, while its in prosses. The desktop is flickering, assume when the hidden dialog being closed.
Have someone an idea how to resolve it?
 

--UPDATE--
 
In realese mode there is a problem in varibal m_bUpdateUI that somehow is always false!!!Confused | :confused:
Some idea??
 
thanks.
Great and Excellent job!
GeneralRe: desktop flickering problem and realese problemmemberStephane Rodriguez.17 Jan '06 - 22:59 

http://www.codeproject.com/shell/iesaveas.asp?msg=483194#xx483194xx

GeneralRe: desktop flickering problem and realese problemmemberLibi123417 Jan '06 - 23:06 
Thanks.
What about the flickering prob?
GeneralHelp to without prompt dialogbox to usermemberbaskarchinnu27 Feb '05 - 23:21 
Hi
I am working in Win32 API SDK, at my program, I am hooking IE window and I am using my own BHO dll (whenever IE is open, dll will attach with IE browser) through this dll I want to save the complete web page without prompting the save as dialogbox. With your example help, I tried by using as follows
 
HRESULT hr = m_pWebBrowser->ExecWB(OLECMDID_SAVEAS,
MSOCMDEXECOPT_DONTPROMPTUSER, NULL, NULL);
 
I used MSOCMDEXECOPT_DONTPROMPTUSER options, even though I am getting dialog box and even if try to save the web page through save as dialog box also, page is not saving. I am getting the error msg "This Web page could not be saved".
 
Can you please help me to save the complete web page without prompting save..as dialog box.
 
Thanks in Advance

 
Baskar
GeneralSave the modified pagememberBerkeley Wong20 Nov '04 - 7:15 
How can I save the modified HTML page to local using C++?
GeneralRe: Save the modified pagememberStephane Rodriguez.20 Nov '04 - 9:04 

You have to switch IE in edit mode first. There are numerous articles available out there (even a few on codeproject if I remember well).

GeneralRe: Save the modified pagememberBerkeley Wong20 Nov '04 - 18:48 
I need to modify the HTML form's value (say one entry field Name) and then save this HTML page with the given name to the local machine. I tried to use this example to save the HTML page to local, it is OK , but the page does not contain modified value (name) in the entry field. Could you explain more and any alternative to do this in C++?
GeneralRe: Save the modified pagemembermcbain3 May '07 - 10:18 
Did you (anyone?) ever find a solution how to save the modified html page to mht? I would be interested to know.
 
Regards, Fredrik
Generalwin98memberNorrieTaylor29 Aug '04 - 20:02 
I can't get this to work with Win98 even if use the tip mentioned in the previous thread. It seems to hang before it even gets to that point. If anyone can help I would greatly appreciate it.
 
Best regards,
 
Norrie Taylor
GeneralWin98 Different DialogIDmemberTDK90000010 May '04 - 5:16 
Great article but it fails for me using IE6 on Win98. I think the reason is that the dialog IDS are different. For example GetDlgItem(hwnd, 0x47c) returns NULL for me in UpdateSaveAs.
 
Does anyone know either
1. What the correct dialog IDS for win 98
2. Which dll the resource is in so I can try to find out the dialog ID
3. A way around this problem.
 
Cheers
 

GeneralRe: Win98 Different DialogIDsussAnonymous11 May '04 - 0:10 
Appologies for answering my own question but the dll in question is COMDLG32.DLL and the SAVEAS resource in this has the following id change
 
editbox filepage = 0x480 (0x047c on xp)
 
so if you change the code in
void CSaveAsWebbrowser::UpdateSaveAs(HWND hwnd) to use this ID when running on 98 it works fine.
 

GeneralRe: Win98 Different DialogIDmemberStephane Rodriguez.11 May '04 - 1:18 

Thanks for the follow up. I admit I didn't want to install a vmware with a WIN98 guest OS only to retrieve that damn ctrl id.Wink | ;-)

GeneralRe: Win98 Different DialogIDsussAnonymous29 Aug '04 - 16:57 
Thanks, This follow up saved my butt!!
GeneralRe: Win98 Different DialogIDmemberNorrieTaylor30 Aug '04 - 19:12 
How would you retrieve that ctrl id? I have a win98 guest running but I can't get spy++ to run on win98. Now I am kinda stuck.
 
Thanks
GeneralRe: Win98 Different DialogIDmemberStephane Rodriguez.30 Aug '04 - 20:36 

If you can't get spy++ to run, then this doesn't bode well for the rest. You can find alternatives to spy++ in codeproject.
Also I wonder why the ip provided in this thread (id = 0x480) wouldn't work for you, unless you have a totally different version of IE - a version where the file dialogs would be hooked by a third-party or something like that.
 
I admit the way of trying to achieve the save as mhtml is not that good in practice. It's probably bes tto either rely on a third-party (read old threads), or reuse the IE cache to get the bits and create the mhtml yourself.
GeneralRe: Win98 Different DialogIDmemberNorrieTaylor31 Aug '04 - 18:57 
Thanks for the quick reply.
 
It seems that in Win 98 there is a problem getting the handle to the "Save Web Page" dialog.
 
In the callback proc under XP three dialogs actually get processed during the HCBT_CREATEWND case. The "Save Web Page" being the third and last. You can trace the names of these dialogs as they pass through the call back. However, in Win 98 only two two seem to pass through the callback function, none of which have the name "Save Web Page". However, the window still gets moved to the side.
 
Without the third and last window going through the callback proc the handle never get saved. Thus you can't manipulate the window later on.
 
Got any ideas?
 
Thanks again!!!
 

 

 

GeneralRe: Win98 Different DialogIDmemberNorrieTaylor31 Aug '04 - 20:16 
Well I found a work around that I think might make this slightly more stable and that would fix my issue mentioned above.
 
In the callback function just replace this:
 
if (hwnd == g_hWnd)
 
with this:
 
TCHAR buff[512];
GetWindowText( hwnd, buff, 512 );
if ( strcmp( buff, "Save Web Page") == 0 )
 
Best regards,
 
Norrie
 

GeneralReference Page is missingmemberinternal27 Apr '04 - 17:09 
About the print dialog hook.
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnie55/html/wb_print.asp
 
Page Cannot Be Found
We apologize for the inconvenience, but the page you are seeking cannot be found in this location.
 
googled no result too.
 
Sing when we're programming.
QuestionMFC70D.dll problem..?memberajay sonawane26 Apr '04 - 19:02 
Hello
When I tried to ren your demo application, It flases an error saying that
 
---------------------------
Browser.exe - Unable To Locate Component
---------------------------
This application has failed to start because MFC70D.DLL was not found. Re-installing the application may fix this problem.
---------------------------
 
Can u give me solution for this problem?
Thanks in advance...
 
ajay sonawane
AnswerRe: MFC70D.dll problem..?memberStephane Rodriguez.26 Apr '04 - 19:08 

Recompile the source code with VC6.

QuestionHow to save only one image ?memberrrrado16 Apr '04 - 3:56 
I'm using WebBrowser embedded in my application.
I just need to get one image after it is downloaded and displayed,
I have it's URL and IHTMLImgElement interface (but this interface contains only methods for getting image's file size and date, not it's local name).
Is it a good idea to use this solution, or is there some easier way ?
Thank you
 

rrrado
AnswerRe: How to save only one image ?sussAnonymous17 Apr '04 - 5:22 

Here is how you would do that with C# (http://www.codeproject.com/jscript/htmlgetshooked.asp?msg=774194&mode=all&userid=42845#xx774194xx). With C++, use UrlDownloadFile (or something like that instead of the C# webrequest helper).
 

GeneralRe: How to save only one image ?memberrrrado18 Apr '04 - 21:08 
Thank you, but unfortunately image I need to download is not cached because of HTTP cache control, and is dynamically created, so it is different every time it is downloaded. So I can't use this solution.
 

rrrado
GeneralRe: How to save only one image ?memberangello3 Mar '05 - 12:57 
Smile | :) I have the same problem (using Delphi).
What I think is to use IHTMLElementRender and it`s method DrawToDC to paint the image in a supplied DC with a bitmap, created with dimensions of image and selected in the DC. And I hope to end up with HBItmap containing the image Smile | :)
GeneralRe: How to save only one image ?membertwinssen14 Jan '06 - 13:36 
Use Winpcap to sniff the image is the most easy solution, if it is not cached and you can/may not turn this of in your browser.
GeneralUsing in .Exe to get IE to save current pagememberNeville Franks21 Mar '04 - 23:37 
Hi I'm trying to use this code in .EXE (the code is actually in a DLL) and save the current IE Web page. I'm using http://www.codeproject.com/shell/iehelper.asp[^] to get an IWebBrowser2* for the instance of IE.
 
My first problem was that ExecWB() always failed. I replaced that with lpOleCommandTarget->Exec() which works.
 
My next problem is with SetWindowsHookEx(). I can't use GetCurrentThreadId() as that is my app. I've tried setting this to NULL and setting hMod to the DLL hInstance but the VC++ debugger spits the dummy. If I set hMod and dwThreadId to NULL SetWindowsHookEx() fails.
 
So I figure if I can get the threadid for the IWebBrowser2* object, that may work. Any ideas how to get the thread id for an IWebBrowser2* or any other suggestions on how to get this to work in app where I just want to get IE to save its current page to a mht.
 
Thanks a bunch for any help. It has been a very long day of head scratching.Sigh | :sigh:
 
Neville Franks, Author of ED for Windows www.getsoft.com and coming soon: Surfulater www.surfulater.com
 

GeneralRe: Using in .Exe to get IE to save current pagememberStephane Rodriguez.27 Mar '04 - 7:53 
Neville Franks wrote:
My first problem was that ExecWB() always failed. I replaced that with lpOleCommandTarget->Exec() which works.
 
Doesn't sound good. The article embeds the browser within a MFC view. Is your app really different from that?
 

 
Neville Franks wrote:
My next problem is with SetWindowsHookEx(). I can't use GetCurrentThreadId() as that is my app.
 
Unless IE as running in a separate process, there is no reason not to use GetCurrentThreadId(). The thread id acts as a filter and since we hook the system, waiting for a dialog box to appear, is kind of required to avoid hooking a dialog box from another app. Of course, if IE is in a separate process, it's a totally different story : in this case, may be you can get that active InternetExplorer_ServerWindow by enumerating the windows, and the find the running thread.
 
All in all this does not sound too much practical anyway. Creating the archive file yourself may be just as convenient, and less dependent to IE, therefore less prone to errors and potential hangs. I would definitely recommend this route : either buy a third-party generator (see the comments below), or build your own (I did from one of my sharewares and it was much more easy then I predicted).
 
Good luck!
 

 


QuestionCan I get the Internet shortcuts in the IE history with other methods?memberxieweihua23 Feb '04 - 21:57 
In the IE history directory,you can see the internet address you have accessed.I can use the IShellFolder to get the interface of the object,but I cann't get the URL of an item.Is there a simple way to get the URL of an item in the IE history directory?

AnswerRe: Can I get the Internet shortcuts in the IE history with other methods?memberStephane Rodriguez.24 Feb '04 - 2:12 
The official way of retrieving the browser history is to use the IUrlHistory interface (Platform SDK, urlhist.h), available with IE5.5 and up.
 
Of course, it's just as smart to do like you did, get the user's History IShellFolder, then start traversing all underlying IShellFolder nodes. You need a special algorithm for that, since there are particular names, etc. Your best bet in my opinion is to get one or two pieces of code that try to mimic Windows Explorer, and supports all the underlying shell namespaces. I know there are a couple of those available in codeproject articles.
 
Good luck!

 

 

-- modified at 5:14 Saturday 5th November, 2005
QuestionHow to set URL of IE through program?membershri_3118 Feb '04 - 0:44 
I want to set URL of the Internet Explorer through Visual C++ program.
If we have given a set of links and now my program will set first link and SaveAs the HTML page and proceed to the next link.
 
Thanks,
Shri

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130516.1 | Last Updated 5 Sep 2002
Article Copyright 2002 by Stephane Rodriguez.
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid