c # wpf Сбой SaveFileDialog даже внутри Try..Catch - PullRequest
0 голосов
/ 14 января 2019

У меня есть приложение WPF, в котором я использую SaveFileDialog. Поток выглядит следующим образом:

1 - пользователь использует SaveFileDialog, чтобы выбрать имя файла и закрыть диалоговое окно

2 - Приложение пытается записать в файл.

3- При попытке записи в файл, если файл заблокирован, выдается IOException.

4 - Если я снова попытаюсь открыть SaveFileDialog, приложение вылетает с сообщением «Повреждена куча» на ntdll.dll

Я не могу найти решение. Даже внутри Try..Catch приложение вылетает.

Код для SaveFileDialog

try{
    Dispatcher.BeginInvoke(new Action(() =>
        {
            SaveFileDialog sfd = new SaveFileDialog();
            sfd.Reset();

            sfd.AddExtension = true;
            sfd.CheckFileExists = false;
            sfd.CheckPathExists = true;
            sfd.CreatePrompt = false;
            sfd.OverwritePrompt = true;
            sfd.DefaultExt = defaultExt;
            sfd.Filter = filter;
            sfd.Title = "Save As " + fileTypeDisplay;
            sfd.InitialDirectory = specialFolder;
            sfd.FileName = newFileNameNoExt;
            sfd.FilterIndex = 1;


            if (!string.IsNullOrEmpty(specialFolder))
            {
                FileDialogCustomPlace cp = new FileDialogCustomPlace(specialFolder);  // does not throw exceptions
                sfd.CustomPlaces.Add(cp);
            }
            try
            {
                if (sfd.ShowDialog(MyMainWindow) == true) //<-- ERROR HERE
                {
                    fileToSave = sfd.FileName;
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
            finally
            {
                sfd = null;
            }
        })).Wait();
}
catch(exception ex)
{
...log exception...
}

1 Ответ

0 голосов
/ 16 января 2019

Это не ответ, а то, как я решил сбой. Это давно унаследованный код, и подсказка в том, что сбой всегда происходит после исключения. Но почему исключение может вызвать проблемы с SaveFileDialog и вызвать сбой приложения?

Если углубиться в код, который я узнал, приведенный ниже код выполняется после того, как файл выбран пользователем в SaveFileDialog.

Обратите внимание на вызов метода AnotherUserIsLockingPkg в блоке catch.

Когда я закомментировал вызов этого метода, вызов SaveFileDialog.ShowDialog () для вопроса прекратился, чтобы завершить работу приложения. Я постараюсь следовать другим предложениям и посмотреть на поведение.

Если кто-либо имеет представление о том, почему это происходит, комментарии приветствуются.

FileStream strm = null;
try
{
    strm = fi.Open(FileMode.Open, forFileAccessMode, fileShare);
}
catch (IOException) // the file is already open
{
    ...

    fiuEx.IsByOtherUser = AnotherUserIsLockingPkg(filePath);

    ...
}
catch (Exception ex) 
{
    ...
}
finally
{
    ....
}

Приведенный ниже код используется для проверки того, заблокирован ли файл другим приложением. Он использует некоторые вызовы API. Похоже, этот код был адаптирован с https://stackoverflow.com/a/20623311/3044154

Метод AnotherUserIsLockingPkg приведен ниже.

#region Check if another user's process is locking a pkg

[StructLayout(LayoutKind.Sequential)]
private struct RM_UNIQUE_PROCESS
{
    public int dwProcessId;
    public System.Runtime.InteropServices.ComTypes.FILETIME ProcessStartTime;
}

private const int RmRebootReasonNone = 0;
private const int CCH_RM_MAX_APP_NAME = 255;
private const int CCH_RM_MAX_SVC_NAME = 63;

//private enum RM_APP_TYPE
//{
//    RmUnknownApp = 0,
//    RmMainWindow = 1,
//    RmOtherWindow = 2,
//    RmService = 3,
//    RmExplorer = 4,
//    RmConsole = 5,
//    RmCritical = 1000
//}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct RM_PROCESS_INFO
{
    public RM_UNIQUE_PROCESS Process;

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_APP_NAME + 1)]
    public string strAppName;

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_SVC_NAME + 1)]
    public string strServiceShortName;

    //public RM_APP_TYPE ApplicationType;
    public uint AppStatus;
    public uint TSSessionId;
    [MarshalAs(UnmanagedType.Bool)]
    public bool bRestartable;
}

[DllImport("rstrtmgr.dll", CharSet = CharSet.Unicode)]
private static extern int RmRegisterResources(uint pSessionHandle,
                                      UInt32 nFiles,
                                      string[] rgsFilenames,
                                      UInt32 nApplications,
                                      [In] RM_UNIQUE_PROCESS[] rgApplications,
                                      UInt32 nServices,
                                      string[] rgsServiceNames);

[DllImport("rstrtmgr.dll", CharSet = CharSet.Auto)]
private static extern int RmStartSession(out uint pSessionHandle, int dwSessionFlags, string strSessionKey);

[DllImport("rstrtmgr.dll")]
private static extern int RmEndSession(uint pSessionHandle);

[DllImport("rstrtmgr.dll")]
private static extern int RmGetList(uint dwSessionHandle,
                            out uint pnProcInfoNeeded,
                            ref uint pnProcInfo,
                            [In, Out] RM_PROCESS_INFO[] rgAffectedApps,
                            ref uint lpdwRebootReasons);


/// <summary>
/// Checks if a pkg has been locked by another user
/// </summary>
/// <param name="path">The pkg file path.</param>
/// <param name="includeCurrentUserProcesses">Check also for current user's processes</param>
/// <returns></returns>
public static bool AnotherUserIsLockingPkg(string path, bool includeCurrentUserProcesses = false)
{
    uint handle;
    string key = Guid.NewGuid().ToString();
    Process currentProcess = Process.GetCurrentProcess();

    int res = RmStartSession(out handle, 0, key);
    if (res != 0)
        throw new Exception("Could not begin restart session. Unable to determine file locker.");

    try
    {
        const int ERROR_MORE_DATA = 234;

        uint pnProcInfoNeeded = 0,
             pnProcInfo = 0,
             lpdwRebootReasons = RmRebootReasonNone;

        string[] resources = new string[] { path }; // Just checking on one resource.

        res = RmRegisterResources(handle, (uint)resources.Length, resources, 0, null, 0, null);

        if (res != 0)
            throw new Exception("Could not register resource.");

        //Note: there's a race condition here -- the first call to RmGetList() returns
        //      the total number of process. However, when we call RmGetList() again to get
        //      the actual processes this number may have increased.
        res = RmGetList(handle, out pnProcInfoNeeded, ref pnProcInfo, null, ref lpdwRebootReasons);

        if (res == ERROR_MORE_DATA)
        {
            // Create an array to store the process results
            RM_PROCESS_INFO[] processInfo = new RM_PROCESS_INFO[pnProcInfoNeeded];
            pnProcInfo = pnProcInfoNeeded;

            // Get the list
            res = RmGetList(handle, out pnProcInfoNeeded, ref pnProcInfo, processInfo, ref lpdwRebootReasons);
            //pnProcInfo contains all the processes that are using the pkg

            if (res == 0)
            {
                // Enumerate all of the results and check for waf3 process and not same session
                for (int i = 0; i < pnProcInfo; i++)
                {
                    try
                    {
                        if (includeCurrentUserProcesses)
                        {
                            if (processInfo[i].strAppName == currentProcess.ProcessName)
                                return true;
                        }
                        else
                        {
                            if (processInfo[i].strAppName == currentProcess.MainModule.ModuleName && processInfo[i].TSSessionId != currentProcess.SessionId)
                                return true;
                        }
                    }
                    // catch the error -- in case the process is no longer running
                    catch (ArgumentException)
                    { }
                }
            }
            else
                throw new Exception("Could not list processes locking resource.");
        }
        else if (res != 0)
            throw new Exception("Could not list processes locking resource. Failed to get size of result.");
    }
    finally
    {
        RmEndSession(handle);
    }

    return false;
}
#endregion
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...