Dimka
Деятель
Команда клуба
Offline
Пол:
|
|
« : 25-07-2012 17:30 » |
|
Есть у нас приложение, интенсивно работающее с памятью и DirectX (т.е. в том числе видеопамятью). И вот стоит в этом приложении вызвать стандартный OpenFileDialog в multiselect режиме, выбрать пару файлов в некоторых (но не всех подряд) папках, как стек процесса разрушается, а затем происходит глобальный сбой видеодрайвера и кратковременное отключение монитора до перезагрузки видеоподсистемы Windows. Причём наблюдается это не на всякой машине. Без вызова OpenFileDialog всё работает стабильно. 0) Отчего сие? Это вопрос философский, риторический. Живо напоминает: Мои папа и мама! Я живу хорошо, просто замечательно. У меня все есть, есть свой дом, он теплый. В нем одна комната и кухня. Я без вас очень скучаю, особенно по вечерам. А здоровье мое не очень. То лапы ломит, то хвост отваливается. А на днях я линять начал. Старая шерсть с меня сыпется, хоть в дом не заходи. Зато новая растет чистая, шелковистая, так что лохматость у меня повысилась. До свидания, ваш сын дядя Шарик. В любом случае такое поведение предвосхищает ожидания заказчика... 1) Есть ли какие-то способы поправить дело без написания собственного диалога?
|
|
|
Записан
|
Программировать - значит понимать (К. Нюгард) Невывернутое лучше, чем вправленное (М. Аврелий) Многие готовы скорее умереть, чем подумать (Б. Рассел)
|
|
|
RXL
|
|
« Ответ #1 : 25-07-2012 19:22 » |
|
Дим, а данный кусочек кода покажешь?
Версия винды? Битность? Версия .NET?
|
|
« Последнее редактирование: 25-07-2012 19:28 от RXL »
|
Записан
|
... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
|
|
|
Алексей++
глобальный и пушистый
Глобальный модератор
Offline
Сообщений: 13
|
|
« Ответ #2 : 25-07-2012 19:55 » |
|
в WinAPI (вернее, я из-под MFC) такое тоже случается именно с этим же диалогом, если неправильно задать параметры в конструкторе и структуре OPENFILENAME. Можно неплохо нарваться на неприятные вещи ) А именно, следующий момент: http://msdn.microsoft.com/en-us/library/wh5hz49d(v=vs.80).aspxRemarks 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
Деятель
Команда клуба
Offline
Пол:
|
|
« Ответ #3 : 26-07-2012 12:30 » |
|
Алексей1153++, да, в MFC-версии у нас то же самое. И описанный в заметке "хак" с буфером приделан. RXL, ну что там за кусочек... Самый обычный мирный кусочек: 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.
|
|
|
Записан
|
Программировать - значит понимать (К. Нюгард) Невывернутое лучше, чем вправленное (М. Аврелий) Многие готовы скорее умереть, чем подумать (Б. Рассел)
|
|
|
Алексей++
глобальный и пушистый
Глобальный модератор
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
Деятель
Команда клуба
Offline
Пол:
|
|
« Ответ #5 : 26-07-2012 16:34 » |
|
Алексей1153++, не, разделители там правильные, а насчёт индекса согласен, только не помогает. База на WinAPI + STL #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
|
|
« Ответ #6 : 26-07-2012 16:41 » |
|
Dimka, я нашел в англоязычном инете три упоминания проблемы, аналогичной твоей. Решения нигде не предложено. Зато есть полно примеров работы с OpenFileDialog - попробуй сравнить свой код, может найдешь отличие.
|
|
|
Записан
|
... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
|
|
|
zubr
Гость
|
|
« Ответ #7 : 26-07-2012 17:07 » |
|
Возможно тут проблема в поле openFileName.hInstance. Давно как-то что то похожее у меня было, но там я эту функцию вызывал в dll. Проблема решилась, когда я в поле .hInstance подставил .hInstance основного приложения.
|
|
|
Записан
|
|
|
|
Dimka
Деятель
Команда клуба
Offline
Пол:
|
|
« Ответ #8 : 26-07-2012 17:54 » |
|
RXL, я с этим OpenFileDialog работал неоднократно. И один раз с ним была какая-то проблема - генерировался внутренний Exception, что на что-то влияло. Теперь же рушится стек. В .NET классе я к полям структуры доступа не имею. Так что без вариантов WinAPI. Вот перевёл на 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 »
|
Записан
|
Программировать - значит понимать (К. Нюгард) Невывернутое лучше, чем вправленное (М. Аврелий) Многие готовы скорее умереть, чем подумать (Б. Рассел)
|
|
|
Алексей++
глобальный и пушистый
Глобальный модератор
Offline
Сообщений: 13
|
|
« Ответ #9 : 27-07-2012 03:14 » |
|
в АПИшном варианте из поста #5 как раз легко завалить стек - как размером буфера, так и выпрыгиванием за край этого буфера (если это происходит в недрах GetOpenFileName) я бы вот так написал 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
Деятель
Команда клуба
Offline
Пол:
|
|
« Ответ #10 : 27-07-2012 06:06 » |
|
Алексей1153++, ты хочешь сказать, что API-функция умеет грамотно писать в string? Ей же буфер нужен. В этом-то всё и дело, ради этого-то и нужно обращение к WinAPI - подставить заранее выделенный буфер достаточного размера.
|
|
|
Записан
|
Программировать - значит понимать (К. Нюгард) Невывернутое лучше, чем вправленное (М. Аврелий) Многие готовы скорее умереть, чем подумать (Б. Рассел)
|
|
|
Dimka
Деятель
Команда клуба
Offline
Пол:
|
|
« Ответ #11 : 27-07-2012 06:14 » |
|
А, понял. Ты про то, что буфер на стеке выделяется.
|
|
|
Записан
|
Программировать - значит понимать (К. Нюгард) Невывернутое лучше, чем вправленное (М. Аврелий) Многие готовы скорее умереть, чем подумать (Б. Рассел)
|
|
|
Dimka
Деятель
Команда клуба
Offline
Пол:
|
|
« Ответ #12 : 27-07-2012 06:57 » |
|
Не, не помогает WinAPI версия. И подставновка hInstance тоже.
Более того, был придуман тест, на котором валится без вызова этого диалога. Значит просто звёзды так сошлись, что наблюдалась устойчивая корреляция между вызовом диалога и обрушением стека. А проблема не в диалоге.
|
|
|
Записан
|
Программировать - значит понимать (К. Нюгард) Невывернутое лучше, чем вправленное (М. Аврелий) Многие готовы скорее умереть, чем подумать (Б. Рассел)
|
|
|
Алексей++
глобальный и пушистый
Глобальный модератор
Offline
Сообщений: 13
|
|
« Ответ #13 : 27-07-2012 08:47 » |
|
Алексей1153++, ты хочешь сказать, что API-функция умеет грамотно писать в string? Ей же буфер нужен.
я выделил размер wstring заранее Более того, был придуман тест, на котором валится без вызова этого диалога.
это значит, что стек ещё раньше задет. Я тоже на такое нарывался (причём индикатором оказался именно этот диалог ) У меня тогда, помнится, было неправильное выравнивание (пропустил #pragma(pop) после #pragma(push,1) ) после чего в одном месте кода структура была одного размера, в другом - другого (в разных файлах реализации). Возможно ли это в шарпе допустить - я не знаю ) как это обнаружил , когда искал: в одну из функцию передаю одни значения переменных, а при входе обнаруживаю там другие
|
|
|
Записан
|
|
|
|
Dimka
Деятель
Команда клуба
Offline
Пол:
|
|
« Ответ #14 : 27-07-2012 08:53 » |
|
Алексей1153++, на C# написан только GUI. Основная же обработка - на C++ в dll. Выравниваний в коде нету. Но что-то сломать стек может. С учётом того, что писалось это не очень-то качественно.
|
|
|
Записан
|
Программировать - значит понимать (К. Нюгард) Невывернутое лучше, чем вправленное (М. Аврелий) Многие готовы скорее умереть, чем подумать (Б. Рассел)
|
|
|
Алексей++
глобальный и пушистый
Глобальный модератор
Offline
Сообщений: 13
|
|
« Ответ #15 : 27-07-2012 08:59 » |
|
для теста - выставь в настройках проекта выравнивание на 1 байт. Если заработает без глюков, то дело в этом
|
|
|
Записан
|
|
|
|
Dimka
Деятель
Команда клуба
Offline
Пол:
|
|
« Ответ #16 : 27-07-2012 09:48 » |
|
Алексей1153++, мысль насчёт выравниваний оказалась полезной. Только не в той степи. Есть выравнивание при работе с видеопамятью - выравнивание данных по степеням двойки. Валится только тогда, когда размер исходных данные не является степенью двойки. Какой-то там баг сидит. Будем копать...
|
|
|
Записан
|
Программировать - значит понимать (К. Нюгард) Невывернутое лучше, чем вправленное (М. Аврелий) Многие готовы скорее умереть, чем подумать (Б. Рассел)
|
|
|
Алексей++
глобальный и пушистый
Глобальный модератор
Offline
Сообщений: 13
|
|
« Ответ #17 : 27-07-2012 15:46 » |
|
это может быть: 1) размер текстур 2) размер первичной поверхности
|
|
|
Записан
|
|
|
|
|