c#wpf SaveFileDialog甚至在Try..Catch内部崩溃

时间:2019-01-14 14:25:13

标签: c# wpf

我有一个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 :(得分:0)

这不是答案,而是我如何解决崩溃的问题。这是很久以前的遗留代码,线索是崩溃总是发生在异常之后。但是,为什么异常会导致SaveFileDialog问题并导致应用崩溃?

深入学习我所了解的代码,是在用户在SaveFileDialog中选择文件后执行的。

请注意在catch块中对方法 AnotherUserIsLockingPkg 的调用。

当我注释掉对该方法的调用时,对该问题的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