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 |
// If microsoft compiler we can call IASIO directly so IASIOThiscallResolver |
162 |
// is not used. |
163 |
#if !defined(_MSC_VER) |
164 |
|
165 |
#include <new> |
166 |
#include <assert.h> |
167 |
|
168 |
// We have a mechanism in iasiothiscallresolver.h to ensure that asio.h is |
169 |
// #include'd before it in client code, we do NOT want to do this test here. |
170 |
#define iasiothiscallresolver_sourcefile 1 |
171 |
#include "iasiothiscallresolver.h" |
172 |
#undef iasiothiscallresolver_sourcefile |
173 |
|
174 |
// iasiothiscallresolver.h redefines ASIOInit for clients, but we don't want |
175 |
// this macro defined in this translation unit. |
176 |
#undef ASIOInit |
177 |
|
178 |
// theAsioDriver is a global pointer to the current IASIO instance which the |
179 |
// ASIO SDK uses to perform all actions on the IASIO interface. We substitute |
180 |
// our own forwarding interface into this pointer. |
181 |
extern IASIO* theAsioDriver; |
182 |
|
183 |
// The following macros define the inline assembler for BORLAND first then gcc |
184 |
|
185 |
#if defined(__BCPLUSPLUS__) || defined(__BORLANDC__) |
186 |
|
187 |
#define CALL_THISCALL_0( resultName, thisPtr, funcOffset )\ |
188 |
void *this_ = (thisPtr); \ |
189 |
__asm { \ |
190 |
mov ecx, this_ ; \ |
191 |
mov eax, [ecx] ; \ |
192 |
call [eax+funcOffset] ; \ |
193 |
mov resultName, eax ; \ |
194 |
} |
195 |
|
196 |
#define CALL_VOID_THISCALL_1( thisPtr, funcOffset, param1 )\ |
197 |
void *this_ = (thisPtr); \ |
198 |
__asm { \ |
199 |
mov eax, param1 ; \ |
200 |
push eax ; \ |
201 |
mov ecx, this_ ; \ |
202 |
mov eax, [ecx] ; \ |
203 |
call [eax+funcOffset] ; \ |
204 |
} |
205 |
|
206 |
#define CALL_THISCALL_1( resultName, thisPtr, funcOffset, param1 )\ |
207 |
void *this_ = (thisPtr); \ |
208 |
__asm { \ |
209 |
mov eax, param1 ; \ |
210 |
push eax ; \ |
211 |
mov ecx, this_ ; \ |
212 |
mov eax, [ecx] ; \ |
213 |
call [eax+funcOffset] ; \ |
214 |
mov resultName, eax ; \ |
215 |
} |
216 |
|
217 |
#define CALL_THISCALL_1_DOUBLE( resultName, thisPtr, funcOffset, param1 )\ |
218 |
void *this_ = (thisPtr); \ |
219 |
void *doubleParamPtr_ (¶m1); \ |
220 |
__asm { \ |
221 |
mov eax, doubleParamPtr_ ; \ |
222 |
push [eax+4] ; \ |
223 |
push [eax] ; \ |
224 |
mov ecx, this_ ; \ |
225 |
mov eax, [ecx] ; \ |
226 |
call [eax+funcOffset] ; \ |
227 |
mov resultName, eax ; \ |
228 |
} |
229 |
|
230 |
#define CALL_THISCALL_2( resultName, thisPtr, funcOffset, param1, param2 )\ |
231 |
void *this_ = (thisPtr); \ |
232 |
__asm { \ |
233 |
mov eax, param2 ; \ |
234 |
push eax ; \ |
235 |
mov eax, param1 ; \ |
236 |
push eax ; \ |
237 |
mov ecx, this_ ; \ |
238 |
mov eax, [ecx] ; \ |
239 |
call [eax+funcOffset] ; \ |
240 |
mov resultName, eax ; \ |
241 |
} |
242 |
|
243 |
#define CALL_THISCALL_4( resultName, thisPtr, funcOffset, param1, param2, param3, param4 )\ |
244 |
void *this_ = (thisPtr); \ |
245 |
__asm { \ |
246 |
mov eax, param4 ; \ |
247 |
push eax ; \ |
248 |
mov eax, param3 ; \ |
249 |
push eax ; \ |
250 |
mov eax, param2 ; \ |
251 |
push eax ; \ |
252 |
mov eax, param1 ; \ |
253 |
push eax ; \ |
254 |
mov ecx, this_ ; \ |
255 |
mov eax, [ecx] ; \ |
256 |
call [eax+funcOffset] ; \ |
257 |
mov resultName, eax ; \ |
258 |
} |
259 |
|
260 |
#elif defined(__GNUC__) |
261 |
|
262 |
#define CALL_THISCALL_0( resultName, thisPtr, funcOffset ) \ |
263 |
__asm__ __volatile__ ("movl (%1), %%edx\n\t" \ |
264 |
"call *"#funcOffset"(%%edx)\n\t" \ |
265 |
:"=a"(resultName) /* Output Operands */ \ |
266 |
:"c"(thisPtr) /* Input Operands */ \ |
267 |
); \ |
268 |
|
269 |
#define CALL_VOID_THISCALL_1( thisPtr, funcOffset, param1 ) \ |
270 |
__asm__ __volatile__ ("pushl %0\n\t" \ |
271 |
"movl (%1), %%edx\n\t" \ |
272 |
"call *"#funcOffset"(%%edx)\n\t" \ |
273 |
: /* Output Operands */ \ |
274 |
:"r"(param1), /* Input Operands */ \ |
275 |
"c"(thisPtr) \ |
276 |
); \ |
277 |
|
278 |
#define CALL_THISCALL_1( resultName, thisPtr, funcOffset, param1 ) \ |
279 |
__asm__ __volatile__ ("pushl %1\n\t" \ |
280 |
"movl (%2), %%edx\n\t" \ |
281 |
"call *"#funcOffset"(%%edx)\n\t" \ |
282 |
:"=a"(resultName) /* Output Operands */ \ |
283 |
:"r"(param1), /* Input Operands */ \ |
284 |
"c"(thisPtr) \ |
285 |
); \ |
286 |
|
287 |
#define CALL_THISCALL_1_DOUBLE( resultName, thisPtr, funcOffset, param1 ) \ |
288 |
__asm__ __volatile__ ("pushl 4(%1)\n\t" \ |
289 |
"pushl (%1)\n\t" \ |
290 |
"movl (%2), %%edx\n\t" \ |
291 |
"call *"#funcOffset"(%%edx);\n\t" \ |
292 |
:"=a"(resultName) /* Output Operands */ \ |
293 |
:"a"(¶m1), /* Input Operands */ \ |
294 |
/* Note: Using "r" above instead of "a" fails */ \ |
295 |
/* when using GCC 3.3.3, and maybe later versions*/\ |
296 |
"c"(thisPtr) \ |
297 |
); \ |
298 |
|
299 |
#define CALL_THISCALL_2( resultName, thisPtr, funcOffset, param1, param2 ) \ |
300 |
__asm__ __volatile__ ("pushl %1\n\t" \ |
301 |
"pushl %2\n\t" \ |
302 |
"movl (%3), %%edx\n\t" \ |
303 |
"call *"#funcOffset"(%%edx)\n\t" \ |
304 |
:"=a"(resultName) /* Output Operands */ \ |
305 |
:"r"(param2), /* Input Operands */ \ |
306 |
"r"(param1), \ |
307 |
"c"(thisPtr) \ |
308 |
); \ |
309 |
|
310 |
#define CALL_THISCALL_4( resultName, thisPtr, funcOffset, param1, param2, param3, param4 )\ |
311 |
__asm__ __volatile__ ("pushl %1\n\t" \ |
312 |
"pushl %2\n\t" \ |
313 |
"pushl %3\n\t" \ |
314 |
"pushl %4\n\t" \ |
315 |
"movl (%5), %%edx\n\t" \ |
316 |
"call *"#funcOffset"(%%edx)\n\t" \ |
317 |
:"=a"(resultName) /* Output Operands */ \ |
318 |
:"r"(param4), /* Input Operands */ \ |
319 |
"r"(param3), \ |
320 |
"r"(param2), \ |
321 |
"r"(param1), \ |
322 |
"c"(thisPtr) \ |
323 |
); \ |
324 |
|
325 |
#endif |
326 |
|
327 |
// Our static singleton instance. |
328 |
IASIOThiscallResolver IASIOThiscallResolver::instance; |
329 |
|
330 |
// Constructor called to initialize static Singleton instance above. Note that |
331 |
// it is important not to clear that_ incase it has already been set by the call |
332 |
// to placement new in ASIOInit(). |
333 |
IASIOThiscallResolver::IASIOThiscallResolver() |
334 |
{ |
335 |
} |
336 |
|
337 |
// Constructor called from ASIOInit() below |
338 |
IASIOThiscallResolver::IASIOThiscallResolver(IASIO* that) |
339 |
: that_( that ) |
340 |
{ |
341 |
} |
342 |
|
343 |
// Implement IUnknown methods as assert(false). IASIOThiscallResolver is not |
344 |
// really a COM object, just a wrapper which will work with the ASIO SDK. |
345 |
// If you wanted to use ASIO without the SDK you might want to implement COM |
346 |
// aggregation in these methods. |
347 |
HRESULT STDMETHODCALLTYPE IASIOThiscallResolver::QueryInterface(REFIID riid, void **ppv) |
348 |
{ |
349 |
(void)riid; // suppress unused variable warning |
350 |
|
351 |
assert( false ); // this function should never be called by the ASIO SDK. |
352 |
|
353 |
*ppv = NULL; |
354 |
return E_NOINTERFACE; |
355 |
} |
356 |
|
357 |
ULONG STDMETHODCALLTYPE IASIOThiscallResolver::AddRef() |
358 |
{ |
359 |
assert( false ); // this function should never be called by the ASIO SDK. |
360 |
|
361 |
return 1; |
362 |
} |
363 |
|
364 |
ULONG STDMETHODCALLTYPE IASIOThiscallResolver::Release() |
365 |
{ |
366 |
assert( false ); // this function should never be called by the ASIO SDK. |
367 |
|
368 |
return 1; |
369 |
} |
370 |
|
371 |
// Implement the IASIO interface methods by performing the vptr manipulation |
372 |
// described above then delegating to the real implementation. |
373 |
ASIOBool IASIOThiscallResolver::init(void *sysHandle) |
374 |
{ |
375 |
ASIOBool result; |
376 |
CALL_THISCALL_1( result, that_, 12, sysHandle ); |
377 |
return result; |
378 |
} |
379 |
|
380 |
void IASIOThiscallResolver::getDriverName(char *name) |
381 |
{ |
382 |
CALL_VOID_THISCALL_1( that_, 16, name ); |
383 |
} |
384 |
|
385 |
long IASIOThiscallResolver::getDriverVersion() |
386 |
{ |
387 |
ASIOBool result; |
388 |
CALL_THISCALL_0( result, that_, 20 ); |
389 |
return result; |
390 |
} |
391 |
|
392 |
void IASIOThiscallResolver::getErrorMessage(char *string) |
393 |
{ |
394 |
CALL_VOID_THISCALL_1( that_, 24, string ); |
395 |
} |
396 |
|
397 |
ASIOError IASIOThiscallResolver::start() |
398 |
{ |
399 |
ASIOBool result; |
400 |
CALL_THISCALL_0( result, that_, 28 ); |
401 |
return result; |
402 |
} |
403 |
|
404 |
ASIOError IASIOThiscallResolver::stop() |
405 |
{ |
406 |
ASIOBool result; |
407 |
CALL_THISCALL_0( result, that_, 32 ); |
408 |
return result; |
409 |
} |
410 |
|
411 |
ASIOError IASIOThiscallResolver::getChannels(long *numInputChannels, long *numOutputChannels) |
412 |
{ |
413 |
ASIOBool result; |
414 |
CALL_THISCALL_2( result, that_, 36, numInputChannels, numOutputChannels ); |
415 |
return result; |
416 |
} |
417 |
|
418 |
ASIOError IASIOThiscallResolver::getLatencies(long *inputLatency, long *outputLatency) |
419 |
{ |
420 |
ASIOBool result; |
421 |
CALL_THISCALL_2( result, that_, 40, inputLatency, outputLatency ); |
422 |
return result; |
423 |
} |
424 |
|
425 |
ASIOError IASIOThiscallResolver::getBufferSize(long *minSize, long *maxSize, |
426 |
long *preferredSize, long *granularity) |
427 |
{ |
428 |
ASIOBool result; |
429 |
CALL_THISCALL_4( result, that_, 44, minSize, maxSize, preferredSize, granularity ); |
430 |
return result; |
431 |
} |
432 |
|
433 |
ASIOError IASIOThiscallResolver::canSampleRate(ASIOSampleRate sampleRate) |
434 |
{ |
435 |
ASIOBool result; |
436 |
CALL_THISCALL_1_DOUBLE( result, that_, 48, sampleRate ); |
437 |
return result; |
438 |
} |
439 |
|
440 |
ASIOError IASIOThiscallResolver::getSampleRate(ASIOSampleRate *sampleRate) |
441 |
{ |
442 |
ASIOBool result; |
443 |
CALL_THISCALL_1( result, that_, 52, sampleRate ); |
444 |
return result; |
445 |
} |
446 |
|
447 |
ASIOError IASIOThiscallResolver::setSampleRate(ASIOSampleRate sampleRate) |
448 |
{ |
449 |
ASIOBool result; |
450 |
CALL_THISCALL_1_DOUBLE( result, that_, 56, sampleRate ); |
451 |
return result; |
452 |
} |
453 |
|
454 |
ASIOError IASIOThiscallResolver::getClockSources(ASIOClockSource *clocks, long *numSources) |
455 |
{ |
456 |
ASIOBool result; |
457 |
CALL_THISCALL_2( result, that_, 60, clocks, numSources ); |
458 |
return result; |
459 |
} |
460 |
|
461 |
ASIOError IASIOThiscallResolver::setClockSource(long reference) |
462 |
{ |
463 |
ASIOBool result; |
464 |
CALL_THISCALL_1( result, that_, 64, reference ); |
465 |
return result; |
466 |
} |
467 |
|
468 |
ASIOError IASIOThiscallResolver::getSamplePosition(ASIOSamples *sPos, ASIOTimeStamp *tStamp) |
469 |
{ |
470 |
ASIOBool result; |
471 |
CALL_THISCALL_2( result, that_, 68, sPos, tStamp ); |
472 |
return result; |
473 |
} |
474 |
|
475 |
ASIOError IASIOThiscallResolver::getChannelInfo(ASIOChannelInfo *info) |
476 |
{ |
477 |
ASIOBool result; |
478 |
CALL_THISCALL_1( result, that_, 72, info ); |
479 |
return result; |
480 |
} |
481 |
|
482 |
ASIOError IASIOThiscallResolver::createBuffers(ASIOBufferInfo *bufferInfos, |
483 |
long numChannels, long bufferSize, ASIOCallbacks *callbacks) |
484 |
{ |
485 |
ASIOBool result; |
486 |
CALL_THISCALL_4( result, that_, 76, bufferInfos, numChannels, bufferSize, callbacks ); |
487 |
return result; |
488 |
} |
489 |
|
490 |
ASIOError IASIOThiscallResolver::disposeBuffers() |
491 |
{ |
492 |
ASIOBool result; |
493 |
CALL_THISCALL_0( result, that_, 80 ); |
494 |
return result; |
495 |
} |
496 |
|
497 |
ASIOError IASIOThiscallResolver::controlPanel() |
498 |
{ |
499 |
ASIOBool result; |
500 |
CALL_THISCALL_0( result, that_, 84 ); |
501 |
return result; |
502 |
} |
503 |
|
504 |
ASIOError IASIOThiscallResolver::future(long selector,void *opt) |
505 |
{ |
506 |
ASIOBool result; |
507 |
CALL_THISCALL_2( result, that_, 88, selector, opt ); |
508 |
return result; |
509 |
} |
510 |
|
511 |
ASIOError IASIOThiscallResolver::outputReady() |
512 |
{ |
513 |
ASIOBool result; |
514 |
CALL_THISCALL_0( result, that_, 92 ); |
515 |
return result; |
516 |
} |
517 |
|
518 |
// Implement our substitute ASIOInit() method |
519 |
ASIOError IASIOThiscallResolver::ASIOInit(ASIODriverInfo *info) |
520 |
{ |
521 |
// To ensure that our instance's vptr is correctly constructed, even if |
522 |
// ASIOInit is called prior to main(), we explicitly call its constructor |
523 |
// (potentially over the top of an existing instance). Note that this is |
524 |
// pretty ugly, and is only safe because IASIOThiscallResolver has no |
525 |
// destructor and contains no objects with destructors. |
526 |
new((void*)&instance) IASIOThiscallResolver( theAsioDriver ); |
527 |
|
528 |
// Interpose between ASIO client code and the real driver. |
529 |
theAsioDriver = &instance; |
530 |
|
531 |
// Note that we never need to switch theAsioDriver back to point to the |
532 |
// real driver because theAsioDriver is reset to zero in ASIOExit(). |
533 |
|
534 |
// Delegate to the real ASIOInit |
535 |
return ::ASIOInit(info); |
536 |
} |
537 |
|
538 |
#endif /* !defined(_MSC_VER) */ |
539 |
|
540 |
#endif /* Win32 */ |