Сбой приложения MFC в ProcessShellCommand () при открытии файла, указанного в командной строке

Проблема, которую мне нужно решить, это как использовать функцию MFC ProcessShellCommand() в InitInstance() из CWinApp обрабатывать файл Open по определенному пути, когда приложение для открытия файла запускается другим приложением.

У меня есть приложение MFC MDI (Multiple Document Interface), которое запускается другим приложением с помощью командной строки, используя ShellExecute() содержащий путь к открываемому файлу. При компиляции с Visual Studio 2005 я не вижу проблемы с запущенным приложением. При компиляции с Visual Studio 2013 запускаемое приложение аварийно завершает работу, и я никогда не вижу окно приложения.

Запустившись в отладчике, я вижу диалоговое окно с сообщением об ошибке «Библиотека времени выполнения Microsoft Visual C ++» с сообщением об ошибке «Ошибка отладки!» указав mfc120ud.dll и файл строки src \ mfc \ filelist.cpp: 221

На этом этапе я могу присоединиться к процессу приложения, а затем нажать кнопку «Повторить» в диалоговом окне. Затем, когда я продолжаю, я вижу диалоговое окно ошибки Visual Studio из необработанного исключения, которое, кажется, генерируется KernelBase.dll,

Необработанное исключение в 0x76EBC54F в NHPOSLM.exe: Microsoft C ++
исключение: CInvalidArgException в ячейке памяти 0x0014F094.

Если я нажму кнопку «Продолжить», на этот раз я получу еще одну «Ошибка отладки» из строки src \ mfc \ filelist.cpp: 234

После изменения источника выполнить Sleep() для того, чтобы использовать Debug->Attach to process Команда Visual Studio 2013 Мне удалось использовать отладчик для просмотра различных областей данных и пошагового выполнения кода.

В какой-то момент, переступив через ProcessShellCommand() Функция и, увидев исключение, когда поток вернулся к оператору после вызова функции, я использовал команду set source line debugger, чтобы вернуть текущую строку обратно к вызову функции, и перешагнул через нее снова. На этот раз исключений не было, и когда я разрешил продолжить поток, приложение открыло правильный файл.

Тогда я нашел эту статью, ProcessShellCommand и окна просмотра и фрейма в котором говорится следующее:

Проблема в том, что код в ProcessShellCommand () открывает
файл документа, прежде чем он завершит создание фрейма и окна просмотра.
Эти окна существуют, но нет доступа к ним, потому что
указатель фрейма окна не сохраняется в переменной приложения до
документ открыт.

Решение, представленное в статье, заключается в ProcesShellCommand() в два раза больше, чем в следующем фрагменте кода.

CCommandLineInfo cmdInfo;

if( !ProcessShellCommand( cmdInfo ) )
return FALSE;

ParseCommandLine( cmdInfo );

if( cmdInfo.m_nShellCommand != CCommandLineInfo::FileNew )
{
if (!ProcessShellCommand( cmdInfo ) )
return FALSE;
}

Я попробовал этот подход в своем приложении, и он действительно открывает документ и, кажется, обрабатывает все правильно. Проблема заключается в том, что хотя это работает для приложения MFC типа SDI (интерфейс с одним документом) для приложения MFC (интерфейс с несколькими документами), вы увидите два окна документа: одно пустое, созданное File New, и фактически хотел создан File Open.

Я также обнаружил, что при использовании отладчика для присоединения к процессу приложения, а затем медленного пошагового выполнения, если я позволю запущенному приложению продолжить после диалоговых окон исключений, приложение завершит работу с запрошенным файлом. Однако, если нет в отладчике, главное окно запущенного приложения не будет отображаться.

Таким образом, похоже, что существует какое-то условие состязания для готовности среды к запуску приложения для полной инициализации среды выполнения.

Для объяснения ProcessShellCommand() функция см CWinApp :: ProcessShellCommand который описывает основной процесс для обработки командной строки как:

  1. После создания в InitInstance, CCommandLineInfo объект
    перешел к ParseCommandLine,
  2. ParseCommandLine затем звонит CCommandLineInfo::ParseParam несколько раз,
    один раз для каждого параметра.
  3. ParseParam заполняет CCommandLineInfo объект, который затем передается
    в ProcessShellCommand,
  4. ProcessShellCommand обрабатывает аргументы командной строки и флаги.

Конкретный источник, который мы используем в InitInstance() является:

// Register the application's document templates.  Document templates
//  serve as the connection between documents, frame windows and views.

CMultiDocTemplate* pDocTemplate;
pDocTemplate = new CMultiDocTemplate(
IDR_NEWLAYTYPE,
RUNTIME_CLASS(CNewLayoutDoc),
RUNTIME_CLASS(CChildFrame), // custom MDI child frame
RUNTIME_CLASS(CNewLayoutView/*CLeftView*/));
AddDocTemplate(pDocTemplate);

// create main MDI Frame window
CMainFrame* pMainFrame = new CMainFrame;
if (!pMainFrame->LoadFrame(IDR_MAINFRAME))
return FALSE;
m_pMainWnd = pMainFrame;

// Parse command line for standard shell commands, DDE, file open
CLOMCommandLineInfo cmdInfo;
/*initialize language identifier to English so we wont have garbage if no language
flag is set on teh command line*/
cmdInfo.lang = LANG_ENGLISH;
cmdInfo.sublang = SUBLANG_ENGLISH_US;
//CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);

BOOL success = pMainFrame->ProcessCmdLineLang(cmdInfo.lang, cmdInfo.sublang);
if(!success){
AfxMessageBox(IDS_CMDLINE_LANG_NF,MB_OK,0);
}
// Dispatch commands specified on the command line
if (!ProcessShellCommand(cmdInfo))
return FALSE;

// The main window has been initialized, so show and update it.
pMainFrame->ShowWindow(SW_SHOWNORMAL);
pMainFrame->UpdateWindow();

Мне не нравится решение, представленное в статье о звонках ProcessShellCommand() дважды, как кажется неопрятным. Это не обеспечивает то, что мне нужно для приложения MDI. Я не знаю, почему этот код работает нормально с VS 2005 и вызывает ошибку в VS2013.

Наконец я наткнулся на эту публикацию в codeproject, Ошибка отладочного утверждения Visual Studio 2010, это указывало на то, что аналогичная ошибка утверждения, связанная с src \ mfc \ filelist.cpp, была прослежена до добавления пути к файлу в список последних файлов, когда путь к файлу содержал звездочку.

Когда я использую отладчик, чтобы посмотреть на cmdInfo объект есть член, (*((CCommandLineInfo*)(&(cmdInfo)))).m_strFileName, который содержит значение L «C: \ Users \ rchamber \ Documents \ ailan_221.dat». Это правильный путь из командной строки, предоставленный приложением, которое запускает запущенное приложение с ShellExecute(),

Замечания: каждая обратная косая черта в строке фактически является двойной обратной косой чертой в часах отладки. Поэтому для правильного рендеринга в переполнении стека мне нужно добавить дополнительные обратные слеши, как в L «C: \\ Users \\ rchamber \\ Documents \\ ailan_221.dat», однако двойной обратный слеш, похоже, используется отладчиком для представления одного обратного слеша. персонаж.

Редактировать 23.03.2016 — примечание об истории источника

Еще один бит информации — это история источников для этого приложения. Исходное приложение было создано с использованием Visual Studio 6.0, а затем перенесено в Visual Studio 2005. InitInstance() метод CWinApp не был изменен в какой-либо степени с момента его создания.

4

Решение

После использования Visual Studio 2013 для создания нового приложения MFC MDI (Multiple Document Interface) для сравнения приложения, с которым у меня возникают проблемы с запуском, и нового сгенерированного исходного кода, у меня есть решение.

Кажется, что основным отличием между правильным запуском и неправильным запуском является требование инициализации COM. Следующий конкретный исходный код был помещен в InitInstance() запускаемого приложения, и теперь оно успешно работает. Часть изменений исходного кода — это вызов для инициализации COM.

// InitCommonControlsEx() is required on Windows XP if an application
// manifest specifies use of ComCtl32.dll version 6 or later to enable
// visual styles.  Otherwise, any window creation will fail.
INITCOMMONCONTROLSEX InitCtrls;
InitCtrls.dwSize = sizeof(InitCtrls);
// Set this to include all the common control classes you want to use
// in your application.
InitCtrls.dwICC = ICC_WIN95_CLASSES;
InitCommonControlsEx(&InitCtrls);

CWinApp::InitInstance();

// Initialize OLE libraries
if (!AfxOleInit())
{
AfxMessageBox(IDP_OLE_INIT_FAILED);
return FALSE;
}

AfxEnableControlContainer();

// AfxInitRichEdit2() is required to use RichEdit control
// AfxInitRichEdit2();

Хотя скомпилированное приложение Visual Studio 2005 не продемонстрировало этой проблемы, я хочу, чтобы исходный код, скомпилированный Visual Studio 2005 и Visual Studio 2013, был как можно более схожим. Я сделал такое же изменение исходного кода в дереве исходных текстов Visual Studio 2005, и оно также работает правильно в исходном дереве Visual Studio 2005.

При использовании Visual Studio 2005 и создании пустого приложения MFC для MDI генерируется исходный код, аналогичный приведенному выше.

1

Другие решения

У меня просто были те же проблемы в Windows 10 с Visual Studio 2013 и приложением MDI. Здесь, предоставленное решение вызова ProcessShellCommand () дважды все еще приводило к сбою. Решением было создать окно раньше, чем интерпретировать командную строку. Это сработало для меня.
Я попробовал вариант CoInitialize (), и это также работает (поместите его где-нибудь перед кодом ниже):

// create main MDI Frame window
CMainFrame* pMainFrame = new CMainFrame;
if (!pMainFrame->LoadFrame(IDR_MAINFRAME))
return FALSE;
m_pMainWnd = pMainFrame;

// The main window has been initialized, so show and update it.
// This needs to be up really before parsing the command line,
// so that any FileOpen command has something to render in.
pMainFrame->ShowWindow(m_nCmdShow);
pMainFrame->UpdateWindow();

// Parse command line for standard shell commands, DDE, file open
CCommandLineInfo cmdInfo;

ParseCommandLine(cmdInfo);
if(!ProcessShellCommand(cmdInfo))
return FALSE;
0

Я обновляю настольное приложение с VC ++ до Visual Studio 2017 и столкнулся с той же проблемой, когда пользователь пытался открыть файл двойным щелчком в Проводнике.
В моем случае мне просто нужно было добавить этот код:

// Initialize OLE libraries
if (!AfxOleInit())
{
AfxMessageBox("Could not open the file! \nTry open CS Setup first and then open the file using the menu \"File->Open...\".", MB_ICONERROR);
return FALSE;
}
0
По вопросам рекламы [email protected]