0

I need to get the selected files in a OpenFileDialog. For a single file, I'm doing this:

EDIT:

var count = SelectedItems(handle); // handle is the handle to ListView control in OpenFiledialog
var bufferSize = 2048 * count;
if (bufferSize > 0)
{
    var path = new StringBuilder(bufferSize)
    SendMessage(handle, CDM_GETFILEPATH, (IntPtr)path.Capacity, path);
}

And it works fine, but I need to check for multiple files (Multiselect = true). I don't know the buffer size (stringBuilder size).

EDIT: I'm customizing the Open File Dialog. I'm trying to get the selected files before the OFD window is closed.

EDIT: Currently I can have a list of selected files, BUT, only the filenames, without path, I'm using this:

var totalItemsCount = SendMessage(handle, LVM_GETITEMCOUNT, 0, 0);
var fileNames = new List<string>();
var lvi = new LVITEM();
var lviPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(LVITEM)));

for (var i = 0; i < totalItemsCount; i++)
{
    var pcsText = Marshal.AllocHGlobal(1024);

    lvi.iSubItem = 0;
    lvi.cchTextMax = 1024;
    lvi.pszText = pcsText;

    Marshal.StructureToPtr(lvi, lviPtr, fDeleteOld: true);

    var success = SendMessage(handle, LVM_GETITEMTEXT, i, (int)lviPtr);
    if (success > 0)
    {
        var itemState = SendMessage(handle, LVM_GETITEMSTATE, i, 2);
        var selected = (itemState & 2) != 0;
        if (selected = (itemState & 2) != 0)
        {
            lvi = (LVITEM)Marshal.PtrToStructure(lviPtr, typeof(LVITEM));
            var name = Marshal.PtrToStringAnsi(lvi.pszText);
            fileNames.Add(name);
        }
     }

     Marshal.FreeHGlobal(pcsText);
 }

 Marshal.FreeHGlobal(lviPtr);

What I'm doing is to get the selected files directly from the listview, but I need gthe filenames with the path.

When the OpenfileDialog change the folder, I can get the folder path with:

var folderPath = new StringBuilder(256);
SendMessage(handle, CDM_GETFOLDERPATH, (IntPtr)256, folderPath);

But it doesnt work when the folder is an special folder, like the libraries.

Luis
  • 436
  • 4
  • 11
  • 1
    Why are you using the API rather than the nice neat NET object? – Ňɏssa Pøngjǣrdenlarp Apr 18 '18 at 22:05
  • It is a customized FileDialog. I added a control inside the OFD dialog. – Luis Apr 18 '18 at 22:07
  • 1
    Perhaps it would be better to customize the dialog from the C# class as in [this answer](https://stackoverflow.com/a/6093152/2557128) ? – NetMage Apr 18 '18 at 22:12
  • @RufusL for 3 or 4 selected files it is the current folder path and separated file names. For a bigger selection, it is empty, and SendMessage return 1. – Luis Apr 18 '18 at 22:16
  • @NetMage I need to put a custom control inside the OFD, I can't find any IFileDialogCustomize example for winforms. – Luis Apr 18 '18 at 22:18
  • In the C# code for `OpenFileDialog`, they setup a buffer of size 8192 to handle multiple files, and they add the path from the beginning of the buffer to each filename for the answer. Did you follow the link in my comment to see the customized `OpenFileDialog` links in that answer? – NetMage Apr 18 '18 at 22:25
  • @NetMage I´ll read your link. I'll trywith the 8192 size. Thanks. – Luis Apr 18 '18 at 22:27
  • @NetMage the 8192 is not working, I'm reading your links but I can't something about multiple files. – Luis Apr 18 '18 at 22:39
  • Yes, I can parse the string, but when the selection is bigger than 7 elements, the stringbuilder is empty. For 1-6 elements the stringbuilder is ok, with the path and the files names, but for 7+, the result from CDM_GETFILEPATH is empty. – Luis Apr 18 '18 at 22:48
  • Yes, but what if the user select a lot of files? I'm using another method for get the selected files, but that method only return the filenames, without the path, check the edit in the question. – Luis Apr 18 '18 at 22:54
  • Note the code also catches the `CDN_SELCHANGE` message and uses `CDM_GETSPEC` to estimate the buffer size needed when the selection changes (plus 2048, but GETFOLDERPATH is more specific). – NetMage Apr 18 '18 at 22:58
  • I tried with: var count = SelectedItems(handle); where SelectedItems get the count of selected items in the listview (sending the LVM_GETSELECTEDCOUNT and then the LVM_GETITEMSTATE for each ListViewitem) and then, for the buffer size : var bufferSize = 1024 * count; but it is the same, if the selection is for 8 files, the return is an empty string. I'll try with 2048. – Luis Apr 18 '18 at 23:03
  • Where is `bufferSize` in your code? – NetMage Apr 18 '18 at 23:04
  • @NetMage I need to edit the OP question, I'm doing changes in my code trying the suggestions. the 2048 did'mt work, for a selection of 7 of more, the stringbuilder is empty. – Luis Apr 18 '18 at 23:07
  • What does `var path = new StringBuilder(8192) SendMessage(handle, CDM_GETFILEPATH, (IntPtr)path.Capacity, path);` do? How long are your filenames you are testing with? – NetMage Apr 18 '18 at 23:09
  • I've tried with new StringBuilder(8192) and didn't work, well, it only work for 6-7 files. The filenames aren't too big, probably 20-30 characters for each filename. I tried with CDM_GETSPEC, "SendMessage(handle, CDM_GETSPEC, IntPtr.Zero, IntPtr.Zero);" and didn't work. – Luis Apr 18 '18 at 23:12
  • Well, it looks that it doesnt works with 8+ files. I need a workaround. – Luis Apr 18 '18 at 23:46
  • @NetMage Is it possible to get the full path of ListViewItems in the listview of OpenFileDialog? I can get the filename (without the path) but I need the full path – Luis Apr 19 '18 at 00:23

2 Answers2

0

Simple :

  IO.Stream st1

 ///Set MultiSelect property to true
  openfiledialog1.MultiSelect = true;



  ///Now get the filenames
  if (openfiledialog1.ShowDialog() == DialogResult.OK)
  {
    foreach (String file in openfiledialog1.FileNames) 

    {
      try
      {
        if ((st1 = openfiledialog1.OpenFile()) != null)
        {
          using (st1)
          {
           List.Add(file);
          }
       }
    }

Note: Don't know if this will also work with the custom control as well as no code is provided regarding the custom control

Software Dev
  • 5,368
  • 5
  • 22
  • 45
  • Ok, I'll try to get the reference to the OpenFileDialog object. – Luis Apr 18 '18 at 22:10
  • Please let me know if it works ... it might help me in the future:) – Software Dev Apr 18 '18 at 22:10
  • Ok, I'm coding now ;) – Luis Apr 18 '18 at 22:11
  • Not working :(, FileNames has an element with an empty string. – Luis Apr 18 '18 at 22:16
  • Bot I need the selected file before user click Ok, I need the selected files after the user is selecting with the mouse, and before it select Ok. – Luis Apr 18 '18 at 22:32
  • It is not possible unless 1.You modify the opd class itself | or | 2.Make your own opd – Software Dev Apr 18 '18 at 22:35
  • Yes, I'm modifing the OFD. – Luis Apr 18 '18 at 22:46
  • I'm following this article: https://www.codeproject.com/Articles/19566/Extend-OpenFileDialog-and-SaveFileDialog-the-easy – Luis Apr 18 '18 at 22:49
  • Basically I override the WndProc method, check for the WM_NOTIFY message, convert the LParam to OFNOTIFY type, check if the code of OFNOTIFY is CDN_SELCHANGE value, and then send a CDM_GETFILEPATH message to the OpenFileDialog, but it works for 1-6 elements, for a selection of 7 or more files, the return value is empty. – Luis Apr 18 '18 at 22:52
-3

the StringBuilder buffer will autoexpand

  • 1
    No, no it really won't. – NetMage Apr 18 '18 at 22:09
  • But I need to tell the buffer size in the wParam parameter. – Luis Apr 18 '18 at 22:09
  • 1
    wait...how does this even answer the question ? – Software Dev Apr 18 '18 at 22:10
  • @NetMage The StringBuilder will create a new buffer if it overflows. I posted that because it seems the OP didn't understand how StringBuilder worked. Microsoft has a good example of using Multiselect [link](https://msdn.microsoft.com/en-us/library/system.windows.forms.openfiledialog.multiselect(v=vs.110).aspx) – user3830154 Apr 18 '18 at 22:22
  • That uses the stock `OpenFileDialog` class and not a custom control and has no relevance to the question. – NetMage Apr 18 '18 at 22:26
  • Note that the user is calling `SendMessage(handle, CDM_GETFILEPATH, (IntPtr)path.Capacity, path);`. In this example, the method needs the capacity to be passed to it as the 3rd argument, so the `StringBuilder` must be initialized to a capacity beforehand. – Rufus L Apr 18 '18 at 22:59