Re: takes Draw a long time to start a new document!
I'm just curious if you use Draw 10 or 11? There were a couple issues fixed in CorelDRAW 11 related to unexpected freezes when calling COM methods from out-of-process.
There was a discussion on this issue long time ago on the CdrVBA mailing list (gosh, I need to transfer those messages from Yahoo to this forum  ).
Here is a brief recap of the thread (dated around November 2001):
Quote:
Originally Posted by Craig Tucker
Is anyone experiencing Coreldrw "sleeping"?
Occasionally, processes to coreldrw seem to be hung - if I hover (not click/activate) over Corel on the desktop, the application starts to work, processing the dll request.
Is there anything I can do in a dll to "WAKE UP" or immediately activate Coreldrw?
|
Quote:
Originally Posted by Alex
I have notice that as well, however the actual cause of this behavior is unknown so far. When you do the call from out-of-process (VB-to-CorelDRAW for example), Windows COM subsystem needs to synchronize the two processes to make sure CorelDRAW is not busy with anything else and can process the request from VB. In this case a special message is posted to CorelDRAW's message queue. When CorelDRAW processes this message, it is guaranteed that it doesn't do anything else and the COM can make the call to CorelDRAW with the VB request.
Unfortunately sometimes the posted message doesn't get through to CorelDRAW's message pump until another "regular" message (such as mouse, keyboard, screen painting, system timer, etc) arrives. At this time, VB request is processed and finally that normal message is processed as well. The reason why a message from COM doesn't get to CorelDRAW's message queue is unknown so far...
As for the work-around, the easiest way would be to force CorelDRAW to process its message queue to post some dummy message to it. I did the following in the past and seemed to help:
Have another thread or process post a neutral message such as WM_NULL to Draw's message queue. Here is how you can do this from VB. In your main VB program, launch another EXE that would post a WM_NULL message to CorelDRAW every now and then. Here is how I did it. Here are the sources of the two EXEs.
//////////// Main.exe ///////////////////
Code:
Option Explicit
Private Declare Function CreateEvent Lib "kernel32" _
Alias "CreateEventA" (ByVal Attr As Long, _
ByVal bManualReset As Long, ByVal bInitialState As Long, _
ByVal lpName As String) As Long
Private Declare Function SetEvent Lib "kernel32" _
(ByVal hEvent As Long) As Long
Private Declare Function CloseHandle Lib "kernel32" _
(ByVal hObject As Long) As Long
Private Declare Function WaitForSingleObject Lib "kernel32" _
(ByVal hHandle As Long, ByVal dwMilliseconds As Long) As Long
Sub Main()
Dim i As Long
Dim draw As New CorelDRAW.Application
Dim hEvent As Long
' Create interprocess communication object
' (to let Poster.exe know when to exit)
hEvent = CreateEvent(0, 0, 0, "MySyncEvent")
If hEvent Then
' Start the helper program to post a message to CorelDRAW
Shell App.Path & "\Poster.exe"
End If
' Do some stuff in CorelDRAW
For i = 0 To 100
draw.ActiveLayer.CreateRectangle 0, 0, 2, 2
Next i
If hEvent Then
' Signal the event and let Poster.exe quit
SetEvent hEvent
CloseHandle hEvent
End If
End Sub
//////////// Poster.exe /////////////////
Code:
Option Explicit
Private Declare Function CloseHandle Lib "kernel32" _
(ByVal hObject As Long) As Long
Private Declare Function PostMessage Lib "user32" _
Alias "PostMessageA" (ByVal hwnd As Long, _
ByVal wMsg As Long, ByVal wParam As Long, _
ByVal lParam As Long) As Long
Private Declare Function WaitForSingleObject Lib "kernel32" _
(ByVal hHandle As Long, ByVal dwMilliseconds As Long) As Long
Private Declare Function OpenEvent Lib "kernel32" _
Alias "OpenEventA" (ByVal dwDesiredAccess As Long, _
ByVal bInheritHandle As Long, ByVal lpName As String) As Long
Private Const WM_NULL As Long = 0
Sub Main()
Dim draw As New CorelDRAW.Application
Dim hEvent As Long, hDrawWnd As Long, Status As Long
hEvent = OpenEvent(&H100002, 0, "MySyncEvent")
hDrawWnd = draw.AppWindow.Handle
Do
' if status in not zero, then we raised the event.
' Quit the application.
' Post the message every 0.5 seconds (500 milliseconds)
Status = WaitForSingleObject(hEvent, 500)
PostMessage hDrawWnd, WM_NULL, 0, 0
Loop While Status <> 0
CloseHandle hEvent
End Sub
If you program in C++ you can implement the Poster.exe as a separate thread within your application, so you don't have to create a new process. Unfortunately VB is not thread-safe and it will crash if you try to start another thread...
I hope this helps.
|
Quote:
Originally Posted by Craig Tucker
I have implemented the Corel wakeup code but am running into timing issues... Under normal circumstances, I can process an image in less than 5 seconds... Now processing time is up over 15 seconds. Here's what I think I'm going to do: Corel sleeping doesn't occur very often and my application checks every 3 seconds for completion, 12 times. If no image has been created (timeout-most likely Corel sleeping) I'm going to call the wakeup routine... Any suggestions?
The culprit seems to be here: Status = WaitForSingleObject(hEvent, 500)
This takes a while to get a result back of 0.
Thanks Much,
Craig...
BTW, Here's wakeup routine...
Code:
Private Sub WakeUpCorel()
Dim i As Long
Dim draw As New CorelDRAW.Application
Dim mDoc As CorelDRAW.Document
Dim hEvent As Long
hEvent = CreateEvent(0, 0, 0, "MySyncEvent")
If hEvent Then
Shell Me.tcCorelDummyAppPath & "\DummyApp.Exe"
End If
Set mDoc = draw.CreateDocument()
For i = 0 To 10
mDoc.ActiveLayer.CreateRectangle 0, 0, 2, 2
Next i
If hEvent Then
SetEvent hEvent
CloseHandle hEvent
End If
mDoc.Dirty = False
mDoc.Close
Set draw = Nothing
End Sub
|
Quote:
Originally Posted by Alex
I'm not sure if you know how those events work in Win32 API. I'll try to explain more in case it helps.
First let's take the original code of the two applications that I showed earlier (see above)
Your main program creates a global event object with MySyncEvent name. This object is used to let the helper application know when the main program finished execution. A Win32 event can have two states: signalled or cleared. CreateEvent creates an event object and by default sets it to cleared status (one of those zeros in call to CreateEvent function). The event remains in cleared state until Main.exe calls SetEvent function which signals the object. When the object is signaled, this lets Poster.exe know that Main.exe is finished and there's no need to post all those dummy messages to CorelDRAW.
When Poster.exe is launched it uses WaitForSingleObject to see if the event was signalled. Normally you would have a loop that checks if the event is signaled or not and then check again until it is. However the problem with this approach is that the application is constantly running and eats up CPU pretty fast. WaitForSingleObject just stops the program until the event is signaled or the timeout occurs. By default, it waits for 0.5 seconds and exits. If the return value is not zero, that means that the event was signaled and we should quit the program, otherwise we just post a message and wait for another 0.5 seconds.
By doing this, Poster.exe sleeps most of the time and posts a message to CorelDRAW's message queue twice a second. You can adjust the timeout interval to set how frequently the dummy messages should be sent to Draw. WaitForSingleObject will return 0 only when Main.exe finishes and calls SetEvent method to signal the event object. In response to this, Poster.exe quits as well.
You can implement a more robust "sensing" technology. For example, create two event objects. One will be used to signal when Main.exe finishes executing as in the previous example. However the second one could be used to indicate when every CorelDRAW command is executed. Poster.exe will check the state of that second event and, if it is not signaled within some time interval (let's say, 2 seconds) it means that some command is "stuck". I guess I'll try to create another program for you that would illustrate this approach...
' ==================== Main.exe ==========================
Code:
Option Explicit
Private Declare Function CreateEvent _
Lib "kernel32" Alias "CreateEventA" _
(ByVal Attr As Long, ByVal bManualReset As Long, _
ByVal bInitialState As Long, _
ByVal lpName As String) As Long
Private Declare Function SetEvent Lib "kernel32" _
(ByVal hEvent As Long) As Long
Private Declare Function CloseHandle _
Lib "kernel32" (ByVal hObject As Long) As Long
Private Declare Function WaitForSingleObject _
Lib "kernel32" (ByVal hHandle As Long, _
ByVal dwMilliseconds As Long) As Long
Sub Start()
Dim i As Long
Dim draw As New coreldraw.Application
Dim hSyncEvent As Long, hFreezeEvent As Long
' Create thread communication objects
hSyncEvent = CreateEvent(0, 0, 0, "MySyncEvent")
hFreezeEvent = CreateEvent(0, 0, 0, "MyFreezeEvent")
If hSyncEvent <> 0 And hFreezeEvent <> 0 Then
Shell App.Path & "\Poster.exe"
End If
draw.Optimization = True
For i = 1 To 500
draw.ActiveLayer.CreateRectangle 0, 0, 2, 2
' Signal the completion of the operation
SetEvent hFreezeEvent
Next i
draw.Optimization = False
If hFreezeEvent Then
SetEvent hFreezeEvent
CloseHandle hFreezeEvent
End If
If hSyncEvent Then
' Signal the event and let the Poster.exe quit
SetEvent hSyncEvent
CloseHandle hSyncEvent
End If
End Sub
' ====================== Poster.exe =================
Code:
Option Explicit
Private Declare Function CloseHandle _
Lib "kernel32" (ByVal hObject As Long) As Long
Private Declare Function PostMessage _
Lib "user32" Alias "PostMessageA" _
(ByVal hWnd As Long, ByVal wMsg As Long, _
ByVal wParam As Long, ByVal lParam As Long) As Long
Private Declare Function WaitForSingleObject _
Lib "kernel32" (ByVal hHandle As Long, _
ByVal dwMilliseconds As Long) As Long
Private Declare Function OpenEvent Lib "kernel32" _
Alias "OpenEventA" (ByVal dwDesiredAccess As Long, _
ByVal bInheritHandle As Long, _
ByVal lpName As String) As Long
Private Const WM_NULL As Long = 0
Sub Main()
Dim hSyncEvent As Long, hFreezeEvent As Long
Dim Draw As New coreldraw.Application
Dim hDrawWnd As Long
Dim Status As Long
hDrawWnd = Draw.AppWindow.Handle
hSyncEvent = OpenEvent(&H100002, 0, "MySyncEvent")
hFreezeEvent = OpenEvent(&H100002, 0, "MyFreezeEvent")
Do
' Check if FreezeEvent is raised.
' If it times out, it means that CorelDRAW froze
' Give 2 seconds for a CorelDRAW command to execute
Status = WaitForSingleObject(hFreezeEvent, 2000)
If Status <> 0 Then
' OK, Draw is frozen.
' Let's try to resurrect it by posting
' the dummy message to its message queue
PostMessage hDrawWnd, WM_NULL, 0, 0
End If
' Check to see if hSyncEvent was signaled.
' If it was, quit the application
' Timeout is set to 0, so just check and
' return immediately
Status = WaitForSingleObject(hSyncEvent, 0)
Loop While Status <> 0
CloseHandle hSyncEvent
CloseHandle hFreezeEvent
End Sub
Here MySyncEvent is used to indicate when Main.exe is finished, so the Poster.exe can quit also. hSyncEvent is the main communication object between Main.exe and Poster.exe. It gets signaled after every CorelDRAW command. If the object is not signaled within 2 seconds, Poster.exe will post a message to CorelDRAW trying to wake it up.
It is not necessary to signal the event after every Draw OM command. You can spread the SetEvent command within some intervals that you believe should be able to execute within 2 seconds or so. For example, try to set the event after every fifth CreateRectangle command or so.
Another problem here could be that launching the Poster.exe with Shell command from Main.exe could take several seconds (on my machine it takes as much as 10 seconds sometimes). So this could be the source of your 12 seconds vs 3 seconds processing time. To resolve this issue, make Poster.exe a completely independent program and do not exit it when Main.exe quits (remove the hSyncEvent). You might want to create the Event objects in Poster.exe and open them in Main.exe, so Poster.exe owns the event objects. Well, it may seem a little confusing, but I guess you've go the idea...
Let me know if something is still not clear :-)
|
I hope it shed some light on the problem for you.
|