1 |
/* |
2 |
IASIOThiscallResolver.cpp see the comments in iasiothiscallresolver.h for |
3 |
the top level description - this comment describes the technical details of |
4 |
the implementation. |
5 |
|
6 |
The latest version of this file is available from: |
7 |
http://www.audiomulch.com/~rossb/code/calliasio |
8 |
|
9 |
please email comments to Ross Bencina <rossb@audiomulch.com> |
10 |
|
11 |
BACKGROUND |
12 |
|
13 |
The IASIO interface declared in the Steinberg ASIO 2 SDK declares |
14 |
functions with no explicit calling convention. This causes MSVC++ to default |
15 |
to using the thiscall convention, which is a proprietary convention not |
16 |
implemented by some non-microsoft compilers - notably borland BCC, |
17 |
C++Builder, and gcc. MSVC++ is the defacto standard compiler used by |
18 |
Steinberg. As a result of this situation, the ASIO sdk will compile with |
19 |
any compiler, however attempting to execute the compiled code will cause a |
20 |
crash due to different default calling conventions on non-Microsoft |
21 |
compilers. |
22 |
|
23 |
IASIOThiscallResolver solves the problem by providing an adapter class that |
24 |
delegates to the IASIO interface using the correct calling convention |
25 |
(thiscall). Due to the lack of support for thiscall in the Borland and GCC |
26 |
compilers, the calls have been implemented in assembly language. |
27 |
|
28 |
A number of macros are defined for thiscall function calls with different |
29 |
numbers of parameters, with and without return values - it may be possible |
30 |
to modify the format of these macros to make them work with other inline |
31 |
assemblers. |
32 |
|
33 |
|
34 |
THISCALL DEFINITION |
35 |
|
36 |
A number of definitions of the thiscall calling convention are floating |
37 |
around the internet. The following definition has been validated against |
38 |
output from the MSVC++ compiler: |
39 |
|
40 |
For non-vararg functions, thiscall works as follows: the object (this) |
41 |
pointer is passed in ECX. All arguments are passed on the stack in |
42 |
right to left order. The return value is placed in EAX. The callee |
43 |
clears the passed arguments from the stack. |
44 |
|
45 |
|
46 |
FINDING FUNCTION POINTERS FROM AN IASIO POINTER |
47 |
|
48 |
The first field of a COM object is a pointer to its vtble. Thus a pointer |
49 |
to an object implementing the IASIO interface also points to a pointer to |
50 |
that object's vtbl. The vtble is a table of function pointers for all of |
51 |
the virtual functions exposed by the implemented interfaces. |
52 |
|
53 |
If we consider a variable declared as a pointer to IASO: |
54 |
|
55 |
IASIO *theAsioDriver |
56 |
|
57 |
theAsioDriver points to: |
58 |
|
59 |
object implementing IASIO |
60 |
{ |
61 |
IASIOvtbl *vtbl |
62 |
other data |
63 |
} |
64 |
|
65 |
in other words, theAsioDriver points to a pointer to an IASIOvtbl |
66 |
|
67 |
vtbl points to a table of function pointers: |
68 |
|
69 |
IASIOvtbl ( interface IASIO : public IUnknown ) |
70 |
{ |
71 |
(IUnknown functions) |
72 |
0 virtual HRESULT STDMETHODCALLTYPE (*QueryInterface)(REFIID riid, void **ppv) = 0; |
73 |
4 virtual ULONG STDMETHODCALLTYPE (*AddRef)() = 0; |
74 |
8 virtual ULONG STDMETHODCALLTYPE (*Release)() = 0; |
75 |
|
76 |
(IASIO functions) |
77 |
12 virtual ASIOBool (*init)(void *sysHandle) = 0; |
78 |
16 virtual void (*getDriverName)(char *name) = 0; |
79 |
20 virtual long (*getDriverVersion)() = 0; |
80 |
24 virtual void (*getErrorMessage)(char *string) = 0; |
81 |
28 virtual ASIOError (*start)() = 0; |
82 |
32 virtual ASIOError (*stop)() = 0; |
83 |
36 virtual ASIOError (*getChannels)(long *numInputChannels, long *numOutputChannels) = 0; |
84 |
40 virtual ASIOError (*getLatencies)(long *inputLatency, long *outputLatency) = 0; |
85 |
44 virtual ASIOError (*getBufferSize)(long *minSize, long *maxSize, |
86 |
long *preferredSize, long *granularity) = 0; |
87 |
48 virtual ASIOError (*canSampleRate)(ASIOSampleRate sampleRate) = 0; |
88 |
52 virtual ASIOError (*getSampleRate)(ASIOSampleRate *sampleRate) = 0; |
89 |
56 virtual ASIOError (*setSampleRate)(ASIOSampleRate sampleRate) = 0; |
90 |
60 virtual ASIOError (*getClockSources)(ASIOClockSource *clocks, long *numSources) = 0; |
91 |
64 virtual ASIOError (*setClockSource)(long reference) = 0; |
92 |
68 virtual ASIOError (*getSamplePosition)(ASIOSamples *sPos, ASIOTimeStamp *tStamp) = 0; |
93 |
72 virtual ASIOError (*getChannelInfo)(ASIOChannelInfo *info) = 0; |
94 |
76 virtual ASIOError (*createBuffers)(ASIOBufferInfo *bufferInfos, long numChannels, |
95 |
long bufferSize, ASIOCallbacks *callbacks) = 0; |
96 |
80 virtual ASIOError (*disposeBuffers)() = 0; |
97 |
84 virtual ASIOError (*controlPanel)() = 0; |
98 |
88 virtual ASIOError (*future)(long selector,void *opt) = 0; |
99 |
92 virtual ASIOError (*outputReady)() = 0; |
100 |
}; |
101 |
|
102 |
The numbers in the left column show the byte offset of each function ptr |
103 |
from the beginning of the vtbl. These numbers are used in the code below |
104 |
to select different functions. |
105 |
|
106 |
In order to find the address of a particular function, theAsioDriver |
107 |
must first be dereferenced to find the value of the vtbl pointer: |
108 |
|
109 |
mov eax, theAsioDriver |
110 |
mov edx, [theAsioDriver] // edx now points to vtbl[0] |
111 |
|
112 |
Then an offset must be added to the vtbl pointer to select a |
113 |
particular function, for example vtbl+44 points to the slot containing |
114 |
a pointer to the getBufferSize function. |
115 |
|
116 |
Finally vtbl+x must be dereferenced to obtain the value of the function |
117 |
pointer stored in that address: |
118 |
|
119 |
call [edx+44] // call the function pointed to by |
120 |
// the value in the getBufferSize field of the vtbl |
121 |
|
122 |
|
123 |
SEE ALSO |
124 |
|
125 |
Martin Fay's OpenASIO DLL at http://www.martinfay.com solves the same |
126 |
problem by providing a new COM interface which wraps IASIO with an |
127 |
interface that uses portable calling conventions. OpenASIO must be compiled |
128 |
with MSVC, and requires that you ship the OpenASIO DLL with your |
129 |
application. |
130 |
|
131 |
|
132 |
ACKNOWLEDGEMENTS |
133 |
|
134 |
Ross Bencina: worked out the thiscall details above, wrote the original |
135 |
Borland asm macros, and a patch for asio.cpp (which is no longer needed). |
136 |
Thanks to Martin Fay for introducing me to the issues discussed here, |
137 |
and to Rene G. Ceballos for assisting with asm dumps from MSVC++. |
138 |
|
139 |
Antti Silvast: converted the original calliasio to work with gcc and NASM |
140 |
by implementing the asm code in a separate file. |
141 |
|
142 |
Fraser Adams: modified the original calliasio containing the Borland inline |
143 |
asm to add inline asm for gcc i.e. Intel syntax for Borland and AT&T syntax |
144 |
for gcc. This seems a neater approach for gcc than to have a separate .asm |
145 |
file and it means that we only need one version of the thiscall patch. |
146 |
|
147 |
Fraser Adams: rewrote the original calliasio patch in the form of the |
148 |
IASIOThiscallResolver class in order to avoid modifications to files from |
149 |
the Steinberg SDK, which may have had potential licence issues. |
150 |
|
151 |
Andrew Baldwin: contributed fixes for compatibility problems with more |
152 |
recent versions of the gcc assembler. |
153 |
*/ |
154 |
|
155 |
|
156 |
// We only need IASIOThiscallResolver at all if we are on Win32. For other |
157 |
// platforms we simply bypass the IASIOThiscallResolver definition to allow us |
158 |
// to be safely #include'd whatever the platform to keep client code portable |
159 |
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) |
160 |
|
161 |
// gcc 4.4 is using the same calling conventions as Microsoft on Win64 |
162 |
#if !defined(__WIN64__) |
163 |
|
164 |
// If microsoft compiler we can call IASIO directly so IASIOThiscallResolver |
165 |
// is not used. |
166 |
#if !defined(_MSC_VER) |
167 |
|
168 |
#include <new> |
169 |
#include <assert.h> |
170 |
|
171 |
// We have a mechanism in iasiothiscallresolver.h to ensure that asio.h is |
172 |
// #include'd before it in client code, we do NOT want to do this test here. |
173 |
#define iasiothiscallresolver_sourcefile 1 |
174 |
#include "iasiothiscallresolver.h" |
175 |
#undef iasiothiscallresolver_sourcefile |
176 |
|
177 |
// iasiothiscallresolver.h redefines ASIOInit for clients, but we don't want |
178 |
// this macro defined in this translation unit. |
179 |
#undef ASIOInit |
180 |
|
181 |
// theAsioDriver is a global pointer to the current IASIO instance which the |
182 |
// ASIO SDK uses to perform all actions on the IASIO interface. We substitute |
183 |
// our own forwarding interface into this pointer. |
184 |
extern IASIO* theAsioDriver; |
185 |
|
186 |
// The following macros define the inline assembler for BORLAND first then gcc |
187 |
|
188 |
#if defined(__BCPLUSPLUS__) || defined(__BORLANDC__) |
189 |
|
190 |
#define CALL_THISCALL_0( resultName, thisPtr, funcOffset )\ |
191 |
void *this_ = (thisPtr); \ |
192 |
__asm { \ |
193 |
mov ecx, this_ ; \ |
194 |
mov eax, [ecx] ; \ |
195 |
call [eax+funcOffset] ; \ |
196 |
mov resultName, eax ; \ |
197 |
} |
198 |
|
199 |
#define CALL_VOID_THISCALL_1( thisPtr, funcOffset, param1 )\ |
200 |
void *this_ = (thisPtr); \ |
201 |
__asm { \ |
202 |
mov eax, param1 ; \ |
203 |
push eax ; \ |
204 |
mov ecx, this_ ; \ |
205 |
mov eax, [ecx] ; \ |
206 |
call [eax+funcOffset] ; \ |
207 |
} |
208 |
|
209 |
#define CALL_THISCALL_1( resultName, thisPtr, funcOffset, param1 )\ |
210 |
void *this_ = (thisPtr); \ |
211 |
__asm { \ |
212 |
mov eax, param1 ; \ |
213 |
push eax ; \ |
214 |
mov ecx, this_ ; \ |
215 |
mov eax, [ecx] ; \ |
216 |
call [eax+funcOffset] ; \ |
217 |
mov resultName, eax ; \ |
218 |
} |
219 |
|
220 |
#define CALL_THISCALL_1_DOUBLE( resultName, thisPtr, funcOffset, param1 )\ |
221 |
void *this_ = (thisPtr); \ |
222 |
void *doubleParamPtr_ (¶m1); \ |
223 |
__asm { \ |
224 |
mov eax, doubleParamPtr_ ; \ |
225 |
push [eax+4] ; \ |
226 |
push [eax] ; \ |
227 |
mov ecx, this_ ; \ |
228 |
mov eax, [ecx] ; \ |
229 |
call [eax+funcOffset] ; \ |
230 |
mov resultName, eax ; \ |
231 |
} |
232 |
|
233 |
#define CALL_THISCALL_2( resultName, thisPtr, funcOffset, param1, param2 )\ |
234 |
void *this_ = (thisPtr); \ |
235 |
__asm { \ |
236 |
mov eax, param2 ; \ |
237 |
push eax ; \ |
238 |
mov eax, param1 ; \ |
239 |
push eax ; \ |
240 |
mov ecx, this_ ; \ |
241 |
mov eax, [ecx] ; \ |
242 |
call [eax+funcOffset] ; \ |
243 |
mov resultName, eax ; \ |
244 |
} |
245 |
|
246 |
#define CALL_THISCALL_4( resultName, thisPtr, funcOffset, param1, param2, param3, param4 )\ |
247 |
void *this_ = (thisPtr); \ |
248 |
__asm { \ |
249 |
mov eax, param4 ; \ |
250 |
push eax ; \ |
251 |
mov eax, param3 ; \ |
252 |
push eax ; \ |
253 |
mov eax, param2 ; \ |
254 |
push eax ; \ |
255 |
mov eax, param1 ; \ |
256 |
push eax ; \ |
257 |
mov ecx, this_ ; \ |
258 |
mov eax, [ecx] ; \ |
259 |
call [eax+funcOffset] ; \ |
260 |
mov resultName, eax ; \ |
261 |
} |
262 |
|
263 |
#elif defined(__GNUC__) |
264 |
|
265 |
#define CALL_THISCALL_0( resultName, thisPtr, funcOffset ) \ |
266 |
__asm__ __volatile__ ("movl (%1), %%edx\n\t" \ |
267 |
"call *"#funcOffset"(%%edx)\n\t" \ |
268 |
:"=a"(resultName) /* Output Operands */ \ |
269 |
:"c"(thisPtr) /* Input Operands */ \ |
270 |
); \ |
271 |
|
272 |
#define CALL_VOID_THISCALL_1( thisPtr, funcOffset, param1 ) \ |
273 |
__asm__ __volatile__ ("pushl %0\n\t" \ |
274 |
"movl (%1), %%edx\n\t" \ |
275 |
"call *"#funcOffset"(%%edx)\n\t" \ |
276 |
: /* Output Operands */ \ |
277 |
:"r"(param1), /* Input Operands */ \ |
278 |
"c"(thisPtr) \ |
279 |
); \ |
280 |
|
281 |
#define CALL_THISCALL_1( resultName, thisPtr, funcOffset, param1 ) \ |
282 |
__asm__ __volatile__ ("pushl %1\n\t" \ |
283 |
"movl (%2), %%edx\n\t" \ |
284 |
"call *"#funcOffset"(%%edx)\n\t" \ |
285 |
:"=a"(resultName) /* Output Operands */ \ |
286 |
:"r"(param1), /* Input Operands */ \ |
287 |
"c"(thisPtr) \ |
288 |
); \ |
289 |
|
290 |
#define CALL_THISCALL_1_DOUBLE( resultName, thisPtr, funcOffset, param1 ) \ |
291 |
__asm__ __volatile__ ("pushl 4(%1)\n\t" \ |
292 |
"pushl (%1)\n\t" \ |
293 |
"movl (%2), %%edx\n\t" \ |
294 |
"call *"#funcOffset"(%%edx);\n\t" \ |
295 |
:"=a"(resultName) /* Output Operands */ \ |
296 |
:"a"(¶m1), /* Input Operands */ \ |
297 |
/* Note: Using "r" above instead of "a" fails */ \ |
298 |
/* when using GCC 3.3.3, and maybe later versions*/\ |
299 |
"c"(thisPtr) \ |
300 |
); \ |
301 |
|
302 |
#define CALL_THISCALL_2( resultName, thisPtr, funcOffset, param1, param2 ) \ |
303 |
__asm__ __volatile__ ("pushl %1\n\t" \ |
304 |
"pushl %2\n\t" \ |
305 |
"movl (%3), %%edx\n\t" \ |
306 |
"call *"#funcOffset"(%%edx)\n\t" \ |
307 |
:"=a"(resultName) /* Output Operands */ \ |
308 |
:"r"(param2), /* Input Operands */ \ |
309 |
"r"(param1), \ |
310 |
"c"(thisPtr) \ |
311 |
); \ |
312 |
|
313 |
#define CALL_THISCALL_4( resultName, thisPtr, funcOffset, param1, param2, param3, param4 )\ |
314 |
__asm__ __volatile__ ("pushl %1\n\t" \ |
315 |
"pushl %2\n\t" \ |
316 |
"pushl %3\n\t" \ |
317 |
"pushl %4\n\t" \ |
318 |
"movl (%5), %%edx\n\t" \ |
319 |
"call *"#funcOffset"(%%edx)\n\t" \ |
320 |
:"=a"(resultName) /* Output Operands */ \ |
321 |
:"r"(param4), /* Input Operands */ \ |
322 |
"r"(param3), \ |
323 |
"r"(param2), \ |
324 |
"r"(param1), \ |
325 |
"c"(thisPtr) \ |
326 |
); \ |
327 |
|
328 |
#endif |
329 |
|
330 |
// Our static singleton instance. |
331 |
IASIOThiscallResolver IASIOThiscallResolver::instance; |
332 |
|
333 |
// Constructor called to initialize static Singleton instance above. Note that |
334 |
// it is important not to clear that_ incase it has already been set by the call |
335 |
// to placement new in ASIOInit(). |
336 |
IASIOThiscallResolver::IASIOThiscallResolver() |
337 |
{ |
338 |
} |
339 |
|
340 |
// Constructor called from ASIOInit() below |
341 |
IASIOThiscallResolver::IASIOThiscallResolver(IASIO* that) |
342 |
: that_( that ) |
343 |
{ |
344 |
} |
345 |
|
346 |
// Implement IUnknown methods as assert(false). IASIOThiscallResolver is not |
347 |
// really a COM object, just a wrapper which will work with the ASIO SDK. |
348 |
// If you wanted to use ASIO without the SDK you might want to implement COM |
349 |
// aggregation in these methods. |
350 |
HRESULT STDMETHODCALLTYPE IASIOThiscallResolver::QueryInterface(REFIID riid, void **ppv) |
351 |
{ |
352 |
(void)riid; // suppress unused variable warning |
353 |
|
354 |
assert( false ); // this function should never be called by the ASIO SDK. |
355 |
|
356 |
*ppv = NULL; |
357 |
return E_NOINTERFACE; |
358 |
} |
359 |
|
360 |
ULONG STDMETHODCALLTYPE IASIOThiscallResolver::AddRef() |
361 |
{ |
362 |
assert( false ); // this function should never be called by the ASIO SDK. |
363 |
|
364 |
return 1; |
365 |
} |
366 |
|
367 |
ULONG STDMETHODCALLTYPE IASIOThiscallResolver::Release() |
368 |
{ |
369 |
assert( false ); // this function should never be called by the ASIO SDK. |
370 |
|
371 |
return 1; |
372 |
} |
373 |
|
374 |
// Implement the IASIO interface methods by performing the vptr manipulation |
375 |
// described above then delegating to the real implementation. |
376 |
ASIOBool IASIOThiscallResolver::init(void *sysHandle) |
377 |
{ |
378 |
ASIOBool result; |
379 |
CALL_THISCALL_1( result, that_, 12, sysHandle ); |
380 |
return result; |
381 |
} |
382 |
|
383 |
void IASIOThiscallResolver::getDriverName(char *name) |
384 |
{ |
385 |
CALL_VOID_THISCALL_1( that_, 16, name ); |
386 |
} |
387 |
|
388 |
long IASIOThiscallResolver::getDriverVersion() |
389 |
{ |
390 |
ASIOBool result; |
391 |
CALL_THISCALL_0( result, that_, 20 ); |
392 |
return result; |
393 |
} |
394 |
|
395 |
void IASIOThiscallResolver::getErrorMessage(char *string) |
396 |
{ |
397 |
CALL_VOID_THISCALL_1( that_, 24, string ); |
398 |
} |
399 |
|
400 |
ASIOError IASIOThiscallResolver::start() |
401 |
{ |
402 |
ASIOBool result; |
403 |
CALL_THISCALL_0( result, that_, 28 ); |
404 |
return result; |
405 |
} |
406 |
|
407 |
ASIOError IASIOThiscallResolver::stop() |
408 |
{ |
409 |
ASIOBool result; |
410 |
CALL_THISCALL_0( result, that_, 32 ); |
411 |
return result; |
412 |
} |
413 |
|
414 |
ASIOError IASIOThiscallResolver::getChannels(long *numInputChannels, long *numOutputChannels) |
415 |
{ |
416 |
ASIOBool result; |
417 |
CALL_THISCALL_2( result, that_, 36, numInputChannels, numOutputChannels ); |
418 |
return result; |
419 |
} |
420 |
|
421 |
ASIOError IASIOThiscallResolver::getLatencies(long *inputLatency, long *outputLatency) |
422 |
{ |
423 |
ASIOBool result; |
424 |
CALL_THISCALL_2( result, that_, 40, inputLatency, outputLatency ); |
425 |
return result; |
426 |
} |
427 |
|
428 |
ASIOError IASIOThiscallResolver::getBufferSize(long *minSize, long *maxSize, |
429 |
long *preferredSize, long *granularity) |
430 |
{ |
431 |
ASIOBool result; |
432 |
CALL_THISCALL_4( result, that_, 44, minSize, maxSize, preferredSize, granularity ); |
433 |
return result; |
434 |
} |
435 |
|
436 |
ASIOError IASIOThiscallResolver::canSampleRate(ASIOSampleRate sampleRate) |
437 |
{ |
438 |
ASIOBool result; |
439 |
CALL_THISCALL_1_DOUBLE( result, that_, 48, sampleRate ); |
440 |
return result; |
441 |
} |
442 |
|
443 |
ASIOError IASIOThiscallResolver::getSampleRate(ASIOSampleRate *sampleRate) |
444 |
{ |
445 |
ASIOBool result; |
446 |
CALL_THISCALL_1( result, that_, 52, sampleRate ); |
447 |
return result; |
448 |
} |
449 |
|
450 |
ASIOError IASIOThiscallResolver::setSampleRate(ASIOSampleRate sampleRate) |
451 |
{ |
452 |
ASIOBool result; |
453 |
CALL_THISCALL_1_DOUBLE( result, that_, 56, sampleRate ); |
454 |
return result; |
455 |
} |
456 |
|
457 |
ASIOError IASIOThiscallResolver::getClockSources(ASIOClockSource *clocks, long *numSources) |
458 |
{ |
459 |
ASIOBool result; |
460 |
CALL_THISCALL_2( result, that_, 60, clocks, numSources ); |
461 |
return result; |
462 |
} |
463 |
|
464 |
ASIOError IASIOThiscallResolver::setClockSource(long reference) |
465 |
{ |
466 |
ASIOBool result; |
467 |
CALL_THISCALL_1( result, that_, 64, reference ); |
468 |
return result; |
469 |
} |
470 |
|
471 |
ASIOError IASIOThiscallResolver::getSamplePosition(ASIOSamples *sPos, ASIOTimeStamp *tStamp) |
472 |
{ |
473 |
ASIOBool result; |
474 |
CALL_THISCALL_2( result, that_, 68, sPos, tStamp ); |
475 |
return result; |
476 |
} |
477 |
|
478 |
ASIOError IASIOThiscallResolver::getChannelInfo(ASIOChannelInfo *info) |
479 |
{ |
480 |
ASIOBool result; |
481 |
CALL_THISCALL_1( result, that_, 72, info ); |
482 |
return result; |
483 |
} |
484 |
|
485 |
ASIOError IASIOThiscallResolver::createBuffers(ASIOBufferInfo *bufferInfos, |
486 |
long numChannels, long bufferSize, ASIOCallbacks *callbacks) |
487 |
{ |
488 |
ASIOBool result; |
489 |
CALL_THISCALL_4( result, that_, 76, bufferInfos, numChannels, bufferSize, callbacks ); |
490 |
return result; |
491 |
} |
492 |
|
493 |
ASIOError IASIOThiscallResolver::disposeBuffers() |
494 |
{ |
495 |
ASIOBool result; |
496 |
CALL_THISCALL_0( result, that_, 80 ); |
497 |
return result; |
498 |
} |
499 |
|
500 |
ASIOError IASIOThiscallResolver::controlPanel() |
501 |
{ |
502 |
ASIOBool result; |
503 |
CALL_THISCALL_0( result, that_, 84 ); |
504 |
return result; |
505 |
} |
506 |
|
507 |
ASIOError IASIOThiscallResolver::future(long selector,void *opt) |
508 |
{ |
509 |
ASIOBool result; |
510 |
CALL_THISCALL_2( result, that_, 88, selector, opt ); |
511 |
return result; |
512 |
} |
513 |
|
514 |
ASIOError IASIOThiscallResolver::outputReady() |
515 |
{ |
516 |
ASIOBool result; |
517 |
CALL_THISCALL_0( result, that_, 92 ); |
518 |
return result; |
519 |
} |
520 |
|
521 |
// Implement our substitute ASIOInit() method |
522 |
ASIOError IASIOThiscallResolver::ASIOInit(ASIODriverInfo *info) |
523 |
{ |
524 |
// To ensure that our instance's vptr is correctly constructed, even if |
525 |
// ASIOInit is called prior to main(), we explicitly call its constructor |
526 |
// (potentially over the top of an existing instance). Note that this is |
527 |
// pretty ugly, and is only safe because IASIOThiscallResolver has no |
528 |
// destructor and contains no objects with destructors. |
529 |
new((void*)&instance) IASIOThiscallResolver( theAsioDriver ); |
530 |
|
531 |
// Interpose between ASIO client code and the real driver. |
532 |
theAsioDriver = &instance; |
533 |
|
534 |
// Note that we never need to switch theAsioDriver back to point to the |
535 |
// real driver because theAsioDriver is reset to zero in ASIOExit(). |
536 |
|
537 |
// Delegate to the real ASIOInit |
538 |
return ::ASIOInit(info); |
539 |
} |
540 |
|
541 |
#endif /* !defined(_MSC_VER) */ |
542 |
#endif /* !defined(__WIN64__) */ |
543 |
#endif /* Win32 */ |