Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1]   Вниз
  Печать  
Автор Тема: OpenFileDialog и загадки природы  (Прочитано 17181 раз)
0 Пользователей и 1 Гость смотрят эту тему.
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« : 25-07-2012 17:30 » 

Есть у нас приложение, интенсивно работающее с памятью и DirectX (т.е. в том числе видеопамятью).

И вот стоит в этом приложении вызвать стандартный OpenFileDialog в multiselect режиме, выбрать пару файлов в некоторых (но не всех подряд) папках, как стек процесса разрушается, а затем происходит глобальный сбой видеодрайвера и кратковременное отключение монитора до перезагрузки видеоподсистемы Windows. Причём наблюдается это не на всякой машине. Без вызова OpenFileDialog всё работает стабильно.

0) Отчего сие?

Это вопрос философский, риторический. Живо напоминает:
Цитата: м/ф "Трое из Простоквашино"
Мои папа и мама! Я живу хорошо, просто замечательно. У меня все есть, есть свой дом, он теплый. В нем одна комната и кухня. Я без вас очень скучаю, особенно по вечерам. А здоровье мое не очень. То лапы ломит, то хвост отваливается. А на днях я линять начал. Старая шерсть с меня сыпется, хоть в дом не заходи. Зато новая растет чистая, шелковистая, так что лохматость у меня повысилась. До свидания, ваш сын дядя Шарик.
В любом случае такое поведение предвосхищает ожидания заказчика...

1) Есть ли какие-то способы поправить дело без написания собственного диалога?
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
RXL
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #1 : 25-07-2012 19:22 » 

Дим, а данный кусочек кода покажешь?

Версия винды? Битность? Версия .NET?
« Последнее редактирование: 25-07-2012 19:28 от RXL » Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #2 : 25-07-2012 19:55 » 

в WinAPI  (вернее, я из-под MFC) такое тоже случается именно с этим же диалогом, если неправильно задать параметры в конструкторе и структуре OPENFILENAME. Можно неплохо нарваться на неприятные вещи )

А именно, следующий момент:
http://msdn.microsoft.com/en-us/library/wh5hz49d(v=vs.80).aspx

Цитата
Remarks
Either a File Open or File Save As dialog box is constructed, depending on the value of bOpenFileDialog.

To allow the user to select multiple files, set the OFN_ALLOWMULTISELECT flag before calling DoModal. You need to supply your own filename buffer to accommodate the returned list of multiple filenames. Do this by replacing m_ofn.lpstrFile with a pointer to a buffer you have allocated, after constructing the CFileDialog, but before calling DoModal. Additionally, you must set m_ofn.nMaxFile with the number of characters in the buffer pointed to by m_ofn.lpstrFile. If you set the maximum number of files to be selected to n, the necessary buffer size is n*(_MAX_PATH + 1) + 1.

надеюсь, помог )
« Последнее редактирование: 26-07-2012 16:03 от Алексей1153++ » Записан

Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #3 : 26-07-2012 12:30 » 

Алексей1153++, да, в MFC-версии у нас то же самое. И описанный в заметке "хак" с буфером приделан.

RXL, ну что там за кусочек... Самый обычный мирный кусочек:
Код: (C#)
            OpenFileDialog dialog = new OpenFileDialog();
            dialog.Multiselect = true;
            dialog.SupportMultiDottedExtensions = true;
            dialog.AutoUpgradeEnabled = true;
            dialog.Filter = "DICOM slices (*.dcm)|*.dcm|All files (*.*)|*.*";
            dialog.FilterIndex = 0;
            dialog.RestoreDirectory = true;
            dialog.CheckPathExists = true;
            dialog.CheckFileExists = true;
            dialog.ValidateNames = true;
            DialogResult result = dialog.ShowDialog(this);
            if (result == DialogResult.OK)
            {
            // ...
Из которого лезет такое...

Попробую на WinAPI.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #4 : 26-07-2012 16:11 » 

Цитата
dialog.Filter = "DICOM slices (*.dcm)|*.dcm|All files (*.*)|*.*";
не знаю, как шарповая,но винапишная версия требует наличия 0-терминаторов после каждой подписи и маски + ещё один дополнительный в конце

то есть так:
"DICOM slices (*.dcm)\0*.dcm\0All files (*.*)\0*.*\0\0";

(ну да, синий терминатор тут показан для наглядности, на самом деле его вставит компилятор, так как это строковая константа)

Цитата
dialog.FilterIndex = 0;
а тут индекс должен начинаться от 1 , а не от 0
« Последнее редактирование: 27-07-2012 02:48 от Алексей1153++ » Записан

Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #5 : 26-07-2012 16:34 » 

Алексей1153++, не, разделители там правильные, а насчёт индекса согласен, только не помогает.

База на WinAPI + STL
Код: (C++)
#include <iostream>
#include <list>
#include <string>
#include <Windows.h>

using namespace std;

int main()
{
        const int bufferSize = MAX_PATH * 1000;
        wchar_t buffer[bufferSize];
        ZeroMemory(buffer, sizeof(buffer));
        OPENFILENAMEW openFileName;
        openFileName.lStructSize = sizeof(OPENFILENAMEW);
        openFileName.hwndOwner = NULL;
        openFileName.lpstrFile = buffer;
        openFileName.lpstrFile[0] = L'\0';
        openFileName.nMaxFile = bufferSize;
        openFileName.lpstrFilter = L"DICOM slices (*.dcm)\0*.dcm\0All\0*.*\0";
        openFileName.nFilterIndex = 1;
        openFileName.Flags = OFN_ALLOWMULTISELECT | OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_EXPLORER;
        openFileName.FlagsEx = 0;
        openFileName.hInstance = NULL;
        openFileName.lCustData = NULL;
        openFileName.lpfnHook = NULL;
        openFileName.lpstrCustomFilter = NULL;
        openFileName.lpstrDefExt = NULL;
        openFileName.lpstrFileTitle = NULL;
        openFileName.lpstrInitialDir = NULL;
        openFileName.lpstrTitle = NULL;
        openFileName.lpTemplateName = NULL;
        openFileName.nFileExtension = 0;
        openFileName.nFileOffset = 0;
        openFileName.nMaxCustFilter = 0;
        openFileName.nMaxFileTitle = 0;
        openFileName.pvReserved = NULL;
        openFileName.dwReserved = 0;
        BOOL result = GetOpenFileNameW(&openFileName);
        if(result != 0)
        {
                wstring path;
                list<wstring> files;
                int i = 0;
                for(; openFileName.lpstrFile[i] != NULL; i += 1)
                {
                        wstring file;
                        for(; openFileName.lpstrFile[i] != NULL; i += 1)
                        {
                                file.push_back(openFileName.lpstrFile[i]);
                        }
                        if(path.size() == 0)
                        {
                                path = file;
                        }
                        else
                        {
                                wstring fullPath = wstring(path).append(L"\\").append(file);
                                files.push_back(fullPath);
                        }
                }
                if(files.size() == 0 && path.size() > 0)
                {
                        files.push_back(path);
                }
                for(list<wstring>::iterator i = files.begin(); i != files.end(); i++)
                {
                        wcout << *i << endl;
                }
        }
        wcout << L"Press any key for exit" << endl;
        getchar();
        return 0;
}
Теперь это же надо повторить на C#...
« Последнее редактирование: 26-07-2012 16:37 от Dimka » Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
RXL
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #6 : 26-07-2012 16:41 » 

Dimka, я нашел в англоязычном инете три упоминания проблемы, аналогичной твоей. Решения нигде не предложено.
Зато есть полно примеров работы с OpenFileDialog - попробуй сравнить свой код, может найдешь отличие.
Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
zubr
Гость
« Ответ #7 : 26-07-2012 17:07 » 

Возможно тут проблема в поле openFileName.hInstance. Давно как-то что то похожее у меня было, но там я эту функцию вызывал в dll. Проблема решилась, когда я в поле .hInstance подставил .hInstance основного приложения.
Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #8 : 26-07-2012 17:54 » 

RXL, я с этим OpenFileDialog работал неоднократно. И один раз с ним была какая-то проблема - генерировался внутренний Exception, что на что-то влияло. Теперь же рушится стек. В .NET классе я к полям структуры доступа не имею. Так что без вариантов WinAPI.

Вот перевёл на C# вышеописанное.
Код: (C#)
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;

namespace Test
{
    class OpenFileDialog
    {
        private const uint OFN_ALLOWMULTISELECT = 0x200;
        private const uint OFN_PATHMUSTEXIST = 0x800;
        private const uint OFN_FILEMUSTEXIST = 0x1000;
        private const uint OFN_EXPLORER = 0x80000;
        private const int BufferSize = 260000;

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        private struct OpenFileName
        {
            public uint lStructSize;
            public IntPtr hwndOwner;
            public IntPtr hInstance;
            public string lpstrFilter;
            public string lpstrCustomFilter;
            public uint nMaxCustFilter;
            public uint nFilterIndex;
            [MarshalAs(UnmanagedType.BStr)]
            public string lpstrFile;
            public uint nMaxFile;
            public string lpstrFileTitle;
            public uint nMaxFileTitle;
            public string lpstrInitialDir;
            public string lpstrTitle;
            public uint Flags;
            public ushort nFileOffset;
            public ushort nFileExtension;
            public string lpstrDefExt;
            public IntPtr lCustData;
            public IntPtr lpfnHook;
            public string lpTemplateName;
            public IntPtr pvReserved;
            public uint dwReserved;
            public uint FlagsEx;
        }

        [DllImport("comdlg32.dll", EntryPoint = "GetOpenFileNameW")]
        private static extern bool GetOpenFileName(ref OpenFileName openFileName);

        private List<string> fileNames;
        private OpenFileName openFileName;

        public OpenFileDialog()
        {
            this.openFileName = new OpenFileName();
            this.openFileName.dwReserved = 0;
            this.openFileName.Flags = OFN_ALLOWMULTISELECT | OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST;
            this.openFileName.FlagsEx = 0;
            this.openFileName.hInstance = IntPtr.Zero;
            this.openFileName.hwndOwner = IntPtr.Zero;
            this.openFileName.lCustData = IntPtr.Zero;
            this.openFileName.lpfnHook = IntPtr.Zero;
            this.openFileName.lpstrCustomFilter = null;
            this.openFileName.lpstrDefExt = null;
            this.openFileName.lpstrFile = new string('\0', BufferSize);
            this.openFileName.lpstrFileTitle = null;
            this.openFileName.lpstrFilter = "DICOM slices (*.dcm)\0*.dcm\0All (*.*)\0*.*\0";
            this.openFileName.lpstrInitialDir = null;
            this.openFileName.lpstrTitle = null;
            this.openFileName.lpTemplateName = null;
            this.openFileName.lStructSize = Convert.ToUInt32(Marshal.SizeOf(this.openFileName));
            this.openFileName.nFileExtension = 0;
            this.openFileName.nFileOffset = 0;
            this.openFileName.nFilterIndex = 1;
            this.openFileName.nMaxCustFilter = 0;
            this.openFileName.nMaxFile = BufferSize;
            this.openFileName.nMaxFileTitle = 0;
            this.openFileName.pvReserved = IntPtr.Zero;
            this.fileNames = new List<string>();
        }

        public bool ShowDialog()
        {
            bool result = GetOpenFileName(ref this.openFileName);
            if (result)
            {
                int i = 0;
                string path = string.Empty;
                string[] parts = this.openFileName.lpstrFile.Split(new char[] { '\0' }, StringSplitOptions.RemoveEmptyEntries);
                foreach (string part in parts)
                {
                    if (path.Length == 0)
                    {
                        path = part;
                    }
                    else
                    {
                        string fullPath = path + "\\" + part;
                        this.fileNames.Add(fullPath);
                    }
                }
                if (this.fileNames.Count == 0 && path.Length > 0)
                {
                    // Selected only 1 file. Its full path is stored into path.
                    this.fileNames.Add(path);
                }
            }
            return result;
        }

        public string[] FileNames
        {
            get
            {
                return this.fileNames.ToArray();
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            OpenFileDialog dialog = new OpenFileDialog();
            if (dialog.ShowDialog())
            {
                foreach (string fileName in dialog.FileNames)
                {
                    Console.WriteLine(fileName);
                }
            }

            Console.WriteLine("Press any key for exit.");
            Console.ReadKey();
        }

    }

}
Завтра буду испытывать.
« Последнее редактирование: 26-07-2012 18:15 от Dimka » Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #9 : 27-07-2012 03:14 » 

в АПИшном варианте из поста #5 как раз легко завалить стек - как размером буфера, так и выпрыгиванием за край этого буфера (если это происходит в недрах GetOpenFileName)

я бы вот так написал
Код: (C++)
        std::wstring buffer(MAX_PATH * 1000,0);

        OPENFILENAMEW openFileName={0};
        openFileName.lStructSize = sizeof(openFileName);
        openFileName.lpstrFile = &buffer[0];
        openFileName.nMaxFile = buffer.size();
        openFileName.lpstrFilter = L"DICOM slices (*.dcm)\0*.dcm\0All\0*.*\0";
        openFileName.nFilterIndex = 1;
        openFileName.Flags = OFN_ALLOWMULTISELECT | OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_EXPLORER;

        BOOL result = GetOpenFileNameW(&openFileName);
        if(result != 0)
        {
                std::wstring path;
                std::list<std::wstring> files;
               
                for(int i=0; openFileName.lpstrFile[i]; i++)
                {
                        std::wstring file=&openFileName.lpstrFile[i];
                        i+=file.size();

                        if(path.empty())
                        {
                                path = file;
                        }
                        else
                        {
                                std::wstring fullPath = std::wstring(path).append(L"\\").append(file);
                                files.push_back(fullPath);
                        }
                }

                if(files.size() == 0 && path.size() > 0)
                {
                        files.push_back(path);
                }
                for(std::list<std::wstring>::iterator i = files.begin(); i != files.end(); i++)
                {
                        std::wcout << *i << std::endl;
                }
        }
        std::wcout << L"Press any key for exit" << endl;
        getchar();
        return 0;
Записан

Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #10 : 27-07-2012 06:06 » 

Алексей1153++, ты хочешь сказать, что API-функция умеет грамотно писать в string? Ей же буфер нужен. В этом-то всё и дело, ради этого-то и нужно обращение к WinAPI - подставить заранее выделенный буфер достаточного размера.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #11 : 27-07-2012 06:14 » 

А, понял. Ты про то, что буфер на стеке выделяется.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #12 : 27-07-2012 06:57 » 

Не, не помогает WinAPI версия. И подставновка hInstance тоже.

Более того, был придуман тест, на котором валится без вызова этого диалога. Значит просто звёзды так сошлись, что наблюдалась устойчивая корреляция между вызовом диалога и обрушением стека. А проблема не в диалоге.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #13 : 27-07-2012 08:47 » 

Алексей1153++, ты хочешь сказать, что API-функция умеет грамотно писать в string? Ей же буфер нужен.
я выделил размер wstring заранее

Более того, был придуман тест, на котором валится без вызова этого диалога.

это значит, что стек ещё раньше задет. Я тоже на такое нарывался (причём индикатором оказался именно этот диалог Отлично )  У меня тогда, помнится, было неправильное выравнивание (пропустил #pragma(pop) после #pragma(push,1) )  после чего в одном месте кода структура была одного размера, в другом - другого (в разных файлах реализации).  Возможно ли это в шарпе допустить - я не знаю )

как это обнаружил , когда искал: в одну из функцию передаю одни значения переменных, а при входе обнаруживаю там другие
Записан

Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #14 : 27-07-2012 08:53 » new

Алексей1153++, на C# написан только GUI. Основная же обработка - на C++ в dll. Выравниваний в коде нету. Но что-то сломать стек может. С учётом того, что писалось это не очень-то качественно.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #15 : 27-07-2012 08:59 » 

для теста - выставь в настройках проекта выравнивание на 1 байт. Если заработает без глюков, то дело в этом
Записан

Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #16 : 27-07-2012 09:48 » 

Алексей1153++, мысль насчёт выравниваний оказалась полезной. Только не в той степи. Улыбаюсь Есть выравнивание при работе с видеопамятью - выравнивание данных по степеням двойки. Валится только тогда, когда размер исходных данные не является степенью двойки. Какой-то там баг сидит. Будем копать...
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #17 : 27-07-2012 15:46 » 

это может быть:
1) размер текстур
2) размер первичной поверхности
Записан

Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines