Cannot load to stream on onquit event of Word Application - delphi

I open a document with
WordApplication1.Connect;
WordApplication1.Documents.OpenOld(FileNameOLE,EmptyParam,EmptyParam,EmptyParam,
EmptyParam,EmptyParam,EmptyParam,EmptyParam,EmptyParam,EmptyParam);
WordApplication1.Visible:=true;
and I want to save it to a Stream after it is closed but I get below error.
"Cannot open file used by another process."
What to do ? Thanks for help.
Sample
procedure TPatient.WordApplication1Quit(Sender: TObject);
var mem:TMemoryStream;
begin
WordApplication1.Disconnect;
WordApplication1.Quit;
//I get filename to global widestring variable File_OLE
mem:=Tmemorystream.Create;
mem.LoadFromFile(File_OLE); -------->>>>error here
mem.Position:=0;

It seems like Word is still blocking the access to the file. So you may have to wait a few milliseconds to seconds until the file is released. You can achieve this by Sleep and/or retrying to save a few times

In your sample procedure, Word is still running when you want to load the document in your TMemoryStream. Word is handling it's quit event at that time (go figure :P ) and hasn't shutdown yet.
What you could do is start a timer (TTimer with an interval of 500/1000) on the quit event and open your document in the timer event. Also don't call WordApplication1.Quit, because you are already quitting, just disconnect your WordApplication1.
An even better solution is to not rely on the quit event. Because when you start Word as in your first code section, and you open another Word document from Windows Explorer, the quit event won't come by when you close the document your application opened. Word will still remain active because it's hosting the other document too.
Add a TWordDocument to your form and when you open the document, connect the TWordDocument to the opened document. In the TWordDocument.Close event you can start a Timer and when the Timer fires, you can load your Document. An even better way is to not use timers but just save the document on the OnClose event to another file (.SaveAs) and load that file into the memorystream. This way you can be sure that the file is not opened by Word.

Related

Delphi - Calling RenameFile results in False; appears to be due to the (.pdf) file in use

I have code that calls RenameFile().
The Result passed back is sometimes False; this appears to happen when the (.pdf) file is in use.
I have seen what appears to be a 'standard' message loop in everyday programs where, if an operation can not be executed against a file that is in use, the message prompts the user to close the file and try again (or Cancel).
Is there a function in Delphi that does this, or does anyone have a coding suggestion that mimics this behavior?
I am working in Delphi-Tokyo.
Thanks!

JEDI Visual Component Library : JvAlarms component (a few questions)

I decided to try the JvAlarms component. So I did :
procedure TForm1.Button1Click(Sender_TObject);
begin
jvAlarms1.Add.Name :=Edit1.Text;
jvAlarms1.Add.Time := dxDateTimeWheelPicker1.DateTime;
label1.caption:=datetimetostr(dxDateTimeWheelPicker1.DateTime);
jvAlarms1.Active:=True;
end;
Now, the strange part is that when I set the alarm and run the application,immediately I get a popup window with my alarm message.
Is this by design ?
After I close this message the application will later trigger the alarm I have set on time.I am just wondering if this immediate popup window is by default or you can turn it off and how. If you can not, is it possible to modify it so you can at least say something to the user like 'you have set the alarm : alarm name, to fire : alarmtime'.
Second question regards the alarm message.
How do you get the alarm message name when the alarm fires ?
I tried :
ShowMessage('Alarm:'+ jvAlarms1.Name);
but it does not seem to work.
I can get it with :
ShowMessage('Alarm:'+jvAlarms1.Items[0].Name;
But I do not know the indexes of the alarms added!? So I can not use that.
Any way I can retrieve the list of alarms added by my code ?
Third question regard the alarms storage.
Do you load them from *.ini or can you use a database ?
I could not find examples of such usage anywhere (over here search results turn '0') so I would be grateful if
someone could point me in the right direction.
You added two alarms because you called Add twice. Call it once instead:
var
Item: TJvAlarmItem;
....
Item := jvAlarms1.Add;
Item.Name :=Edit1.Text;
Item.Time := dxDateTimeWheelPicker1.DateTime;
When the alarm fires the component's OnAlarm event receives a reference to the specific alarm that fired. You can read the name from that reference.
It is entirely up to you where you store the alarms in your application.

Eseper event timeout

I want to timeout events individually for each incoming event in esper. How to achieve that?
If i use time or batch windows, it will wait for other events to first fill the window ,only then the events are moved to rstream.
Use a named window with keep-all and put the condition when events get deleted into an on-delete.
create window CustomExpiryWindow.win:keepall() as MyEvent
insert into CustomExpiryWindow select * from MyEvent
on <.......> delete from CustomExpiryWindow where <......>
In Alternative there is an extension API for data windows where you could write code to keep and expire events.

node.js process out of memory error

FATAL ERROR: CALL_AND_RETRY_2 Allocation Failed - process out of memory
I'm seeing this error and not quite sure where it's coming from. The project I'm working on has this basic workflow:
Receive XML post from another source
Parse the XML using xml2js
Extract the required information from the newly created JSON object and create a new object.
Send that object to connected clients (using socket.io)
Node Modules in use are:
xml2js
socket.io
choreographer
mysql
When I receive an XML packet the first thing I do is write it to a log.txt file in the event that something needs to be reviewed later. I first fs.readFile to get the current contents, then write the new contents + the old. The log.txt file was probably around 2400KB around last crash, but upon restarting the server it's working fine again so I don't believe this to be the issue.
I don't see a packet in the log right before the crash happened, so I'm not sure what's causing the crash... No new clients connected, no messages were being sent... nothing was being parsed.
Edit
Seeing as node is running constantly should I be using delete <object> after every object I'm using serves its purpose, such as var now = new Date() which I use to compare to things that happen in the past. Or, result object from step 3 after I've passed it to the callback?
Edit 2
I am keeping a master object in the event that a new client connects, they need to see past messages, objects are deleted though, they don't stay for the life of the server, just until their completed on client side. Currently, I'm doing something like this
function parsingFunction(callback) {
//Construct Object
callback(theConstructedObject);
}
parsingFunction(function (data) {
masterObject[someIdentifier] = data;
});
Edit 3
As another step for troubleshooting I dumped the process.memoryUsage().heapUsed right before the parser starts at the parser.on('end', function() {..}); and parsed several xml packets. The highest heap used was around 10-12 MB throughout the test, although during normal conditions the program rests at about 4-5 MB. I don't think this is particularly a deal breaker, but may help in finding the issue.
Perhaps you are accidentally closing on objects recursively. A crazy example:
function f() {
var shouldBeDeleted = function(x) { return x }
return function g() { return shouldBeDeleted(shouldBeDeleted) }
}
To find what is happening fire up node-inspector and set a break point just before the suspected out of memory error. Then click on "Closure" (below Scope Variables near the right border). Perhaps if you click around something will click and you realize what happens.

How can I fix "Cannot open clipboard: Access Denied" errors?

I am using the following code to copy text to the clipboard:
Clipboard.Open;
try
Clipboard.AsText := GenerateClipboardText;
finally
Clipboard.Close;
end;
Seemingly at random I get "Cannot open clipboard: Access Denied" errors. I'm guessing that these errors are caused by other application locking the clipboard, but I never seem to be doing anything with other applications that should cause the locks.
Strangely my users seem to be reporting more of the errors with Vista and Windows 7 than with XP.
Is there a way to check if the clipboard is locked before trying to access it?
This is not a Delphi problem. Because the clipboard can be locked any moment, even if you check, if the clipboard is currently not locked, it might become locked directly after the check.
You have two possibilities here:
Don't use the Delphi clipboard class. Instead use raw API functions, where you have a little more fine-grained control over possible error situations.
Expect your code to fail by adding an exception handler. Then add some retry code, i.e. retry to set the text three times, perhaps with exponential backoff, before throwing your own error.
I'd recommend the second solution, because it'd be the more Delphi-like approach and in the end will result in cleaner code.
var
Success : boolean;
RetryCount : integer;
begin
RetryCount := 0;
Success := false;
while not Success do
try
//
// Set the clipboard here
//
Success := True;
except
on E: EClipboardException do
begin
Inc(RetryCount);
if RetryCount < 3 then
Sleep(RetryCount * 100)
else
raise Exception.Create('Cannot set clipboard after three attempts');
end else
raise; // if not a clipboard problem then re-raise
end;
end;
Strangely my users seem to be
reporting more of the errors with
Vista and Windows 7 than with XP
This may have to do with how Vista/Win7 deal with clipboard viewer notification. While they still support the XP "clipboard viewer chain", which sends one notification message that must be re-sent to each listener in turn (and if one app fails to do this, the other apps aren't notified). Starting with Vista, apps are notified directly. And there's nothing to keep them from trying to access the clipboard all at once.
Analogy: I have 3 children. I have a cake. With XP rules, I tell the oldest child to have some cake, then tell the next oldest child to have a slice. She gets her slice, tells her brother, he gets his, and tells his brother, who gets his, and everything proceeds in an orderly fashion.
Problem: The middle child takes the cake to his room, doesn't tell the youngest, and the youngest misses out.
With Vista/Windows7, that system still exists. But newer apps can request to be notified immediately, by me, as soon as the cake arrives in the kitchen. I shout "cake is ready!" and they all show up at the same time and try to grab some. But there's only one serving knife, so they have to keep reaching for the knife, failing to get it, and waiting for the next opportunity.
Try to check GetClipboardOwner, if it's not null and not your Application.Handle, you cannot Open to modify it's content.
And even it seems good to go, it might not be anymore when you actually do it.
So add a try except in a loop until you get it or give up nicely (notifying the user for instance).
There is no way to check for something and then depending on the result do something else with the expectation that it could not fail, because unless the check and the action are one atomic operation there is always the possibility that another process or thread does the same in parallel.
This holds whether you try to open the clipboard, open a file, create or delete a directory - you should simply try to do it, maybe several times in a loop, and gracefully handle errors.
First of all please notice that this probably isn't a problem in your application. Other applications locked the clipboard or messed up the notification chain and now your application fails to access it. When I do have problems like this I restart the computer and they magically go away... well... at least until I run again the application that creates the problem.
This code (not checked in Delphi) may help you. It won't fix the problem is the notification chain is broken (nothing except a PC restart will ever fix it) but it will fix the problem if an application is locking the clipboard for a while. Increase the MaxRetries if that pesky application keeps the clipboard locked for A REALLY LONG time (seconds):
procedure Str2Clipboard(CONST Str: string; iDelayMs: integer);
CONST
MaxRetries= 5;
VAR RetryCount: Integer;
begin
RetryCount:= 0;
for RetryCount:= 1 to MaxRetries DO
TRY
inc(RetryCount);
Clipboard.AsText:= Str;
Break;
EXCEPT
on Exception DO
if RetryCount = MaxRetries
then RAISE Exception.Create('Cannot set clipboard')
else Sleep(iDelayMs)
END;
end;
Also, it may be a good idea to drop the 'raise' and convert it to a function and use it like this:
if not Str2Clipboard
then Log.AddMsg('Dear user, other applications are blocking the clipboard. We have tried. We really did. But it didn''t work. Try again in a few seconds.');
I guess you are running your app on Win 8 or higher.
Just right-click on your App .exe file, go to Compatibility tab and change compatibility mode on Windows XP or lower versions. It'll work, guaranteed!

Resources