MAKEMSI quickly and reliably creates MSI files in a non-programmatic way
Have your say! Join the MAKEMSI discussion list or view archive! Suggest improvements. No question too simple or too complex.
[Bottom][Contents][Prev]: Billboards[Next]: Control Removal
Have your say! Join the MAKEMSI discussion list or view archive! Suggest improvements. No question too simple or too complex.
\->Tips and Tricks->User Interface Tips->Browse for File Dialog (in DLL Custom Action)

Browse for File Dialog (in DLL Custom Action)

This uses the "guts" of a DLL custom action by "Kallely L Sajan" (from InstallSite, see "File Browse Dialog" by Jeff Briggs) and the "TryMeDllCustomAction.MM" sample to create a "browse for file" dialog in an msi.

The following code was extracted from Kallely's "BrowseForFile.cpp":

    //***********************************************************
    //** Call back function for EnumChildWindows()
    //**---------------------------------------------------------
    //** Author:    Kallely L Sajan
    //***********************************************************
    BOOL CALLBACK EnumChildProc(HWND hwnd,LPARAM lParam)
    {
      TCHAR buf[100];
      GetClassName( hwnd, (LPTSTR)&buf, 100 );

      if ( _tcscmp ( buf, (_T("RichEdit20W")) ) == 0 )
      {
       *(HWND*)lParam = hwnd;
       return FALSE;
      }
    }

    UINT __stdcall BrowseForFile(MSIHANDLE hInstall)
    {
        //MessageBox(NULL, "BrowseForFile", "BrowseForFile", MB_OK);

        //Set up storage for PATHTOFILE
        TCHAR szOriginalPath[MAX_PATH];
        DWORD cchValue = sizeof(szOriginalPath)/sizeof(TCHAR);
        ZeroMemory(szOriginalPath, sizeof(TCHAR)*MAX_PATH);

        // Get PATHTOFILE
        MsiGetProperty(hInstall, TEXT("PATHTOFILE"), szOriginalPath, &cchValue);

        long    lErrMsg = 0;
        OPENFILENAME ofn;

        ZeroMemory(&ofn, sizeof(ofn));

        TCHAR szFilters[] =
        _T("All Files (*.*)\0*.*\0")
        _T("Text File (*.txt)\0*.txt\0");

        // Initialize OPENFILENAME structure.
        ofn.lStructSize = sizeof(ofn);
        ofn.hwndOwner = GetForegroundWindow();
        ofn.lpstrFile = szOriginalPath;
        ofn.nMaxFile = sizeof(szOriginalPath);
        ofn.lpstrFilter = szFilters;
        ofn.nFilterIndex = 1;
        ofn.lpstrFileTitle = NULL;
        ofn.nMaxFileTitle = 0;
        ofn.lpstrInitialDir = NULL;
        //ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
        //ofn.Flags = OFN_HIDEREADONLY;

        if (GetOpenFileName(&ofn))
        {
            MsiSetProperty(hInstall, TEXT("PATHTOFILE"), szOriginalPath);

            //This next bit of code fixes a problem that occurs if the author is
            //using an Edit box for the path. For whatever reason, if a user
            //types into the edit box before browsing for a file or folder
            //the Edit box is not updated once the custom action completes.
            //Much of the following code was modified from
            //Kallely L Sajan's IsLicensedViewed article from InstallSite.org
            HWND hWnd;
            HWND hWndChild=NULL;

            hWnd = FindWindow(NULL, "Default - InstallShield Wizard");
            if (hWnd) {
                EnumChildWindows( hWnd, EnumChildProc, (LPARAM)&hWndChild );
                if ( hWndChild ) {
                    TCHAR buf[100];
                    _tcscpy(buf, szOriginalPath);
                    //the following call will not work if _UNICODE is defined in Prepocessor definitions
                    SendMessage(hWndChild, WM_SETTEXT, 0, (LPARAM)szOriginalPath);
                }
            }
        }
        return ERROR_SUCCESS;
    }

Insert the above code and the stub below just before the "<$/DllCa-C>" command in "TryMeDllCustomAction.MM":

    //============================================================================
    <$DllCaEntry "Browse4File">
    //============================================================================
    {
        //--- Call Kallely's code ---
        CaDebug(PROGRESS_LOG, "Invoking Kallely's BrowseForFile()");
           UINT BrowseRc;
           BrowseRc = BrowseForFile(hInstall);
        CaDebug(PROGRESS_LOG, "Completed Kallely's BrowseForFile()");
        return( BrowseRc );
    }
    <$/DllCaEntry>

Then we need to schedule the invokation etc by inserting the following immediately after the "<$/DllCa-C>" command in "TryMeDllCustomAction.MM":

;--- Set starting directory and file mask ---
<$PropertyCa "PATHTOFILE" Value="[TempFolder]*.*"                    Seq="CostFinalize-" SeqTable=^InstallUISequence^>

;--- Invoke the DLL ---------------------------------------------------------
<$DllCa Binary="MyTestDll.dll"  Entry=^<$DllCaEntry? "Browse4File">^ Seq="CostFinalize-" SeqTable=^InstallUISequence^ Type="Immediate" Condition="<$DLLCA_CONDITION_INSTALL_ONLY>">

;--- Make a copy (you may wish to use more than once) ---
<$PropertyCa "RETURNED_FILE" Value="[PATHTOFILE]"                    Seq="CostFinalize-" SeqTable=^InstallUISequence^>

;--- Display the returned value (also abort - don't really want to install...) ---
#(
    <$AbortIf
        Condition="<$DLLCA_CONDITION_INSTALL_ONLY>"
          Message=^The DLL returned "[RETURNED_FILE]". Aborting the install now...^
         SeqTable="InstallUISequence"
              Seq="CostFinalize-"
    >
#)

You can then build and test the msi, a verbose log will give you more details.

Improvements

I left Kallely's code exactly as was purely to demonstrate how you can easily grab code and insert it into a dll based custom action.

Some recommended improvements:


Microsoft awarded me an MVP (Most Valuable Professional award) in 2004, 2005, 2006, 2007, 2008 & 2009 for the Windows SDK (Windows Installer) area.Please email me any feedback, additional information or corrections.
See this page online (look for updates)

[Top][Contents][Prev]: Billboards[Next]: Control Removal


MAKEMSI© is (C)opyright Dennis Bareis 2003-2008 (All rights reserved).
Saturday May 28 2022 at 3:11pm
Visit MAKEMSI's Home Page
Microsoft awarded me an MVP (Most Valuable Professional award) in 2004, 2005, 2006, 2007, 2008 & 2009 for the Windows SDK (Windows Installer) area.