Browse Source

Updated Timer code to avoid a rare messaging problem. Fixed a couple of minor build errors. Rearranged the atomic functions and added a new compare-and-swap operation. Added a thread-priority tweak to WASAPI. Removed MS-specific classes from the web browser component.

tags/2021-05-28
Julian Storer 15 years ago
parent
commit
35a4b5085f
18 changed files with 528 additions and 609 deletions
  1. +4
    -0
      build/macosx/Juce.xcodeproj/project.pbxproj
  2. +0
    -8
      juce.h
  3. +2
    -2
      juce_Config.h
  4. +206
    -145
      juce_amalgamated.cpp
  5. +35
    -156
      juce_amalgamated.h
  6. +2
    -2
      src/audio/plugins/formats/juce_AudioUnitPluginFormat.mm
  7. +7
    -2
      src/containers/juce_MemoryBlock.cpp
  8. +4
    -0
      src/containers/juce_MemoryBlock.h
  9. +34
    -148
      src/core/juce_Atomic.h
  10. +0
    -6
      src/core/juce_SystemStats.cpp
  11. +166
    -128
      src/events/juce_Timer.cpp
  12. +1
    -1
      src/gui/components/filebrowser/juce_FileBrowserComponent.h
  13. +10
    -5
      src/gui/components/properties/juce_ChoicePropertyComponent.cpp
  14. +12
    -4
      src/gui/components/properties/juce_ChoicePropertyComponent.h
  15. +1
    -0
      src/native/windows/juce_win32_NativeIncludes.h
  16. +11
    -0
      src/native/windows/juce_win32_Threads.cpp
  17. +18
    -0
      src/native/windows/juce_win32_WASAPI.cpp
  18. +15
    -2
      src/native/windows/juce_win32_WebBrowserComponent.cpp

+ 4
- 0
build/macosx/Juce.xcodeproj/project.pbxproj View File

@@ -4214,6 +4214,7 @@
COPY_PHASE_STRIP = NO;
GCC_DYNAMIC_NO_PIC = NO;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
GCC_VERSION = 4.2;
PREBINDING = NO;
SDKROOT = iphoneos3.0;
@@ -4226,6 +4227,7 @@
COPY_PHASE_STRIP = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
GCC_ENABLE_FIX_AND_CONTINUE = NO;
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
PREBINDING = NO;
SDKROOT = iphoneos3.0;
ZERO_LINK = NO;
@@ -4235,6 +4237,7 @@
84A487F808A22DD800752A2B /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
GCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES;
GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES;
MACOSX_DEPLOYMENT_TARGET = 10.4;
@@ -4245,6 +4248,7 @@
84A487F908A22DD800752A2B /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES;
STRIP_STYLE = "non-global";
};


+ 0
- 8
juce.h View File

@@ -47,10 +47,6 @@ BEGIN_JUCE_NAMESPACE
#pragma warning (disable: 4786) // (old vc6 warning about long class names)
#endif
#if JUCE_MAC || JUCE_IPHONE
#pragma align=natural
#endif
// this is where all the class header files get brought in..
#include "src/juce_core_includes.h"
@@ -65,10 +61,6 @@ BEGIN_JUCE_NAMESPACE
#pragma pack (pop)
#endif
#if JUCE_MAC || JUCE_IPHONE
#pragma align=reset
#endif
END_JUCE_NAMESPACE


+ 2
- 2
juce_Config.h View File

@@ -41,7 +41,7 @@
//=============================================================================
/** JUCE_FORCE_DEBUG: Normally, JUCE_DEBUG is set to 1 or 0 based on compiler and
project settings, but if you define this value, you can override this can force
project settings, but if you define this value, you can override this to force
it to be true or false.
*/
#ifndef JUCE_FORCE_DEBUG
@@ -123,7 +123,7 @@
#define JUCE_USE_FLAC 1
#endif
/** JUCE_USE_OGGBORBIS: Enables the Ogg-Vorbis audio codec classes (available on all platforms).
/** JUCE_USE_OGGVORBIS: Enables the Ogg-Vorbis audio codec classes (available on all platforms).
If your app doesn't need to read Ogg-Vorbis files, you might want to disable this to
reduce the size of your codebase and build time.
*/


+ 206
- 145
juce_amalgamated.cpp View File

@@ -361,6 +361,7 @@
#include <MMReg.h>
#include <mmdeviceapi.h>
#include <Audioclient.h>
#include <Avrt.h>
#include <functiondiscoverykeys.h>
#endif

@@ -1350,12 +1351,6 @@ void JUCE_PUBLIC_FUNCTION initialiseJuce_NonGUI()
jassert (ByteOrder::swap ((uint16) 0x1122) == 0x2211);
jassert (ByteOrder::swap ((uint32) 0x11223344) == 0x44332211);

// quick test to make sure the run-time lib doesn't crash on freeing a null-pointer.
SystemStats* nullPointer = 0;
juce_free (nullPointer);
delete[] nullPointer;
delete nullPointer;

// Some quick stream tests..
int randomInt = Random::getSystemRandom().nextInt();
int64 randomInt64 = Random::getSystemRandom().nextInt64();
@@ -2937,8 +2932,7 @@ MemoryBlock& MemoryBlock::operator= (const MemoryBlock& other) throw()

bool MemoryBlock::operator== (const MemoryBlock& other) const throw()
{
return (size == other.size)
&& (memcmp (data, other.data, size) == 0);
return matches (other.data, other.size);
}

bool MemoryBlock::operator!= (const MemoryBlock& other) const throw()
@@ -2946,6 +2940,12 @@ bool MemoryBlock::operator!= (const MemoryBlock& other) const throw()
return ! operator== (other);
}

bool MemoryBlock::matches (const void* dataToCompare, size_t dataSize) const throw()
{
return size == dataSize
&& memcmp (data, dataToCompare, size) == 0;
}

// this will resize the block to this size
void MemoryBlock::setSize (const size_t newSize,
const bool initialiseToZero) throw()
@@ -30191,8 +30191,8 @@ private:
{
for (int i = 0; i < supportedChannelsSize / sizeof (AUChannelInfo); ++i)
{
numIns = jmax (numIns, supportedChannels[i].inChannels);
numOuts = jmax (numOuts, supportedChannels[i].outChannels);
numIns = jmax (numIns, (int) supportedChannels[i].inChannels);
numOuts = jmax (numOuts, (int) supportedChannels[i].outChannels);
}
}
else
@@ -38011,118 +38011,6 @@ class InternalTimerThread : private Thread,
private DeletedAtShutdown,
private AsyncUpdater
{
private:
friend class Timer;
static InternalTimerThread* instance;
static CriticalSection lock;

Timer* volatile firstTimer;
bool volatile callbackNeeded;

InternalTimerThread (const InternalTimerThread&);
const InternalTimerThread& operator= (const InternalTimerThread&);

void addTimer (Timer* const t) throw()
{
#ifdef JUCE_DEBUG
Timer* tt = firstTimer;

while (tt != 0)
{
// trying to add a timer that's already here - shouldn't get to this point,
// so if you get this assertion, let me know!
jassert (tt != t);

tt = tt->next;
}

jassert (t->previous == 0 && t->next == 0);
#endif

Timer* i = firstTimer;

if (i == 0 || i->countdownMs > t->countdownMs)
{
t->next = firstTimer;
firstTimer = t;
}
else
{
while (i->next != 0 && i->next->countdownMs <= t->countdownMs)
i = i->next;

jassert (i != 0);

t->next = i->next;
t->previous = i;
i->next = t;
}

if (t->next != 0)
t->next->previous = t;

jassert ((t->next == 0 || t->next->countdownMs >= t->countdownMs)
&& (t->previous == 0 || t->previous->countdownMs <= t->countdownMs));

notify();
}

void removeTimer (Timer* const t) throw()
{
#ifdef JUCE_DEBUG
Timer* tt = firstTimer;
bool found = false;

while (tt != 0)
{
if (tt == t)
{
found = true;
break;
}

tt = tt->next;
}

// trying to remove a timer that's not here - shouldn't get to this point,
// so if you get this assertion, let me know!
jassert (found);
#endif

if (t->previous != 0)
{
jassert (firstTimer != t);
t->previous->next = t->next;
}
else
{
jassert (firstTimer == t);
firstTimer = t->next;
}

if (t->next != 0)
t->next->previous = t->previous;

t->next = 0;
t->previous = 0;
}

void decrementAllCounters (const int numMillisecs) const
{
Timer* t = firstTimer;

while (t != 0)
{
t->countdownMs -= numMillisecs;
t = t->next;
}
}

void handleAsyncUpdate()
{
startThread (7);
}

public:
InternalTimerThread()
: Thread ("Juce Timer"),
@@ -38147,7 +38035,7 @@ public:

while (! threadShouldExit())
{
uint32 now = Time::getMillisecondCounter();
const uint32 now = Time::getMillisecondCounter();

if (now <= lastTime)
{
@@ -38166,24 +38054,22 @@ public:

if (timeUntilFirstTimer <= 0)
{
callbackNeeded = true;
postMessage (new Message());

// sometimes, our message could get discarded by the OS (particularly when running as an RTAS when the app has a modal loop),
// so this is how long to wait before assuming the message has been lost and trying again.
const uint32 messageDeliveryTimeout = now + 2000;

while (callbackNeeded)
if (callbackNeeded.set (true))
{
wait (4);
postMessage (new Message());

if (threadShouldExit())
return;
const uint32 messageDeliveryTimeout = now + 2000;

now = Time::getMillisecondCounter();
while (callbackNeeded.get())
{
wait (4);

if (now > messageDeliveryTimeout)
break;
if (threadShouldExit())
return;

if (Time::getMillisecondCounter() > messageDeliveryTimeout)
break;
}
}
}
else
@@ -38216,7 +38102,7 @@ public:
JUCE_CATCH_EXCEPTION
}

callbackNeeded = false;
callbackNeeded.set (false);
}

static void callAnyTimersSynchronously()
@@ -38263,6 +38149,134 @@ public:
}
}
}

private:
friend class Timer;
static InternalTimerThread* instance;
static CriticalSection lock;
Timer* volatile firstTimer;

class AtomicBool
{
public:
AtomicBool (const bool value) throw() : value (static_cast<int32> (value)) {}
~AtomicBool() throw() {}

bool get() const throw() { return value != 0; }
bool set (const bool newValue) { return Atomic::compareAndExchange (value, newValue ? 1 : 0, value) != 0; }

private:
int32 value;

AtomicBool (const AtomicBool&);
AtomicBool& operator= (const AtomicBool&);
};

AtomicBool callbackNeeded;

void addTimer (Timer* const t) throw()
{
#ifdef JUCE_DEBUG
Timer* tt = firstTimer;

while (tt != 0)
{
// trying to add a timer that's already here - shouldn't get to this point,
// so if you get this assertion, let me know!
jassert (tt != t);

tt = tt->next;
}

jassert (t->previous == 0 && t->next == 0);
#endif

Timer* i = firstTimer;

if (i == 0 || i->countdownMs > t->countdownMs)
{
t->next = firstTimer;
firstTimer = t;
}
else
{
while (i->next != 0 && i->next->countdownMs <= t->countdownMs)
i = i->next;

jassert (i != 0);

t->next = i->next;
t->previous = i;
i->next = t;
}

if (t->next != 0)
t->next->previous = t;

jassert ((t->next == 0 || t->next->countdownMs >= t->countdownMs)
&& (t->previous == 0 || t->previous->countdownMs <= t->countdownMs));

notify();
}

void removeTimer (Timer* const t) throw()
{
#ifdef JUCE_DEBUG
Timer* tt = firstTimer;
bool found = false;

while (tt != 0)
{
if (tt == t)
{
found = true;
break;
}

tt = tt->next;
}

// trying to remove a timer that's not here - shouldn't get to this point,
// so if you get this assertion, let me know!
jassert (found);
#endif

if (t->previous != 0)
{
jassert (firstTimer != t);
t->previous->next = t->next;
}
else
{
jassert (firstTimer == t);
firstTimer = t->next;
}

if (t->next != 0)
t->next->previous = t->previous;

t->next = 0;
t->previous = 0;
}

void decrementAllCounters (const int numMillisecs) const
{
Timer* t = firstTimer;

while (t != 0)
{
t->countdownMs -= numMillisecs;
t = t->next;
}
}

void handleAsyncUpdate()
{
startThread (7);
}

InternalTimerThread (const InternalTimerThread&);
InternalTimerThread& operator= (const InternalTimerThread&);
};

InternalTimerThread* InternalTimerThread::instance = 0;
@@ -70888,12 +70902,13 @@ ChoicePropertyComponent::ChoicePropertyComponent (const String& name)

ChoicePropertyComponent::ChoicePropertyComponent (const Value& valueToControl,
const String& name,
const StringArray& choices_)
const StringArray& choices_,
const Array <int>* choiceIDs)
: PropertyComponent (name),
choices (choices_),
comboBox (0)
{
createComboBox();
createComboBox (choiceIDs);

comboBox->getSelectedIdAsValue().referTo (valueToControl);
}
@@ -70903,14 +70918,18 @@ ChoicePropertyComponent::~ChoicePropertyComponent()
deleteAllChildren();
}

void ChoicePropertyComponent::createComboBox()
void ChoicePropertyComponent::createComboBox (const Array <int>* choiceIDs)
{
// The array of IDs must contain the same number of values as the choices list!
jassert (choiceIDs == 0 || choiceIDs->size() == choices.size());

addAndMakeVisible (comboBox = new ComboBox (String::empty));

for (int i = 0; i < choices.size(); ++i)
{
if (choices[i].isNotEmpty())
comboBox->addItem (choices[i], i + 1);
comboBox->addItem (choices[i], choiceIDs == 0 ? (i + 1)
: ((*choiceIDs)[i]));
else
comboBox->addSeparator();
}
@@ -70937,7 +70956,7 @@ void ChoicePropertyComponent::refresh()
{
if (comboBox == 0)
{
createComboBox();
createComboBox (0);
comboBox->addListener (this);
}

@@ -212362,6 +212381,17 @@ int SystemStats::getPageSize() throw()
extern HWND juce_messageWindowHandle;
#endif

#if ! JUCE_USE_INTRINSICS
// In newer compilers, the inline versions of these are used (in juce_Atomic.h), but in
// older ones we have to actually call the ops as win32 functions..
void Atomic::increment (int32& variable) { InterlockedIncrement (reinterpret_cast <volatile long*> (&variable)); }
int32 Atomic::incrementAndReturn (int32& variable) { return InterlockedIncrement (reinterpret_cast <volatile long*> (&variable)); }
void Atomic::decrement (int32& variable) { InterlockedDecrement (reinterpret_cast <volatile long*> (&variable)); }
int32 Atomic::decrementAndReturn (int32& variable) { return InterlockedDecrement (reinterpret_cast <volatile long*> (&variable)); }
int32 Atomic::compareAndExchange (int32& destination, int32 newValue, int32 oldValue)
{ return InterlockedCompareExchange (reinterpret_cast <volatile long*> (&destination), newValue, oldValue); }
#endif

CriticalSection::CriticalSection() throw()
{
// (just to check the MS haven't changed this structure and broken things...)
@@ -219239,10 +219269,18 @@ public:
if (browser != 0)
{
LPSAFEARRAY sa = 0;
_variant_t flags, frame, postDataVar, headersVar;

VARIANT flags, frame, postDataVar, headersVar; // (_variant_t isn't available in all compilers)
VariantInit (&flags);
VariantInit (&frame);
VariantInit (&postDataVar);
VariantInit (&headersVar);

if (headers != 0)
headersVar = (const tchar*) headers->joinIntoString ("\r\n");
{
V_VT (&headersVar) = VT_BSTR;
V_BSTR (&headersVar) = SysAllocString ((const tchar*) headers->joinIntoString ("\r\n"));
}

if (postData != 0 && postData->getSize() > 0)
{
@@ -219275,6 +219313,11 @@ public:

if (sa != 0)
SafeArrayDestroy (sa);

VariantClear (&flags);
VariantClear (&frame);
VariantClear (&postDataVar);
VariantClear (&headersVar);
}
}

@@ -227197,8 +227240,26 @@ public:
}
}

void setMMThreadPriority()
{
DynamicLibraryLoader dll ("avrt.dll");
DynamicLibraryImport (AvSetMmThreadCharacteristics, avSetMmThreadCharacteristics, HANDLE, dll, (LPCTSTR, LPDWORD))
DynamicLibraryImport (AvSetMmThreadPriority, avSetMmThreadPriority, HANDLE, dll, (HANDLE, AVRT_PRIORITY))

if (avSetMmThreadCharacteristics != 0 && avSetMmThreadPriority != 0)
{
DWORD dummy = 0;
HANDLE h = avSetMmThreadCharacteristics (_T("Pro Audio"), &dummy);

if (h != 0)
avSetMmThreadPriority (h, AVRT_PRIORITY_NORMAL);
}
}

void run()
{
setMMThreadPriority();

const int bufferSize = currentBufferSizeSamples;

HANDLE events[2];


+ 35
- 156
juce_amalgamated.h View File

@@ -1445,10 +1445,6 @@ BEGIN_JUCE_NAMESPACE
#pragma warning (disable: 4786) // (old vc6 warning about long class names)
#endif

#if JUCE_MAC || JUCE_IPHONE
#pragma align=natural
#endif

// this is where all the class header files get brought in..

/********* Start of inlined file: juce_core_includes.h *********/
@@ -2701,6 +2697,8 @@ public:

bool operator!= (const MemoryBlock& other) const throw();

bool matches (const void* data, size_t dataSize) const throw();

template <class DataType>
operator DataType*() const throw() { return (DataType*) data; }

@@ -4364,165 +4362,49 @@ private:
class JUCE_API Atomic
{
public:
static void increment (int& variable);
static void increment (int32& variable);

static int incrementAndReturn (int& variable);
static int32 incrementAndReturn (int32& variable);

static void decrement (int& variable);
static void decrement (int32& variable);

static int decrementAndReturn (int& variable);
static int32 decrementAndReturn (int32& variable);

static int32 compareAndExchange (int32& destination, int32 newValue, int32 requiredCurrentValue);
};

#if (JUCE_MAC || JUCE_IPHONE) // Mac and iPhone...

#include <libkern/OSAtomic.h>
inline void Atomic::increment (int& variable) { OSAtomicIncrement32 ((int32_t*) &variable); }
inline int Atomic::incrementAndReturn (int& variable) { return OSAtomicIncrement32 ((int32_t*) &variable); }
inline void Atomic::decrement (int& variable) { OSAtomicDecrement32 ((int32_t*) &variable); }
inline int Atomic::decrementAndReturn (int& variable) { return OSAtomicDecrement32 ((int32_t*) &variable); }

#elif JUCE_GCC

#if JUCE_USE_GCC_ATOMIC_INTRINSICS // Linux with intrinsics...
inline void Atomic::increment (int32& variable) { OSAtomicIncrement32 ((volatile int32_t*) &variable); }
inline int32 Atomic::incrementAndReturn (int32& variable) { return OSAtomicIncrement32 ((volatile int32_t*) &variable); }
inline void Atomic::decrement (int32& variable) { OSAtomicDecrement32 ((volatile int32_t*) &variable); }
inline int32 Atomic::decrementAndReturn (int32& variable) { return OSAtomicDecrement32 ((volatile int32_t*) &variable); }
inline int32 Atomic::compareAndExchange (int32& destination, int32 newValue, int32 oldValue)
{ return OSAtomicCompareAndSwap32Barrier (oldValue, newValue, (volatile int32_t*) &destination); }

inline void Atomic::increment (int& variable) { __sync_add_and_fetch (&variable, 1); }
inline int Atomic::incrementAndReturn (int& variable) { return __sync_add_and_fetch (&variable, 1); }
inline void Atomic::decrement (int& variable) { __sync_add_and_fetch (&variable, -1); }
inline int Atomic::decrementAndReturn (int& variable) { return __sync_add_and_fetch (&variable, -1); }
#elif JUCE_GCC // Linux...

#else // Linux without intrinsics...
inline void Atomic::increment (int32& variable) { __sync_add_and_fetch (&variable, 1); }
inline int32 Atomic::incrementAndReturn (int32& variable) { return __sync_add_and_fetch (&variable, 1); }
inline void Atomic::decrement (int32& variable) { __sync_add_and_fetch (&variable, -1); }
inline int32 Atomic::decrementAndReturn (int32& variable) { return __sync_add_and_fetch (&variable, -1); }
inline int32 Atomic::compareAndExchange (int32& destination, int32 newValue, int32 oldValue)
{ return __sync_val_compare_and_swap (&destination, oldValue, newValue); }

inline void Atomic::increment (int& variable)
{
__asm__ __volatile__ (
#if JUCE_64BIT
"lock incl (%%rax)"
:
: "a" (&variable)
: "cc", "memory");
#else
"lock incl %0"
: "=m" (variable)
: "m" (variable));
#endif
}

inline int Atomic::incrementAndReturn (int& variable)
{
int result;

__asm__ __volatile__ (
#if JUCE_64BIT
"lock xaddl %%ebx, (%%rax) \n\
incl %%ebx"
: "=b" (result)
: "a" (&variable), "b" (1)
: "cc", "memory");
#else
"lock xaddl %%eax, (%%ecx) \n\
incl %%eax"
: "=a" (result)
: "c" (&variable), "a" (1)
: "memory");
#endif

return result;
}

inline void Atomic::decrement (int& variable)
{
__asm__ __volatile__ (
#if JUCE_64BIT
"lock decl (%%rax)"
:
: "a" (&variable)
: "cc", "memory");
#else
"lock decl %0"
: "=m" (variable)
: "m" (variable));
#endif
}

inline int Atomic::decrementAndReturn (int& variable)
{
int result;

__asm__ __volatile__ (
#if JUCE_64BIT
"lock xaddl %%ebx, (%%rax) \n\
decl %%ebx"
: "=b" (result)
: "a" (&variable), "b" (-1)
: "cc", "memory");
#else
"lock xaddl %%eax, (%%ecx) \n\
decl %%eax"
: "=a" (result)
: "c" (&variable), "a" (-1)
: "memory");
#endif
return result;
}
#endif

#elif JUCE_USE_INTRINSICS // Windows with intrinsics...
#elif JUCE_USE_INTRINSICS // Windows...

// (If JUCE_USE_INTRINSICS isn't enabled, a fallback version of these methods is
// declared in juce_win32_Threads.cpp)
#pragma intrinsic (_InterlockedIncrement)
#pragma intrinsic (_InterlockedDecrement)
#pragma intrinsic (_InterlockedCompareExchange)

inline void Atomic::increment (int& variable) { _InterlockedIncrement (reinterpret_cast <volatile long*> (&variable)); }
inline int Atomic::incrementAndReturn (int& variable) { return _InterlockedIncrement (reinterpret_cast <volatile long*> (&variable)); }
inline void Atomic::decrement (int& variable) { _InterlockedDecrement (reinterpret_cast <volatile long*> (&variable)); }
inline int Atomic::decrementAndReturn (int& variable) { return _InterlockedDecrement (reinterpret_cast <volatile long*> (&variable)); }

#else // Windows without intrinsics...

inline void Atomic::increment (int& variable)
{
__asm {
mov ecx, dword ptr [variable]
lock inc dword ptr [ecx]
}
}

inline int Atomic::incrementAndReturn (int& variable)
{
int result;

__asm {
mov ecx, dword ptr [variable]
mov eax, 1
lock xadd dword ptr [ecx], eax
inc eax
mov result, eax
}

return result;
}

inline void Atomic::decrement (int& variable)
{
__asm {
mov ecx, dword ptr [variable]
lock dec dword ptr [ecx]
}
}

inline int Atomic::decrementAndReturn (int& variable)
{
int result;

__asm {
mov ecx, dword ptr [variable]
mov eax, -1
lock xadd dword ptr [ecx], eax
dec eax
mov result, eax
}

return result;
}
inline void Atomic::increment (int32& variable) { _InterlockedIncrement (reinterpret_cast <volatile long*> (&variable)); }
inline int32 Atomic::incrementAndReturn (int32& variable) { return _InterlockedIncrement (reinterpret_cast <volatile long*> (&variable)); }
inline void Atomic::decrement (int32& variable) { _InterlockedDecrement (reinterpret_cast <volatile long*> (&variable)); }
inline int32 Atomic::decrementAndReturn (int32& variable) { return _InterlockedDecrement (reinterpret_cast <volatile long*> (&variable)); }
inline int32 Atomic::compareAndExchange (int32& destination, int32 newValue, int32 oldValue)
{ return _InterlockedCompareExchange (reinterpret_cast <volatile long*> (&destination), newValue, oldValue); }

#endif

@@ -5118,7 +5000,7 @@ public:
// avoids getting warning messages about the parameter being unused

lock.enter();
sortArray (comparator, (ObjectClass*) data.elements, 0, size() - 1, retainOrderOfEquivalentItems);
sortArray (comparator, (ObjectClass**) data.elements, 0, size() - 1, retainOrderOfEquivalentItems);
lock.exit();
}

@@ -25219,7 +25101,8 @@ protected:
public:
ChoicePropertyComponent (const Value& valueToControl,
const String& propertyName,
const StringArray& choices);
const StringArray& choices,
const Array <int>* choiceIDs = 0);

~ChoicePropertyComponent();

@@ -25240,7 +25123,7 @@ protected:
private:
ComboBox* comboBox;

void createComboBox();
void createComboBox (const Array <int>* choiceIDs);

ChoicePropertyComponent (const ChoicePropertyComponent&);
const ChoicePropertyComponent& operator= (const ChoicePropertyComponent&);
@@ -27620,10 +27503,6 @@ public:
#pragma pack (pop)
#endif

#if JUCE_MAC || JUCE_IPHONE
#pragma align=reset
#endif

END_JUCE_NAMESPACE

#ifndef DONT_SET_USING_JUCE_NAMESPACE


+ 2
- 2
src/audio/plugins/formats/juce_AudioUnitPluginFormat.mm View File

@@ -351,8 +351,8 @@ private:
{
for (int i = 0; i < supportedChannelsSize / sizeof (AUChannelInfo); ++i)
{
numIns = jmax (numIns, supportedChannels[i].inChannels);
numOuts = jmax (numOuts, supportedChannels[i].outChannels);
numIns = jmax (numIns, (int) supportedChannels[i].inChannels);
numOuts = jmax (numOuts, (int) supportedChannels[i].outChannels);
}
}
else


+ 7
- 2
src/containers/juce_MemoryBlock.cpp View File

@@ -98,8 +98,7 @@ MemoryBlock& MemoryBlock::operator= (const MemoryBlock& other) throw()
//==============================================================================
bool MemoryBlock::operator== (const MemoryBlock& other) const throw()
{
return (size == other.size)
&& (memcmp (data, other.data, size) == 0);
return matches (other.data, other.size);
}
bool MemoryBlock::operator!= (const MemoryBlock& other) const throw()
@@ -107,6 +106,12 @@ bool MemoryBlock::operator!= (const MemoryBlock& other) const throw()
return ! operator== (other);
}
bool MemoryBlock::matches (const void* dataToCompare, size_t dataSize) const throw()
{
return size == dataSize
&& memcmp (data, dataToCompare, size) == 0;
}
//==============================================================================
// this will resize the block to this size
void MemoryBlock::setSize (const size_t newSize,


+ 4
- 0
src/containers/juce_MemoryBlock.h View File

@@ -83,6 +83,10 @@ public:
*/
bool operator!= (const MemoryBlock& other) const throw();
/** Returns true if the data in this MemoryBlock matches the raw bytes passed-in.
*/
bool matches (const void* data, size_t dataSize) const throw();
//==============================================================================
/** Returns a pointer to the data, casting it to any type of primitive data required.


+ 34
- 148
src/core/juce_Atomic.h View File

@@ -34,174 +34,60 @@ class JUCE_API Atomic
{
public:
/** Increments an integer in a thread-safe way. */
static void increment (int& variable);
static void increment (int32& variable);
/** Increments an integer in a thread-safe way and returns its new value. */
static int incrementAndReturn (int& variable);
static int32 incrementAndReturn (int32& variable);
/** Decrements an integer in a thread-safe way. */
static void decrement (int& variable);
static void decrement (int32& variable);
/** Decrements an integer in a thread-safe way and returns its new value. */
static int decrementAndReturn (int& variable);
static int32 decrementAndReturn (int32& variable);
/** If the current value of destination is equal to requiredCurrentValue, this
will set it to newValue; otherwise, it will leave it unchanged.
@returns the new value of destination
*/
static int32 compareAndExchange (int32& destination, int32 newValue, int32 requiredCurrentValue);
};
//==============================================================================
#if (JUCE_MAC || JUCE_IPHONE) // Mac and iPhone...
#include <libkern/OSAtomic.h>
inline void Atomic::increment (int& variable) { OSAtomicIncrement32 ((int32_t*) &variable); }
inline int Atomic::incrementAndReturn (int& variable) { return OSAtomicIncrement32 ((int32_t*) &variable); }
inline void Atomic::decrement (int& variable) { OSAtomicDecrement32 ((int32_t*) &variable); }
inline int Atomic::decrementAndReturn (int& variable) { return OSAtomicDecrement32 ((int32_t*) &variable); }
#elif JUCE_GCC
//==============================================================================
#if JUCE_USE_GCC_ATOMIC_INTRINSICS // Linux with intrinsics...
inline void Atomic::increment (int32& variable) { OSAtomicIncrement32 ((volatile int32_t*) &variable); }
inline int32 Atomic::incrementAndReturn (int32& variable) { return OSAtomicIncrement32 ((volatile int32_t*) &variable); }
inline void Atomic::decrement (int32& variable) { OSAtomicDecrement32 ((volatile int32_t*) &variable); }
inline int32 Atomic::decrementAndReturn (int32& variable) { return OSAtomicDecrement32 ((volatile int32_t*) &variable); }
inline int32 Atomic::compareAndExchange (int32& destination, int32 newValue, int32 oldValue)
{ return OSAtomicCompareAndSwap32Barrier (oldValue, newValue, (volatile int32_t*) &destination); }
inline void Atomic::increment (int& variable) { __sync_add_and_fetch (&variable, 1); }
inline int Atomic::incrementAndReturn (int& variable) { return __sync_add_and_fetch (&variable, 1); }
inline void Atomic::decrement (int& variable) { __sync_add_and_fetch (&variable, -1); }
inline int Atomic::decrementAndReturn (int& variable) { return __sync_add_and_fetch (&variable, -1); }
#elif JUCE_GCC // Linux...
//==============================================================================
#else // Linux without intrinsics...
inline void Atomic::increment (int& variable)
{
__asm__ __volatile__ (
#if JUCE_64BIT
"lock incl (%%rax)"
:
: "a" (&variable)
: "cc", "memory");
#else
"lock incl %0"
: "=m" (variable)
: "m" (variable));
#endif
}
inline int Atomic::incrementAndReturn (int& variable)
{
int result;
__asm__ __volatile__ (
#if JUCE_64BIT
"lock xaddl %%ebx, (%%rax) \n\
incl %%ebx"
: "=b" (result)
: "a" (&variable), "b" (1)
: "cc", "memory");
#else
"lock xaddl %%eax, (%%ecx) \n\
incl %%eax"
: "=a" (result)
: "c" (&variable), "a" (1)
: "memory");
#endif
return result;
}
inline void Atomic::decrement (int& variable)
{
__asm__ __volatile__ (
#if JUCE_64BIT
"lock decl (%%rax)"
:
: "a" (&variable)
: "cc", "memory");
#else
"lock decl %0"
: "=m" (variable)
: "m" (variable));
#endif
}
inline int Atomic::decrementAndReturn (int& variable)
{
int result;
__asm__ __volatile__ (
#if JUCE_64BIT
"lock xaddl %%ebx, (%%rax) \n\
decl %%ebx"
: "=b" (result)
: "a" (&variable), "b" (-1)
: "cc", "memory");
#else
"lock xaddl %%eax, (%%ecx) \n\
decl %%eax"
: "=a" (result)
: "c" (&variable), "a" (-1)
: "memory");
#endif
return result;
}
#endif
inline void Atomic::increment (int32& variable) { __sync_add_and_fetch (&variable, 1); }
inline int32 Atomic::incrementAndReturn (int32& variable) { return __sync_add_and_fetch (&variable, 1); }
inline void Atomic::decrement (int32& variable) { __sync_add_and_fetch (&variable, -1); }
inline int32 Atomic::decrementAndReturn (int32& variable) { return __sync_add_and_fetch (&variable, -1); }
inline int32 Atomic::compareAndExchange (int32& destination, int32 newValue, int32 oldValue)
{ return __sync_val_compare_and_swap (&destination, oldValue, newValue); }
//==============================================================================
#elif JUCE_USE_INTRINSICS // Windows with intrinsics...
#elif JUCE_USE_INTRINSICS // Windows...
// (If JUCE_USE_INTRINSICS isn't enabled, a fallback version of these methods is
// declared in juce_win32_Threads.cpp)
#pragma intrinsic (_InterlockedIncrement)
#pragma intrinsic (_InterlockedDecrement)
inline void Atomic::increment (int& variable) { _InterlockedIncrement (reinterpret_cast <volatile long*> (&variable)); }
inline int Atomic::incrementAndReturn (int& variable) { return _InterlockedIncrement (reinterpret_cast <volatile long*> (&variable)); }
inline void Atomic::decrement (int& variable) { _InterlockedDecrement (reinterpret_cast <volatile long*> (&variable)); }
inline int Atomic::decrementAndReturn (int& variable) { return _InterlockedDecrement (reinterpret_cast <volatile long*> (&variable)); }
//==============================================================================
#else // Windows without intrinsics...
inline void Atomic::increment (int& variable)
{
__asm {
mov ecx, dword ptr [variable]
lock inc dword ptr [ecx]
}
}
inline int Atomic::incrementAndReturn (int& variable)
{
int result;
__asm {
mov ecx, dword ptr [variable]
mov eax, 1
lock xadd dword ptr [ecx], eax
inc eax
mov result, eax
}
return result;
}
inline void Atomic::decrement (int& variable)
{
__asm {
mov ecx, dword ptr [variable]
lock dec dword ptr [ecx]
}
}
inline int Atomic::decrementAndReturn (int& variable)
{
int result;
__asm {
mov ecx, dword ptr [variable]
mov eax, -1
lock xadd dword ptr [ecx], eax
dec eax
mov result, eax
}
return result;
}
#pragma intrinsic (_InterlockedCompareExchange)
inline void Atomic::increment (int32& variable) { _InterlockedIncrement (reinterpret_cast <volatile long*> (&variable)); }
inline int32 Atomic::incrementAndReturn (int32& variable) { return _InterlockedIncrement (reinterpret_cast <volatile long*> (&variable)); }
inline void Atomic::decrement (int32& variable) { _InterlockedDecrement (reinterpret_cast <volatile long*> (&variable)); }
inline int32 Atomic::decrementAndReturn (int32& variable) { return _InterlockedDecrement (reinterpret_cast <volatile long*> (&variable)); }
inline int32 Atomic::compareAndExchange (int32& destination, int32 newValue, int32 oldValue)
{ return _InterlockedCompareExchange (reinterpret_cast <volatile long*> (&destination), newValue, oldValue); }
#endif


+ 0
- 6
src/core/juce_SystemStats.cpp View File

@@ -79,12 +79,6 @@ void JUCE_PUBLIC_FUNCTION initialiseJuce_NonGUI()
jassert (ByteOrder::swap ((uint16) 0x1122) == 0x2211);
jassert (ByteOrder::swap ((uint32) 0x11223344) == 0x44332211);
// quick test to make sure the run-time lib doesn't crash on freeing a null-pointer.
SystemStats* nullPointer = 0;
juce_free (nullPointer);
delete[] nullPointer;
delete nullPointer;
// Some quick stream tests..
int randomInt = Random::getSystemRandom().nextInt();
int64 randomInt64 = Random::getSystemRandom().nextInt64();


+ 166
- 128
src/events/juce_Timer.cpp View File

@@ -44,118 +44,6 @@ class InternalTimerThread : private Thread,
private DeletedAtShutdown,
private AsyncUpdater
{
private:
friend class Timer;
static InternalTimerThread* instance;
static CriticalSection lock;
Timer* volatile firstTimer;
bool volatile callbackNeeded;
InternalTimerThread (const InternalTimerThread&);
const InternalTimerThread& operator= (const InternalTimerThread&);
void addTimer (Timer* const t) throw()
{
#ifdef JUCE_DEBUG
Timer* tt = firstTimer;
while (tt != 0)
{
// trying to add a timer that's already here - shouldn't get to this point,
// so if you get this assertion, let me know!
jassert (tt != t);
tt = tt->next;
}
jassert (t->previous == 0 && t->next == 0);
#endif
Timer* i = firstTimer;
if (i == 0 || i->countdownMs > t->countdownMs)
{
t->next = firstTimer;
firstTimer = t;
}
else
{
while (i->next != 0 && i->next->countdownMs <= t->countdownMs)
i = i->next;
jassert (i != 0);
t->next = i->next;
t->previous = i;
i->next = t;
}
if (t->next != 0)
t->next->previous = t;
jassert ((t->next == 0 || t->next->countdownMs >= t->countdownMs)
&& (t->previous == 0 || t->previous->countdownMs <= t->countdownMs));
notify();
}
void removeTimer (Timer* const t) throw()
{
#ifdef JUCE_DEBUG
Timer* tt = firstTimer;
bool found = false;
while (tt != 0)
{
if (tt == t)
{
found = true;
break;
}
tt = tt->next;
}
// trying to remove a timer that's not here - shouldn't get to this point,
// so if you get this assertion, let me know!
jassert (found);
#endif
if (t->previous != 0)
{
jassert (firstTimer != t);
t->previous->next = t->next;
}
else
{
jassert (firstTimer == t);
firstTimer = t->next;
}
if (t->next != 0)
t->next->previous = t->previous;
t->next = 0;
t->previous = 0;
}
void decrementAllCounters (const int numMillisecs) const
{
Timer* t = firstTimer;
while (t != 0)
{
t->countdownMs -= numMillisecs;
t = t->next;
}
}
void handleAsyncUpdate()
{
startThread (7);
}
public:
InternalTimerThread()
: Thread ("Juce Timer"),
@@ -180,7 +68,7 @@ public:
while (! threadShouldExit())
{
uint32 now = Time::getMillisecondCounter();
const uint32 now = Time::getMillisecondCounter();
if (now <= lastTime)
{
@@ -199,24 +87,31 @@ public:
if (timeUntilFirstTimer <= 0)
{
callbackNeeded = true;
postMessage (new Message());
// sometimes, our message could get discarded by the OS (particularly when running as an RTAS when the app has a modal loop),
// so this is how long to wait before assuming the message has been lost and trying again.
const uint32 messageDeliveryTimeout = now + 2000;
while (callbackNeeded)
/* If we managed to set the atomic boolean to true then send a message, this is needed
as a memory barrier so the message won't be sent before callbackNeeded is set to true,
but if it fails it means the message-thread changed the value from under us so at least
some processing is happenening and we can just loop around and try again
*/
if (callbackNeeded.set (true))
{
wait (4);
postMessage (new Message());
if (threadShouldExit())
return;
/* Sometimes our message can get discarded by the OS (e.g. when running as an RTAS
when the app has a modal loop), so this is how long to wait before assuming the
message has been lost and trying again.
*/
const uint32 messageDeliveryTimeout = now + 2000;
now = Time::getMillisecondCounter();
while (callbackNeeded.get())
{
wait (4);
if (now > messageDeliveryTimeout)
break;
if (threadShouldExit())
return;
if (Time::getMillisecondCounter() > messageDeliveryTimeout)
break;
}
}
}
else
@@ -249,7 +144,13 @@ public:
JUCE_CATCH_EXCEPTION
}
callbackNeeded = false;
/* This is needed as a memory barrier to make sure all processing of current timers is done
before the boolean is set. This set should never fail since if it was false in the first place,
we wouldn't get a message (so it can't be changed from false to true from under us), and if we
get a message then the value is true and the other thread can only set it to true again and
we will get another callback to set it to false.
*/
callbackNeeded.set (false);
}
static void callAnyTimersSynchronously()
@@ -296,6 +197,143 @@ public:
}
}
}
private:
friend class Timer;
static InternalTimerThread* instance;
static CriticalSection lock;
Timer* volatile firstTimer;
//==============================================================================
class AtomicBool
{
public:
AtomicBool (const bool value) throw() : value (static_cast<int32> (value)) {}
~AtomicBool() throw() {}
bool get() const throw() { return value != 0; }
bool set (const bool newValue) { return Atomic::compareAndExchange (value, newValue ? 1 : 0, value) != 0; }
/*bool setIfNotAlreadyThisValue (const bool newValue)
{
int32 valueNew = newValue ? 1 : 0;
int32 valueCurrent = 1 - valueNew;
return Atomic::compareAndExchange (value, valueNew, valueCurrent);
}*/
private:
int32 value;
AtomicBool (const AtomicBool&);
AtomicBool& operator= (const AtomicBool&);
};
AtomicBool callbackNeeded;
//==============================================================================
void addTimer (Timer* const t) throw()
{
#ifdef JUCE_DEBUG
Timer* tt = firstTimer;
while (tt != 0)
{
// trying to add a timer that's already here - shouldn't get to this point,
// so if you get this assertion, let me know!
jassert (tt != t);
tt = tt->next;
}
jassert (t->previous == 0 && t->next == 0);
#endif
Timer* i = firstTimer;
if (i == 0 || i->countdownMs > t->countdownMs)
{
t->next = firstTimer;
firstTimer = t;
}
else
{
while (i->next != 0 && i->next->countdownMs <= t->countdownMs)
i = i->next;
jassert (i != 0);
t->next = i->next;
t->previous = i;
i->next = t;
}
if (t->next != 0)
t->next->previous = t;
jassert ((t->next == 0 || t->next->countdownMs >= t->countdownMs)
&& (t->previous == 0 || t->previous->countdownMs <= t->countdownMs));
notify();
}
void removeTimer (Timer* const t) throw()
{
#ifdef JUCE_DEBUG
Timer* tt = firstTimer;
bool found = false;
while (tt != 0)
{
if (tt == t)
{
found = true;
break;
}
tt = tt->next;
}
// trying to remove a timer that's not here - shouldn't get to this point,
// so if you get this assertion, let me know!
jassert (found);
#endif
if (t->previous != 0)
{
jassert (firstTimer != t);
t->previous->next = t->next;
}
else
{
jassert (firstTimer == t);
firstTimer = t->next;
}
if (t->next != 0)
t->next->previous = t->previous;
t->next = 0;
t->previous = 0;
}
void decrementAllCounters (const int numMillisecs) const
{
Timer* t = firstTimer;
while (t != 0)
{
t->countdownMs -= numMillisecs;
t = t->next;
}
}
void handleAsyncUpdate()
{
startThread (7);
}
InternalTimerThread (const InternalTimerThread&);
InternalTimerThread& operator= (const InternalTimerThread&);
};
InternalTimerThread* InternalTimerThread::instance = 0;


+ 1
- 1
src/gui/components/filebrowser/juce_FileBrowserComponent.h View File

@@ -127,7 +127,7 @@ public:
This may be different from getCurrentFile(), which returns the value
that is shown in the filename box, and if there are multiple selections,
this will only return one of them.
@see getCurrentFile
@see getSelectedFile
*/
const File getHighlightedFile() const throw();


+ 10
- 5
src/gui/components/properties/juce_ChoicePropertyComponent.cpp View File

@@ -39,12 +39,13 @@ ChoicePropertyComponent::ChoicePropertyComponent (const String& name)
ChoicePropertyComponent::ChoicePropertyComponent (const Value& valueToControl,
const String& name,
const StringArray& choices_)
const StringArray& choices_,
const Array <int>* choiceIDs)
: PropertyComponent (name),
choices (choices_),
comboBox (0)
{
createComboBox();
createComboBox (choiceIDs);
comboBox->getSelectedIdAsValue().referTo (valueToControl);
}
@@ -55,14 +56,18 @@ ChoicePropertyComponent::~ChoicePropertyComponent()
}
//==============================================================================
void ChoicePropertyComponent::createComboBox()
void ChoicePropertyComponent::createComboBox (const Array <int>* choiceIDs)
{
// The array of IDs must contain the same number of values as the choices list!
jassert (choiceIDs == 0 || choiceIDs->size() == choices.size());
addAndMakeVisible (comboBox = new ComboBox (String::empty));
for (int i = 0; i < choices.size(); ++i)
{
if (choices[i].isNotEmpty())
comboBox->addItem (choices[i], i + 1);
comboBox->addItem (choices[i], choiceIDs == 0 ? (i + 1)
: ((*choiceIDs)[i]));
else
comboBox->addSeparator();
}
@@ -90,7 +95,7 @@ void ChoicePropertyComponent::refresh()
{
if (comboBox == 0)
{
createComboBox();
createComboBox (0);
comboBox->addListener (this);
}


+ 12
- 4
src/gui/components/properties/juce_ChoicePropertyComponent.h View File

@@ -63,12 +63,20 @@ protected:
public:
/** Creates the component.
Your subclass's constructor must add a list of options to the choices
member variable.
@param valueToControl the value that the combo box will read and control
@param propertyName the name of the property
@param choices the list of possible values that the user can choose between
@param choiceIDs if this is 0, then the value corresponding to each item in the
'choices' StringArray is simply its index + 1. But if the
choiceIDs parameter is specified, it lets you provide a set
of IDs for each item in the choices list. If you use this
parameter, it must contain the same number of elements as
the choices list.
*/
ChoicePropertyComponent (const Value& valueToControl,
const String& propertyName,
const StringArray& choices);
const StringArray& choices,
const Array <int>* choiceIDs = 0);
/** Destructor. */
~ChoicePropertyComponent();
@@ -113,7 +121,7 @@ protected:
private:
ComboBox* comboBox;
void createComboBox();
void createComboBox (const Array <int>* choiceIDs);
ChoicePropertyComponent (const ChoicePropertyComponent&);
const ChoicePropertyComponent& operator= (const ChoicePropertyComponent&);


+ 1
- 0
src/native/windows/juce_win32_NativeIncludes.h View File

@@ -142,6 +142,7 @@
#include <MMReg.h>
#include <mmdeviceapi.h>
#include <Audioclient.h>
#include <Avrt.h>
#include <functiondiscoverykeys.h>
#endif


+ 11
- 0
src/native/windows/juce_win32_Threads.cpp View File

@@ -31,6 +31,17 @@
extern HWND juce_messageWindowHandle;
#endif
//==============================================================================
#if ! JUCE_USE_INTRINSICS
// In newer compilers, the inline versions of these are used (in juce_Atomic.h), but in
// older ones we have to actually call the ops as win32 functions..
void Atomic::increment (int32& variable) { InterlockedIncrement (reinterpret_cast <volatile long*> (&variable)); }
int32 Atomic::incrementAndReturn (int32& variable) { return InterlockedIncrement (reinterpret_cast <volatile long*> (&variable)); }
void Atomic::decrement (int32& variable) { InterlockedDecrement (reinterpret_cast <volatile long*> (&variable)); }
int32 Atomic::decrementAndReturn (int32& variable) { return InterlockedDecrement (reinterpret_cast <volatile long*> (&variable)); }
int32 Atomic::compareAndExchange (int32& destination, int32 newValue, int32 oldValue)
{ return InterlockedCompareExchange (reinterpret_cast <volatile long*> (&destination), newValue, oldValue); }
#endif
//==============================================================================
CriticalSection::CriticalSection() throw()


+ 18
- 0
src/native/windows/juce_win32_WASAPI.cpp View File

@@ -780,8 +780,26 @@ public:
}
}
void setMMThreadPriority()
{
DynamicLibraryLoader dll ("avrt.dll");
DynamicLibraryImport (AvSetMmThreadCharacteristics, avSetMmThreadCharacteristics, HANDLE, dll, (LPCTSTR, LPDWORD))
DynamicLibraryImport (AvSetMmThreadPriority, avSetMmThreadPriority, HANDLE, dll, (HANDLE, AVRT_PRIORITY))
if (avSetMmThreadCharacteristics != 0 && avSetMmThreadPriority != 0)
{
DWORD dummy = 0;
HANDLE h = avSetMmThreadCharacteristics (_T("Pro Audio"), &dummy);
if (h != 0)
avSetMmThreadPriority (h, AVRT_PRIORITY_NORMAL);
}
}
void run()
{
setMMThreadPriority();
const int bufferSize = currentBufferSizeSamples;
HANDLE events[2];


+ 15
- 2
src/native/windows/juce_win32_WebBrowserComponent.cpp View File

@@ -79,10 +79,18 @@ public:
if (browser != 0)
{
LPSAFEARRAY sa = 0;
_variant_t flags, frame, postDataVar, headersVar;
VARIANT flags, frame, postDataVar, headersVar; // (_variant_t isn't available in all compilers)
VariantInit (&flags);
VariantInit (&frame);
VariantInit (&postDataVar);
VariantInit (&headersVar);
if (headers != 0)
headersVar = (const tchar*) headers->joinIntoString ("\r\n");
{
V_VT (&headersVar) = VT_BSTR;
V_BSTR (&headersVar) = SysAllocString ((const tchar*) headers->joinIntoString ("\r\n"));
}
if (postData != 0 && postData->getSize() > 0)
{
@@ -115,6 +123,11 @@ public:
if (sa != 0)
SafeArrayDestroy (sa);
VariantClear (&flags);
VariantClear (&frame);
VariantClear (&postDataVar);
VariantClear (&headersVar);
}
}


Loading…
Cancel
Save