Используйте VB, чтобы отличить событие xlsx от C # Interop или Excel.

1

У меня есть событие, которое я хочу вызвать, когда пользователь закрывает файл.xlsx. У меня также есть программа на С#, которая регулярно проверяет содержимое в том же файле. Проблема в том, что когда моя программа на С# (которая использует Interop) закрывает файл.xlsx, он вызывает мой код VB. Есть ли способ изолировать доступ к файлу excel, чтобы я знал, что он был отредактирован в программе Excel?

Я хочу, чтобы мой код VB запускался, когда пользователь редактирует файл в Excel.

благодаря

Теги:
excel-vba
excel
xlsx
interop

3 ответа

1
Лучший ответ

Я решил просто запустить мою программу на С# в начале временного файла и удалить ее в конце. Если VB видит временный файл, это означает, что С# должен редактировать файл excel, иначе мы можем предположить, что это пользователь.

0

Это непонятно из вашего вопроса, но в типичной фоновой работе С# в режиме реального времени приложение Excel не отображается пользователю. 1 Если это и ваш случай, вы можете использовать

Application.Visible Property (Excel)

чтобы различать два случая.

Если это не ваш случай, то 2 вы можете оставить message со своего кода на С# для кода VBA в ячейке скрытого листа.

Использование скрытых листов для множества конфигурационных элементов и других внутренних временных таблиц весьма полезно. Конечный пользователь просто не знает об этом, но остальная часть Excel (как формулы, так и код VBA) может использовать его как обычно.

0

Надеюсь, вы получите лучший ответ, но я считаю, что этот ответ предлагает решение, если вы не получите ничего лучшего.

В нижней части находится макрос, который возвращает массив активных процессов. Обычно я использую это, когда я вызвал многолетнюю программу из макроса, и я хочу знать, когда она закончится. Я определяю это, вызывая процедуру каждые несколько секунд, пока программа не исчезнет из массива.

Над этой рутиной находится макрос события 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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню