|
![]() |
| 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:
![]() | ![]() |