У меня есть событие, которое я хочу вызвать, когда пользователь закрывает файл.xlsx. У меня также есть программа на С#, которая регулярно проверяет содержимое в том же файле. Проблема в том, что когда моя программа на С# (которая использует Interop) закрывает файл.xlsx, он вызывает мой код VB. Есть ли способ изолировать доступ к файлу excel, чтобы я знал, что он был отредактирован в программе Excel?
Я хочу, чтобы мой код VB запускался, когда пользователь редактирует файл в Excel.
благодаря
Я решил просто запустить мою программу на С# в начале временного файла и удалить ее в конце. Если VB видит временный файл, это означает, что С# должен редактировать файл excel, иначе мы можем предположить, что это пользователь.
Это непонятно из вашего вопроса, но в типичной фоновой работе С# в режиме реального времени приложение Excel не отображается пользователю. 1 Если это и ваш случай, вы можете использовать
чтобы различать два случая.
Если это не ваш случай, то 2 вы можете оставить message
со своего кода на С# для кода VBA в ячейке скрытого листа.
Использование скрытых листов для множества конфигурационных элементов и других внутренних временных таблиц весьма полезно. Конечный пользователь просто не знает об этом, но остальная часть Excel (как формулы, так и код VBA) может использовать его как обычно.
Надеюсь, вы получите лучший ответ, но я считаю, что этот ответ предлагает решение, если вы не получите ничего лучшего.
В нижней части находится макрос, который возвращает массив активных процессов. Обычно я использую это, когда я вызвал многолетнюю программу из макроса, и я хочу знать, когда она закончится. Я определяю это, вызывая процедуру каждые несколько секунд, пока программа не исчезнет из массива.
Над этой рутиной находится макрос события Workbook_BeforeClose. Это получает список активных процессов и записывает затем в файл.
Я написал программу, которая открывает и закрывает книгу Excel. Это VB.Net, а не С#, но он использует 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, если она есть. Я не проверял эту ситуацию.
Кажется, это предлагает два альтернативных способа определения того, что открыло вашу книгу: (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