c# - thisaddin - visual studio excel
Obtenga Hashcode para Excel Workbook en VSTO para habilitar botones basados en estado (1)
Estoy creando un complemento VSTO Ribbon para Excel, y estoy almacenando información de estado de mi libro de trabajo en mi aplicación que utilizo para actualizar los botones visuales habilitados. Teniendo en cuenta que puede haber varios libros de trabajo, estoy almacenando este objeto de estado en un diccionario en la clase ThisAddIn. Mi problema es que no sé cómo obtener un Hash / Key / Guid único para el libro de trabajo porque lo único que obtengo es un contenedor COM que cambia continuamente el hash. bastante justo, entiendo totalmente eso.
Una solución que he usado durante mucho tiempo ha sido crear un guid y almacenarlo en CustomDocumentProperties para el libro de trabajo, y mapear el estado basado en eso como una clave. Esto al menos funciona, pero falla si creo una copia del libro de trabajo y lo abro en la misma instancia de la aplicación y tengo varios libros de trabajo con el mismo guid ahora.
Solo tengo una idea ahora que supongo que podría actualizar este Guid en el evento Workbook_Open. Pero aún así parece una solución poco fiable.
La segunda solución que encontré aquí: http://social.msdn.microsoft.com/Forums/en-US/vsto/thread/04efa74d-83bd-434d-ab07-36742fd8410e/
Así que usé ese código de chicos y creé esto:
public static class WorkbookExtensions
{
public static IntPtr GetHashery(this msExcel.Workbook workbook)
{
IntPtr punk = IntPtr.Zero;
try
{
punk = Marshal.GetIUnknownForObject(workbook);
return punk;
}
finally
{
//Release to decrease ref count
Marshal.Release(punk);
}
}
}
Funciona muy bien durante unos minutos, hasta que comienza dándome el infame error de que "el objeto COM que se ha separado de su RCW subyacente no se puede usar" cuando se accede al Application.ActiveWorkbook.
¿Es esta una forma segura de hacer referencia al objeto COM Libro de trabajo? ¿Qué sucede si tengo dos aplicaciones de cinta que usan este método para obtener un solo GUID de libro de trabajo? ¿Qué sucede si una de esas aplicaciones ejecuta el recolector de elementos no utilizados en mi objeto de estado, que llama a un finalizador para llamar a Marshal.FinalReleaseComObject (libro de trabajo)? ¿Hay alguna forma de que pueda obtener el Recuento de Ref de un Libro de Trabajo para que no llame a FinalRelease antes de que otras Aplicaciones de Cinta hayan terminado con ellos? ¿Cuáles son algunas de las mejores prácticas para limpiar objetos COM de Workbook en VSTO para seguir jugando limpio con estas otras aplicaciones?
Seguramente no soy la primera persona en querer tener los botones habilitados en función del estado del Libro de trabajo, ¿cómo lo hacen todos los demás? He visto algunos otros artículos aquí en Stack Overflow, pero ninguno me ayudó con la solución Workbook Guid.
Estoy usando el Diseñador de Cinta, y conectando al Libro de Trabajo Cargar y Desactivar eventos.
Gracias de antemano, espero haber incluido todos los detalles.
Terminé resolviendo esto simplemente lanzando el IntPtr a un largo, y luego la eliminación del IntPtr no me afectó. No necesito preservar el IntPtr porque todo lo que realmente necesito es algo único sobre el libro de trabajo.
El siguiente código me permite almacenar información de estado específica del Libro de trabajo, por lo que puedo actualizar el estado visual de los botones en mi cinta en función de un estado de libro de objetos personalizado. Puede almacenar cualquier información que desee en su clase personalizada WorkbookState, pero normalmente sería información específica de la sesión que no desea que persista en la propia hoja de cálculo.
Extensiones separadas del libro de trabajo:
public static class WorkbookExtensions
{
public static long GetHashery(this msExcel.Workbook workbook)
{
if (workbook == null)
{
throw new ArgumentNullException("workbook");
}
IntPtr pUnknown = IntPtr.Zero;
try
{
pUnknown = Marshal.GetIUnknownForObject(workbook);
return pUnknown.ToInt64();
}
finally
{
// GetIUnknownForObject causes AddRef.
if (pUnknown != IntPtr.Zero)
{
Marshal.Release(pUnknown);
}
}
}
}
Luego, en mi clase VSTO / ExcelDna ThisAddIn, almaceno un mapa de todos los estados del libro de trabajo con el método anterior para encontrar una clave hash de libro única:
private Dictionary<long, WorkbookState> _workbookStates = new Dictionary<long, WorkbookState>();
public WorkbookState WorkbookState
{
get
{
long hash = Application.ActiveWorkbook.GetHashery();
WorkbookState state;
if (!_workbookStates.TryGetValue(hash, out state))
{
state = _workbookStates[hash] = new WorkbookState();
}
return state;
}
}
Y, por supuesto, ahora puedo acceder a mi WorkbookState desde cualquier lugar de mi aplicación de cinta simplemente llamando a ThisAddIn.WorkbookState