cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
knoepdan
Level 6

Populate Combobox dynamically with values from file

Hi

In my Basic MSI project i have a combobox (could also be a listbox) that i would like to populate with items that are read in from a already existing file with a certain format. However, i only manage to populate the combobox via the InstallShield wizard, which - unfortunately - does fullfill the requirements..

Is there a way to populate the combobox items using a file (custom actions, events, etc....)? Any help is greatly appreciated.

Thanks in advance

Daniel
Labels (1)
0 Kudos
(7) Replies
RobertDickau
Flexera Alumni

As far as the populating goes, please see the help topic "Windows Installer API Functions Example" for an example in InstallScript. (You'll find VBScript examples in the tips & tricks archive, and C examples here and there.) You should be able to mix in your file-reading code with that...
0 Kudos
gavin_landon
Level 6

Are you having problems with opening a file, loading a combo, or both?

At a quick glance, there are methods called OpenFile() and GetLine().

There are also methods called ListCreate(STRINGLIST) & ListAddItem() to add them to a the list var, then use CtrlSetList() to set the values to a combobox.

It would look something like this:

szExampleFile = "Readme.txt";
szExampleDir = "C:\\Tester";
OpenFileMode (FILE_MODE_NORMAL);
if (OpenFile (nvFileHandle, szExampleDir, szExampleFile) < 0) then
MessageBox ("OpenFile failed.", SEVERE);
abort;
endif;

listID = ListCreate(STRINGLIST);
if (listID = LIST_NULL) then
MessageBox ("Unable to create list.", SEVERE);
abort;
endif;

while GetLine (nvFileHandle, svLine) = 0
ListAddString (listID, svLine, AFTER);
endwhile;

nResult = CtrlSetList (szDialogName, RES_DIALOG_COMBOBOX, listID);

ListDestroy (listID);


Hope that helps.
0 Kudos
RobertDickau
Flexera Alumni

(Actually, going down that road, ListReadFromFile might be better.)
0 Kudos
gavin_landon
Level 6

That sounds like a winner.. So many API's.. I can't keep up.
0 Kudos
knoepdan
Level 6

Thanks a lot for your replies. I have not yet found the time to include anything in my project. My initial goal was to have everything coded in C#, however, if this impossible i will resort to installScript.

Greetings
0 Kudos
knoepdan
Level 6

Hi

i found a solution that works for me and populates that combobox in standard msi dialog. It makes use of an external dll to read in the file values. The crucial code parts are to be found in the function: AddListBoxRecord



#include "ifx.h"

// public functions
export prototype TestFillCombobox(HWND);


// helper functions
prototype string ReturnSupportFileDir(HWND); // returns location of current support file (dont use SUPPORTDIR or GetSupportDir function
prototype LoadInstallAssist( BOOL, HWND );
prototype InstallAssistError( SHORT, STRING, STRING );
prototype AddListBoxRecord(STRING, INT, STRING, STRING, HWND) ;

// external DLL's -----------------------------------------------------------
// InstallAssist

prototype CDECL SHORT InstallAssist.InstallAssist.GetNumberOfEntries();();
prototype CDECL SHORT InstallAssist.GetLanguageDescription( INT, BYREF
#define PROPERTY "LANGUAGEPROPERTY" // must comply with the property set for the combobox we want to set




function TestFillCombobox(hMSI)
//================================================================
STRING szLangName, szIdDotNet, szDllName, szHelpFileName, szHelpLangIdDotNet; // we only need szLangName, the rest is to comply with dll interface
NUMBER nResult;
SHORT nsResult, nReturn, nsNumberOfLanguages;
INT iCount;
STRING szQuery;
HWND hDataBase, hView;

begin

LoadInstallAssist(TRUE,hMSI); // via external dll we read the file that resides in the same folder as the InstallAssists dll ( // szFileName = ReturnSupportFileDir(hMSI) ^ "TestLanguageFile.TXT")

nsNumberOfLanguages = InstallAssist.GetNumberOfEntries();

// prepare access to msi db
szQuery = "SELECT * FROM ComboBox WHERE Property ='"+PROPERTY +"'";
hDataBase = MsiGetActiveDatabase(hMSI);

if(!hDataBase) then
return ERROR_INSTALL_FAILURE;
endif;

nReturn = MsiDatabaseOpenView(hDataBase, szQuery, hView);

if(nReturn != ERROR_SUCCESS) then
MsiCloseHandle(hDataBase);
return ERROR_INSTALL_FAILURE;
endif;

// now fill combobox
for iCount = 0 to ( nsNumberOfLanguages - 1 )
nsResult = InstallAssist.GetLanguageDescription( iCount, szLangName, szIdDotNet, szDllName, szHelpFileName, szHelpLangIdDotNet );
// MessageBox (szLangName, INFORMATION );
if ( nsResult = 0 ) then
InstallAssistError ( nsResult, "DialogEnterLanguage, GetLanguageDescription", "SEVERE" );
else
// add it to list
AddListBoxRecord(PROPERTY, iCount+1, szLangName, szLangName, hView);
endif;
endfor;

LoadInstallAssist(FALSE,hMSI);
end;



function string ReturnSupportFileDir(hMSI)
string supportDir;
number buffer;
begin
buffer=256;
MsiGetProperty(hMSI, "SUPPORTDIR", supportDir, buffer);
return supportDir;
end;




// helper functions

function LoadInstallAssist( bLoadIt, hMSI )
//===================================
begin


// MessageBox( SUPPORTDIR , SEVERE) ; //fails because files are not yet copied to support folder!!
// MessageBox( GetSupportDir() , SEVERE) ; //fails because files are not yet copied to support folder!! GetSupportDir();
// MessageBox( ReturnSupportFileDir(hMSI) ^ "InstallAssist.dll", SEVERE) ; // is ok
if bLoadIt then
if ( UseDLL ( ReturnSupportFileDir(hMSI) ^ "InstallAssist.dll" ) != 0 ) then
MessageBox ( @STR_CANNOT_LOAD_INSTALLASSIST_DLL, SEVERE );
abort;
endif;
else
UnUseDLL ( ReturnSupportFileDir(hMSI) ^ "InstallAssist.dll" );
endif;
end;


function InstallAssistError( nsResult, szFunction, szMessageType )
//================================================================
STRING szResult, szMsg;
begin

Sprintf (szMsg, @STR_INSTALLASSIST_ERROR, nsResult, szFunction);

if szMessageType = "Severe" then
MessageBox ( szMsg + @STR_INSTALLASSIST_MUST_EXIT, SEVERE );
abort;
elseif szMessageType = "Warning" then
MessageBox ( szMsg, WARNING );
else
MessageBox ( szMsg, INFORMATION );
endif;

end;


function AddListBoxRecord(szProperty, nOrder, szValue, szText, hView)
NUMBER nReturn;
HWND hRecord;
begin

hRecord = MsiCreateRecord(4);

if(!hRecord) then
return ERROR_INSTALL_FAILURE;
endif;

MsiRecordSetString(hRecord, 1, szProperty);
MsiRecordSetInteger(hRecord, 2, nOrder);
MsiRecordSetString(hRecord, 3, szValue);
MsiRecordSetString(hRecord, 4, szText);

nReturn = MsiViewModify(hView, MSIMODIFY_INSERT_TEMPORARY, hRecord);

if(nReturn != ERROR_SUCCESS) then
MsiCloseHandle(hRecord);
return ERROR_INSTALL_FAILURE;
endif;

MsiCloseHandle(hRecord);

return ERROR_SUCCESS;

end;
0 Kudos
knoepdan
Level 6

I also found a vb solution. Be aware that this is for a msi project using msi dialogs (and not some created using installScript):

Function GetNetworkDrives()

Set objInstaller = Session.Installer
Set objDB = Session.Database

Set objView = objDB.OpenView("SELECT * FROM `ComboBox` WHERE `Property` = 'TESTPROPERTY'")
objView.Execute

j = GetStartingIndex(objView)

Set objRec = objInstaller.CreateRecord(4)
Set WshNetwork = CreateObject("WScript.Network")
Set objDrives = WshNetwork.EnumNetworkDrives


Const ForReading = 1
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objTextFile = objFSO.OpenTextFile _
("C:\InstallShield 2009 Projects\FilesForFillDialogTestProject\TESTFILE.TXT", ForReading)


Do Until objTextFile.AtEndOfStream
strNextLine = objTextFile.Readline
strTrimmed = Trim(strNextLine)
If Not strTrimmed = "" Then ' we dont handle empty strings

objRec.StringData(1) = "TESTPROPERTY"
objRec.IntegerData(2) = j
objRec.StringData(3) = CStr(j)
objRec.StringData(4) = strTrimmed
objView.Modify 7, objRec
objRec.ClearData
j = j + 1

END If
Loop

End Function



Function GetStartingIndex(view)

' The index will never be less than 1
j = 1

' We need to loop through the view to make sure
' that there are not any existing records
' for our particualr property
' There is no way to get the number of records
' in a view except to loop through the view
' and increment a counter
Do
Set rec = view.Fetch
j = j + 1
Loop Until rec Is Nothing

GetStartingIndex = j

End Function


I hope this may be of use to somebody.
I still would be glad if someone found a way to do this things in C#, but i believe this is not (yet) possible. I welcome any better solutions, remarks, suggestions.

Greetings
0 Kudos