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.
Related
This might seem a little sketch, but I am trying to automate a minor inconvenience that I have.
Every day at the beginning of the day, I want to start a chat on skype with a certain group of people.
I created an app so I can just type chat daily at the command line and it will create the chat automatically. The issue is that it's just a blank chat and I need to start the chat with the same message. I want to be able to pass a -m flag to the app followed by the message that should be sent to everyone. The issue is being able to get that text into the skype chat.
Here are my thoughts on how this could be done:
Send Keyboard events so that the text entered into the text box and then sent in the chat.
Add the message to the clipboard, and subsequently paste the message. If I can't get it to send the text and I have to manually click [enter] I'm ok with that.
Lastly, if there's not a way to send keyboard events already, a much more involved approach would be to somehow emulate a keyboard to the system.
So basically I want to know if there's a way to control the clipboard of the system (copy/paste) or if there's a way to send keypresses to the system.
If you have any other ideas on how this, or if this, can be achieved, I'd like to hear.
Thanks so much in advance
Have a look at the win32 package, which has some bindings from the Win32 Windows Api to Dart, it has a ton of examples, but what you need should be something like this:
final kbd = KEYBDINPUT.allocate();
// Send the A key.
kbd.wVk = VK_A;
var result = SendInput(
1, Pointer.fromAddress(kbd.addressOf.address), sizeOf<KEYBDINPUT>());
if (result != 1) print('Error: ${GetLastError()}');
kbd.dwFlags = KEYEVENTF_KEYUP;
result = SendInput(
1, Pointer.fromAddress(kbd.addressOf.address), sizeOf<KEYBDINPUT>());
if (result != 1) print('Error: ${GetLastError()}');
(from https://github.com/timsneath/win32/blob/master/example/sendinput.dart#L20-L30)
After logon, i would like to send a chat message to the Guild channel.
I'm currently listening for events:
PLAYER_ENTERING_WORLD
GUILD_ROSTER_UPDATE
Once those have fired (in order), i'd like to send a chat message. However, it never sends.
Code:
print("Should_send")
SendChatMessage(msgToSend, "GUILD");
It's also worth noting that if i then trigger this manually, it works.
I do see the "Should_send" print statement appearing in the default chat window each time - as expected. I've also checked that "msgToSend" contains content - and is less than 255 characters.
So, when can i call SendChatMessage?
Ok, in order to be able to send a chat message to guild, you need to wait for the event "CLUB_STREAM_SUBSCRIBED" to fire.
This is due to the Guild channel becoming a "community" channel of sorts - previously, it seems this wasn't required.
So, adding an event listener:
frame:RegisterEvent("CLUB_STREAM_SUBSCRIBED");
Resolves the issue.
You will likely need to set a flag for the event, then print later on another event.
You can send chat messages any time after you see the welcome message or after the welcome message was posted. Which is pretty soon after you able to receive events from your frames.
Here is what I would do to complete a similar mission:
Just put your send code in a macro to test it first. Don't worry about timing the message until you see it work in a macro.
You can make your own print to send generic messages to the chat window which should always work similar to:
function MyPrint( msg, r, g, b, frame, id)
(frame or DEFAULT_CHAT_FRAME):AddMessage(msg, r or 1, g or 1, b or 0, id or 0)
end
-- put these in your event handlers
MyPrint("event PLAYER_ENTERING_WORLD")
MyPrint("event GUILD_ROSTER_UPDATE")
And use that for debugging instead.
You need to divide and conquer the problem, because there are so many things that could be wrong causing your issue, no one here can really have a definitive answer.
I know for sure that if you try to write to chat before the welcome message with print it at least used to not work. I remember spooling messages in the past until a certain event had fired then printing them.
I'm looking for a way to detect how long a key has been held down in a Delphi project and warn user.
I'm working on a chat program and need to see if the person is holding down a letter, like the W key, to spam that chat box. I'll give sample what trying to do in Delphi 7:
//Looking up if key in use and held for lets say 20 seconds
if (GetAsyncKeyState(Byte(VkKeyScan('W'))) shl 20) <> 0 then
begin
ShowMessage('W Key Held down too long!');
end;
I'm not sure whether GetAsyncKeyState will give me that information, though. If it doesn't, what will?
Windows does not report the duration of how long a key has been held down, only that the same WM_KEY... message is being repeated for a key that is held down. You have to keep track of the duration yourself manually. When you detect a WM_KEYDOWN message with its wParam bit 30 set to 1, if you are not tracking that key yet, start tracking it and store the current system/tick with it, otherwise grab the current system/tick time, calculate the duration, and act accordingly. When you receive a WM_KEYUP message, stop tracking that key if you are tracking it.
I use the StopWatch class in the StopWatch unit to check for timing. Here's how you could try using it for what you're trying to do:
uses StopWatch;
//in the Form's private declaration:
StopWatch : TStopWatch;
//in the Form's onCreate:
StopWatch := TStopWatch.Create(nil);
//in the Form's onDestroy:
StopWatch.Free();
//in the form/box onKeyDown:
StopWatch.Start();
//in the form/box onChange:
if (StopWatch.ElapsedMiliseconds > 1000)
ShowMessage('W Key Held down too long!');
//in the form/box onKeyUp:
StopWatch.Stop();
StopWatch.Start();
StopWatch.Stop();
There are likely lots of other ways to accomplish what you're trying to do though. But for a quick try this should work.
The reason I stop and start the stopwatch again onKeyUp is to clear out ElapsedMiliseconds, in case the user changes the box with some method other than the keyboard, after having been alerted -- so they won't get alerted twice.
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.
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!