COM (홈 메이드) 타이머를 실행하는 동안 Excel에서 오류가 발생합니다. 기본적으로 Excel은 타이머를 인스턴스화하고 초기화 한 다음 시작합니다. 그런 다음 Timer는 Excel에서 catch하는 이벤트를 발생시키는 모든 X 밀리 초를 틱 (꽤 표준적인 작업)합니다. 매 초마다 빠른 속도로 체크하지 않기 때문에 Excel 자체를 타이머로 사용하지 않습니다. (내 용도로는 너무 길다)Excel 스레드로 COM 개체를 실행할 때 충돌 발생
내 문제는 이벤트가 발생하는 동안 스프레드 시트를 클릭 한 상태로 타이머, 엑셀 꽤 나쁜 충돌. 불행히도 사용자는 스프레드 시트를 클릭하고 타이머가 실행되는 동안 수정할 필요가 있습니다.
나는 나의 타이머에서 IMessageFilter 인터페이스를 사용할 수있는 곳을 보았다. 이것은 이벤트가 발생했을 때 Excel이 사용 중이면 타이머가이를 볼 수 있고 이에 따라 행동 할 수 있어야합니다. 그러나 제대로 구현할 수 없었습니다.
누군가 나를 도와 줄 수 있다면 좋을 것입니다. 여기에, 나는에서 WithEvents ExcelTimer.ExcelTimer 객체를 전달 싱글이 Excel에서
내 싱글의 코드입니다 : 여기
소스 내가 사용하고 코드 여기Option Explicit
Private Const m_sMODULE_NAME = "cTimerManager"
Public WithEvents oCsharpTimer As ExcelTimer.ExcelTimer
Private Sub Class_Initialize()
Set oCsharpTimer = New ExcelTimer.ExcelTimer
'The following two lines are called dynamically from somewhere else
'normally but for simplicity of my post I have put them here
oCsharpTimer.Initialize 500
oCsharpTimer.StartTimer
End Sub
Private Sub oCsharpTimer_TimeTickEvt(ByVal o As Variant, ByVal Time As String)
Const sPROCEDURE_NAME = "oCsharpTimer_TimeTickEvt"
On Error GoTo ErrorHandler
'"Send" confirmation with time to the COM object.
oCsharpTimer.TimeReceived Time
'Do whatever I wanna do when the event is trigger
CleanUp:
Exit Sub
ErrorHandler:
'My Error handling structure
If ProcessError(m_sMODULE_NAME, sPROCEDURE_NAME, Err) Then
Stop
Resume
Else
Resume Next
End If
End Sub
가있다
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace ExcelTimer
{
public delegate void EventTimeRaiser(object o, string Time);
//COM Interface
public interface ICOMExcelTimer
{
[DispId(1)]
void StartTimer();
[DispId(2)]
void StopTimer();
[DispId(3)]
void Initialize(int TimeInMilliseconds);
[DispId(4)]
void TimeReceived(string ReceivedTime);
}
//Event interface
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface ICOMExcelTimerEvent
{
[DispId(1000)]
void TimeTickEvt(object o, string Time);
}
[ClassInterface(ClassInterfaceType.None),
ComSourceInterfaces(typeof(ICOMExcelTimerEvent)),
ComVisible(true)]
public class ExcelTimer : ICOMExcelTimer, IMessageFilter
{
private event EventTimeRaiser TimeTickEvt;
private bool _started;
private bool _initialised;
private int _timeInMilliseconds;
private string _lastTimeReceived;
private Control _control;
private Thread _timerThread;
private IAsyncResult _AsynchronousResult;
[ComVisible(true)]
public void Initialize(int TimeInMilliSeconds)
{
//To be called by Excel to set which timer parameters it wants
_timeInMilliseconds = TimeInMilliSeconds;
_initialised = true;
//Make sure we clear the last confirmation received
//since we are re-initialising the object
_lastTimeReceived = "";
}
[ComVisible(true)]
public void TimeReceived(string ReceivedTime)
{
//Store the last time received. Excel calls this function
_lastTimeReceived = ReceivedTime;
}
public ExcelTimer()
{
_lastTimeReceived = "";
}
[ComVisible(true)]
//Start the Timer
public void StartTimer()
{
//If the timer has not been initialised just yet
if (!_initialised)
{
//Sends back an error message to Excel
TimeTickEvt(this, "Error: Timer Not Initialised");
return;
}
try
{
//Start the timer
_timerThread = new Thread(new ThreadStart(TimeTicking));
//Start the Thread
_started = true;
_timerThread.Start();
}
catch (Exception ex)
{
System.IO.File.AppendAllText(@"C:\ErrorLog.txt", ex.Message + " - StartTimer - " + DateTime.Now.ToString("hh:mm:ss.f") + "\n");
}
}
[ComVisible(true)]
//Stop the timer
public void StopTimer()
{
//Stop the Thread
_timerThread.Abort();
//Change the status
_started = false;
}
private void TimeTicking()
{
string SentTime;
//As long as the timer is running
while (_started)
{
try
{
//Pause the timer for the right number of milliseconds
Thread.Sleep(_timeInMilliseconds);
SentTime = DateTime.Now.ToString("hh:mm:ss.ffff");
//########### The CODE Errors Here when Excel is busy! ###########
//Raise an event for Excel to grab with the time that the thread finished the sleep at.
OnTimeTick(SentTime);
//_lastTimeReceived is used so that if the link between Excel and the Thread is broken the thread stops after sometimes
//if no confirmation was received from Excel.
//If no last time was received just yet, we setup the last time received to the sent time
if (_lastTimeReceived.Equals(""))
{
_lastTimeReceived = SentTime;
}
//If the last time received is older than 10 x TimeInMilliseconds (in Seconds) we stop the timer.
else if (Convert.ToDateTime(_lastTimeReceived).AddSeconds(_timeInMilliseconds * 10/1000) < Convert.ToDateTime(SentTime))
{
OnTimeTick("Timer timed out. No Confirmation for more than " + _timeInMilliseconds * 10/1000 + " second(s).");
//Stop the timer because the thread has not received a last time recently
_started = false;
}
}
catch (Exception ex)
{
System.IO.File.AppendAllText(@"C:\ErrorLog.txt", ex.Message + " - TimeTicking - " + DateTime.Now.ToString("hh:mm:ss.f") + "\n");
}
}
}
protected virtual void OnTimeTick(string Time)
{
try
{
if (Time != null)
{
//Raise the event
TimeTickEvt(this, Time);
}
}
catch (Exception ex)
{
System.IO.File.AppendAllText(@"C:\ErrorLog.txt", ex.Message + " - OnTimeTick - " + DateTime.Now.ToString("hh:mm:ss.f") + "\n");
}
}
}
}
감사합니다. 첫 번째 생각으로, 링크에 설명 된 접근법의 문제점은 타이머의 루프가 절전 모드 인 동안 (클릭, 수정 등) 사용자가 시트에 손을 대지 않는다는 것입니다. DoEvents). 내 용도로 큰 문제입니다. –
당신은 userform을 추가하고, 그것의'.Visible = False'를 설정 한 다음,'.vowModeless'를 실행 중에 그것을 표시 할 수 있습니다. 이렇게하면 사용자가 루프 중에 워크 시트와 상호 작용할 수 있습니다. –
문제는 Excel/VBA에서 C# 다중 스레드 개체를 사용하는 방법을 익히기위한 첫 번째 단계로이 작업을 수행했다는 것입니다. 다음 단계는 (C# 측면에서) 좀 더 복잡하고 Excel/VBA와의 상호 작용을 원활하고 효율적으로 만들 수 있는지 확인하고 싶었습니다. –