使用VB区分来自C#Interop或Excel程序的xlsx事件

我有一个事件,我想要在用户closures.xlsx文件时触发。 我也有一个C#程序定期检查同一个文件中的内容。 我遇到的问题是,当我的C#程序(使用Interop)closures.xlsx文件时,它会触发我的VB代码。 有没有办法隔离对Excel文件的访问,以便我知道它是在Excel程序中编辑的?

我只想让用户在Excel中编辑文件时触发我的VB代码。

谢谢

我决定只是让我的C#程序在开始时创build一个临时文件,并在最后删除它。 如果VB看到临时文件,这意味着C#必须编辑excel文件,否则我们可以认为它是一个用户。

我希望你能得到更好的答案,但我相信这个答案提供了一个解决scheme,如果你没有得到更好的。

底部是一个返回活动进程数组的macros。 当我从一个macros中调用一个长时间运行的程序时,我通常会使用它,并且我想知道它何时完成。 我通过每隔几秒钟调用一次例程来确定,直到程序从数组中消失。

该例程之上是一个Workbook_BeforeClose事件macros。 这将获得活动进程的列表,然后写入文件。

我编写了一个打开和closuresExcel工作簿的程序。 这是VB.Net而不是C#,但它使用InterOp,所以我怀疑这一点。

我有80个左右的进程在我的笔记本上运行,所以我只包括我认为相关的那些进程。

如果通过点击打开工作簿,我会得到:

Current Parent Process 3396 3252 explorer.exe 5452 3396 EXCEL.EXE 

请注意,EXCEL.EXE的父项是explorer.exe。

如果我用我的程序打开工作簿,我会得到:

 Current Parent Process 920 760 svchost.exe 3396 3252 explorer.exe 5912 3396 OpenCloseExcel.exe 1056 920 EXCEL.EXE 

这里EXCEL.EXE已经被svchost.exe打开了,我的程序OpenCloseExcel.exe被激活了。 注意:进程列表中有许多svchost.exe的副本,但是我只包含了相关的副本。

最后,我打开了一个不同的工作簿,然后运行OpenCloseExcel.exe。 这次我得到:

 Current Parent Process 920 760 svchost.exe 3396 3252 explorer.exe 324 3396 EXCEL.EXE 5116 3396 OpenCloseExcel.exe 5108 920 EXCEL.EXE 

这里有两个EXCEL.EXE副本; 一个由explorer.exe打开,一个由svchost.exe打开。 我的程序再次被列为一个活跃的过程。 InterOp允许程序使用现有的Excel副本(如果有的话)。 我还没有testing过这种情况。

这似乎提供了两种可供select的方法来确定打开工作簿的方式:(1)是否由explorer.exe打开,(2)您的程序是否处于活动状态?

 Option Explicit Private Sub Workbook_BeforeClose(Cancel As Boolean) Dim FlOut As Object Dim FlSysObj As Object Dim InxP As Long Dim Process() As ProcessDtl Set FlSysObj = CreateObject("Scripting.FileSystemObject") Set FlOut = FlSysObj.CreateTextFile( _ ActiveWorkbook.Path & "\" & Format(Now(), "hhmmss") & ".txt") Call GetProcessList(Process) FlOut.WriteLine "Current Parent Process" For InxP = LBound(Process) To UBound(Process) With Process(InxP) FlOut.WriteLine Right$(Space(7) & .IdCrnt, 7) & _ Right$(Space(8) & .IdParent, 8) & " " & .Name End With Next FlOut.Close End Sub Option Explicit ' Source http://vbadud.blogspot.co.uk/2007/06/show-all-processes-using-vba.html ' Modified by Tony Dallimore Const TH32CS_SNAPHEAPLIST = &H1 Const TH32CS_SNAPPROCESS = &H2 Const TH32CS_SNAPTHREAD = &H4 Const TH32CS_SNAPMODULE = &H8 Const TH32CS_SNAPALL = (TH32CS_SNAPHEAPLIST Or _ TH32CS_SNAPPROCESS Or _ TH32CS_SNAPTHREAD Or _ TH32CS_SNAPMODULE) Const TH32CS_INHERIT = &H80000000 Const MAX_PATH As Integer = 260 Private Type PROCESSENTRY32 dwSize As Long cntUsage As Long th32ProcessID As Long th32DefaultHeapID As Long th32ModuleID As Long cntThreads As Long th32ParentProcessID As Long pcPriClassBase As Long dwFlags As Long szExeFile As String * MAX_PATH End Type Public Type ProcessDtl IdCrnt As Long IdParent As Long Name As String End Type Private Declare Function CreateToolhelp32Snapshot Lib "kernel32" _ (ByVal lFlags As Long, ByVal lProcessID As Long) As Long Private Declare Sub CloseHandle Lib "kernel32" (ByVal hPass As Long) ' API Functions to get the processes Private Declare Function Process32First Lib "kernel32" _ (ByVal hSnapShot As Long, uProcess As PROCESSENTRY32) As Long Private Declare Function Process32Next Lib "kernel32" _ (ByVal hSnapShot As Long, uProcess As PROCESSENTRY32) As Long Public Sub GetProcessList(Process() As ProcessDtl) Dim hSnapShot As Long '* Handle Dim uProcess As PROCESSENTRY32 '* Process Dim lRet '* Return Val Dim InxP As Long Dim Pos As Long ReDim Process(1 To 100) InxP = 0 ' Last used entry in array ' Takes a snapshot of the running processes and the heaps, modules, ' and threads used by the processes hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPALL, 0&) uProcess.dwSize = Len(uProcess) ' Retrieve information about the first process encountered in our system snapshot ' uProcess.szExeFile is a fixed length string of 260 characters. Each new process ' name is terminated with &H0 and overwrites the previous name. Hence the need to ' discard the first &H0 and any characters that follow. ' In the original code, the first process name was ignored. During my ' experimentation, the first name was always "[System Process]" which appears to be ' a header. I continue to discard the first process name but only if it is ' "[System Process]" ' In the original code, the final lRet was output before being tested to be true. ' This meant the last name was junk. I always test lRet before extracting the name. lRet = Process32First(hSnapShot, uProcess) If Left$(uProcess.szExeFile, 16) = "[System Process]" Then lRet = Process32Next(hSnapShot, uProcess) End If ' lRet is 0 or 1. 1 means uProcess has been loaded with another process. Do While lRet InxP = InxP + 1 If InxP > UBound(Process) Then ReDim Preserve Process(1 To UBound(Process) + 100) End If Process(InxP).IdCrnt = uProcess.th32ProcessID Process(InxP).IdParent = uProcess.th32ParentProcessID Pos = InStr(1, uProcess.szExeFile, Chr$(0)) If Pos > 0 Then Pos = Pos - 1 Else Pos = 1 End If Process(InxP).Name = Left$(uProcess.szExeFile, Pos) lRet = Process32Next(hSnapShot, uProcess) Loop CloseHandle hSnapShot ' This ReDim assumes there is at least one process. ReDim Preserve Process(1 To InxP) ' Discard empty entries End Sub 

从你的问题不清楚,但在典型的C#互操作的后台工作中,实际的Excel应用程序不会显示给用户。 1如果这也是你的情况,你可以使用

Application.Visible属性(Excel)

区分这两种情况。

如果这不是你的情况,那么你可以在隐藏工作表的单元格中留下来自你的VBA代码的C#代码的message

使用隐藏工作表来处理大量的configuration和其他内部临时表是非常有用的。 最终用户只是不知道它,但其余的Excel(公式和VBA代码)可以正常使用它。