-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathsdk-readme.html
654 lines (544 loc) · 43.8 KB
/
sdk-readme.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
lang="en" dir="ltr">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>foobar2000 SDK Readme</title>
<link rel="stylesheet" type="text/css" href="sdk-readme.css" />
</head>
<body>
<div class="dokuwiki export">
<div class="toc">
<div class="tocheader toctoggle" id="toc__header">Table of Contents</div>
<div id="toc__inside">
<ul class="toc">
<li class="level1"><div class="li"><span class="li"><a href="#foobar2000_v1.4_beta_sdk_readme" class="toc">foobar2000 v1.4 beta SDK readme</a></span></div>
<ul class="toc">
<li class="level2"><div class="li"><span class="li"><a href="#beta_release_notice" class="toc">Beta release notice</a></span></div></li>
<li class="level2"><div class="li"><span class="li"><a href="#compatibility" class="toc">Compatibility</a></span></div></li>
<li class="level2"><div class="li"><span class="li"><a href="#microsoft_visual_studio_compatibility" class="toc">Microsoft Visual Studio compatibility</a></span></div>
<ul class="toc">
<li class="level3"><div class="li"><span class="li"><a href="#ill_behavior_of_visual_c_whole_program_optimization" class="toc">Ill behavior of Visual C whole program optimization</a></span></div></li>
<li class="level3"><div class="li"><span class="li"><a href="#virtual_memory_range_for_pch_exceeded_error" class="toc">"virtual memory range for PCH exceeded" error</a></span></div></li>
</ul>
</li>
<li class="level2"><div class="li"><span class="li"><a href="#version_1.4_notes" class="toc">Version 1.4 notes</a></span></div>
<ul class="toc">
<li class="level3"><div class="li"><span class="li"><a href="#namespace_cleanup" class="toc">Namespace cleanup</a></span></div></li>
<li class="level3"><div class="li"><span class="li"><a href="#decoders" class="toc">Decoders</a></span></div></li>
<li class="level3"><div class="li"><span class="li"><a href="#dynamic_runtime" class="toc">Dynamic runtime</a></span></div></li>
<li class="level3"><div class="li"><span class="li"><a href="#service_query" class="toc">service_query()</a></span></div></li>
</ul>
</li>
<li class="level2"><div class="li"><span class="li"><a href="#version_1.3_notes" class="toc">Version 1.3 notes</a></span></div></li>
<li class="level2"><div class="li"><span class="li"><a href="#basic_usage" class="toc">Basic usage</a></span></div></li>
<li class="level2"><div class="li"><span class="li"><a href="#structure_of_a_component" class="toc">Structure of a component</a></span></div>
<ul class="toc">
<li class="level3"><div class="li"><span class="li"><a href="#services" class="toc">Services</a></span></div></li>
<li class="level3"><div class="li"><span class="li"><a href="#entrypoint_services" class="toc">Entrypoint services</a></span></div></li>
<li class="level3"><div class="li"><span class="li"><a href="#service_extensions" class="toc">Service extensions</a></span></div></li>
<li class="level3"><div class="li"><span class="li"><a href="#autopointer_template_use" class="toc">Autopointer template use</a></span></div></li>
<li class="level3"><div class="li"><span class="li"><a href="#exception_use" class="toc">Exception use</a></span></div></li>
<li class="level3"><div class="li"><span class="li"><a href="#storing_configuration" class="toc">Storing configuration</a></span></div></li>
<li class="level3"><div class="li"><span class="li"><a href="#use_of_global_callback_services" class="toc">Use of global callback services</a></span></div></li>
</ul>
</li>
<li class="level2"><div class="li"><span class="li"><a href="#service_class_design_guidelines_advanced" class="toc">Service class design guidelines (advanced)</a></span></div>
<ul class="toc">
<li class="level3"><div class="li"><span class="li"><a href="#cross-dll_safety" class="toc">Cross-DLL safety</a></span></div></li>
<li class="level3"><div class="li"><span class="li"><a href="#entrypoint_service_efficiency" class="toc">Entrypoint service efficiency</a></span></div></li></ul>
</li></ul>
</li></ul>
</div>
</div>
<h1><a name="foobar2000_v1.4_beta_sdk_readme" id="foobar2000_v1.4_beta_sdk_readme">foobar2000 v1.4 beta SDK readme</a></h1>
<div class="level1">
</div>
<!-- SECTION "foobar2000 v1.4 beta SDK readme" [1-47] -->
<h2><a name="beta_release_notice" id="beta_release_notice">Beta release notice</a></h2>
<div class="level2">
<p>
This <acronym title="Software Development Kit">SDK</acronym> is safe for general use.
</p>
<p>
However please keep in mind that foobar2000 v1.4 is currently in beta and some of the new features may undergo changes before the final version is out.
</p>
</div>
<!-- SECTION "Beta release notice" [48-269] -->
<h2><a name="compatibility" id="compatibility">Compatibility</a></h2>
<div class="level2">
<p>
Components built with this <acronym title="Software Development Kit">SDK</acronym> are compatible with foobar2000 1.3 and 1.4 series. They are not compatible with any earlier versions (will fail to load), and not guaranteed to be compatible with any future versions, though upcoming releases will aim to maintain compatibility as far as possible without crippling newly added functionality.
</p>
<p>
Note that components built with this <acronym title="Software Development Kit">SDK</acronym> can provide foobar2000 1.4 functionality while maintaining compatibility with the 1.3 series.
</p>
<p>
While this is not recommended, you can alter the targeted foobar2000 <acronym title="Application Programming Interface">API</acronym> level, edit <acronym title="Software Development Kit">SDK</acronym>/foobar2000.h and change the value of FOOBAR2000_TARGET_VERSION.
</p>
<p>
Currently supported values are 78 (for 1.3 series) and 79 (for 1.4 series).
</p>
<p>
<acronym title="Application Programming Interface">API</acronym> 78 components load in foobar2000 1.3 and 1.4; <acronym title="Application Programming Interface">API</acronym> 79 components load in 1.4 only.
</p>
</div>
<!-- SECTION "Compatibility" [270-1090] -->
<h2><a name="microsoft_visual_studio_compatibility" id="microsoft_visual_studio_compatibility">Microsoft Visual Studio compatibility</a></h2>
<div class="level2">
<p>
This <acronym title="Software Development Kit">SDK</acronym> contains project files for Visual Studio 2017.
</p>
</div>
<!-- SECTION "Microsoft Visual Studio compatibility" [1091-1197] -->
<h3><a name="ill_behavior_of_visual_c_whole_program_optimization" id="ill_behavior_of_visual_c_whole_program_optimization">Ill behavior of Visual C whole program optimization</a></h3>
<div class="level3">
<p>
Visual Studio versions from 2012 up produce incorrect output with default release settings on foobar2000 code - see <a href="http://www.hydrogenaud.io/forums/index.php?showtopic=108411" class="urlextern" title="http://www.hydrogenaud.io/forums/index.php?showtopic=108411" rel="nofollow">forum thread</a> for details. You can mitigate this issue by compiling all affected code (foobar2000 <acronym title="Software Development Kit">SDK</acronym> code along with everything that uses foobar2000 <acronym title="Software Development Kit">SDK</acronym> classes) with /d2notypeopt option. This is set by default on project files provided with the <acronym title="Software Development Kit">SDK</acronym>; please make sure that you set this option on any projects of your own.
</p>
<p>
If you're aware of a better workaround - such as a source code change rather than setting an obscure compiler flag - please let us know; posting on the forum is preferred for the benefit of other users of this <acronym title="Software Development Kit">SDK</acronym>.
</p>
</div>
<!-- SECTION "Ill behavior of Visual C whole program optimization" [1198-1977] -->
<h3><a name="virtual_memory_range_for_pch_exceeded_error" id="virtual_memory_range_for_pch_exceeded_error">"virtual memory range for PCH exceeded" error</a></h3>
<div class="level3">
<p>
By convention, foobar2000 code used to #include every single <acronym title="Software Development Kit">SDK</acronym> and utility header in a precompiled header (PCH) file. This became an issue with certain Visual Studio setups, as the PCH payload became extremely large.
</p>
<p>
The <acronym title="Software Development Kit">SDK</acronym> has been changed to reduce the amount of unnecessary code shoved into #included headers; errors of this type are yet to be seen with this version of the foobar2000 <acronym title="Software Development Kit">SDK</acronym> compiled under VS2017.
</p>
<p>
If you run into this error, we recommend the following steps:
</p>
<ul>
<li class="level1"><div class="li"> ATLHelpers: do not #include ATLHelpers/ATLHelpers.h - use ATLHelpersLean.h instead + additional ones on need-to-use basis.</div>
</li>
<li class="level1"><div class="li"> Helpers: do not #include helpers.h, #include individual ones as needed; most of the declare functionality needed for very specific components only.</div>
</li>
</ul>
</div>
<!-- SECTION "virtual memory range for PCH exceeded error" [1978-2795] -->
<h2><a name="version_1.4_notes" id="version_1.4_notes">Version 1.4 notes</a></h2>
<div class="level2">
</div>
<!-- SECTION "Version 1.4 notes" [2796-2825] -->
<h3><a name="namespace_cleanup" id="namespace_cleanup">Namespace cleanup</a></h3>
<div class="level3">
<p>
Some very old inconsistencies in the code have been cleaned up. Various bit_array classes are now in pfc namespace where they belong. Please use pfc::bit_array and so on in new code. If you have code that references bit_array classes without the pfc:: prefix, put “using pfc::bit_array” in your headers to work around it.
</p>
</div>
<!-- SECTION "Namespace cleanup" [2826-3175] -->
<h3><a name="decoders" id="decoders">Decoders</a></h3>
<div class="level3">
</div>
<h4><a name="invoking_decoders" id="invoking_decoders">Invoking decoders</a></h4>
<div class="level4">
<p>
Each new decoder (input_entry) now provides a get_guid() and get_name() to allow user to choose the order in which they're invoked as well as disable individual installed decoders. Because of this, you are no longer supposed to walk input_entry instances in your code; however many existing components do so because this was the way things were expected to work in all past versions before 1.4.
</p>
<p>
In most cases there's nothing that you need to do about this, unless you have code that talks to input_entry instance methods directly.
</p>
</div>
<h5><a name="input_manager" id="input_manager">input_manager</a></h5>
<div class="level5">
<p>
The proper way to instantiate any input related classes is to call input_manager; it manages opening of decoders respecting user's decoder merit settings. Calling any of the helper methods to open decoders / read tags / etc in the <acronym title="Software Development Kit">SDK</acronym> will call input_manager if available (v1.4) and fall back to walking input_entry services if not (v1.3).
</p>
</div>
<h5><a name="input_entry_shim" id="input_entry_shim">input_entry shim</a></h5>
<div class="level5">
<p>
If your component targets <acronym title="Application Programming Interface">API</acronym> level lower than 79 (this <acronym title="Software Development Kit">SDK</acronym> comes configured for 78 by default), all your attempts to walk input_entry services return a shim service that redirects all your calls to input_manager. You cannot walk actual input_entry services.
</p>
<p>
This is to keep existing components working as intended.
</p>
<p>
If your component targets <acronym title="Application Programming Interface">API</acronym> level 79 (which means it won't load on v1.3), the shim is not installed as your component is expected to be aware of the new semantics.
</p>
</div>
<h4><a name="implementing_decoders" id="implementing_decoders">Implementing decoders</a></h4>
<div class="level4">
<p>
Many new input methods have been added and some are mandatory for all code, in particular reporting of decoder name and GUID; existing code not providing these will not compile. However some of the methods now required to be provided by an input class are mundane and will be left blank in majority of implementations; an input_stubs class has been introduced to help with these. In past <acronym title="Software Development Kit">SDK</acronym> versions, your input class would not derive from another class; it is now recommended to derive from input_stubs to avoid having to supply mundane methods yourself.
</p>
</div>
<h5><a name="specify_supported_interfaces" id="specify_supported_interfaces">Specify supported interfaces</a></h5>
<div class="level5">
<p>
You can now control which level of decoder <acronym title="Application Programming Interface">API</acronym> your instance supports from your input class instead of using multi parameter input_factory classes.
</p>
<p>
The input_stubs class provides these for your convenience:
</p>
<pre class="code">
typedef input_decoder_v4 interface_decoder_t;
typedef input_info_reader interface_info_reader_t;
typedef input_info_writer interface_info_writer_t;
</pre>
<p>
Override these in your input class to indicate supported interfaces.
</p>
<p>
For an example, if your input supports remove_tags(), indicate that you implement input_info_writer_v2:
</p>
<pre class="code">
typedef input_info_writer_v2 interface_info_writer_t;
</pre>
</div>
<!-- SECTION "Decoders" [3176-5857] -->
<h3><a name="dynamic_runtime" id="dynamic_runtime">Dynamic runtime</a></h3>
<div class="level3">
<p>
As of version 1.4, foobar2000 is compiled with dynamic Visual C runtime and redistributes Visual C runtime libraries with the installer, putting them in the foobar2000 installation folder if necessary. The benefits of this are: * Smaller component DLLs * Increased limit of how many component DLLs can be loaded.
</p>
<p>
This <acronym title="Software Development Kit">SDK</acronym> comes configured for static runtime by default, for compatibility with foobar2000 version 1.3. If your component is for foobar2000 series 1.4 only, you can switch to using dynamic runtime instead - make sure to change the setting for all projects in your workspace.
</p>
</div>
<!-- SECTION "Dynamic runtime" [5858-6474] -->
<h3><a name="service_query" id="service_query">service_query()</a></h3>
<div class="level3">
<p>
tl;dr if you don't know what this is about you probably don't care and your component isn't in any way affected by this.
</p>
<p>
The way service_query() is implemented has been redesigned for performance reasons - the old way ( implemented per interface class via FB2K_MAKE_SERVICE_INTERFACE macro ) resulted in every single class_guid from the entire <acronym title="Software Development Kit">SDK</acronym> being included in your DLL, due to strange behaviors of Microsoft linker.
</p>
<p>
The service_query() function itself is now provided by service_impl_* classes, so it's materialized only for services that your code implements and spawns.
</p>
<p>
The default implementation calls a newly added static method of handle_service_query(), implemented in service_base to check against all supported interfaces by walking derived class chain.
</p>
<p>
If you wish to override it, create a function with a matching signature in your class and it will be called instead:
</p>
<pre class="code">
class myClass : public service_base {
public:
static bool handle_service_query(service_ptr & out, const GUID & guid, myClass * in) {
if ( guid == myClassGUID ) {
out = in; return true;
}
return false;
}
};
</pre>
</div>
<h4><a name="multi-inheritance" id="multi-inheritance">Multi-inheritance</a></h4>
<div class="level4">
<p>
The above change to service_query() implementation rules obviously breaks any existing code where one class inherits from multiple service classes and supplies a custom service_query().
</p>
<p>
While using multi inheritance is not recommended and very rarely done, a new template has been added to help with such cases: service_multi_inherit<class1, class2>.
</p>
<p>
You can use it to avoid having to supply service_query() code yourself and possibly change it if service_query() semantics change again in the future.
</p>
</div>
<!-- SECTION "service_query()" [6475-8185] -->
<h2><a name="version_1.3_notes" id="version_1.3_notes">Version 1.3 notes</a></h2>
<div class="level2">
<p>
foobar2000 version 1.3 uses different metadb behavior semantics than older versions, to greatly improve the performance of multithreaded operation by reducing the time spent within global metadb locks.
</p>
<p>
Any methods that:
</p>
<ul>
<li class="level1"><div class="li"> Lock the metadb - database_lock() etc</div>
</li>
<li class="level1"><div class="li"> Retrieve direct pointers to metadb info - get_info_locked() style</div>
</li>
</ul>
<p>
.. are now marked deprecated and implemented only for backwards compatibility; they should not be used in any new code.
</p>
<p>
It is recommended that you change your existing code using these to obtain track information using new get_info_ref() style methods for much better performance as these methods have minimal overhead and require no special care when used in multiple concurrent threads.
</p>
</div>
<!-- SECTION "Version 1.3 notes" [8186-8941] -->
<h2><a name="basic_usage" id="basic_usage">Basic usage</a></h2>
<div class="level2">
<p>
Each component must link against:
</p>
<ul>
<li class="level1"><div class="li"> foobar2000_<acronym title="Software Development Kit">SDK</acronym> project (contains declarations of services and various service-specific helper code)</div>
</li>
<li class="level1"><div class="li"> foobar2000_component_client project (contains DLL entrypoint)</div>
</li>
<li class="level1"><div class="li"> shared.dll (various helper code, mainly win32 function wrappers taking UTF-8 strings)</div>
</li>
<li class="level1"><div class="li"> PFC (non-<acronym title="Operating System">OS</acronym>-specific helper class library)</div>
</li>
</ul>
<p>
Optionally, components can use helper libraries with various non-critical code that is commonly reused across various foobar2000 components:
</p>
<ul>
<li class="level1"><div class="li"> foobar2000_<acronym title="Software Development Kit">SDK</acronym>_helpers - a library of various helper code commonly used by foobar2000 components.</div>
</li>
<li class="level1"><div class="li"> foobar2000_ATL_helpers - another library of various helper code commonly used by foobar2000 components; requires WTL.</div>
</li>
</ul>
<p>
Foobar2000_<acronym title="Software Development Kit">SDK</acronym>, foobar2000_component_client and PFC are included in sourcecode form; you can link against them by adding them to your workspace and using dependencies. To link against shared.dll, you must add “shared.lib” to linker input manually.
</p>
<p>
Component code should include the following header files:
</p>
<ul>
<li class="level1"><div class="li"> foobar2000.h from <acronym title="Software Development Kit">SDK</acronym> - do not include other headers from the <acronym title="Software Development Kit">SDK</acronym> directory directly, they're meant to be referenced by foobar2000.h only; it also includes PFC headers and shared.dll helper declaration headers.</div>
</li>
<li class="level1"><div class="li"> Optionally: helpers.h from helpers directory (foobar2000_<acronym title="Software Development Kit">SDK</acronym>_helpers project) - a library of various helper code commonly used by foobar2000 components.</div>
</li>
<li class="level1"><div class="li"> Optionally: ATLHelpers.h from ATLHelpers directory (foobar2000_ATL_helpers project) - another library of various helper code commonly used by foobar2000 components; requires WTL. Note that ATLHelpers.h already includes <acronym title="Software Development Kit">SDK</acronym>/foobar2000.h and helpers/helpers.h so you can replace your other include lines with a reference to ATLHelpers.h.</div>
</li>
</ul>
</div>
<!-- SECTION "Basic usage" [8942-10693] -->
<h2><a name="structure_of_a_component" id="structure_of_a_component">Structure of a component</a></h2>
<div class="level2">
<p>
A component is a DLL that implements one or more entrypoint services and interacts with services provided by other components.
</p>
</div>
<!-- SECTION "Structure of a component" [10694-10860] -->
<h3><a name="services" id="services">Services</a></h3>
<div class="level3">
<p>
A service type is an interface class, deriving directly or indirectly from <code>service_base</code> class. A service type class must not have any data members; it can only provide virtual methods (to be overridden by service implementation), helper non-virtual methods built around virtual methods, static helper methods, and constants / enums. Each service interface class must have a static <code>class_guid</code> member, used for identification when enumerating services or querying for supported functionality. A service type declaration should declare a class with public virtual/helper/static methods, and use <code>FB2K_MAKE_SERVICE_INTERFACE()</code> / <code>FB2K_MAKE_SERVICE_INTERFACE_ENTRYPOINT()</code> macro to implement standard service behaviors for the class; additionally, <code>class_guid</code> needs to be defined outside class declaration (e.g. <code>const GUID someclass::class_guid = {….};</code> ). Note that most of components will not declare their own service types, they will only implement existing ones declared in the <acronym title="Software Development Kit">SDK</acronym>.
</p>
<p>
A service implementation is a class derived from relevant service type class, implementing virtual methods declared by service type class. Note that service implementation class does not implement virtual methods declared by service_base; those are implemented by service type declaration framework (<code>service_query</code>) or by instantiation framework (<code>service_add_ref</code> / <code>service_release</code>). Service implementation classes are instantiated using <code>service_factory</code> templates in case of entrypoint services (see below), or using <code>service_impl_t</code> template and operator <code>new</code>: ”<code>myserviceptr = new service_impl_t<myservice_impl>(params);</code>”.
</p>
<p>
Each service object provides reference counter features and (<code>service_add_ref()</code> and <code>service_release()</code> methods) as well as a method to query for extended functionality (<code>service_query()</code> method). Those methods are implemented by service framework and should be never overridden by service implementations. These methods should also never be called directly - reference counter methods are managed by relevant autopointer templates, <code>service_query_t</code> function template should be used instead of calling <code>service_query</code> directly, to ensure type safety and correct type conversions.
</p>
</div>
<!-- SECTION "Services" [10861-13126] -->
<h3><a name="entrypoint_services" id="entrypoint_services">Entrypoint services</a></h3>
<div class="level3">
<p>
An entrypoint service type is a special case of a service type that can be registered using <code>service_factory</code> templates, and then accessed from any point of service system (excluding DLL startup/shutdown code, such as code inside static object constructors/destructors). An entrypoint service type class must directly derive from <code>service_base</code>.
</p>
<p>
Registered entrypoint services can be accessed using:
</p>
<ul>
<li class="level1"><div class="li"> For services types with variable number of implementations registered: <code>service_enum_t<T></code> template, <code>service_class_helper_t<T></code> template, etc, e.g. <pre class="code cpp">service_enum_t<someclass> e; service_ptr_t<someclass> ptr; <span class="kw1">while</span><span class="br0">(</span>e.<span class="me1">next</span><span class="br0">(</span>ptr<span class="br0">)</span><span class="br0">)</span> ptr->dosomething<span class="br0">(</span><span class="br0">)</span>;</pre></div>
</li>
<li class="level1"><div class="li"> For services types with a single always-present implementation registered - such as core services like <code>playlist_manager</code> - using <code>someclass::get()</code>, e.g.: <pre class="code cpp"><span class="kw4">auto</span> api = someclass::<span class="me2">get</span><span class="br0">(</span><span class="br0">)</span>; api->dosomething<span class="br0">(</span><span class="br0">)</span>; api->dosomethingelse<span class="br0">(</span><span class="br0">)</span>;</pre></div>
</li>
<li class="level1"><div class="li"> Using per-service-type defined static helper functions, e.g. <code>someclass::g_dosomething()</code> - those use relevant service enumeration methods internally.</div>
</li>
</ul>
<p>
An entrypoint service type must use <code>FB2K_MAKE_SERVICE_INTERFACE_ENTRYPOINT()</code> macro to implement standard entrypoint service behaviors, as opposed to all other service types that use <code>FB2K_MAKE_SERVICE_INTERFACE()</code> macro instead.
</p>
<p>
You can register your own entrypoint service implementations using either <code>service_factory_t</code> or <code>service_factory_single_t</code> template - the difference between the two is that the former instantiates the class on demand, while the latter keeps a single static instance and returns references to it when requested; the latter is faster but usable only for things that have no per-instance member data to maintain. Each <code>service_factory_t</code> / <code>service_factory_single_t</code> instance should be a static variable, such as: ”<code>static service_factory_t<myclass> g_myclass_factory;</code>”.
</p>
<p>
Certain service types require custom <code>service_factory</code> helper templates to be used instead of standard <code>service_factory_t</code> / <code>service_factory_single_t</code> templates; see documentation of specific service type for exact info about registering.
</p>
<p>
A typical entrypoint service implementation looks like this:
</p>
<pre class="code cpp"><span class="kw2">class</span> myservice_impl : <span class="kw2">public</span> myservice <span class="br0">{</span>
<span class="kw2">public</span>:
<span class="kw4">void</span> dosomething<span class="br0">(</span><span class="br0">)</span> <span class="br0">{</span>....<span class="br0">}</span>;
<span class="kw4">void</span> dosomethingelse<span class="br0">(</span><span class="kw4">int</span> meh<span class="br0">)</span> <span class="br0">{</span>...<span class="br0">}</span>;
<span class="br0">}</span>;
<span class="kw4">static</span> service_factory_single_t<myservice_impl> g_myservice_impl_factory;</pre>
</div>
<!-- SECTION "Entrypoint services" [13127-15584] -->
<h3><a name="service_extensions" id="service_extensions">Service extensions</a></h3>
<div class="level3">
<p>
Additional methods can be added to any service type, by declaring a new service type class deriving from service type class you want to extend. For example:
</p>
<pre class="code cpp"><span class="kw2">class</span> myservice : <span class="kw2">public</span> service_base <span class="br0">{</span> <span class="kw2">public</span>: <span class="kw2">virtual</span> <span class="kw4">void</span> dosomething<span class="br0">(</span><span class="br0">)</span> = <span class="nu0">0</span>; FB2K_MAKE_SERVICE_INTERFACE_ENTRYPOINT<span class="br0">(</span>myservice<span class="br0">)</span>; <span class="br0">}</span>;
<span class="kw2">class</span> myservice_v2 : <span class="kw2">public</span> myservice <span class="br0">{</span> <span class="kw2">public</span>: <span class="kw2">virtual</span> <span class="kw4">void</span> dosomethingelse<span class="br0">(</span><span class="br0">)</span> = <span class="nu0">0</span>; FB2K_MAKE_SERVICE_INTERFACE<span class="br0">(</span>myservice_v2, myservice<span class="br0">)</span>; <span class="br0">}</span>;</pre>
<p>
In such scenario, to query whether a myservice instance is a <code>myservice_v2</code> and to retrieve <code>myservice_v2</code> pointer, use <code>service_query</code> functionality:
</p>
<pre class="code cpp">service_ptr_t<myservice> ptr;
<span class="br0">(</span>...<span class="br0">)</span>
service_ptr_t<myservice_v2> ptr_ex;
<span class="kw1">if</span> <span class="br0">(</span>ptr->service_query_t<span class="br0">(</span>ptr_ex<span class="br0">)</span><span class="br0">)</span> <span class="br0">{</span> <span class="coMULTI">/* ptr_ex is a valid pointer to myservice_v2 interface of our myservice instance */</span> <span class="br0">(</span>...<span class="br0">)</span> <span class="br0">}</span>
<span class="kw1">else</span> <span class="br0">{</span><span class="coMULTI">/* this myservice implementation does not implement myservice_v2 */</span> <span class="br0">(</span>...<span class="br0">)</span> <span class="br0">}</span></pre>
</div>
<!-- SECTION "Service extensions" [15585-16524] -->
<h3><a name="autopointer_template_use" id="autopointer_template_use">Autopointer template use</a></h3>
<div class="level3">
<p>
When performing most kinds of service operations, <code>service_ptr_t<T></code> template should be used rather than working with service pointers directly; it automatically manages reference counter calls, ensuring that the service object is deleted when it is no longer referenced.
</p>
<p>
For convenience, all service classes have <code>myclass::ptr</code> typedef'd to <code>service_ptr_t<myclass></code>.
</p>
<p>
When working with pointers to core fb2k services, just use C++11 <code>auto</code> keyword and <code>someclass::get()</code>, e.g. <code>auto myAPI = playlist_manager::get();</code>
</p>
</div>
<!-- SECTION "Autopointer template use" [16525-17092] -->
<h3><a name="exception_use" id="exception_use">Exception use</a></h3>
<div class="level3">
<p>
Most of <acronym title="Application Programming Interface">API</acronym> functions use C++ exceptions to signal failure conditions. All used exception classes must derive from <code>std::exception</code> (which <code>pfc::exception</code> is typedef'd to); this design allows various instances of code to use single <code>catch()</code> line to get human-readable description of the problem to display.
</p>
<p>
Additionally, special subclasses of exceptions are defined for use in specific conditions, such as <code>exception_io</code> for I/O failures. As a result, you must provide an exception handler whenever you invoke any kind of I/O code that may fail, unless in specific case calling context already handles exceptions (e.g. input implementation code - any exceptions should be forwarded to calling context, since exceptions are a part of input <acronym title="Application Programming Interface">API</acronym>).
</p>
<p>
Implementations of global callback services such as <code>playlist_callback</code>, <code>playback_callback</code> or <code>library_callback</code> must not throw unhandled exceptions; behaviors in case they do are undefined (app termination is to be expected).
</p>
</div>
<!-- SECTION "Exception use" [17093-18110] -->
<h3><a name="storing_configuration" id="storing_configuration">Storing configuration</a></h3>
<div class="level3">
<p>
In order to create your entries in the configuration file, you must instantiate some objects that derive from <code>cfg_var</code> class. Those can be either predefined classes (<code>cfg_int</code>, <code>cfg_string</code>, etc) or your own classes implementing relevant methods.
</p>
<p>
Each <code>cfg_var</code> instance has a GUID assigned, to identify its configuration file entry. The GUID is passed to its constructor (which implementations must take care of, typically by providing a constructor that takes a GUID and forwards it to <code>cfg_var</code> constructor).
</p>
<p>
Note that <code>cfg_var</code> objects can only be instantiated statically (either directly as static objects, or as members of other static objects). Additionally, you can create configuration data objects that can be accessed by other components, by implementing <code>config_object</code> service. Some standard configuration variables can be also accessed using <code>config_object</code> interface.
</p>
</div>
<!-- SECTION "Storing configuration" [18111-19046] -->
<h3><a name="use_of_global_callback_services" id="use_of_global_callback_services">Use of global callback services</a></h3>
<div class="level3">
<p>
Multiple service classes presented by the <acronym title="Software Development Kit">SDK</acronym> allow your component to receive notifications about various events:
</p>
<ul>
<li class="level1"><div class="li"> file_operation_callback - tracking file move/copy/delete operations.</div>
</li>
<li class="level1"><div class="li"> library_callback - tracking Media Library content changes.</div>
</li>
<li class="level1"><div class="li"> metadb_io_callback - tracking tag read / write operations altering cached/displayed media information.</div>
</li>
<li class="level1"><div class="li"> play_callback - tracking playback related events.</div>
</li>
<li class="level1"><div class="li"> playback_statistics_collector - collecting information about played tracks.</div>
</li>
<li class="level1"><div class="li"> playlist_callback, playlist_callback_single - tracking playlist changes (the latter tracks only active playlist changes).</div>
</li>
<li class="level1"><div class="li"> playback_queue_callback - tracking playback queue changes.</div>
</li>
<li class="level1"><div class="li"> titleformat_config_callback - tracking changes of title formatting configuration.</div>
</li>
<li class="level1"><div class="li"> ui_drop_item_callback - filtering items dropped into the UI.</div>
</li>
</ul>
<p>
All of global callbacks operate only within main app thread, allowing easy cooperation with windows <acronym title="Graphical User Interface">GUI</acronym> - for an example, you perform playlist view window repainting directly from your playlist_callback implementation.
</p>
</div>
<h4><a name="global_callback_recursion_issues" id="global_callback_recursion_issues">Global callback recursion issues</a></h4>
<div class="level4">
<p>
There are restrictions on things that are legal to call from within global callbacks. For an example, you can't modify a playlist from inside a playlist callback, because there are other registered callbacks tracking playlist changes that haven't been notified about the change being currently processed yet.
</p>
<p>
You must not enter modal message loops from inside global callbacks, as those allow any unrelated code (queued messages, user input, etc.) to be executed, without being aware that a global callback is being processed. Certain global <acronym title="Application Programming Interface">API</acronym> methods such as metadb_io::load_info_multi or threaded_process::run_modal enter modal loops when called. Use main_thread_callback service to avoid this problem and delay execution of problematic code.
</p>
<p>
You should also avoid firing a cross-thread SendMessage() inside global callbacks as well as performing any operations that dispatch global callbacks when handling a message that was sent through a cross-thread SendMessage(). Doing so may result in rare unwanted recursions - SendMessage() call will block the calling thread and immediately process any incoming cross-thread SendMessage() messages. If you're handling a cross-thread SendMessage() and need to perform such operation, delay it using PostMessage() or main_thread_callback.
</p>
</div>
<!-- SECTION "Use of global callback services" [19047-21468] -->
<h2><a name="service_class_design_guidelines_advanced" id="service_class_design_guidelines_advanced">Service class design guidelines (advanced)</a></h2>
<div class="level2">
<p>
This chapter describes things you should keep on your mind when designing your own service type classes. Since 99% of components will only implement existing service types rather than adding their own cross-DLL-communication protocols, you can probably skip reading this chapter.
</p>
</div>
<!-- SECTION "Service class design guidelines (advanced)" [21469-21805] -->
<h3><a name="cross-dll_safety" id="cross-dll_safety">Cross-DLL safety</a></h3>
<div class="level3">
<p>
It is important that all function parameters used by virtual methods of services are cross-DLL safe (do not depend on compiler-specific or runtime-specific behaviors, so no unexpected behaviors occur when calling code is built with different compiler/runtime than callee). To achieve this, any classes passed around must be either simple objects with no structure that could possibly vary with different compilers/runtimes (i.e. make sure that any memory blocks are freed on the side that allocated them); easiest way to achieve this is to reduce all complex data objects or classes passed around to interfaces with virtual methods, with implementation details hidden from callee. For an example, use <code>pfc::string_base&</code> as parameter to a function that is meant to return variable-length strings.
</p>
</div>
<!-- SECTION "Cross-DLL safety" [21806-22633] -->
<h3><a name="entrypoint_service_efficiency" id="entrypoint_service_efficiency">Entrypoint service efficiency</a></h3>
<div class="level3">
<p>
When designing an entrypoint service interface meant to have multiple different implementations, you should consider making it possible for all its implementations to use <code>service_factory_single_t</code> (i.e. no per-instance member data); by e.g. moving functionality that needs multi-instance operation to a separate service type class that is created on-demand by one of entrypoint service methods. For example:
</p>
<pre class="code cpp"><span class="kw2">class</span> myservice : <span class="kw2">public</span> service_base <span class="br0">{</span>
<span class="kw2">public</span>:
<span class="co1">//this method accesses per-instance member data of the implementation class</span>
<span class="kw2">virtual</span> <span class="kw4">void</span> workerfunction<span class="br0">(</span><span class="kw4">const</span> <span class="kw4">void</span> * p_databuffer,t_size p_buffersize<span class="br0">)</span> = <span class="nu0">0</span>;
<span class="co1">//this method is used to determine which implementation can be used to process specific data stream.</span>
<span class="kw2">virtual</span> <span class="kw4">bool</span> queryfunction<span class="br0">(</span><span class="kw4">const</span> <span class="kw4">char</span> * p_dataformat<span class="br0">)</span> = <span class="nu0">0</span>;
FB2K_MAKE_SERVICE_INTERFACE_ENTRYPOINT<span class="br0">(</span>myservice<span class="br0">)</span>;
<span class="br0">}</span>;
<span class="br0">(</span>...<span class="br0">)</span>
service_ptr_t<myservice> findservice<span class="br0">(</span><span class="kw4">const</span> <span class="kw4">char</span> * p_dataformat<span class="br0">)</span> <span class="br0">{</span>
service_enum_t<myservice> e; service_ptr_t<myservice> ptr;
<span class="co1">//BOTTLENECK, this dynamically instantiates the service for each query.</span>
<span class="kw1">while</span><span class="br0">(</span>e.<span class="me1">next</span><span class="br0">(</span>ptr<span class="br0">)</span><span class="br0">)</span> <span class="br0">{</span>
<span class="kw1">if</span> <span class="br0">(</span>ptr->queryfunction<span class="br0">(</span>p_dataformat<span class="br0">)</span><span class="br0">)</span> <span class="kw1">return</span> ptr;
<span class="br0">}</span>
throw exception_io_data<span class="br0">(</span><span class="br0">)</span>;
<span class="br0">}</span></pre>
<p>
.. should be changed to:
</p>
<pre class="code cpp"><span class="co1">//no longer an entrypoint service - use myservice::instantiate to get an instance instead.</span>
<span class="kw2">class</span> myservice_instance : <span class="kw2">public</span> service_base <span class="br0">{</span>
<span class="kw2">public</span>:
<span class="kw2">virtual</span> <span class="kw4">void</span> workerfunction<span class="br0">(</span><span class="kw4">const</span> <span class="kw4">void</span> * p_databuffer,t_size p_buffersize<span class="br0">)</span> = <span class="nu0">0</span>;
FB2K_MAKE_SERVICE_INTERFACE<span class="br0">(</span>myservice_instance,service_base<span class="br0">)</span>;
<span class="br0">}</span>;
<span class="kw2">class</span> myservice : <span class="kw2">public</span> service_base <span class="br0">{</span>
<span class="kw2">public</span>:
<span class="co1">//this method is used to determine which implementation can be used to process specific data stream.</span>
<span class="kw2">virtual</span> <span class="kw4">bool</span> queryfunction<span class="br0">(</span><span class="kw4">const</span> <span class="kw4">char</span> * p_dataformat<span class="br0">)</span> = <span class="nu0">0</span>;
<span class="kw2">virtual</span> service_ptr_t<myservice_instance> instantiate<span class="br0">(</span><span class="br0">)</span> = <span class="nu0">0</span>;
FB2K_MAKE_SERVICE_INTERFACE_ENTRYPOINT<span class="br0">(</span>myservice<span class="br0">)</span>;
<span class="br0">}</span>;
template<typename t_myservice_instance_impl>
<span class="kw2">class</span> myservice_impl_t : <span class="kw2">public</span> myservice <span class="br0">{</span>
<span class="kw2">public</span>:
<span class="co1">//implementation of myservice_instance must provide static bool g_queryformatfunction(const char*);</span>
<span class="kw4">bool</span> queryfunction<span class="br0">(</span><span class="kw4">const</span> <span class="kw4">char</span> * p_dataformat<span class="br0">)</span> <span class="br0">{</span><span class="kw1">return</span> t_myservice_instance_impl::<span class="me2">g_queryfunction</span><span class="br0">(</span>p_dataformat<span class="br0">)</span>;<span class="br0">}</span>
service_ptr_t<myservice_instance> instantiate<span class="br0">(</span><span class="br0">)</span> <span class="br0">{</span><span class="kw1">return</span> <span class="kw3">new</span> service_impl_t<t_myservice_instance_impl><span class="br0">(</span><span class="br0">)</span>;<span class="br0">}</span>
<span class="br0">}</span>;
template<typename t_myservice_instance_impl> <span class="kw2">class</span> myservice_factory_t :
<span class="kw2">public</span> service_factory_single_t<myservice_impl_t<t_myservice_instance_impl> > <span class="br0">{</span><span class="br0">}</span>;
<span class="co1">//usage: static myservice_factory_t<myclass> g_myclass_factory;</span>
<span class="br0">(</span>...<span class="br0">)</span>
service_ptr_t<myservice_instance> findservice<span class="br0">(</span><span class="kw4">const</span> <span class="kw4">char</span> * p_dataformat<span class="br0">)</span> <span class="br0">{</span>
service_enum_t<myservice> e; service_ptr_t<myservice> ptr;
<span class="co1">//no more bottleneck, enumerated service does not perform inefficient operations when requesting an instance.</span>
<span class="kw1">while</span><span class="br0">(</span>e.<span class="me1">next</span><span class="br0">(</span>ptr<span class="br0">)</span><span class="br0">)</span> <span class="br0">{</span>
<span class="co1">//"inefficient" part is used only once, with implementation that actually supports what we request.</span>
<span class="kw1">if</span> <span class="br0">(</span>ptr->queryfunction<span class="br0">(</span>p_dataformat<span class="br0">)</span><span class="br0">)</span> <span class="kw1">return</span> ptr->instantiate<span class="br0">(</span><span class="br0">)</span>;
<span class="br0">}</span>
throw exception_io_data<span class="br0">(</span><span class="br0">)</span>;
<span class="br0">}</span></pre>
</div>
<!-- SECTION "Entrypoint service efficiency" [22634-] --></div>
</body>
</html>