|
Hi,
I am writing a small program with some zip archiving functionality (using the ZipArchive library from www.artpol-software.com). I would like to add a list box to one of the dialog boxes and a browse button for multiple file selection, but am not sure how to go about multiple file selection at all. I would like it so that the user can click on the Browse... button and select any number of files. Upon clicking "open", the list of files will then appear in the list box above the browse button (and the list must be stored so that when the user clicks on "Zip", all of those files will get zipped up). I would also like to make it so that the user can select all of the files in the list box, or select and remove individual files... However, I am getting ahead of myself with that. First, I need to find out how to create a multiple selection procedure. (I am of course using the Windows API and *no* MFC.)
The following code opens a Browse box which allows users to select multiple files (using OFN_EXPLORER and OFN_ALLOWMULTISELECT):
[code]
BOOL OpenMultipleFiles(CHAR *filename, int len, HWND hWnd)
{
OPENFILENAME files;
ZeroMemory(&files, sizeof(OPENFILENAME));
files.lStructSize = sizeof(OPENFILENAME);
files.hwndOwner = hWnd;
files.lpstrFilter = "All Files (*.*)\0*.*\0\0";
files.lpstrFile = filename;
files.nMaxFile = len;
files.lpstrTitle = "Select Files";
files.Flags = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_EXPLORER | OFN_ALLOWMULTISELECT;
return GetOpenFileName(&files);
}
[/code]
This allows multiple section, though I am not sure what to do with the returned string, nor whether I am doing it right, as I have just modified the above code from code designed for opening a single file.
I managed to find the following code, designed to get the information returned from a multipe file selection box, from http://www.angelfire.com/biz/rhaminisys/tricks.html:
[code]
if ( ofn.nFileOffset < lstrlen(Buffer) )
AddToFileList( Buffer );
else
{
strcpy( FilePath, Buffer );
FilePath[ofn.nFileOffset] = 0;
while ( Buffer[ofn.nFileOffset] != 0 )
{
strcpy( Message, FilePath );
strcat( Message, "\\" );
strcat( Message, Buffer+ofn.nFileOffset );
AddToFileList( Message );
ofn.nFileOffset += (strlen(Buffer+ofn.nFileOffset) + 1);
}
}
[/code]
Unfortunately, I have no idea of how to put all this code and information together! For a start, "Buffer" in the above code is a buffer designed to hold all of the file names. But I have no idea how to define buffer. EG:
char Buffer[MAX_PATH];
doesn't work, because MAX_PATH is not big enough to hold the names of, say, 50 odd files plus a filepath.
If anyone could tell me how I could create a multiple file selection function that allows my users to select a number of files for inclusion in their .zip file, and a function that then allows me to access the returned information (ie. getting the names of all the files individually), I would be really, really grateful. (One thing that occurs to me is that wouldn't using ofn.nFileOffset only work when first selecting the files, and not later when I need to get all the file names for zipping?)
Oh, and I am a novice at this stuff, btw, so if you can see I'm heading down the wrong path completely, please tell me, and feel free to overexplain as though I were an idiot.
Many thanks,
KB
|
|
|
|
|
void OnBrowse( void )
{
OPENFILENAME files = {0};
char szBuffer[32000] = {0},
szMessage[MAX_PATH],
FilePath[MAX_PATH];
files.lStructSize = sizeof(OPENFILENAME);
files.hwndOwner = GetSafeHwnd();
files.lpstrFilter = "All Files (*.*)\0*.*\0";
files.lpstrFile = szBuffer;
files.nMaxFile = sizeof(szBuffer);
files.lpstrTitle = "Select Files";
files.Flags = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_EXPLORER | OFN_ALLOWMULTISELECT;
if (GetOpenFileName(&files) == TRUE)
{
if (files.nFileOffset < lstrlen(szBuffer))
m_lbFiles.AddString(szBuffer);
else
{
strcpy(FilePath, szBuffer);
FilePath[files.nFileOffset] = 0;
while (szBuffer[files.nFileOffset] != 0)
{
strcpy(szMessage, FilePath);
strcat(szMessage, "\\");
strcat(szMessage, szBuffer + files.nFileOffset);
m_lbFiles.AddString(szMessage);
files.nFileOffset += (strlen(szBuffer + files.nFileOffset) + 1);
}
}
}
}
|
|
|
|
|
Hi,
Many thanks for this!
Unfortunately, I'm getting a few errors:
GetSafeHwnd() is an undeclared identifier...
m_lbFiles is an undeclared identifier...
How do I declare these to get it to work? (Sorry if this is really obvious to you but I am still a novice.)
Presumably (if I can work out how to define it), m_lbFiles is a list of all the filenames selected, is that right? How would I then use that file list?
Sorry again for the dumb questions.
Many thanks,
KB
|
|
|
|
|
Kayembi wrote:
GetSafeHwnd() is an undeclared identifier...
It is a method of CWnd. If you are not using MFC, just use whatever window handle is appropriate.
Kayembi wrote:
m_lbFiles is an undeclared identifier...
It is a control variable for a listbox. Again, this is an MFC thing. You can add items to a listbox without MFC by using SendMessage(hwnd_of_listbox, LB_ADDSTRING, 0, (LPARAM) string_to_add) .
Kayembi wrote:
Sorry again for the dumb questions.
Just because you are a beginner does not make the questions dumb.
|
|
|
|
|
Ahhh... (Slaps forehead with palm)... That works perfectly, thank you! Now I have multiple files selected and displayed in a listbox.
Presumably to then use this list and zip up the selected files, I just use LB_GETCOUNT and then use LB_GETTEXT in a while() loop until all files from the list have been added... That should be fairly straightforward.
All I have to figure out then is how to store the list in a file (so that projects can be saved and loaded, ie. so the list can be reloaded into the listbox).
Many, many thanks for your help - I've spent ages trying to work this out, and you've got me up and running within a couple of hours.
Cheers,
KB
|
|
|
|
|
Hi,
This is probably going to seem a very dumb and easy question to many (I hope!), but I am a novice with C++ so please bear with me...
I have an application that saves certain settings to file, and loads them on opening. All of these settings are just simple strings and integers, so at the moment I just save them to a (renamed) INI file using WritePrivateProfileString() and load them using GetPrivateProfileString() and GetPrivateProfileInt(). However, using these instructions means that anyone using my program can open the INI file in a text editor and look at the stored information. Ideally, I would like to create saved files that can't be opened in a text editor.
Could anybody tell me how to do this, please? (I am using the Windows API but *not* using MFC.) If somebody could show me a function (or tell me where to look) that creates a save file, and then can save strings and integers to it, and also a function on how I would then read the information back (load it), I would be very grateful. (One advantage with using the PrivateProfile functions is that I can read whatever value I want arbitrarily without having to load the info of the whole fle - can this functionality be maintained in a custom file?)
Many thanks,
KB
|
|
|
|
|
CreateFile in MSDN has all your answers for creating and opening files. WriteFile and ReadFile can do just what you want with them. When you master that, get onto File Mapping in memory for fast access to file data
|
|
|
|
|
I think you can use register after i watched your requirement .
i'm a software engineer
|
|
|
|
|
Kayembi wrote:
However, using these instructions means that anyone using my program can open the INI file in a text editor and look at the stored information.
Is this a bad thing? If the settings are related to your application, what's the harm?
Ideally, Win32 programs use the registry instead of text-based INI files. It's no more secure, at least not to any experienced person, but it does give your application more flexibility.
|
|
|
|
|
Thanks for the replies.
I can live with INI files - it's not really a bad thing - but I just think it would be a little more "professional" if people can't look in the file. Also, I may need to store a password in there, in which case (although I could encrypt it in an INI file), a non-readable file would be better.
Thanks again for the replies, I'm looking into all the suggestions on the MSDN site and trying to work it all out... It's still a bit complex for me as I'm still learning, but I'm trying...
Cheers,
KB
|
|
|
|
|
Kayembi wrote:
but I just think it would be a little more "professional" if people can't look in the file.
Sometimes editing a file outside the app is the only way to get it to work.
Kayembi wrote:
Also, I may need to store a password in there...
A very bad design.
|
|
|
|
|
"A Very bad design" - you're right; I think I have eliminated the need for a password to be stored in the file, fortunately.
Maybe you're right about leaving the files as INI files... The main reason I want to do it is that there is information in there that, if changed, will definitely cause the program to crash, or at least behave oddly. This is probably bad design again... Essentially I am writing a launcher app and a wizard. Through the wizard, users can choose different styles and settings for the launcher app. It then copies the launcher app, with the files it is launching, and the settings file (which is the file I am trying to save/load, currently an INI) to a new directory. Thus all of the settings they chose through the wizard (JPG to display while launching, titlebar name etc) are all saved in the INI file, and the launcher reads that in order to find the files it need to launch and to find the picture files it needs to display, etc.
In an ideal world there would be no need for an INI or saved file at all - all the information would be stored inside a newly generated launcher executable, along with the JPG files that have been selected for use by the launcher. This would be tidier and more professional, but as I have no idea where to begin in trying something like that, I have to instead go the route of external files and an INI (or save file of another format).
Many thanks for all your help, I really appreciate it,
KB
|
|
|
|
|
Kayembi wrote:
The main reason I want to do it is that there is information in there that, if changed, will definitely cause the program to crash, or at least behave oddly. This is probably bad design again...
A well-behaved app would indeed not crash, but should instead detect when "bad data" is present and respond accordingly. For example, if you were storing the window coordinates of the app when it was last shut down (t=0, r=1024, b=768, l=0) so that it could be positioned in the same spot next time, and someone changed those coordinates to something non-sense (t=768, r=1024, b=0, l=0), the app would know to correct the problem, or use some default values at the very least.
|
|
|
|
|
Thanks. I guess I need to add lots of error-handling code to sort this out. I ought to get into good habits now, so thanks for the advice.
Cheers,
KB
|
|
|
|
|
Hi,
I'm trying to use a timer call back function but I can't get it to compile.
I set up the timer for a 1 second interval like this:-
m_uSampleTimerID = SetTimer( 1,1000,TimerTakeSample );
My function is declared like this:-
void CALLBACK EXPORT COxygraphView::TimerTakeSample(HWND hWnd, UINT nMsg, UINT nTimerID, DWORD dwTime)
BUT the SetTime command gives the following error:-
:\Oxygraph\OxygraphView.cpp(575) : error C2664: 'SetTimer' : cannot convert parameter 3 from 'void (struct HWND__ *,unsigned int,unsigned int,unsigned long)' to 'void (__stdcall *)(struct HWND__ *,unsigned int,unsigned int,unsigned l
ong)'
This is all in the View part of an MFC app. It doesn't seem to like the return type of the function.
Any ideas what's wrong with it?
Thanks
Ali
|
|
|
|
|
Hey can i suggest u one thing..
Check out if u like this thing
instead of calling a call back function
just go to class wizard map the WM_TIMER for the view class
then call the set timer function with parameters(1000, timeasperuwant, NULL)
in this case the Functiopn mapped for WM_TIMER will be called..
please check out and let me know
bye
Thanx
TAKE CARE
|
|
|
|
|
this thing will work if u calling the set timer function in the view class itself..
Thanx
TAKE CARE
|
|
|
|
|
Thanks for the suggestion. In fact I have got my code working like that at the moment, but I have several timers and I wanted to assign each one its own handling function.
Thanks anyway
Ali
|
|
|
|
|
The problem is with the declaration of your TimerTakeSample function. I think its probably got something to do with that EXPORT (try removing it?) because the CALLBACK simply defines the function type as an _stdcall* and so that should compile.
|
|
|
|
|
Thanks for the suggestion, I've tried it but the compiler still doesn't want to know (still the same error message).
Thanks anyway
Ali
|
|
|
|
|
The actual problem is that the compiler is expecting a void (_stdcall*) (arglist...)function and your function has somehow been defined as a mere void (arglist...) function hence the error. Since the CALLBACK statement defines the (_stdcall*) you should not be having this issue, unless the EXPORT statement is doing something dopy (so try removing it), or the TimerTakeSample referred to in the SetTimer function is being linked to a different function? try specifying COxygraphView::TimerTakeSample(blah...) in your SetTimer function to make sure your linkage is correct (can't tell from your example where the SetTimer is being called from). I'm sure the answer will reveal itself
|
|
|
|
|
The function must be declared as static
Hope this helps,
Ryan "Punctuality is only a virtue for those who aren't smart enough to think of good excuses for being late" John Nichol "Point Of Impact"
|
|
|
|
|
Haha, yes the declaration should be static void CALLBACK etc. well done ryan
|
|
|
|
|
To expand on Ryan's post - it's all to do with pointers to member functions. Take a look at this[^] site - it will explain all!
Phil
|
|
|
|
|
Thanks, the link looks really helpful, looks like I need to do some reading!
Ali
|
|
|
|
|