У меня есть приложение Windows Form, которое дает пользователю открытое диалоговое окно для открытия электронной таблицы.
Приложение выполняет некоторые задачи в этой электронной таблице, такие как сортировка и удаление пустых строк. Затем, когда он закончен, он выполняет SaveAs, используя текущую дату как часть нового имени файла.
После этого я хочу удалить исходную таблицу.
Я использую Microsoft.Office.Interop.Excel.
Я нашел код здесь, в StackOverflow (Как правильно очистить объекты взаимодействия Excel ?), который показывает, как закрыть Excel, а третий ответ в этом сообщении от nightcoder даже показал способ (который я использую), который удалит Excel с вкладки «Процессы» диспетчера задач.
Я думал, что как только Excel будет сброшен из диспетчера задач, он снимет свою мертвую хватку, похожую на ледяную щупальце, с файлом, который я пытаюсь закрыть, но как же я ошибался.
Когда код попадает в команду File.Delete(MyFile), он все еще говорит, что я не могу удалить этот файл, потому что на нем мертвая хватка, похожая на ледяную щупальце, или что-то в этом роде.
Кто-нибудь знает, где я могу найти бейсбольную биту, достаточно большую, чтобы приложение отпустило файл. Я бы очень хотел удалить его.
================================================== ============
22.07.2013 Обновление
Вот часть моего кода, который у меня есть до сих пор.
Этот код позволяет пользователю выбрать электронную таблицу Excel, открыть ее, переименовать и закрыть. Я удалил часть кода, который манипулировал электронной таблицей, который я не считал нужным.
Я хотел бы, чтобы после сохранения переименованного файла он закрыл оригинал, открыл переименованный файл и вышел из приложения, оставив переименованную электронную таблицу открытой для дальнейшего редактирования.
В настоящее время он этого не делает. Он только создает электронную таблицу с новым именем, а затем закрывает и приложение, и электронную таблицу. Затем мне нужно открыть новую таблицу вручную.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Runtime.InteropServices;
using Excel = Microsoft.Office.Interop.Excel;
using System.Diagnostics;
using Microsoft.Office.Interop.Excel;
namespace Weekly_Stats_Organizer
{
public partial class Form1 : Form
{
[DllImport("user32.dll")]
private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
string _ExcelFileName;
private string ExcelFileName
{
get { return _ExcelFileName; }
set { _ExcelFileName = value; }
}
private string DefaultPath = "C:\\My Documents\\Weekly Stats";
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Stream FileName = null;
OpenFileDialog OpenExcelSpreadsheet = new OpenFileDialog();
OpenExcelSpreadsheet.InitialDirectory = DefaultPath;
OpenExcelSpreadsheet.Filter = "Excel 2003 (*.xls)|*.xls|Excel 2007 (*.xlsx)|*.xlsx";
OpenExcelSpreadsheet.FilterIndex = 1;
OpenExcelSpreadsheet.RestoreDirectory = true;
if(OpenExcelSpreadsheet.ShowDialog() == DialogResult.OK)
{
if((FileName = OpenExcelSpreadsheet.OpenFile()) != null)
{
ExcelFileName = ((System.Windows.Forms.FileDialog)(OpenExcelSpreadsheet)).FileName;
GenerateWorkbook();
}
}
}
private void GetExcel()
{
Excel.Application ExcelApp = null;
Excel.Workbook ExcelWorkBook = null;
Excel.Sheets ExcelSheets = null;
Excel.Worksheet MySheet = null;
try
{
DateTime CurrentDate = DateTime.Today;
string FileName = DefaultPath + "\\" + CurrentDate.Year.ToString() + "-" + CurrentDate.Month.ToString("D2") + "-" + CurrentDate.Day.ToString("D2") + " Weekly Stats.xls";
ExcelApp = new Excel.Application();
ExcelApp.Visible = false;
ExcelWorkBook = ExcelApp.Workbooks.Open(ExcelFileName, 0, true, 5, "", "", false, Excel.XlPlatform.xlWindows, "", true, false, 0, true, false, false);
ExcelSheets = ExcelWorkBook.Worksheets;
MySheet = (Excel.Worksheet)ExcelSheets.get_Item("Sheet 1");
ExcelWorkBook.SaveAs(FileName, Excel.XlFileFormat.xlWorkbookNormal, "", "", false, false,
Excel.XlSaveAsAccessMode.xlNoChange, Excel.XlSaveConflictResolution.xlUserResolution, true, "", "", "");
//GC.Collect();
//GC.WaitForPendingFinalizers();
Marshal.ReleaseComObject(MySheet);
Marshal.ReleaseComObject(ExcelSheets);
MySheet = null;
ExcelSheets = null;
ExcelWorkBook.Close(false, System.Reflection.Missing.Value, System.Reflection.Missing.Value);
}
finally
{
Marshal.ReleaseComObject(ExcelWorkBook);
int hWnd = ExcelApp.Application.Hwnd;
TryKillProcessByMainWindowHwnd(hWnd);
Marshal.ReleaseComObject(ExcelApp);
ExcelWorkBook = null;
ExcelApp = null;
//if (File.Exists(ExcelFileName))
// File.Delete(ExcelFileName);
System.Windows.Forms.Application.Exit();
}
}
//=========================================================================================================================================
//=========================================================================================================================================
//=========================================================================================================================================
// This code was found at https://stackoverflow.com/questions/158706/how-to-properly-clean-up-excel-interop-objects
// is the answer provided by nightcoder. This solution seems to be the only one that actually clears the Excel instance out of the
// Processes in Windows Task Manager. The idea is to completely shut down Excel so that I can delete the original spreadsheet. So far not
// working out so well.
private void GenerateWorkbook()
{
try
{
GetExcel();
}
finally
{
GC.Collect();
GC.WaitForPendingFinalizers();
}
}
//=============================================================================================================================================
/// <summary> Tries to find and kill process by hWnd to the main window of the process.</summary>
/// <param name="hWnd">Handle to the main window of the process.</param>
/// <returns>True if process was found and killed. False if process was not found by hWnd or if it could not be killed.</returns>
public static bool TryKillProcessByMainWindowHwnd(int hWnd)
{
uint processID;
GetWindowThreadProcessId((IntPtr)hWnd, out processID);
if (processID == 0) return false;
try
{
Process.GetProcessById((int)processID).Kill();
}
catch (ArgumentException)
{
return false;
}
catch (Win32Exception)
{
return false;
}
catch (NotSupportedException)
{
return false;
}
catch (InvalidOperationException)
{
return false;
}
return true;
}
/// <summary> Finds and kills process by hWnd to the main window of the process.</summary>
/// <param name="hWnd">Handle to the main window of the process.</param>
/// <exception cref="ArgumentException">
/// Thrown when process is not found by the hWnd parameter (the process is not running).
/// The identifier of the process might be expired.
/// </exception>
/// <exception cref="Win32Exception">See Process.Kill() exceptions documentation.</exception>
/// <exception cref="NotSupportedException">See Process.Kill() exceptions documentation.</exception>
/// <exception cref="InvalidOperationException">See Process.Kill() exceptions documentation.</exception>
public static void KillProcessByMainWindowHwnd(int hWnd)
{
uint processID;
GetWindowThreadProcessId((IntPtr)hWnd, out processID);
if (processID == 0)
throw new ArgumentException("Process has not been found by the given main window handle.", "hWnd");
Process.GetProcessById((int)processID).Kill();
}
//=========================================================================================================================================
//=========================================================================================================================================
//=========================================================================================================================================
}
}