├── Example
├── DelphiTimerQueue.res
├── DelphiTimerQueue.dpr
├── DelphiTimerQueue.deployproj
└── DelphiTimerQueue.dproj
└── README.md
/Example/DelphiTimerQueue.res:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grijjy/DelphiTimerQueue/HEAD/Example/DelphiTimerQueue.res
--------------------------------------------------------------------------------
/Example/DelphiTimerQueue.dpr:
--------------------------------------------------------------------------------
1 | program DelphiTimerQueue;
2 |
3 | {$APPTYPE CONSOLE}
4 |
5 | {$R *.res}
6 |
7 | uses
8 | {$IFDEF MSWINDOWS}
9 | Windows,
10 | Grijjy.TimerQueue.Win,
11 | {$ENDIF}
12 | {$IFDEF LINUX}
13 | Posix.Pthread,
14 | Grijjy.TimerQueue.Linux,
15 | {$ENDIF}
16 | System.Generics.Collections,
17 | System.SyncObjs,
18 | System.SysUtils;
19 |
20 | type
21 | TMyClass = class(TObject)
22 | private
23 | FTimerQueue: TgoTimerQueue;
24 | private
25 | procedure OnTimer(const ASender: TObject);
26 | public
27 | constructor Create;
28 | destructor Destroy; override;
29 | end;
30 |
31 | var
32 | MyClass: TMyClass;
33 |
34 | procedure Log(const AText: String);
35 | begin
36 | Writeln(AText);
37 | end;
38 |
39 | { TMyClass }
40 |
41 | constructor TMyClass.Create;
42 | var
43 | I: Integer;
44 | Handle: THandle;
45 | begin
46 | FTimerQueue := TgoTimerQueue.Create;
47 | for I := 1 to 5 do // add 5 timers to the queue
48 | begin
49 | // set interval to 1000ms, and the event to OnTimer()
50 | Handle := FTimerQueue.Add(1000, OnTimer);
51 | Writeln(Format('Timer Added (Handle=%d)', [Handle]));
52 | end;
53 | end;
54 |
55 | destructor TMyClass.Destroy;
56 | begin
57 | FTimerQueue.Free;
58 | inherited;
59 | end;
60 |
61 | procedure TMyClass.OnTimer(const ASender: TObject);
62 | var
63 | Timer: TgoTimer;
64 | begin
65 | Timer := ASender as TgoTimer;
66 | // each timer callback event with unique handle and threadid
67 | Log(Format('OnTimer (Handle=%d, ThreadId=%d)', [Timer.Handle, GetCurrentThreadId]));
68 | end;
69 |
70 | begin
71 | try
72 | MyClass := TMyClass.Create;
73 | try
74 | Readln; // wait
75 | finally
76 | MyClass.Free;
77 | end;
78 | except
79 | on E: Exception do
80 | Writeln(E.ClassName, ': ', E.Message);
81 | end;
82 | end.
83 |
--------------------------------------------------------------------------------
/Example/DelphiTimerQueue.deployproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 12
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | DelphiTimerQueue\
18 | DelphiTimerQueue.exe
19 | ProjectOutput
20 | 0
21 |
22 |
23 | True
24 | True
25 |
26 |
27 |
28 |
29 | DelphiTimerQueue\
30 | DelphiTimerQueue
31 | ProjectOutput
32 | 1
33 |
34 |
35 | True
36 | True
37 |
38 |
39 |
40 |
41 | DelphiTimerQueue.app\Contents\MacOS\
42 | libcgunwind.1.0.dylib
43 | DependencyModule
44 | 1
45 |
46 |
47 | True
48 |
49 |
50 | DelphiTimerQueue.app\Contents\MacOS\
51 | libcgsqlite3.dylib
52 | DependencyModule
53 | 1
54 |
55 |
56 | True
57 |
58 |
59 |
60 |
61 |
62 | DelphiTimerQueue.app\
63 | libcgunwind.1.0.dylib
64 | DependencyModule
65 | 1
66 |
67 |
68 | True
69 |
70 |
71 | DelphiTimerQueue.app\
72 | libPCRE.dylib
73 | DependencyModule
74 | 1
75 |
76 |
77 | True
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Cross-platform timer queues for Windows and Linux
2 | In this article we will show how to use timer queues to create fast, lightweight, multi-threaded OnTimer() events that work on Windows and Linux in a uniform method using our helper class TgoTimerQueue. We also discuss how they operate on Windows and Linux and show an example application using timer queues.
3 |
4 | For more information about us, our support and services visit the [Grijjy homepage](http://www.grijjy.com) or the [Grijjy developers blog](http://blog.grijjy.com).
5 |
6 | The example contained here depends upon part of our [Grijjy Foundation library](https://github.com/grijjy/GrijjyFoundation).
7 |
8 | The source code and related example repository is hosted on GitHub at [https://github.com/grijjy/DelphiTimerQueue](https://github.com/grijjy/DelphiTimerQueue).
9 |
10 | ## What is a timer queue?
11 | You are probably already familiar with Delphi timer objects. You set an interval and your OnTimer() event is called at the given interval. Timer queues are a bit different. They provide a lightweight object to handle numerous timers that fire at different intervals. These lightweight objects are handled from a thread pool that is managed by the operating system so that multiple timers can be handled by a single thread.
12 |
13 | If your callback event executes fast enough so that it take less time than the internal rate of the timer, then it is possible for the operating system to use only a single thread to call your OnTimer() events. However the operating system handles the issues related to making sure that more threads are used if other OnTimer events must be called.
14 |
15 | Timer queues tend to be much more precise in their interval rate and scale up more efficiently than traditional timers. Since they are operating from a thread, you have to make sure anything you do within the event itself is thread-safe. With traditional Delphi timers your OnTimer() events are happening in the main application thread so this is not an issue. However, this may be a problem for existing code that is not thread-safe or a may be a benefit if you need your timer events to happen in the background.
16 |
17 | ## Windows CreateTimerQueueTimer
18 | On Windows we have a few APIs related to timer queues including `CreateTimerQueue`, `CreateTimerQueueTimer`, `ChangeTimerQueueTimer` and `DeleteTimerQueueTimer`. These APIs allow you to define a queue to manage the timers and create individual handles to timer objects. With each given handle you specify an interval rate and a callback procedure.
19 |
20 | To create a Windows timer queue:
21 | ```Delphi
22 | TimerQueueHandle := CreateTimerQueue;
23 | ```
24 |
25 | To destroy a Windows timer queue:
26 | ```Delphi
27 | DeleteTimerQueueEx(TimerQueueHandle, INVALID_HANDLE_VALUE);
28 | ```
29 |
30 | To create a timer and add it to the queue:
31 | ```Delphi
32 | if CreateTimerQueueTimer(Handle, TimerQueueHandle, @WaitOrTimerCallback, MyObject, 0, Interval, 0) then
33 | begin
34 | // success
35 | end
36 | ```
37 | In the above example, `Handle` is an `out` parameter that contains the resulting handle of the timer object once it is created. `TimerQueueHandle` is the primary handle for the timer queue. `WaitOrTimerCallback` is your callback procedure that is called for every timer event.
38 |
39 | The API allows you to specify your own user data, so in this case we provide our own MyObject that we will retrieve in the callback event. You can simply pass a Delphi TObject to `CreateTimerQueueTimer()` and use it directly by defining it as a parameter in the callback to `WaitOrTimerCallback`.
40 |
41 | ```Delphi
42 | procedure WaitOrTimerCallback(MyObject: TMyObject; TimerOrWaitFired: ByteBool); stdcall;
43 | begin
44 | if TimerOrWaitFired then
45 | begin
46 | // do something
47 | end;
48 | end;
49 | ```
50 | The `stdcall` procedure will be called for each interval for each and every timer in the queue. In other words, you can expect this event to be called by multiple threads and it needs to be completely thread safe.
51 |
52 | In our example code we use MyObject to actually call an OnTimer() event that is part of MyObject.
53 |
54 | To delete a timer from the queue:
55 | ```Delphi
56 | if DeleteTimerQueueTimer(TimerQueueHandle, Handle, INVALID_HANDLE_VALUE) then
57 | ATimer.Free;
58 | ```
59 | > The DeleteTimerQueueTimer method will block until all the pending callbacks for this specific timer object are completed.
60 |
61 | ## Linux timerfd_create
62 |
63 | Linux offers a special set of APIs including `timerfd_creat`e and `timerfd_settime` that allow you to define descriptors for timer objects. These are used in conjunction with the EPoll APIs to manage a queue of lightweight timer objects from a thread pool.
64 |
65 | Just like Windows timer queues, multiple timer objects can be handled by the same thread or different threads allowing your application using timer objects to scale up more efficiently.
66 |
67 | To create a Linux timer queue:
68 | ```Delphi
69 | TimerQueueHandle := epoll_create(IGNORED);
70 | ```
71 | Since EPoll manages the queue for us, we create an EPoll handle.
72 |
73 | To destroy a Linux timer queue:
74 | ```Delphi
75 | __close(TimerQueueHandle);
76 | ```
77 |
78 | To create a timer and add it to the queue:
79 | ```Delphi
80 | Handle := timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK);
81 | Event.data.ptr := MyObject;
82 | Event.events := EPOLLIN or EPOLLET;
83 | if epoll_ctl(TimerQueueHandle, EPOLL_CTL_ADD, Handle, @Event) <> -1 then
84 | begin
85 | // success
86 | end;
87 | ```
88 | In the above example we create a timer object by calling timerfd_create requesting a MONOTONIC timer, a timer that isn't impacted by system clock changes, and TFD_NONBLOCK for a non-blocking event timer. Then we add the timer to the queue managed by EPoll by calling `epoll_ctl`. Our `MyObject` is assigned to the `Event` data.ptr parameter so we can access it during the callback just like we do on Windows.
89 |
90 | To delete a timer from the queue:
91 | ```Delphi
92 | epoll_ctl(TimerQueueHandle.Handle, EPOLL_CTL_DEL, Handle, @Event);
93 | ```
94 | We simply call epoll_ctl again with the handle to delete.
95 |
96 | To access the timerfd related APIs we added a new import header unit called Linuxapi.Timerfd that is part of the [Grijjy Foundation library](https://github.com/grijjy/GrijjyFoundation).
97 |
98 | ## Linux epoll_wait event loop
99 | Unlike Windows that directly calls back into your procedure when an internal is reached, on Linux you need to create an event loop that waits for the timer interval to be reached using `epoll_wait`. You perform this inside one or more worker threads.
100 |
101 | Each of the threads will wait for a timer interval, but only one of the threads will handle the event. This allows Epoll to load balance multiple timer events across a thread pool.
102 |
103 | ```Delphi
104 | procedure TTimerQueueWorker.Execute;
105 | var
106 | NumberOfEvents: Integer;
107 | I: Integer;
108 | Event: epoll_event;
109 | TotalTimeouts: Int64;
110 | Timer: TgoTimer;
111 | Error: Integer;
112 | begin
113 | while not Terminated do
114 | begin
115 | NumberOfEvents := epoll_wait(FOwner.Handle, @FEvents, MAX_EVENTS, 100);
116 | if NumberOfEvents = 0 then { timeout }
117 | Continue
118 | else
119 | if NumberOfEvents = -1 then { error }
120 | begin
121 | Error := errno;
122 | if Error = EINTR then
123 | Continue
124 | else
125 | Break;
126 | end;
127 | for I := 0 to NumberOfEvents - 1 do
128 | begin
129 | Timer := FEvents[I].data.ptr;
130 | if (FEvents[I].events AND EPOLLIN) = EPOLLIN then
131 | begin
132 | if __read(Timer.Handle, @TotalTimeouts, SizeOf(TotalTimeouts)) >= 0 then
133 | begin
134 | end;
135 | end;
136 | end;
137 | end;
138 | end;
139 | ```
140 | In the above we still call the `__read()` against the timer event. This clears the timer event from the queue.
141 |
142 | ## Putting it all together
143 | To make it easy to use, we created the TgoTimerQueue class that operates on both Windows and Linux in a uniform manner.
144 |
145 | You simple create a timer queue:
146 | ```Delphi
147 | TimerQueue := TgoTimerQueue.Create;
148 | ```
149 |
150 | Add add one or more timers to the queue:
151 | ```Delphi
152 | MyHandle := TimerQueue.Add(1000, OnTimer);
153 | ```
154 | Here we add a timer than fires every 1000ms and calls your OnTimer() procedure.
155 |
156 | Your OnTimer() event is similar to a standard OnTimer() event in Delphi:
157 | ```Delphi
158 | procedure TMyClass.OnTimer(const ASender: TObject);
159 | var
160 | Timer: TgoTimer;
161 | begin
162 | Timer := ASender as TgoTimer;
163 | // each timer callback event with unique handle and threadid
164 | Log(Format('OnTimer (Handle=%d, ThreadId=%d)', [Timer.Handle, GetCurrentThreadId]));
165 | end;
166 | ```
167 | Each timer has a different handle. We also show the ThreadId so it is clear that that your OnTimer() event needs to be thread-safe.
168 | > Please note that in our example code we call Writeln() to the console. This is not reliable as the Writeln() console routine in Delphi is not thread-safe. Making the console thread-safe is beyond the scope of this article.
169 |
170 | ## Example Application
171 |
172 | The example program for Linux and Windows is hosted on GitHub at [https://github.com/grijjy/DelphiTimerQueue](https://github.com/grijjy/DelphiTimerQueue).
173 |
174 | ## Conclusion
175 | We hope you find timer queues a useful addition to your application. They are certainly nice at solving some real world issues when you need precisely timed threaded callbacks.
176 |
177 | For more information about us, our support and services visit the [Grijjy homepage](http://www.grijjy.com) or the [Grijjy developers blog](http://blog.grijjy.com).
178 |
179 | The base classes described herein are part of our [Grijjy Foundation library](https://github.com/grijjy/GrijjyFoundation).
--------------------------------------------------------------------------------
/Example/DelphiTimerQueue.dproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | {07F979C6-B90A-48CD-B7A1-8B827BB70333}
4 | 18.2
5 | None
6 | DelphiTimerQueue.dpr
7 | True
8 | Debug
9 | Win32
10 | 129
11 | Console
12 |
13 |
14 | true
15 |
16 |
17 | true
18 | Base
19 | true
20 |
21 |
22 | true
23 | Base
24 | true
25 |
26 |
27 | true
28 | Base
29 | true
30 |
31 |
32 | true
33 | Base
34 | true
35 |
36 |
37 | true
38 | Base
39 | true
40 |
41 |
42 | true
43 | Base
44 | true
45 |
46 |
47 | true
48 | Base
49 | true
50 |
51 |
52 | true
53 | Base
54 | true
55 |
56 |
57 | true
58 | Base
59 | true
60 |
61 |
62 | true
63 | Cfg_1
64 | true
65 | true
66 |
67 |
68 | true
69 | Base
70 | true
71 |
72 |
73 | .\$(Platform)\$(Config)
74 | .\$(Platform)\$(Config)
75 | false
76 | false
77 | false
78 | false
79 | false
80 | RESTComponents;emsclientfiredac;FireDACIBDriver;emsclient;FireDACCommon;RESTBackendComponents;soapserver;CloudService;FireDACCommonDriver;inet;FireDAC;FireDACSqliteDriver;soaprtl;soapmidas;$(DCC_UsePackage)
81 | System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace)
82 | DelphiTimerQueue
83 | ..\..\GrijjyFoundation;$(DCC_UnitSearchPath)
84 |
85 |
86 | DBXSqliteDriver;DBXInterBaseDriver;tethering;DataSnapFireDAC;bindcompfmx;fmx;FireDACDBXDriver;dbexpress;IndyCore;dsnap;DataSnapCommon;bindengine;DataSnapClient;IndyIPCommon;bindcompdbx;IndyIPServer;IndySystem;fmxFireDAC;DbxCommonDriver;xmlrtl;DataSnapNativeClient;FireDACDSDriver;rtl;DbxClientDriver;CustomIPTransport;bindcomp;IndyIPClient;dbxcds;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;$(DCC_UsePackage)
87 | $(BDS)\bin\Artwork\Android\FM_LauncherIcon_36x36.png
88 | $(BDS)\bin\Artwork\Android\FM_LauncherIcon_48x48.png
89 | $(BDS)\bin\Artwork\Android\FM_LauncherIcon_72x72.png
90 | $(BDS)\bin\Artwork\Android\FM_LauncherIcon_96x96.png
91 | $(BDS)\bin\Artwork\Android\FM_LauncherIcon_144x144.png
92 | $(BDS)\bin\Artwork\Android\FM_SplashImage_426x320.png
93 | $(BDS)\bin\Artwork\Android\FM_SplashImage_470x320.png
94 | $(BDS)\bin\Artwork\Android\FM_SplashImage_640x480.png
95 | $(BDS)\bin\Artwork\Android\FM_SplashImage_960x720.png
96 | android-support-v4.dex.jar;cloud-messaging.dex.jar;fmx.dex.jar;google-analytics-v2.dex.jar;google-play-billing.dex.jar;google-play-licensing.dex.jar;google-play-services.dex.jar
97 |
98 |
99 | DBXSqliteDriver;fmxase;DBXInterBaseDriver;tethering;DataSnapFireDAC;bindcompfmx;fmx;FireDACDBXDriver;dbexpress;IndyCore;dsnap;DataSnapCommon;bindengine;DataSnapClient;IndyIPCommon;bindcompdbx;IndyIPServer;IndySystem;fmxFireDAC;DbxCommonDriver;xmlrtl;DataSnapNativeClient;FireDACDSDriver;rtl;DbxClientDriver;CustomIPTransport;bindcomp;IndyIPClient;dbxcds;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;$(DCC_UsePackage)
100 |
101 |
102 | DBXSqliteDriver;fmxase;DBXInterBaseDriver;tethering;DataSnapFireDAC;bindcompfmx;fmx;FireDACDBXDriver;dbexpress;IndyCore;dsnap;DataSnapCommon;bindengine;DataSnapClient;IndyIPCommon;bindcompdbx;IndyIPServer;IndySystem;fmxFireDAC;DbxCommonDriver;xmlrtl;DataSnapNativeClient;FireDACDSDriver;rtl;DbxClientDriver;CustomIPTransport;bindcomp;IndyIPClient;dbxcds;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;$(DCC_UsePackage)
103 |
104 |
105 | DBXSqliteDriver;fmxase;DBXInterBaseDriver;tethering;DataSnapFireDAC;bindcompfmx;fmx;FireDACDBXDriver;dbexpress;IndyCore;dsnap;DataSnapCommon;bindengine;DataSnapClient;IndyIPCommon;bindcompdbx;IndyIPServer;IndySystem;fmxFireDAC;DbxCommonDriver;xmlrtl;DataSnapNativeClient;FireDACDSDriver;rtl;DbxClientDriver;CustomIPTransport;bindcomp;IndyIPClient;dbxcds;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;$(DCC_UsePackage)
106 |
107 |
108 | FireDACADSDriver;DatasnapConnectorsFreePascal;FireDACMSSQLDriver;inetdb;emsedge;IndyCore;dsnap;DataSnapCommon;DataSnapConnectors;bindengine;FireDACOracleDriver;FireDACMySQLDriver;FireDACCommonODBC;DataSnapClient;IndySystem;FireDACDb2Driver;FireDACInfxDriver;emshosting;FireDACPgDriver;FireDACASADriver;FireDACTDataDriver;DbxCommonDriver;DataSnapServer;xmlrtl;DataSnapNativeClient;rtl;DbxClientDriver;CustomIPTransport;bindcomp;FireDACODBCDriver;DataSnapIndy10ServerTransport;dsnapxml;dbrtl;IndyProtocols;FireDACMongoDBDriver;DataSnapServerMidas;$(DCC_UsePackage)
109 |
110 |
111 | DBXSqliteDriver;fmxase;DBXInterBaseDriver;tethering;DataSnapFireDAC;FireDACMSSQLDriver;bindcompfmx;DBXOracleDriver;inetdb;emsedge;fmx;fmxdae;FireDACDBXDriver;dbexpress;IndyCore;dsnap;DataSnapCommon;bindengine;DBXMySQLDriver;FireDACOracleDriver;FireDACMySQLDriver;DBXFirebirdDriver;FireDACCommonODBC;DataSnapClient;IndyIPCommon;bindcompdbx;IndyIPServer;IndySystem;fmxFireDAC;emshosting;FireDACPgDriver;FireDACASADriver;FireDACTDataDriver;DbxCommonDriver;DataSnapServer;xmlrtl;DataSnapNativeClient;fmxobj;FireDACDSDriver;rtl;DbxClientDriver;DBXSybaseASADriver;CustomIPTransport;bindcomp;DBXInformixDriver;IndyIPClient;dbxcds;FireDACODBCDriver;DataSnapIndy10ServerTransport;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;inetdbxpress;FireDACMongoDBDriver;DataSnapServerMidas;$(DCC_UsePackage)
112 | true
113 |
114 |
115 | DBXSqliteDriver;fmxase;DBXDb2Driver;DBXInterBaseDriver;vclactnband;vclFireDAC;tethering;svnui;DataSnapFireDAC;FireDACADSDriver;DBXMSSQLDriver;DatasnapConnectorsFreePascal;FireDACMSSQLDriver;vcltouch;vcldb;bindcompfmx;svn;DBXOracleDriver;inetdb;emsedge;fmx;fmxdae;FireDACDBXDriver;dbexpress;IndyCore;vclx;dsnap;DataSnapCommon;DataSnapConnectors;VCLRESTComponents;vclie;bindengine;DBXMySQLDriver;FireDACOracleDriver;FireDACMySQLDriver;DBXFirebirdDriver;FireDACCommonODBC;DataSnapClient;IndyIPCommon;bindcompdbx;vcl;IndyIPServer;DBXSybaseASEDriver;IndySystem;FireDACDb2Driver;dsnapcon;FireDACMSAccDriver;fmxFireDAC;FireDACInfxDriver;vclimg;emshosting;FireDACPgDriver;FireDACASADriver;DBXOdbcDriver;FireDACTDataDriver;DbxCommonDriver;DataSnapServer;xmlrtl;DataSnapNativeClient;fmxobj;vclwinx;FireDACDSDriver;rtl;DbxClientDriver;DBXSybaseASADriver;CustomIPTransport;vcldsnap;bindcomp;appanalytics;DBXInformixDriver;IndyIPClient;bindcompvcl;dbxcds;VclSmp;adortl;FireDACODBCDriver;DataSnapIndy10ServerTransport;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;inetdbxpress;FireDACMongoDBDriver;DataSnapServerMidas;$(DCC_UsePackage)
116 | Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace)
117 | Debug
118 | CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=
119 | 1033
120 | true
121 | $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png
122 | $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png
123 | .\Bin
124 | (None)
125 |
126 |
127 | DBXSqliteDriver;fmxase;DBXDb2Driver;DBXInterBaseDriver;vclactnband;vclFireDAC;tethering;DataSnapFireDAC;FireDACADSDriver;DBXMSSQLDriver;DatasnapConnectorsFreePascal;FireDACMSSQLDriver;vcltouch;vcldb;bindcompfmx;DBXOracleDriver;inetdb;emsedge;fmx;fmxdae;FireDACDBXDriver;dbexpress;IndyCore;vclx;dsnap;DataSnapCommon;DataSnapConnectors;VCLRESTComponents;vclie;bindengine;DBXMySQLDriver;FireDACOracleDriver;FireDACMySQLDriver;DBXFirebirdDriver;FireDACCommonODBC;DataSnapClient;IndyIPCommon;bindcompdbx;vcl;IndyIPServer;DBXSybaseASEDriver;IndySystem;FireDACDb2Driver;dsnapcon;FireDACMSAccDriver;fmxFireDAC;FireDACInfxDriver;vclimg;emshosting;FireDACPgDriver;FireDACASADriver;DBXOdbcDriver;FireDACTDataDriver;DbxCommonDriver;DataSnapServer;xmlrtl;DataSnapNativeClient;fmxobj;vclwinx;FireDACDSDriver;rtl;DbxClientDriver;DBXSybaseASADriver;CustomIPTransport;vcldsnap;bindcomp;appanalytics;DBXInformixDriver;IndyIPClient;bindcompvcl;dbxcds;VclSmp;adortl;FireDACODBCDriver;DataSnapIndy10ServerTransport;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;inetdbxpress;FireDACMongoDBDriver;DataSnapServerMidas;$(DCC_UsePackage)
128 | true
129 | $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png
130 | $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png
131 |
132 |
133 | DEBUG;$(DCC_Define)
134 | true
135 | false
136 | true
137 | true
138 | true
139 |
140 |
141 | false
142 | 1033
143 | (None)
144 |
145 |
146 | false
147 | RELEASE;$(DCC_Define)
148 | 0
149 | 0
150 |
151 |
152 |
153 | MainSource
154 |
155 |
156 | Cfg_2
157 | Base
158 |
159 |
160 | Base
161 |
162 |
163 | Cfg_1
164 | Base
165 |
166 |
167 |
168 | Delphi.Personality.12
169 | Application
170 |
171 |
172 |
173 | DelphiTimerQueue.dpr
174 |
175 |
176 | Microsoft Office 2000 Sample Automation Server Wrapper Components
177 | Microsoft Office XP Sample Automation Server Wrapper Components
178 |
179 |
180 |
181 |
182 |
183 | DelphiTimerQueue
184 | true
185 |
186 |
187 |
188 |
189 | true
190 |
191 |
192 |
193 |
194 | true
195 |
196 |
197 |
198 |
199 | true
200 |
201 |
202 |
203 |
204 | true
205 |
206 |
207 |
208 |
209 | DelphiTimerQueue.exe
210 | true
211 |
212 |
213 |
214 |
215 | 1
216 |
217 |
218 | Contents\MacOS
219 | 1
220 |
221 |
222 | Contents\MacOS
223 | 0
224 |
225 |
226 |
227 |
228 | classes
229 | 1
230 |
231 |
232 |
233 |
234 | library\lib\armeabi-v7a
235 | 1
236 |
237 |
238 |
239 |
240 | library\lib\armeabi
241 | 1
242 |
243 |
244 |
245 |
246 | library\lib\mips
247 | 1
248 |
249 |
250 |
251 |
252 | library\lib\armeabi-v7a
253 | 1
254 |
255 |
256 |
257 |
258 | res\drawable
259 | 1
260 |
261 |
262 |
263 |
264 | res\values
265 | 1
266 |
267 |
268 |
269 |
270 | res\drawable
271 | 1
272 |
273 |
274 |
275 |
276 | res\drawable-xxhdpi
277 | 1
278 |
279 |
280 |
281 |
282 | res\drawable-ldpi
283 | 1
284 |
285 |
286 |
287 |
288 | res\drawable-mdpi
289 | 1
290 |
291 |
292 |
293 |
294 | res\drawable-hdpi
295 | 1
296 |
297 |
298 |
299 |
300 | res\drawable-xhdpi
301 | 1
302 |
303 |
304 |
305 |
306 | res\drawable-small
307 | 1
308 |
309 |
310 |
311 |
312 | res\drawable-normal
313 | 1
314 |
315 |
316 |
317 |
318 | res\drawable-large
319 | 1
320 |
321 |
322 |
323 |
324 | res\drawable-xlarge
325 | 1
326 |
327 |
328 |
329 |
330 | 1
331 |
332 |
333 | Contents\MacOS
334 | 1
335 |
336 |
337 | 0
338 |
339 |
340 |
341 |
342 | Contents\MacOS
343 | 1
344 | .framework
345 |
346 |
347 | 0
348 |
349 |
350 |
351 |
352 | 1
353 | .dylib
354 |
355 |
356 | 1
357 | .dylib
358 |
359 |
360 | 1
361 | .dylib
362 |
363 |
364 | Contents\MacOS
365 | 1
366 | .dylib
367 |
368 |
369 | 0
370 | .dll;.bpl
371 |
372 |
373 |
374 |
375 | 1
376 | .dylib
377 |
378 |
379 | 1
380 | .dylib
381 |
382 |
383 | 1
384 | .dylib
385 |
386 |
387 | Contents\MacOS
388 | 1
389 | .dylib
390 |
391 |
392 | 0
393 | .bpl
394 |
395 |
396 |
397 |
398 | 0
399 |
400 |
401 | 0
402 |
403 |
404 | 0
405 |
406 |
407 | 0
408 |
409 |
410 | Contents\Resources\StartUp\
411 | 0
412 |
413 |
414 | 0
415 |
416 |
417 |
418 |
419 | 1
420 |
421 |
422 | 1
423 |
424 |
425 | 1
426 |
427 |
428 |
429 |
430 | 1
431 |
432 |
433 | 1
434 |
435 |
436 | 1
437 |
438 |
439 |
440 |
441 | 1
442 |
443 |
444 | 1
445 |
446 |
447 | 1
448 |
449 |
450 |
451 |
452 | 1
453 |
454 |
455 | 1
456 |
457 |
458 | 1
459 |
460 |
461 |
462 |
463 | 1
464 |
465 |
466 | 1
467 |
468 |
469 | 1
470 |
471 |
472 |
473 |
474 | 1
475 |
476 |
477 | 1
478 |
479 |
480 | 1
481 |
482 |
483 |
484 |
485 | 1
486 |
487 |
488 | 1
489 |
490 |
491 | 1
492 |
493 |
494 |
495 |
496 | 1
497 |
498 |
499 |
500 |
501 | ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF
502 | 1
503 |
504 |
505 | ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF
506 | 1
507 |
508 |
509 |
510 |
511 | 1
512 |
513 |
514 | 1
515 |
516 |
517 |
518 |
519 | ..\
520 | 1
521 |
522 |
523 | ..\
524 | 1
525 |
526 |
527 |
528 |
529 | 1
530 |
531 |
532 | 1
533 |
534 |
535 | 1
536 |
537 |
538 |
539 |
540 | 1
541 |
542 |
543 | 1
544 |
545 |
546 | 1
547 |
548 |
549 |
550 |
551 | ..\
552 | 1
553 |
554 |
555 |
556 |
557 | Contents
558 | 1
559 |
560 |
561 |
562 |
563 | Contents\Resources
564 | 1
565 |
566 |
567 |
568 |
569 | library\lib\armeabi-v7a
570 | 1
571 |
572 |
573 | 1
574 |
575 |
576 | 1
577 |
578 |
579 | 1
580 |
581 |
582 | 1
583 |
584 |
585 | Contents\MacOS
586 | 1
587 |
588 |
589 | 0
590 |
591 |
592 |
593 |
594 | 1
595 |
596 |
597 | 1
598 |
599 |
600 |
601 |
602 | Assets
603 | 1
604 |
605 |
606 | Assets
607 | 1
608 |
609 |
610 |
611 |
612 | Assets
613 | 1
614 |
615 |
616 | Assets
617 | 1
618 |
619 |
620 |
621 |
622 |
623 |
624 |
625 |
626 |
627 |
628 |
629 |
630 | False
631 | False
632 | False
633 | False
634 | True
635 | False
636 | True
637 | False
638 |
639 |
640 | 12
641 |
642 |
643 |
644 |
645 |
646 |
--------------------------------------------------------------------------------