aboutsummaryrefslogtreecommitdiff
path: root/Src/nu
diff options
context:
space:
mode:
Diffstat (limited to 'Src/nu')
-rw-r--r--Src/nu/Alias.h54
-rw-r--r--Src/nu/AudioOutput.h388
-rw-r--r--Src/nu/AutoChar.h189
-rw-r--r--Src/nu/AutoCharFn.h50
-rw-r--r--Src/nu/AutoHeader.h98
-rw-r--r--Src/nu/AutoLock.h379
-rw-r--r--Src/nu/AutoUrl.h141
-rw-r--r--Src/nu/AutoWide.h99
-rw-r--r--Src/nu/AutoWideFn.h191
-rw-r--r--Src/nu/CCVersion.cpp45
-rw-r--r--Src/nu/CCVersion.h6
-rw-r--r--Src/nu/CGlobalAtom.h21
-rw-r--r--Src/nu/ChildSizer.cpp33
-rw-r--r--Src/nu/ChildSizer.h38
-rw-r--r--Src/nu/ComboBox.h105
-rw-r--r--Src/nu/Config.h188
-rw-r--r--Src/nu/ConfigCOM.cpp373
-rw-r--r--Src/nu/ConfigCOM.h51
-rw-r--r--Src/nu/DialogSkinner.cpp8
-rw-r--r--Src/nu/DialogSkinner.h78
-rw-r--r--Src/nu/GaplessRingBuffer.cpp64
-rw-r--r--Src/nu/GaplessRingBuffer.h20
-rw-r--r--Src/nu/GrowBuf.h93
-rw-r--r--Src/nu/HTMLContainer.cpp839
-rw-r--r--Src/nu/HTMLContainer.h177
-rw-r--r--Src/nu/HTMLContainer2.cpp2108
-rw-r--r--Src/nu/HTMLContainer2.h292
-rw-r--r--Src/nu/LockFreeItem.h47
-rw-r--r--Src/nu/LockFreeStack.h156
-rw-r--r--Src/nu/MainThread.cpp42
-rw-r--r--Src/nu/MainThread.h82
-rw-r--r--Src/nu/MakeThunk.h95
-rw-r--r--Src/nu/MediaLibraryInterface.cpp463
-rw-r--r--Src/nu/MediaLibraryInterface.h157
-rw-r--r--Src/nu/NonBlockLock.h343
-rw-r--r--Src/nu/Nullsoft Utility Library.txt0
-rw-r--r--Src/nu/Pairs.h15
-rw-r--r--Src/nu/ProgressTracker.h125
-rw-r--r--Src/nu/ReEntry.h68
-rw-r--r--Src/nu/RedBlackTree.cpp640
-rw-r--r--Src/nu/RedBlackTree.h77
-rw-r--r--Src/nu/RingBuffer.cpp461
-rw-r--r--Src/nu/RingBuffer.h77
-rw-r--r--Src/nu/SampleQueue.h81
-rw-r--r--Src/nu/SendTo.h106
-rw-r--r--Src/nu/ServiceBuilder.h40
-rw-r--r--Src/nu/ServiceWatcher.cpp183
-rw-r--r--Src/nu/ServiceWatcher.h60
-rw-r--r--Src/nu/Singleton.h86
-rw-r--r--Src/nu/Slider.h35
-rw-r--r--Src/nu/SpillBuffer.cpp105
-rw-r--r--Src/nu/SpillBuffer.h23
-rw-r--r--Src/nu/ThreadQueue.cpp66
-rw-r--r--Src/nu/ThreadQueue.h23
-rw-r--r--Src/nu/Vectors.h221
-rw-r--r--Src/nu/VideoClock.h63
-rw-r--r--Src/nu/bitbuffer.cpp71
-rw-r--r--Src/nu/bitbuffer.h26
-rw-r--r--Src/nu/cast64.h41
-rw-r--r--Src/nu/comboskin.cpp15
-rw-r--r--Src/nu/comboskin.h15
-rw-r--r--Src/nu/cstrlib.cpp59
-rw-r--r--Src/nu/cstrlib.h10
-rw-r--r--Src/nu/dispatchTable.h55
-rw-r--r--Src/nu/factoryt.h77
-rw-r--r--Src/nu/listview.cpp285
-rw-r--r--Src/nu/listview.h229
-rw-r--r--Src/nu/menuHelpers.cpp132
-rw-r--r--Src/nu/menuHelpers.h18
-rw-r--r--Src/nu/menushortcuts.cpp204
-rw-r--r--Src/nu/menushortcuts.h20
-rw-r--r--Src/nu/mtbrowser.cpp611
-rw-r--r--Src/nu/mtbrowser.h63
-rw-r--r--Src/nu/noarg.c28
-rw-r--r--Src/nu/nochkclr.c26
-rw-r--r--Src/nu/noenv.c27
-rw-r--r--Src/nu/nomsgs.c17
-rw-r--r--Src/nu/nonewthrow.c18
-rw-r--r--Src/nu/nosec.c12
-rw-r--r--Src/nu/ns_wc.h67
-rw-r--r--Src/nu/refcount.h21
-rw-r--r--Src/nu/regexp.cpp233
-rw-r--r--Src/nu/regexp.h14
-rw-r--r--Src/nu/simple_rwlock.h49
-rw-r--r--Src/nu/smalheap.c182
-rw-r--r--Src/nu/sort.cpp361
-rw-r--r--Src/nu/sort.h11
-rw-r--r--Src/nu/strsafe.c2
-rw-r--r--Src/nu/strsafe.h6647
-rw-r--r--Src/nu/strsafe.sln23
-rw-r--r--Src/nu/strsafe.vcproj150
-rw-r--r--Src/nu/threadname.h36
-rw-r--r--Src/nu/threadpool/ThreadFunctions.cpp79
-rw-r--r--Src/nu/threadpool/ThreadFunctions.h31
-rw-r--r--Src/nu/threadpool/ThreadID.cpp274
-rw-r--r--Src/nu/threadpool/ThreadID.h56
-rw-r--r--Src/nu/threadpool/ThreadPool.cpp365
-rw-r--r--Src/nu/threadpool/ThreadPool.h98
-rw-r--r--Src/nu/threadpool/TimerHandle.hpp54
-rw-r--r--Src/nu/threadpool/api_threadpool.h92
-rw-r--r--Src/nu/threadpool/threadpool.sln23
-rw-r--r--Src/nu/threadpool/threadpool.vcproj137
-rw-r--r--Src/nu/threadpool/threadpool_types.h8
-rw-r--r--Src/nu/trace.cpp86
-rw-r--r--Src/nu/trace.h56
-rw-r--r--Src/nu/wa_str.h61
-rw-r--r--Src/nu/windowsTheme.cpp266
-rw-r--r--Src/nu/windowsTheme.h65
-rw-r--r--Src/nu/winstr.c17
109 files changed, 21691 insertions, 0 deletions
diff --git a/Src/nu/Alias.h b/Src/nu/Alias.h
new file mode 100644
index 00000000..0c31818f
--- /dev/null
+++ b/Src/nu/Alias.h
@@ -0,0 +1,54 @@
+#ifndef NULLSOFT_UTILITY_ALIASH
+#define NULLSOFT_UTILITY_ALIASH
+
+template <class ptr_t>
+class Alias
+{
+public:
+ Alias()
+ :pointer(0)
+ {
+ }
+ Alias(ptr_t *_pointer) : pointer(_pointer)
+ {
+ }
+ Alias(const Alias<ptr_t> &copy)
+ {
+ pointer=copy.pointer;
+ }
+ ~Alias()
+ {
+ pointer=0;
+ }
+ ptr_t *operator = (ptr_t *copy)
+ {
+ pointer=copy;
+ return copy;
+ }
+
+ ptr_t *operator ->()
+ {
+ return pointer;
+ }
+
+ operator bool()
+ {
+ return !!pointer;
+ }
+
+ bool operator == (ptr_t *compare)
+ {
+ return pointer == compare;
+ }
+/*
+ operator ptr_t *()
+ {
+ return pointer;
+ }
+ */
+
+private:
+ ptr_t *pointer;
+};
+
+#endif \ No newline at end of file
diff --git a/Src/nu/AudioOutput.h b/Src/nu/AudioOutput.h
new file mode 100644
index 00000000..cf33f6cf
--- /dev/null
+++ b/Src/nu/AudioOutput.h
@@ -0,0 +1,388 @@
+#pragma once
+#include <bfc/platform/types.h>
+#include "../Winamp/in2.h"
+#include "../Winamp/out.h"
+#include "SpillBuffer.h"
+#include <assert.h>
+
+/* A class to manage Winamp input plugin audio output
+** It handles the following for you:
+** * Ensuring that Vis data is sent in chunks of 576
+** * Dealing with gapless audio
+** (you need to pass in the number of pre-delay and post-delay samples)
+** * dealing with the DSP plugin
+** * Waiting for CanWrite()
+** * dealing with inter-timestamps
+** e.g. you pass it >576 samples and it can give you a timestamp based on the divided chunk position
+
+to use, you need to derive from a class that declares
+int WaitOrAbort(int time_in_ms);
+return 0 on success, non-zero when you need to abort. the return value is passed back through Write()
+*/
+
+namespace nu // namespace it since "AudioOutput" isn't a unique enough name
+{
+ template <class wait_t>
+ class AudioOutput : public wait_t
+ {
+ public:
+ AudioOutput( In_Module *plugin ) : plugin( plugin )
+ {
+ Init( nullptr );
+ }
+
+ ~AudioOutput()
+ {
+ post_buffer.reset();
+ buffer576.reset();
+ }
+
+ /* Initializes and sets the output plugin pointer
+ ** for most input plugins, the nu::AudioOutput object will be a global,
+ ** so this will be necessary to call at the start of Play thread */
+ void Init( Out_Module *_output )
+ {
+ output = _output;
+ audio_opened = false;
+ first_timestamp = 0;
+ sample_size = 0;
+ output_latency = 0;
+
+ post_buffer.reset();
+ buffer576.reset();
+
+ cut_size = 0;
+ pre_cut_size = 0;
+ pre_cut = 0;
+ decoder_delay = 0;
+ channels = 0;
+ sample_rate = 0;
+ bps = 0;
+ }
+
+ /* sets end-of-stream delay (in samples)
+ ** WITHOUT componesating for post-delay.
+ ** some filetypes (e.g. iTunes MP4) store gapless info this way */
+ void SetPostDelay(int postSize)
+ {
+ if (postSize < 0)
+ {
+ postSize = 0;
+ }
+ else if (postSize)
+ {
+ if (sample_size)
+ post_buffer.reserve(postSize*sample_size);
+
+ cut_size = postSize;
+ }
+ }
+
+ /* set end-of-stream zero padding, in samples
+ ** compensates for decoder delay */
+ void SetZeroPadding(int postSize)
+ {
+ postSize -= decoder_delay;
+ if (postSize < 0)
+ {
+ postSize = 0;
+ }
+ SetPostDelay(postSize);
+ }
+
+ /* set decoder delay, initial zero samples and end-of-stream zero samples, all in one shot
+ ** adjusts zero samples for decoder delay. call SetDelays() if your zero samples are already compensated */
+ void SetGapless(int decoderDelaySize, int preSize, int postSize)
+ {
+ decoder_delay = decoderDelaySize;
+ SetZeroPadding(postSize);
+
+ pre_cut_size = preSize;
+ pre_cut = pre_cut_size + decoder_delay;
+ }
+
+ /* set decoder delay, initial delay and end-of-stream delay, all in one shot
+ ** WITHOUT componesating for post-delay.
+ ** some filetypes (e.g. iTunes MP4) store gapless info this way */
+ void SetDelays(int decoderDelaySize, int preSize, int postSize)
+ {
+ decoder_delay = decoderDelaySize;
+ SetPostDelay(postSize);
+
+ pre_cut_size = preSize;
+ pre_cut = pre_cut_size;
+ }
+
+ /* Call on seek */
+ void Flush(int time_in_ms)
+ {
+ if (audio_opened)
+ {
+ pre_cut = pre_cut_size;
+
+ output->Flush(time_in_ms);
+ first_timestamp = 0; // once we've flushed, we should be accurate so no need for this anymore
+ buffer576.clear();
+ post_buffer.clear();
+ }
+ else
+ first_timestamp = time_in_ms;
+ }
+
+ bool Opened() const
+ {
+ return audio_opened;
+ }
+
+ int GetLatency() const
+ {
+ return output_latency;
+ }
+
+ int GetFirstTimestamp() const
+ {
+ return first_timestamp;
+ }
+
+ /* timestamp is meant to be the first timestamp according to the containing file format
+ ** e.g. many MP4 videos start on 12ms or something, for accurate a/v syncing */
+ bool Open(int timestamp, int channels, int sample_rate, int bps, int buffer_len_ms=-1, int pre_buffer_ms=-1)
+ {
+ if (!audio_opened)
+ {
+ int latency = output->Open(sample_rate, channels, bps, buffer_len_ms, pre_buffer_ms);
+ if (latency < 0)
+ return false;
+ plugin->SAVSAInit(latency, sample_rate);
+ plugin->VSASetInfo(sample_rate, channels);
+ output->SetVolume(-666);
+ plugin->SetInfo(-1, sample_rate / 1000, channels, /* TODO? 0*/1);
+
+ output_latency = latency;
+ first_timestamp = timestamp;
+ sample_size = channels*bps / 8;
+ this->channels=channels;
+ this->sample_rate=sample_rate;
+ this->bps=bps;
+ SetPostDelay((int)cut_size); // set this again now that we know sample_size, so buffers get allocated correctly
+ buffer576.reserve(576*sample_size);
+ audio_opened=true;
+ }
+ return audio_opened;
+ }
+
+ void Close()
+ {
+ if (audio_opened && output)
+ {
+ output->Close();
+ plugin->SAVSADeInit();
+ }
+ output = 0;
+ first_timestamp = 0;
+ }
+
+ /* outSize is in bytes
+ ** */
+ int Write(char *out, size_t outSize)
+ {
+ if (!out && !outSize)
+ {
+ /* --- write contents of buffered audio (end-zero-padding buffer) */
+ if (!post_buffer.empty())
+ {
+ void *buffer = 0;
+ size_t len = 0;
+ if (post_buffer.get(&buffer, &len))
+ {
+ int ret = Write576((char *)buffer, len);
+ if (ret != 0)
+ return ret;
+ }
+ }
+
+ /* --- write any remaining data in 576 spill buffer (skip vis) */
+ if (!buffer576.empty())
+ {
+ void *buffer = 0;
+ size_t len = 0;
+ if (buffer576.get(&buffer, &len))
+ {
+ int ret = WriteOutput((char *)buffer, len);
+ if (ret != 0)
+ return ret;
+ }
+ }
+
+ output->Write(0, 0);
+ return 0;
+ }
+
+ // this probably should not happen but have seen it in some crash reports
+ if (!sample_size)
+ return 0;
+
+ assert((outSize % sample_size) == 0);
+ size_t outSamples = outSize / sample_size;
+
+ /* --- cut pre samples, if necessary --- */
+ size_t pre = min(pre_cut, outSamples);
+ out += pre * sample_size;
+ outSize -= pre * sample_size;
+ pre_cut -= pre;
+ //outSize = outSamples * sample_size;
+
+ // do we will have samples to output after cutting pre-delay?
+ if (!outSize)
+ return 0;
+
+ /* --- if we don't have enough to fully fill the end-zero-padding buffer, go ahead and fill --- */
+ if (outSize < post_buffer.length())
+ {
+ size_t bytes_written = post_buffer.write(out, outSize);
+ out+=bytes_written;
+ outSize-=bytes_written;
+ }
+
+ // if we're out of samples, go ahead and bail
+ if (!outSize)
+ return 0;
+
+ /* --- write contents of buffered audio (end-zero-padding buffer) */
+ if (!post_buffer.empty())
+ {
+ void *buffer = 0;
+ size_t len = 0;
+ if (post_buffer.get(&buffer, &len))
+ {
+ int ret = Write576((char *)buffer, len);
+ if (ret != 0)
+ return ret;
+ }
+ }
+
+ /* --- make sure we have enough samples left over to fill our post-zero-padding buffer --- */
+ size_t remainingFill = /*cut_size - */post_buffer.remaining();
+ int outWrite = max(0, (int)outSize - (int)remainingFill);
+
+ /* --- write the output that doesn't end up in the post buffer */
+ if (outWrite)
+ {
+ int ret = Write576(out, outWrite);
+ if (ret != 0)
+ return ret;
+ }
+ out += outWrite;
+ outSize -= outWrite;
+
+ /* --- write whatever is left over into the end-zero-padding buffer --- */
+ if (outSize)
+ {
+ post_buffer.write(out, outSize);
+ }
+ return 0;
+ }
+
+ /* meant to be called after Write(0,0) */
+ int WaitWhilePlaying()
+ {
+ while (output->IsPlaying())
+ {
+ int ret = WaitOrAbort(10);
+ if (ret != 0)
+ return ret;
+
+ output->CanWrite(); // some output drivers need CanWrite
+ // to be called on a regular basis.
+ }
+ return 0;
+ }
+ private:
+ /* helper methods */
+ int WaitForOutput(int write_size_bytes)
+ {
+ while (output->CanWrite() < write_size_bytes)
+ {
+ int ret = WaitOrAbort(55);
+ if (ret != 0)
+ return ret;
+ }
+ return 0;
+ }
+
+
+ /* writes one chunk (576 samples) to the output plugin, waiting as necessary */
+ int WriteOutput(char *buffer, size_t len)
+ {
+ int ret = WaitForOutput((int)len);
+ if (ret != 0)
+ return ret;
+
+ // write vis data before so we guarantee 576 samples
+ if (len == 576*sample_size)
+ {
+ plugin->SAAddPCMData(buffer, channels, bps, output->GetWrittenTime() + first_timestamp);
+ plugin->VSAAddPCMData(buffer, channels, bps, output->GetWrittenTime() + first_timestamp);
+ }
+
+ if (plugin->dsp_isactive())
+ len = sample_size * plugin->dsp_dosamples((short *)buffer, (int)(len / sample_size), bps, channels, sample_rate);
+
+ output->Write(buffer, (int)len);
+ return 0;
+ }
+
+ /* given a large buffer, writes 576 sample chunks to the vis, dsp and output plugin */
+ int Write576(char *buffer, size_t out_size)
+ {
+ /* if we have some stuff leftover in the 576 sample spill buffer, fill it up */
+ if (!buffer576.empty())
+ {
+ size_t bytes_written = buffer576.write(buffer, out_size);
+ out_size -= bytes_written;
+ buffer += bytes_written;
+ }
+
+ if (buffer576.full())
+ {
+ void *buffer = 0;
+ size_t len = 0;
+ if (buffer576.get(&buffer, &len))
+ {
+ int ret = WriteOutput((char *)buffer, len);
+ if (ret != 0)
+ return ret;
+ }
+ }
+
+ while (out_size >= 576*sample_size)
+ {
+ int ret = WriteOutput(buffer, 576*sample_size);
+ if (ret != 0)
+ return ret;
+
+ out_size -= 576*sample_size;
+ buffer+=576*sample_size;
+ }
+
+ if (out_size)
+ {
+ assert(out_size < 576*sample_size);
+ buffer576.write(buffer, out_size);
+ }
+ return 0;
+ }
+
+ private:
+ Out_Module *output;
+ In_Module *plugin;
+ SpillBuffer post_buffer, buffer576;
+ size_t cut_size;
+ size_t pre_cut, pre_cut_size, decoder_delay;
+ bool audio_opened;
+ int first_timestamp; /* timestamp of the first decoded audio frame, necessary for accurate video syncing */
+ size_t sample_size; /* size, in bytes, of one sample of audio (channels*bps/8) */
+ int output_latency; /* as returned from Out_Module::Open() */
+ int channels, sample_rate, bps;
+ };
+}
diff --git a/Src/nu/AutoChar.h b/Src/nu/AutoChar.h
new file mode 100644
index 00000000..0e3e0ea2
--- /dev/null
+++ b/Src/nu/AutoChar.h
@@ -0,0 +1,189 @@
+#ifndef NULLSOFT_AUTOCHARH
+#define NULLSOFT_AUTOCHARH
+#ifdef WIN32
+#include <windows.h>
+
+
+inline char *AutoCharDupN(const wchar_t *convert, size_t len, UINT codePage = CP_ACP, UINT flags=0)
+{
+ if (!convert)
+ return 0;
+ int size = WideCharToMultiByte(codePage, flags, convert, (int)len, 0, 0, NULL, NULL);
+
+ if (!size)
+ return 0;
+
+ char *narrow = (char *)malloc((size+1)*sizeof(char));
+ if ( narrow == 0 )
+ return 0;
+
+ if (!WideCharToMultiByte(codePage, flags, convert, (int)len, narrow, size, NULL, NULL))
+ {
+ free(narrow);
+ narrow=0;
+ }
+ else
+ narrow[size]=0;
+
+ return narrow;
+}
+
+inline char *AutoCharDup(const wchar_t *convert, UINT codePage = CP_ACP, UINT flags=0)
+{
+ if (!convert)
+ return 0;
+
+ int size = WideCharToMultiByte(codePage, flags, convert, -1, 0, 0, NULL, NULL);
+
+ if (!size)
+ return 0;
+
+ char *narrow = (char *)malloc(size*sizeof(char));
+ if ( narrow == 0 )
+ return 0;
+
+ if (!WideCharToMultiByte(codePage, flags, convert, -1, narrow, size, NULL, NULL))
+ {
+ free(narrow);
+ narrow=0;
+ }
+ return narrow;
+}
+
+class AutoChar
+{
+public:
+ AutoChar(const wchar_t *convert, UINT codePage = CP_ACP, UINT flags=0) : narrow(0)
+ {
+ narrow = AutoCharDup(convert, codePage, flags);
+ }
+ ~AutoChar()
+ {
+ free(narrow);
+ narrow=0;
+ }
+ operator const char *()
+ {
+ return narrow;
+ }
+ operator char *()
+ {
+ return narrow;
+ }
+protected:
+ AutoChar() : narrow(0)
+ {
+ }
+ char *narrow;
+};
+
+class AutoCharGrow
+{
+public:
+ AutoCharGrow()
+ {
+ narrow=0;
+ size=0;
+ }
+
+ ~AutoCharGrow()
+ {
+ free(narrow);
+ }
+
+ const char *Convert(const wchar_t *convert, UINT codePage = CP_ACP, UINT flags=0, size_t *cch=0)
+ {
+ size_t new_size = WideCharToMultiByte(codePage, flags, convert, -1, 0, 0, NULL, NULL);
+
+ if (!new_size)
+ return 0;
+
+ if ((size_t)new_size > size)
+ {
+ free(narrow);
+ narrow = (char *)malloc(new_size * sizeof(char));
+ if (!narrow)
+ {
+ size=0;
+ return 0;
+ }
+ size=(size_t)new_size;
+ }
+
+ if (!WideCharToMultiByte(codePage, flags, convert, -1, narrow, (int)new_size, NULL, NULL))
+ {
+ return 0;
+ }
+
+ if (cch)
+ *cch=new_size-1;
+
+ return narrow;
+ }
+
+protected:
+ char *narrow;
+ size_t size;
+};
+class AutoCharN : public AutoChar
+{
+public:
+ AutoCharN(const wchar_t *convert, size_t len, UINT codePage = CP_ACP, UINT flags=0)
+ {
+ narrow = AutoCharDupN(convert, len, codePage, flags);
+ }
+};
+#else
+#include <stdlib.h>
+#include <wchar.h>
+
+inline char *AutoCharDup(const wchar_t *convert)
+{
+ if (!convert)
+ return 0;
+
+ size_t size = wcslen(convert)+1;
+
+ if (!size)
+ return 0;
+
+ char *narrow = (char *)malloc(size*sizeof(char));
+ if ( narrow == 0 )
+ return 0;
+
+ if (wcstombs(narrow, convert, size) == (size_t)-1)
+ {
+ free(narrow);
+ narrow=0;
+ }
+ return narrow;
+}
+
+
+class AutoChar
+{
+public:
+
+ AutoChar(const wchar_t *convert) : narrow(0)
+ {
+ narrow = AutoCharDup(convert);
+ }
+ ~AutoChar()
+ {
+ free(narrow);
+ narrow=0;
+ }
+ operator const char *()
+ {
+ return narrow;
+ }
+ operator char *()
+ {
+ return narrow;
+ }
+private:
+ char *narrow;
+};
+#endif
+
+#endif \ No newline at end of file
diff --git a/Src/nu/AutoCharFn.h b/Src/nu/AutoCharFn.h
new file mode 100644
index 00000000..3b5b7750
--- /dev/null
+++ b/Src/nu/AutoCharFn.h
@@ -0,0 +1,50 @@
+#ifndef NULLSOFT_UTILITY_AUTOCHARFN_H
+#define NULLSOFT_UTILITY_AUTOCHARFN_H
+
+/* Winamp defines this, but this little block lets us use this thing outside of Winamp */
+#ifndef FILENAME_SIZE
+#define FILENAME_SIZE (MAX_PATH*4)
+#define REMOVE_FILENAME_SIZE
+#endif
+
+
+#include <windows.h>
+#include <shlwapi.h>
+
+class AutoCharFn
+{
+public:
+ AutoCharFn(const wchar_t *filename)
+ {
+ out[0]=0;
+ if (!filename)
+ return;
+ if (PathIsURLW(filename))
+ {
+ WideCharToMultiByte(CP_ACP, 0, filename, -1, out, FILENAME_SIZE, NULL, NULL);
+ return ;
+ }
+
+ BOOL unconvertable = FALSE;
+ WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, filename, -1, out, FILENAME_SIZE, NULL, &unconvertable);
+
+ if (unconvertable)
+ {
+ wchar_t temp[MAX_PATH];
+ if (GetShortPathNameW(filename, temp, MAX_PATH))
+ WideCharToMultiByte(CP_ACP, 0, temp, -1, out, FILENAME_SIZE, NULL, NULL);
+
+ }
+ }
+
+ operator char *() { return out; }
+private:
+ char out[FILENAME_SIZE];
+};
+
+
+#ifdef REMOVE_FILENAME_SIZE
+#undef FILENAME_SIZE
+#endif
+
+#endif
diff --git a/Src/nu/AutoHeader.h b/Src/nu/AutoHeader.h
new file mode 100644
index 00000000..366b169d
--- /dev/null
+++ b/Src/nu/AutoHeader.h
@@ -0,0 +1,98 @@
+#ifndef NULLSOFT_AUTOHEADERH
+#define NULLSOFT_AUTOHEADERH
+
+#include <windows.h>
+#include "AutoChar.h"
+
+
+
+/* encodes a UTF-8 string into a buffer, returns a pointer to the end of the string */
+inline char *AutoHeader_Encode(const char *in, char *out, size_t len)
+{
+ if (!len)
+ return 0;
+
+ char *dest=out;
+ const unsigned char *src = (const unsigned char *)in;
+ while (*src && --len)
+ {
+ if ((*src >= 'A' && *src <= 'Z') ||
+ (*src >= 'a' && *src <= 'z') ||
+ (*src >= '0' && *src <= '9') || *src == '.' || *src == '-' || *src == '~')
+ {
+ *dest++=*src++;
+ }
+ else if (len > 2)
+ {
+ int i = *src++;
+ *dest++ = '=';
+ int b = (i >> 4) & 15;
+ if (b < 10) *dest++ = '0' + b;
+ else *dest++ = 'A' + b - 10;
+ b = i & 15;
+ if (b < 10) *dest++ = '0' + b;
+ else *dest++ = 'A' + b - 10;
+ }
+ else
+ break;
+ }
+ *dest=0;
+ return dest;
+}
+
+
+inline char *AutoHeaderDup(const wchar_t *convert)
+{
+ if (!convert)
+ return 0;
+
+ BOOL failed=FALSE;
+ int n = WideCharToMultiByte(28591, WC_NO_BEST_FIT_CHARS, convert, -1, 0, 0, 0, &failed);
+ if (n && !failed)
+ {
+ char *url = (char *)malloc(n + 1);
+ WideCharToMultiByte(28591, WC_NO_BEST_FIT_CHARS, convert, -1, url, n+1, 0, 0);
+ return url;
+ }
+
+ AutoChar utf8(convert, CP_UTF8);
+ size_t size = strlen(utf8)*3+1; // one byte might get encoded to 3 bytes, so we'll malloc for worst-case
+
+ char *url= (char *)malloc((9 /*?utf-8?q?*/+ size + 2 /*?=*/)*sizeof(char));
+ memcpy(url, "?utf-8?q?", 9);
+ char *end = AutoHeader_Encode(utf8, url+9, size);
+ strcpy(end, "?=");
+ return url;
+}
+
+class AutoHeader
+{
+public:
+
+ AutoHeader(const wchar_t *convert) : narrow(0)
+ {
+ narrow = AutoHeaderDup(convert);
+ }
+ AutoHeader(const AutoHeader &convert) : narrow(0)
+ {
+ if (convert.narrow)
+ narrow = _strdup(convert.narrow);
+ }
+ ~AutoHeader()
+ {
+ free(narrow);
+ narrow=0;
+ }
+ operator const char *()
+ {
+ return narrow;
+ }
+ operator char *()
+ {
+ return narrow;
+ }
+private:
+ char *narrow;
+};
+
+#endif \ No newline at end of file
diff --git a/Src/nu/AutoLock.h b/Src/nu/AutoLock.h
new file mode 100644
index 00000000..92c9524b
--- /dev/null
+++ b/Src/nu/AutoLock.h
@@ -0,0 +1,379 @@
+#pragma warning (disable:4786)
+#ifndef AUTOLOCKH
+#define AUTOLOCKH
+
+#ifdef _WIN32
+#include <windows.h>
+#else
+#include <CoreServices/CoreServices.h>
+#endif
+
+/*
+NULLSOFT_LOCK_OUTPUT_STATUS turns on/off debugging output
+this can be VERY useful if you are trying to find a deadlock
+each time the guard is locked or unlocked, it outputs a list of
+any threads using the mutex, and their function stack
+*/
+//#define NULLSOFT_LOCK_OUTPUT_STATS
+
+#ifdef NULLSOFT_LOCK_OUTPUT_STATS
+
+#include <string> // we save each function name as a string
+#include <deque> // we make a list of the recursive function stack for each thread
+#include <map> // and map
+#include <iostream> // we output to std::cerr
+#include <windows.h>
+
+
+/*****
+Description:
+ This class uses scoping to wrap a critical section (lightweight in-process mutex)
+ The constructor enters the mutex and the destructor leaves it. This allows it to
+ take advantage of automatic scoping in C++, because C++ automatically calls the destructor
+ when an object leaves scope.
+
+ This is _especially_ useful when you have multiple return paths, since you don't have to
+ repeat mutex-leaving code.
+
+To use:
+ Make a LockGuard for a resource you want to protect. The guard is shared, so make it part
+ of your class, or a global, or whatever. The LockGuard is essentially a "token", equivalent
+ to your mutex handle or critical section handle.
+
+ Make an AutoLock object on the stack to lock. It will unlock automatically when the object
+ leaves scope.
+
+ Note: You'll want to make an object on the stack - don't use a heap object (new/delete)
+ unless you have weird requirements and know what you are doing.
+
+ Example:
+
+ class MyClass
+ {
+ LockGuard fileGuard;
+ fstream file;
+ void DumpSomeData() //
+ {
+ AutoLock lock(fileGuard);
+ file << GetData();
+ }
+
+ void CALLBACK NewData() // potentially called by another thread
+ {
+ AutoLock lock(fileGuard)
+ file << newData;
+ }
+ };
+
+
+ Tip: You can use "false scoping" to tweak the mutex lifetime, for example:
+
+ void DoStuff()
+ {
+ a = GetData();
+ { // false scope
+ AutoLock lock(dataGuard);
+ DoCalculationsWith(a);
+ } // mutex will release here
+ SetData(a);
+ }
+
+ Tip: A common mistake is making a temporary object.
+ i.e.
+ CORRECT: AutoLock lock(fileGuard); // an AutoLock object called "lock" is put on the stack
+ INCORRECT: AutoLock(fileGuard); // An unnamed temporary is created which will be destroyed IMMEDIATELY
+
+*******/
+#define MANUALLOCKNAME(x) x
+#define LOCKNAME(x) ,x
+#define GUARDNAME(x) (x)
+namespace Nullsoft
+{
+ namespace Utility
+ {
+ /* the token which represents a resource to be locked */
+ class LockGuard
+ {
+ public:
+ inline LockGuard(char *name = "Unnamed Guard") : lockName(name), owner(0)
+ {
+ InitializeCriticalSection(&cerr_cs);
+ InitializeCriticalSection(&map_cs);
+ InitializeCriticalSection(&m_cs);
+ }
+
+ inline LockGuard(DWORD spin_count,char *name = "Unnamed Guard") : lockName(name), owner(0)
+ {
+ InitializeCriticalSection(&cerr_cs);
+ InitializeCriticalSection(&map_cs);
+ InitializeCriticalSection(&m_cs);
+ }
+
+ inline ~LockGuard()
+ {
+ DeleteCriticalSection(&cerr_cs);
+ DeleteCriticalSection(&map_cs);
+ DeleteCriticalSection(&m_cs);
+ }
+
+ inline void Lock()
+ {
+ EnterCriticalSection(&m_cs);
+ }
+
+ inline void Unlock()
+ {
+ LeaveCriticalSection(&m_cs);
+ }
+
+ int ThreadCount()
+ {
+ EnterCriticalSection(&map_cs);
+ int count = 0;
+ for (ThreadMap::iterator itr = threads.begin(); itr != threads.end(); itr++)
+ {
+ if (!itr->second.empty())
+ count++;
+ }
+
+ LeaveCriticalSection(&map_cs);
+ return count;
+ }
+
+ void Display()
+ {
+ EnterCriticalSection(&map_cs);
+ EnterCriticalSection(&cerr_cs);
+
+ if (ThreadCount() > 1 && owner)
+ {
+ wchar_t disp[256];
+ wsprintfW(disp, L"Guard: %S\r\n", lockName.c_str());
+ OutputDebugStringW(disp);
+ for (ThreadMap::iterator itr = threads.begin(); itr != threads.end(); itr++)
+ {
+ if (itr->second.empty())
+ continue;
+
+
+ wsprintfW(disp, L" Thread ID: %x", itr->first);
+ if (owner == itr->first)
+ wcscat(disp, L" [holding the mutex] *****\r\n");
+ else
+ wcscat(disp, L" [blocked]\r\n");
+ OutputDebugStringW(disp);
+ for (FunctionStack::iterator fitr = itr->second.begin(); fitr != itr->second.end(); fitr++)
+ {
+ wsprintfW(disp, L" %S();\r\n", fitr->c_str());
+ OutputDebugStringW(disp);
+ }
+ }
+ }
+ LeaveCriticalSection(&cerr_cs);
+ LeaveCriticalSection(&map_cs);
+ }
+
+ void In(DWORD thread, char *functionName)
+ {
+ EnterCriticalSection(&map_cs);
+ threads[thread].push_back(functionName);
+ LeaveCriticalSection(&map_cs);
+ }
+
+ void Out(DWORD thread)
+ {
+ EnterCriticalSection(&map_cs);
+ threads[thread].pop_back();
+ LeaveCriticalSection(&map_cs);
+ }
+ std::string lockName;
+ CRITICAL_SECTION cerr_cs, map_cs;
+ typedef std::deque<std::string> FunctionStack; // this typedef reduce ugly c++ <>::<>::<> overkill
+ typedef std::map<DWORD, FunctionStack> ThreadMap;
+ ThreadMap threads;
+
+ DWORD owner;
+ private:
+ CRITICAL_SECTION m_cs;
+
+ };
+
+ /* an AutoLock locks a resource (represented by a LockGuard) for the duration of its lifetime */
+ class AutoLock
+ {
+ public:
+ /*
+ @param functionName The function name which wants the mutex
+ we pass it in as a char * even though it'll be converted to a std::string
+ to reduce overhead when OUTPUT_STATS is off
+ */
+ inline AutoLock(LockGuard &_guard, char *functionName = "function name not passed") : guard(&_guard)
+ {
+ ManualLock(functionName);
+ }
+ inline void ManualLock(char *functionName = "manual lock")
+ {
+ thisThread = GetCurrentThreadId();
+ guard->In(thisThread, functionName);
+ guard->Display();
+ guard->Lock();
+ guard->owner = thisThread;
+ guard->Display();
+ }
+
+ inline void ManualUnlock()
+ {
+ guard->Display();
+ guard->Unlock();
+
+ InterlockedCompareExchange((LONG volatile *)&guard->owner, 0, (LONG)thisThread);
+ /* above line is functionally equivalent to:
+ if (guard->owner == thisThread)
+ guard->owner=0;
+ */
+ guard->Out(thisThread);
+ guard->Display();
+ }
+
+ inline ~AutoLock()
+ {
+ ManualUnlock();
+ }
+
+ LockGuard *guard;
+ DWORD thisThread;
+ };
+
+ }
+}
+
+
+#else
+#define MANUALLOCKNAME(x)
+#define LOCKNAME(x)
+#define GUARDNAME(x)
+namespace Nullsoft
+{
+ namespace Utility
+ {
+ /* the token which represents a resource to be locked */
+ class LockGuard
+ {
+ public:
+ inline LockGuard(char *guardName = "")
+ {
+ #ifdef _WIN32
+ InitializeCriticalSection(&m_cs);
+ #else
+ MPCreateCriticalRegion(&cr);
+ #endif
+ }
+#if _WIN32_WINNT >= 0x403
+ inline LockGuard(DWORD spin_count, char *guardName = "")
+ {
+ if (spin_count)
+ InitializeCriticalSectionAndSpinCount(&m_cs, spin_count);
+ else
+ InitializeCriticalSection(&m_cs);
+ }
+#endif
+ inline ~LockGuard()
+ {
+ #ifdef _WIN32
+ DeleteCriticalSection(&m_cs);
+ #else
+ MPDeleteCriticalRegion(cr);
+ #endif
+ }
+ inline void Lock()
+ {
+ #ifdef _WIN32
+ EnterCriticalSection(&m_cs);
+ #else
+ MPEnterCriticalRegion(cr, kDurationForever);
+ #endif
+ }
+
+ inline void Unlock()
+ {
+ #ifdef _WIN32
+ LeaveCriticalSection(&m_cs);
+ #else
+ MPExitCriticalRegion(cr);
+ #endif
+ }
+ private:
+ #ifdef _WIN32
+ CRITICAL_SECTION m_cs;
+ #else
+ MPCriticalRegionID cr;
+ #endif
+ LockGuard(const LockGuard &copy) { } // make copy constructor private so it can't be used
+ LockGuard &operator =(const LockGuard &copy) {} // same with operator=
+ };
+
+ /* an AutoLock locks a resource (represented by a LockGuard) for the duration of its lifetime */
+ class AutoLock
+ {
+ public:
+ inline AutoLock(LockGuard &_guard) : guard(&_guard)
+ {
+ guard->Lock();
+ }
+
+ inline AutoLock(LockGuard *_guard) : guard(_guard)
+ {
+ guard->Lock();
+ }
+ inline void ManualLock()
+ {
+ guard->Lock();
+ }
+
+ inline void ManualUnlock()
+ {
+ guard->Unlock();
+
+ }
+ inline ~AutoLock()
+ {
+ guard->Unlock();
+ }
+ LockGuard *guard;
+ };
+
+ // will lock anything that implements Lock() and Unlock()
+ template <class LockGuard_t>
+ class AutoLockT
+ {
+ public:
+ inline AutoLockT(LockGuard_t &_guard) : guard(&_guard)
+ {
+ guard->Lock();
+ }
+
+ inline AutoLockT(LockGuard_t *_guard) : guard(_guard)
+ {
+ guard->Lock();
+ }
+ inline void ManualLock()
+ {
+ guard->Lock();
+ }
+
+ inline void ManualUnlock()
+ {
+ guard->Unlock();
+
+ }
+ inline ~AutoLockT()
+ {
+ guard->Unlock();
+ }
+ LockGuard_t *guard;
+ };
+ }
+}
+#endif
+
+#endif \ No newline at end of file
diff --git a/Src/nu/AutoUrl.h b/Src/nu/AutoUrl.h
new file mode 100644
index 00000000..2431d9ce
--- /dev/null
+++ b/Src/nu/AutoUrl.h
@@ -0,0 +1,141 @@
+#ifndef NULLSOFT_AUTOURLH
+#define NULLSOFT_AUTOURLH
+
+#include <windows.h>
+#include "AutoChar.h"
+
+/* benski> i'm sure there's a nice optimized way of doing this, but I need to implement it _right now_ */
+#define HEXCASE(d) case 0x##d: return #@d
+inline char quickhex(unsigned char in)
+{
+ switch (in)
+ {
+ HEXCASE(0);
+ HEXCASE(1);
+ HEXCASE(2);
+ HEXCASE(3);
+ HEXCASE(4);
+ HEXCASE(5);
+ HEXCASE(6);
+ HEXCASE(7);
+ HEXCASE(8);
+ HEXCASE(9);
+ HEXCASE(A);
+ HEXCASE(B);
+ HEXCASE(C);
+ HEXCASE(D);
+ HEXCASE(E);
+ HEXCASE(F);
+
+ }
+ return 0;
+}
+
+/* encodes a UTF-8 string into a buffer */
+inline void AutoUrl_Encode(const char *in, char *out, size_t len)
+{
+ if (!len)
+ return;
+
+ char *dest=out;
+ const unsigned char *src = (const unsigned char *)in;
+ while (*src && --len)
+ {
+ if ((*src >= 'A' && *src <= 'Z') ||
+ (*src >= 'a' && *src <= 'z') ||
+ (*src >= '0' && *src <= '9') || *src == '.' || *src == '_' || *src == '-' || *src == '~')
+ {
+ *dest++=*src++;
+ }
+ else if (len > 2)
+ {
+ int i = *src++;
+ *dest++ = '%';
+ int b = (i >> 4) & 15;
+ if (b < 10) *dest++ = (char)('0' + b);
+ else *dest++ = (char)('A' + b - 10);
+ b = i & 15;
+ if (b < 10) *dest++ = (char)('0' + b);
+ else *dest++ = (char)('A' + b - 10);
+ }
+ else
+ break;
+ }
+ *dest=0;
+}
+
+inline char *AutoUrlDupN(const wchar_t *convert, size_t len)
+{
+ if (!convert)
+ return 0;
+ AutoCharN utf8(convert, len, CP_UTF8);
+ size_t size = strlen(utf8)*3+1; // one byte might get encoded to 3 bytes, so we'll malloc for worst-case
+
+ char *url= (char *)malloc(size*sizeof(char));
+ AutoUrl_Encode(utf8, url, size);
+ return url;
+}
+
+inline char *AutoUrlDup(const wchar_t *convert)
+{
+ if (!convert)
+ return 0;
+ AutoChar utf8(convert, CP_UTF8);
+ size_t size = strlen(utf8)*3+1; // one byte might get encoded to 3 bytes, so we'll malloc for worst-case
+
+ char *url= (char *)malloc(size*sizeof(char));
+ AutoUrl_Encode(utf8, url, size);
+ return url;
+}
+
+inline char *AutoUrlDup(const char *utf8)
+{
+ if (!utf8)
+ return 0;
+
+ size_t size = strlen(utf8)*3+1; // one byte might get encoded to 3 bytes, so we'll malloc for worst-case
+
+ char *url= (char *)malloc(size*sizeof(char));
+ AutoUrl_Encode(utf8, url, size);
+ return url;
+}
+
+class AutoUrl
+{
+public:
+
+ AutoUrl(const wchar_t *convert) : narrow(0)
+ {
+ narrow = AutoUrlDup(convert);
+ }
+ AutoUrl(const wchar_t *convert, size_t len) : narrow(0)
+ {
+ narrow = AutoUrlDupN(convert, len);
+ }
+ AutoUrl(const char *convert) : narrow(0)
+ {
+ narrow = AutoUrlDup(convert);
+ }
+ AutoUrl(const AutoUrl &convert) : narrow(0)
+ {
+ if (convert.narrow)
+ narrow = _strdup(convert.narrow);
+ }
+ ~AutoUrl()
+ {
+ free(narrow);
+ narrow=0;
+ }
+ operator const char *()
+ {
+ return narrow;
+ }
+ operator char *()
+ {
+ return narrow;
+ }
+private:
+ char *narrow;
+};
+
+#endif \ No newline at end of file
diff --git a/Src/nu/AutoWide.h b/Src/nu/AutoWide.h
new file mode 100644
index 00000000..505bfb24
--- /dev/null
+++ b/Src/nu/AutoWide.h
@@ -0,0 +1,99 @@
+#ifndef AUTOWIDEH
+#define AUTOWIDEH
+#ifdef WIN32
+#include <windows.h>
+
+inline wchar_t *AutoWideDup( const char *convert, UINT codePage = CP_ACP )
+{
+ if ( !convert )
+ return 0;
+
+ int size = MultiByteToWideChar( codePage, 0, convert, -1, 0, 0 );
+ if ( !size )
+ return 0;
+
+ wchar_t *wide = (wchar_t *)malloc( size * sizeof( wchar_t ) );
+
+ if ( !MultiByteToWideChar( codePage, 0, convert, -1, wide, size ) )
+ {
+ free( wide );
+ wide = 0;
+ }
+
+ return wide;
+}
+
+class AutoWide
+{
+public:
+ AutoWide( const char *convert, UINT codePage = CP_ACP )
+ {
+ wide = AutoWideDup( convert, codePage );
+ }
+ ~AutoWide()
+ {
+ free( wide );
+ wide = 0;
+ }
+ operator wchar_t *( )
+ {
+ return wide;
+ }
+ operator const wchar_t *( )
+ {
+ return wide;
+ }
+ operator bool()
+ {
+ return !!wide;
+ }
+
+private:
+ wchar_t *wide = 0;
+};
+
+#elif defined(__APPLE__)
+#include <string.h>
+inline wchar_t *AutoWideDup( const char *convert )
+{
+ if ( !convert )
+ return 0;
+
+ int size = strlen( convert ) + 1;
+ if ( !size )
+ return 0;
+
+ wchar_t *wide = (wchar_t *)malloc( size * sizeof( wchar_t ) );
+
+ if ( mbstowcs( wide, convert, size ) == (size_t)-1 )
+ {
+ free( wide );
+ wide = 0;
+ }
+
+ return wide;
+}
+
+class AutoWide
+{
+public:
+ AutoWide( const char *convert )
+ {
+ wide = AutoWideDup( convert );
+ }
+ ~AutoWide()
+ {
+ free( wide );
+ wide = 0;
+ }
+ operator wchar_t *( )
+ {
+ return wide;
+ }
+private:
+ wchar_t *wide = 0;
+};
+
+#endif
+
+#endif \ No newline at end of file
diff --git a/Src/nu/AutoWideFn.h b/Src/nu/AutoWideFn.h
new file mode 100644
index 00000000..5d44dee3
--- /dev/null
+++ b/Src/nu/AutoWideFn.h
@@ -0,0 +1,191 @@
+#ifndef NULLSOFT_UTILITY_AUTOWIDEFN_H
+#define NULLSOFT_UTILITY_AUTOWIDEFN_H
+
+
+/* Winamp defines this, but this little block lets us use this thing outside of Winamp */
+#ifndef FILENAME_SIZE
+#define FILENAME_SIZE (MAX_PATH*4)
+#define REMOVE_FILENAME_SIZE
+#endif
+
+#include <windows.h>
+#include "AutoWide.h"
+#include "AutoChar.h"
+#include <shlwapi.h>
+/*
+Tries to find a filename that underwent a destructive Unicode-to-ANSI conversion
+*/
+
+#pragma warning(push)
+#pragma warning(disable:4995)
+class AutoWideFn
+{
+public:
+ AutoWideFn(const char *narrowFn)
+ {
+ wideFn[0]=0;
+ if (!narrowFn)
+ return;
+ CreateFile_HACK(narrowFn, wideFn);
+ }
+
+ operator wchar_t *() { return wideFn; }
+ bool unicode_find(/*char *path,*/ char *pattern, wchar_t *out, UINT out_ptr, bool dir, HANDLE *f)
+ {
+ WIN32_FIND_DATAW fd = {0};
+
+ if (*f == INVALID_HANDLE_VALUE)
+ {
+ lstrcpyW(out + out_ptr, L"*");
+ *f = FindFirstFileW(out, &fd);
+ out[out_ptr] = 0;
+ if (*f == INVALID_HANDLE_VALUE) return 0;
+ }
+ else
+ {
+ if (!FindNextFileW(*f, &fd))
+ {
+ FindClose(*f);
+ *f = INVALID_HANDLE_VALUE;
+ return 0;
+ }
+ }
+
+ if (*f == INVALID_HANDLE_VALUE) return 0;
+ char temp[MAX_PATH*2 + 1] = {0};
+ do
+ {
+ if (dir)
+ {
+ if (!(fd.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)) continue;
+ }
+ else
+ {
+ if (fd.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) continue;
+ }
+ WideCharToMultiByte(CP_ACP, 0, fd.cFileName, -1, temp, sizeof(temp), 0, 0);
+ //wcstombs(temp,fd.cFileName,sizeof(temp));
+ if (!_stricmp(temp, pattern))
+ { //found
+ lstrcpyW(out + out_ptr, fd.cFileName);
+ return 1;
+ }
+
+ WideCharToMultiByte(CP_ACP, 0, fd.cAlternateFileName, -1, temp, sizeof(temp), 0, 0);
+ if (!_stricmp(temp, pattern))
+ { //found
+ lstrcpyW(out + out_ptr, fd.cFileName);
+ return 1;
+ }
+ }
+ while (FindNextFileW(*f, &fd));
+ FindClose(*f);
+ *f = INVALID_HANDLE_VALUE;
+
+ return 0;
+ }
+
+ bool unicode_open_recur(char *path, char *ptr, wchar_t *out, UINT out_ptr)
+ {
+ char * next = strchr(ptr, '\\');
+ if (next)
+ { //dig another dir
+ HANDLE f = INVALID_HANDLE_VALUE;
+ bool found;
+ do
+ {
+ next[0] = 0;
+ char * zz = _strdup(ptr);
+ char bk = *ptr;
+ *ptr = 0;
+ found = unicode_find(/*path,*/ zz, out, out_ptr, 1, &f);
+ free(zz);
+ *ptr = bk;
+ next[0] = '\\';
+ if (found)
+ {
+ UINT op_bk = out_ptr;
+ while (out_ptr < FILENAME_SIZE && out[out_ptr]) out_ptr++;
+ out[out_ptr++] = '\\';
+ if (unicode_open_recur(path, next + 1, out, out_ptr))
+ {
+ if (f != INVALID_HANDLE_VALUE) FindClose(f);
+ return 1;
+ }
+ out_ptr = op_bk;
+ out[out_ptr] = 0;
+ }
+ } while (found);
+ }
+ else
+ { //final dir
+ HANDLE f = INVALID_HANDLE_VALUE;
+ char * zz = _strdup(ptr);
+ char bk = *ptr;
+ *ptr = 0;
+ bool found = unicode_find(/*path,*/ zz, out, out_ptr, 0, &f);
+ if (!found)
+ {
+ if (f != INVALID_HANDLE_VALUE)
+ {
+ FindClose(f);
+ f = INVALID_HANDLE_VALUE;
+ }
+ found = unicode_find(/*path,*/ zz, out, out_ptr, 1, &f);
+ }
+ free(zz);
+ *ptr = bk;
+ if (f != INVALID_HANDLE_VALUE) FindClose(f);
+ return found;
+ }
+ return 0;
+ }
+
+
+ void CreateFile_HACK(const char * path, wchar_t out[FILENAME_SIZE])
+ {
+ MultiByteToWideChar(CP_ACP, 0, path, -1, out, FILENAME_SIZE);
+
+ if (PathIsURLW(out))
+ return ;
+
+ if (!StrChrW(out, L'?'))
+ return ; // no unconvertables? Great!
+
+// if (PathFileExistsW(out))
+ // return ; // no unconvertables? Great!
+
+ bool found = false;
+
+ memset(out, 0, FILENAME_SIZE * sizeof(wchar_t));
+
+ char * _p = _strdup(path);
+ char * t = strchr(_p, '\\');
+
+ if (t)
+ {
+ char bk = t[1];
+ t[1] = 0;
+ UINT o = MultiByteToWideChar(CP_ACP, 0, _p, -1, out, FILENAME_SIZE);
+ o--;
+ t[1] = bk;
+ found = unicode_open_recur(_p, t + 1, out, o);
+ }
+ else
+ found = unicode_open_recur(_p, _p, out, 0);
+ free(_p);
+
+ if (!found)
+ MultiByteToWideChar(CP_ACP, 0, path, -1, out, FILENAME_SIZE);
+ }
+private:
+ wchar_t wideFn[FILENAME_SIZE];
+};
+
+#pragma warning(pop)
+
+#ifdef REMOVE_FILENAME_SIZE
+#undef FILENAME_SIZE
+#endif
+
+#endif
diff --git a/Src/nu/CCVersion.cpp b/Src/nu/CCVersion.cpp
new file mode 100644
index 00000000..8d0b52c2
--- /dev/null
+++ b/Src/nu/CCVersion.cpp
@@ -0,0 +1,45 @@
+#include "CCVersion.h"
+#include <windows.h>
+#include <commctrl.h>
+#include <shlwapi.h>
+
+DWORD GetCommCtrlDllVersion(LPCTSTR lpszDllName)
+{
+ DWORD dwVersion = 0;
+
+ /* In theory, we should limit the search path to only the system folder
+ at this point, I don't care */
+
+ HINSTANCE hinstDll = LoadLibraryW(lpszDllName);
+
+ if(hinstDll)
+ {
+ DLLGETVERSIONPROC pDllGetVersion;
+ pDllGetVersion = (DLLGETVERSIONPROC)GetProcAddress(hinstDll,
+ "DllGetVersion");
+
+ /* Because some DLLs might not implement this function, you
+ must test for it explicitly. Depending on the particular
+ DLL, the lack of a DllGetVersion function can be a useful
+ indicator of the version. */
+
+ if(pDllGetVersion)
+ {
+ DLLVERSIONINFO dvi;
+ HRESULT hr;
+
+ ZeroMemory(&dvi, sizeof(dvi));
+ dvi.cbSize = sizeof(dvi);
+
+ hr = (*pDllGetVersion)(&dvi);
+
+ if(SUCCEEDED(hr))
+ {
+ dwVersion = PACKVERSION(dvi.dwMajorVersion, dvi.dwMinorVersion);
+ }
+ }
+
+ FreeLibrary(hinstDll);
+ }
+ return dwVersion;
+} \ No newline at end of file
diff --git a/Src/nu/CCVersion.h b/Src/nu/CCVersion.h
new file mode 100644
index 00000000..aba59014
--- /dev/null
+++ b/Src/nu/CCVersion.h
@@ -0,0 +1,6 @@
+#ifndef NULLSOFT_CCVERSIONH
+#define NULLSOFT_CCVERSIONH
+#include <windows.h>
+DWORD GetCommCtrlDllVersion(LPCTSTR);
+#define PACKVERSION(major,minor) MAKELONG(minor,major)
+#endif \ No newline at end of file
diff --git a/Src/nu/CGlobalAtom.h b/Src/nu/CGlobalAtom.h
new file mode 100644
index 00000000..b9fbd89a
--- /dev/null
+++ b/Src/nu/CGlobalAtom.h
@@ -0,0 +1,21 @@
+#pragma once
+
+#include <windows.h>
+class CGlobalAtom
+{
+public:
+ CGlobalAtom(LPCWSTR name)
+ {
+ prop = GlobalAddAtomW(name);
+ }
+ ~CGlobalAtom()
+ {
+ if (prop)
+ GlobalDeleteAtom(prop);
+ prop=0;
+ }
+ operator ATOM() { return prop; }
+ operator LPCWSTR() { return (LPCWSTR) prop; }
+private:
+ ATOM prop;
+}; \ No newline at end of file
diff --git a/Src/nu/ChildSizer.cpp b/Src/nu/ChildSizer.cpp
new file mode 100644
index 00000000..b797ff04
--- /dev/null
+++ b/Src/nu/ChildSizer.cpp
@@ -0,0 +1,33 @@
+#include "ChildSizer.h"
+
+ChildSizer childSizer;
+
+ChildSizer::ChildSizer()
+: childresize_init(0),
+childresize_resize(0)
+{}
+
+void ChildSizer::Init(HWND dlg, ChildWndResizeItem *list, int count)
+{
+ if (!childresize_init)
+ childresize_init = (ChildResizeFunc)mediaLibrary.GetWADLGFunc(32);
+ childresize_init(dlg, list, count);
+}
+
+void ChildSizer::Resize(HWND dlg, ChildWndResizeItem *list, int count)
+{
+ if (!childresize_resize)
+ childresize_resize = (ChildResizeFunc)mediaLibrary.GetWADLGFunc(33);
+ childresize_resize(dlg, list, count);
+}
+
+ChildWndResizeItem *ChildSizer::Lookup(int id, ChildWndResizeItem *list, size_t numElements)
+{
+ for (size_t i=0;i!=numElements;i++)
+ {
+ if (list[i].id == id)
+ return &list[i];
+ }
+ return 0;
+}
+
diff --git a/Src/nu/ChildSizer.h b/Src/nu/ChildSizer.h
new file mode 100644
index 00000000..acf280f7
--- /dev/null
+++ b/Src/nu/ChildSizer.h
@@ -0,0 +1,38 @@
+#ifndef NULLSOFT_CHILDSIZERH
+#define NULLSOFT_CHILDSIZERH
+
+#include "MediaLibraryInterface.h"
+typedef struct
+{
+ int id;
+ int type; // 0xLTRB
+ RECT rinfo;
+}
+ChildWndResizeItem;
+
+enum
+{
+ Stationary = 0x0000,
+ ResizeBottom = 0x0001,
+ ResizeRight = 0x0010,
+ ResizeTop = 0x0100,
+ ResizeLeft=0x1000,
+ DockToBottom = 0x0101,
+ DockToBottomRight = 0x1111,
+};
+class ChildSizer
+{
+ typedef void (*ChildResizeFunc)(HWND, ChildWndResizeItem*, int);
+public:
+ ChildSizer();
+
+ void Init(HWND dlg, ChildWndResizeItem *list, int count);
+
+ void Resize(HWND dlg, ChildWndResizeItem *list, int count);
+ static ChildWndResizeItem *Lookup(int id, ChildWndResizeItem *list, size_t numElements);
+
+ ChildResizeFunc childresize_init, childresize_resize;
+
+};
+extern ChildSizer childSizer;
+#endif
diff --git a/Src/nu/ComboBox.h b/Src/nu/ComboBox.h
new file mode 100644
index 00000000..150d6a6b
--- /dev/null
+++ b/Src/nu/ComboBox.h
@@ -0,0 +1,105 @@
+#ifndef NULLSOFT_UTILITY_COMBOBOX_H
+#define NULLSOFT_UTILITY_COMBOBOX_H
+#include <windows.h>
+class ComboBox
+{
+public:
+ ComboBox(HWND hwndDlg, int id)
+ {
+ cbHwnd = GetDlgItem(hwndDlg, id);
+ }
+
+ ComboBox(HWND control)
+ {
+ cbHwnd = control;
+ }
+
+ operator HWND()
+ {
+ return cbHwnd;
+ }
+
+ LRESULT AddString(const wchar_t *string)
+ {
+ return SendMessageW(cbHwnd, CB_ADDSTRING, 0, (LPARAM)string);
+ }
+
+ LRESULT AddString(const wchar_t *string, LPARAM data)
+ {
+ LRESULT index = SendMessageW(cbHwnd, CB_ADDSTRING, 0, (LPARAM)string);
+ SendMessage(cbHwnd, CB_SETITEMDATA, index, data);
+ return index;
+ }
+
+ LRESULT AddString(const char *string)
+ {
+ return SendMessageA(cbHwnd, CB_ADDSTRING, 0, (LPARAM)string);
+ }
+
+ void SetItemData(LPARAM index, LPARAM data)
+ {
+ SendMessage(cbHwnd, CB_SETITEMDATA, index, data);
+ }
+
+ int GetCount()
+ {
+ return (int)SendMessage(cbHwnd, CB_GETCOUNT, 0, 0);
+ }
+
+ LRESULT GetItemData(LPARAM index)
+ {
+ return SendMessage(cbHwnd, CB_GETITEMDATA, index, 0);
+ }
+
+ void Clear()
+ {
+ SendMessage(cbHwnd, CB_RESETCONTENT, 0, 0);
+ }
+
+ void SelectItem(LPARAM index)
+ {
+ SendMessage(cbHwnd, CB_SETCURSEL, index, 0);
+ }
+
+ LPARAM GetSelection()
+ {
+ return SendMessage(cbHwnd, CB_GETCURSEL, 0, 0);
+ }
+
+ LRESULT SelectString(const wchar_t *str)
+ {
+ return SendMessageW(cbHwnd, CB_SELECTSTRING, (WPARAM)-1, (LPARAM)str);
+ }
+
+ LRESULT GetTextLen(int index)
+ {
+ return SendMessageW(cbHwnd, CB_GETLBTEXTLEN, (WPARAM)index, 0);
+ }
+
+ void GetText(int index, wchar_t *str)
+ {
+ SendMessageW(cbHwnd, CB_GETLBTEXT, (WPARAM)index, (LPARAM)str);
+ }
+#if (_WIN32_WINNT >= 0x0501)
+ void SetCueBanner(const wchar_t *str)
+ {
+ SendMessageW(cbHwnd, CB_SETCUEBANNER, 0, (LPARAM)str);
+ //CB_SETCUEBANNER;
+ }
+#endif
+
+ void SetEditText(const wchar_t *str)
+ {
+ SendMessageW(cbHwnd, WM_SETTEXT, 0, (LPARAM)str);
+ }
+
+ unsigned int GetEditText(wchar_t *str, unsigned int cch)
+ {
+ return (unsigned int)SendMessageW(cbHwnd, WM_GETTEXT, (WPARAM)cch, (LPARAM)str);
+ }
+
+
+ HWND cbHwnd;
+};
+
+#endif \ No newline at end of file
diff --git a/Src/nu/Config.h b/Src/nu/Config.h
new file mode 100644
index 00000000..ddc992de
--- /dev/null
+++ b/Src/nu/Config.h
@@ -0,0 +1,188 @@
+#ifndef NULLSOFT_UTILITY_CONFIGH
+#define NULLSOFT_UTILITY_CONFIGH
+#include <string>
+#include <map>
+#include <windows.h>
+
+typedef std::wstring tstring;
+
+namespace Nullsoft
+{
+ namespace Utility
+ {
+
+ class ConfigItemBase
+ {
+ public:
+ ConfigItemBase(const wchar_t *_appName, const wchar_t * _fileName, LPCTSTR _keyName)
+ : appName(nullptr), fileName(nullptr), keyName(nullptr)
+ {
+ appName = _appName;
+ fileName = _fileName;
+ keyName = _keyName;
+ }
+
+ ~ConfigItemBase()
+ {
+
+ }
+
+ const wchar_t *appName;
+ const wchar_t *fileName;
+ const wchar_t *keyName;
+ };
+
+ template <class config_t>
+ class ConfigItem : public ConfigItemBase
+ {
+ public:
+ ConfigItem(const wchar_t *_appName, const wchar_t * _fileName, LPCTSTR _keyName) : ConfigItemBase(_appName, _fileName, _keyName)
+ {
+ }
+
+ ~ConfigItem() {}
+
+ void operator =(config_t input)
+ {
+ WritePrivateProfileStruct(appName,
+ keyName,
+ (void *) & input,
+ sizeof(input),
+ fileName);
+ }
+
+ operator config_t()
+ {
+ config_t temp;
+
+ memset(&temp, 0, sizeof(temp));
+ GetPrivateProfileStruct(appName,
+ keyName,
+ &temp,
+ sizeof(temp),
+ fileName);
+ return temp;
+ }
+ };
+
+
+ template <>
+ class ConfigItem<TCHAR *> : public ConfigItemBase
+ {
+ public:
+ ConfigItem(const wchar_t *_appName, const wchar_t * _fileName, LPCTSTR _keyName) : ConfigItemBase(_appName, _fileName, _keyName)
+ {
+ }
+
+ ~ConfigItem(){}
+
+ void operator =(LPCTSTR input)
+ {
+ WritePrivateProfileString(appName,
+ keyName,
+ input,
+ fileName);
+ }
+ void GetString(LPTSTR str, size_t len)
+ {
+ GetPrivateProfileString(appName, keyName, TEXT(""), str, len, fileName);
+ }
+ };
+
+ template <>
+ class ConfigItem<int> : public ConfigItemBase
+ {
+ public:
+ ConfigItem(const wchar_t *_appName, const wchar_t * _fileName, LPCTSTR _keyName) : ConfigItemBase(_appName, _fileName, _keyName), def(0)
+ {
+ }
+
+ ~ConfigItem() {}
+
+ void operator =(int input)
+ {
+ TCHAR tmp[(sizeof(int) / 2) * 5 + 1]; // enough room to hold for 16,32 or 64 bit ints, plus null terminator
+ wsprintf(tmp, TEXT("%d"), input);
+ WritePrivateProfileString(appName,
+ keyName,
+ tmp,
+ fileName);
+ }
+
+ operator int ()
+ {
+ return GetPrivateProfileInt(appName, keyName, def, fileName);
+ }
+
+ void SetDefault(int _def)
+ {
+ def = _def;
+ }
+ int def;
+ };
+
+ class Config
+ {
+ public:
+ Config() : appName(nullptr),fileName(nullptr)
+ {
+ }
+
+ ~Config()
+ {
+ if (appName != nullptr)
+ {
+ free(appName);
+ appName = nullptr;
+ }
+ if (fileName != nullptr)
+ {
+ free(fileName);
+ fileName = nullptr;
+ }
+ }
+
+ void SetFile(LPCTSTR iniFile, LPCTSTR _appName)
+ {
+ if (appName != nullptr)
+ {
+ free(appName);
+ appName = nullptr;
+ }
+ if (fileName != nullptr)
+ {
+ free(fileName);
+ fileName = nullptr;
+ }
+ appName = _wcsdup(_appName);
+ fileName = _wcsdup(iniFile);
+ }
+
+ ConfigItem<int> cfg_int(LPCTSTR keyName, int def)
+ {
+ ConfigItem<int> item(appName, fileName, keyName);
+ item.SetDefault(def);
+ return item;
+ }
+
+ ConfigItem<TCHAR *> cfg_str(LPCTSTR keyName)
+ {
+ return ConfigItem<TCHAR *>(appName, fileName, keyName);
+ }
+
+ ConfigItem<GUID> cfg_guid(LPCTSTR keyName)
+ {
+ return ConfigItem<GUID>(appName, fileName, keyName);
+
+ }
+ ConfigItem<__int64> cfg_int64(LPCTSTR keyName)
+ {
+ ConfigItem<__int64> item(appName, fileName, keyName);
+ return item;
+ }
+
+ wchar_t *appName, *fileName;
+ };
+ }
+}
+#endif
diff --git a/Src/nu/ConfigCOM.cpp b/Src/nu/ConfigCOM.cpp
new file mode 100644
index 00000000..3c3dfea8
--- /dev/null
+++ b/Src/nu/ConfigCOM.cpp
@@ -0,0 +1,373 @@
+#include "ConfigCOM.h"
+#include "AutoChar.h"
+#include "../Winamp/JSAPI.h"
+
+#include <shlwapi.h>
+#include <strsafe.h>
+
+#define CSTR_INVARIANT MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT)
+
+
+ConfigCOM::ConfigCOM()
+ : ref(1), index(31337), pathA(NULL), nameA(NULL)
+{
+}
+
+ConfigCOM::~ConfigCOM()
+{
+ ConfigMap::iterator config_it;
+ for(config_it = config_map.begin(); config_it != config_map.end(); config_it++)
+ {
+ free(config_it->second);
+ }
+ config_map.clear();
+ if (NULL != pathA) free(pathA);
+ if (NULL != nameA) free(nameA);
+}
+
+HRESULT ConfigCOM::CreateInstanceW(const wchar_t *pszName, const char *pszPath, ConfigCOM **config)
+{
+ if (NULL == config) return E_POINTER;
+ *config = NULL;
+
+ if (NULL == pszName)
+ return E_INVALIDARG;
+
+
+ *config = new ConfigCOM();
+ if (NULL == *config) return E_OUTOFMEMORY;
+
+ char *buffer = NULL;
+ int cbBuffer = WideCharToMultiByte(CP_UTF8, 0, pszName, -1, 0, 0, NULL, NULL);
+ if (0 != cbBuffer)
+ {
+ buffer = (char*)calloc(cbBuffer, sizeof(char));
+ if (NULL != buffer && 0 == WideCharToMultiByte(CP_UTF8, 0, pszName, -1, buffer, cbBuffer, NULL, NULL))
+ {
+ free(buffer);
+ buffer = NULL;
+ }
+ }
+
+ if (NULL == buffer)
+ {
+ (*config)->Release();
+ *config = NULL;
+ return E_OUTOFMEMORY;
+ }
+
+ (*config)->nameA = buffer;
+ if (NULL != pszPath)
+ (*config)->SetPathA(pszPath);
+ return S_OK;
+
+}
+
+HRESULT ConfigCOM::CreateInstanceA(const char *pszName, const char *pszPath, ConfigCOM **config)
+{
+ if (NULL == config) return E_POINTER;
+ *config = NULL;
+
+ if (NULL == pszName)
+ return E_INVALIDARG;
+
+
+ *config = new ConfigCOM();
+ if (NULL == *config) return E_OUTOFMEMORY;
+
+ char *nameA = _strdup(pszName);
+ if (NULL == nameA)
+ {
+ (*config)->Release();
+ *config = NULL;
+ return E_OUTOFMEMORY;
+ }
+
+ (*config)->nameA = nameA;
+ if (NULL != pszPath)
+ (*config)->SetPathA(pszPath);
+
+ return S_OK;
+}
+
+STDMETHODIMP_(ULONG) ConfigCOM::AddRef(void)
+{
+ return InterlockedIncrement((LONG*)&ref);
+}
+
+STDMETHODIMP_(ULONG) ConfigCOM::Release(void)
+{
+ if (0 == ref)
+ return ref;
+
+ LONG r = InterlockedDecrement((LONG*)&ref);
+ if (0 == r)
+ delete(this);
+
+ return r;
+}
+STDMETHODIMP ConfigCOM::QueryInterface(REFIID riid, PVOID *ppvObject)
+{
+ if (!ppvObject)
+ return E_POINTER;
+
+ else if (IsEqualIID(riid, IID_IDispatch))
+ *ppvObject = (IDispatch *)this;
+ else if (IsEqualIID(riid, IID_IUnknown))
+ *ppvObject = this;
+ else
+ {
+ *ppvObject = NULL;
+ return E_NOINTERFACE;
+ }
+
+ AddRef();
+ return S_OK;
+}
+
+STDMETHODIMP ConfigCOM::GetIDsOfNames(REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames, LCID lcid, DISPID FAR* rgdispid)
+{
+ UNREFERENCED_PARAMETER(riid);
+
+ bool unknowns = false;
+ for (unsigned int i = 0;i != cNames;i++)
+ {
+ bool found = false;
+ AutoChar item(rgszNames[i], CP_UTF8);
+
+ ConfigMap::iterator config_it;
+ for(config_it = config_map.begin();config_it != config_map.end(); config_it++)
+ {
+ if (config_it->second &&
+ CSTR_EQUAL == CompareStringA(lcid, NORM_IGNORECASE, config_it->second, -1, item, -1))
+ {
+ found = true;
+ rgdispid[i] = config_it->first;
+ }
+ }
+ if (!found) // if they reference a config item, well by golly they want that config item.
+ {
+ config_map[++index] = _strdup(item);
+ rgdispid[i] = index;
+ }
+
+ }
+ if (unknowns)
+ return DISP_E_UNKNOWNNAME;
+
+ return S_OK;
+}
+
+STDMETHODIMP ConfigCOM::GetTypeInfo(unsigned int itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo)
+{
+ UNREFERENCED_PARAMETER(itinfo);
+ UNREFERENCED_PARAMETER(lcid);
+ UNREFERENCED_PARAMETER(pptinfo);
+
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP ConfigCOM::GetTypeInfoCount(unsigned int FAR * pctinfo)
+{
+ UNREFERENCED_PARAMETER(pctinfo);
+
+ return E_NOTIMPL;
+}
+
+
+STDMETHODIMP ConfigCOM::Invoke(DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, EXCEPINFO FAR * pexecinfo, unsigned int FAR *puArgErr)
+{
+ UNREFERENCED_PARAMETER(riid);
+ UNREFERENCED_PARAMETER(lcid);
+ UNREFERENCED_PARAMETER(pexecinfo);
+
+ ConfigMap::iterator config_it = config_map.find(dispid);
+ if (config_it == config_map.end())
+ return DISP_E_MEMBERNOTFOUND;
+
+ if (0 != (DISPATCH_PROPERTYPUT & wFlags))
+ {
+ VARIANTARG *varArg;
+ JSAPI_VERIFY_PARAMCOUNT(pdispparams, 1);
+
+ varArg = &pdispparams->rgvarg[0];
+ switch(V_VT(varArg))
+ {
+ case VT_BSTR:
+ WriteString(config_it->second, V_BSTR(varArg));
+ break;
+
+ case VT_BOOL:
+ {
+ BOOL boolVal;
+ switch(V_BOOL(varArg))
+ {
+ case VARIANT_TRUE:
+ boolVal = TRUE;
+ break;
+ case VARIANT_FALSE:
+ boolVal = FALSE;
+ break;
+ default:
+ *puArgErr = 0;
+ return DISP_E_BADVARTYPE;
+ }
+
+ WriteBool(config_it->second, boolVal);
+ }
+ break;
+
+ case VT_I4:
+ WriteLong(config_it->second, V_I4(varArg));
+ break;
+ default:
+ *puArgErr = 0;
+ return DISP_E_TYPEMISMATCH;
+ }
+ return S_OK;
+ }
+
+ if (0 != (DISPATCH_PROPERTYGET & wFlags))
+ {
+ JSAPI_VERIFY_PARAMCOUNT(pdispparams, 0);
+ if (NULL == pvarResult)
+ return DISP_E_PARAMNOTOPTIONAL;
+
+ VariantInit(pvarResult);
+ BSTR tag = ReadBSTR(config_it->second, NULL);
+ if (NULL != tag)
+ {
+ V_VT(pvarResult) = VT_BSTR;
+ V_BSTR(pvarResult) = tag;
+ }
+ else
+ {
+ V_VT(pvarResult) = VT_NULL;
+ }
+
+ return S_OK;
+ }
+
+ return ResultFromScode(E_INVALIDARG);
+}
+BOOL ConfigCOM::WriteStringAnsi(const char *key, const char* string)
+{
+ return WritePrivateProfileStringA(nameA, key, string, pathA);
+}
+BOOL ConfigCOM::WriteString(const char *key, const wchar_t *string)
+{
+ AutoChar buffer(string, CP_UTF8);
+ return WriteStringAnsi(key, buffer);
+}
+
+BOOL ConfigCOM::WriteBool(const char *key, BOOL value)
+{
+ return WriteStringAnsi(key, (FALSE != value) ? "true" : "false");
+}
+
+BOOL ConfigCOM::WriteLong(const char *key, long value)
+{
+ char item[64] = {0};
+ if (FAILED(StringCchPrintfA(item, ARRAYSIZE(item), "%ld", value)))
+ return FALSE;
+ return WriteStringAnsi(key, item);
+}
+
+DWORD ConfigCOM::ReadString(const char *key, const char *defaultVal, char *buffer, int bufferMax)
+{
+ return GetPrivateProfileStringA(nameA, key, defaultVal, buffer, bufferMax, pathA);
+}
+
+LONG ConfigCOM::ReadLong(const char *key, long defaultVal)
+{
+ return GetPrivateProfileIntA(nameA, key, defaultVal, pathA);
+}
+
+BOOL ConfigCOM::ReadBool(const char *key, BOOL defaultVal)
+{
+ char szBuffer[32] = {0};
+ INT cchLen = ReadString(key, NULL, szBuffer, ARRAYSIZE(szBuffer));
+ if (0 == cchLen) return defaultVal;
+
+ if (1 == cchLen)
+ {
+ switch(*szBuffer)
+ {
+ case '0':
+ case 'n':
+ case 'f':
+ return FALSE;
+ case '1':
+ case 'y':
+ case 't':
+ return TRUE;
+ }
+ }
+ else
+ {
+ if (CSTR_EQUAL == CompareStringA(CSTR_INVARIANT, NORM_IGNORECASE, "yes", -1, szBuffer, cchLen) ||
+ CSTR_EQUAL == CompareStringA(CSTR_INVARIANT, NORM_IGNORECASE, "true", -1, szBuffer, cchLen))
+ {
+ return TRUE;
+ }
+
+ if (CSTR_EQUAL == CompareStringA(CSTR_INVARIANT, NORM_IGNORECASE, "no", -1, szBuffer, cchLen) ||
+ CSTR_EQUAL == CompareStringA(CSTR_INVARIANT, NORM_IGNORECASE, "false", -1, szBuffer, cchLen))
+ {
+ return FALSE;
+ }
+ }
+
+ INT v;
+ if (FALSE != StrToIntExA(szBuffer, STIF_SUPPORT_HEX, &v))
+ return (0 != v);
+
+ return defaultVal;
+}
+
+BSTR ConfigCOM::ReadBSTR(const char *key, const wchar_t *defaultVal)
+{
+ char szBuffer[16384] = {0};
+
+ ReadString(key, "__emptee__", szBuffer, ARRAYSIZE(szBuffer));
+ if (CSTR_EQUAL != CompareStringA(CSTR_INVARIANT, 0, szBuffer, -1, "__emptee__", -1))
+ {
+ int size = MultiByteToWideChar(CP_UTF8, 0, szBuffer, -1, 0,0);
+ if (0 != size)
+ {
+ BSTR result;
+ result = SysAllocStringLen(0, size-1);
+ if (NULL == result) return NULL;
+
+ if (0 != MultiByteToWideChar(CP_UTF8, 0, szBuffer, -1, result, size))
+ return result;
+
+ SysFreeString(result);
+ }
+ }
+ return (NULL != defaultVal) ? SysAllocString(defaultVal) : NULL;
+}
+
+void ConfigCOM::SetPathA(const char *pszPath)
+{
+ if (NULL != pathA)
+ {
+ free(pathA);
+ pathA = NULL;
+ }
+
+ if (NULL == pszPath)
+ {
+ pathA = NULL;
+ return;
+ }
+
+ pathA = _strdup(pszPath);
+}
+
+
+BOOL ConfigCOM::IsEqual(const char *pszName)
+{
+ if (NULL == pszName) return FALSE;
+ return (CSTR_EQUAL == CompareStringA(CSTR_INVARIANT, 0, nameA, -1, pszName, -1));
+} \ No newline at end of file
diff --git a/Src/nu/ConfigCOM.h b/Src/nu/ConfigCOM.h
new file mode 100644
index 00000000..a4bc5444
--- /dev/null
+++ b/Src/nu/ConfigCOM.h
@@ -0,0 +1,51 @@
+#ifndef NULLSOFT_NU_CONFIGCOMH
+#define NULLSOFT_NU_CONFIGCOMH
+
+#include <ocidl.h>
+#include <map>
+
+class ConfigCOM : public IDispatch
+{
+protected:
+ ConfigCOM();
+ ~ConfigCOM();
+
+public:
+ static HRESULT CreateInstanceW(const wchar_t *pszName, const char *pszPath, ConfigCOM **config);
+ static HRESULT CreateInstanceA(const char *pszName, const char *pszPath, ConfigCOM **config);
+
+public:
+ /* IUnknown */
+ STDMETHOD(QueryInterface)(REFIID riid, PVOID *ppvObject);
+ STDMETHOD_(ULONG, AddRef)(void);
+ STDMETHOD_(ULONG, Release)(void);
+
+ /* IDispatch */
+ STDMETHOD (GetIDsOfNames)(REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames, LCID lcid, DISPID FAR* rgdispid);
+ STDMETHOD (GetTypeInfo)(unsigned int itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo);
+ STDMETHOD (GetTypeInfoCount)(unsigned int FAR * pctinfo);
+ STDMETHOD (Invoke)(DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, EXCEPINFO FAR * pexecinfo, unsigned int FAR *puArgErr);
+
+ void SetPathA(const char *pszPath);
+ BOOL IsEqual(const char *pszName);
+
+ BOOL WriteStringAnsi(const char *key, const char *string);
+ BOOL WriteString(const char *key, const wchar_t *string);
+ BOOL WriteBool(const char *key, BOOL value);
+ BOOL WriteLong(const char *key, long value);
+
+ DWORD ReadString(const char *key, const char *defaultVal, char *buffer, int bufferMax);
+ LONG ReadLong(const char *key, long defaultVal);
+ BOOL ReadBool(const char *key, BOOL defaultVal);
+ BSTR ReadBSTR(const char *key, const wchar_t *defaultVal);
+
+private:
+ typedef std::map<long, char*> ConfigMap;
+ ConfigMap config_map;
+ long index;
+ char *pathA;
+ char *nameA;
+ LONG ref;
+};
+
+#endif \ No newline at end of file
diff --git a/Src/nu/DialogSkinner.cpp b/Src/nu/DialogSkinner.cpp
new file mode 100644
index 00000000..7f1d8996
--- /dev/null
+++ b/Src/nu/DialogSkinner.cpp
@@ -0,0 +1,8 @@
+#include "DialogSkinner.h"
+
+DialogSkinner dialogSkinner;
+
+COLORREF GetHTMLColor(int color)
+{
+ return ( ( color >> 16 ) & 0xff | ( color & 0xff00 ) | ( ( color << 16 ) & 0xff0000 ) );
+} \ No newline at end of file
diff --git a/Src/nu/DialogSkinner.h b/Src/nu/DialogSkinner.h
new file mode 100644
index 00000000..425f2108
--- /dev/null
+++ b/Src/nu/DialogSkinner.h
@@ -0,0 +1,78 @@
+#ifndef DIALOGSKINNERH
+#define DIALOGSKINNERH
+
+#include "MediaLibraryInterface.h"
+#include "../winamp/wa_dlg.h"
+
+COLORREF GetHTMLColor( int color );
+
+class DialogSkinner
+{
+ typedef HBITMAP( *BitmapFunc )( );
+ typedef int ( *ColorFunc )( int idx ); // pass this an index, returns a RGB value (passing 0 or > 3 returns NULL)
+ typedef int ( *HandleFunc )( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam );
+ typedef void ( *DrawFunc )( HWND hwndDlg, int *tab, int tabsize ); // each entry in tab would be the id | DCW_*
+
+public:
+ DialogSkinner()
+ {}
+
+ int Color( int index )
+ {
+ if ( !color )
+ color = (ColorFunc)mediaLibrary.GetWADLGFunc( 1 );
+
+ return color( index );
+ }
+
+ RGBQUAD GetRGB( int index )
+ {
+ COLORREF color = Color( index );
+
+ RGBQUAD rgb;
+ rgb.rgbReserved = 0;
+ rgb.rgbBlue = GetBValue( color );
+ rgb.rgbGreen = GetGValue( color );
+ rgb.rgbRed = GetRValue( color );
+
+ return rgb;
+ }
+
+ INT_PTR Handle( HWND dlg, UINT msg, WPARAM wParam, LPARAM lParam )
+ {
+ if ( !handle )
+ handle = (HandleFunc)mediaLibrary.GetWADLGFunc( 2 );
+
+ return handle( dlg, msg, wParam, lParam );
+ }
+
+ void Draw( HWND dlg, int *tab, int tabSize )
+ {
+ if ( !draw )
+ draw = (DrawFunc)mediaLibrary.GetWADLGFunc( 3 );
+
+ draw( dlg, tab, tabSize );
+ }
+
+ HFONT GetFont()
+ {
+ return (HFONT)mediaLibrary.GetWADLGFunc( 66 );
+ }
+
+ HBITMAP GetBitmap()
+ {
+ if ( !bitmap )
+ bitmap = (BitmapFunc)mediaLibrary.GetWADLGFunc( 4 );
+
+ return bitmap();
+ }
+
+ ColorFunc color = 0;
+ HandleFunc handle = 0;
+ DrawFunc draw = 0;
+ BitmapFunc bitmap = 0;
+};
+
+extern DialogSkinner dialogSkinner;
+
+#endif
diff --git a/Src/nu/GaplessRingBuffer.cpp b/Src/nu/GaplessRingBuffer.cpp
new file mode 100644
index 00000000..6628cddc
--- /dev/null
+++ b/Src/nu/GaplessRingBuffer.cpp
@@ -0,0 +1,64 @@
+#include "GaplessRingBuffer.h"
+#include <bfc/platform/types.h>
+#include <bfc/error.h>
+#include <algorithm>
+
+GaplessRingBuffer::GaplessRingBuffer()
+{
+ pregapBytes = 0;
+ frameBytes = 0;
+ postgapBytes = 0;
+ currentPregapBytes = 0;
+}
+
+GaplessRingBuffer::~GaplessRingBuffer()
+{
+}
+
+int GaplessRingBuffer::Initialize(size_t samples, size_t bps, size_t channels, size_t pregap, size_t postgap)
+{
+ this->frameBytes = channels * bps / 8;
+ this->currentPregapBytes = this->pregapBytes = pregap * frameBytes;
+ this->postgapBytes = postgap * frameBytes;
+
+ ring_buffer.reserve(samples * frameBytes + pregapBytes);
+ return NErr_Success;
+}
+
+bool GaplessRingBuffer::Empty() const
+{
+ return (ring_buffer.size() <= pregapBytes);
+}
+
+size_t GaplessRingBuffer::Read(void *destination, size_t destination_bytes)
+{
+ // make sure we've filled enough of the buffer to satisfy the postgap
+ if (Empty()) {
+ return 0;
+ }
+
+ // don't read into postgap area
+ size_t remaining = ring_buffer.size() - postgapBytes;
+ destination_bytes = min(remaining, destination_bytes);
+
+ return ring_buffer.read(destination, destination_bytes);
+}
+
+size_t GaplessRingBuffer::Write(const void *input, size_t input_bytes)
+{
+ // cut pregap if necessary
+ if (currentPregapBytes) {
+ size_t cut = min(input_bytes, currentPregapBytes);
+ currentPregapBytes -= cut;
+ input_bytes -= cut;
+ input = (const uint8_t *)input + cut;
+ }
+
+ return ring_buffer.write(input, input_bytes);
+}
+
+void GaplessRingBuffer::Reset()
+{
+ currentPregapBytes = pregapBytes;
+ ring_buffer.clear();
+} \ No newline at end of file
diff --git a/Src/nu/GaplessRingBuffer.h b/Src/nu/GaplessRingBuffer.h
new file mode 100644
index 00000000..ca4abd7d
--- /dev/null
+++ b/Src/nu/GaplessRingBuffer.h
@@ -0,0 +1,20 @@
+#pragma once
+#include "RingBuffer.h"
+
+class GaplessRingBuffer
+{
+public:
+ GaplessRingBuffer();
+ ~GaplessRingBuffer();
+ int Initialize(size_t samples, size_t bps, size_t channels, size_t pregap, size_t postgap);
+ size_t Read(void *destination, size_t destination_bytes);
+ bool Empty() const;
+ size_t Write(const void *input, size_t input_bytes);
+ void Reset();
+private:
+ RingBuffer ring_buffer;
+ size_t frameBytes; // byte size of one frame (channels*bps/8)
+ size_t currentPregapBytes;
+ size_t pregapBytes;
+ size_t postgapBytes;
+}; \ No newline at end of file
diff --git a/Src/nu/GrowBuf.h b/Src/nu/GrowBuf.h
new file mode 100644
index 00000000..e6084755
--- /dev/null
+++ b/Src/nu/GrowBuf.h
@@ -0,0 +1,93 @@
+#ifndef NULLSOFT_UTILITY_GROWBUF_H
+#define NULLSOFT_UTILITY_GROWBUF_H
+
+#include <memory.h>
+#include <stdlib.h>
+class GrowBuf
+{
+public:
+ GrowBuf() {}
+
+ ~GrowBuf()
+ {
+ if ( m_s )
+ free( m_s );
+
+ m_s = NULL;
+ }
+
+ void reserve( size_t len )
+ {
+ if ( len > m_alloc )
+ {
+ void *ne;
+ m_alloc = len;
+ ne = realloc( m_s, m_alloc );
+ if ( !ne )
+ {
+ ne = malloc( m_alloc );
+ memcpy( ne, m_s, m_used );
+ free( m_s );
+ }
+ m_s = ne;
+ }
+ }
+
+ size_t add( void *data, size_t len )
+ {
+ if ( !len )
+ return 0;
+
+ resize( m_used + len );
+ memcpy( (char *)get() + m_used - len, data, len );
+
+ return m_used - len;
+ }
+
+ void set( void *data, size_t len )
+ {
+ resize( len );
+ memcpy( (char *)get(), data, len );
+ }
+
+ void resize( size_t newlen )
+ {
+ m_used = newlen;
+ if ( newlen > m_alloc )
+ {
+ m_alloc = newlen * 2;
+ if ( m_alloc < 1024 )
+ m_alloc = 1024;
+
+ void *ne = realloc( m_s, m_alloc );
+ if ( !ne )
+ {
+ ne = malloc( m_alloc );
+ if ( !ne )
+ *( (char *)ne ) = NULL;
+
+ memcpy( ne, m_s, m_used );
+ free( m_s );
+ }
+
+ m_s = ne;
+ }
+ }
+
+ size_t getlen()
+ {
+ return m_used;
+ }
+
+ void *get()
+ {
+ return m_s;
+ }
+
+private:
+ void *m_s = NULL;
+ size_t m_alloc = 0;
+ size_t m_used = 0;
+};
+
+#endif \ No newline at end of file
diff --git a/Src/nu/HTMLContainer.cpp b/Src/nu/HTMLContainer.cpp
new file mode 100644
index 00000000..9591ac3a
--- /dev/null
+++ b/Src/nu/HTMLContainer.cpp
@@ -0,0 +1,839 @@
+#include "HTMLContainer.h"
+
+#include <exdisp.h>
+#include <mshtmdid.h>
+#include <mshtml.h>
+#include <exdispid.h>
+#include <strsafe.h>
+
+#ifndef DISPID_NEWWINDOW3
+#define DISPID_NEWWINDOW3 273
+#endif
+
+// ---------------------------------------------------------------
+IConnectionPoint *HTMLContainer::GetConnectionPoint (REFIID riid)
+{
+ IUnknown *punk = getUnknown ();
+ if (!punk)
+ return 0;
+
+ IConnectionPointContainer *pcpc;
+ IConnectionPoint *pcp = 0;
+
+ HRESULT hr = punk->QueryInterface (IID_IConnectionPointContainer, (void **) & pcpc);
+ if (SUCCEEDED (hr))
+ {
+ pcpc->FindConnectionPoint (riid, &pcp);
+ pcpc->Release();
+ }
+ punk->Release();
+ return pcp;
+}
+
+void HTMLContainer::SyncSizeToWindow(HWND hwnd)
+{
+ RECT rect;
+ GetWindowRect(hwnd, &rect);
+ int height = (rect.bottom - rect.top);
+
+ // if we get a null height then hide the html control (after limiting to 1px)
+ // and also hide it's parent window - is mainly for ml_wire to prevent display
+ // glitches when resizing the bottom segment all the way to the bottom
+ ShowWindow(m_hwnd,height?SW_SHOWNA:SW_HIDE);
+ ShowWindow(hwnd,height?SW_SHOWNA:SW_HIDE);
+ setLocation(0, 0, rect.right - rect.left, height?height:1);
+}
+
+// uncomment if you ever want to use mozilla instead of IE
+// change the CLSID_WebBrowser in the constructor below to CLSID_MozillaBrowser
+// but window.external from javascript doesn't work :(
+
+static const CLSID CLSID_MozillaBrowser=
+ { 0x1339B54C, 0x3453, 0x11D2, { 0x93, 0xB9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } };
+
+ HTMLContainer::HTMLContainer(HWND hwnd)
+ : m_pweb (0), pszHostCSS(NULL), m_cRefs(1), m_hwnd(hwnd), m_punk(NULL)
+ {
+ bInitialized = (S_OK == CoInitializeEx(NULL, COINIT_APARTMENTTHREADED)) ? true : false;
+ dwHostInfoFlags = DOCHOSTUIFLAG_NO3DOUTERBORDER | DOCHOSTUIFLAG_ENABLE_INPLACE_NAVIGATION | DOCHOSTUIFLAG_NO3DBORDER | DOCHOSTUIDBLCLK_DEFAULT;
+ dwDownloadFlags = DLCTL_DLIMAGES | DLCTL_VIDEOS | DLCTL_PRAGMA_NO_CACHE;
+
+ memset(&m_rect, 0, sizeof(m_rect));
+ add(CLSID_WebBrowser);
+
+ IUnknown *punk = getUnknown();
+ if (punk)
+ {
+ if (SUCCEEDED(punk->QueryInterface (IID_IWebBrowser2, (void **) & m_pweb))
+ || SUCCEEDED(punk->QueryInterface (IID_IWebBrowser, (void **) & m_pweb)))
+ {
+ IConnectionPoint *icp = GetConnectionPoint(DIID_DWebBrowserEvents2);
+ if (icp)
+ {
+ m_dwCookie = 0;
+ icp->Advise(static_cast<IDispatch *>(this), &m_dwCookie);
+ icp->Release();
+ }
+ }
+ else
+ m_pweb=0;
+ punk->Release();
+ }
+}
+
+HTMLContainer::HTMLContainer()
+ : m_pweb (0), pszHostCSS(NULL), m_cRefs(1), m_hwnd(NULL), m_punk(NULL)
+{
+ bInitialized = (S_OK == CoInitializeEx(NULL, COINIT_APARTMENTTHREADED)) ? true : false;
+ dwHostInfoFlags = DOCHOSTUIFLAG_NO3DOUTERBORDER | DOCHOSTUIFLAG_ENABLE_INPLACE_NAVIGATION | DOCHOSTUIFLAG_NO3DBORDER | DOCHOSTUIDBLCLK_DEFAULT;
+ dwDownloadFlags = DLCTL_DLIMAGES | DLCTL_VIDEOS | DLCTL_PRAGMA_NO_CACHE;
+
+ memset(&m_rect, 0, sizeof(m_rect));
+ add(CLSID_WebBrowser);
+
+ IUnknown *punk = getUnknown();
+ if (punk)
+ {
+ if (SUCCEEDED(punk->QueryInterface (IID_IWebBrowser2, (void **) & m_pweb))
+ || SUCCEEDED(punk->QueryInterface (IID_IWebBrowser, (void **) & m_pweb)))
+ {
+ IConnectionPoint *icp = GetConnectionPoint(DIID_DWebBrowserEvents2);
+ if (icp)
+ {
+ m_dwCookie = 0;
+ icp->Advise(static_cast<IDispatch *>(this), &m_dwCookie);
+ icp->Release();
+ }
+ }
+ else
+ m_pweb=0;
+ punk->Release();
+ }
+}
+
+HTMLContainer::~HTMLContainer()
+{
+ close();
+ if (pszHostCSS) { free(pszHostCSS); pszHostCSS = NULL; }
+
+ if (bInitialized) CoUninitialize();
+}
+
+void HTMLContainer::close()
+{
+ IOleObject *pioo;
+ if ( m_punk )
+ {
+ HRESULT hr = m_punk->QueryInterface(IID_IOleObject, (PVOID *) & pioo);
+ if (SUCCEEDED(hr))
+ {
+ pioo->Close(OLECLOSE_NOSAVE);
+ pioo->Release();
+ }
+ }
+
+ if (m_punk)
+ {
+ m_punk->Release();
+ m_punk = NULL;
+ }
+
+ if (m_pweb)
+ {
+ m_pweb->Quit();
+ m_pweb->Release();
+ m_pweb = 0;
+ }
+}
+
+STDMETHODIMP HTMLContainer::QueryInterface(REFIID riid, PVOID *ppvObject)
+{
+ if (!ppvObject)
+ return E_POINTER;
+
+ if (IsEqualIID(riid, IID_IOleClientSite))
+ *ppvObject = (IOleClientSite *)this;
+ else if (IsEqualIID(riid, IID_IOleInPlaceSite))
+ *ppvObject = (IOleInPlaceSite *)this;
+ else if (IsEqualIID(riid, IID_IOleInPlaceFrame))
+ *ppvObject = (IOleInPlaceFrame *)this;
+ else if (IsEqualIID(riid, IID_IOleInPlaceUIWindow))
+ *ppvObject = (IOleInPlaceUIWindow *)this;
+ else if (IsEqualIID(riid, IID_IOleControlSite))
+ *ppvObject = (IOleControlSite *)this;
+ else if (IsEqualIID(riid, IID_IOleWindow))
+ *ppvObject = this;
+ else if (IsEqualIID(riid, IID_IDispatch))
+ *ppvObject = (IDispatch *)this;
+ else if (IsEqualIID(riid, IID_IUnknown))
+ *ppvObject = this;
+ else if (IsEqualIID(riid, IID_IDocHostUIHandler))
+ *ppvObject = (IDocHostUIHandler *)this;
+ else
+ {
+ *ppvObject = NULL;
+ return E_NOINTERFACE;
+ }
+
+ AddRef();
+ return S_OK;
+}
+
+ULONG HTMLContainer::AddRef(void)
+{
+ return ++m_cRefs;
+}
+
+ULONG HTMLContainer::Release(void)
+{
+ if (--m_cRefs)
+ return m_cRefs;
+ return 0;
+}
+
+HRESULT HTMLContainer::SaveObject()
+{
+ return E_NOTIMPL;
+}
+
+HRESULT HTMLContainer::GetMoniker(DWORD dwAssign, DWORD dwWhichMoniker, LPMONIKER * ppMk)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT HTMLContainer::GetContainer(LPOLECONTAINER * ppContainer)
+{
+ return E_NOINTERFACE;
+}
+
+HRESULT HTMLContainer::ShowObject()
+{
+ return S_OK;
+}
+
+HRESULT HTMLContainer::OnShowWindow(BOOL fShow)
+{
+ return S_OK;
+}
+
+HRESULT HTMLContainer::RequestNewObjectLayout()
+{
+ return E_NOTIMPL;
+}
+
+HRESULT HTMLContainer::GetWindow(HWND * lphwnd)
+{
+ if (!IsWindow(m_hwnd))
+ return S_FALSE;
+
+ *lphwnd = m_hwnd;
+ return S_OK;
+}
+
+HRESULT HTMLContainer::ContextSensitiveHelp(BOOL fEnterMode)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT HTMLContainer::CanInPlaceActivate(void)
+{
+ return S_OK;
+}
+
+HRESULT HTMLContainer::OnInPlaceActivate(void)
+{
+ return S_OK;
+}
+
+HRESULT HTMLContainer::OnUIActivate(void)
+{
+ return S_OK;
+}
+
+HRESULT HTMLContainer::GetWindowContext(IOleInPlaceFrame ** ppFrame, IOleInPlaceUIWindow ** ppIIPUIWin,
+ LPRECT lprcPosRect, LPRECT lprcClipRect, LPOLEINPLACEFRAMEINFO lpFrameInfo)
+{
+ *ppFrame = (IOleInPlaceFrame *)this;
+ *ppIIPUIWin = NULL;
+
+ RECT rect;
+ GetClientRect(m_hwnd, &rect);
+ lprcPosRect->left = 0;
+ lprcPosRect->top = 0;
+ lprcPosRect->right = rect.right;
+ lprcPosRect->bottom = rect.bottom;
+
+ CopyRect(lprcClipRect, lprcPosRect);
+
+ lpFrameInfo->cb = sizeof(OLEINPLACEFRAMEINFO);
+ lpFrameInfo->fMDIApp = FALSE;
+ lpFrameInfo->hwndFrame = m_hwnd;
+ lpFrameInfo->haccel = 0;
+ lpFrameInfo->cAccelEntries = 0;
+
+ (*ppFrame)->AddRef();
+ return S_OK;
+}
+
+HRESULT HTMLContainer::Scroll(SIZE scrollExtent)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT HTMLContainer::OnUIDeactivate(BOOL fUndoable)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT HTMLContainer::OnInPlaceDeactivate(void)
+{
+ return S_OK;
+}
+
+HRESULT HTMLContainer::DiscardUndoState(void)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT HTMLContainer::DeactivateAndUndo(void)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT HTMLContainer::OnPosRectChange(LPCRECT lprcPosRect)
+{
+ return S_OK;
+}
+
+HRESULT HTMLContainer::InsertMenus(HMENU hmenuShared, LPOLEMENUGROUPWIDTHS lpMenuWidths)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT HTMLContainer::SetMenu(HMENU hmenuShared, HOLEMENU holemenu, HWND hwndActiveObject)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT HTMLContainer::RemoveMenus(HMENU hmenuShared)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT HTMLContainer::SetStatusText(LPCOLESTR pszStatusText)
+{
+ return S_OK;
+}
+
+HRESULT HTMLContainer::TranslateAccelerator(LPMSG lpmsg, WORD wID)
+{
+ return S_OK;
+}
+
+HRESULT HTMLContainer::EnableModeless(BOOL fEnable)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT HTMLContainer::OnControlInfoChanged()
+{
+ return E_NOTIMPL;
+}
+
+HRESULT HTMLContainer::LockInPlaceActive(BOOL fLock)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT HTMLContainer::GetExtendedControl(IDispatch **ppDisp)
+{
+ if (ppDisp == NULL)
+ return E_INVALIDARG;
+
+ *ppDisp = (IDispatch *)this;
+ (*ppDisp)->AddRef();
+
+ return S_OK;
+}
+
+HRESULT HTMLContainer::TransformCoords(POINTL *pptlHimetric, POINTF *pptfContainer, DWORD dwFlags)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT HTMLContainer::TranslateAccelerator(LPMSG pMsg, DWORD grfModifiers)
+{
+ return S_FALSE;
+}
+
+HRESULT HTMLContainer::OnFocus(BOOL fGotFocus)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT HTMLContainer::ShowPropertyFrame(void)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT HTMLContainer::GetIDsOfNames(REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames, LCID lcid, DISPID FAR* rgdispid)
+{
+ *rgdispid = DISPID_UNKNOWN;
+ return DISP_E_UNKNOWNNAME;
+}
+
+HRESULT HTMLContainer::GetTypeInfo(unsigned int itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT HTMLContainer::GetTypeInfoCount(unsigned int FAR * pctinfo)
+{
+ return E_NOTIMPL;
+}
+
+void HTMLContainer::OnBeforeNavigate(IDispatch *pDispatch, LPCWSTR pszURL, DWORD dwFlags, LPCWSTR pszTargetFrameName, VARIANT *vtPostData, LPCWSTR pszHeaders, VARIANT_BOOL *Cancel)
+{
+
+}
+
+void HTMLContainer::OnNavigateError(IDispatch *pDispatch, LPCWSTR pszURL, LPCWSTR pszTargetFrameName, INT nStatusCode, VARIANT_BOOL *Cancel)
+{
+}
+
+void HTMLContainer::OnNavigateComplete(IDispatch *pDispatch, LPCWSTR pszURL)
+{
+}
+
+void HTMLContainer::OnDocumentComplete(IDispatch *pDisp, LPCWSTR pszURL)
+{
+}
+
+void HTMLContainer::OnDownloadBegin(void)
+{
+}
+
+void HTMLContainer::OnDownloadComplete(void)
+{
+}
+
+void HTMLContainer::OnFileDownload(VARIANT_BOOL *ActiveDocument, VARIANT_BOOL *Cancel)
+{
+}
+
+void HTMLContainer::OnNewWindow2(IDispatch **ppDisp, VARIANT_BOOL *Cancel)
+{
+}
+
+void HTMLContainer::OnNewWindow3(IDispatch **ppDisp, VARIANT_BOOL *Cancel, DWORD dwFlags, LPCWSTR pszUrlContext, LPCWSTR pszUrl)
+{
+}
+
+void HTMLContainer::OnProgressChange(long Progress, long ProgressMax)
+{
+}
+
+void HTMLContainer::OnStatusTextChange(LPCWSTR pszText)
+{
+}
+
+
+#define GET_SAFE_DISP_BSTR(_val) ((_val.pvarVal && VT_BSTR == _val.pvarVal->vt) ? _val.pvarVal->bstrVal : NULL)
+#define GET_SAFE_DISP_I4(_val) ((_val.pvarVal && VT_I4 == _val.pvarVal->vt) ? _val.pvarVal->intVal : 0)
+
+HRESULT HTMLContainer::Invoke(DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, EXCEPINFO FAR * pexecinfo, unsigned int FAR *puArgErr)
+{
+ switch (dispid)
+ {
+ case DISPID_BEFORENAVIGATE2:
+ OnBeforeNavigate();
+ OnBeforeNavigate( pdispparams->rgvarg[6].pdispVal, GET_SAFE_DISP_BSTR(pdispparams->rgvarg[5]),
+ GET_SAFE_DISP_I4(pdispparams->rgvarg[4]), GET_SAFE_DISP_BSTR(pdispparams->rgvarg[3]),
+ pdispparams->rgvarg[2].pvarVal, GET_SAFE_DISP_BSTR(pdispparams->rgvarg[1]), pdispparams->rgvarg[0].pboolVal);
+ break;
+ case DISPID_NAVIGATEERROR:
+ {
+ VARIANT * vt_statuscode = pdispparams->rgvarg[1].pvarVal;
+ DWORD dwStatusCode = vt_statuscode->lVal;
+ if (dwStatusCode == 200)
+ {
+ *pdispparams->rgvarg[0].pboolVal = VARIANT_TRUE;
+ break;
+ }
+ OnNavigateError();
+ OnNavigateError(pdispparams->rgvarg[4].pdispVal, GET_SAFE_DISP_BSTR(pdispparams->rgvarg[3]),
+ GET_SAFE_DISP_BSTR(pdispparams->rgvarg[2]), GET_SAFE_DISP_I4(pdispparams->rgvarg[1]), pdispparams->rgvarg[0].pboolVal);
+ }
+ break;
+ case DISPID_NAVIGATECOMPLETE2:
+ OnNavigateComplete();
+ OnNavigateComplete(pdispparams->rgvarg[1].pdispVal, GET_SAFE_DISP_BSTR(pdispparams->rgvarg[0]));
+ break;
+ case DISPID_DOCUMENTCOMPLETE:
+ OnDocumentComplete(pdispparams->rgvarg[1].pdispVal, GET_SAFE_DISP_BSTR(pdispparams->rgvarg[0]));
+ break;
+ case DISPID_DOWNLOADBEGIN:
+ OnDownloadBegin();
+ break;
+ case DISPID_DOWNLOADCOMPLETE:
+ OnDownloadComplete();
+ break;
+ case DISPID_FILEDOWNLOAD:
+ OnFileDownload(pdispparams->rgvarg[1].pboolVal, pdispparams->rgvarg[0].pboolVal);
+ break;
+ case DISPID_NEWWINDOW2:
+ OnNewWindow2(pdispparams->rgvarg[1].ppdispVal, pdispparams->rgvarg[0].pboolVal);
+ break;
+ case DISPID_NEWWINDOW3:
+ OnNewWindow3(pdispparams->rgvarg[4].ppdispVal, pdispparams->rgvarg[3].pboolVal,
+ pdispparams->rgvarg[2].intVal, pdispparams->rgvarg[1].bstrVal, pdispparams->rgvarg[0].bstrVal);
+ break;
+ case DISPID_PROGRESSCHANGE:
+ OnProgressChange(pdispparams->rgvarg[1].lVal, pdispparams->rgvarg[0].lVal);
+ break;
+ case DISPID_STATUSTEXTCHANGE:
+ OnStatusTextChange(GET_SAFE_DISP_BSTR(pdispparams->rgvarg[0]));
+ break;
+ case DISPID_AMBIENT_USERAGENT:
+ /* TODO:
+ pvar->vt = VT_BSTR;
+ pvar->bstrVal = SysAllocString("...");
+ return S_OK;
+ */
+ break;
+ case DISPID_AMBIENT_DLCONTROL:
+ pvarResult->vt = VT_I4;
+ pvarResult->lVal = dwDownloadFlags;
+ return S_OK;
+ }
+ return DISP_E_MEMBERNOTFOUND;
+}
+
+void HTMLContainer::add(CLSID clsid)
+{
+ HRESULT hr; // return code
+ CoCreateInstance(clsid,
+ NULL,
+ CLSCTX_INPROC_SERVER/* | CLSCTX_LOCAL_SERVER*/,
+ IID_IUnknown,
+ (PVOID *)&m_punk);
+
+ if (!m_punk)
+ return ;
+
+ IOleObject *pioo;
+ hr = m_punk->QueryInterface(IID_IOleObject, (PVOID *) & pioo);
+ if (FAILED(hr))
+ return ;
+
+ pioo->SetClientSite(this);
+ pioo->Release();
+
+ IPersistStreamInit *ppsi;
+ hr = m_punk->QueryInterface(IID_IPersistStreamInit, (PVOID *) & ppsi);
+ if (SUCCEEDED(hr))
+ {
+ ppsi->InitNew();
+ ppsi->Release();
+ }
+}
+
+void HTMLContainer::remove()
+{
+ if (!m_punk)
+ return ;
+
+ HRESULT hr;
+ IOleObject *pioo;
+ IOleInPlaceObject *pipo;
+
+ /*
+ benski> enabling this makes everything lock up!
+ IConnectionPoint *icp = GetConnectionPoint(DIID_DWebBrowserEvents2);
+ if (icp)
+ {
+// m_dwCookie = 0;
+ HRESULT hr = icp->Unadvise(m_dwCookie);
+ icp->Release();
+ }
+ */
+
+ hr = m_punk->QueryInterface(IID_IOleObject, (PVOID *) & pioo);
+ if (SUCCEEDED(hr))
+ {
+ pioo->Close(OLECLOSE_NOSAVE);
+ pioo->SetClientSite(NULL);
+ pioo->Release();
+ }
+
+ hr = m_punk->QueryInterface(IID_IOleInPlaceObject, (PVOID *) & pipo);
+ if (SUCCEEDED(hr))
+ {
+ pipo->UIDeactivate();
+ pipo->InPlaceDeactivate();
+ pipo->Release();
+ }
+
+ m_punk->Release();
+ m_punk = NULL;
+}
+
+void HTMLContainer::setLocation(int x, int y, int width, int height)
+{
+ m_rect.left = x;
+ m_rect.top = y;
+ m_rect.right = x + width;
+ m_rect.bottom = y + height;
+
+ if (!m_punk)
+ return ;
+
+ HRESULT hr;
+ IOleInPlaceObject *pipo;
+
+ hr = m_punk->QueryInterface(IID_IOleInPlaceObject, (PVOID *) & pipo);
+ if (FAILED(hr))
+ return ;
+
+ pipo->SetObjectRects(&m_rect, &m_rect);
+ pipo->Release();
+}
+
+HRESULT HTMLContainer::GetBorder(LPRECT lprectBorder)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT HTMLContainer::RequestBorderSpace(LPCBORDERWIDTHS lpborderwidths)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT HTMLContainer::SetBorderSpace(LPCBORDERWIDTHS lpborderwidths)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT HTMLContainer::SetActiveObject(IOleInPlaceActiveObject * pActiveObject, LPCOLESTR lpszObjName)
+{
+ return E_NOTIMPL;
+}
+
+void HTMLContainer::setVisible(BOOL fVisible)
+{
+ if (!m_punk)
+ return ;
+
+ HRESULT hr;
+ IOleObject *pioo;
+
+ hr = m_punk->QueryInterface(IID_IOleObject, (PVOID *) & pioo);
+ if (FAILED(hr))
+ return ;
+
+ if (fVisible)
+ {
+ pioo->DoVerb(OLEIVERB_INPLACEACTIVATE, NULL, this, 0, m_hwnd, &m_rect);
+ pioo->DoVerb(OLEIVERB_SHOW, NULL, this, 0, m_hwnd, &m_rect);
+ }
+ else
+ pioo->DoVerb(OLEIVERB_HIDE, NULL, this, 0, m_hwnd, NULL);
+
+ pioo->Release();
+}
+
+void HTMLContainer::setFocus(BOOL fFocus)
+{
+ if (!m_punk)
+ return ;
+
+ HRESULT hr;
+ IOleObject *pioo;
+
+ if (fFocus)
+ {
+ hr = m_punk->QueryInterface(IID_IOleObject, (PVOID *) & pioo);
+ if (FAILED(hr))
+ return ;
+
+ pioo->DoVerb(OLEIVERB_UIACTIVATE, NULL, this, 0, m_hwnd, &m_rect);
+ pioo->Release();
+ }
+}
+
+bool HTMLContainer::translateKey(LPMSG pMsg)
+{
+ if (!m_punk)
+ return false;
+
+ HRESULT hr;
+ IOleInPlaceActiveObject *pao;
+
+ hr = m_punk->QueryInterface(IID_IOleInPlaceActiveObject, (PVOID *) & pao);
+ if (FAILED(hr))
+ return false;
+
+ HRESULT res = pao->TranslateAccelerator(pMsg);
+ pao->Release();
+ return res == S_OK;
+}
+
+/**************************************************************************
+
+* adContainer::getDispatch()
+
+**************************************************************************/
+
+IDispatch * HTMLContainer::getDispatch()
+{
+ if (!m_punk)
+ return NULL;
+
+ IDispatch *pdisp = NULL;
+ m_punk->QueryInterface(IID_IDispatch, (PVOID *) & pdisp);
+ return pdisp;
+}
+
+/**************************************************************************
+
+* adContainer::getUnknown()
+
+**************************************************************************/
+
+IUnknown * HTMLContainer::getUnknown()
+{
+ if (!m_punk)
+ return NULL;
+
+ m_punk->AddRef();
+ return m_punk;
+}
+
+// ***********************************************************************
+// IDocHostUIHandler
+// ***********************************************************************
+
+HRESULT HTMLContainer::ShowContextMenu(DWORD dwID, POINT __RPC_FAR *ppt, IUnknown __RPC_FAR *pcmdtReserved, IDispatch __RPC_FAR *pdispReserved)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT HTMLContainer::GetHostInfo(DOCHOSTUIINFO __RPC_FAR *pInfo)
+{
+ pInfo->cbSize = sizeof(DOCHOSTUIINFO);
+ pInfo->dwFlags = dwHostInfoFlags;
+
+ if (pszHostCSS)
+ {
+ INT strlen;
+ OLECHAR *pocCSS;
+ strlen = lstrlenW(pszHostCSS);
+ if (strlen)
+ {
+ strlen++;
+ pocCSS = (OLECHAR*)CoTaskMemAlloc(strlen * sizeof(OLECHAR));
+ if (pocCSS && S_OK== StringCchCopyW(pocCSS, strlen, pszHostCSS)) pInfo->pchHostCss = pocCSS;
+ }
+ }
+ return S_OK;
+}
+
+HRESULT HTMLContainer::ShowUI(DWORD dwID, IOleInPlaceActiveObject __RPC_FAR *pActiveObject, IOleCommandTarget __RPC_FAR *pCommandTarget, IOleInPlaceFrame __RPC_FAR *pFrame, IOleInPlaceUIWindow __RPC_FAR *pDoc)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT HTMLContainer::HideUI(void)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT HTMLContainer::UpdateUI(void)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT HTMLContainer::OnDocWindowActivate(BOOL fActivate)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT HTMLContainer::OnFrameWindowActivate(BOOL fActivate)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT HTMLContainer::ResizeBorder(LPCRECT prcBorder, IOleInPlaceUIWindow __RPC_FAR *pUIWindow, BOOL fRameWindow)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT HTMLContainer::TranslateAccelerator(LPMSG lpMsg, const GUID __RPC_FAR *pguidCmdGroup, DWORD nCmdID)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT HTMLContainer::GetOptionKeyPath(LPOLESTR __RPC_FAR *pchKey, DWORD dw)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT HTMLContainer::GetDropTarget(IDropTarget __RPC_FAR *pDropTarget, IDropTarget __RPC_FAR *__RPC_FAR *ppDropTarget)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT HTMLContainer::GetExternal(IDispatch __RPC_FAR *__RPC_FAR *ppDispatch)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT HTMLContainer::TranslateUrl(DWORD dwTranslate, OLECHAR __RPC_FAR *pchURLIn, OLECHAR __RPC_FAR *__RPC_FAR *ppchURLOut)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT HTMLContainer::FilterDataObject(IDataObject __RPC_FAR *pDO, IDataObject __RPC_FAR *__RPC_FAR *ppDORet)
+{
+ return E_NOTIMPL;
+}
+
+BOOL HTMLContainer::SetHostCSS(LPCWSTR pszHostCSS)
+{
+ if (this->pszHostCSS) { free(this->pszHostCSS); this->pszHostCSS = NULL; }
+ if (pszHostCSS && *pszHostCSS) this->pszHostCSS = _wcsdup(pszHostCSS);
+ return TRUE;
+}
+
+HWND HTMLContainer::GetHostHWND(void)
+{
+ if (m_punk)
+ {
+ IOleInPlaceObject *pipo;
+ m_punk->QueryInterface(IID_IOleInPlaceObject, (PVOID *)&pipo);
+ if (pipo)
+ {
+ HWND hwndHost;
+ pipo->GetWindow(&hwndHost);
+ pipo->Release();
+ return hwndHost;
+ }
+ }
+ return NULL;
+}
+
+DWORD HTMLContainer::SetDownloadFlags(DWORD dwFlags)
+{
+ DWORD temp;
+ temp = dwDownloadFlags;
+ dwDownloadFlags = dwFlags;
+ return temp;
+}
+
+DWORD HTMLContainer::SetHostInfoFlags(DWORD dwFlags)
+{
+ DWORD temp;
+ temp = dwHostInfoFlags;
+ dwHostInfoFlags = dwFlags;
+ return temp;
+} \ No newline at end of file
diff --git a/Src/nu/HTMLContainer.h b/Src/nu/HTMLContainer.h
new file mode 100644
index 00000000..fd4c194c
--- /dev/null
+++ b/Src/nu/HTMLContainer.h
@@ -0,0 +1,177 @@
+#ifndef NULLSOFT_HTMLCONTAINERH
+#define NULLSOFT_HTMLCONTAINERH
+
+#include <ocidl.h>
+#include <mshtmhst.h>
+#include <mshtmdid.h>
+#include <shlobj.h>
+/**************************************************************************
+ class definitions
+**************************************************************************/
+
+
+#ifndef DOCHOSTUIFLAG_HOST_NAVIGATES
+#define DOCHOSTUIFLAG_HOST_NAVIGATES 0x02000000
+#endif
+#ifndef DOCHOSTUIFLAG_ENABLE_REDIRECT_NOTIFICATION
+#define DOCHOSTUIFLAG_ENABLE_REDIRECT_NOTIFICATION 0x04000000
+#endif
+#ifndef DOCHOSTUIFLAG_USE_WINDOWLESS_SELECTCONTROL
+#define DOCHOSTUIFLAG_USE_WINDOWLESS_SELECTCONTROL 0x08000000
+#endif
+#ifndef DOCHOSTUIFLAG_USE_WINDOWED_SELECTCONTROL
+#define DOCHOSTUIFLAG_USE_WINDOWED_SELECTCONTROL 0x10000000
+#endif
+#ifndef DOCHOSTUIFLAG_ENABLE_ACTIVEX_INACTIVATE_MODE
+#define DOCHOSTUIFLAG_ENABLE_ACTIVEX_INACTIVATE_MODE 0x20000000
+#endif
+
+
+
+class HTMLContainer : public IOleClientSite,
+ public IOleInPlaceSite,
+ public IOleInPlaceFrame,
+ public IOleControlSite,
+ public IDocHostUIHandler,
+ public IDispatch
+{
+protected:
+ ULONG m_cRefs; // ref count
+
+ IUnknown *m_punk; // IUnknown of contained object
+ RECT m_rect; // size of control
+
+ bool bInitialized;
+public:
+ HWND m_hwnd; // window handle of the container
+ HTMLContainer();
+ HTMLContainer(HWND hwnd);
+ virtual ~HTMLContainer();
+
+public:
+ // *** IUnknown Methods ***
+ STDMETHOD(QueryInterface)(REFIID riid, PVOID *ppvObject);
+ STDMETHOD_(ULONG, AddRef)(void);
+ STDMETHOD_(ULONG, Release)(void);
+
+ // *** IOleInPlaceUIWindow Methods ***
+ STDMETHOD (GetBorder)(LPRECT lprectBorder);
+ STDMETHOD (RequestBorderSpace)(LPCBORDERWIDTHS lpborderwidths);
+ STDMETHOD (SetBorderSpace)(LPCBORDERWIDTHS lpborderwidths);
+ STDMETHOD (SetActiveObject)(IOleInPlaceActiveObject * pActiveObject,
+ LPCOLESTR lpszObjName);
+ // *** IOleClientSite Methods ***
+ STDMETHOD (SaveObject)();
+ STDMETHOD (GetMoniker)(DWORD dwAssign, DWORD dwWhichMoniker, LPMONIKER *ppMk);
+ STDMETHOD (GetContainer)(LPOLECONTAINER *ppContainer);
+ STDMETHOD (ShowObject)();
+ STDMETHOD (OnShowWindow)(BOOL fShow);
+ STDMETHOD (RequestNewObjectLayout)();
+
+ // *** IOleWindow Methods ***
+ STDMETHOD (GetWindow) (HWND * phwnd);
+ STDMETHOD (ContextSensitiveHelp) (BOOL fEnterMode);
+
+ // *** IOleInPlaceSite Methods ***
+ STDMETHOD (CanInPlaceActivate) (void);
+ STDMETHOD (OnInPlaceActivate) (void);
+ STDMETHOD (OnUIActivate) (void);
+ STDMETHOD (GetWindowContext) (IOleInPlaceFrame ** ppFrame, IOleInPlaceUIWindow ** ppDoc, LPRECT lprcPosRect, LPRECT lprcClipRect, LPOLEINPLACEFRAMEINFO lpFrameInfo);
+ STDMETHOD (Scroll) (SIZE scrollExtent);
+ STDMETHOD (OnUIDeactivate) (BOOL fUndoable);
+ STDMETHOD (OnInPlaceDeactivate) (void);
+ STDMETHOD (DiscardUndoState) (void);
+ STDMETHOD (DeactivateAndUndo) (void);
+ STDMETHOD (OnPosRectChange) (LPCRECT lprcPosRect);
+
+
+ // *** IOleInPlaceFrame Methods ***
+ STDMETHOD (InsertMenus)(HMENU hmenuShared, LPOLEMENUGROUPWIDTHS lpMenuWidths);
+ STDMETHOD (SetMenu)(HMENU hmenuShared, HOLEMENU holemenu, HWND hwndActiveObject);
+ STDMETHOD (RemoveMenus)(HMENU hmenuShared);
+ STDMETHOD (SetStatusText)(LPCOLESTR pszStatusText);
+ STDMETHOD (EnableModeless)(BOOL fEnable);
+ STDMETHOD (TranslateAccelerator)(LPMSG lpmsg, WORD wID);
+
+ // *** IOleControlSite Methods ***
+ STDMETHOD (OnControlInfoChanged)(void);
+ STDMETHOD (LockInPlaceActive)(BOOL fLock);
+ STDMETHOD (GetExtendedControl)(IDispatch **ppDisp);
+ STDMETHOD (TransformCoords)(POINTL *pptlHimetric, POINTF *pptfContainer, DWORD dwFlags);
+ STDMETHOD (TranslateAccelerator)(LPMSG pMsg, DWORD grfModifiers);
+ STDMETHOD (OnFocus)(BOOL fGotFocus);
+ STDMETHOD (ShowPropertyFrame)(void);
+
+ // *** IDispatch Methods ***
+ STDMETHOD (GetIDsOfNames)(REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames, LCID lcid, DISPID FAR* rgdispid);
+ STDMETHOD (GetTypeInfo)(unsigned int itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo);
+ STDMETHOD (GetTypeInfoCount)(unsigned int FAR * pctinfo);
+ STDMETHOD (Invoke)(DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, EXCEPINFO FAR * pexecinfo, unsigned int FAR *puArgErr);
+
+ // *** IDocHostUIHandler Methods ***
+ STDMETHOD (ShowContextMenu)(DWORD dwID, POINT __RPC_FAR *ppt, IUnknown __RPC_FAR *pcmdtReserved, IDispatch __RPC_FAR *pdispReserved);
+ STDMETHOD (GetHostInfo)(DOCHOSTUIINFO __RPC_FAR *pInfo);
+ STDMETHOD (ShowUI)(DWORD dwID, IOleInPlaceActiveObject __RPC_FAR *pActiveObject, IOleCommandTarget __RPC_FAR *pCommandTarget, IOleInPlaceFrame __RPC_FAR *pFrame, IOleInPlaceUIWindow __RPC_FAR *pDoc);
+ STDMETHOD (HideUI)(void);
+ STDMETHOD (UpdateUI)(void);
+ STDMETHOD (OnDocWindowActivate)(BOOL fActivate);
+ STDMETHOD (OnFrameWindowActivate)(BOOL fActivate);
+ STDMETHOD (ResizeBorder)(LPCRECT prcBorder, IOleInPlaceUIWindow __RPC_FAR *pUIWindow, BOOL fRameWindow);
+ STDMETHOD (TranslateAccelerator)(LPMSG lpMsg, const GUID __RPC_FAR *pguidCmdGroup, DWORD nCmdID);
+ STDMETHOD (GetOptionKeyPath)(LPOLESTR __RPC_FAR *pchKey, DWORD dw);
+ STDMETHOD (GetDropTarget)(IDropTarget __RPC_FAR *pDropTarget, IDropTarget __RPC_FAR *__RPC_FAR *ppDropTarget);
+ STDMETHOD (GetExternal)(IDispatch __RPC_FAR *__RPC_FAR *ppDispatch);
+ STDMETHOD (TranslateUrl)(DWORD dwTranslate, OLECHAR __RPC_FAR *pchURLIn, OLECHAR __RPC_FAR *__RPC_FAR *ppchURLOut);
+ STDMETHOD (FilterDataObject)(IDataObject __RPC_FAR *pDO, IDataObject __RPC_FAR *__RPC_FAR *ppDORet);
+ // STDMETHOD (EnableModeless)(BOOL fEnable);
+
+public:
+ void add(CLSID clsid);
+ void remove();
+
+ void setLocation(int x, int y, int width, int height);
+ void setVisible(BOOL fVisible);
+ void setFocus(BOOL fFocus);
+ void setStatusWindow(HWND hwndStatus);
+ bool translateKey(LPMSG pMsg);
+
+
+ virtual void OnBeforeNavigate(void) {} // deprecated
+ virtual void OnNavigateError(void) {} // deprecated
+ virtual void OnNavigateComplete(void) {} // deprecated
+
+ virtual void OnBeforeNavigate(IDispatch *pDispatch, LPCWSTR pszURL, DWORD dwFlags, LPCWSTR pszTargetFrameName, VARIANT *vtPostData, LPCWSTR pszHeaders, VARIANT_BOOL *Cancel);
+ virtual void OnNavigateError(IDispatch *pDispatch, LPCWSTR pszURL, LPCWSTR pszTargetFrameName, INT nStatusCode, VARIANT_BOOL *Cancel);
+ virtual void OnNavigateComplete(IDispatch *pDispatch, LPCWSTR pszURL);
+ virtual void OnDocumentComplete(IDispatch *pDisp, LPCWSTR pszURL);
+ virtual void OnDownloadBegin(void);
+ virtual void OnDownloadComplete(void);
+ virtual void OnFileDownload(VARIANT_BOOL *ActiveDocument, VARIANT_BOOL *Cancel);
+ virtual void OnNewWindow2(IDispatch **ppDisp, VARIANT_BOOL *Cancel);
+ virtual void OnNewWindow3(IDispatch **ppDisp, VARIANT_BOOL *Cancel, DWORD dwFlags, LPCWSTR pszUrlContext, LPCWSTR pszUrl);
+ virtual void OnProgressChange(long Progress, long ProgressMax);
+ virtual void OnStatusTextChange(LPCWSTR pszText);
+
+ void close();
+ virtual BOOL SetHostCSS(LPCWSTR pszHostCSS);
+ virtual HWND GetHostHWND(void);
+ DWORD SetDownloadFlags(DWORD dwFlags);
+ DWORD SetHostInfoFlags(DWORD dwFlags);
+
+ IDispatch *getDispatch();
+ IUnknown * getUnknown();
+ IWebBrowser2 *m_pweb;
+ void SyncSizeToWindow(HWND window);
+ IConnectionPoint *GetConnectionPoint(REFIID riid);
+
+
+
+ DWORD m_dwCookie;
+private:
+ wchar_t *pszHostCSS;
+ DWORD dwDownloadFlags;
+ DWORD dwHostInfoFlags;
+};
+
+
+#endif
diff --git a/Src/nu/HTMLContainer2.cpp b/Src/nu/HTMLContainer2.cpp
new file mode 100644
index 00000000..cfb5bbd6
--- /dev/null
+++ b/Src/nu/HTMLContainer2.cpp
@@ -0,0 +1,2108 @@
+#include "./HTMLContainer2.h"
+#include "../winamp/wa_ipc.h"
+#include "CGlobalAtom.h"
+
+#include <exdisp.h>
+#include <mshtmdid.h>
+#include <mshtml.h>
+#include <exdispid.h>
+#include <urlmon.h>
+#include <Wininet.h>
+#include <shlwapi.h>
+#include <strsafe.h>
+
+#ifndef GetWindowStyle
+#define GetWindowStyle(__hwnd) ((UINT)GetWindowLongPtrW((__hwnd), GWL_STYLE))
+#endif //GetWindowStyle
+
+#ifndef GetWindowStyleEx
+#define GetWindowStyleEx(__hwnd) ((UINT)GetWindowLongPtrW((__hwnd), GWL_EXSTYLE))
+#endif // GetWindowStyleEx
+
+static UINT WM_REDIRECTNAVIGATE = 0;
+
+#ifndef DISPID_NEWWINDOW3
+#define DISPID_NEWWINDOW3 273
+#endif
+
+static CGlobalAtom WNDPROP_SCCTRLW(L"SCCTRL");
+
+static UINT WINAMP_WM_DIRECT_MOUSE_WHEEL = WM_NULL;
+
+#define REGISTRY_FEATURE_CONTROL L"Software\\Microsoft\\Internet Explorer\\Main\\FeatureControl"
+#define REGISTRY_FEATURE_USE_LEGACY_JSCRIPT (REGISTRY_FEATURE_CONTROL L"\\FEATURE_USE_LEGACY_JSCRIPT")
+
+#ifndef LONGX86
+#ifdef _WIN64
+ #define LONGX86 LONG_PTR
+#else /*_WIN64*/
+ #define LONGX86 LONG
+#endif /*_WIN64*/
+#endif // LONGX86
+
+#ifndef CSTR_INVARIANT
+#define CSTR_INVARIANT MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT)
+#endif //CSTR_INVARIANT
+
+#define IS_EQUALCLASS(__class1, __class2)\
+ (CSTR_EQUAL == CompareStringW(CSTR_INVARIANT, NORM_IGNORECASE, __class1, -1, __class2, -1))
+
+#define DEFAULT_HOSTBKCOLOR 0x00FFFFFF
+#define DEFAULT_DOCHOSTUIFLAGS (DOCHOSTUIFLAG_NO3DOUTERBORDER | \
+ DOCHOSTUIFLAG_ENABLE_INPLACE_NAVIGATION | \
+ DOCHOSTUIFLAG_NO3DBORDER | \
+ DOCHOSTUIDBLCLK_DEFAULT | \
+ 0)
+
+#define DEFAULT_DOWNLOADFLAGS (DLCTL_DLIMAGES | \
+ DLCTL_VIDEOS | \
+ DLCTL_PRAGMA_NO_CACHE | \
+ 0)
+
+#define CF_DISABLEBEFORENAVFILTER 0x00000002
+
+typedef struct __RMNAVIGATE2
+{
+ VARIANT URL;
+ VARIANT Flags;
+ VARIANT TargetFrameName;
+ VARIANT PostData;
+ VARIANT Headers;
+}RMNAVIGATE2;
+
+typedef struct __RMNAVIGATETONAME
+{
+ BSTR url;
+ UINT flags;
+} RMNAVIGATETONAME;
+
+typedef struct _IECURSOR
+{
+ INT nSysId;
+ HCURSOR hSys;
+ HCURSOR hUser;
+} IECURSOR;
+
+static INT bIsVistaOrHigher = -1;
+
+typedef struct __LIBHDR LIBHDR;
+
+typedef BOOL (WINAPI *LIBRARYLOADER)(LIBHDR* /*header*/);
+
+typedef struct __LIBHDR
+{
+ HMODULE hMod;
+ LPCWSTR name;
+ LIBRARYLOADER loader;
+ size_t size;
+} LIBHDR;
+
+typedef struct __LIBWININET
+{
+ LIBHDR hdr;
+
+ typedef BOOL (WINAPI *INTERNETCRACKURL)(LPCWSTR /*lpszUrl*/, DWORD /*dwUrlLength*/, DWORD /*dwFlags*/, URL_COMPONENTSW* /*lpUrlComponents*/);
+
+ INTERNETCRACKURL InternetCrackUrl;
+
+ static BOOL CALLBACK Loader(LIBHDR *pHdr)
+ {
+ LIBWININET *lib = (LIBWININET*)pHdr;
+ lib->InternetCrackUrl = (LIBWININET::INTERNETCRACKURL)GetProcAddress(pHdr->hMod, "InternetCrackUrlW");
+ return TRUE;
+ }
+} LIBWININET;
+
+typedef struct __LIBURLMON
+{
+ LIBHDR hdr;
+
+ typedef HRESULT (WINAPI *COINTERNETSETFEATUREENABLED)(INTERNETFEATURELIST /*FeatureEntry*/, DWORD /*dwFlags*/, BOOL /*fEnable*/);
+ typedef HRESULT (WINAPI *COINTERNETISFEATUREENABLED)(INTERNETFEATURELIST /*FeatureEntry*/, DWORD /*dwFlags*/);
+
+ COINTERNETSETFEATUREENABLED CoInternetSetFeatureEnabled;
+ COINTERNETISFEATUREENABLED CoInternetIsFeatureEnabled;
+
+ static BOOL CALLBACK Loader(LIBHDR *pHdr)
+ {
+ LIBURLMON *lib = (LIBURLMON*)pHdr;
+ lib->CoInternetSetFeatureEnabled = (LIBURLMON::COINTERNETSETFEATUREENABLED)GetProcAddress(pHdr->hMod, "CoInternetSetFeatureEnabled");
+ lib->CoInternetIsFeatureEnabled = (LIBURLMON::COINTERNETISFEATUREENABLED)GetProcAddress(pHdr->hMod, "CoInternetIsFeatureEnabled");
+ return TRUE;
+ }
+} LIBURLMON;
+
+static LIBWININET libWininet = { { NULL, L"WinInet.dll", LIBWININET::Loader, sizeof(LIBWININET)}, NULL, };
+static LIBURLMON libUrlmon = { { NULL, L"UrlMon.dll", LIBURLMON::Loader, sizeof(LIBURLMON)}, NULL, };
+static LIBHDR* szLibrary[] = { (LIBHDR*)&libWininet, (LIBHDR*)&libUrlmon };
+
+BOOL HTMLContainer2_Initialize()
+{
+ for(size_t i = 0; i < ARRAYSIZE(szLibrary); i++)
+ {
+ if (NULL == szLibrary[i]->hMod)
+ {
+ size_t size = szLibrary[i]->size - sizeof(LIBHDR);
+ ZeroMemory((BYTE*)szLibrary[i] + sizeof(LIBHDR), size);
+ }
+ }
+ return TRUE;
+}
+
+BOOL HTMLContainer2_Uninitialize()
+{
+ for(size_t i = 0; i < ARRAYSIZE(szLibrary); i++)
+ {
+ if (NULL != szLibrary[i]->hMod)
+ {
+ FreeLibrary(szLibrary[i]->hMod);
+ szLibrary[i]->hMod = NULL;
+ }
+ size_t size = szLibrary[i]->size - sizeof(LIBHDR);
+ ZeroMemory((BYTE*)szLibrary[i] + sizeof(LIBHDR), size);
+ }
+ return TRUE;
+}
+
+BOOL HTMLContainer2_LoadLibray(LIBHDR *libraryHeader)
+{
+ if (NULL == libraryHeader)
+ return FALSE;
+
+ if (NULL != libraryHeader->hMod)
+ return TRUE;
+
+ libraryHeader->hMod = LoadLibraryW(libraryHeader->name);
+ if (NULL == libraryHeader->hMod)
+ return FALSE;
+
+ return (NULL != libraryHeader->loader) ?
+ libraryHeader->loader(libraryHeader) :
+ TRUE;
+}
+
+static BOOL SublassControl_IsAttached(HWND hwnd);
+static BOOL SubclassControl_Attach(HWND hControl, HTMLContainer2 *pContainer);
+
+// uncomment if you ever want to use mozilla instead of IE
+// change the CLSID_WebBrowser in the constructor below to CLSID_MozillaBrowser
+// but window.external from javascript doesn't work :(
+
+static const CLSID CLSID_MozillaBrowser=
+ { 0x1339B54C, 0x3453, 0x11D2, { 0x93, 0xB9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } };
+
+HTMLContainer2::HTMLContainer2(HWND waWindow, HWND hwndParent) :
+ ref(1), pUnk(NULL), hParent(hwndParent), dwCookie(0),
+ dwFlags(0), fnBrwoserCB(NULL) ,userData(NULL),
+ bNavigating(FALSE), hCursors(NULL), nCursors(0),
+ ensureChakraLoaded(TRUE), winampWindow(waWindow)
+{
+ if (NULL == hParent || FALSE == GetClientRect(hParent, &rect))
+ SetRectEmpty(&rect);
+
+ if (-1 == bIsVistaOrHigher)
+ {
+ OSVERSIONINFO os;
+ os.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+ bIsVistaOrHigher = (GetVersionEx(&os) && os.dwMajorVersion >= 6);
+ }
+}
+
+HTMLContainer2::~HTMLContainer2(void)
+{
+ if (NULL != fnBrwoserCB)
+ fnBrwoserCB(this, DISPID_DESTRUCTOR, NULL, userData);
+
+ if (hCursors)
+ {
+ HCURSOR hc;
+ for(int i = 0; i < nCursors; i++)
+ {
+ hc = ((IECURSOR*)hCursors)[i].hUser;
+ if (hc) DestroyCursor(hc);
+ }
+ free(hCursors);
+ }
+}
+
+STDMETHODIMP HTMLContainer2::Initialize(void)
+{
+ IOleObject *pioo = NULL;
+ IConnectionPoint *pcp = NULL;
+ IConnectionPointContainer *pcpc = NULL;
+
+ HRESULT hr = CoCreateInstance(CLSID_WebBrowser, NULL, CLSCTX_INPROC, IID_IUnknown, (PVOID*)&pUnk);
+ if (FAILED(hr))
+ return hr;
+
+ hr = pUnk->QueryInterface(IID_IOleObject, (PVOID*)&pioo);
+ if (SUCCEEDED(hr))
+ {
+ DWORD dwFlags = 0;
+ if (FAILED(pioo->GetMiscStatus(DVASPECT_CONTENT, &dwFlags)))
+ dwFlags = 0;
+
+ if (0 != (OLEMISC_SETCLIENTSITEFIRST & dwFlags))
+ {
+ IOleClientSite *pClientSite = NULL;
+ hr = QueryInterface(IID_IOleClientSite, (void**)&pClientSite);
+ if (SUCCEEDED(hr))
+ {
+ pioo->SetClientSite(pClientSite);
+ pioo->SetHostNames(L"NullsoftHtmlContainer2", NULL);
+
+ if(NULL == hParent || FALSE == GetClientRect(hParent, &rect))
+ SetRectEmpty(&rect);
+
+ pioo->DoVerb(OLEIVERB_INPLACEACTIVATE, NULL, pClientSite, 0, hParent, &rect);
+
+ pClientSite->Release();
+ }
+ }
+ pioo->Release();
+
+ HWND hHost = GetHostHWND();
+ if (IsWindow(hHost))
+ {
+ SubclassControl_Attach(hHost, this);
+ }
+ }
+
+ if (FAILED(hr)) return hr;
+
+ hr = pUnk->QueryInterface(IID_IConnectionPointContainer, (PVOID*)&pcpc);
+ if (SUCCEEDED (hr))
+ {
+ hr = pcpc->FindConnectionPoint(DIID_DWebBrowserEvents2, &pcp);
+ if (SUCCEEDED(hr))
+ {
+ hr = pcp->Advise(static_cast<IDispatch*>(this), &dwCookie);
+ if (FAILED(hr)) dwCookie = 0;
+ pcp->Release();
+ }
+ pcpc->Release();
+ }
+
+ return hr;
+}
+
+STDMETHODIMP HTMLContainer2::Finish(void)
+{
+ if (!pUnk) return S_OK;
+
+ AddRef();
+
+ UnadviseBrowserEvents();
+
+ IOleObject *pioo = NULL;
+ IOleInPlaceObject *pipo = NULL;
+ HRESULT hr = pUnk->QueryInterface(IID_IOleInPlaceObject, (PVOID*)&pipo);
+ if (SUCCEEDED(hr))
+ {
+ pipo->InPlaceDeactivate();
+ pipo->UIDeactivate();
+ pipo->Release();
+ }
+
+ hr = pUnk->QueryInterface(IID_IOleObject, (PVOID*)&pioo);
+ if (SUCCEEDED(hr))
+ {
+ pioo->Close(OLECLOSE_NOSAVE);
+ pioo->SetClientSite(NULL);
+ pioo->Release();
+ }
+
+ ULONG r = pUnk->Release();
+ pUnk = NULL;
+
+ Release();
+
+ return hr;
+}
+
+STDMETHODIMP HTMLContainer2::QueryInterface(REFIID riid, PVOID *ppvObject)
+{
+ if (!ppvObject)
+ return E_POINTER;
+
+ if (IsEqualIID(riid, IID_IOleClientSite))
+ *ppvObject = (IOleClientSite*)this;
+ else if (IsEqualIID(riid, IID_IOleInPlaceSite))
+ *ppvObject = (IOleInPlaceSite*)this;
+ else if (IsEqualIID(riid, IID_IOleInPlaceFrame))
+ *ppvObject = (IOleInPlaceFrame*)this;
+ else if (IsEqualIID(riid, IID_IOleInPlaceUIWindow))
+ *ppvObject = (IOleInPlaceUIWindow*)this;
+ else if (IsEqualIID(riid, IID_IOleControlSite))
+ *ppvObject = (IOleControlSite*)this;
+ else if (IsEqualIID(riid, IID_IOleWindow))
+ *ppvObject = reinterpret_cast<IOleWindow*>(this);
+ else if (IsEqualIID(riid, IID_IDispatch))
+ *ppvObject = (IDispatch*)this;
+ else if (IsEqualIID(riid, IID_IUnknown))
+ *ppvObject = reinterpret_cast<IUnknown*>(this);
+ else if (IsEqualIID(riid, IID_IDocHostUIHandler))
+ *ppvObject = (IDocHostUIHandler*)this;
+ else if (IsEqualIID(riid, IID_IDocHostUIHandler2))
+ *ppvObject = (IDocHostUIHandler2*)this;
+ else if (IsEqualIID(riid, IID_IDocHostShowUI))
+ *ppvObject = (IDocHostShowUI*)this;
+ else if (IsEqualIID(riid, IID_IOleCommandTarget))
+ *ppvObject = (IOleCommandTarget*)this;
+ else if (IsEqualIID(riid, IID_IServiceProvider))
+ *ppvObject = (IServiceProvider*)this;
+ else
+ {
+ *ppvObject = NULL;
+ return E_NOINTERFACE;
+ }
+
+ AddRef();
+ return S_OK;
+}
+
+ULONG HTMLContainer2::AddRef(void)
+{
+ return InterlockedIncrement(&ref);
+}
+
+ULONG HTMLContainer2::Release(void)
+{
+ if (0 == ref)
+ return ref;
+
+ LONG r = InterlockedDecrement(&ref);
+ if (0 == r)
+ delete(this);
+
+ return r;
+}
+
+STDMETHODIMP HTMLContainer2::SaveObject()
+{
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP HTMLContainer2::GetMoniker(DWORD dwAssign, DWORD dwWhichMoniker, LPMONIKER * ppMk)
+{
+ *ppMk = NULL;
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP HTMLContainer2::GetContainer(LPOLECONTAINER * ppContainer)
+{
+ *ppContainer = NULL;
+ return E_NOINTERFACE;
+}
+
+STDMETHODIMP HTMLContainer2::ShowObject()
+{
+ return S_OK;
+}
+
+STDMETHODIMP HTMLContainer2::OnShowWindow(BOOL fShow)
+{
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP HTMLContainer2::RequestNewObjectLayout()
+{
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP HTMLContainer2::GetWindow(HWND * lphwnd)
+{
+ if (NULL != hParent && IsWindow(hParent))
+ {
+ *lphwnd = hParent;
+ return S_OK;
+ }
+ else
+ {
+ *lphwnd = NULL;
+ return E_FAIL;
+ }
+}
+
+STDMETHODIMP HTMLContainer2::ContextSensitiveHelp(BOOL fEnterMode)
+{
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP HTMLContainer2::CanInPlaceActivate(void)
+{
+ return S_OK;
+}
+
+STDMETHODIMP HTMLContainer2::OnInPlaceActivate(void)
+{
+ return S_OK;
+}
+
+STDMETHODIMP HTMLContainer2::OnUIActivate(void)
+{
+ return S_OK;
+}
+
+STDMETHODIMP HTMLContainer2::GetWindowContext(IOleInPlaceFrame ** ppFrame, IOleInPlaceUIWindow ** ppIIPUIWin,
+ LPRECT lprcPosRect, LPRECT lprcClipRect, LPOLEINPLACEFRAMEINFO lpFrameInfo)
+{
+ if (FAILED(QueryInterface(IID_IOleInPlaceFrame, (void**)ppFrame)))
+ *ppFrame = NULL;
+
+ *ppIIPUIWin = NULL;
+
+ CopyRect(lprcPosRect, &rect);
+ CopyRect(lprcClipRect, &rect);
+
+ lpFrameInfo->cb = sizeof(OLEINPLACEFRAMEINFO);
+ lpFrameInfo->fMDIApp = FALSE;
+ lpFrameInfo->hwndFrame = hParent;
+ lpFrameInfo->haccel = NULL;
+ lpFrameInfo->cAccelEntries = 0;
+
+ return S_OK;
+}
+
+STDMETHODIMP HTMLContainer2::Scroll(SIZE scrollExtent)
+{
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP HTMLContainer2::OnUIDeactivate(BOOL fUndoable)
+{
+ return S_OK;
+}
+
+STDMETHODIMP HTMLContainer2::OnInPlaceDeactivate(void)
+{
+ return S_OK;
+}
+
+STDMETHODIMP HTMLContainer2::DiscardUndoState(void)
+{
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP HTMLContainer2::DeactivateAndUndo(void)
+{
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP HTMLContainer2::OnPosRectChange(LPCRECT lprcPosRect)
+{
+ return S_OK;
+}
+
+STDMETHODIMP HTMLContainer2::InsertMenus(HMENU hmenuShared, LPOLEMENUGROUPWIDTHS lpMenuWidths)
+{
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP HTMLContainer2::SetMenu(HMENU hmenuShared, HOLEMENU holemenu, HWND hwndActiveObject)
+{
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP HTMLContainer2::RemoveMenus(HMENU hmenuShared)
+{
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP HTMLContainer2::SetStatusText(LPCOLESTR pszStatusText)
+{
+ return S_OK;
+}
+
+STDMETHODIMP HTMLContainer2::TranslateAccelerator(LPMSG lpmsg, WORD wID)
+{
+ return S_OK;
+}
+
+STDMETHODIMP HTMLContainer2::EnableModeless(BOOL fEnable)
+{
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP HTMLContainer2::OnControlInfoChanged()
+{
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP HTMLContainer2::LockInPlaceActive(BOOL fLock)
+{
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP HTMLContainer2::GetExtendedControl(IDispatch **ppDisp)
+{
+ if (ppDisp == NULL)
+ return E_INVALIDARG;
+
+ *ppDisp = (IDispatch *)this;
+ (*ppDisp)->AddRef();
+
+ return S_OK;
+}
+
+STDMETHODIMP HTMLContainer2::TransformCoords(POINTL *pptlHimetric, POINTF *pptfContainer, DWORD dwFlags)
+{
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP HTMLContainer2::TranslateAccelerator(LPMSG pMsg, DWORD grfModifiers)
+{
+ return S_FALSE;
+}
+
+STDMETHODIMP HTMLContainer2::OnFocus(BOOL fGotFocus)
+{
+ return S_OK;
+}
+
+STDMETHODIMP HTMLContainer2::ShowPropertyFrame(void)
+{
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP HTMLContainer2::GetIDsOfNames(REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames, LCID lcid, DISPID FAR* rgdispid)
+{
+ *rgdispid = DISPID_UNKNOWN;
+ return DISP_E_UNKNOWNNAME;
+}
+
+STDMETHODIMP HTMLContainer2::GetTypeInfo(unsigned int itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo)
+{
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP HTMLContainer2::GetTypeInfoCount(unsigned int FAR * pctinfo)
+{
+ return E_NOTIMPL;
+}
+
+static HRESULT HTMLContainer2_OnShowUiElementHelper(HTMLContainer2 *instance, UINT elementId, DISPPARAMS *pDispParams)
+{
+ if (NULL == pDispParams) return E_UNEXPECTED;
+ if (1 != pDispParams->cArgs) return DISP_E_BADPARAMCOUNT;
+ if (VT_BOOL != pDispParams->rgvarg[0].vt) return DISP_E_BADVARTYPE;
+
+ instance->OnShowUiElement(elementId, pDispParams->rgvarg[0].boolVal);
+ return S_OK;
+}
+
+STDMETHODIMP HTMLContainer2::Invoke(DISPID dispId, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR *pDispParams, VARIANT FAR *pvarResult, EXCEPINFO FAR * pexecinfo, unsigned int FAR *puArgErr)
+{
+ if (fnBrwoserCB && fnBrwoserCB(this, dispId, pDispParams, userData))
+ {
+ return DISP_E_MEMBERNOTFOUND;
+ }
+
+ switch (dispId)
+ {
+ case DISPID_BEFORENAVIGATE2:
+ if(7 == pDispParams->cArgs)
+ {
+ OnBeforeNavigate(pDispParams->rgvarg[6].pdispVal, pDispParams->rgvarg[5].pvarVal,
+ pDispParams->rgvarg[4].pvarVal, pDispParams->rgvarg[3].pvarVal,
+ pDispParams->rgvarg[2].pvarVal, pDispParams->rgvarg[1].pvarVal,
+ pDispParams->rgvarg[0].pboolVal);
+ return S_OK;
+ }
+ break;
+
+ case DISPID_NAVIGATEERROR:
+ if (200 == pDispParams->rgvarg[1].pvarVal->lVal)
+ {
+ *pDispParams->rgvarg[0].pboolVal = VARIANT_TRUE;
+ }
+ if (5 == pDispParams->cArgs)
+ {
+ OnNavigateError(pDispParams->rgvarg[4].pdispVal, pDispParams->rgvarg[3].pvarVal,
+ pDispParams->rgvarg[2].pvarVal, pDispParams->rgvarg[1].pvarVal,
+ pDispParams->rgvarg[0].pboolVal);
+ return S_OK;
+ }
+ break;
+
+ case DISPID_NAVIGATECOMPLETE2:
+ if (2 == pDispParams->cArgs)
+ {
+ OnNavigateComplete(pDispParams->rgvarg[1].pdispVal, pDispParams->rgvarg[0].pvarVal);
+ return S_OK;
+ }
+ break;
+
+ case DISPID_DOCUMENTCOMPLETE:
+ if (2 == pDispParams->cArgs)
+ {
+ OnDocumentComplete(pDispParams->rgvarg[1].pdispVal, pDispParams->rgvarg[0].pvarVal);
+ }
+
+ if (NULL != pUnk)
+ {
+ IWebBrowser2 *pWeb1 = NULL, *pWeb2 = NULL;
+
+ if (FAILED(GetIWebBrowser2(&pWeb1)))
+ pWeb1 = NULL;
+
+ if (pDispParams->cArgs < 2 ||
+ NULL == pDispParams->rgvarg[1].pdispVal ||
+ FAILED(pDispParams->rgvarg[1].pdispVal->QueryInterface(IID_IWebBrowser2, (void**)&pWeb2)))
+ {
+ pWeb2 = NULL;
+ }
+
+ if (NULL != pWeb1) pWeb1->Release();
+ if (NULL != pWeb2) pWeb2->Release();
+
+ if (NULL != pWeb1 && pWeb1 == pWeb2)
+ OnDocumentReady(pDispParams->rgvarg[1].pdispVal, pDispParams->rgvarg[0].pvarVal);
+ }
+ return S_OK;
+
+ case DISPID_DOWNLOADBEGIN:
+ OnDownloadBegin();
+ return S_OK;
+
+ case DISPID_DOWNLOADCOMPLETE:
+ OnDownloadComplete();
+ return S_OK;
+
+ case DISPID_FILEDOWNLOAD:
+ if (2 == pDispParams->cArgs)
+ {
+ OnFileDownload(pDispParams->rgvarg[1].pboolVal, pDispParams->rgvarg[0].pboolVal);
+ return S_OK;
+ }
+ break;
+
+ case DISPID_NEWWINDOW2:
+ if (2 == pDispParams->cArgs)
+ {
+ OnNewWindow2(pDispParams->rgvarg[1].ppdispVal, pDispParams->rgvarg[0].pboolVal);
+ return S_OK;
+ }
+ break;
+
+ case DISPID_NEWWINDOW3:
+ if (5 != pDispParams->cArgs)
+ return DISP_E_BADPARAMCOUNT;
+
+ if ((VT_DISPATCH | VT_BYREF) != pDispParams->rgvarg[4].vt ||
+ (VT_BOOL | VT_BYREF) != pDispParams->rgvarg[3].vt ||
+ VT_I4 != pDispParams->rgvarg[2].vt ||
+ VT_BSTR != pDispParams->rgvarg[1].vt ||
+ VT_BSTR != pDispParams->rgvarg[0].vt)
+ {
+ return DISP_E_BADVARTYPE;
+ }
+
+ OnNewWindow3(pDispParams->rgvarg[4].ppdispVal, pDispParams->rgvarg[3].pboolVal,
+ pDispParams->rgvarg[2].intVal, pDispParams->rgvarg[1].bstrVal, pDispParams->rgvarg[0].bstrVal);
+ return S_OK;
+
+ case DISPID_PROGRESSCHANGE:
+ if (2 != pDispParams->cArgs) return DISP_E_BADPARAMCOUNT;
+ if (VT_I4 != pDispParams->rgvarg[1].vt ||
+ VT_I4 != pDispParams->rgvarg[0].vt) return DISP_E_BADVARTYPE;
+ OnProgressChange(pDispParams->rgvarg[1].lVal, pDispParams->rgvarg[0].lVal);
+ return S_OK;
+
+ case DISPID_STATUSTEXTCHANGE:
+ if (1 != pDispParams->cArgs) return DISP_E_BADPARAMCOUNT;
+ if (VT_BSTR != pDispParams->rgvarg[0].vt) return DISP_E_BADVARTYPE;
+ OnStatusTextChange(pDispParams->rgvarg[0].bstrVal);
+ return S_OK;
+
+ case DISPID_AMBIENT_USERAGENT:
+ pvarResult->vt = VT_BSTR;
+ pvarResult->bstrVal = SysAllocString(OnGetUserAgent());
+ if (NULL != pvarResult->bstrVal)
+ return S_OK;
+ break;
+
+ case DISPID_AMBIENT_DLCONTROL:
+ pvarResult->vt = VT_I4;
+ pvarResult->lVal = OnGetDownlodFlags();
+ return S_OK;
+
+ case DISPID_COMMANDSTATECHANGE:
+ if (2 != pDispParams->cArgs) return DISP_E_BADPARAMCOUNT;
+ if (VT_I4 != pDispParams->rgvarg[1].vt ||
+ VT_BOOL != pDispParams->rgvarg[0].vt) return DISP_E_BADVARTYPE;
+ OnCommandStateChange(pDispParams->rgvarg[1].lVal, pDispParams->rgvarg[0].boolVal);
+ return S_OK;
+
+ case DISPID_SETSECURELOCKICON:
+ if (1 != pDispParams->cArgs) return DISP_E_BADPARAMCOUNT;
+ if (VT_I4 != pDispParams->rgvarg[0].vt) return DISP_E_BADVARTYPE;
+ OnSetSecureLockIcon(pDispParams->rgvarg[0].lVal);
+ return S_OK;
+
+ case DISPID_TITLECHANGE:
+ if (1 != pDispParams->cArgs) return DISP_E_BADPARAMCOUNT;
+ if (VT_BSTR != pDispParams->rgvarg[0].vt) return DISP_E_BADVARTYPE;
+ OnTitleChange(pDispParams->rgvarg[0].bstrVal);
+ return S_OK;
+
+ case DISPID_ONVISIBLE:
+ if(1 != pDispParams->cArgs) return DISP_E_BADPARAMCOUNT;
+ if (VT_BOOL != pDispParams->rgvarg[0].vt) return DISP_E_BADVARTYPE;
+ OnVisibleChange(pDispParams->rgvarg[0].boolVal);
+ return S_OK;
+
+ case DISPID_WINDOWCLOSING:
+ if (2 != pDispParams->cArgs) return DISP_E_BADPARAMCOUNT;
+ if (VT_BOOL != pDispParams->rgvarg[1].vt ||
+ (VT_BOOL|VT_BYREF) != pDispParams->rgvarg[0].vt) return DISP_E_BADVARTYPE;
+ OnWindowClosing(pDispParams->rgvarg[1].boolVal, pDispParams->rgvarg[0].pboolVal);
+ return S_OK;
+
+ case DISPID_ONTOOLBAR: return HTMLContainer2_OnShowUiElementHelper(this, uiToolbar, pDispParams);
+ case DISPID_ONMENUBAR: return HTMLContainer2_OnShowUiElementHelper(this, uiMenubar, pDispParams);
+ case DISPID_ONSTATUSBAR: return HTMLContainer2_OnShowUiElementHelper(this, uiStatusbar, pDispParams);
+ case DISPID_ONADDRESSBAR: return HTMLContainer2_OnShowUiElementHelper(this, uiAddressbar, pDispParams);
+
+ case DISPID_ONFULLSCREEN:
+ if (1 != pDispParams->cArgs) return DISP_E_BADPARAMCOUNT;
+ if (VT_BOOL != pDispParams->rgvarg[0].vt) DISP_E_BADVARTYPE;
+ OnEnableFullscreen(pDispParams->rgvarg[0].boolVal);
+ return S_OK;
+
+ case DISPID_WINDOWSETRESIZABLE:
+ if (1 != pDispParams->cArgs) return DISP_E_BADPARAMCOUNT;
+ if (VT_BOOL != pDispParams->rgvarg[0].vt) DISP_E_BADVARTYPE;
+ OnWindowSetResizable(pDispParams->rgvarg[0].boolVal);
+ return S_OK;
+
+ case DISPID_CLIENTTOHOSTWINDOW:
+ if (2 != pDispParams->cArgs) return DISP_E_BADPARAMCOUNT;
+ if ((VT_BYREF|VT_I4) != pDispParams->rgvarg[1].vt ||
+ (VT_BYREF|VT_I4) != pDispParams->rgvarg[0].vt) return DISP_E_BADVARTYPE;
+ OnClientToHostWindow(pDispParams->rgvarg[1].plVal, pDispParams->rgvarg[0].plVal);
+ return S_OK;
+
+ case DISPID_WINDOWSETLEFT:
+ if (1 != pDispParams->cArgs) return DISP_E_BADPARAMCOUNT;
+ if (VT_I4 != pDispParams->rgvarg[0].vt) return DISP_E_BADVARTYPE;
+ OnSetWindowPos(wndLeft, pDispParams->rgvarg[0].lVal, 0, 0, 0);
+ return S_OK;
+
+ case DISPID_WINDOWSETTOP:
+ if (1 != pDispParams->cArgs) return DISP_E_BADPARAMCOUNT;
+ if (VT_I4 != pDispParams->rgvarg[0].vt) return DISP_E_BADVARTYPE;
+ OnSetWindowPos(wndTop, 0, pDispParams->rgvarg[0].lVal, 0, 0);
+ return S_OK;
+
+ case DISPID_WINDOWSETWIDTH:
+ if (1 != pDispParams->cArgs) return DISP_E_BADPARAMCOUNT;
+ if (VT_I4 != pDispParams->rgvarg[0].vt) return DISP_E_BADVARTYPE;
+ OnSetWindowPos(wndWidth, 0, 0, pDispParams->rgvarg[0].lVal, 0);
+ return S_OK;
+
+ case DISPID_WINDOWSETHEIGHT:
+ if (1 != pDispParams->cArgs) return DISP_E_BADPARAMCOUNT;
+ if (VT_I4 != pDispParams->rgvarg[0].vt) return DISP_E_BADVARTYPE;
+ OnSetWindowPos(wndHeight, 0, 0, 0, pDispParams->rgvarg[0].lVal);
+ return S_OK;
+ }
+
+ return DISP_E_MEMBERNOTFOUND;
+}
+
+STDMETHODIMP HTMLContainer2::SetLocation(int x, int y, int width, int height)
+{
+ if (!pUnk) return E_NOINTERFACE;
+
+ SetRect(&rect, x, y, x + width, y + height);
+
+ IOleInPlaceObject *pipo = NULL;
+ HRESULT hr = pUnk->QueryInterface(IID_IOleInPlaceObject, (PVOID*)&pipo);
+ if (SUCCEEDED(hr))
+ {
+ hr = pipo->SetObjectRects(&rect, &rect);
+ pipo->Release();
+ }
+ return hr;
+}
+
+STDMETHODIMP HTMLContainer2::GetBorder(LPRECT lprectBorder)
+{
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP HTMLContainer2::RequestBorderSpace(LPCBORDERWIDTHS lpborderwidths)
+{
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP HTMLContainer2::SetBorderSpace(LPCBORDERWIDTHS lpborderwidths)
+{
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP HTMLContainer2::SetActiveObject(IOleInPlaceActiveObject * pActiveObject, LPCOLESTR lpszObjName)
+{
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP HTMLContainer2::SetFocus(BOOL fFocused)
+{
+ if (NULL == pUnk)
+ return E_NOINTERFACE;
+
+ if (FALSE != fFocused)
+ {
+ IOleObject *pioo = NULL;
+ if (SUCCEEDED(pUnk->QueryInterface(IID_IOleObject, (PVOID*)&pioo)))
+ {
+ pioo->DoVerb(OLEIVERB_UIACTIVATE, NULL, this, 0, hParent, &rect);
+ pioo->Release();
+ }
+ }
+ else
+ {
+ IOleInPlaceObject *pipo = NULL;
+ if (SUCCEEDED(pUnk->QueryInterface(IID_IOleInPlaceObject, (void**)&pipo)))
+ {
+ pipo->UIDeactivate();
+ pipo->Release();
+ }
+ }
+
+ return S_OK;
+}
+
+BOOL HTMLContainer2::TranslateKey(LPMSG pMsg)
+{
+ if (!pUnk) return FALSE;
+
+ IOleInPlaceActiveObject *pao = NULL;
+ HRESULT hr = pUnk->QueryInterface(IID_IOleInPlaceActiveObject, (PVOID*)&pao);
+ if (SUCCEEDED(hr))
+ {
+ hr = pao->TranslateAccelerator(pMsg);
+ pao->Release();
+ }
+
+ return (S_OK == hr);
+}
+
+STDMETHODIMP HTMLContainer2::ShowHelp(HWND hwnd, LPOLESTR pszHelpFile, UINT uCommand, DWORD dwData, POINT ptMouse, IDispatch *pDispatchObjectHit)
+{
+ return S_FALSE;
+}
+
+STDMETHODIMP HTMLContainer2::ShowMessage(HWND hwnd, LPOLESTR lpstrText, LPOLESTR lpstrCaption, DWORD dwType, LPOLESTR lpstrHelpFile, DWORD dwHelpContext, LRESULT *plResult)
+{
+ return S_FALSE;
+}
+
+// ***********************************************************************
+// IDocHostUIHandler
+// ***********************************************************************
+
+STDMETHODIMP HTMLContainer2::ShowContextMenu(DWORD dwID, POINT __RPC_FAR *ppt, IUnknown __RPC_FAR *pcmdtReserved, IDispatch __RPC_FAR *pdispReserved)
+{
+ return S_FALSE;
+}
+
+STDMETHODIMP HTMLContainer2::GetHostInfo(DOCHOSTUIINFO __RPC_FAR *pInfo)
+{
+ pInfo->cbSize = sizeof(DOCHOSTUIINFO);
+ pInfo->dwFlags = OnGetHostInfoFlags();
+
+ pInfo->pchHostCss = OnGetHostCSS();
+ pInfo->pchHostNS = OnGetHostNamespace();
+
+ return S_OK;
+}
+
+STDMETHODIMP HTMLContainer2::ShowUI(DWORD dwID, IOleInPlaceActiveObject __RPC_FAR *pActiveObject, IOleCommandTarget __RPC_FAR *pCommandTarget, IOleInPlaceFrame __RPC_FAR *pFrame, IOleInPlaceUIWindow __RPC_FAR *pDoc)
+{
+ return S_FALSE;
+}
+
+STDMETHODIMP HTMLContainer2::HideUI(void)
+{
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP HTMLContainer2::UpdateUI(void)
+{
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP HTMLContainer2::OnDocWindowActivate(BOOL fActivate)
+{
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP HTMLContainer2::OnFrameWindowActivate(BOOL fActivate)
+{
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP HTMLContainer2::ResizeBorder(LPCRECT prcBorder, IOleInPlaceUIWindow __RPC_FAR *pUIWindow, BOOL fRameWindow)
+{
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP HTMLContainer2::TranslateAccelerator(LPMSG lpMsg, const GUID __RPC_FAR *pguidCmdGroup, DWORD nCmdID)
+{
+ if ((0x8000 & GetAsyncKeyState(VK_MENU)) || (0x8000 & GetAsyncKeyState(VK_CONTROL)))
+ {
+ if (NULL != hParent && IsWindow(hParent) &&
+ PostMessageW(hParent, lpMsg->message, lpMsg->wParam, lpMsg->lParam))
+ {
+ return S_OK;
+ }
+ }
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP HTMLContainer2::GetOptionKeyPath(LPOLESTR __RPC_FAR *pchKey, DWORD dw)
+{
+ *pchKey = NULL;
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP HTMLContainer2::GetDropTarget(IDropTarget __RPC_FAR *pDropTarget, IDropTarget __RPC_FAR *__RPC_FAR *ppDropTarget)
+{
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP HTMLContainer2::GetExternal(IDispatch __RPC_FAR *__RPC_FAR *ppDispatch)
+{
+ *ppDispatch = NULL;
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP HTMLContainer2::TranslateUrl(DWORD dwTranslate, OLECHAR __RPC_FAR *pchURLIn, OLECHAR __RPC_FAR *__RPC_FAR *ppchURLOut)
+{
+ *ppchURLOut = NULL;
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP HTMLContainer2::FilterDataObject(IDataObject __RPC_FAR *pDO, IDataObject __RPC_FAR *__RPC_FAR *ppDORet)
+{
+ *ppDORet = NULL;
+ return S_FALSE;
+}
+
+STDMETHODIMP HTMLContainer2::GetOverrideKeyPath(LPOLESTR __RPC_FAR *pchKey, DWORD dw)
+{
+ *pchKey = NULL;
+ return S_FALSE;
+}
+
+STDMETHODIMP HTMLContainer2::QueryStatus(const GUID *pguidCmdGroup, ULONG cCmds, OLECMD *prgCmds, OLECMDTEXT *pCmdText)
+{
+ return OLECMDERR_E_NOTSUPPORTED;
+}
+
+STDMETHODIMP HTMLContainer2::Exec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdExecOpt, VARIANTARG *pvaIn, VARIANTARG *pvaOut)
+{
+ return OLECMDERR_E_UNKNOWNGROUP;
+}
+
+STDMETHODIMP HTMLContainer2::QueryService(REFGUID guidService, REFIID riid, void **ppv)
+{
+ return E_NOINTERFACE;
+}
+
+HRESULT HTMLContainer2::GetIUnknown(IUnknown **ppUnk)
+{
+ if (!pUnk)
+ {
+ *ppUnk = NULL;
+ return E_NOINTERFACE;
+ }
+ pUnk->AddRef();
+ *ppUnk = pUnk;
+ return S_OK;
+}
+
+HRESULT HTMLContainer2::GetIDispatch(IDispatch **ppDisp)
+{
+ *ppDisp = NULL;
+ return (pUnk) ? pUnk->QueryInterface(IID_IDispatch, (PVOID*)ppDisp) : E_NOINTERFACE;
+}
+
+HRESULT HTMLContainer2::GetIWebBrowser2(IWebBrowser2 **ppWeb2)
+{
+ *ppWeb2 = NULL;
+ return (pUnk) ? pUnk->QueryInterface(IID_IWebBrowser2, (PVOID*)ppWeb2) : E_NOINTERFACE;
+}
+
+HWND HTMLContainer2::GetHostHWND(void)
+{
+ if (NULL == pUnk) return NULL;
+
+ HWND hwndHost = NULL;
+ IOleInPlaceObject *pipo = NULL;
+ HRESULT hr = pUnk->QueryInterface(IID_IOleInPlaceObject, (PVOID *)&pipo);
+ if (SUCCEEDED(hr))
+ {
+ hr = pipo->GetWindow(&hwndHost);
+ if (FAILED(hr)) hwndHost = NULL;
+ pipo->Release();
+ }
+
+ return hwndHost;
+}
+
+HWND HTMLContainer2::GetParentHWND(void)
+{
+ return hParent;
+}
+
+STDMETHODIMP HTMLContainer2::UnadviseBrowserEvents()
+{
+ if (0 == dwCookie)
+ return S_OK;
+
+ IConnectionPoint *pcp = NULL;
+ IConnectionPointContainer *pcpc = NULL;
+ HRESULT hr = pUnk->QueryInterface(IID_IConnectionPointContainer, (PVOID*)&pcpc);
+ if (SUCCEEDED (hr))
+ {
+ hr = pcpc->FindConnectionPoint(DIID_DWebBrowserEvents2, &pcp);
+ if (SUCCEEDED(hr))
+ {
+ hr = pcp->Unadvise(dwCookie);
+ if (SUCCEEDED(hr))
+ {
+ dwCookie = 0;
+ }
+ pcp->Release();
+ }
+ pcpc->Release();
+ }
+ return hr;
+}
+
+HRESULT HTMLContainer2::PostRedirectMessage(UINT messageId, LPARAM param)
+{
+ HWND hHost = GetHostHWND();
+ if (NULL == hHost) return E_HANDLE;
+ if (FALSE == SublassControl_IsAttached(hHost)) return E_NOTIMPL;
+
+ if (0 == WM_REDIRECTNAVIGATE)
+ {
+ WM_REDIRECTNAVIGATE = RegisterWindowMessageW(L"htmlContainterRedirectNavigate");
+ if (0 == WM_REDIRECTNAVIGATE) return E_UNEXPECTED;
+ }
+
+ if (FALSE == PostMessageW(hHost, WM_REDIRECTNAVIGATE, (WPARAM)messageId, param))
+ {
+ DWORD errorCode = GetLastError();
+ return HRESULT_FROM_WIN32(errorCode);
+ }
+
+ return S_OK;
+}
+
+HRESULT HTMLContainer2::NavigateEx(IWebBrowser2 *pWeb2, VARIANT *URL, VARIANT *Flags, VARIANT *TargetFrameName, VARIANT *PostData, VARIANT *Headers)
+{
+ HRESULT hr;
+ if (NULL == pWeb2)
+ return E_NOINTERFACE;
+
+ pWeb2->AddRef();
+
+ VARIANT vtHeader;
+ BOOL performNavigate = TRUE;
+
+ VariantInit(&vtHeader);
+
+ LPCWSTR pszUserAgent = OnGetUserAgent();
+ if (!Headers && NULL != pszUserAgent && L'\0' != *pszUserAgent)
+ {
+ wchar_t szBuffer[256] = {0};
+ if (SUCCEEDED(StringCchPrintfW(szBuffer, ARRAYSIZE(szBuffer), L"User-Agent: %s", pszUserAgent)))
+ {
+ V_VT(&vtHeader) = VT_BSTR;
+ vtHeader.bstrVal = SysAllocString(szBuffer);
+ Headers = &vtHeader;
+ }
+ }
+
+ if (FALSE != ensureChakraLoaded)
+ {
+ if (NULL == GetModuleHandleW(L"mshtml.dll"))
+ {
+ HKEY hKey = NULL;
+ unsigned long disposition = 0;
+ long createResult = 0;
+ wchar_t processName[2*MAX_PATH] = {0};
+ HANDLE lock = CreateMutexW(NULL, FALSE, L"HTMLContainer2::ChakraEnabler");
+ if (NULL != lock)
+ WaitForSingleObject(lock, INFINITE);
+
+ createResult = RegCreateKeyExW(HKEY_CURRENT_USER, REGISTRY_FEATURE_USE_LEGACY_JSCRIPT,
+ NULL, NULL, REG_OPTION_VOLATILE, KEY_SET_VALUE,
+ NULL, &hKey, &disposition);
+
+ if (ERROR_SUCCESS == createResult)
+ {
+ unsigned long featureEnabled = 0;
+ if (0 != GetModuleFileNameW(NULL, processName, ARRAYSIZE(processName)))
+ {
+ featureEnabled = 0;
+ PathStripPathW(processName);
+
+ RegSetValueExW(hKey, processName, 0, REG_DWORD, (const BYTE*)&featureEnabled, sizeof(unsigned long));
+
+ hr = pWeb2->Navigate2(URL, Flags, TargetFrameName, PostData, Headers);
+ performNavigate = FALSE;
+
+ RegDeleteValueW(hKey, processName);
+ }
+
+ RegCloseKey(hKey);
+ }
+
+ if (NULL != lock)
+ {
+ ReleaseMutex(lock);
+ CloseHandle(lock);
+ }
+ }
+ ensureChakraLoaded = FALSE;
+ }
+
+ if (FALSE != performNavigate)
+ hr = pWeb2->Navigate2(URL, Flags, TargetFrameName, PostData, Headers);
+
+ VariantClear(&vtHeader);
+ pWeb2->Release();
+ return hr;
+}
+
+HRESULT HTMLContainer2::Navigate2(VARIANT *URL, VARIANT *Flags, VARIANT *TargetFrameName, VARIANT *PostData, VARIANT *Headers)
+{
+ if (NULL == pUnk)
+ return E_NOINTERFACE;
+
+ IWebBrowser2 *pWeb2 = NULL;
+ HRESULT hr = pUnk->QueryInterface(IID_IWebBrowser2, (LPVOID*)&pWeb2);
+
+ if(SUCCEEDED(hr))
+ {
+ hr = NavigateEx(pWeb2, URL, Flags, TargetFrameName, PostData, Headers);
+ pWeb2->Release();
+ }
+ return hr;
+}
+
+HRESULT HTMLContainer2::PostNavigate2(VARIANT *URL, VARIANT *Flags, VARIANT *TargetFrameName, VARIANT *PostData, VARIANT *Headers)
+{
+ RMNAVIGATE2 *param = (RMNAVIGATE2*)calloc(1, sizeof(RMNAVIGATE2));
+ if (NULL == param) return E_OUTOFMEMORY;
+
+ VariantInit(&param->URL);
+ VariantInit(&param->Flags);
+ VariantInit(&param->TargetFrameName);
+ VariantInit(&param->PostData);
+ VariantInit(&param->Headers);
+
+ if (NULL != URL) VariantCopyInd(&param->URL, URL);
+ if (NULL != Flags) VariantCopyInd(&param->Flags, Flags);
+ if (NULL != TargetFrameName) VariantCopyInd(&param->TargetFrameName, TargetFrameName);
+ if (NULL != PostData) VariantCopyInd(&param->PostData, PostData);
+ if (NULL != Headers) VariantCopyInd(&param->Headers, Headers);
+
+ HRESULT hr = PostRedirectMessage(HTMLContainer2::msgNavigate2, (LPARAM)param);
+ if (FAILED(hr))
+ {
+ VariantClear(&param->URL);
+ VariantClear(&param->Flags);
+ VariantClear(&param->TargetFrameName);
+ VariantClear(&param->PostData);
+ VariantClear(&param->Headers);
+ free(param);
+ }
+ return hr;
+}
+
+HRESULT HTMLContainer2::NavigateToNameEx(IWebBrowser2 *pWeb2, LPCWSTR pszUrl, UINT fFlags)
+{
+ VARIANT Flags;
+ VARIANT URL;
+
+ if (NULL == pszUrl || L'\0' == *pszUrl)
+ {
+ pszUrl = L"about:blank";
+ }
+
+ VariantInit(&Flags);
+ V_VT(&Flags) = VT_I4;
+ V_I4(&Flags) = fFlags;
+
+ VariantInit(&URL);
+ V_VT(&URL) = VT_BSTR;
+ URL.bstrVal = SysAllocString(pszUrl);
+
+ HRESULT hr = (NULL != URL.bstrVal) ? NavigateEx(pWeb2, &URL, &Flags, NULL, NULL, NULL) : E_FAIL;
+
+ VariantClear(&URL);
+ VariantClear(&Flags);
+ return hr;
+}
+
+HRESULT HTMLContainer2::NavigateToName(LPCWSTR pszUrl, UINT fFlags)
+{
+ if (NULL == pUnk)
+ return E_NOINTERFACE;
+
+ IWebBrowser2 *pWeb2 = NULL;
+ HRESULT hr = pUnk->QueryInterface(IID_IWebBrowser2, (LPVOID*)&pWeb2);
+
+ if(SUCCEEDED(hr))
+ {
+ hr = NavigateToNameEx(pWeb2, pszUrl, fFlags);
+ pWeb2->Release();
+ }
+ return hr;
+}
+
+HRESULT HTMLContainer2::PostNavigateToName(LPCWSTR pszUrl, UINT fFlags)
+{
+ RMNAVIGATETONAME *param = (RMNAVIGATETONAME*)calloc(1, sizeof(RMNAVIGATETONAME));
+ if (NULL == param) return E_OUTOFMEMORY;
+
+ param->url = SysAllocString(pszUrl);
+ param->flags = fFlags;
+
+ HRESULT hr = PostRedirectMessage(HTMLContainer2::msgNavigateToName, (LPARAM)param);
+ if (FAILED(hr))
+ {
+ SysFreeString(param->url);
+ free(param);
+ }
+ return hr;
+}
+
+HRESULT HTMLContainer2::WriteHTML(LPCWSTR pszHTML)
+{
+ BSTR data = SysAllocString(pszHTML);
+ if (NULL == data && NULL != pszHTML)
+ return E_OUTOFMEMORY;
+
+ HRESULT hr = WriteDocument(data);
+ if (FAILED(hr))
+ {
+ SysFreeString(data);
+ }
+
+ return hr;
+}
+
+HRESULT HTMLContainer2::WriteDocument(BSTR data)
+{
+ IWebBrowser2 *pWeb2 = NULL;
+ HRESULT hr = GetIWebBrowser2(&pWeb2);
+ if (FAILED(hr)) return hr;
+
+ IDispatch *pDisp = NULL;
+ hr = pWeb2->get_Document(&pDisp);
+ if (S_OK == hr)
+ {
+ IHTMLDocument2 *pDoc = NULL;
+ hr = pDisp->QueryInterface(IID_IHTMLDocument2, (void**)&pDoc);
+ if (SUCCEEDED(hr))
+ {
+ SAFEARRAY *array = SafeArrayCreateVector(VT_VARIANT, 0, 1);
+ if (NULL != array)
+ {
+ VARIANT *item = NULL;
+ hr = SafeArrayAccessData(array, (void**)&item);
+ if (SUCCEEDED(hr))
+ {
+ VariantInit(item);
+ V_VT(item) = VT_BSTR;
+ V_BSTR(item) = data;
+
+ hr = pDoc->write(array);
+ pDoc->close();
+
+ if (FAILED(hr))
+ V_BSTR(item) = NULL;
+
+ SafeArrayUnaccessData(array);
+ }
+ SafeArrayDestroy(array);
+ }
+ else
+ {
+ hr = E_OUTOFMEMORY;
+ }
+
+ pDoc->Release();
+ }
+ pDisp->Release();
+ }
+ pWeb2->Release();
+ return hr;
+}
+
+COLORREF HTMLContainer2::OnGetHostBkColor(void)
+{
+ return DEFAULT_HOSTBKCOLOR;
+}
+
+DWORD HTMLContainer2::OnGetHostInfoFlags(void)
+{
+ return DEFAULT_DOCHOSTUIFLAGS;
+}
+
+OLECHAR *HTMLContainer2::OnGetHostCSS(void)
+{
+ return NULL;
+}
+
+OLECHAR *HTMLContainer2::OnGetHostNamespace(void)
+{
+ return NULL;
+}
+
+DWORD HTMLContainer2::OnGetDownlodFlags(void)
+{
+ return DEFAULT_DOWNLOADFLAGS;
+}
+
+LPCWSTR HTMLContainer2::OnGetUserAgent(void)
+{
+ return NULL;
+}
+
+HRESULT HTMLContainer2::InvokeScriptFunction(LPCWSTR pszFuncName, LCID lcid, DISPPARAMS FAR *pDispParams, VARIANT FAR *pVarResult, EXCEPINFO FAR *pExcepInfo, UINT FAR *puArgErr)
+{
+ IWebBrowser2 *pWeb2 = NULL;
+ IDispatch *pDisp = NULL;
+ IDispatch *pDispScript = NULL;
+ IHTMLDocument2 *pDoc = NULL;
+ DISPID dispid;
+
+ HRESULT hr = GetIWebBrowser2(&pWeb2);
+ if (SUCCEEDED(hr))
+ {
+ hr = pWeb2->get_Document(&pDisp);
+ if (SUCCEEDED(hr))
+ {
+ hr = pDisp->QueryInterface(IID_IHTMLDocument2, (void**)&pDoc);
+ if (SUCCEEDED(hr))
+ {
+ hr = pDoc->get_Script(&pDispScript);
+ if (SUCCEEDED(hr))
+ {
+ hr = pDispScript->GetIDsOfNames(IID_NULL, (LPWSTR*)&pszFuncName, 1, lcid, &dispid);
+ if (SUCCEEDED(hr))
+ {
+ hr = pDispScript->Invoke(dispid, IID_NULL, lcid, DISPATCH_METHOD, pDispParams, pVarResult, pExcepInfo, puArgErr);
+ }
+ pDispScript->Release();
+ }
+ pDoc->Release();
+ }
+ pDisp->Release();
+ }
+ pWeb2->Release();
+ }
+ return hr;
+}
+
+BROWSERCB HTMLContainer2::RegisterBrowserEventCB(BROWSERCB fnBrowserCB, LPVOID pUserData)
+{
+ BROWSERCB fnTemp = this->fnBrwoserCB;
+ this->fnBrwoserCB = fnBrowserCB;
+ this->userData = pUserData;
+ return fnTemp;
+}
+
+DWORD HTMLContainer2::GetContainerStyle(void)
+{
+ return CSTYLE_NORMAL;
+}
+
+HRESULT HTMLContainer2::IsFrameset(IWebBrowser2 *pWeb2)
+{
+ if (NULL == pWeb2)
+ return E_INVALIDARG;
+
+ IDispatch *pDocDisp = NULL;
+ HRESULT hr = pWeb2->get_Document(&pDocDisp);
+ if (SUCCEEDED(hr) && NULL != pDocDisp)
+ {
+ IHTMLDocument2 *pDoc = NULL;
+ hr = pDocDisp->QueryInterface(IID_IHTMLDocument2, (void**)&pDoc);
+ if (SUCCEEDED(hr) && NULL != pDoc)
+ {
+ IHTMLElement *pElement = NULL;
+ if (SUCCEEDED(pDoc->get_body(&pElement)) && NULL != pElement)
+ {
+ IHTMLBodyElement* pBodyElement = NULL;
+ if (SUCCEEDED(pElement->QueryInterface(IID_IHTMLBodyElement, (void**)&pBodyElement)))
+ {
+ hr = S_FALSE;
+ }
+ pElement->Release();
+ }
+ pDoc->Release();
+ }
+ pDocDisp->Release();
+ }
+ return hr;
+}
+
+HRESULT HTMLContainer2::GetFramesCount(IWebBrowser2 *pWeb2, INT *frameCount)
+{
+ if (NULL == frameCount)
+ return E_POINTER;
+
+ *frameCount = 0;
+
+ if(NULL == pWeb2)
+ return E_INVALIDARG;
+
+ IDispatch *pDocDisp = NULL;
+ HRESULT hr = pWeb2->get_Document(&pDocDisp);
+ if (SUCCEEDED(hr) && NULL != pDocDisp)
+ {
+ IOleContainer *pContainer = NULL;
+ hr = pDocDisp->QueryInterface(IID_IOleContainer, (void**)&pContainer);
+ if (SUCCEEDED(hr))
+ {
+ IEnumUnknown* pEnumerator = NULL;
+ hr = pContainer->EnumObjects(OLECONTF_EMBEDDINGS, &pEnumerator);
+ if (SUCCEEDED(hr))
+ {
+ IUnknown* pUnknown = NULL;
+ ULONG uFetched = 0;
+ for (UINT i = 0; S_OK == pEnumerator->Next(1, &pUnknown, &uFetched); i++)
+ {
+ IWebBrowser2* pBrowser = NULL;
+ if (SUCCEEDED(pUnknown->QueryInterface(IID_IWebBrowser2, (void**)&pBrowser)))
+ {
+ if (S_OK == IsFrameset(pBrowser))
+ {
+ INT f = 0;
+ if (SUCCEEDED(GetFramesCount(pBrowser, &f)))
+ *frameCount += f;
+ }
+ else
+ {
+ (*frameCount)++;
+ }
+ pBrowser->Release();
+ }
+ pUnknown->Release();
+ }
+ pEnumerator->Release();
+ }
+ pContainer->Release();
+ }
+ pDocDisp->Release();
+ }
+ return hr;
+}
+
+BOOL HTMLContainer2::ValidateURLHost(LPCWSTR pszUrl)
+{
+ if (NULL == libWininet.InternetCrackUrl)
+ {
+ HTMLContainer2_LoadLibray((LIBHDR*)&libWininet);
+ if (NULL == libWininet.InternetCrackUrl)
+ {
+ return TRUE;
+ }
+ }
+
+ if (NULL == pszUrl || L'\0' == *pszUrl)
+ return FALSE;
+
+ URL_COMPONENTSW uc;
+ WCHAR szHost[2048] = {0};
+
+ ZeroMemory(&uc, sizeof(URL_COMPONENTSW));
+ uc.dwStructSize = sizeof(URL_COMPONENTSW);
+ uc.lpszHostName = szHost;
+ uc.dwHostNameLength = ARRAYSIZE(szHost);
+
+ if (FALSE == libWininet.InternetCrackUrl(pszUrl, 0, NULL, &uc))
+ {
+ // ERROR_INTERNET_INVALID_URL
+ DWORD error = GetLastError();
+
+ BSTR version = NULL;
+ if (SUCCEEDED(GetAppVersion(&version)) && NULL != version)
+ {
+ if (NULL != StrStrIW(version, L"MSIE 6.0"))
+ {
+ LPCWSTR c = pszUrl;
+ while(c && L'\0' != *c)
+ {
+ if (L'?' == *c)
+ {
+ WCHAR szTemp[2048] = {0};
+ if (SUCCEEDED(StringCchCopyNW(szTemp, ARRAYSIZE(szTemp), pszUrl, (c - pszUrl))) &&
+ FALSE != libWininet.InternetCrackUrl(szTemp, 0, NULL, &uc))
+ {
+ error = ERROR_SUCCESS;
+ }
+ break;
+ }
+ c++;
+ }
+ }
+
+ SysFreeString(version);
+ }
+ if (ERROR_SUCCESS != error)
+ return FALSE;
+ }
+
+ if (0 == uc.dwHostNameLength)
+ return TRUE;
+
+ switch(uc.nScheme)
+ {
+ case INTERNET_SCHEME_PARTIAL:
+ case INTERNET_SCHEME_DEFAULT:
+ case INTERNET_SCHEME_FTP:
+ case INTERNET_SCHEME_GOPHER:
+ case INTERNET_SCHEME_HTTP:
+ case INTERNET_SCHEME_HTTPS:
+ {
+ LPCWSTR block = uc.lpszHostName;
+ LPCWSTR end = block + uc.dwHostNameLength;
+ LPCWSTR cursor = block;
+
+ for(;;)
+ {
+ if (cursor == end || L'.' == *cursor)
+ {
+ if (block == cursor && end != cursor)
+ return FALSE;
+
+ if (end == cursor)
+ return TRUE;
+ }
+ cursor++;
+ }
+ }
+ break;
+ case INTERNET_SCHEME_RES:
+ case INTERNET_SCHEME_FILE:
+ if (CSTR_EQUAL == CompareStringW(CSTR_INVARIANT, 0, L"shdoclc", -1, uc.lpszHostName, -1))
+ {
+ if (NULL != StrStrIW(pszUrl, L"syntax.htm"))
+ {
+ return FALSE;
+ }
+ }
+ break;
+ }
+ return TRUE;
+}
+
+HRESULT HTMLContainer2::GetAppVersion(BSTR *p)
+{
+ if (NULL == p)
+ return E_POINTER;
+
+ IWebBrowser2 *pWeb2 = NULL;
+ HRESULT hr = GetIWebBrowser2(&pWeb2);
+ if (FAILED(hr)) return hr;
+
+ IDispatch *pDispatch = NULL;
+ hr = pWeb2->get_Document(&pDispatch);
+ if (NULL == pDispatch) hr = E_FAIL;
+ if (SUCCEEDED(hr))
+ {
+ IHTMLDocument2 *pDoc = NULL;
+ hr = pDispatch->QueryInterface(IID_IHTMLDocument2, (void**)&pDoc);
+ if (SUCCEEDED(hr))
+ {
+ IHTMLWindow2 *pWnd = NULL;
+ hr = pDoc->get_parentWindow(&pWnd);
+ if (NULL == pWnd) hr = E_FAIL;
+ if (SUCCEEDED(hr))
+ {
+ IOmNavigator *pNavigator = NULL;
+ hr = pWnd->get_navigator(&pNavigator);
+ if (NULL == pNavigator) hr = E_FAIL;
+ if (SUCCEEDED(hr))
+ {
+ hr = pNavigator->get_appVersion(p);
+ pNavigator->Release();
+ }
+ pWnd->Release();
+ }
+ pDoc->Release();
+ }
+ pDispatch->Release();
+ }
+
+ pWeb2->Release();
+ return hr;
+}
+
+HRESULT HTMLContainer2::GetUserAgent(BSTR *p)
+{
+ if (NULL == p)
+ return E_POINTER;
+
+ IWebBrowser2 *pWeb2 = NULL;
+ HRESULT hr = GetIWebBrowser2(&pWeb2);
+ if (FAILED(hr)) return hr;
+
+ IDispatch *pDispatch = NULL;
+ hr = pWeb2->get_Document(&pDispatch);
+ if (NULL == pDispatch) hr = E_FAIL;
+ if (SUCCEEDED(hr))
+ {
+ IHTMLDocument2 *pDoc = NULL;
+ hr = pDispatch->QueryInterface(IID_IHTMLDocument2, (void**)&pDoc);
+ if (SUCCEEDED(hr))
+ {
+ IHTMLWindow2 *pWnd = NULL;
+ hr = pDoc->get_parentWindow(&pWnd);
+ if (NULL == pWnd) hr = E_FAIL;
+ if (SUCCEEDED(hr))
+ {
+ IOmNavigator *pNavigator = NULL;
+ hr = pWnd->get_navigator(&pNavigator);
+ if (NULL == pNavigator) hr = E_FAIL;
+ if (SUCCEEDED(hr))
+ {
+ hr = pNavigator->get_userAgent(p);
+ pNavigator->Release();
+ }
+ pWnd->Release();
+ }
+ pDoc->Release();
+ }
+ pDispatch->Release();
+ }
+
+ pWeb2->Release();
+ return hr;
+}
+
+void HTMLContainer2::OnBeforeNavigate(IDispatch *pDispatch, VARIANT *URL, VARIANT *Flags, VARIANT *TargetFrameName, VARIANT *PostData, VARIANT *Headers, VARIANT_BOOL *Cancel)
+{
+ if (0 == (CF_DISABLEBEFORENAVFILTER & dwFlags))
+ {
+ // follow the Winamp internet preferences options
+ if (!SendMessageW(winampWindow, WM_WA_IPC, 0, IPC_INETAVAILABLE))
+ {
+ LPCWSTR pszTemplate = L"res://";
+ LPCWSTR pszEntry = StrStrIW(URL->bstrVal, pszTemplate);
+ if (NULL == pszEntry)
+ {
+ if (StrCmpNIW(URL->bstrVal, L"about:blank", 11) && StrCmpNIW(URL->bstrVal, L"javascript:", 11))
+ {
+ *Cancel = ((VARIANT_BOOL)-2);
+ dwFlags |= CF_DISABLEBEFORENAVFILTER;
+ OnNavigateCancelled(NULL, Cancel);
+ dwFlags &= ~CF_DISABLEBEFORENAVFILTER;
+ if (NULL != Cancel && VARIANT_TRUE == *Cancel)
+ return;
+ }
+ }
+ }
+
+ if (FALSE == ValidateURLHost(URL->bstrVal))
+ {
+ VARIANT StatusCode;
+ VariantInit(&StatusCode);
+ V_VT(&StatusCode) = VT_I4;
+ V_I4(&StatusCode) = INET_E_INVALID_URL;
+
+ dwFlags |= CF_DISABLEBEFORENAVFILTER;
+
+ *Cancel = VARIANT_TRUE;
+ OnNavigateError(pDispatch, URL, TargetFrameName, &StatusCode, Cancel);
+ Navigate2(URL, NULL, TargetFrameName, NULL, NULL);
+
+ dwFlags &= ~CF_DISABLEBEFORENAVFILTER;
+ if (NULL != Cancel && VARIANT_TRUE == *Cancel)
+ return;
+ }
+
+ IWebBrowser2 *pWebMine = NULL, *pWebFrame = NULL;
+ if (FAILED(GetIWebBrowser2(&pWebMine)))
+ pWebMine = NULL;
+
+ if (NULL == pDispatch ||
+ FAILED(pDispatch->QueryInterface(IID_IWebBrowser2, (void**)&pWebFrame)))
+ {
+ pWebFrame = NULL;
+ }
+
+ if (NULL != pWebMine) pWebMine->Release();
+ if (NULL != pWebFrame) pWebFrame->Release();
+
+ if (NULL != pWebMine && pWebMine == pWebFrame && NULL != URL && VT_BSTR == URL->vt && NULL != URL->bstrVal)
+ {
+ LPCWSTR pszTemplate = L"navcancl.htm#";
+ LPCWSTR pszEntry = StrStrIW(URL->bstrVal, pszTemplate);
+ if (NULL != pszEntry)
+ {
+ dwFlags |= CF_DISABLEBEFORENAVFILTER;
+ OnNavigateCancelled(pszEntry + lstrlenW(pszTemplate), Cancel);
+ dwFlags &= ~CF_DISABLEBEFORENAVFILTER;
+
+ if (NULL != Cancel && VARIANT_TRUE == *Cancel)
+ return;
+ }
+ }
+ }
+}
+
+STDMETHODIMP HTMLContainer2::RegisterBrowserCursor(INT nSysCurID, HCURSOR hCurToUse)
+{
+ IECURSOR *pCursor = (IECURSOR*)hCursors;
+ if (pCursor)
+ {
+ for (int i =0; i < nCursors; i++)
+ {
+ if (pCursor[i].nSysId == nSysCurID)
+ {
+ if (pCursor[i].hUser) DestroyCursor(pCursor[i].hUser);
+ pCursor[i].hSys = NULL;
+ if (hCurToUse) pCursor[i].hUser = hCurToUse;
+ else
+ {
+ MoveMemory(&pCursor[i], &pCursor[i + 1], sizeof(IECURSOR)*(nCursors - i - 1));
+ nCursors--;
+ if (!nCursors)
+ {
+ free(hCursors);
+ hCursors = NULL;
+ }
+
+ VOID* data = realloc(hCursors, sizeof(IECURSOR)*nCursors);
+ if (data) hCursors = data;
+ else return E_OUTOFMEMORY;
+ }
+ return S_OK;
+ }
+ }
+ }
+
+ if (hCurToUse)
+ {
+ VOID *data = realloc(hCursors, sizeof(IECURSOR)*(nCursors + 1));
+ if (!data) return E_OUTOFMEMORY;
+ hCursors = data;
+ pCursor = (IECURSOR*)hCursors;
+ pCursor[nCursors].nSysId = nSysCurID;
+ pCursor[nCursors].hSys = NULL;
+ pCursor[nCursors].hUser = hCurToUse;
+ nCursors++;
+ }
+ return S_OK;
+}
+
+HRESULT HTMLContainer2::InternetSetFeatureEnabled(INTERNETFEATURELIST FeatureEntry, DWORD dwFlags, BOOL fEnable)
+{
+ if (NULL == libUrlmon.CoInternetSetFeatureEnabled)
+ {
+ HTMLContainer2_LoadLibray((LIBHDR*)&libUrlmon);
+ if (NULL == libUrlmon.CoInternetSetFeatureEnabled)
+ {
+ return E_FAIL;
+ }
+ }
+ return libUrlmon.CoInternetSetFeatureEnabled(FeatureEntry, dwFlags, fEnable);
+}
+
+HRESULT HTMLContainer2::InternetIsFeatureEnabled(INTERNETFEATURELIST FeatureEntry, DWORD dwFlags)
+{
+ if (NULL == libUrlmon.CoInternetIsFeatureEnabled)
+ {
+ HTMLContainer2_LoadLibray((LIBHDR*)&libUrlmon);
+ if (NULL == libUrlmon.CoInternetIsFeatureEnabled)
+ {
+ return E_FAIL;
+ }
+ }
+ return libUrlmon.CoInternetIsFeatureEnabled(FeatureEntry, dwFlags);
+}
+
+void HTMLContainer2::ProcessRedirectedMessage(HWND hwnd, UINT messageId, LPARAM param)
+{
+ switch(messageId)
+ {
+ case HTMLContainer2::msgNavigate2:
+ {
+ RMNAVIGATE2 *pnp = (RMNAVIGATE2*)param;
+ if (NULL != pnp)
+ {
+ Navigate2(&pnp->URL, &pnp->Flags, NULL, NULL, &pnp->Headers);
+ VariantClear(&pnp->URL);
+ VariantClear(&pnp->Flags);
+ VariantClear(&pnp->TargetFrameName);
+ VariantClear(&pnp->PostData);
+ VariantClear(&pnp->Headers);
+ free(pnp);
+ }
+ }
+ break;
+
+ case HTMLContainer2::msgNavigateToName:
+ {
+ RMNAVIGATETONAME *pntn = (RMNAVIGATETONAME*)param;
+ if (NULL != pntn)
+ {
+ NavigateToName(pntn->url, pntn->flags);
+ free(pntn);
+ }
+ }
+ break;
+ }
+}
+
+#define SUBCLASS_UNKNOWN 0
+#define SUBCLASS_EMBED 1
+#define SUBCLASS_DOCOBJECT 2
+#define SUBCLASS_IESERVER 3
+
+typedef struct __SUBCLASSCTRL
+{
+ UINT type;
+ WNDPROC originalProc;
+ HTMLContainer2 *container;
+} SUBCLASSCTRL;
+
+static LRESULT SubclassControl_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ SUBCLASSCTRL *psc = (SUBCLASSCTRL*)GetPropW(hwnd, WNDPROP_SCCTRLW);
+ if (NULL == psc || NULL == psc->originalProc)
+ return DefWindowProcW(hwnd, uMsg, wParam, lParam);
+
+ switch(uMsg)
+ {
+ case WM_NCDESTROY:
+ case WM_DESTROY: // detach
+ RemovePropW(hwnd, WNDPROP_SCCTRLW);
+ SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (LONGX86)(LONG_PTR)psc->originalProc);
+ CallWindowProcW(psc->originalProc, hwnd, uMsg, wParam, lParam);
+ free(psc);
+ return 0;
+
+ case WM_ERASEBKGND:
+ if( NULL != wParam &&
+ NULL != psc->container &&
+ (SUBCLASS_EMBED == psc->type || SUBCLASS_DOCOBJECT == psc->type))
+ {
+ HWND hChild = GetWindow(hwnd, GW_CHILD);
+ if (NULL == hChild || 0 == (WS_VISIBLE & GetWindowLongPtrW(hChild, GWL_STYLE)))
+ {
+ RECT paintRect;
+ if (FALSE != GetUpdateRect(hwnd, &paintRect, FALSE) ||
+ FALSE != GetClientRect(hwnd, &paintRect))
+ {
+ SetBkColor((HDC)wParam, psc->container->OnGetHostBkColor());
+ ExtTextOutW((HDC)wParam, 0, 0, ETO_OPAQUE, &paintRect, NULL, 0, NULL);
+ }
+ }
+ }
+ return 1;
+
+ case WM_SETFOCUS:
+ if (SUBCLASS_IESERVER == psc->type)
+ {
+ if (NULL != psc->container)
+ psc->container->SetFocus(TRUE);
+ }
+ else
+ {
+ HWND hFocus = (HWND)wParam;
+ if (NULL != hFocus && IsWindowEnabled(hFocus))
+ {
+ HWND hRoot = GetAncestor(hwnd, GA_ROOT);
+ if (hFocus == hRoot || IsChild(hRoot, hFocus))
+ {
+ SetFocus(hFocus);
+ return 0;
+ }
+ }
+ }
+ break;
+
+ case WM_KILLFOCUS:
+ if (SUBCLASS_IESERVER == psc->type && NULL != psc->container)
+ {
+ psc->container->SetFocus(FALSE);
+ }
+ break;
+
+ case WM_PARENTNOTIFY:
+ if ((SUBCLASS_EMBED == psc->type || SUBCLASS_DOCOBJECT == psc->type) &&
+ WM_CREATE == LOWORD(wParam) && 0L != lParam)
+ {
+ SubclassControl_Attach((HWND)lParam, psc->container);
+ }
+
+ if (SUBCLASS_EMBED == psc->type && WM_DESTROY == LOWORD(wParam) && NULL != lParam)
+ {
+ HWND hChild = (HWND)lParam;
+ WCHAR szClass[64] = {0};
+ if (0 != GetClassNameW(hChild, szClass, ARRAYSIZE(szClass)) &&
+ IS_EQUALCLASS(szClass, L"Shell DocObject View"))
+ {
+ HWND hPrimary = FindWindowExW(hwnd, NULL, L"Shell DocObject View", NULL);
+ if (hPrimary != NULL && hPrimary != hChild)
+ {
+ if(NULL != psc->container)
+ psc->container->OnClosePopupInternal();
+ }
+ }
+ }
+ break;
+
+ case WM_SETCURSOR:
+ if (SUBCLASS_IESERVER == psc->type && FALSE == bIsVistaOrHigher &&
+ NULL != psc->container && psc->container->nCursors)
+ {
+ ShowCursor(FALSE);
+ CallWindowProcW(psc->originalProc, hwnd, uMsg, wParam, lParam);
+
+ HCURSOR hCur = GetCursor();
+ IECURSOR *pCur = (IECURSOR*)(psc->container->hCursors);
+ for( int i= psc->container->nCursors -1; i > -1 ; i--)
+ {
+ if (NULL == pCur[i].hSys)
+ pCur[i].hSys = LoadCursor(NULL, MAKEINTRESOURCE(pCur[i].nSysId));
+
+ if (pCur[i].hSys == hCur)
+ {
+ if (NULL != pCur[i].hUser)
+ SetCursor(pCur[i].hUser);
+ break;
+ }
+ }
+
+ ShowCursor(TRUE);
+
+ return TRUE;
+ }
+ break;
+
+ case WM_CONTEXTMENU:
+ if (NULL != psc->container)
+ {
+ HANDLE hook = psc->container->InitializePopupHook(hwnd, uMsg, wParam, lParam);
+ if (NULL != hook)
+ {
+ LRESULT result = CallWindowProcW(psc->originalProc, hwnd, uMsg, wParam, lParam);
+ psc->container->DeletePopupHook(hook);
+ return result;
+ }
+ }
+ break;
+
+ case WM_INITMENUPOPUP:
+ if (NULL != psc->container)
+ {
+ psc->container->InitializeMenuPopup(hwnd, (HMENU)wParam, LOWORD(lParam), HIWORD(lParam));
+ }
+ break;
+
+ case WM_INPUTLANGCHANGEREQUEST:
+ if (SUBCLASS_IESERVER == psc->type &&
+ NULL != psc->container &&
+ FALSE != psc->container->InputLangChangeRequest(hwnd, (UINT)wParam, (HKL)lParam))
+ {
+ return 0;
+ }
+ break;
+
+ case WM_INPUTLANGCHANGE:
+ if (SUBCLASS_IESERVER == psc->type && NULL != psc->container)
+ psc->container->InputLangChange((UINT)wParam, (HKL)lParam);
+ break;
+ }
+
+ if (0 != WM_REDIRECTNAVIGATE && WM_REDIRECTNAVIGATE == uMsg)
+ {
+ if(SUBCLASS_EMBED == psc->type && NULL != psc->container)
+ psc->container->ProcessRedirectedMessage(hwnd, (UINT)wParam, lParam);
+ return TRUE;
+ }
+
+ if (WINAMP_WM_DIRECT_MOUSE_WHEEL == uMsg &&
+ WM_NULL != WINAMP_WM_DIRECT_MOUSE_WHEEL)
+ {
+ ReplyMessage(TRUE);
+ SendMessageW(hwnd, WM_MOUSEWHEEL, wParam, lParam);
+ return TRUE;
+ }
+
+ return CallWindowProcW(psc->originalProc, hwnd, uMsg, wParam, lParam);
+}
+
+static BOOL SublassControl_IsAttached(HWND hwnd)
+{
+ SUBCLASSCTRL *psc = (SUBCLASSCTRL*)GetPropW(hwnd, WNDPROP_SCCTRLW);
+ return (NULL != psc);
+}
+
+static BOOL SubclassControl_Attach(HWND hControl, HTMLContainer2 *pContainer)
+{
+ if (NULL == hControl || FALSE == IsWindow(hControl))
+ return FALSE;
+
+ SUBCLASSCTRL *psc = (SUBCLASSCTRL*)GetPropW(hControl, WNDPROP_SCCTRLW);
+ if (NULL != psc) return TRUE;
+
+ UINT controlType = SUBCLASS_UNKNOWN;
+
+ if (WM_NULL == WINAMP_WM_DIRECT_MOUSE_WHEEL)
+ WINAMP_WM_DIRECT_MOUSE_WHEEL = RegisterWindowMessageW(L"WINAMP_WM_DIRECT_MOUSE_WHEEL");
+
+ WCHAR szClass[128] = {0};
+ if (!GetClassNameW(hControl, szClass, ARRAYSIZE(szClass)))
+ return FALSE;
+
+ if (IS_EQUALCLASS(szClass, L"Shell Embedding")) controlType = SUBCLASS_EMBED;
+ else if (IS_EQUALCLASS(szClass, L"Shell DocObject View")) controlType = SUBCLASS_DOCOBJECT;
+ else if (IS_EQUALCLASS(szClass, L"Internet Explorer_Server")) controlType = SUBCLASS_IESERVER;
+
+ if (SUBCLASS_UNKNOWN == controlType)
+ return FALSE;
+
+ if (SUBCLASS_IESERVER != controlType)
+ {
+ DWORD styleEx = GetWindowStyleEx(hControl);
+ SetWindowLongPtrW(hControl, GWL_EXSTYLE, styleEx | WS_EX_CONTROLPARENT);
+ }
+ else
+ {
+ DWORD style = GetWindowStyle(hControl);
+ SetWindowLongPtrW(hControl, GWL_STYLE, style | WS_TABSTOP);
+ }
+
+ psc = (SUBCLASSCTRL*)calloc(1, sizeof(SUBCLASSCTRL));
+ if (NULL == psc) return FALSE;
+
+ psc->container = pContainer;
+ psc->type = controlType;
+ psc->originalProc = (WNDPROC)(LONG_PTR)SetWindowLongPtrW(hControl, GWLP_WNDPROC, (LONGX86)(LONG_PTR)SubclassControl_WindowProc);
+
+ if (NULL == psc->originalProc ||
+ FALSE == SetPropW(hControl, WNDPROP_SCCTRLW, psc))
+ {
+ if (NULL != psc->originalProc)
+ SetWindowLongPtrW(hControl, GWLP_WNDPROC, (LONGX86)(LONG_PTR)psc->originalProc);
+ free(psc);
+ return FALSE;
+ }
+
+ return TRUE;
+} \ No newline at end of file
diff --git a/Src/nu/HTMLContainer2.h b/Src/nu/HTMLContainer2.h
new file mode 100644
index 00000000..2866b780
--- /dev/null
+++ b/Src/nu/HTMLContainer2.h
@@ -0,0 +1,292 @@
+#ifndef NULLSOFT_HTMLCONTAINERH
+#define NULLSOFT_HTMLCONTAINERH
+
+#include <ocidl.h>
+#include <mshtmhst.h>
+#include <mshtmdid.h>
+#include <shlobj.h>
+#include <urlmon.h>
+/**************************************************************************
+ class definitions
+**************************************************************************/
+
+
+#ifndef DOCHOSTUIFLAG_HOST_NAVIGATES
+#define DOCHOSTUIFLAG_HOST_NAVIGATES 0x02000000
+#endif
+#ifndef DOCHOSTUIFLAG_ENABLE_REDIRECT_NOTIFICATION
+#define DOCHOSTUIFLAG_ENABLE_REDIRECT_NOTIFICATION 0x04000000
+#endif
+#ifndef DOCHOSTUIFLAG_USE_WINDOWLESS_SELECTCONTROL
+#define DOCHOSTUIFLAG_USE_WINDOWLESS_SELECTCONTROL 0x08000000
+#endif
+#ifndef DOCHOSTUIFLAG_USE_WINDOWED_SELECTCONTROL
+#define DOCHOSTUIFLAG_USE_WINDOWED_SELECTCONTROL 0x10000000
+#endif
+#ifndef DOCHOSTUIFLAG_ENABLE_ACTIVEX_INACTIVATE_MODE
+#define DOCHOSTUIFLAG_ENABLE_ACTIVEX_INACTIVATE_MODE 0x20000000
+#endif
+
+class HTMLContainer2;
+
+typedef BOOL( CALLBACK *BROWSERCB )( HTMLContainer2 *pContiner, DISPID dispId, DISPPARAMS FAR *pDispParams, LPVOID pUser ); // return TRUE to block normal processing
+
+typedef enum tagCONTAINERSTYLE
+{
+ CSTYLE_NORMAL = 0x00, // nothing
+ CSTYLE_NAVIGATE2_NOCLICKSOUND = 0x01, // prevents click sound in Nvigate2 calls
+ CSTYLE_NOCLICKSOUND = 0x02, // prevents all click sounds ( requires testing )
+
+} CONTAINERSTYLE;
+
+
+BOOL HTMLContainer2_Initialize();
+BOOL HTMLContainer2_Uninitialize();
+
+class HTMLContainer2 : public IOleClientSite,
+ public IOleInPlaceSite,
+ public IOleInPlaceFrame,
+ public IOleControlSite,
+ public IDocHostUIHandler2,
+ public IDocHostShowUI,
+ public IOleCommandTarget,
+ public IServiceProvider,
+ public IDispatch
+{
+
+ public:
+ typedef enum
+ {
+ uiToolbar = 1,
+ uiStatusbar = 2,
+ uiMenubar = 3,
+ uiAddressbar = 4,
+ } uiElement;
+
+ typedef enum
+ {
+ wndLeft = 0x0001,
+ wndTop = 0x0002,
+ wndWidth = 0x0004,
+ wndHeight = 0x0008,
+ wndRelative = 0x0010,
+ } windowPosFlags;
+
+ typedef enum
+ {
+ msgNavigate2 = 0,
+ msgNavigateToName = 1,
+ } redirectedMessage;
+
+ protected:
+ HTMLContainer2( HWND waWindow, HWND hwndParent );
+ virtual ~HTMLContainer2( void );
+
+ public:
+ // *** IUnknown Methods ***
+ STDMETHOD( QueryInterface )( REFIID riid, PVOID *ppvObject );
+ STDMETHOD_( ULONG, AddRef )( void );
+ STDMETHOD_( ULONG, Release )( void );
+
+ protected:
+ // *** IOleInPlaceUIWindow Methods ***
+ STDMETHOD( GetBorder )( LPRECT lprectBorder );
+ STDMETHOD( RequestBorderSpace )( LPCBORDERWIDTHS lpborderwidths );
+ STDMETHOD( SetBorderSpace )( LPCBORDERWIDTHS lpborderwidths );
+ STDMETHOD( SetActiveObject )( IOleInPlaceActiveObject *pActiveObject,
+ LPCOLESTR lpszObjName );
+ // *** IOleClientSite Methods ***
+ STDMETHOD( SaveObject )( );
+ STDMETHOD( GetMoniker )( DWORD dwAssign, DWORD dwWhichMoniker, LPMONIKER *ppMk );
+ STDMETHOD( GetContainer )( LPOLECONTAINER *ppContainer );
+ STDMETHOD( ShowObject )( );
+ STDMETHOD( OnShowWindow )( BOOL fShow );
+ STDMETHOD( RequestNewObjectLayout )( );
+
+ // *** IOleWindow Methods ***
+ STDMETHOD( GetWindow ) ( HWND *phwnd );
+ STDMETHOD( ContextSensitiveHelp ) ( BOOL fEnterMode );
+
+ // *** IOleInPlaceSite Methods ***
+ STDMETHOD( CanInPlaceActivate ) ( void );
+ STDMETHOD( OnInPlaceActivate ) ( void );
+ STDMETHOD( OnUIActivate ) ( void );
+ STDMETHOD( GetWindowContext ) ( IOleInPlaceFrame **ppFrame, IOleInPlaceUIWindow **ppDoc, LPRECT lprcPosRect, LPRECT lprcClipRect, LPOLEINPLACEFRAMEINFO lpFrameInfo );
+ STDMETHOD( Scroll ) ( SIZE scrollExtent );
+ STDMETHOD( OnUIDeactivate ) ( BOOL fUndoable );
+ STDMETHOD( OnInPlaceDeactivate ) ( void );
+ STDMETHOD( DiscardUndoState ) ( void );
+ STDMETHOD( DeactivateAndUndo ) ( void );
+ STDMETHOD( OnPosRectChange ) ( LPCRECT lprcPosRect );
+
+ // *** IOleInPlaceFrame Methods ***
+ STDMETHOD( InsertMenus )( HMENU hmenuShared, LPOLEMENUGROUPWIDTHS lpMenuWidths );
+ STDMETHOD( SetMenu )( HMENU hmenuShared, HOLEMENU holemenu, HWND hwndActiveObject );
+ STDMETHOD( RemoveMenus )( HMENU hmenuShared );
+ STDMETHOD( SetStatusText )( LPCOLESTR pszStatusText );
+ STDMETHOD( EnableModeless )( BOOL fEnable );
+ STDMETHOD( TranslateAccelerator )( LPMSG lpmsg, WORD wID );
+
+ // *** IOleControlSite Methods ***
+ STDMETHOD( OnControlInfoChanged )( void );
+ STDMETHOD( LockInPlaceActive )( BOOL fLock );
+ STDMETHOD( GetExtendedControl )( IDispatch **ppDisp );
+ STDMETHOD( TransformCoords )( POINTL *pptlHimetric, POINTF *pptfContainer, DWORD dwFlags );
+ STDMETHOD( TranslateAccelerator )( LPMSG pMsg, DWORD grfModifiers );
+ STDMETHOD( OnFocus )( BOOL fGotFocus );
+ STDMETHOD( ShowPropertyFrame )( void );
+
+ // *** IDispatch Methods ***
+ STDMETHOD( GetIDsOfNames )( REFIID riid, OLECHAR FAR *FAR *rgszNames, unsigned int cNames, LCID lcid, DISPID FAR *rgdispid );
+ STDMETHOD( GetTypeInfo )( unsigned int itinfo, LCID lcid, ITypeInfo FAR *FAR *pptinfo );
+ STDMETHOD( GetTypeInfoCount )( unsigned int FAR *pctinfo );
+ STDMETHOD( Invoke )( DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, EXCEPINFO FAR *pexecinfo, unsigned int FAR *puArgErr );
+
+ // *** IDocHostUIHandler Methods ***
+ STDMETHOD( ShowContextMenu )( DWORD dwID, POINT __RPC_FAR *ppt, IUnknown __RPC_FAR *pcmdtReserved, IDispatch __RPC_FAR *pdispReserved );
+ STDMETHOD( GetHostInfo )( DOCHOSTUIINFO __RPC_FAR *pInfo );
+ STDMETHOD( ShowUI )( DWORD dwID, IOleInPlaceActiveObject __RPC_FAR *pActiveObject, IOleCommandTarget __RPC_FAR *pCommandTarget, IOleInPlaceFrame __RPC_FAR *pFrame, IOleInPlaceUIWindow __RPC_FAR *pDoc );
+ STDMETHOD( HideUI )( void );
+ STDMETHOD( UpdateUI )( void );
+ STDMETHOD( OnDocWindowActivate )( BOOL fActivate );
+ STDMETHOD( OnFrameWindowActivate )( BOOL fActivate );
+ STDMETHOD( ResizeBorder )( LPCRECT prcBorder, IOleInPlaceUIWindow __RPC_FAR *pUIWindow, BOOL fRameWindow );
+ STDMETHOD( TranslateAccelerator )( LPMSG lpMsg, const GUID __RPC_FAR *pguidCmdGroup, DWORD nCmdID );
+ STDMETHOD( GetOptionKeyPath )( LPOLESTR __RPC_FAR *pchKey, DWORD dw );
+ STDMETHOD( GetDropTarget )( IDropTarget __RPC_FAR *pDropTarget, IDropTarget __RPC_FAR *__RPC_FAR *ppDropTarget );
+ STDMETHOD( GetExternal )( IDispatch __RPC_FAR *__RPC_FAR *ppDispatch );
+ STDMETHOD( TranslateUrl )( DWORD dwTranslate, OLECHAR __RPC_FAR *pchURLIn, OLECHAR __RPC_FAR *__RPC_FAR *ppchURLOut );
+ STDMETHOD( FilterDataObject )( IDataObject __RPC_FAR *pDO, IDataObject __RPC_FAR *__RPC_FAR *ppDORet );
+ // STDMETHOD (EnableModeless)(BOOL fEnable);
+
+ // *** IDocHostUIHandler2 Methods ***
+ STDMETHOD( GetOverrideKeyPath )( LPOLESTR __RPC_FAR *pchKey, DWORD dw );
+
+ // *** IDocHostShowUI ***
+ STDMETHOD( ShowHelp )( HWND hwnd, LPOLESTR pszHelpFile, UINT uCommand, DWORD dwData, POINT ptMouse, IDispatch *pDispatchObjectHit );
+ STDMETHOD( ShowMessage )( HWND hwnd, LPOLESTR lpstrText, LPOLESTR lpstrCaption, DWORD dwType, LPOLESTR lpstrHelpFile, DWORD dwHelpContext, LRESULT *plResult );
+
+ /*** IOleCommandTarget ***/
+ STDMETHOD( QueryStatus )( const GUID *pguidCmdGroup, ULONG cCmds, OLECMD *prgCmds, OLECMDTEXT *pCmdText );
+ STDMETHOD( Exec )( const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdExecOpt, VARIANTARG *pvaIn, VARIANTARG *pvaOut );
+
+ /*** IServiceProvider ***/
+ STDMETHOD( QueryService )( REFGUID guidService, REFIID riid, void **ppv );
+
+ public:
+ STDMETHOD( Initialize )( void );
+ STDMETHOD( Finish )( void );
+
+ STDMETHOD( UnadviseBrowserEvents )( void );
+
+ STDMETHOD( SetLocation )( int x, int y, int width, int height );
+ STDMETHOD( SetFocus )( BOOL fFocused );
+ virtual BOOL TranslateKey( LPMSG pMsg );
+
+ HWND GetHostHWND( void );
+ HWND GetParentHWND( void );
+
+ HRESULT NavigateEx( IWebBrowser2 *pWeb2, VARIANT *URL, VARIANT *Flags, VARIANT *TargetFrameName, VARIANT *PostData, VARIANT *Headers );
+ HRESULT Navigate2( VARIANT *URL, VARIANT *Flags, VARIANT *TargetFrameName, VARIANT *PostData, VARIANT *Headers );
+
+ HRESULT PostNavigate2( VARIANT *URL, VARIANT *Flags, VARIANT *TargetFrameName, VARIANT *PostData, VARIANT *Headers ); // navigate using postmessage API
+ HRESULT NavigateToName( LPCWSTR pszUrl, UINT fFlags );
+ HRESULT NavigateToNameEx( IWebBrowser2 *pWeb2, LPCWSTR pszUrl, UINT fFlags );
+ HRESULT PostNavigateToName( LPCWSTR pszUrl, UINT fFlags );
+ HRESULT WriteHTML( LPCWSTR pszHTML );
+ HRESULT WriteDocument( BSTR data ); // if succeeded will free bstr
+ HRESULT InvokeScriptFunction( LPCWSTR pszFuncName, LCID lcid, DISPPARAMS FAR *pDispParams, VARIANT FAR *pVarResult, EXCEPINFO FAR *pExcepInfo, UINT FAR *puArgErr );
+
+ HRESULT GetIDispatch( IDispatch **pDisp );
+ HRESULT GetIUnknown( IUnknown **pUnk );
+ HRESULT GetIWebBrowser2( IWebBrowser2 **pWeb2 );
+
+ // Registers cursors to use with browser// set hCurToUse = NULL to remove
+ // hCurToUse will be destryoed using DestroyCursor, make sure that this is not shared resource (use CopyCursor)
+ STDMETHOD( RegisterBrowserCursor )( INT nSysCurID, HCURSOR hCurToUse );
+
+ // used by MTBrowser
+ BROWSERCB RegisterBrowserEventCB( BROWSERCB fnBrowserCB, LPVOID pUserData );
+
+ static HRESULT InternetSetFeatureEnabled( INTERNETFEATURELIST FeatureEntry, DWORD dwFlags, BOOL fEnable );
+ static HRESULT InternetIsFeatureEnabled( INTERNETFEATURELIST FeatureEntry, DWORD dwFlags );
+
+ protected:
+ virtual void OnBeforeNavigate( IDispatch *pDispatch, VARIANT *URL, VARIANT *Flags, VARIANT *TargetFrameName, VARIANT *PostData, VARIANT *Headers, VARIANT_BOOL *Cancel );
+
+ virtual void OnNavigateError( IDispatch *pDispatch, VARIANT *URL, VARIANT *TargetFrameName, VARIANT *StatusCode, VARIANT_BOOL *Cancel ){}
+ virtual void OnNavigateComplete( IDispatch *pDispatch, VARIANT *URL ) {}
+ virtual void OnDocumentComplete( IDispatch *pDispatch, VARIANT *URL ) {}
+ virtual void OnDocumentReady( IDispatch *pDispatch, VARIANT *URL ) {} // top frame OnDocumentComplete
+ virtual void OnDownloadBegin( void ) {}
+ virtual void OnDownloadComplete( void ) {}
+ virtual void OnFileDownload( VARIANT_BOOL *ActiveDocument, VARIANT_BOOL *Cancel ) {}
+ virtual void OnNewWindow2( IDispatch **ppDisp, VARIANT_BOOL *Cancel ) {}
+ virtual void OnNewWindow3( IDispatch **ppDisp, VARIANT_BOOL *Cancel, DWORD dwFlags, BSTR bstrUrlContext, BSTR bstrUrl ) {}
+ virtual void OnProgressChange( long Progress, long ProgressMax ) {}
+ virtual void OnStatusTextChange( LPCWSTR pszText ) {}
+ virtual void OnCommandStateChange( LONG commandId, VARIANT_BOOL Enable ) {}
+ virtual void OnSetSecureLockIcon( UINT secureLockIcon ) {}
+ virtual void OnNavigateCancelled( LPCWSTR pszUrl, VARIANT_BOOL *Cancel ) {}
+ virtual void OnTitleChange( BSTR pszText ) {}
+ virtual void OnVisibleChange( VARIANT_BOOL fVisible ) {}
+ virtual void OnWindowClosing( VARIANT_BOOL IsChildWindow, VARIANT_BOOL *Cancel ) {}
+ virtual void OnShowUiElement( UINT elementId, VARIANT_BOOL fSHow ) {}
+ virtual void OnWindowSetResizable( VARIANT_BOOL Enable ) {}
+ virtual void OnClientToHostWindow( LONG *CX, LONG *CY ) {}
+ virtual void OnSetWindowPos( UINT flags, LONG x, LONG y, LONG cx, LONG cy ) {}
+ virtual void OnEnableFullscreen( VARIANT_BOOL Enable ) {}
+
+ virtual COLORREF OnGetHostBkColor( void );
+ virtual DWORD OnGetHostInfoFlags( void );
+ virtual OLECHAR *OnGetHostCSS( void ); // use CoTaskMemAlloc to allocate string
+ virtual OLECHAR *OnGetHostNamespace( void ); // use CoTaskMemAlloc to allocate string
+ virtual DWORD OnGetDownlodFlags( void );
+ virtual LPCWSTR OnGetUserAgent( void );
+
+ virtual DWORD GetContainerStyle( void );
+
+ BOOL ValidateURLHost( LPCWSTR pszUrl );
+
+ HRESULT IsFrameset( IWebBrowser2 *pWeb2 );
+ HRESULT GetFramesCount( IWebBrowser2 *pWeb2, INT *frameCount );
+
+ HRESULT GetAppVersion( BSTR *p );
+ HRESULT GetUserAgent( BSTR *p );
+
+ virtual HANDLE InitializePopupHook( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) { return NULL; }
+ virtual void DeletePopupHook( HANDLE hHoook ) {}
+ virtual void InitializeMenuPopup( HWND hwnd, HMENU hMenu, INT iPos, BOOL fWindowMenu ) {}
+
+ virtual void ProcessRedirectedMessage( HWND hwnd, UINT messageId, LPARAM param );
+ HRESULT PostRedirectMessage( UINT messageId, LPARAM param );
+
+ virtual BOOL InputLangChangeRequest( HWND hwnd, UINT flags, HKL hkl ) { return FALSE; }
+ virtual void InputLangChange( UINT charset, HKL hkl ) {}
+ virtual void OnClosePopupInternal() {}
+
+ private:
+ /// helpers
+ friend static HRESULT HTMLContainer2_OnShowUiElementHelper( HTMLContainer2 *instance, UINT elementId, DISPPARAMS *pDispParams );
+
+ protected:
+ LONG ref; // ref count
+ IUnknown *pUnk; // IUnknown of contained object
+ RECT rect; //
+ HWND hParent; // window handle of the container
+
+ private:
+ DWORD dwCookie;
+ DWORD dwFlags;
+ BROWSERCB fnBrwoserCB;
+ LPVOID userData;
+ BOOL bNavigating;
+ VOID *hCursors;
+ INT nCursors;
+ BOOL ensureChakraLoaded;
+ HWND winampWindow;
+
+ friend static LRESULT SubclassControl_WindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
+};
+
+#endif \ No newline at end of file
diff --git a/Src/nu/LockFreeItem.h b/Src/nu/LockFreeItem.h
new file mode 100644
index 00000000..b3cc0649
--- /dev/null
+++ b/Src/nu/LockFreeItem.h
@@ -0,0 +1,47 @@
+#pragma once
+#include <windows.h>
+
+/* This data structure holds one item, and returns you the old item when you replace it
+ it's sort of a "stack of 1" */
+
+template <class value_t>
+class LockFreeItem
+{
+public:
+ typedef value_t *ptr_t;
+ LockFreeItem() { item = 0; }
+
+ value_t *GetItem()
+ {
+ for(;;) // keep trying until we get this right
+ {
+ volatile ptr_t ret;
+ InterlockedExchangePointer(&ret, item);
+ if (InterlockedCompareExchangePointer((volatile PVOID *)&item, 0, ret) == ret)
+ return ret;
+ }
+ }
+
+ // returns the previous value
+ value_t *SetItem(value_t *new_item)
+ {
+ for(;;) // keep trying until we get this right
+ {
+ volatile ptr_t ret;
+ InterlockedExchangePointer(&ret, item);
+ if (InterlockedCompareExchangePointer((volatile PVOID *)&item, new_item, ret) == ret)
+ return ret;
+ }
+ }
+
+ // if there's already a value, returns what you passed in
+ value_t *SetItemIfZero(value_t *new_item)
+ {
+ if (InterlockedCompareExchangePointer((volatile PVOID *)&item, new_item, 0) == 0)
+ return 0;
+ else
+ return new_item;
+ }
+
+ volatile ptr_t item;
+}; \ No newline at end of file
diff --git a/Src/nu/LockFreeStack.h b/Src/nu/LockFreeStack.h
new file mode 100644
index 00000000..7c1d1b1e
--- /dev/null
+++ b/Src/nu/LockFreeStack.h
@@ -0,0 +1,156 @@
+#pragma once
+#include <windows.h>
+
+/* lock free stack object
+multiple threads can push and pop without locking
+note that order is not guaranteed. that is, if Thread 1 calls Insert before Thread 2, Thread 2's item might still make it in before.
+
+since it uses malloc/free for the linked list nodes, it might not be considered truely lock-free.
+
+use LockFreeStack2 if you can assure that you have a 'next' pointer in your class/struct
+*/
+template <class value_t>
+class LockFreeStackNode
+{
+public:
+ LockFreeStackNode(value_t _data)
+ {
+ data = _data;
+ next = 0;
+ }
+ value_t data;
+
+ LockFreeStackNode *next;
+};
+
+template <class value_t>
+class LockFreeStack
+{
+public:
+ LockFreeStack()
+ {
+ head = 0;
+ }
+
+ void push(value_t data)
+ {
+ LockFreeStackNode<value_t> *new_head = new LockFreeStackNode<value_t>(data);
+ LockFreeStackNode<value_t> *old_head = 0;
+ do
+ {
+ old_head = (LockFreeStackNode<value_t> *)head;
+ new_head->next = old_head;
+ } while (InterlockedCompareExchangePointer((volatile PVOID *)&head, new_head, old_head) != old_head);
+ }
+
+ value_t pop()
+ {
+ LockFreeStackNode<value_t> *new_head = 0, *old_head = 0;
+ do
+ {
+ old_head = (LockFreeStackNode<value_t> *)head;
+ if (old_head)
+ {
+ new_head = old_head->next;
+ }
+ else
+ {
+ new_head = 0;
+ }
+ } while (InterlockedCompareExchangePointer((volatile PVOID *)&head, new_head, old_head) != old_head);
+ value_t ret = old_head?old_head->data:0;
+ delete old_head;
+ return ret;
+ }
+
+ bool pop_ref(value_t *val)
+ {
+ LockFreeStackNode<value_t> *new_head = 0, *old_head = 0;
+ do
+ {
+ old_head = (LockFreeStackNode<value_t> *)head;
+ if (old_head)
+ {
+ new_head = old_head->next;
+ }
+ else
+ {
+ new_head = 0;
+ }
+ } while (InterlockedCompareExchangePointer((volatile PVOID *)&head, new_head, old_head) != old_head);
+ if (old_head)
+ {
+ *val = old_head->data;
+ delete old_head;
+ return true;
+ }
+
+ return false;
+ }
+
+ volatile LockFreeStackNode<value_t> *head;
+};
+
+template <class value_t>
+class LockFreeStack2
+{
+public:
+ LockFreeStack()
+ {
+ head = 0;
+ }
+
+ void push(value_t *data)
+ {
+ value_t *old_head = 0;
+ do
+ {
+ old_head = head;
+ data->next = old_head;
+ } while (InterlockedCompareExchangePointer((volatile PVOID *)&head, data, old_head) != old_head);
+ }
+
+ value_t *pop()
+ {
+ value_t *new_head = 0, *old_head = 0;
+ do
+ {
+ old_head = head;
+ if (old_head)
+ {
+ new_head = old_head->next;
+ }
+ else
+ {
+ new_head = 0;
+ }
+ } while (InterlockedCompareExchangePointer((volatile PVOID *)&head, new_head, old_head) != old_head);
+ return old_head;
+ }
+
+ bool pop_ref(value_t **val)
+ {
+ value_t *new_head = 0, *old_head = 0;
+ do
+ {
+ old_head = head;
+ if (old_head)
+ {
+ new_head = old_head->next;
+ }
+ else
+ {
+ new_head = 0;
+ }
+ } while (InterlockedCompareExchangePointer((volatile PVOID *)&head, new_head, old_head) != old_head);
+ if (old_head)
+ {
+ *val = old_head;
+ return true;
+ }
+
+ return false;
+ }
+
+ volatile value_t *head;
+}; \ No newline at end of file
diff --git a/Src/nu/MainThread.cpp b/Src/nu/MainThread.cpp
new file mode 100644
index 00000000..520ebc4c
--- /dev/null
+++ b/Src/nu/MainThread.cpp
@@ -0,0 +1,42 @@
+#include "MainThread.h"
+
+
+LRESULT CALLBACK MarshallProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ switch (message)
+ {
+ case WM_USER:
+ {
+ Lambda *lambda = (Lambda *)wParam;
+ lambda->Run();
+ delete lambda;
+ return 0;
+ }
+ default:
+ return DefWindowProc(hWnd, message, wParam, lParam);
+ }
+ return 0;
+}
+
+
+MainThread::MainThread()
+{
+ WNDCLASSEX wcex;
+ wcex.cbSize = sizeof(WNDCLASSEX);
+ wcex.style = 0;
+ wcex.lpfnWndProc = (WNDPROC)MarshallProc;
+ wcex.cbClsExtra = 0;
+ wcex.cbWndExtra = 0;
+ wcex.hInstance = GetModuleHandle(0);
+ wcex.hIcon = 0;
+ wcex.hCursor = 0;
+ wcex.hbrBackground = 0;
+ wcex.lpszMenuName = 0;
+ wcex.lpszClassName = "MainWindowMarshaller";
+ wcex.hIconSm = 0;
+ RegisterClassEx(&wcex);
+
+ mainWindow = CreateWindow( "MainWindowMarshaller", "MainWindowMarshaller", WS_DISABLED, 0, 0, 0, 0, NULL, NULL, GetModuleHandle(0), NULL);
+}
+
+MainThread mainThread;
diff --git a/Src/nu/MainThread.h b/Src/nu/MainThread.h
new file mode 100644
index 00000000..0d3e84d1
--- /dev/null
+++ b/Src/nu/MainThread.h
@@ -0,0 +1,82 @@
+#ifndef NULLSOFT_MAINTHREADH
+#define NULLSOFT_MAINTHREADH
+#include <windows.h>
+class Lambda
+{
+public:
+ virtual void Run() = 0;
+};
+
+template <class func_t, class param_t>
+class LambdaC : public Lambda
+{
+public:
+ LambdaC(func_t _func, param_t _param)
+ : func(_func), param(_param)
+ {
+ event = CreateEvent(0, FALSE, FALSE, 0);
+ }
+ ~LambdaC()
+ {
+ CloseHandle(event);
+ }
+ void Run()
+ {
+ func(param);
+ SetEvent(event);
+ }
+private:
+ HANDLE event;
+ func_t func;
+ param_t param;
+};
+
+template <class object_t, class func_t, class param_t>
+class LambdaCPP
+{
+public:
+ LambdaCPP(object_t *_object, func_t *_func, param_t _param)
+ : func(_func), param(_param)
+ {
+ event = CreateEvent(0, FALSE, FALSE, 0);
+ }
+ ~LambdaCPP()
+ {
+ CloseHandle(event);
+ }
+ void Run()
+ {
+ object->*func(param);
+ SetEvent(event);
+ }
+private:
+ HANDLE event;
+ object_t *object;
+ func_t *func;
+ param_t param;
+};
+
+class MainThread
+{
+public:
+ MainThread();
+ template <class func_t, class param_t>
+ void Run(func_t *func, param_t param)
+ {
+ Lambda *lambda = new LambdaC(func, param);
+ PostMessage(mainWindow, WM_USER, lambda, 0);
+ }
+
+ template <class object_t, class func_t, class param_t>
+ void Run(object_t *object, func_t *func, param_t param)
+ {
+ Lambda *lambda = new LambdaCPP(object, func, param);
+ PostMessage(mainWindow, WM_USER, lambda, 0);
+ }
+private:
+ HWND mainWindow;
+};
+
+extern MainThread mainThread;
+
+#endif \ No newline at end of file
diff --git a/Src/nu/MakeThunk.h b/Src/nu/MakeThunk.h
new file mode 100644
index 00000000..8538e005
--- /dev/null
+++ b/Src/nu/MakeThunk.h
@@ -0,0 +1,95 @@
+#ifndef NULLSOFT_MAKETHUNKH
+#define NULLSOFT_MAKETHUNKH
+
+#include <vector>
+
+
+class ThunkHolder
+{
+private:
+#pragma pack(push,1)
+ class ThisThunk
+ {
+ private:
+ unsigned __int8 mov_eax_imm32;
+ unsigned __int32 save_ebx;
+ unsigned __int16 mov_reax_ebx;
+ unsigned __int8 pop_ebx;
+ unsigned __int8 push_imm32;
+ unsigned __int32 m_this;
+ unsigned __int8 m_call_rel32;
+ unsigned __int32 m_rel_proc;
+ unsigned __int8 m_pop_eax;
+ unsigned __int8 m_push_ebx;
+ unsigned __int8 m_mov_ecx_imm32_2;
+ unsigned __int32 m_restore_ebx;
+ unsigned __int16 m_mov_ebx_recx;
+ unsigned __int8 m_ret;
+ unsigned __int32 m_ebx;
+ public:
+ template <class class_t, class proc_t>
+ ThisThunk(class_t *pThis, proc_t proc)
+ {
+ __int32 procAdr = *(__int32 *) & proc;
+
+
+ /* first, save ebx to memory,
+ effectively: save_ebx = ebx;
+ */
+ mov_eax_imm32 = 0xB8;
+ save_ebx = (__int32) & m_ebx;
+ mov_reax_ebx = 0x1889;
+ pop_ebx = 0x5B;
+ push_imm32 = 0x68;
+ m_this = (__int32)pThis;
+ m_call_rel32 = 0xE8;
+ m_rel_proc = procAdr - (__int32) & m_pop_eax;
+ m_pop_eax = 0x59;
+ m_push_ebx = 0x53;
+ m_mov_ecx_imm32_2 = 0xB9;
+ m_restore_ebx = (__int32) & m_ebx;
+ m_mov_ebx_recx = 0x198B;
+ m_ret = 0xC3;
+ }
+
+ /*
+ mov eax, &save_ebx
+ mov [eax], ebx
+ pop ebx
+ push pThis
+ call rel32 m_relproc
+ pop ecx
+ push ebx
+ mov ecx, &save_ebx
+ mov ebx, [ecx]
+ ret
+
+ */
+
+ };
+#pragma pack(pop)
+public:
+
+ template <class class_t, class proc_t, class this_proc_t>
+ void operator ()(class_t *pThis, proc_t &proc, this_proc_t thisProc)
+ {
+ ThisThunk *newThunk = new ThisThunk(pThis, thisProc);
+ thunks.push_back(newThunk);
+ proc = (proc_t)newThunk;
+ }
+
+ ~ThunkHolder()
+ {
+ std::vector<ThisThunk *>::iterator itr;
+ for (itr = thunks.begin();itr != thunks.end();itr++)
+ {
+ delete (*itr);
+ *itr = 0;
+ }
+ thunks.clear();
+ }
+
+ std::vector<ThisThunk *> thunks;
+};
+
+#endif
diff --git a/Src/nu/MediaLibraryInterface.cpp b/Src/nu/MediaLibraryInterface.cpp
new file mode 100644
index 00000000..2c84baca
--- /dev/null
+++ b/Src/nu/MediaLibraryInterface.cpp
@@ -0,0 +1,463 @@
+#include "MediaLibraryInterface.h"
+#include <commctrl.h>
+#include "../nu/ns_wc.h"
+#include "../winamp/wa_ipc.h"
+#include "../Agave/Language/api_language.h"
+#define _ML_HEADER_IMPMLEMENT
+#include "..\Plugins\General\gen_ml/ml_ipc_0313.h"
+#undef _ML_HEADER_IMPMLEMENT
+#include "..\Plugins\General\gen_ml/ml_imageloader.h"
+#include <shlwapi.h>
+#include <strsafe.h>
+MediaLibraryInterface mediaLibrary;
+
+void MediaLibraryInterface::AddTreeItem(MLTREEITEM &newItem)
+{
+ SendMessage(library, WM_ML_IPC, (WPARAM) &newItem, ML_IPC_TREEITEM_ADD);
+}
+
+void MediaLibraryInterface::AddTreeItem(MLTREEITEMW &newItem)
+{
+ SendMessage(library, WM_ML_IPC, (WPARAM) &newItem, ML_IPC_TREEITEM_ADDW);
+}
+
+int MediaLibraryInterface::AddTreeImage(int resourceId)
+{
+ return AddTreeImage(resourceId, -1, (BMPFILTERPROC)FILTER_DEFAULT1);
+}
+
+int MediaLibraryInterface::AddTreeImage(int resourceId, int imageIndex, BMPFILTERPROC filter)
+{
+ MLTREEIMAGE img = {instance, resourceId, imageIndex, filter, 0, 0};
+ return (int)(INT_PTR)SendMessage(library, WM_ML_IPC, (WPARAM) &img, ML_IPC_TREEIMAGE_ADD);
+}
+
+int MediaLibraryInterface::AddTreeImageBmp(int resourceId)
+{
+ HMLIMGLST hmlilNavigation = MLNavCtrl_GetImageList(library);
+ MLIMAGESOURCE mlis = {sizeof(MLIMAGESOURCE),0};
+
+ MLIMAGELISTITEM item = {0};
+ item.cbSize = sizeof( MLIMAGELISTITEM );
+ item.hmlil = hmlilNavigation;
+ item.filterUID = MLIF_FILTER1_UID;
+ item.pmlImgSource = &mlis;
+
+ mlis.hInst = instance;
+ mlis.bpp = 24;
+ mlis.lpszName = MAKEINTRESOURCEW(resourceId);
+ mlis.type = SRC_TYPE_BMP;
+ mlis.flags = ISF_FORCE_BPP;
+
+ return MLImageList_Add(library, &item);
+}
+
+void MediaLibraryInterface::SetTreeItem(MLTREEITEM &item)
+{
+ MLTREEITEMINFO ii = {0};
+ ii.item = item;
+ ii.mask = MLTI_IMAGE | MLTI_CHILDREN | MLTI_TEXT | MLTI_ID;
+
+ SendMessage(library, WM_ML_IPC, (WPARAM) &ii, ML_IPC_TREEITEM_SETINFO);
+}
+
+void MediaLibraryInterface::SetTreeItem(MLTREEITEMW &item)
+{
+ MLTREEITEMINFOW ii = {0};
+ ii.item = item;
+ ii.mask = MLTI_IMAGE | MLTI_CHILDREN | MLTI_TEXT | MLTI_ID;
+
+ SendMessage(library, WM_ML_IPC, (WPARAM) &ii, ML_IPC_TREEITEM_SETINFOW);
+}
+
+void MediaLibraryInterface::InsertTreeItem(MLTREEITEM &newItem)
+{
+ SendMessage(library, WM_ML_IPC, (WPARAM) &newItem, ML_IPC_TREEITEM_INSERT);
+}
+
+void MediaLibraryInterface::InsertTreeItem(MLTREEITEMW &newItem)
+{
+ SendMessage(library, WM_ML_IPC, (WPARAM) &newItem, ML_IPC_TREEITEM_INSERTW);
+}
+
+void MediaLibraryInterface::RemoveTreeItem(INT_PTR treeId)
+{
+ SendMessage(library, WM_ML_IPC, (WPARAM)treeId, ML_IPC_DELTREEITEM);
+}
+
+void MediaLibraryInterface::SelectTreeItem(INT_PTR treeId)
+{
+ SendMessage(library, WM_ML_IPC, (WPARAM)treeId, ML_IPC_SETCURTREEITEM);
+}
+
+INT_PTR MediaLibraryInterface::GetSelectedTreeItem(void)
+{
+ return (INT_PTR)SendMessage(library, WM_ML_IPC, 0, ML_IPC_GETCURTREEITEM);
+}
+
+void MediaLibraryInterface::UpdateTreeItem(MLTREEITEMINFO &newItem)
+{
+ SendMessage(library, WM_ML_IPC, (WPARAM) &newItem, ML_IPC_TREEITEM_SETINFO);
+}
+
+void MediaLibraryInterface::UpdateTreeItem(MLTREEITEMINFOW &newItem)
+{
+ SendMessage(library, WM_ML_IPC, (WPARAM) &newItem, ML_IPC_TREEITEM_SETINFOW);
+}
+
+int MediaLibraryInterface::SkinList(HWND list)
+{
+ return (int)(INT_PTR)SendMessage(library, WM_ML_IPC, (WPARAM)list, ML_IPC_SKIN_LISTVIEW);
+}
+
+void MediaLibraryInterface::UnskinList(int token)
+{
+ SendMessage(library, WM_ML_IPC, (WPARAM)token, ML_IPC_UNSKIN_LISTVIEW);
+}
+
+void MediaLibraryInterface::ListViewShowSort(int token, BOOL show)
+{
+ LV_SKIN_SHOWSORT lvs = {0};
+ lvs.listView = token;
+ lvs.showSort = show;
+
+ SendMessage(library, WM_ML_IPC, (WPARAM)&lvs, ML_IPC_LISTVIEW_SHOWSORT);
+}
+
+void MediaLibraryInterface::ListViewSort(int token, int columnIndex, BOOL ascending)
+{
+ LV_SKIN_SORT lvs = {0};
+ lvs.listView = token;
+ lvs.ascending = ascending;
+ lvs.columnIndex = columnIndex;
+
+ SendMessage(library, WM_ML_IPC, (WPARAM)&lvs, ML_IPC_LISTVIEW_SORT);
+}
+
+void MediaLibraryInterface::ListSkinUpdateView(int listSkin)
+{
+ SendMessage(library, WM_ML_IPC, listSkin, ML_IPC_LISTVIEW_UPDATE);
+}
+
+void *MediaLibraryInterface::GetWADLGFunc(int num)
+{
+ return (void *)SendMessage(library, WM_ML_IPC, (WPARAM)num, ML_IPC_SKIN_WADLG_GETFUNC);
+}
+
+void MediaLibraryInterface::PlayStream(wchar_t *url, bool force)
+{
+ wchar_t temp[ 2048 ] = { 0 };
+ wchar_t *end = 0;
+ StringCchCopyExW( temp, 2047, url, &end, 0, 0 );
+ if ( end )
+ {
+ end[ 1 ] = 0; // double null terminate
+
+ mlSendToWinampStruct send;
+ send.type = ML_TYPE_STREAMNAMESW;
+ send.enqueue = force ? ( 0 | 2 ) : 0;
+ send.data = (void *)temp;
+
+ SendMessage( library, WM_ML_IPC, (WPARAM)&send, ML_IPC_SENDTOWINAMP );
+ }
+}
+
+void MediaLibraryInterface::PlayStreams(std::vector<const wchar_t*> &urls, bool force)
+{
+ size_t totalSize = 0;
+ std::vector<const wchar_t*>::iterator itr;
+ for (itr = urls.begin();itr != urls.end();itr++)
+ {
+ totalSize += lstrlenW(*itr) + 1;
+ }
+ totalSize++;
+ wchar_t *sendTo = new wchar_t[totalSize];
+ wchar_t *ptr = sendTo;
+ for (itr = urls.begin();itr != urls.end();itr++)
+ {
+ //AutoChar narrow(*itr);
+ StringCchCopyExW(ptr, totalSize, *itr, &ptr, &totalSize, 0);
+
+ ptr++;
+ if (totalSize)
+ totalSize--;
+ else
+ break;
+ }
+ ptr[0] = 0;
+
+ mlSendToWinampStruct send;
+ send.type = ML_TYPE_STREAMNAMESW;
+ send.enqueue = force?(0 | 2):0;
+ send.data = sendTo;
+ SendMessage(library, WM_ML_IPC, (WPARAM)&send, ML_IPC_SENDTOWINAMP);
+ delete [] sendTo;
+}
+
+void MediaLibraryInterface::EnqueueStream(wchar_t *url, bool force)
+{
+ wchar_t temp[2048] = {0};
+ wchar_t *end=0;
+ StringCchCopyExW(temp, 2047, url, &end, 0, 0);
+ if (end)
+ {
+ end[1]=0; // double null terminate
+
+ mlSendToWinampStruct send;
+ send.type = ML_TYPE_STREAMNAMESW;
+ send.enqueue = force?(1 | 2):1;
+ send.data = (void *)temp;
+
+ SendMessage(library, WM_ML_IPC, (WPARAM)&send, ML_IPC_SENDTOWINAMP);
+ }
+}
+
+int MediaLibraryInterface::SkinComboBox(HWND comboBox)
+{
+ return (int)(INT_PTR)SendMessage(library, WM_ML_IPC, (WPARAM)comboBox, ML_IPC_SKIN_COMBOBOX);
+}
+
+void MediaLibraryInterface::UnskinComboBox(int token)
+{
+ SendMessage(library, WM_ML_IPC, (WPARAM)token, ML_IPC_UNSKIN_COMBOBOX);
+}
+
+const char *MediaLibraryInterface::GetIniDirectory()
+{
+ if (!iniDirectory)
+ {
+ iniDirectory = (const char*)SendMessage(winamp, WM_WA_IPC, 0, IPC_GETINIDIRECTORY);
+ if (((unsigned int)(ULONG_PTR)iniDirectory) < 65536)
+ iniDirectory=0;
+ }
+ return iniDirectory;
+}
+
+const wchar_t *MediaLibraryInterface::GetIniDirectoryW()
+{
+ if (!iniDirectoryW)
+ {
+ iniDirectoryW = (const wchar_t*)SendMessage(winamp, WM_WA_IPC, 0, IPC_GETINIDIRECTORYW);
+ if (((unsigned int)(ULONG_PTR)iniDirectoryW) < 65536)
+ iniDirectoryW=0;
+ }
+ return iniDirectoryW;
+}
+
+void MediaLibraryInterface::BuildPath(const wchar_t *pathEnd, wchar_t *path, size_t numChars)
+{
+ PathCombineW(path, GetIniDirectoryW(), pathEnd);
+}
+
+void MediaLibraryInterface::AddToSendTo(char description[], INT_PTR context, INT_PTR unique)
+{
+ mlAddToSendToStruct s;
+ s.context = context;
+ s.desc = description;
+ s.user32 = unique;
+
+ SendMessage(library, WM_ML_IPC, (WPARAM)&s, ML_IPC_ADDTOSENDTO);
+}
+
+void MediaLibraryInterface::AddToSendTo(wchar_t description[], INT_PTR context, INT_PTR unique)
+{
+ mlAddToSendToStructW s;
+ s.context = context;
+ s.desc = description;
+ s.user32 = unique;
+
+ SendMessage(library, WM_ML_IPC, (WPARAM)&s, ML_IPC_ADDTOSENDTOW);
+}
+
+void MediaLibraryInterface::BranchSendTo(INT_PTR context)
+{
+ mlAddToSendToStructW s = {0};
+ s.context = context;
+
+ SendMessage(library, WM_ML_IPC, (WPARAM)&s, ML_IPC_BRANCHSENDTO);
+}
+
+void MediaLibraryInterface::AddToBranchSendTo(const wchar_t description[], INT_PTR context, INT_PTR unique)
+{
+ mlAddToSendToStructW s;
+ s.context = context;
+ s.desc = const_cast<wchar_t *>(description);
+ s.user32 = unique;
+
+ SendMessage(library, WM_ML_IPC, (WPARAM)&s, ML_IPC_ADDTOBRANCH);
+}
+
+void MediaLibraryInterface::EndBranchSendTo(const wchar_t description[], INT_PTR context)
+{
+ mlAddToSendToStructW s = {0};
+ s.context = context;
+ s.desc = const_cast<wchar_t *>(description);
+
+ SendMessage(library, WM_ML_IPC, (WPARAM)&s, ML_IPC_BRANCHSENDTO);
+}
+
+void MediaLibraryInterface::PlayFile(const wchar_t *url)
+{
+ wchar_t temp[2048] = {0};
+ wchar_t *end=0;
+ StringCchCopyExW(temp, 2047, url, &end, 0, 0);
+ if (end)
+ {
+ end[1]=0; // double null terminate
+
+ mlSendToWinampStruct send;
+ send.type = ML_TYPE_FILENAMESW;
+ send.enqueue = 0 | 2;
+ send.data = (void *)temp;
+
+ SendMessage(library, WM_ML_IPC, (WPARAM)&send, ML_IPC_SENDTOWINAMP);
+ }
+}
+
+void MediaLibraryInterface::EnqueueFile(const wchar_t *url)
+{
+ wchar_t temp[2048] = {0};
+ wchar_t *end=0;
+ StringCchCopyExW(temp, 2047, url, &end, 0, 0);
+ if (end)
+ {
+ end[1]=0; // double null terminate
+
+ mlSendToWinampStruct send;
+ send.type = ML_TYPE_FILENAMESW;
+ send.enqueue = 1 | 2;
+ send.data = (void *)temp;
+
+ SendMessage(library, WM_ML_IPC, (WPARAM)&send, ML_IPC_SENDTOWINAMP);
+ }
+}
+
+void MediaLibraryInterface::BuildPluginPath(const TCHAR *filename, TCHAR *path, size_t pathSize)
+{
+ if (!pluginDirectory)
+ pluginDirectory = (const char *)SendMessage(winamp, WM_WA_IPC, 0, IPC_GETPLUGINDIRECTORY);
+#ifdef UNICODE
+ StringCchPrintf(path, pathSize, L"%S\\%s", pluginDirectory, filename);
+#else
+ StringCchPrintf(path, pathSize, "%s\\%s", pluginDirectory, filename);
+#endif
+}
+
+void MediaLibraryInterface::AddToMediaLibrary(const char *filename)
+{
+ LMDB_FILE_ADD_INFO fi = {const_cast<char *>(filename), -1, -1};
+ SendMessage(library, WM_ML_IPC, (WPARAM)&fi, ML_IPC_DB_ADDORUPDATEFILE);
+ PostMessage(library, WM_ML_IPC, 0, ML_IPC_DB_SYNCDB);
+}
+
+void MediaLibraryInterface::AddToMediaLibrary(const wchar_t *filename)
+{
+ LMDB_FILE_ADD_INFOW fi = {const_cast<wchar_t *>(filename), -1, -1};
+ SendMessage(library, WM_ML_IPC, (WPARAM)&fi, ML_IPC_DB_ADDORUPDATEFILEW);
+ PostMessage(library, WM_ML_IPC, 0, ML_IPC_DB_SYNCDB);
+}
+
+IDispatch *MediaLibraryInterface::GetDispatchObject()
+{
+ IDispatch *dispatch = (IDispatch *)SendMessage(winamp, WM_WA_IPC, 0, IPC_GET_DISPATCH_OBJECT);
+ if (dispatch == (IDispatch *)1)
+ return 0;
+ else
+ return dispatch;
+}
+
+int MediaLibraryInterface::GetUniqueDispatchId()
+{
+ return (int)(INT_PTR)SendMessage(winamp, WM_WA_IPC, 0, IPC_GET_UNIQUE_DISPATCH_ID);
+}
+
+void MediaLibraryInterface::ListSkinDisableHorizontalScrollbar(int listSkin)
+{
+ SendMessage(library, WM_ML_IPC, listSkin, ML_IPC_LISTVIEW_DISABLEHSCROLL);
+}
+
+DWORD MediaLibraryInterface::AddDispatch(wchar_t *name, IDispatch *object)
+{
+ DispatchInfo dispatchInfo;
+ dispatchInfo.name = name;
+ dispatchInfo.dispatch = object;
+
+ if (SendMessage(winamp, WM_WA_IPC, (WPARAM)&dispatchInfo, IPC_ADD_DISPATCH_OBJECT) == 0)
+ return dispatchInfo.id;
+ else
+ return 0;
+}
+
+void MediaLibraryInterface::GetFileInfo(const char *filename, char *title, int titleCch, int *length)
+{
+ basicFileInfoStruct infoStruct = {0};
+ infoStruct.filename = filename;
+ infoStruct.title = title;
+ infoStruct.titlelen = titleCch;
+
+ SendMessage(winamp, WM_WA_IPC, (WPARAM)&infoStruct, IPC_GET_BASIC_FILE_INFO);
+
+ *length = infoStruct.length;
+}
+
+void MediaLibraryInterface::GetFileInfo(const wchar_t *filename, wchar_t *title, int titleCch, int *p_length)
+{
+ if (filename)
+ {
+ basicFileInfoStructW infoStruct = {0};
+ infoStruct.filename = filename;
+ infoStruct.title = title;
+ infoStruct.titlelen = titleCch;
+
+ SendMessage(winamp, WM_WA_IPC, (WPARAM)&infoStruct, IPC_GET_BASIC_FILE_INFOW);
+
+ if ( p_length != NULL )
+ *p_length = infoStruct.length;
+ }
+ else
+ {
+ title[0] = 0;
+ *p_length = -1;
+ }
+}
+
+const char *MediaLibraryInterface::GetWinampIni()
+{
+ if (winampIni && *winampIni)
+ return winampIni;
+
+ winampIni = (const char *)SendMessage(winamp, WM_WA_IPC, 0, IPC_GETINIFILE);
+
+ return winampIni;
+}
+
+const wchar_t *MediaLibraryInterface::GetWinampIniW()
+{
+ if (winampIniW && *winampIniW)
+ return winampIniW;
+
+ winampIniW = (const wchar_t *)SendMessage(winamp, WM_WA_IPC, 0, IPC_GETINIFILEW);
+
+ return winampIniW;
+}
+
+INT_PTR MediaLibraryInterface::GetChildId(INT_PTR id)
+{
+ return SendMessage(library, WM_ML_IPC, id, ML_IPC_TREEITEM_GETCHILD_ID);
+}
+
+INT_PTR MediaLibraryInterface::GetNextId(INT_PTR id)
+{
+ return SendMessage(library, WM_ML_IPC, id, ML_IPC_TREEITEM_GETNEXT_ID);
+}
+
+void MediaLibraryInterface::RenameTreeId(INT_PTR treeId, const wchar_t *newName)
+{
+ MLTREEITEMINFOW info = {0};
+ info.mask = MLTI_TEXT;
+ info.item.size = sizeof(info.item);
+ info.item.id = treeId;
+ info.item.title = const_cast<wchar_t *>(newName);
+
+ SendMessage(library, WM_ML_IPC, (WPARAM) &info, ML_IPC_TREEITEM_SETINFOW);
+} \ No newline at end of file
diff --git a/Src/nu/MediaLibraryInterface.h b/Src/nu/MediaLibraryInterface.h
new file mode 100644
index 00000000..45e6d2f9
--- /dev/null
+++ b/Src/nu/MediaLibraryInterface.h
@@ -0,0 +1,157 @@
+
+#ifndef MEDIALIBRARYINTERFACEH
+#define MEDIALIBRARYINTERFACEH
+
+#include <windows.h>
+#include <tchar.h>
+
+#include "..\Plugins\General\gen_ml/ml.h"
+#include "../winamp/wa_ipc.h"
+
+#ifndef REPLICANT_COMPAT
+#include <vector>
+#endif
+
+
+class MediaLibraryInterface
+{
+public:
+ MediaLibraryInterface() : httpRetrieveFile( 0 ) {}
+
+ // children communicate back to the media library by SendMessage(plugin.hwndLibraryParent,WM_ML_IPC,param,ML_IPC_X);
+ int AddTreeImage( int resourceId ); // returns index of the image.
+ int AddTreeImage( int resourceId, int imageIndex, BMPFILTERPROC filter );
+ int AddTreeImageBmp( int resourceId );
+ void AddTreeItem( MLTREEITEM &newItem );
+ void AddTreeItem( MLTREEITEMW &newItem );
+ void SetTreeItem( MLTREEITEM &item );
+ void SetTreeItem( MLTREEITEMW &item );
+ void RemoveTreeItem( INT_PTR treeId );
+ void SelectTreeItem( INT_PTR treeId );
+ void UpdateTreeItem( MLTREEITEMINFO &newItem );
+ void UpdateTreeItem( MLTREEITEMINFOW &newItem );
+ INT_PTR GetSelectedTreeItem( void );
+ void RenameTreeId( INT_PTR treeId, const wchar_t *newName );
+
+ INT_PTR GetChildId( INT_PTR id );
+ INT_PTR GetNextId( INT_PTR id );
+
+ void AddToSendTo( char description[], INT_PTR context, INT_PTR unique );
+ void AddToSendTo( wchar_t description[], INT_PTR context, INT_PTR unique );
+ void BranchSendTo( INT_PTR context );
+ void AddToBranchSendTo( const wchar_t description[], INT_PTR context, INT_PTR unique );
+ void EndBranchSendTo( const wchar_t description[], INT_PTR context );
+
+ const char *GetIniDirectory();
+ const wchar_t *GetIniDirectoryW();
+ const char *GetWinampIni();
+ const wchar_t *GetWinampIniW();
+ void BuildPath( const wchar_t *pathEnd, wchar_t *path, size_t numChars );
+
+
+ int SkinList( HWND list );
+ void UnskinList( int token );
+ void ListViewShowSort( int token, BOOL show );
+ void ListViewSort( int token, int columnIndex, BOOL ascending );
+ void ListSkinUpdateView( int listSkin );
+
+ void OpenURL( const wchar_t *url ) { SendMessage( winamp, WM_WA_IPC, (WPARAM)url, IPC_OPEN_URL ); }
+
+ int SkinComboBox( HWND comboBox );
+ void UnskinComboBox( int token );
+
+ void *GetWADLGFunc( int num );
+
+ #ifndef REPLICANT_COMPAT
+ void PlayStreams( std::vector<const wchar_t *> &urls, bool force = false );
+ #endif
+
+ void PlayStream( wchar_t *url, bool force = false );
+ void PlayFile( const wchar_t *url );
+ void EnqueueFile( const wchar_t *url );
+ void EnqueueStream( wchar_t *url, bool force = true );
+ void GetFileInfo( const char *filename, char *title, int titleCch, int *length );
+ void GetFileInfo( const wchar_t *filename, wchar_t *title, int titleCch, int *length );
+
+ void ClearPlaylist() { SendMessage( winamp, WM_WA_IPC, 0, IPC_DELETE ); }
+ void SwitchToPluginView( int itemId ) { SendMessage( library, WM_ML_IPC, itemId, ML_IPC_SETCURTREEITEM ); }
+ int GetWinampVersion() { return (int)(INT_PTR)SendMessage( winamp, WM_WA_IPC, 0, IPC_GETVERSION ); }
+
+ HWND library = 0;
+ HWND winamp = 0;
+
+ HINSTANCE instance = 0;
+
+ const char *iniDirectory = 0;
+ const wchar_t *iniDirectoryW = 0;
+
+ const char *winampIni = 0;
+ const wchar_t *winampIniW = 0;
+
+ const char *pluginDirectory = 0;
+
+ void AddPreferences( prefsDlgRec &prefs ) { SendMessage( winamp, WM_WA_IPC, (WPARAM)&prefs, IPC_ADD_PREFS_DLG ); }
+ void AddPreferences( prefsDlgRecW &prefs ) { SendMessage( winamp, WM_WA_IPC, (WPARAM)&prefs, IPC_ADD_PREFS_DLGW ); }
+ void InsertTreeItem( MLTREEITEM &newItem );
+ void InsertTreeItem( MLTREEITEMW &newItem );
+
+ char *GetProxy()
+ {
+ char *proxy = (char *)SendMessage( winamp, WM_WA_IPC, 0, IPC_GET_PROXY_STRING );
+ if ( proxy == (char *)1 || strlen(proxy) == 0 )
+ return 0;
+ else
+ return proxy;
+ }
+
+ void BuildPluginPath( const TCHAR *filename, TCHAR *path, size_t pathSize );
+
+ int ( *httpRetrieveFile )( HWND hwnd, const char *url, const char *file, const char *dlgtitle );
+
+ IDispatch *GetDispatchObject();
+ int GetUniqueDispatchId();
+ void ListSkinDisableHorizontalScrollbar( int listSkin );
+
+ void AddToMediaLibrary( const char *filename );
+ void AddToMediaLibrary( const wchar_t *filename );
+
+ void GoToPreferences( int id ) { SendMessage( winamp, WM_WA_IPC, id, IPC_OPENPREFSTOPAGE ); }
+ void GoToPreferences( prefsDlgRec &prefs ) { SendMessage( winamp, WM_WA_IPC, (WPARAM)&prefs, IPC_OPENPREFSTOPAGE ); }
+ void GoToPreferences( prefsDlgRecW &prefs ) { SendMessage( winamp, WM_WA_IPC, (WPARAM)&prefs, IPC_OPENPREFSTOPAGE ); }
+
+ const char *GetFontName() { return (const char *)SendMessage( winamp, WM_WA_IPC, 1, IPC_GET_GENSKINBITMAP ); }
+ int GetFontSize() { return (int)SendMessage( winamp, WM_WA_IPC, 3, IPC_GET_GENSKINBITMAP ); }
+
+ void AddBookmark( char *bookmark )
+ {
+ COPYDATASTRUCT cds;
+ cds.dwData = IPC_ADDBOOKMARK;
+ cds.lpData = bookmark;
+ cds.cbData = (int)strlen( bookmark ) + 1;
+
+ SendMessage( winamp, WM_COPYDATA, NULL, (LPARAM)&cds );
+ }
+
+ void AddBookmarkW( wchar_t *bookmark )
+ {
+ COPYDATASTRUCT cds;
+ cds.dwData = IPC_ADDBOOKMARKW;
+ cds.lpData = bookmark;
+ cds.cbData = (int)( sizeof( wchar_t ) * wcslen( bookmark ) + sizeof( wchar_t ) );
+
+ SendMessage( winamp, WM_COPYDATA, NULL, (LPARAM)&cds );
+ }
+
+ void ShowMediaLibrary() { SendMessage( library, WM_ML_IPC, 0, ML_IPC_ENSURE_VISIBLE ); }
+
+ const char *GetExtensionList() { return (const char *)SendMessage( winamp, WM_WA_IPC, 0, IPC_GET_EXTLIST ); }
+ bool IsShowing() { return !!SendMessage( library, WM_ML_IPC, 0, ML_IPC_IS_VISIBLE ); }
+ void RefreshPrefs( int prefs ) { SendMessage( library, WM_ML_IPC, prefs, ML_IPC_REFRESH_PREFS ); }
+ void GracenoteCancelRequest() { SendMessage( library, WM_ML_IPC, GRACENOTE_CANCEL_REQUEST, ML_IPC_GRACENOTE ); }// TODO: should we post this?
+
+ DWORD AddDispatch( wchar_t *name, IDispatch *object );
+};
+
+extern MediaLibraryInterface mediaLibrary;
+
+#endif
diff --git a/Src/nu/NonBlockLock.h b/Src/nu/NonBlockLock.h
new file mode 100644
index 00000000..c9dbb2c1
--- /dev/null
+++ b/Src/nu/NonBlockLock.h
@@ -0,0 +1,343 @@
+#pragma warning (disable:4786)
+#ifndef NONBLOCKLOCKH
+#define NONBLOCKLOCKH
+#include <windows.h>
+
+
+/*
+NULLSOFT_LOCK_OUTPUT_STATUS turns on/off debugging output
+this can be VERY useful if you are trying to find a deadlock
+each time the guard is locked or unlocked, it outputs a list of
+any threads using the mutex, and their function stack
+*/
+#define NULLSOFT_LOCK_OUTPUT_STATS
+
+
+
+#ifdef NULLSOFT_LOCK_OUTPUT_STATS
+
+#include <string> // we save each function name as a string
+#include <deque> // we make a list of the recursive function stack for each thread
+#include <map> // and map
+#include <iostream> // we output to std::cerr
+#include <windows.h>
+#endif
+
+/*****
+Description:
+This class uses scoping to wrap a critical section (lightweight in-process mutex)
+The constructor enters the mutex and the destructor leaves it. This allows it to
+take advantage of automatic scoping in C++, because C++ automatically calls the destructor
+when an object leaves scope.
+
+This is _especially_ useful when you have multiple return paths, since you don't have to
+repeat mutex-leaving code.
+
+To use:
+Make a LockGuard for a resource you want to protect. The guard is shared, so make it part
+of your class, or a global, or whatever. The LockGuard is essentially a "token", equivalent
+to your mutex handle or critical section handle.
+
+Make an AutoLock object on the stack to lock. It will unlock automatically when the object
+leaves scope.
+
+Note: You'll want to make an object on the stack - don't use a heap object (new/delete)
+unless you have weird requirements and know what you are doing.
+
+Example:
+
+class MyClass
+{
+LockGuard fileGuard;
+fstream file;
+void DumpSomeData() //
+{
+AutoLock lock(fileGuard);
+file << GetData();
+}
+
+void CALLBACK NewData() // potentially called by another thread
+{
+AutoLock lock(fileGuard)
+file << newData;
+}
+};
+
+
+Tip: You can use "false scoping" to tweak the mutex lifetime, for example:
+
+void DoStuff()
+{
+a = GetData();
+{ // false scope
+AutoLock lock(dataGuard);
+DoCalculationsWith(a);
+} // mutex will release here
+SetData(a);
+}
+
+Tip: A common mistake is making a temporary object.
+i.e.
+CORRECT: AutoLock lock(fileGuard); // an AutoLock object called "lock" is put on the stack
+INCORRECT: AutoLock(fileGuard); // An unnamed temporary is created which will be destroyed IMMEDIATELY
+
+*******/
+
+namespace Nullsoft
+{
+ namespace Utility
+ {
+ class NonBlockLock;
+ /* the token which represents a resource to be locked */
+ class NonBlockLockGuard
+ {
+ public:
+ friend class NonBlockLock;
+ inline NonBlockLockGuard(char *name = "Unnamed Guard")
+ {
+#ifdef NULLSOFT_LOCK_OUTPUT_STATS
+ lockName = name;
+ InitializeCriticalSection(&cerr_cs);
+ InitializeCriticalSection(&map_cs);
+#endif
+ event=CreateEvent(NULL, FALSE, TRUE, NULL);
+ ownerThread=-1;
+ InitializeCriticalSection(&threads_cs);
+ }
+ inline ~NonBlockLockGuard()
+ {
+#ifdef NULLSOFT_LOCK_OUTPUT_STATS
+ DeleteCriticalSection(&cerr_cs);
+ DeleteCriticalSection(&map_cs);
+#endif
+ CloseHandle(event);
+ DeleteCriticalSection(&threads_cs);
+ }
+ private:
+ inline bool Lock()
+ {
+ HRESULT hr;
+ EnterCriticalSection(&threads_cs);
+ hr=WaitForSingleObject(event, 0);
+ if (hr == WAIT_TIMEOUT && ownerThread==GetCurrentThreadId())
+ {
+ LeaveCriticalSection(&threads_cs);
+ return false;
+ }
+ else if (hr == WAIT_OBJECT_0)
+ {
+ ownerThread=GetCurrentThreadId();
+ LeaveCriticalSection(&threads_cs);
+ return true;
+ }
+ LeaveCriticalSection(&threads_cs);
+
+ do
+ {
+ EnterCriticalSection(&threads_cs);
+ if (WaitForSingleObject(event, 3)==WAIT_OBJECT_0)
+ {
+ ownerThread=GetCurrentThreadId();
+ LeaveCriticalSection(&threads_cs);
+ break;
+ }
+ else
+ {
+ LeaveCriticalSection(&threads_cs);
+ MSG msg;
+ while(PeekMessage(&msg, NULL, 0, 0, 1))
+ {
+ //TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ Sleep(3);
+ }
+ } while(true);
+ return true;
+
+ }
+
+ inline void Unlock()
+ {
+ //LeaveCriticalSection(&m_cs);
+
+ EnterCriticalSection(&threads_cs);
+ ownerThread=-1;
+ SetEvent(event);
+ LeaveCriticalSection(&threads_cs);
+ }
+
+#ifdef NULLSOFT_LOCK_OUTPUT_STATS
+
+ int ThreadCount()
+ {
+ EnterCriticalSection(&map_cs);
+ int count = 0;
+ for (ThreadMap::iterator itr = threads.begin(); itr != threads.end(); itr++)
+ {
+ if (!itr->second.empty())
+ count++;
+ }
+
+ LeaveCriticalSection(&map_cs);
+ return count;
+ }
+
+ void Display()
+ {
+ EnterCriticalSection(&map_cs);
+ EnterCriticalSection(&cerr_cs);
+
+ if (ThreadCount() > 1 && owner)
+ {
+
+ std::cerr << "Guard: " << lockName << std::endl;
+ for (ThreadMap::iterator itr = threads.begin(); itr != threads.end(); itr++)
+ {
+ if (itr->second.empty())
+ continue;
+
+ std::cerr << " Thread ID: " << std::hex << itr->first << std::dec;
+ if (owner == itr->first)
+ std::cerr << " [holding the mutex] *****";
+ else
+ std::cerr << " [blocked]";
+ std::cerr << std::endl;
+ for (FunctionStack::iterator fitr = itr->second.begin(); fitr != itr->second.end(); fitr++)
+ {
+ std::cerr << " " << *fitr << "();" << std::endl;
+ }
+
+ }
+ }
+ LeaveCriticalSection(&cerr_cs);
+ LeaveCriticalSection(&map_cs);
+ }
+
+ void In(DWORD thread, char *functionName)
+ {
+ EnterCriticalSection(&map_cs);
+ threads[thread].push_back(functionName);
+ LeaveCriticalSection(&map_cs);
+ }
+
+ void Out(DWORD thread)
+ {
+ EnterCriticalSection(&map_cs);
+ threads[thread].pop_back();
+ LeaveCriticalSection(&map_cs);
+ }
+ std::string lockName;
+ CRITICAL_SECTION cerr_cs, map_cs;
+ typedef std::deque<std::string> FunctionStack; // this typedef reduce ugly c++ <>::<>::<> overkill
+ typedef std::map<DWORD, FunctionStack> ThreadMap;
+ ThreadMap threads;
+
+ DWORD owner;
+#endif
+ private:
+ //CRITICAL_SECTION m_cs;
+ CRITICAL_SECTION threads_cs;
+ HANDLE event;
+ DWORD ownerThread;
+
+
+
+ };
+
+ /* an AutoLock locks a resource (represented by a LockGuard) for the duration of its lifetime */
+ class NonBlockLock
+ {
+ public:
+ /*
+ @param functionName The function name which wants the mutex
+ we pass it in as a char * even though it'll be converted to a std::string
+ to reduce overhead when OUTPUT_STATS is off
+ */
+ inline NonBlockLock(NonBlockLockGuard &_guard, char *functionName = "function name not passed") : guard(&_guard), owner(false)
+ {
+#ifdef NULLSOFT_LOCK_OUTPUT_STATS
+ thisThread = GetCurrentThreadId();
+ guard->In(thisThread, functionName);
+ guard->Display();
+#endif
+
+ owner=guard->Lock();
+
+#ifdef NULLSOFT_LOCK_OUTPUT_STATS
+ guard->owner = thisThread;
+ guard->Display();
+#endif
+
+ }
+
+ inline void ManualLock(char *functionName = "manual lock")
+ {
+#ifdef NULLSOFT_LOCK_OUTPUT_STATS
+ thisThread = GetCurrentThreadId();
+ guard->In(thisThread,functionName);
+ guard->Display();
+#endif
+
+ owner=guard->Lock();
+
+#ifdef NULLSOFT_LOCK_OUTPUT_STATS
+ guard->owner = thisThread;
+ guard->Display();
+#endif
+ }
+
+ inline void ManualUnlock()
+ {
+ #ifdef NULLSOFT_LOCK_OUTPUT_STATS
+ guard->Display();
+#endif
+ if (owner)
+ guard->Unlock();
+
+ owner=false;
+#ifdef NULLSOFT_LOCK_OUTPUT_STATS
+
+ InterlockedCompareExchange((LONG *)(void *)&guard->owner, 0, (LONG)thisThread);
+ /* above line is functionally equivalent to:
+ if (guard->owner == thisThread)
+ guard->owner=0;
+ */
+ guard->Out(thisThread);
+ guard->Display();
+#endif
+ }
+
+ inline ~NonBlockLock()
+ {
+#ifdef NULLSOFT_LOCK_OUTPUT_STATS
+ guard->Display();
+#endif
+ if (owner)
+ guard->Unlock();
+#ifdef NULLSOFT_LOCK_OUTPUT_STATS
+
+ InterlockedCompareExchange((LONG *)(void *)&guard->owner, 0, (LONG)thisThread);
+ /* above line is functionally equivalent to:
+ if (guard->owner == thisThread)
+ guard->owner=0;
+ */
+ guard->Out(thisThread);
+ guard->Display();
+#endif
+
+ }
+
+ NonBlockLockGuard *guard;
+ bool owner;
+#ifdef NULLSOFT_LOCK_OUTPUT_STATS
+ DWORD thisThread;
+#endif
+
+ };
+
+
+ }
+}
+
+#endif
diff --git a/Src/nu/Nullsoft Utility Library.txt b/Src/nu/Nullsoft Utility Library.txt
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/Src/nu/Nullsoft Utility Library.txt
diff --git a/Src/nu/Pairs.h b/Src/nu/Pairs.h
new file mode 100644
index 00000000..80438ba6
--- /dev/null
+++ b/Src/nu/Pairs.h
@@ -0,0 +1,15 @@
+#pragma once
+namespace nu
+{
+
+ template <class A, class B>
+ class Pair
+ {
+ public:
+ Pair() {}
+ Pair(const A &_a, const B &_b) : first(_a), second(_b) {}
+
+ A first;
+ B second;
+ };
+}
diff --git a/Src/nu/ProgressTracker.h b/Src/nu/ProgressTracker.h
new file mode 100644
index 00000000..c0f1153a
--- /dev/null
+++ b/Src/nu/ProgressTracker.h
@@ -0,0 +1,125 @@
+#include <map>
+#include "AutoLock.h"
+#include <bfc/platform/types.h>
+
+class ProgressTracker
+{
+public:
+ static const uint64_t null_position;
+ ProgressTracker()
+ {
+ current_position=0;
+ current_chunk=0;
+ chunks[0]=0;
+ chunks[null_position]=null_position;
+ };
+
+ void Write(uint64_t bytes_written)
+ {
+ Nullsoft::Utility::AutoLock list_lock(list_guard);
+ ChunkList::iterator next, itr = chunks.find(current_chunk);
+ current_position += bytes_written;
+ if (itr->second < current_position)
+ itr->second = current_position;
+
+ for (;;)
+ {
+ next = itr;
+ ++next;
+ if (next != chunks.end() && (next->first <= itr->second))
+ {
+ itr->second = next->second;
+ chunks.erase(next);
+ }
+ else
+ break;
+ }
+ }
+
+ bool Valid(uint64_t requested_position, uint64_t requested_end)
+ {
+ Nullsoft::Utility::AutoLock list_lock(list_guard);
+ for (ChunkList::iterator itr=chunks.begin();itr!=chunks.end();itr++)
+ {
+ if (requested_position >= itr->first)
+ {
+ if (requested_position < itr->second)
+ {
+ if (requested_end <= itr->second)
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ bool Seek(uint64_t requested_position, uint64_t requested_end, uint64_t *new_start, uint64_t *new_end)
+ {
+ Nullsoft::Utility::AutoLock list_lock(list_guard);
+ uint64_t last_good_start=0;
+ ChunkList::iterator itr;
+ for (itr=chunks.begin();itr!=chunks.end();itr++)
+ {
+ if (requested_position >= itr->first)
+ {
+ current_chunk = itr->first;
+ if (requested_position <= itr->second)
+ {
+ ChunkList::iterator next = itr;
+ ++next;
+ *new_end = next->first;
+
+ *new_start = current_position = itr->second;
+
+ if (requested_end <= itr->second)
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ else
+ {
+ last_good_start = itr->second;
+ }
+ }
+ }
+ if (last_good_start > requested_position)
+ *new_start = current_position = last_good_start;
+ else
+ {
+
+ *new_start = current_chunk = current_position = requested_position;
+ chunks[current_chunk] = current_chunk;
+ }
+ *new_end = null_position;
+ return false;
+ }
+
+#if 0
+ void Dump()
+ {
+ ChunkList::iterator itr;
+ for (itr=chunks.begin();itr!=chunks.end();itr++)
+ {
+ printf("%I64u - %I64u\n", itr->first, itr->second);
+ }
+
+ }
+ #endif
+
+ typedef std::map<uint64_t, uint64_t> ChunkList;
+ ChunkList chunks;
+ uint64_t current_chunk;
+ uint64_t current_position;
+ Nullsoft::Utility::LockGuard list_guard;
+};
+const uint64_t ProgressTracker::null_position = (uint64_t)-1; \ No newline at end of file
diff --git a/Src/nu/ReEntry.h b/Src/nu/ReEntry.h
new file mode 100644
index 00000000..87035d29
--- /dev/null
+++ b/Src/nu/ReEntry.h
@@ -0,0 +1,68 @@
+#ifndef NULLSOFT_UTILITY_RENTRYH
+#define NULLSOFT_UTILITY_RENTRYH
+
+#include <string>
+
+
+namespace Nullsoft
+{
+ namespace Utility
+ {
+
+
+ class ReEntryGuard
+ {
+ public:
+ ReEntryGuard() : entered(false)
+ {}
+
+
+ bool FunctionCall(std::string funcName = "Unknown")
+ {
+
+ if (entered)
+ {
+ char errorMsg[256];
+ sprintf(errorMsg, "%s branched to %s", firstFunc.c_str(), funcName.c_str());
+ ::MessageBox(NULL, errorMsg, "Class ReEntry error", MB_OK);
+ return false;
+ }
+ else
+ {
+ firstFunc = funcName;
+ entered = true;
+ return true;
+ }
+
+
+ }
+ void LeaveFunction()
+ {
+ entered = false;
+ firstFunc = "";
+ }
+ private:
+ bool entered;
+ std::string firstFunc;
+
+ };
+ class ReEntry
+ {
+ public:
+ ReEntry(ReEntryGuard &_entry, std::string funcName = "Unknown") : entry(&_entry)
+ {
+ entry->FunctionCall(funcName);
+ }
+
+ ~ReEntry()
+ {
+ entry->LeaveFunction();
+ }
+
+ private:
+ ReEntryGuard *entry;
+
+ };
+ }
+}
+#endif
diff --git a/Src/nu/RedBlackTree.cpp b/Src/nu/RedBlackTree.cpp
new file mode 100644
index 00000000..8978e8e6
--- /dev/null
+++ b/Src/nu/RedBlackTree.cpp
@@ -0,0 +1,640 @@
+#include "RedBlackTree.h"
+#include <limits>
+#include "PtrList.h"
+
+#ifdef min
+#undef min
+#endif
+
+#ifdef max
+#undef max
+#endif
+
+void RedBlackTreeIterator::next()
+{
+ if (node)
+ node = tree->GetSuccessorOf(node);
+}
+
+bool RedBlackTreeIterator::get(RedBlackTreeIterator::val_t *val)
+{
+ if (node)
+ {
+ *val = node->GetEntry();
+ return true;
+ }
+ return false;
+}
+
+RedBlackTreeNode::RedBlackTreeNode()
+{
+}
+
+RedBlackTreeNode::RedBlackTreeNode(key_t _key, val_t newEntry)
+ : storedEntry(newEntry) , key(_key)
+{
+}
+
+RedBlackTreeNode::~RedBlackTreeNode()
+{
+}
+
+RedBlackTreeNode::val_t RedBlackTreeNode::GetEntry() const
+{
+ return storedEntry;
+}
+
+
+RedBlackTree::RedBlackTree()
+{
+ nil = new RedBlackTreeNode;
+ nil->left = nil->right = nil->parent = nil;
+ nil->red = 0;
+ nil->key = std::numeric_limits<key_t>::min();
+ nil->storedEntry = NULL;
+
+ root = new RedBlackTreeNode;
+ root->parent = root->left = root->right = nil;
+ root->key = std::numeric_limits<key_t>::max();
+ root->red=0;
+ root->storedEntry = NULL;
+
+ numElements = 0;
+}
+RedBlackTreeIterator RedBlackTree::end()
+{
+ RedBlackTreeIterator temp;
+ return temp;
+}
+
+RedBlackTreeIterator RedBlackTree::begin()
+{
+ return RedBlackTreeIterator(root->left, this);
+}
+/***********************************************************************/
+/* FUNCTION: LeftRotate */
+/**/
+/* INPUTS: the node to rotate on */
+/**/
+/* OUTPUT: None */
+/**/
+/* Modifies Input: this, x */
+/**/
+/* EFFECTS: Rotates as described in _Introduction_To_Algorithms by */
+/* Cormen, Leiserson, Rivest (Chapter 14). Basically this */
+/* makes the parent of x be to the left of x, x the parent of */
+/* its parent before the rotation and fixes other pointers */
+/* accordingly. */
+/***********************************************************************/
+
+void RedBlackTree::LeftRotate(RedBlackTreeNode* x)
+{
+ RedBlackTreeNode* y;
+
+ /* I originally wrote this function to use the sentinel for */
+ /* nil to avoid checking for nil. However this introduces a */
+ /* very subtle bug because sometimes this function modifies */
+ /* the parent pointer of nil. This can be a problem if a */
+ /* function which calls LeftRotate also uses the nil sentinel */
+ /* and expects the nil sentinel's parent pointer to be unchanged */
+ /* after calling this function. For example, when DeleteFixUP */
+ /* calls LeftRotate it expects the parent pointer of nil to be */
+ /* unchanged. */
+
+ y=x->right;
+ x->right=y->left;
+
+ if (y->left != nil) y->left->parent=x; /* used to use sentinel here */
+ /* and do an unconditional assignment instead of testing for nil */
+
+ y->parent=x->parent;
+
+ /* instead of checking if x->parent is the root as in the book, we */
+ /* count on the root sentinel to implicitly take care of this case */
+ if (x == x->parent->left)
+ {
+ x->parent->left=y;
+ }
+ else
+ {
+ x->parent->right=y;
+ }
+ y->left=x;
+ x->parent=y;
+
+#ifdef CHECK_RB_TREE_ASSUMPTIONS
+ CheckAssumptions();
+#elif defined(DEBUG_ASSERT)
+ Assert(!nil->red,"nil not red in RedBlackTree::LeftRotate");
+#endif
+}
+
+/***********************************************************************/
+/* FUNCTION: RighttRotate */
+/**/
+/* INPUTS: node to rotate on */
+/**/
+/* OUTPUT: None */
+/**/
+/* Modifies Input?: this, y */
+/**/
+/* EFFECTS: Rotates as described in _Introduction_To_Algorithms by */
+/* Cormen, Leiserson, Rivest (Chapter 14). Basically this */
+/* makes the parent of x be to the left of x, x the parent of */
+/* its parent before the rotation and fixes other pointers */
+/* accordingly. */
+/***********************************************************************/
+
+void RedBlackTree::RightRotate(RedBlackTreeNode* y)
+{
+ RedBlackTreeNode* x;
+
+ /* I originally wrote this function to use the sentinel for */
+ /* nil to avoid checking for nil. However this introduces a */
+ /* very subtle bug because sometimes this function modifies */
+ /* the parent pointer of nil. This can be a problem if a */
+ /* function which calls LeftRotate also uses the nil sentinel */
+ /* and expects the nil sentinel's parent pointer to be unchanged */
+ /* after calling this function. For example, when DeleteFixUP */
+ /* calls LeftRotate it expects the parent pointer of nil to be */
+ /* unchanged. */
+
+ x=y->left;
+ y->left=x->right;
+
+ if (nil != x->right) x->right->parent=y; /*used to use sentinel here */
+ /* and do an unconditional assignment instead of testing for nil */
+
+ /* instead of checking if x->parent is the root as in the book, we */
+ /* count on the root sentinel to implicitly take care of this case */
+ x->parent=y->parent;
+ if (y == y->parent->left)
+ {
+ y->parent->left=x;
+ }
+ else
+ {
+ y->parent->right=x;
+ }
+ x->right=y;
+ y->parent=x;
+
+#ifdef CHECK_RB_TREE_ASSUMPTIONS
+ CheckAssumptions();
+#elif defined(DEBUG_ASSERT)
+ Assert(!nil->red,"nil not red in RedBlackTree::RightRotate");
+#endif
+}
+
+/***********************************************************************/
+/* FUNCTION: TreeInsertHelp */
+/**/
+/* INPUTS: z is the node to insert */
+/**/
+/* OUTPUT: none */
+/**/
+/* Modifies Input: this, z */
+/**/
+/* EFFECTS: Inserts z into the tree as if it were a regular binary tree */
+/* using the algorithm described in _Introduction_To_Algorithms_ */
+/* by Cormen et al. This funciton is only intended to be called */
+/* by the Insert function and not by the user */
+/***********************************************************************/
+
+void RedBlackTree::TreeInsertHelp(RedBlackTreeNode* z)
+{
+ /* This function should only be called by RedBlackTree::Insert */
+ RedBlackTreeNode* x;
+ RedBlackTreeNode* y;
+
+ z->left=z->right=nil;
+ y=root;
+ x=root->left;
+ while (x != nil)
+ {
+ y=x;
+ if (x->key > z->key)
+ {
+ x=x->left;
+ }
+ else /* x->key <= z->key */
+ {
+ x=x->right;
+ }
+ }
+ z->parent=y;
+ if ((y == root) ||
+ (y->key > z->key))
+ {
+ y->left=z;
+ }
+ else
+ {
+ y->right=z;
+ }
+
+#if defined(DEBUG_ASSERT)
+ Assert(!nil->red,"nil not red in RedBlackTree::TreeInsertHelp");
+#endif
+}
+
+RedBlackTreeIterator RedBlackTree::Search(key_t key)
+{
+ RedBlackTreeNode* x;
+
+ x=root->left;
+ while (x != nil)
+ {
+ if (x->key > key)
+ {
+ x=x->left;
+ }
+ else if (x->key < key)
+ {
+ x=x->right;
+ }
+ else
+ {
+ return RedBlackTreeIterator(x, this);
+ }
+ }
+ return end();
+}
+
+/* Before calling InsertNode the node x should have its key set */
+
+/***********************************************************************/
+/* FUNCTION: InsertNode */
+/**/
+/* INPUTS: newEntry is the entry to insert*/
+/**/
+/* OUTPUT: This function returns a pointer to the newly inserted node */
+/* which is guarunteed to be valid until this node is deleted. */
+/* What this means is if another data structure stores this */
+/* pointer then the tree does not need to be searched when this */
+/* is to be deleted. */
+/**/
+/* Modifies Input: tree */
+/**/
+/* EFFECTS: Creates a node node which contains the appropriate key and */
+/* info pointers and inserts it into the tree. */
+/***********************************************************************/
+
+RedBlackTreeIterator RedBlackTree::Insert(key_t _key, val_t newEntry)
+{
+ RedBlackTreeNode * y;
+ RedBlackTreeNode * x;
+ RedBlackTreeNode * newNode;
+
+ x = new RedBlackTreeNode(_key, newEntry);
+ TreeInsertHelp(x);
+ newNode = x;
+ x->red=1;
+ while (x->parent->red) /* use sentinel instead of checking for root */
+ {
+ if (x->parent == x->parent->parent->left)
+ {
+ y=x->parent->parent->right;
+ if (y->red)
+ {
+ x->parent->red=0;
+ y->red=0;
+ x->parent->parent->red=1;
+ x=x->parent->parent;
+ }
+ else
+ {
+ if (x == x->parent->right)
+ {
+ x=x->parent;
+ LeftRotate(x);
+ }
+ x->parent->red=0;
+ x->parent->parent->red=1;
+ RightRotate(x->parent->parent);
+ }
+ }
+ else /* case for x->parent == x->parent->parent->right */
+ {
+ /* this part is just like the section above with */
+ /* left and right interchanged */
+ y=x->parent->parent->left;
+ if (y->red)
+ {
+ x->parent->red=0;
+ y->red=0;
+ x->parent->parent->red=1;
+ x=x->parent->parent;
+ }
+ else
+ {
+ if (x == x->parent->left)
+ {
+ x=x->parent;
+ RightRotate(x);
+ }
+ x->parent->red=0;
+ x->parent->parent->red=1;
+ LeftRotate(x->parent->parent);
+ }
+ }
+ }
+ root->left->red=0;
+ numElements++;
+ return RedBlackTreeIterator(newNode, this);
+
+#ifdef CHECK_RB_TREE_ASSUMPTIONS
+ CheckAssumptions();
+#elif defined(DEBUG_ASSERT)
+ Assert(!nil->red,"nil not red in RedBlackTree::Insert");
+ Assert(!root->red,"root not red in RedBlackTree::Insert");
+#endif
+}
+
+RedBlackTree::~RedBlackTree()
+{
+ RedBlackTreeNode * x = root->left;
+ nu::PtrList<RedBlackTreeNode> stuffToFree;
+
+ if (x != nil)
+ {
+ if (x->left != nil)
+ {
+ stuffToFree.push_back(x->left);
+ }
+ if (x->right != nil)
+ {
+ stuffToFree.push_back(x->right);
+ }
+ // delete x->storedEntry;
+ delete x;
+ while (!stuffToFree.empty())
+ {
+ x = stuffToFree.back();
+ stuffToFree.pop_back();
+ if (x->left != nil)
+ {
+ stuffToFree.push_back(x->left);
+ }
+ if (x->right != nil)
+ {
+ stuffToFree.push_back(x->right);
+ }
+ // delete x->storedEntry;
+ delete x;
+ }
+ }
+ delete nil;
+ delete root;
+}
+
+void RedBlackTree::DeleteFixUp(RedBlackTreeNode* x)
+{
+ RedBlackTreeNode * w;
+ RedBlackTreeNode * rootLeft = root->left;
+
+ while ((!x->red) && (rootLeft != x))
+ {
+ if (x == x->parent->left)
+ {
+ w=x->parent->right;
+ if (w->red)
+ {
+ w->red=0;
+ x->parent->red=1;
+ LeftRotate(x->parent);
+ w=x->parent->right;
+ }
+ if ((!w->right->red) && (!w->left->red))
+ {
+ w->red=1;
+ x=x->parent;
+ }
+ else
+ {
+ if (!w->right->red)
+ {
+ w->left->red=0;
+ w->red=1;
+ RightRotate(w);
+ w=x->parent->right;
+ }
+ w->red=x->parent->red;
+ x->parent->red=0;
+ w->right->red=0;
+ LeftRotate(x->parent);
+ x=rootLeft; /* this is to exit while loop */
+ }
+ }
+ else /* the code below is has left and right switched from above */
+ {
+ w=x->parent->left;
+ if (w->red)
+ {
+ w->red=0;
+ x->parent->red=1;
+ RightRotate(x->parent);
+ w=x->parent->left;
+ }
+ if ((!w->right->red) && (!w->left->red))
+ {
+ w->red=1;
+ x=x->parent;
+ }
+ else
+ {
+ if (!w->left->red)
+ {
+ w->right->red=0;
+ w->red=1;
+ LeftRotate(w);
+ w=x->parent->left;
+ }
+ w->red=x->parent->red;
+ x->parent->red=0;
+ w->left->red=0;
+ RightRotate(x->parent);
+ x=rootLeft; /* this is to exit while loop */
+ }
+ }
+ }
+ x->red=0;
+
+#ifdef CHECK_RB_TREE_ASSUMPTIONS
+ CheckAssumptions();
+#elif defined(DEBUG_ASSERT)
+ Assert(!nil->red,"nil not black in RedBlackTree::DeleteFixUp");
+#endif
+}
+void RedBlackTree::Delete(RedBlackTree::key_t key)
+{
+ RedBlackTreeIterator itr = Search(key);
+ DeleteNode(itr.node);
+}
+
+/***********************************************************************/
+/* FUNCTION: DeleteNode */
+/**/
+/* INPUTS: tree is the tree to delete node z from */
+/**/
+/* OUTPUT: returns the RedBlackEntry stored at deleted node */
+/**/
+/* EFFECT: Deletes z from tree and but don't call destructor */
+/**/
+/* Modifies Input: z */
+/**/
+/* The algorithm from this function is from _Introduction_To_Algorithms_ */
+/***********************************************************************/
+
+RedBlackTree::val_t RedBlackTree::DeleteNode(RedBlackTreeNode * z)
+{
+ RedBlackTreeNode* y;
+ RedBlackTreeNode* x;
+ val_t returnValue = z->storedEntry;
+
+ y= ((z->left == nil) || (z->right == nil)) ? z : GetSuccessorOf(z);
+ x= (y->left == nil) ? y->right : y->left;
+ if (root == (x->parent = y->parent)) /* assignment of y->p to x->p is intentional */
+ {
+ root->left=x;
+ }
+ else
+ {
+ if (y == y->parent->left)
+ {
+ y->parent->left=x;
+ }
+ else
+ {
+ y->parent->right=x;
+ }
+ }
+ if (y != z) /* y should not be nil in this case */
+ {
+
+#ifdef DEBUG_ASSERT
+ Assert((y!=nil),"y is nil in DeleteNode \n");
+#endif
+ /* y is the node to splice out and x is its child */
+
+ y->left=z->left;
+ y->right=z->right;
+ y->parent=z->parent;
+ z->left->parent=z->right->parent=y;
+ if (z == z->parent->left)
+ {
+ z->parent->left=y;
+ }
+ else
+ {
+ z->parent->right=y;
+ }
+ if (!(y->red))
+ {
+ y->red = z->red;
+ DeleteFixUp(x);
+ }
+ else
+ y->red = z->red;
+ delete z;
+#ifdef CHECK_RB_TREE_ASSUMPTIONS
+ CheckAssumptions();
+#elif defined(DEBUG_ASSERT)
+ Assert(!nil->red,"nil not black in RedBlackTree::Delete");
+#endif
+ }
+ else
+ {
+ if (!(y->red)) DeleteFixUp(x);
+ delete y;
+#ifdef CHECK_RB_TREE_ASSUMPTIONS
+ CheckAssumptions();
+#elif defined(DEBUG_ASSERT)
+ Assert(!nil->red,"nil not black in RedBlackTree::Delete");
+#endif
+ }
+ numElements--;
+ return returnValue;
+}
+
+size_t RedBlackTree::size() const
+{
+ return numElements;
+}
+
+/***********************************************************************/
+/* FUNCTION: GetPredecessorOf */
+/**/
+/* INPUTS: x is the node to get predecessor of */
+/**/
+/* OUTPUT: This function returns the predecessor of x or NULL if no */
+/* predecessor exists. */
+/**/
+/* Modifies Input: none */
+/**/
+/* Note: uses the algorithm in _Introduction_To_Algorithms_ */
+/***********************************************************************/
+
+RedBlackTreeNode *RedBlackTree::GetPredecessorOf(RedBlackTreeNode * x) const
+{
+ RedBlackTreeNode* y;
+
+ if (nil != (y = x->left)) /* assignment to y is intentional */
+ {
+ while (y->right != nil) /* returns the maximum of the left subtree of x */
+ {
+ y=y->right;
+ }
+ return(y);
+ }
+ else
+ {
+ y=x->parent;
+ while (x == y->left)
+ {
+ if (y == root) return(nil);
+ x=y;
+ y=y->parent;
+ }
+ return(y);
+ }
+}
+
+
+/***********************************************************************/
+/* FUNCTION: GetSuccessorOf */
+/**/
+/* INPUTS: x is the node we want the succesor of */
+/**/
+/* OUTPUT: This function returns the successor of x or NULL if no */
+/* successor exists. */
+/**/
+/* Modifies Input: none */
+/**/
+/* Note: uses the algorithm in _Introduction_To_Algorithms_ */
+/***********************************************************************/
+
+RedBlackTreeNode *RedBlackTree::GetSuccessorOf(RedBlackTreeNode * x) const
+{
+ RedBlackTreeNode* y;
+
+ if (nil != (y = x->right)) /* assignment to y is intentional */
+ {
+ while (y->left != nil) /* returns the minium of the right subtree of x */
+ {
+ y=y->left;
+ }
+ return(y);
+ }
+ else
+ {
+ y=x->parent;
+ while (x == y->right) /* sentinel used instead of checking for nil */
+ {
+ x=y;
+ y=y->parent;
+ }
+ if (y == root) return(nil);
+ return(y);
+ }
+}
diff --git a/Src/nu/RedBlackTree.h b/Src/nu/RedBlackTree.h
new file mode 100644
index 00000000..17339d2b
--- /dev/null
+++ b/Src/nu/RedBlackTree.h
@@ -0,0 +1,77 @@
+#pragma once
+// http://web.mit.edu/~emin/www/source_code/cpp_trees/index.html
+
+class RedBlackTreeNode
+{
+public:
+ typedef int key_t;
+ typedef void *val_t;
+ friend class RedBlackTree;
+public:
+ RedBlackTreeNode();
+ RedBlackTreeNode(key_t, val_t);
+ val_t GetEntry() const;
+ ~RedBlackTreeNode();
+protected:
+ val_t storedEntry;
+ key_t key;
+ int red; /* if red=0 then the node is black */
+ RedBlackTreeNode *left;
+ RedBlackTreeNode *right;
+ RedBlackTreeNode *parent;
+};
+class RedBlackTree;
+
+class RedBlackTreeIterator
+{
+public:
+ friend RedBlackTree;
+ typedef int key_t;
+ typedef void *val_t;
+ RedBlackTreeIterator() : node(0), tree(0) {}
+ RedBlackTreeIterator(RedBlackTreeNode *_node, RedBlackTree *_tree) : node(_node), tree(_tree) {}
+ void next();
+ bool get(val_t *val);
+private:
+ RedBlackTreeNode *node;
+ RedBlackTree *tree;
+};
+
+class RedBlackTree
+{
+public:
+ typedef int key_t;
+ typedef void *val_t;
+public:
+ RedBlackTree();
+ ~RedBlackTree();
+
+ RedBlackTreeIterator end();
+ RedBlackTreeIterator Insert(key_t, val_t);
+ RedBlackTreeIterator Search(key_t key);
+ RedBlackTreeIterator begin();
+// semi-public:
+ void Delete(key_t key);
+ val_t DeleteNode(RedBlackTreeNode *);
+ RedBlackTreeNode *GetPredecessorOf(RedBlackTreeNode *) const;
+ RedBlackTreeNode *GetSuccessorOf(RedBlackTreeNode *) const;
+ size_t size() const;
+
+ //TemplateStack<RedBlackTreeNode *> * Enumerate(int low, int high) ;
+protected:
+ /* A sentinel is used for root and for nil. These sentinels are */
+ /* created when RedBlackTreeCreate is caled. root->left should always */
+ /* point to the node which is the root of the tree. nil points to a */
+ /* node which should always be black but has aribtrary children and */
+ /* parent and no key or info. The point of using these sentinels is so */
+ /* that the root and nil nodes do not require special cases in the code */
+ RedBlackTreeNode *root;
+ RedBlackTreeNode *nil;
+ void LeftRotate(RedBlackTreeNode *);
+ void RightRotate(RedBlackTreeNode *);
+ void TreeInsertHelp(RedBlackTreeNode *);
+ void TreePrintHelper(RedBlackTreeNode *) const;
+ void FixUpMaxHigh(RedBlackTreeNode *);
+ void DeleteFixUp(RedBlackTreeNode *);
+ size_t numElements;
+};
diff --git a/Src/nu/RingBuffer.cpp b/Src/nu/RingBuffer.cpp
new file mode 100644
index 00000000..d59acb5e
--- /dev/null
+++ b/Src/nu/RingBuffer.cpp
@@ -0,0 +1,461 @@
+/*
+ * RingBuffer.cpp
+ * simple_mp3_playback
+ *
+ * Created by Ben Allison on 11/10/07.
+ * Copyright 2007 Nullsoft, Inc. All rights reserved.
+ *
+ */
+
+#include "RingBuffer.h"
+#include "../replicant/foundation/error.h"
+
+#include "bfc/platform/types.h"
+#include "bfc/platform/minmax.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <algorithm>
+
+#ifdef MIN
+#undef MIN
+#endif // MIN
+
+#define MIN(a,b) ((a<b)?(a):(b))
+
+
+RingBuffer::~RingBuffer()
+{
+ if ( ringBuffer )
+ free( ringBuffer );
+
+ ringBuffer = 0;
+}
+
+void RingBuffer::Reset()
+{
+ if ( ringBuffer )
+ free( ringBuffer );
+
+ ringBuffer = 0;
+}
+
+bool RingBuffer::reserve( size_t bytes )
+{
+ Reset();
+
+ ringBufferSize = bytes;
+
+ ringBuffer = (char *)calloc( ringBufferSize, sizeof( char ) );
+ if ( !ringBuffer )
+ return false;
+
+ clear();
+
+ return true;
+}
+
+int RingBuffer::expand( size_t bytes )
+{
+ if ( bytes > ringBufferSize )
+ {
+ char *new_buffer = (char *)realloc( ringBuffer, bytes );
+ if ( !new_buffer )
+ return NErr_OutOfMemory;
+
+ size_t write_offset = ringReadPosition - ringBuffer;
+ size_t read_offset = ringWritePosition - ringBuffer;
+
+ /* update write pointer for the new buffer */
+ ringWritePosition = new_buffer + write_offset;
+
+ if ( write_offset > read_offset || !ringBufferUsed ) /* ringBufferUsed will resolve the ambiguity when ringWritePosition == ringReadPosition */
+ {
+ /* the ring buffer looks like [ RXXXW ], so we don't need to move anything.
+ Just update the read pointer */
+
+ ringReadPosition = new_buffer + write_offset;
+ }
+ else
+ {
+ /* [XXW RXX] needs to become [XXW RXX] */
+ size_t end_bytes = ringBufferSize - read_offset; // number of bytes that we need to relocate (the RXX portion)
+ char *new_read_pointer = &new_buffer[ bytes - end_bytes ];
+
+ memmove( new_read_pointer, ringReadPosition, end_bytes );
+
+ ringReadPosition = new_read_pointer; /* update read pointer */
+ }
+
+ ringBufferSize = bytes;
+ ringBuffer = new_buffer;
+
+ return NErr_Success;
+ }
+ else
+ return NErr_NoAction;
+}
+
+bool RingBuffer::empty() const
+{
+ return ( ringBufferUsed == 0 );
+}
+
+size_t RingBuffer::read( void *dest, size_t len )
+{
+ int8_t *out = (int8_t *)dest; // lets us do pointer math easier
+ size_t toCopy = MIN( ringBufferUsed, len );
+ size_t copied = 0;
+
+ len -= toCopy;
+
+ // read to the end of the ring buffer
+ size_t end = ringBufferSize - ( ringReadPosition - ringBuffer );
+ size_t read1 = MIN( end, toCopy );
+
+ memcpy( out, ringReadPosition, read1 );
+
+ copied += read1;
+ ringReadPosition += read1;
+
+ if ( ringReadPosition == ringBuffer + ringBufferSize )
+ ringReadPosition = ringBuffer;
+
+ // update positions
+ ringBufferUsed -= read1;
+ toCopy -= read1;
+ out = (int8_t *)out + read1;
+
+ // see if we still have more to read after wrapping around
+ if ( toCopy )
+ {
+ memcpy( out, ringReadPosition, toCopy );
+
+ copied += toCopy;
+ ringReadPosition += toCopy;
+ ringBufferUsed -= toCopy;
+
+ if ( ringReadPosition == ringBuffer + ringBufferSize )
+ ringReadPosition = ringBuffer;
+ }
+
+ return copied;
+}
+
+size_t RingBuffer::at(size_t offset, void *dest, size_t len) const
+{
+ size_t toCopy = ringBufferUsed;
+
+ // make a local copy of this so we don't blow the original
+ char *ringReadPosition = this->ringReadPosition;
+
+ /* --- do a "dummy read" to deal with the offset request --- */
+ size_t dummy_end = ringBufferSize-(ringReadPosition-ringBuffer);
+
+ offset = MIN(toCopy, offset);
+ size_t read0 = MIN(dummy_end, offset);
+ ringReadPosition+=read0;
+
+ if (ringReadPosition == ringBuffer + ringBufferSize)
+ ringReadPosition = ringBuffer;
+
+ // update positions
+ toCopy -= read0;
+ offset -= read0;
+
+ // do second-half read (wraparound)
+ if ( offset )
+ {
+ ringReadPosition += offset;
+ toCopy -= offset;
+ }
+
+ // dummy read done
+
+ /* --- set up destination buffer and copy size --- */
+ int8_t *out = (int8_t *)dest; // lets us do pointer math easier
+
+ if ( toCopy > len )
+ toCopy = len;
+
+ size_t copied=0;
+
+ /* --- read to the end of the ring buffer --- */
+ size_t end = ringBufferSize - ( ringReadPosition - ringBuffer );
+ size_t read1 = MIN( end, toCopy );
+
+ memcpy( out, ringReadPosition, read1 );
+
+ copied += read1;
+ ringReadPosition += read1;
+
+ if (ringReadPosition == ringBuffer + ringBufferSize)
+ ringReadPosition = ringBuffer;
+
+ // update positions
+ toCopy -= read1;
+ out = (int8_t *)out + read1;
+
+ /* --- see if we still have more to read after wrapping around --- */
+ if (toCopy)
+ {
+ memcpy(out, ringReadPosition, toCopy);
+
+ copied += toCopy;
+ ringReadPosition += toCopy;
+ }
+
+ return copied;
+}
+
+size_t RingBuffer::peek( void *dest, size_t len ) const
+{
+ int8_t *out = (int8_t *)dest; // lets us do pointer math easier
+
+ size_t toCopy = MIN( ringBufferUsed, len );
+ size_t copied = 0;
+
+ // make a local copy of this so we don't blow the original
+ char *ringReadPosition = this->ringReadPosition;
+
+ // read to the end of the ring buffer
+ size_t end = ringBufferSize - ( ringReadPosition - ringBuffer );
+ size_t read1 = MIN( end, toCopy );
+
+ memcpy( out, ringReadPosition, read1 );
+
+ copied += read1;
+ ringReadPosition += read1;
+
+ if ( ringReadPosition == ringBuffer + ringBufferSize )
+ ringReadPosition = ringBuffer;
+
+ // update positions
+ toCopy -= read1;
+ out = (int8_t *)out + read1;
+
+ // see if we still have more to read after wrapping around
+ if ( toCopy )
+ {
+ memcpy( out, ringReadPosition, toCopy );
+
+ copied += toCopy;
+ ringReadPosition += toCopy;
+ }
+
+ return copied;
+}
+
+size_t RingBuffer::advance( size_t len )
+{
+ size_t toCopy = MIN( ringBufferUsed, len );
+ size_t copied = 0;
+
+ len -= toCopy;
+
+ // read to the end of the ring buffer
+ size_t end = ringBufferSize - ( ringReadPosition - ringBuffer );
+ size_t read1 = MIN( end, toCopy );
+
+ copied += read1;
+ ringReadPosition += read1;
+
+ if ( ringReadPosition == ringBuffer + ringBufferSize )
+ ringReadPosition = ringBuffer;
+
+ // update positions
+ toCopy -= read1;
+ ringBufferUsed -= read1;
+
+ // see if we still have more to read after wrapping around
+ if ( toCopy )
+ {
+ copied += toCopy;
+ ringReadPosition += toCopy;
+ ringBufferUsed -= toCopy;
+
+ if ( ringReadPosition == ringBuffer + ringBufferSize )
+ ringReadPosition = ringBuffer;
+ }
+
+ return copied;
+}
+
+size_t RingBuffer::avail() const
+{
+ return ringBufferSize - ringBufferUsed;
+}
+
+size_t RingBuffer::write( const void *buffer, size_t bytes )
+{
+ size_t used = ringBufferUsed;
+ size_t avail = ringBufferSize - used;
+
+ bytes = MIN( avail, bytes );
+
+ // write to the end of the ring buffer
+ size_t end = ringBufferSize - ( ringWritePosition - ringBuffer );
+ size_t copied = 0;
+ size_t write1 = MIN( end, bytes );
+
+ memcpy( ringWritePosition, buffer, write1 );
+
+ copied += write1;
+ ringWritePosition += write1;
+
+ if ( ringWritePosition == ringBuffer + ringBufferSize )
+ ringWritePosition = ringBuffer;
+
+ // update positions
+ ringBufferUsed += write1;
+ bytes -= write1;
+ buffer = (const int8_t *)buffer + write1;
+
+ // see if we still have more to write after wrapping around
+ if ( bytes )
+ {
+ memcpy( ringWritePosition, buffer, bytes );
+
+ copied += bytes;
+ ringWritePosition += bytes;
+ ringBufferUsed += bytes;
+
+ if ( ringWritePosition == ringBuffer + ringBufferSize )
+ ringWritePosition = ringBuffer;
+ }
+
+ return copied;
+}
+
+size_t RingBuffer::drain( Drainer *drainer, size_t max_bytes )
+{
+ // read to the end of the ring buffer
+ size_t used = ringBufferUsed;
+ size_t bytes = used;
+
+ bytes = MIN(bytes, max_bytes);
+
+ size_t copied = 0;
+ size_t end = ringBufferSize-(ringReadPosition-ringBuffer);
+ size_t drain1 = MIN(end, bytes);
+
+ if (!drain1)
+ return 0;
+
+ size_t read1 = drainer->Write(ringReadPosition, drain1);
+ if (read1 == 0)
+ return 0;
+
+ copied+=read1;
+ ringReadPosition+=read1;
+ if (ringReadPosition == ringBuffer + ringBufferSize)
+ ringReadPosition=ringBuffer;
+
+ // update positions
+ ringBufferUsed -= read1;
+ bytes-=read1;
+
+ // see if we still have more to read after wrapping around
+ if (drain1 == read1 && bytes)
+ {
+ size_t read2 = drainer->Write(ringReadPosition, bytes);
+
+ copied += read2;
+ ringReadPosition += read2;
+ ringBufferUsed -= read2;
+
+ if (ringReadPosition == ringBuffer + ringBufferSize)
+ ringReadPosition=ringBuffer;
+ }
+
+ return copied;
+}
+
+size_t RingBuffer::fill(Filler *filler, size_t max_bytes)
+{
+ // write to the end of the ring buffer
+ size_t used = ringBufferUsed;
+ size_t bytes = ringBufferSize - used;
+
+ bytes = MIN(bytes, max_bytes);
+
+ size_t copied = 0;
+ size_t end = ringBufferSize-(ringWritePosition-ringBuffer);
+ size_t fill1 = MIN(end, bytes);
+
+ if (!fill1)
+ return 0;
+
+ size_t write1 = filler->Read(ringWritePosition, fill1);
+ if (write1 == 0)
+ return 0;
+
+ copied+=write1;
+ ringWritePosition+=write1;
+
+ if (ringWritePosition == ringBuffer + ringBufferSize)
+ ringWritePosition=ringBuffer;
+
+ // update positions
+ ringBufferUsed += write1;
+ bytes-=write1;
+
+ // see if we still have more to write after wrapping around
+ if (fill1 == write1 && bytes)
+ {
+ size_t write2 = filler->Read(ringWritePosition, bytes);
+
+ copied += write2;
+ ringWritePosition += write2;
+ ringBufferUsed += write2;
+
+ if (ringWritePosition == ringBuffer + ringBufferSize)
+ ringWritePosition=ringBuffer;
+ }
+
+ return copied;
+}
+
+size_t RingBuffer::size() const
+{
+ return ringBufferUsed;
+}
+
+void RingBuffer::clear()
+{
+ ringBufferUsed = 0;
+ ringWritePosition = ringBuffer;
+ ringReadPosition = ringBuffer;
+}
+
+void *RingBuffer::LockBuffer()
+{
+ return ringBuffer;
+}
+
+void RingBuffer::UnlockBuffer( size_t written )
+{
+ ringWritePosition = ringBuffer+written;
+ ringBufferUsed = written;
+}
+
+size_t RingBuffer::write_position() const
+{
+ return (size_t)ringWritePosition;
+}
+
+size_t RingBuffer::read_position() const
+{
+ return (size_t)ringReadPosition;
+}
+
+void RingBuffer::get_read_buffer(size_t bytes, const void **buffer, size_t *bytes_available) const
+{
+ size_t toCopy = MIN( ringBufferUsed, bytes );
+
+ // read to the end of the ring buffer
+ size_t end = ringBufferSize-(ringReadPosition-ringBuffer);
+
+ *bytes_available = MIN(end, toCopy);
+ *buffer = ringReadPosition;
+}
diff --git a/Src/nu/RingBuffer.h b/Src/nu/RingBuffer.h
new file mode 100644
index 00000000..2c84c463
--- /dev/null
+++ b/Src/nu/RingBuffer.h
@@ -0,0 +1,77 @@
+/*
+ * RingBuffer.h
+ * simple_mp3_playback
+ *
+ * Created by Ben Allison on 11/10/07.
+ * Copyright 2007 Nullsoft, Inc. All rights reserved.
+ *
+ * Ring Buffer class
+ * Thread safety:
+ * This class can be used from exactly two simultaneous threads without locking
+ * as long as one thread only writes and the other thread only reads
+ * the writer thread may call empty(), avail(), size(), write(), fill(
+ * the reader thread my call empty(), avail(), size(), read(), peek(), advance()
+ *
+ * two (or more) readers or two (or more) writers requires external locking
+ *
+ * Reset(), reserve(), clear(), LockBuffer(), UnlockBuffer() are not thread-safe
+ */
+
+#pragma once
+#include <stddef.h>
+
+class Filler
+{
+public:
+ virtual size_t Read(void *dest, size_t len)=0;
+};
+
+class Drainer
+{
+public:
+ virtual size_t Write(const void *dest, size_t len)=0;
+};
+
+class RingBuffer
+{
+public:
+ RingBuffer() {}
+ ~RingBuffer();
+
+ void Reset();
+ bool reserve( size_t bytes ); // destructive.
+ int expand( size_t bytes ); // like reserve, but only expands upward. non-destructive. returns an NError
+ bool empty() const;
+ size_t avail() const; // how much available for writing
+ size_t size() const; // how much available for reading
+ void clear();
+ size_t read( void *dest, size_t len ); // returns amount actually read
+ size_t advance( size_t len ); // same as read() but doesn't write the data any where.
+ size_t peek( void *dest, size_t len ) const; // same as read() but doesn't advance the read pointer
+ size_t write( const void *src, size_t len );
+ size_t fill( Filler *filler, size_t max_bytes );
+ size_t drain( Drainer *drainer, size_t max_bytes );
+ size_t at( size_t offset, void *dest, size_t len ) const; // peeks() from offset. returns bytes read
+
+ size_t write_position() const; // returns an integer representing a write position
+ size_t read_position() const; // returns an integer representing the read position
+
+ void get_read_buffer( size_t bytes, const void **buffer, size_t *bytes_available ) const; /* returns a pointer that you can read data from, call advance() when you are done */
+ /* DO NOT USING THIS UNLESS YOU KNOW WHAT YOU'RE DOING
+ you should only use it when the ring buffer is empty
+ 1) call clear() beforehand - very important!
+ 2) call LockBuffer(), it'll give you a buffer
+ 3) call UnlockBufer() with how much you've written
+ 4) you catch the man
+ */
+ void *LockBuffer();
+ void UnlockBuffer( size_t written );
+
+private:
+ volatile size_t ringBufferUsed = 0;
+
+ size_t ringBufferSize = 0;
+ char *ringBuffer = 0;
+ char *ringWritePosition = 0;
+ char *ringReadPosition = 0;
+};
diff --git a/Src/nu/SampleQueue.h b/Src/nu/SampleQueue.h
new file mode 100644
index 00000000..802d4d9d
--- /dev/null
+++ b/Src/nu/SampleQueue.h
@@ -0,0 +1,81 @@
+#pragma once
+#include <bfc/platform/types.h>
+#include <deque>
+#include "../nu/AutoLock.h"
+
+template <class SampleData>
+class SampleQueue
+{
+public:
+ void PushFree(SampleData *new_sample)
+ {
+ queue_guard.Lock();
+ free_queue.push_front(new_sample);
+ queue_guard.Unlock();
+ }
+
+ void PushProcessed(SampleData *new_sample)
+ {
+ queue_guard.Lock();
+ processed_queue.push_front(new_sample);
+ queue_guard.Unlock();
+ }
+
+ // will return 0 if none ready
+ SampleData *PopProcessed()
+ {
+ SampleData *sample=0;
+ queue_guard.Lock();
+ if (!processed_queue.empty())
+ {
+ sample = processed_queue.back();
+ processed_queue.pop_back();
+ }
+ queue_guard.Unlock();
+ return sample;
+ }
+
+ SampleData *PopFree()
+ {
+ SampleData *sample=0;
+ queue_guard.Lock();
+ if (!free_queue.empty())
+ {
+ sample = free_queue.back();
+ free_queue.pop_back();
+ }
+ queue_guard.Unlock();
+ if (!sample)
+ sample = new SampleData;
+ return sample;
+ }
+
+ void Trim()
+ {
+ queue_guard.Lock();
+ //free_queue.deleteAll();
+ auto it_f = free_queue.begin();
+ while (it_f != free_queue.end())
+ {
+ SampleData* p = *it_f;
+ delete p;
+ it_f = free_queue.erase(it_f);
+ }
+
+ //processed_queue.deleteAll();
+ auto it_p = processed_queue.begin();
+ while (it_p != processed_queue.end())
+ {
+ SampleData* p = *it_p;
+ delete p;
+ it_p = processed_queue.erase(it_p);
+ }
+ queue_guard.Unlock();
+ }
+
+private:
+ std::deque<SampleData*> free_queue;
+ std::deque<SampleData*> processed_queue;
+
+ Nullsoft::Utility::LockGuard queue_guard;
+};
diff --git a/Src/nu/SendTo.h b/Src/nu/SendTo.h
new file mode 100644
index 00000000..597b1b6c
--- /dev/null
+++ b/Src/nu/SendTo.h
@@ -0,0 +1,106 @@
+#ifndef NULLSOFT_SEND_TO_HELPER_CLASS_H
+#define NULLSOFT_SEND_TO_HELPER_CLASS_H
+
+#include "../gen_ml/ml_ipc.h"
+#include "../gen_ml/ml.h"
+#include "../winamp/wa_ipc.h"
+
+class SendToMenu
+{
+public:
+ SendToMenu(winampMediaLibraryPlugin &_plugin) : IPC_LIBRARY_SENDTOMENU(0), plugin(&_plugin)
+ {
+ memset(&sendTo, 0, sizeof(sendTo));
+ }
+ void AddHere(HWND hwnd, HMENU hMenu, int type)
+ {
+ sendTo.mode = 0;
+ sendTo.hwnd = 0;
+ sendTo.build_hMenu = 0;
+
+ IPC_LIBRARY_SENDTOMENU = SendMessage(plugin->hwndWinampParent, WM_WA_IPC, (WPARAM)&"LibrarySendToMenu", IPC_REGISTER_WINAMP_IPCMESSAGE);
+ if (IPC_LIBRARY_SENDTOMENU > 65536 && SendMessage(plugin->hwndWinampParent, WM_WA_IPC, (WPARAM)0, IPC_LIBRARY_SENDTOMENU) == (LRESULT)-1)
+ {
+ sendTo.mode = 1;
+ sendTo.hwnd = hwnd;
+ sendTo.data_type = type; //ML_TYPE_ITEMRECORDLIST;
+ sendTo.build_hMenu = hMenu;
+ }
+ }
+ bool WasClicked(int popUpReturnVal)
+ {
+ if (sendTo.mode == 2)
+ {
+ sendTo.menu_id = popUpReturnVal;
+ if (SendMessage(plugin->hwndWinampParent, WM_WA_IPC, (WPARAM)&sendTo, IPC_LIBRARY_SENDTOMENU) == (LRESULT)-1)
+ return true;
+ }
+ return false;
+ }
+ void Cleanup()
+ {
+ if (sendTo.mode)
+ {
+ sendTo.mode = 4;
+ SendMessage(plugin->hwndWinampParent, WM_WA_IPC, (WPARAM)&sendTo, IPC_LIBRARY_SENDTOMENU); // cleanup
+ }
+ sendTo.build_hMenu = 0;
+ }
+
+ bool InitPopupMenu(WPARAM wParam)
+ {
+ if (wParam && (HMENU)wParam == sendTo.build_hMenu && sendTo.mode == 1)
+ {
+ if (SendMessage(plugin->hwndWinampParent, WM_WA_IPC, (WPARAM)&sendTo, IPC_LIBRARY_SENDTOMENU) == (LRESULT)-1)
+ sendTo.mode = 2;
+
+ return true;
+ }
+ return false;
+ }
+
+ // still need to free it on your own
+ void SendItemRecordList(itemRecordList *obj)
+ {
+ sendTo.data_type = ML_TYPE_ITEMRECORDLIST;
+ sendTo.mode = 3;
+ sendTo.data = (void*) & obj;
+
+ SendMessage(plugin->hwndWinampParent, WM_WA_IPC, (WPARAM)&sendTo, IPC_LIBRARY_SENDTOMENU);
+ }
+
+ void SendFilenames(const char *filenames)
+ {
+ sendTo.data_type = ML_TYPE_FILENAMES;
+ sendTo.mode = 3;
+ sendTo.data = (void*)filenames;
+
+ SendMessage(plugin->hwndWinampParent, WM_WA_IPC, (WPARAM)&sendTo, IPC_LIBRARY_SENDTOMENU);
+ }
+
+ void SendFilenames(const wchar_t *filenames)
+ {
+ sendTo.data_type = ML_TYPE_FILENAMESW;
+ sendTo.mode = 3;
+ sendTo.data = (void*)filenames;
+
+ SendMessage(plugin->hwndWinampParent, WM_WA_IPC, (WPARAM)&sendTo, IPC_LIBRARY_SENDTOMENU);
+ }
+
+
+ LRESULT SendPlaylist(mlPlaylist *playlist)
+ {
+ sendTo.data_type = ML_TYPE_PLAYLIST;
+ sendTo.mode = 3;
+ sendTo.data = (void*)playlist;
+
+ return SendMessage(plugin->hwndWinampParent, WM_WA_IPC, (WPARAM)&sendTo, IPC_LIBRARY_SENDTOMENU);
+ }
+
+private:
+ LRESULT IPC_LIBRARY_SENDTOMENU;
+ librarySendToMenuStruct sendTo;
+ winampMediaLibraryPlugin *plugin;
+};
+
+#endif
diff --git a/Src/nu/ServiceBuilder.h b/Src/nu/ServiceBuilder.h
new file mode 100644
index 00000000..709eb4d0
--- /dev/null
+++ b/Src/nu/ServiceBuilder.h
@@ -0,0 +1,40 @@
+
+#pragma once
+#ifndef NULLSOFT_UTILITY_SERVICE_BUILDER_H
+#define NULLSOFT_UTILITY_SERVICE_BUILDER_H
+
+#include <api/service/waservicefactoryi.h>
+#include <api/service/services.h>
+
+#ifndef WASABI_API_SVC
+#define WASABI_API_SVC serviceManager
+#endif
+
+template <class api_T>
+static void ServiceBuild(api_service *service, api_T *&api_t, GUID factoryGUID_t)
+{
+ if (service)
+ {
+ waServiceFactory *factory = service->service_getServiceByGuid(factoryGUID_t);
+ if (factory)
+ {
+ api_t = reinterpret_cast<api_T *>( factory->getInterface() );
+ }
+ }
+}
+
+template <class api_T>
+static void ServiceRelease(api_service *service, api_T *&api_t, GUID factoryGUID_t)
+{
+ if (service && api_t)
+ {
+ waServiceFactory *factory = service->service_getServiceByGuid(factoryGUID_t);
+ if (factory)
+ {
+ factory->releaseInterface(api_t);
+ }
+ }
+ api_t = NULL;
+}
+
+#endif
diff --git a/Src/nu/ServiceWatcher.cpp b/Src/nu/ServiceWatcher.cpp
new file mode 100644
index 00000000..dcf8e096
--- /dev/null
+++ b/Src/nu/ServiceWatcher.cpp
@@ -0,0 +1,183 @@
+#include "ServiceWatcher.h"
+#include <api/service/waservicefactory.h>
+
+
+static void *GetService(api_service *p_serviceManager, GUID p_serviceGUID)
+{
+ waServiceFactory *sf = p_serviceManager->service_getServiceByGuid( p_serviceGUID);
+ if (sf)
+ return sf->getInterface();
+ else
+ return 0;
+}
+
+static void ReleaseService(api_service *p_serviceManager, GUID p_serviceGUID, void *p_service)
+{
+ waServiceFactory *sf = p_serviceManager->service_getServiceByGuid( p_serviceGUID);
+ if (sf)
+ sf->releaseInterface( p_service);
+}
+
+void ServiceWatcher::WatchWith(api_service *_serviceApi)
+{
+ serviceManager =_serviceApi;
+ systemCallbacks=(api_syscb*)GetService( serviceManager, syscbApiServiceGuid);
+}
+
+void ServiceWatcher::StopWatching()
+{
+ if (systemCallbacks)
+ {
+ systemCallbacks->syscb_deregisterCallback(this);
+ ReleaseService( serviceManager, syscbApiServiceGuid, systemCallbacks);
+ }
+ systemCallbacks=0;
+}
+void ServiceWatcher::Clear()
+{
+ //watchList.Reset();
+ watchList.clear();
+}
+
+ServiceWatcher::~ServiceWatcher()
+{
+ //StopWatching();
+}
+
+void ServiceWatcher::WatchForT(void **ptr, GUID watchGUID)
+{
+ watchList[watchGUID]=ptr;
+ if (!*ptr) // try to get it if we need it
+ {
+ *ptr = GetService( serviceManager, watchGUID);
+ }
+}
+
+int ServiceWatcher::Notify(int msg, intptr_t param1, intptr_t param2)
+{
+ switch (msg)
+ {
+ case SvcCallback::ONREGISTER:
+ {
+ waServiceFactory *sf = reinterpret_cast<waServiceFactory*>(param2);
+ GUID serviceGUID = sf->getGuid();
+ if (serviceGUID != INVALID_GUID)
+ {
+ WatchList::iterator itr = watchList.find(serviceGUID);
+ if (itr!=watchList.end())
+ {
+ void **ptr = itr->second;
+ if (ptr && !*ptr) // don't re-retrieve service if we already have it
+ {
+ *ptr = sf->getInterface();
+ }
+ }
+ }
+ }
+ break;
+ case SvcCallback::ONDEREGISTER:
+ {
+ waServiceFactory *sf = reinterpret_cast<waServiceFactory*>(param2);
+ GUID serviceGUID = sf->getGuid();
+ if (serviceGUID != INVALID_GUID)
+ {
+ WatchList::iterator itr = watchList.find(serviceGUID);
+ if (itr!=watchList.end())
+ {
+ void **ptr = itr->second;
+ if (ptr && *ptr)
+ {
+ // benski> probably not safe to do, so i'll leave it commented out: sf->releaseInterface(*ptr);
+ *ptr = 0;
+ }
+ }
+ }
+ }
+ break;
+ default: return 0;
+ }
+ return 1;
+}
+
+#define CBCLASS ServiceWatcher
+START_DISPATCH;
+CB(SYSCALLBACK_GETEVENTTYPE, GetEventType);
+CB(SYSCALLBACK_NOTIFY, Notify);
+END_DISPATCH;
+#undef CBCLASS
+
+ServiceWatcherSingle::~ServiceWatcherSingle()
+{
+ //StopWatching();
+}
+
+void ServiceWatcherSingle::StopWatching()
+{
+ if (systemCallbacks)
+ {
+ systemCallbacks->syscb_deregisterCallback(this);
+ ReleaseService( serviceManager, syscbApiServiceGuid, systemCallbacks);
+ }
+ systemCallbacks=0;
+}
+
+void ServiceWatcherSingle::WatchWith(api_service *_serviceApi)
+{
+ serviceManager =_serviceApi;
+ systemCallbacks=(api_syscb*)GetService( serviceManager, syscbApiServiceGuid);
+}
+
+void ServiceWatcherSingle::WatchForT(void **ptr, GUID watchGUID)
+{
+ service=ptr;
+ serviceGUID=watchGUID;
+ if (ptr && !*ptr) // try to get it if we need it
+ {
+ *ptr = GetService( serviceManager, watchGUID);
+ if (*ptr)
+ OnRegister();
+ }
+}
+
+int ServiceWatcherSingle::Notify(int msg, intptr_t param1, intptr_t param2)
+{
+ switch (msg)
+ {
+ case SvcCallback::ONREGISTER:
+ {
+ if (service && !*service) // don't re-retrieve service if we already have it
+ {
+ waServiceFactory *sf = reinterpret_cast<waServiceFactory*>(param2);
+ if (sf && sf->getGuid() == serviceGUID)
+ {
+ *service = sf->getInterface();
+ if (*service)
+ OnRegister();
+ }
+ }
+ }
+ break;
+ case SvcCallback::ONDEREGISTER:
+ {
+ if (service && *service)
+ {
+ waServiceFactory *sf = reinterpret_cast<waServiceFactory*>(param2);
+ if (serviceGUID == sf->getGuid())
+ {
+ OnDeregister();
+ *service=0;
+ }
+ }
+ }
+ break;
+ default: return 0;
+ }
+ return 1;
+}
+
+#define CBCLASS ServiceWatcherSingle
+START_DISPATCH;
+CB(SYSCALLBACK_GETEVENTTYPE, GetEventType);
+CB(SYSCALLBACK_NOTIFY, Notify);
+END_DISPATCH;
+#undef CBCLASS \ No newline at end of file
diff --git a/Src/nu/ServiceWatcher.h b/Src/nu/ServiceWatcher.h
new file mode 100644
index 00000000..7eb4d3da
--- /dev/null
+++ b/Src/nu/ServiceWatcher.h
@@ -0,0 +1,60 @@
+#pragma once
+#include <api/service/api_service.h>
+#include <api/syscb/callbacks/svccb.h>
+#include <map>
+#include <api/syscb/api_syscb.h>
+
+class ServiceWatcher : public SysCallback
+{
+public:
+ ServiceWatcher() : serviceManager(0),systemCallbacks(0) {}
+ ~ServiceWatcher();
+
+ void WatchWith(api_service *_serviceApi);
+
+ template <class T>
+ void WatchFor(T **ptr, GUID watchGUID)
+ {
+ WatchForT((void **)ptr, watchGUID);
+ }
+ void StopWatching();
+ void Clear();
+private:
+ void WatchForT(void **ptr, GUID watchGUID);
+ typedef std::map<GUID, void **> WatchList;
+ WatchList watchList;
+ FOURCC GetEventType() { return SysCallback::SERVICE; }
+ int Notify(int msg, intptr_t param1, intptr_t param2);
+ api_service *serviceManager;
+ api_syscb *systemCallbacks;
+protected:
+ RECVS_DISPATCH;
+};
+
+class ServiceWatcherSingle : public SysCallback
+{
+public:
+ ServiceWatcherSingle() : serviceManager(0),systemCallbacks(0),service(0) {}
+ virtual ~ServiceWatcherSingle();
+ void WatchWith(api_service *_serviceApi);
+
+ template <class T>
+ void WatchFor(T **ptr, GUID watchGUID)
+ {
+ WatchForT((void **)ptr, watchGUID);
+ }
+
+ virtual void OnRegister() {}
+ virtual void OnDeregister()=0;
+ void StopWatching();
+private:
+ void WatchForT(void **ptr, GUID watchGUID);
+ FOURCC GetEventType() { return SysCallback::SERVICE; }
+ int Notify(int msg, intptr_t param1, intptr_t param2);
+ api_service *serviceManager;
+ api_syscb *systemCallbacks;
+ void **service;
+ GUID serviceGUID;
+protected:
+ RECVS_DISPATCH;
+}; \ No newline at end of file
diff --git a/Src/nu/Singleton.h b/Src/nu/Singleton.h
new file mode 100644
index 00000000..7ee808ce
--- /dev/null
+++ b/Src/nu/Singleton.h
@@ -0,0 +1,86 @@
+#pragma once
+#ifndef NULLSOFT_UTILITY_SINGLETON_H
+#define NULLSOFT_UTILITY_SINGLETON_H
+
+#include <api/service/waservicefactoryi.h>
+#include <api/service/services.h>
+
+/*
+====== Usage ======
+disp_t: your Dispatchable base class
+implt_t: your implementation class
+
+SingletonServiceFactory<disp_t, impl_t> myFactory;
+impl_t myImplementation;
+
+//....
+
+//during service registration
+myFactory.Register(WASABI_API_SVC, &myImplementation);
+
+//during service deregistration
+myFactory.Deregister(WASABI_API_SVC, &myImplementation);
+
+==== Class requirements ====
+your base or implementation class requires the following three static methods
+static FOURCC getServiceType(); // return your type (e.g. WaSvc::UNIQUE)... might already be defined in the dispatchable base class
+static const char *getServiceName(); // return your service name
+static GUID getServiceGuid(); // return your service GUID
+*/
+
+template <class disp_t, class impl_t>
+class SingletonServiceFactory : public waServiceFactory
+{
+public:
+ SingletonServiceFactory() : impl(0)
+ {
+ }
+
+ ~SingletonServiceFactory()
+ {
+ }
+
+ void Register(api_service *serviceManager, impl_t *new_impl)
+ {
+ impl=new_impl;
+ serviceManager->service_register(this);
+ }
+
+ void Deregister(api_service *serviceManager)
+ {
+ if (impl)
+ {
+ serviceManager->service_deregister(this);
+ impl=0;
+ }
+ }
+
+private:
+ FOURCC svc_serviceType() { return impl_t::getServiceType(); }
+ const char *svc_getServiceName() { return impl_t::getServiceName(); }
+ GUID svc_getGuid() { return impl_t::getServiceGuid(); } // GUID per service factory, can be INVALID_GUID
+ void *svc_getInterface(int global_lock = TRUE) { return static_cast<disp_t *>(impl); }
+ int svc_supportNonLockingGetInterface() { return 1; }
+ int svc_releaseInterface(void *ifc) { return 0; }
+ const wchar_t *svc_getTestString() { return 0; }
+ int svc_notify(int msg, int param1 = 0, int param2 = 0) { return 0; }
+
+private:
+ impl_t *impl;
+
+protected:
+#define CBCLASS SingletonServiceFactory<disp_t, impl_t>
+ START_DISPATCH_INLINE;
+ CB(WASERVICEFACTORY_GETSERVICETYPE, svc_serviceType);
+ CB(WASERVICEFACTORY_GETSERVICENAME, svc_getServiceName);
+ CB(WASERVICEFACTORY_GETGUID, svc_getGuid);
+ CB(WASERVICEFACTORY_GETINTERFACE, svc_getInterface);
+ CB(WASERVICEFACTORY_SUPPORTNONLOCKINGGETINTERFACE, svc_supportNonLockingGetInterface);
+ CB(WASERVICEFACTORY_RELEASEINTERFACE, svc_releaseInterface);
+ CB(WASERVICEFACTORY_GETTESTSTRING, svc_getTestString);
+ CB(WASERVICEFACTORY_SERVICENOTIFY, svc_notify);
+ END_DISPATCH;
+#undef CBCLASS
+};
+
+#endif \ No newline at end of file
diff --git a/Src/nu/Slider.h b/Src/nu/Slider.h
new file mode 100644
index 00000000..8db42a6e
--- /dev/null
+++ b/Src/nu/Slider.h
@@ -0,0 +1,35 @@
+#pragma once
+#include <windows.h>
+#include <commctrl.h>
+class Slider
+{
+public:
+
+ Slider(HWND hwndDlg, int id)
+ {
+ slider_hwnd = GetDlgItem(hwndDlg, id);
+ }
+
+ void SetRange(WORD low, WORD high, BOOL redraw = TRUE)
+ {
+ SendMessage(slider_hwnd, TBM_SETRANGE, redraw, MAKELONG(low,high));
+ }
+
+ void SetPosition(LPARAM position, BOOL redraw = TRUE)
+ {
+ SendMessage(slider_hwnd, TBM_SETPOS, redraw, position);
+ }
+
+ void SetTickFrequency(LPARAM frequency)
+ {
+ SendMessage(slider_hwnd, TBM_SETTICFREQ, frequency, 0);
+ }
+
+ enum
+ {
+ NO_REDRAW = FALSE,
+ REDRAW = TRUE,
+ };
+private:
+ HWND slider_hwnd;
+}; \ No newline at end of file
diff --git a/Src/nu/SpillBuffer.cpp b/Src/nu/SpillBuffer.cpp
new file mode 100644
index 00000000..1bd420a1
--- /dev/null
+++ b/Src/nu/SpillBuffer.cpp
@@ -0,0 +1,105 @@
+#include "SpillBuffer.h"
+#include <stdlib.h>
+#include <string.h>
+#ifndef min
+#define min(a,b) ((a<b)?(a):(b))
+#endif
+
+SpillBuffer::SpillBuffer()
+{
+ spillBufferUsed=0;
+ spillBufferSize=0;
+ spillBuffer=0;
+}
+
+SpillBuffer::~SpillBuffer()
+{
+ free(spillBuffer);
+}
+
+void SpillBuffer::reset()
+{
+ free(spillBuffer);
+ spillBuffer=0;
+ spillBufferUsed=0;
+ spillBufferSize=0;
+}
+
+bool SpillBuffer::reserve(size_t bytes)
+{
+ size_t old_spillBufferSize = spillBufferSize;
+ spillBufferSize=bytes;
+ char *new_spillBuffer = (char *)realloc(spillBuffer, spillBufferSize*2);
+ if (new_spillBuffer) spillBuffer = new_spillBuffer;
+ else
+ {
+ new_spillBuffer = (char *)calloc(spillBufferSize*2, sizeof(char));
+ if (new_spillBuffer)
+ {
+ memcpy(new_spillBuffer, spillBuffer, old_spillBufferSize * 2);
+ free(spillBuffer);
+ spillBuffer = new_spillBuffer;
+ }
+ else
+ {
+ spillBufferSize = old_spillBufferSize;
+ return false;
+ }
+ }
+ if (!spillBuffer) return false;
+ clear();
+ return true;
+}
+
+void SpillBuffer::clear()
+{
+ spillBufferUsed=0;
+}
+
+size_t SpillBuffer::write(const void *buffer, size_t bytes)
+{
+ size_t avail = spillBufferSize - spillBufferUsed;
+ bytes = min(avail, bytes);
+ memcpy(spillBuffer + spillBufferUsed, buffer, bytes);
+ spillBufferUsed+=bytes;
+ return bytes;
+}
+
+bool SpillBuffer::get(void **buffer, size_t *len)
+{
+ *buffer = spillBuffer;
+ *len = spillBufferUsed;
+ spillBufferUsed = 0;
+ return true;
+}
+
+bool SpillBuffer::full() const
+{
+ return spillBufferUsed == spillBufferSize;
+}
+
+bool SpillBuffer::empty() const
+{
+ return spillBufferUsed == 0;
+}
+
+void SpillBuffer::remove(size_t len)
+{
+ if (len > spillBufferUsed)
+ len=spillBufferUsed;
+
+ if (len)
+ {
+ memmove(spillBuffer, spillBuffer + len, spillBufferUsed - len);
+ }
+}
+
+size_t SpillBuffer::remaining() const
+{
+ return spillBufferSize - spillBufferUsed;
+}
+
+size_t SpillBuffer::length() const
+{
+ return spillBufferSize;
+}
diff --git a/Src/nu/SpillBuffer.h b/Src/nu/SpillBuffer.h
new file mode 100644
index 00000000..8d5e33da
--- /dev/null
+++ b/Src/nu/SpillBuffer.h
@@ -0,0 +1,23 @@
+#pragma once
+
+class SpillBuffer
+{
+public:
+ SpillBuffer();
+ ~SpillBuffer();
+ bool reserve(size_t bytes);
+ void clear();
+ void reset();
+ size_t write(const void *src, size_t len);
+ bool get(void **buffer, size_t *len);
+ bool full() const;
+ bool empty() const;
+ void remove(size_t len); // removes len bytes from the start of the spill buffer
+ size_t remaining() const; // how many bytes to fill it up
+ size_t length() const; /* buffer length when full */
+
+private:
+ volatile size_t spillBufferUsed;
+ size_t spillBufferSize;
+ char *spillBuffer;
+}; \ No newline at end of file
diff --git a/Src/nu/ThreadQueue.cpp b/Src/nu/ThreadQueue.cpp
new file mode 100644
index 00000000..34def11f
--- /dev/null
+++ b/Src/nu/ThreadQueue.cpp
@@ -0,0 +1,66 @@
+ #include "ThreadQueue.h"
+#include <assert.h>
+#include <time.h>
+
+static inline __attribute__((always_inline))
+void get_exceed_time(struct timespec* ptime, long delay)
+{
+ clock_gettime(CLOCK_REALTIME, ptime);
+
+ ptime->tv_nsec += delay;
+ if (ptime->tv_nsec >= 1000000000L) // overflow
+ {
+ ptime->tv_nsec -= 1000000000L;
+ ++ptime->tv_sec;
+ }
+}
+
+ThreadQueue::ThreadQueue()
+{
+ buffer.reserve(256 * sizeof(void *));
+ sem_init(&event, 0, 0);
+}
+
+ThreadQueue::~ThreadQueue()
+{
+ sem_destroy(&event);
+}
+
+void ThreadQueue::Queue(const void *in)
+{
+ buffer.write(&in, sizeof(in));
+ sem_post(&event);
+}
+
+void *ThreadQueue::Get()
+{
+ sem_wait(&event);
+ void *out=0;
+ size_t read = buffer.read(&out, sizeof(out));
+ assert(read == sizeof(out));
+ return out;
+}
+
+int ThreadQueue::Wait(long delay, void **val)
+{
+ timespec t;
+ get_exceed_time(&t, delay);
+ int ret = sem_timedwait(&event, &t);
+ if (ret == 0)
+ {
+ size_t read = buffer.read(val, sizeof(*val));
+ assert(read == sizeof(*val));
+ }
+ return ret;
+}
+
+int ThreadQueue::Try(void **val)
+{
+ int ret = sem_trywait(&event);
+ if (ret == 0)
+ {
+ size_t read = buffer.read(val, sizeof(*val));
+ assert(read == sizeof(*val));
+ }
+ return ret;
+}
diff --git a/Src/nu/ThreadQueue.h b/Src/nu/ThreadQueue.h
new file mode 100644
index 00000000..0fc9fb23
--- /dev/null
+++ b/Src/nu/ThreadQueue.h
@@ -0,0 +1,23 @@
+#pragma once
+#include "RingBuffer.h"
+#include <semaphore.h>
+
+class ThreadQueue
+{
+public:
+ ThreadQueue();
+ ~ThreadQueue();
+ void Queue(const void *);
+ // Get() blocks until there's something in the queue
+ void *Get();
+ // return value is same as sem_wait
+ // delay is in nanoseconds
+ int Wait(long delay, void **val);
+ // kind of like sem_trywait
+ int Try(void **val);
+private:
+ // TODO: need to use something safer than RingBuffer, preferably a lock-free linked list so we can grow unlimited
+ RingBuffer buffer;
+ sem_t event;
+};
+
diff --git a/Src/nu/Vectors.h b/Src/nu/Vectors.h
new file mode 100644
index 00000000..16a19e0e
--- /dev/null
+++ b/Src/nu/Vectors.h
@@ -0,0 +1,221 @@
+#ifndef NULLSOFT_VECTOR_H
+#define NULLSOFT_VECTOR_H
+#include <assert.h>
+#include <bfc/platform/types.h>
+
+template <class Type, int INCREMENT = 32, int MULTIPLIER=1>
+class Vector
+{
+public:
+ typedef Type *iterator;
+ typedef const Type *const_iterator;
+public:
+ Vector() {}
+ virtual ~Vector()
+ {
+ delete [] values;
+ }
+
+ Vector(const Vector<Type, INCREMENT, MULTIPLIER> &copy)
+ {
+ if (copy.numPtrs)
+ {
+ values = new Type[copy.numPtrs];
+ allocSize = copy.numPtrs;
+ numPtrs = copy.numPtrs;
+ for (size_t i = 0;i != numPtrs;i++)
+ {
+ values[i] = copy.values[i];
+ }
+ }
+ }
+
+ void operator=(const Vector<Type, INCREMENT, MULTIPLIER> &copy)
+ {
+ Reset();
+ if (copy.numPtrs)
+ {
+ values = new Type[copy.numPtrs];
+ allocSize = copy.numPtrs;
+ numPtrs = copy.numPtrs;
+ for (size_t i = 0;i != numPtrs;i++)
+ {
+ values[i] = copy.values[i];
+ }
+ }
+ }
+
+ Type &operator[](size_t index)
+ {
+ return values[index];
+ }
+ Type *data()
+ {
+ return values;
+ }
+ Type *begin() const
+ {
+ return values;
+ }
+ Type *end() const
+ {
+ if (values) return values + numPtrs; else return 0;
+ }
+ void Reset()
+ {
+ delete [] values; values = 0; numPtrs = 0; allocSize=0;
+ }
+ void clear()
+ {
+ numPtrs = 0;
+ }
+ size_t size() const
+ {
+ return numPtrs;
+ }
+ size_t capacity()
+ {
+ return allocSize;
+ }
+ Type &back()
+ {
+ return values[numPtrs-1];
+ }
+
+ Type &at(size_t index) const
+ {
+ return values[index];
+ }
+
+ void reserve(size_t size)
+ {
+ if (size <= numPtrs)
+ return;
+
+ Type *newTable = new Type[size];
+ for (size_t i = 0;i != numPtrs;i++)
+ {
+ newTable[i] = values[i];
+ }
+ allocSize = size;
+ delete[] values;
+ values = newTable;
+ }
+
+ void push_back(Type t)
+ {
+ if (numPtrs == allocSize)
+ reserve(allocSize*MULTIPLIER + INCREMENT);
+ values[numPtrs++] = t;
+ }
+
+ void insert(size_t index, const Type &value)
+ {
+ if (numPtrs == allocSize)
+ reserve(allocSize*MULTIPLIER + INCREMENT);
+
+ for (size_t i = numPtrs;i != index;i--)
+ {
+ values[i] = values[i-1];
+ }
+ values[index] = value;
+ numPtrs++;
+ }
+
+ void append(size_t size, Type *t)
+ {
+ reserve(numPtrs + size + INCREMENT);
+ for (size_t i = 0;i != size;i++)
+ {
+ push_back(t[i]);
+ }
+ }
+
+ void pop_back()
+ {
+ if (numPtrs)
+ {
+ numPtrs--;
+ // next line removed to allow structs and classes
+ // values[numPtrs] = 0; // TODO: an inplace delete might be better?
+ }
+ }
+
+ void erase(iterator itr)
+ {
+ size_t index = itr - values;
+ eraseAt(index);
+ }
+
+ void eraseAt(size_t index)
+ {
+ if (numPtrs > index)
+ {
+ for (size_t k = index + 1; k < numPtrs; k++)
+ values[k-1] = values[k];
+
+ --numPtrs;
+ }
+ }
+
+ /* Removes an item by swapping it with the last item in the list. faster but can ruin order */
+ void eraseAtFast(size_t index)
+ {
+ if (index < numPtrs)
+ {
+ values[index] = values[--numPtrs];
+// if (numPtrs != index)
+// values[numPtrs]=0;
+ }
+ }
+
+ bool empty() const
+ {
+ return numPtrs == 0;
+ }
+
+ void resize(size_t newSize, Type val)
+ {
+ if (newSize < numPtrs)
+ {
+ numPtrs = newSize;
+ }
+ else if (newSize > numPtrs)
+ {
+ reserve(allocSize + (newSize - numPtrs) + INCREMENT);
+
+ while(numPtrs < newSize)
+ {
+ values[numPtrs] = val;
+ numPtrs++;
+ }
+ }
+ }
+
+ void resize(size_t newSize)
+ {
+ if (newSize < numPtrs)
+ {
+ numPtrs = newSize;
+ }
+ else if (newSize > numPtrs)
+ {
+ reserve(allocSize + (newSize - numPtrs) + INCREMENT);
+ numPtrs = newSize;
+ }
+ }
+
+ void set(Type *ptr, size_t num)
+ {
+ delete [] values;
+ values=ptr;
+ numPtrs=num;
+ }
+
+private:
+ size_t numPtrs = 0;
+ size_t allocSize = 0;
+ Type *values = 0;
+};
+
+#endif
diff --git a/Src/nu/VideoClock.h b/Src/nu/VideoClock.h
new file mode 100644
index 00000000..0ff82cf0
--- /dev/null
+++ b/Src/nu/VideoClock.h
@@ -0,0 +1,63 @@
+#pragma once
+#include <windows.h>
+
+namespace nu
+{
+class VideoClock
+{
+public:
+ VideoClock()
+ {
+ video_sync_start_time=0;
+ pause_start_time=0;
+ length_paused=0;
+ paused=0;
+ }
+
+ void Reset()
+ {
+ length_paused = 0;
+ paused=0;
+ }
+
+ void Pause()
+ {
+ paused=1;
+ pause_start_time = GetTickCount();
+ }
+
+ void Unpause()
+ {
+ paused=0;
+ length_paused += (GetTickCount() - pause_start_time);
+ }
+
+ int GetOutputTime()
+ {
+ if (paused)
+ {
+ return pause_start_time - video_sync_start_time - length_paused;
+ }
+ else
+ {
+ return GetTickCount() - video_sync_start_time - length_paused;
+ }
+ }
+
+ void Seek(int time_ms)
+ {
+ video_sync_start_time = GetTickCount() - time_ms;
+ length_paused = 0;
+ }
+
+ void Start()
+ {
+ video_sync_start_time = GetTickCount();
+ }
+private:
+ DWORD video_sync_start_time;
+ DWORD pause_start_time;
+ DWORD length_paused;
+ int paused;
+};
+} \ No newline at end of file
diff --git a/Src/nu/bitbuffer.cpp b/Src/nu/bitbuffer.cpp
new file mode 100644
index 00000000..0e3d61db
--- /dev/null
+++ b/Src/nu/bitbuffer.cpp
@@ -0,0 +1,71 @@
+#include "bitbuffer.h"
+#include <memory.h>
+#include <stdlib.h>
+
+BitBuffer::BitBuffer()
+{
+ buffer=0;
+ length=0;
+ bits=0;
+}
+
+void BitBuffer::WriteBit(char bit)
+{
+ if (bits == 0)
+ Resize(length+1);
+
+ bit = !!bit;
+ unsigned char mask = 1 << (7-bits);
+ buffer[length-1] &= ~mask;
+ buffer[length-1] |= (bit << (7-bits));
+ bits=(bits+1)%8;
+}
+
+void BitBuffer::Resize(size_t newlen)
+{
+ if (newlen > length)
+ {
+ unsigned char *new_buffer = (unsigned char *)realloc(buffer, newlen);
+ if (new_buffer)
+ {
+ buffer = new_buffer;
+ memset(buffer+length, 0, newlen-length); // zero out new data
+ length=newlen;
+ }
+ else
+ {
+ new_buffer = (unsigned char *)malloc(newlen);
+ if (new_buffer)
+ {
+ memcpy(new_buffer, buffer, length);
+ free(buffer);
+ buffer = new_buffer;
+ memset(buffer+length, 0, newlen-length); // zero out new data
+ length=newlen;
+ }
+ }
+ }
+}
+
+void BitBuffer::WriteBits(uintptr_t num, size_t bitlen)
+{
+ for (size_t i=0;i!=bitlen;i++)
+ {
+ WriteBit((num >> (bitlen-i-1))&1);
+ }
+}
+
+void BitBuffer::WriteBytes(void *buffer, size_t bytes)
+{
+ unsigned char *b = (unsigned char *)buffer;
+ for (size_t i=0;i!=bytes;i++)
+ WriteBits(b[i], 8);
+}
+
+void BitBuffer::WriteByte(unsigned char byte)
+{
+ for (size_t i=0;i!=8;i++)
+ {
+ WriteBit((byte >> (7-i))&1);
+ }
+} \ No newline at end of file
diff --git a/Src/nu/bitbuffer.h b/Src/nu/bitbuffer.h
new file mode 100644
index 00000000..71849f7a
--- /dev/null
+++ b/Src/nu/bitbuffer.h
@@ -0,0 +1,26 @@
+#ifndef NULLSOFT_UTILITY_BITBUFFER_H
+#define NULLSOFT_UTILITY_BITBUFFER_H
+#include <stddef.h>
+#ifdef _WIN32
+#include <stddef.h>
+#else
+#include <inttypes.h>
+#endif
+class BitBuffer
+{
+public:
+ BitBuffer();
+ void WriteBit(char bit);
+ void WriteBits(uintptr_t num, size_t bitlen);
+ void WriteBytes(void *buffer, size_t bytes);
+ void WriteByte(unsigned char byte);
+ unsigned char *Get() { return buffer; }
+ size_t GetLength() { return length; }
+
+private:
+ void Resize(size_t newlen);
+ unsigned char *buffer;
+ size_t length;
+ size_t bits;
+};
+#endif \ No newline at end of file
diff --git a/Src/nu/cast64.h b/Src/nu/cast64.h
new file mode 100644
index 00000000..15313fb3
--- /dev/null
+++ b/Src/nu/cast64.h
@@ -0,0 +1,41 @@
+#ifndef NULLSOFT_UTILITY_CAST_64_H
+#define NULLSOFT_UTILITY_CAST_64_H
+
+#include <limits>
+
+#ifdef max
+#undef max
+#endif
+
+#ifdef min
+#undef min
+#endif
+
+namespace nu
+{
+ template<class dest, class src>
+ dest saturate_cast(src srcVal)
+ {
+ if (std::numeric_limits<dest>::is_bounded && srcVal > std::numeric_limits<dest>::max())
+ return (dest)std::numeric_limits<dest>::min);
+ else
+ return (dest)srcVal;
+ }
+
+ template<class dest, class src>
+ bool checked_cast_to(src srcVal, dest *dstVal)
+ {
+ if (!std::numeric_limits<dest>::is_bounded ||
+ (srcVal >= std::numeric_limits<dest>::min() && srcVal <= std::numeric_limits<dest>::max()))
+ {
+ *dstVal = (dest)srcVal;
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+}
+
+#endif
diff --git a/Src/nu/comboskin.cpp b/Src/nu/comboskin.cpp
new file mode 100644
index 00000000..87ea3e8d
--- /dev/null
+++ b/Src/nu/comboskin.cpp
@@ -0,0 +1,15 @@
+#include "comboskin.h"
+#include "../nu/MediaLibraryInterface.h"
+
+
+
+ComboSkin::ComboSkin(HWND hwnd)
+ : token(0)
+{
+ token = mediaLibrary.SkinComboBox(hwnd);
+}
+
+ComboSkin::~ComboSkin()
+{
+ mediaLibrary.UnskinComboBox(token);
+}
diff --git a/Src/nu/comboskin.h b/Src/nu/comboskin.h
new file mode 100644
index 00000000..39f7bb4e
--- /dev/null
+++ b/Src/nu/comboskin.h
@@ -0,0 +1,15 @@
+#ifndef _COMBOSKIN_H
+#define _COMBOSKIN_H
+
+#include <windows.h>
+
+class ComboSkin
+{
+public:
+ ComboSkin(HWND hwnd);
+ ~ComboSkin();
+
+ int token;
+};
+
+#endif \ No newline at end of file
diff --git a/Src/nu/cstrlib.cpp b/Src/nu/cstrlib.cpp
new file mode 100644
index 00000000..be23972a
--- /dev/null
+++ b/Src/nu/cstrlib.cpp
@@ -0,0 +1,59 @@
+/* Utility library for C strings */
+
+#include <windows.h>
+
+extern "C"
+char *scanstr_back(char *str, char *toscan, char *defval)
+{
+ char *s=str+strlen(str)-1;
+ if (strlen(str) < 1) return defval;
+ if (strlen(toscan) < 1) return defval;
+ while (1)
+ {
+ char *t=toscan;
+ while (*t)
+ if (*t++ == *s) return s;
+ t=CharPrev(str,s);
+ if (t==s) return defval;
+ s=t;
+ }
+}
+
+extern "C"
+char *extension(char *fn)
+{
+ char *s = scanstr_back(fn,".\\",fn-1);
+ if (s < fn) return "";
+ if (*s == '\\') return "";
+ return (s+1);
+}
+
+void CleanDirectory(char *str)
+{
+ if (!str)
+ return;
+ int l = strlen(str);
+
+ while (l--)
+ {
+ if (str[l] == ' '
+ || str[l] == '.')
+ str[l]=0;
+ else
+ break;
+ }
+}
+
+
+void FormatSizeStr64(char *out, __int64 size)
+{
+ if (size < 1024*1024) wsprintf(out, "%u KB", (DWORD)(size >> 10));
+ else if (size < 1024*1024*1024)
+ {
+ wsprintf(out, "%u.%02u MB", (DWORD)(size >> 20), ((((DWORD)(size >> 10))&1023)*100) >> 10);
+ }
+ else
+ {
+ wsprintf(out, "%u.%02u GB", (DWORD)(size >> 30), ((((DWORD)(size >> 20))&1023)*100) >> 10);
+ }
+} \ No newline at end of file
diff --git a/Src/nu/cstrlib.h b/Src/nu/cstrlib.h
new file mode 100644
index 00000000..74910187
--- /dev/null
+++ b/Src/nu/cstrlib.h
@@ -0,0 +1,10 @@
+#ifndef NULLSOFT_NU_CSTRLIBH
+#define NULLSOFT_NU_CSTRLIBH
+extern "C"
+{
+ char *scanstr_back(char *str, char *toscan, char *defval);
+ char *extension(char *fn);
+}
+void CleanDirectory(char *str);
+void FormatSizeStr64(char *out, __int64 size);
+#endif \ No newline at end of file
diff --git a/Src/nu/dispatchTable.h b/Src/nu/dispatchTable.h
new file mode 100644
index 00000000..f22d1221
--- /dev/null
+++ b/Src/nu/dispatchTable.h
@@ -0,0 +1,55 @@
+#ifndef NULLSOFT_ORB_PLUGIN_DISPATCHTABLE_HEADER
+#define NULLSOFT_ORB_PLUGIN_DISPATCHTABLE_HEADER
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+#pragma once
+#endif
+
+#define DISPTABLE_INCLUDE() public:\
+ STDMETHOD (GetIDsOfNames)(REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames, LCID lcid, DISPID FAR* rgdispid);\
+ STDMETHOD (GetTypeInfo)(unsigned int itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo);\
+ STDMETHOD (GetTypeInfoCount)(unsigned int FAR * pctinfo);\
+ STDMETHOD (Invoke)(DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, EXCEPINFO FAR * pexecinfo, unsigned int FAR *puArgErr);\
+ private:\
+ typedef struct __DISPATCHENTRY DISPATCHENTRY;\
+ const static DISPATCHENTRY szDispatchTable[];
+
+#define DISPTABLE_BEGIN()\
+ typedef HRESULT (DISPTABLE_CLASS::*DISPATCHHANDLER)(WORD /*wFlags*/, DISPPARAMS FAR* /*pdispparams*/, VARIANT FAR* /*pvarResult*/, UINT FAR* /*puArgErr*/);\
+ typedef struct __DISPATCHENTRY {\
+ UINT id;\
+ LPCWSTR name;\
+ DISPATCHHANDLER handler;\
+ } DISPATCHENTRY;\
+ const DISPTABLE_CLASS::DISPATCHENTRY DISPTABLE_CLASS::szDispatchTable[] = {
+
+#define DISPTABLE_END\
+ };\
+ HRESULT DISPTABLE_CLASS::GetIDsOfNames(REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames, LCID lcid, DISPID FAR* rgdispid) {\
+ UINT unknowns = 0;\
+ for (unsigned int i = 0; i != cNames; i++) {\
+ rgdispid[i] = DISPID_UNKNOWN;\
+ for (INT j =0; j < ARRAYSIZE(szDispatchTable); j++) {\
+ if (CSTR_EQUAL == CompareString(CSTR_INVARIANT, 0, rgszNames[i], -1, szDispatchTable[j].name, -1)) {\
+ rgdispid[i] = szDispatchTable[j].id; break; }\
+ }\
+ if (DISPID_UNKNOWN == rgdispid[i]) { unknowns++; }\
+ }\
+ return (0 != unknowns) ? DISP_E_UNKNOWNNAME : S_OK;\
+ }\
+ HRESULT DISPTABLE_CLASS::GetTypeInfo(unsigned int itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo) { return E_NOTIMPL; }\
+ HRESULT DISPTABLE_CLASS::GetTypeInfoCount(unsigned int FAR * pctinfo) { return E_NOTIMPL; }\
+ HRESULT DISPTABLE_CLASS::Invoke(DISPID dispId, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, EXCEPINFO FAR * pexecinfo, unsigned int FAR *puArgErr) {\
+ for (INT i = 0; i < ARRAYSIZE(szDispatchTable); i++) {\
+ if (dispId == szDispatchTable[i].id) { return (this->*szDispatchTable[i].handler)(wFlags, pdispparams, pvarResult, puArgErr); }\
+ }\
+ return DISP_E_MEMBERNOTFOUND;\
+ }
+
+#define DISPENTRY_ADD(__id, __name, __handler)\
+ { DISPTABLE_CLASS::__id, __name, &DISPTABLE_CLASS::__handler },
+
+#define DISPHANDLER_REGISTER(__handler)\
+ HRESULT __handler(WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, unsigned int FAR *puArgErr);
+
+#endif //NULLSOFT_ORB_PLUGIN_DISPATCHTABLE_HEADER \ No newline at end of file
diff --git a/Src/nu/factoryt.h b/Src/nu/factoryt.h
new file mode 100644
index 00000000..1f6f47f3
--- /dev/null
+++ b/Src/nu/factoryt.h
@@ -0,0 +1,77 @@
+#pragma once
+
+#include <api/service/waservicefactoryi.h>
+#include <api/service/services.h>
+
+/*
+====== Usage ======
+disp_t: your Dispatchable base class
+implt_t: your implementation class
+
+ServiceFactoryT<disp_t, impl_t> myFactory;
+impl_t myImplementation;
+
+//....
+
+//during service registration
+myFactory.Register(WASABI_API_SVC);
+
+//during service deregistration
+myFactory.Deregister(WASABI_API_SVC);
+
+==== Class requirements ====
+your base or implementation class requires the following three static methods
+static FOURCC getServiceType(); // return your type (e.g. WaSvc::UNIQUE)... might already be defined in the dispatchable base class
+static const char *getServiceName(); // return your service name
+static GUID getServiceGuid(); // return your service GUID
+must implementation a constructor that requires no parameters
+*/
+
+template <class disp_t, class impl_t>
+class ServiceFactoryT : public waServiceFactory
+{
+public:
+ ServiceFactoryT()
+ {
+ }
+
+ ~ServiceFactoryT()
+ {
+ }
+
+ void Register(api_service *serviceManager)
+ {
+ serviceManager->service_register(this);
+ }
+
+ void Deregister(api_service *serviceManager)
+ {
+ serviceManager->service_deregister(this);
+ }
+
+private:
+ FOURCC svc_serviceType() { return impl_t::getServiceType(); }
+ const char *svc_getServiceName() { return impl_t::getServiceName(); }
+ GUID svc_getGuid() { return impl_t::getServiceGuid(); } // GUID per service factory, can be INVALID_GUID
+ void *svc_getInterface(int global_lock = TRUE) { return static_cast<disp_t *>(new impl_t); }
+ int svc_supportNonLockingGetInterface() { return 1; }
+ int svc_releaseInterface(void *ifc) { disp_t *disp = static_cast<disp_t *>(ifc); impl_t *impl = static_cast<impl_t *>(disp); delete impl; return 1; }
+ const wchar_t *svc_getTestString() { return 0; }
+ int svc_notify(int msg, int param1 = 0, int param2 = 0) { return 0; }
+
+
+protected:
+#define CBCLASS ServiceFactoryT<disp_t, impl_t>
+ START_DISPATCH_INLINE;
+ CB(WASERVICEFACTORY_GETSERVICETYPE, svc_serviceType);
+ CB(WASERVICEFACTORY_GETSERVICENAME, svc_getServiceName);
+ CB(WASERVICEFACTORY_GETGUID, svc_getGuid);
+ CB(WASERVICEFACTORY_GETINTERFACE, svc_getInterface);
+ CB(WASERVICEFACTORY_SUPPORTNONLOCKINGGETINTERFACE, svc_supportNonLockingGetInterface);
+ CB(WASERVICEFACTORY_RELEASEINTERFACE, svc_releaseInterface);
+ CB(WASERVICEFACTORY_GETTESTSTRING, svc_getTestString);
+ CB(WASERVICEFACTORY_SERVICENOTIFY, svc_notify);
+ END_DISPATCH;
+#undef CBCLASS
+};
+
diff --git a/Src/nu/listview.cpp b/Src/nu/listview.cpp
new file mode 100644
index 00000000..05e60efb
--- /dev/null
+++ b/Src/nu/listview.cpp
@@ -0,0 +1,285 @@
+/*
+** Copyright (C) 2003 Nullsoft, Inc.
+**
+** This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held
+** liable for any damages arising from the use of this software.
+**
+** Permission is granted to anyone to use this software for any purpose, including commercial applications, and to
+** alter it and redistribute it freely, subject to the following restrictions:
+**
+** 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software.
+** If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
+**
+** 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+**
+** 3. This notice may not be removed or altered from any source distribution.
+**
+*/
+
+#include <windows.h>
+#include <commctrl.h>
+
+#include "listview.h"
+
+void W_ListView::AddImageCol( int w )
+{
+ LVCOLUMN lvc = { 0, };
+ lvc.mask = LVCF_WIDTH | LVCF_FMT | LVCF_SUBITEM;
+ lvc.fmt = LVCFMT_IMAGE;
+ lvc.iSubItem = m_col;
+
+ if ( w )
+ lvc.cx = w;
+
+ ListView_InsertColumn( m_hwnd, m_col, &lvc );
+ ++m_col;
+}
+
+int W_ListView::GetColumnWidth( int col )
+{
+ if ( col < 0 || col >= m_col )
+ return 0;
+
+ return ListView_GetColumnWidth( m_hwnd, col );
+}
+
+int W_ListView::GetParam( int p )
+{
+ LVITEM lvi = { 0, };
+ lvi.mask = LVIF_PARAM;
+ lvi.iItem = p;
+
+ ListView_GetItem( m_hwnd, &lvi );
+
+ return (int)(INT_PTR)lvi.lParam;
+}
+
+void W_ListView::SetItemParam( int p, int param )
+{
+ LVITEM lvi = { 0, };
+ lvi.iItem = p;
+ lvi.mask = LVIF_PARAM;
+ lvi.lParam = param;
+
+ ListView_SetItem( m_hwnd, &lvi );
+}
+
+void W_ListView::SetFont( HFONT newFont )
+{
+ if ( m_font )
+ {
+ if ( m_hwnd )
+ SetWindowFont( m_hwnd, NULL, FALSE );
+
+ DeleteFont( m_font );
+ }
+
+ m_font = NULL;
+ if ( m_hwnd )
+ {
+ SetWindowFont( m_hwnd, newFont, FALSE );
+ InvalidateRect( m_hwnd, NULL, TRUE );
+ }
+}
+
+int W_ListView::FindItemByPoint( int x, int y )
+{
+ int l = GetCount();
+ for ( int i = 0; i < l; i++ )
+ {
+ RECT r;
+ GetItemRect( i, &r );
+ if ( r.left <= x && r.right >= x && r.top <= y && r.bottom >= y )
+ return i;
+ }
+ return -1;
+}
+
+W_ListView::W_ListView()
+{
+ m_hwnd = NULL;
+ m_col = 0;
+ m_font = NULL;
+}
+
+W_ListView::W_ListView( HWND hwnd )
+{
+ m_hwnd = NULL;
+ m_col = 0;
+ m_font = NULL;
+
+ if ( IsWindow( hwnd ) )
+ {
+ m_hwnd = hwnd;
+ ListView_SetExtendedListViewStyleEx( m_hwnd, LVS_EX_FULLROWSELECT | LVS_EX_INFOTIP, LVS_EX_FULLROWSELECT | LVS_EX_INFOTIP );
+ }
+}
+
+W_ListView::W_ListView( HWND hwndDlg, int resourceId )
+{
+ m_hwnd = NULL;
+ m_col = 0;
+ m_font = NULL;
+
+ m_hwnd = GetDlgItem( hwndDlg, resourceId );
+ if ( IsWindow( m_hwnd ) )
+ {
+ ListView_SetExtendedListViewStyleEx( m_hwnd, LVS_EX_FULLROWSELECT | LVS_EX_INFOTIP, LVS_EX_FULLROWSELECT | LVS_EX_INFOTIP );
+ }
+}
+
+W_ListView::~W_ListView()
+{
+ if ( m_font )
+ DeleteFont( m_font );
+
+ m_font = 0;
+}
+
+void W_ListView::SetTextColors(COLORREF foregroundColor, COLORREF backgroundColor)
+{
+ ListView_SetTextColor(m_hwnd, foregroundColor);
+ ListView_SetTextBkColor(m_hwnd, backgroundColor);
+}
+
+void W_ListView::InvertSelection()
+{
+ int n = GetCount();
+ for (int i = 0; i < n; i++)
+ {
+ if (GetSelected(i))
+ Unselect(i);
+ else
+ SetSelected(i);
+ }
+}
+
+/* unicode / ansi trouble spots go below this line */
+void W_ListView::AddAutoCol(LPTSTR text)
+{
+ LVCOLUMN lvc = {0};
+ lvc.mask = LVCF_TEXT;
+ lvc.pszText = text;
+
+ ListView_InsertColumn(m_hwnd, m_col, &lvc);
+ m_col++;
+}
+
+void W_ListView::AddCol(const wchar_t *text, int w)
+{
+ LVCOLUMN lvc = {0};
+ lvc.mask = LVCF_TEXT | LVCF_WIDTH;
+ lvc.pszText = (LPTSTR)text;
+
+ if (w)
+ lvc.cx = w;
+
+ SendMessageW(m_hwnd, LVM_INSERTCOLUMNW, (WPARAM)m_col, (LPARAM)&lvc);
+ m_col++;
+}
+
+void W_ListView::AddCol(const char *text, int w)
+{
+ LVCOLUMN lvc = {0};
+ lvc.mask = LVCF_TEXT | LVCF_WIDTH;
+ lvc.pszText = (LPTSTR) text;
+ if (w) lvc.cx = w;
+ SendMessageA(m_hwnd, LVM_INSERTCOLUMNA, (WPARAM)m_col, (LPARAM)&lvc);
+ m_col++;
+}
+
+int W_ListView::AppendItem(LPCWSTR text, LPARAM param)
+{
+ LVITEM lvi = {0};
+ lvi.mask = LVIF_TEXT | LVIF_PARAM;
+ lvi.iItem = GetCount();
+ lvi.pszText = (LPTSTR) text;
+ lvi.cchTextMax = wcslen(text);
+ lvi.lParam = param;
+
+ return (int)(INT_PTR)SendMessageW(m_hwnd, LVM_INSERTITEMW, 0, (LPARAM)&lvi);
+}
+
+int W_ListView::InsertItem(int p, const wchar_t *text, LPARAM param)
+{
+ LVITEM lvi = {0};
+ lvi.mask = LVIF_TEXT | LVIF_PARAM;
+ lvi.iItem = p;
+ lvi.pszText = (LPTSTR) text;
+ lvi.cchTextMax = wcslen(text);
+ lvi.lParam = param;
+
+ return (int)(INT_PTR)SendMessageW(m_hwnd, LVM_INSERTITEMW, 0, (LPARAM)&lvi);
+}
+
+int W_ListView::InsertItem(int p, const char *text, LPARAM param)
+{
+ LVITEM lvi = {0};
+ lvi.mask = LVIF_TEXT | LVIF_PARAM;
+ lvi.iItem = p;
+ lvi.pszText = (LPTSTR) text;
+ lvi.cchTextMax = lstrlenA(text);
+ lvi.lParam = param;
+
+ return (int)(INT_PTR)SendMessageA(m_hwnd, LVM_INSERTITEMA, 0, (LPARAM)&lvi);
+}
+
+void W_ListView::SetItemText(int p, int si, const wchar_t *text)
+{
+ LVITEM lvi = {0};
+ lvi.iItem = p;
+ lvi.iSubItem = si;
+ lvi.mask = LVIF_TEXT;
+ lvi.pszText = (LPTSTR)text;
+ lvi.cchTextMax = wcslen(text);
+
+ SendMessageW(m_hwnd, LVM_SETITEMW, 0, (LPARAM)&lvi);
+}
+
+void W_ListView::SetItemText(int p, int si, const char *text)
+{
+ LVITEM lvi = {0};
+ lvi.iItem = p;
+ lvi.iSubItem = si;
+ lvi.mask = LVIF_TEXT;
+ lvi.pszText = (LPTSTR)text;
+ lvi.cchTextMax = lstrlenA(text);
+
+ SendMessageA(m_hwnd, LVM_SETITEMA, 0, (LPARAM)&lvi);
+}
+
+void W_ListView::setwnd (HWND hwnd)
+{
+ m_hwnd = hwnd;
+ if (IsWindow(hwnd))
+ {
+ ListView_SetExtendedListViewStyleEx(hwnd, LVS_EX_FULLROWSELECT | LVS_EX_INFOTIP, LVS_EX_FULLROWSELECT | LVS_EX_INFOTIP);
+#if defined(_UNICODE) || defined(UNICODE)
+ SendMessageW(hwnd, CCM_SETUNICODEFORMAT, TRUE, 0);
+#endif
+ }
+}
+
+void W_ListView::GetText(int p, int si, wchar_t *text, int maxlen)
+{
+ LVITEM lvi = {0};
+ lvi.iItem = p;
+ lvi.iSubItem = si;
+ lvi.mask = LVIF_TEXT;
+ lvi.pszText = (LPTSTR)text;
+ lvi.cchTextMax = maxlen;
+
+ SendMessageW(m_hwnd, LVM_GETITEMTEXTW, p, (LPARAM)&lvi);
+}
+
+void W_ListView::GetText(int p, int si, char *text, int maxlen)
+{
+ LVITEM lvi = {0};
+ lvi.iItem = p;
+ lvi.iSubItem = si;
+ lvi.mask = LVIF_TEXT;
+ lvi.pszText = (LPTSTR)text;
+ lvi.cchTextMax = maxlen;
+
+ SendMessageA(m_hwnd, LVM_GETITEMTEXTA, p, (LPARAM)&lvi);
+} \ No newline at end of file
diff --git a/Src/nu/listview.h b/Src/nu/listview.h
new file mode 100644
index 00000000..d29db58a
--- /dev/null
+++ b/Src/nu/listview.h
@@ -0,0 +1,229 @@
+/*
+** Copyright (C) 2003 Nullsoft, Inc.
+**
+** This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held
+** liable for any damages arising from the use of this software.
+**
+** Permission is granted to anyone to use this software for any purpose, including commercial applications, and to
+** alter it and redistribute it freely, subject to the following restrictions:
+**
+** 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software.
+** If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
+**
+** 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+**
+** 3. This notice may not be removed or altered from any source distribution.
+**
+*/
+
+#ifndef _LISTVIEW_H_
+#define _LISTVIEW_H_
+
+#include <windows.h>
+#include <windowsx.h>
+#include <commctrl.h>
+
+#ifndef LVS_EX_DOUBLEBUFFER //this will work XP only
+#define LVS_EX_DOUBLEBUFFER 0x00010000
+#endif
+
+class W_ListView
+{
+public:
+ W_ListView();
+ W_ListView( HWND hwndView );
+ W_ListView( HWND hwndDlg, int resourceId );
+ ~W_ListView();
+
+ void InvertSelection();
+ void SetTextColors( COLORREF foregroundColor, COLORREF backgroundColor );
+ void SetFont( HFONT newFont );
+ void setwnd( HWND hwnd );
+ void AddCol( const wchar_t *text, int w );
+ void AddCol( const char *text, int w );
+ void AddAutoCol( LPTSTR text );
+ void AddImageCol( int w );
+
+ void JustifyColumn( int column, int justificationFlag )
+ {
+ LVCOLUMN col;
+ col.mask = LVCF_FMT;
+ col.fmt = justificationFlag;
+
+ ListView_SetColumn( m_hwnd, column, &col );
+ }
+
+ void SetColumnWidth( int column, int width )
+ {
+ ListView_SetColumnWidth( m_hwnd, column, width );
+ }
+
+ int GetCount( void )
+ {
+ return ListView_GetItemCount( m_hwnd );
+ }
+
+ int GetParam( int p );
+
+ void DeleteItem( int n )
+ {
+ ListView_DeleteItem( m_hwnd, n );
+ }
+
+ void RefreshItem( int item )
+ {
+ ListView_RedrawItems( m_hwnd, item, item );
+ }
+
+ void RefreshAll()
+ {
+ ListView_RedrawItems( m_hwnd, 0, GetCount() );
+ }
+
+ void Clear( void )
+ {
+ ListView_DeleteAllItems( m_hwnd );
+ }
+
+ int GetSelected( int x )
+ {
+ return( ListView_GetItemState( m_hwnd, x, LVIS_SELECTED ) & LVIS_SELECTED ) ? 1 : 0;
+ }
+
+ int GetSelectedCount()
+ {
+ return ListView_GetSelectedCount( m_hwnd );
+ }
+
+ int GetNextSelected( int start = -1 )
+ {
+ return ListView_GetNextItem( m_hwnd, start, LVNI_ALL | LVNI_SELECTED );
+ }
+
+ int GetSelectionMark()
+ {
+ return ListView_GetSelectionMark( m_hwnd );
+ }
+
+ void SetSelected( int x )
+ {
+ ListView_SetItemState( m_hwnd, x, LVIS_SELECTED, LVIS_SELECTED );
+ }
+
+ void SelectAll()
+ {
+ ListView_SetItemState( m_hwnd, -1, LVIS_SELECTED, LVIS_SELECTED );
+ }
+
+ void UnselectAll()
+ {
+ ListView_SetItemState( m_hwnd, -1, 0, LVIS_SELECTED );
+ }
+
+ void Unselect( int x )
+ {
+ ListView_SetItemState( m_hwnd, x, 0, LVIS_SELECTED );
+ }
+
+ void EditItem( int x )
+ {
+ SetFocus( m_hwnd );
+ ListView_EditLabel( m_hwnd, x );
+ }
+
+ int AppendItem( LPCWSTR text, LPARAM param );
+ int InsertItem( int p, const wchar_t *text, LPARAM param );
+ int InsertItem( int p, const char *text, LPARAM param );
+
+ void GetItemRect( int i, RECT *r )
+ {
+ ListView_GetItemRect( m_hwnd, i, r, LVIR_BOUNDS );
+ }
+
+ void SetItemText( int p, int si, const wchar_t *text );
+ void SetItemText( int p, int si, const char *text );
+ void SetItemParam( int p, int param );
+
+ void GetText( int p, int si, char *text, int maxlen );
+ void GetText( int p, int si, wchar_t *text, int maxlen );
+
+ size_t GetTextLength( int p, int si )
+ {
+ LVITEM lvItem;
+ lvItem.cchTextMax = 0;
+ lvItem.pszText = 0;
+ lvItem.iSubItem = si;
+ lvItem.iItem = p;
+
+ return SendMessage( m_hwnd, LVM_GETITEMTEXT, p, (LPARAM)&lvItem );
+ }
+
+ int FindItemByParam( int param )
+ {
+ LVFINDINFO fi = { LVFI_PARAM,0,param };
+
+ return ListView_FindItem( m_hwnd, -1, &fi );
+ }
+
+ int FindItemByPoint( int x, int y );
+
+ void SetVirtualCount( int count, DWORD flags = 0 )
+ {
+ ListView_SetItemCountEx( m_hwnd, count, flags );
+ }
+
+ void SetVirtualCountAsync( int count, DWORD flags = 0 )
+ {
+ if ( m_hwnd )
+ PostMessage( m_hwnd, LVM_SETITEMCOUNT, count, flags );
+ }
+
+ int GetColumnWidth( int col );
+
+ void AutoColumnWidth( int col )
+ {
+ ListView_SetColumnWidth( m_hwnd, col, LVSCW_AUTOSIZE_USEHEADER );
+ }
+
+ void AutoSizeColumn( int col )
+ {
+ ListView_SetColumnWidth( m_hwnd, col, LVSCW_AUTOSIZE );
+ }
+
+ HWND getwnd( void )
+ {
+ return m_hwnd;
+ }
+
+ void ScrollTo( int index )
+ {
+ ListView_EnsureVisible( m_hwnd, index, FALSE );
+ }
+
+ void SetDoubleBuffered( bool buffered = true )
+ {
+ ListView_SetExtendedListViewStyleEx( m_hwnd, LVS_EX_DOUBLEBUFFER, buffered ? LVS_EX_DOUBLEBUFFER : 0 );
+ }
+
+ bool ColumnExists( int columnNum )
+ {
+ LVCOLUMN col;
+ col.mask = LVCF_WIDTH;
+
+ return ListView_GetColumn( m_hwnd, columnNum, &col );
+ }
+
+ void ForceUnicode()
+ {
+ SendMessage( m_hwnd, CCM_SETUNICODEFORMAT, TRUE, 0 );
+ }
+
+protected:
+ HWND m_hwnd;
+ HFONT m_font;
+ int m_col;
+
+};
+
+#endif//_LISTVIEW_H_
+
diff --git a/Src/nu/menuHelpers.cpp b/Src/nu/menuHelpers.cpp
new file mode 100644
index 00000000..83640f91
--- /dev/null
+++ b/Src/nu/menuHelpers.cpp
@@ -0,0 +1,132 @@
+#include "./menuHelpers.h"
+#include <strsafe.h>
+
+INT MenuHelper_CopyMenuEx(HMENU hDest, INT iDstStart, HMENU hSource, INT iSrcStart, INT iSrcCount)
+{
+ if (!hDest || !hSource) return 0;
+ TCHAR szText[1024] = {0};
+
+ MENUITEMINFO mii = {sizeof(MENUITEMINFO), };
+ mii.fMask = MIIM_BITMAP | MIIM_CHECKMARKS | MIIM_DATA | MIIM_FTYPE | MIIM_ID | MIIM_STATE | MIIM_STRING | MIIM_SUBMENU;
+ mii.dwTypeData = szText;
+
+ if (iDstStart < 0) iDstStart = 0;
+ if (iSrcStart < 0) iSrcStart = 0;
+
+ INT pos = iDstStart;
+
+ if ( 0 != iSrcCount)
+ {
+ INT c = GetMenuItemCount(hSource);
+ if (-1 == c || iSrcStart > c) return 0;
+
+ if (iSrcCount < 0)
+ iSrcCount = c - iSrcStart;
+ else if (iSrcCount < (c - iSrcStart))
+ c = iSrcCount + iSrcStart;
+
+ for (int i = iSrcStart; i < c; i++)
+ {
+ mii.cch = ARRAYSIZE(szText);
+ if (GetMenuItemInfo(hSource, i, TRUE, &mii))
+ {
+ if(InsertMenuItem(hDest, pos, TRUE, &mii))
+ {
+ pos++;
+ }
+ }
+ }
+ }
+ else
+ {
+ mii.cch = ARRAYSIZE(szText);
+ if (GetMenuItemInfo(hSource, iSrcStart, FALSE, &mii))
+ {
+ if (InsertMenuItem(hDest, pos, TRUE, &mii))
+ {
+ pos++;
+ }
+ }
+ }
+ return pos - iDstStart;
+}
+
+INT MenuHelper_CopyMenu(HMENU hDest, INT iDstStart, HMENU hSource)
+{
+ return MenuHelper_CopyMenuEx(hDest, iDstStart, hSource, 0, -1);
+}
+
+HMENU MenuHelper_DuplcateMenu(HMENU hMenu)
+{
+ HMENU hDest = CreatePopupMenu();
+ if (NULL == hMenu)
+ return NULL;
+
+ MenuHelper_CopyMenu(hDest, 0, hMenu);
+ return hDest;
+}
+
+INT MenuHelper_InsertSeparator(HMENU hMenu, INT iPos)
+{
+ if (!hMenu) return FALSE;
+ MENUITEMINFO mii = {sizeof(MENUITEMINFO), };
+ mii.fMask = MIIM_FTYPE;
+ mii.fType = MFT_SEPARATOR;
+ return InsertMenuItem(hMenu, iPos, TRUE, &mii);
+}
+
+void MenuHelper_RemoveRange(HMENU hMenu, UINT min, UINT max)
+{
+ MENUITEMINFO mii = {sizeof(MENUITEMINFO), };
+ mii.fMask = MIIM_ID;
+ INT count = GetMenuItemCount(hMenu);
+ if (-1 == count)
+ return;
+ while(count--)
+ {
+ if (GetMenuItemInfo(hMenu, count, TRUE, &mii) &&
+ mii.wID >= min && mii.wID <= max)
+ {
+ RemoveMenu(hMenu, count, MF_BYPOSITION);
+ }
+ }
+}
+
+void MenuHelper_EnableGroup(HMENU hMenu, UINT *pszItems, INT cchItems, BOOL fByPos, BOOL bEnable)
+{
+ UINT enableGroup = (bEnable) ? MF_ENABLED : (MF_DISABLED | MF_GRAYED);
+
+ if (!fByPos)
+ enableGroup |= MF_BYCOMMAND;
+
+ for (INT i = 0; i < cchItems; i++)
+ {
+ EnableMenuItem(hMenu, pszItems[i], enableGroup);
+ }
+}
+
+BOOL MenuHelper_GetMenuItemPos(HMENU hMenu, UINT itemId, INT *pPos)
+{
+ if (pPos)
+ *pPos = -1;
+
+ MENUITEMINFO mii = {sizeof(MENUITEMINFO), };
+ mii.fMask = MIIM_ID;
+
+ INT count = GetMenuItemCount(hMenu);
+ if (count < 1)
+ return FALSE;
+
+ while(count--)
+ {
+ if (GetMenuItemInfo(hMenu, count, TRUE, &mii) &&
+ mii.wID == itemId)
+ {
+ if (pPos)
+ *pPos = count;
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+} \ No newline at end of file
diff --git a/Src/nu/menuHelpers.h b/Src/nu/menuHelpers.h
new file mode 100644
index 00000000..5fd3e7fc
--- /dev/null
+++ b/Src/nu/menuHelpers.h
@@ -0,0 +1,18 @@
+#ifndef NULLOSFT_MENUHELPERS_HEADER
+#define NULLOSFT_MENUHELPERS_HEADER
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+#pragma once
+#endif
+
+#include <wtypes.h>
+INT MenuHelper_CopyMenuEx(HMENU hDest, INT iDstStart, HMENU hSource, INT iSrcStart, INT iSrcCount);
+INT MenuHelper_CopyMenu(HMENU hDest, INT iDstStart, HMENU hSource);
+HMENU MenuHelper_DuplcateMenu(HMENU hMenu);
+
+INT MenuHelper_InsertSeparator(HMENU hMenu, INT iPos);
+void MenuHelper_RemoveRange(HMENU hMenu, UINT min, UINT max);
+void MenuHelper_EnableGroup(HMENU hMenu, UINT *pszItems, INT cchItems, BOOL fByPos, BOOL bEnable);
+BOOL MenuHelper_GetMenuItemPos(HMENU hMenu, UINT itemId, INT *pPos);
+
+#endif // NULLOSFT_MENUHELPERS_HEADER \ No newline at end of file
diff --git a/Src/nu/menushortcuts.cpp b/Src/nu/menushortcuts.cpp
new file mode 100644
index 00000000..f9a4a5d2
--- /dev/null
+++ b/Src/nu/menushortcuts.cpp
@@ -0,0 +1,204 @@
+#include "./menushortcuts.h"
+#include <strsafe.h>
+
+// change key string to capitalised only so it's more UI consistant (otherwise messes up the localisation look)
+wchar_t* CapitaliseKeyName(wchar_t* szAccelName){
+ if(szAccelName) {
+ wchar_t *p = &szAccelName[1];
+ int space = 0;
+ while(p && *p){
+ if(*p == L' ') space = 1;
+ else {
+ if(!space) {
+ *p = tolower(*p);
+ }
+ else {
+ space = 0;
+ }
+ }
+ p = CharNextW(p);
+ }
+ }
+ return szAccelName;
+}
+
+BOOL AppendShortcutTextEx(LPWSTR pszItemText, INT cchTextMax, WORD wID, ACCEL *pszAccel, INT cchAccel, UINT uMode)
+{
+ BOOL bDirty = FALSE;
+ if (!pszItemText) return FALSE;
+
+ UINT cchLen = lstrlenW(pszItemText);
+
+ if (MSF_REPLACE == (0x0F & uMode))
+ {
+ UINT len;
+ for (len = 0; len < cchLen && L'\t' != pszItemText[len]; len++);
+ if (cchLen != len)
+ {
+ cchLen = len;
+ pszItemText[len] = L'\0';
+ bDirty = TRUE;
+ }
+ }
+
+ if (wID > 0)
+ {
+ wchar_t szAccelName[64] = {0};
+ BOOL bFirst = TRUE;
+ pszItemText += cchLen;
+ size_t cchText = cchTextMax - cchLen;
+
+ for(int k = 0; k < cchAccel; k++)
+ {
+ if (wID == pszAccel[k].cmd)
+ {
+ HRESULT hr = StringCchCopyExW(pszItemText, cchText, ((bFirst) ? L"\t" : L", "), &pszItemText, &cchText, STRSAFE_IGNORE_NULLS);
+ if (S_OK == hr && (FCONTROL & pszAccel[k].fVirt)) {
+ GetKeyNameTextW(MapVirtualKey(VK_CONTROL, 0)<<16, szAccelName, sizeof(szAccelName)/sizeof(szAccelName[0]));
+ hr = StringCchCopyExW(pszItemText, cchText, CapitaliseKeyName(szAccelName), &pszItemText, &cchText, STRSAFE_IGNORE_NULLS);
+ if(S_OK == hr) StringCchCatExW(pszItemText, cchText, L"+", &pszItemText, &cchText, STRSAFE_IGNORE_NULLS);
+ }
+ if (S_OK == hr && (FALT & pszAccel[k].fVirt)) {
+ GetKeyNameTextW(MapVirtualKey(VK_MENU, 0)<<16, szAccelName, sizeof(szAccelName)/sizeof(szAccelName[0]));
+ hr = StringCchCopyExW(pszItemText, cchText, CapitaliseKeyName(szAccelName), &pszItemText, &cchText, STRSAFE_IGNORE_NULLS);
+ if(S_OK == hr) hr = StringCchCatExW(pszItemText, cchText, L"+", &pszItemText, &cchText, STRSAFE_IGNORE_NULLS);
+ }
+ if (S_OK == hr && (FSHIFT & pszAccel[k].fVirt)) {
+ GetKeyNameTextW(MapVirtualKey(VK_SHIFT, 0)<<16, szAccelName, sizeof(szAccelName)/sizeof(szAccelName[0]));
+ hr = StringCchCopyExW(pszItemText, cchText, CapitaliseKeyName(szAccelName), &pszItemText, &cchText, STRSAFE_IGNORE_NULLS);
+ if(S_OK == hr) hr = StringCchCatExW(pszItemText, cchText, L"+", &pszItemText, &cchText, STRSAFE_IGNORE_NULLS);
+ }
+ if (S_OK == hr)
+ {
+ if (FVIRTKEY & pszAccel[k].fVirt)
+ {
+ szAccelName[0] = L'\0';
+ UINT vkey = MapVirtualKey(pszAccel[k].key, 0);
+
+ /* benski> this removes "NUM" from the descriptions of certain characters */
+ switch(pszAccel[k].key)
+ {
+ case VK_INSERT:
+ case VK_DELETE:
+ case VK_HOME:
+ case VK_END:
+ case VK_NEXT: // Page down
+ case VK_PRIOR: // Page up
+ case VK_LEFT:
+ case VK_RIGHT:
+ case VK_UP:
+ case VK_DOWN:
+ vkey |= 0x100; // Add extended bit
+ }
+ vkey<<=16;
+ if (GetKeyNameTextW(vkey, szAccelName, sizeof(szAccelName)/sizeof(szAccelName[0]))) {
+ hr = StringCchCopyExW(pszItemText, cchText, CapitaliseKeyName(szAccelName), &pszItemText, &cchText, STRSAFE_IGNORE_NULLS);
+ }
+ }
+ else if (cchText > 1)
+ {
+ pszItemText[0] = (wchar_t)pszAccel[k].key;
+ pszItemText[1] = L'\0';
+ pszItemText += 2;
+ cchText -= 2;
+ }
+ }
+
+ bFirst = FALSE;
+ bDirty = (S_OK == hr);
+ }
+ }
+ }
+ return bDirty;
+}
+
+BOOL AppendMenuShortcutsEx(HMENU hMenu, ACCEL *pszAccel, INT cchAccel, UINT uMode)
+{
+ wchar_t szText[4096] = {0};
+ if (!hMenu) return FALSE;
+
+ MENUITEMINFOW mii = { sizeof(MENUITEMINFOW), };
+
+ INT c = GetMenuItemCount(hMenu);
+ if (0 == c || -1 == c) return TRUE;
+
+ for (int i = 0; i < c; i++)
+ {
+ mii.fMask = MIIM_ID | MIIM_STRING | MIIM_FTYPE | MIIM_SUBMENU;
+ mii.cch = sizeof(szText)/sizeof(szText[0]);
+ mii.dwTypeData = szText;
+
+ if (GetMenuItemInfoW(hMenu, i, TRUE, &mii) &&
+ 0 == ((MFT_SEPARATOR | MFT_MENUBREAK |MFT_MENUBARBREAK) & mii.fType))
+ {
+ if (AppendShortcutTextEx(mii.dwTypeData, sizeof(szText)/sizeof(szText[0]), (WORD)mii.wID, pszAccel, cchAccel, uMode))
+ {
+ mii.fMask = MIIM_STRING;
+ SetMenuItemInfoW(hMenu, i, TRUE, &mii);
+ }
+ if (NULL != mii.hSubMenu && (MSF_WALKSUBMENU & uMode))
+ {
+ AppendMenuShortcutsEx(mii.hSubMenu, pszAccel, cchAccel, uMode);
+ }
+ }
+ }
+ return TRUE;
+}
+
+BOOL AppendMenuShortcuts(HMENU hMenu, HACCEL *phAccel, INT count, UINT uMode)
+{
+ ACCEL *pszAccel = NULL;
+ INT c = 0;
+
+ if (!hMenu) return FALSE;
+
+ if (phAccel)
+ {
+ for (int i = 0; i < count; i++)
+ {
+ c += CopyAcceleratorTable(phAccel[i], NULL, 0);
+ }
+ if (c)
+ {
+ pszAccel = (ACCEL*)calloc(c, sizeof(ACCEL));
+ if (!pszAccel) return FALSE;
+ }
+ int k = 0;
+ for (int i = 0; i < count; i++)
+ {
+ k += CopyAcceleratorTable(phAccel[i], &pszAccel[k], c - k);
+ }
+ }
+ BOOL r = AppendMenuShortcutsEx(hMenu, pszAccel, c, uMode);
+ if (pszAccel) free(pszAccel);
+ return r;
+}
+
+BOOL AppendShortcutText(LPWSTR pszItemText, INT cchTextMax, WORD wID, HACCEL *phAccel, INT count, UINT uMode)
+{
+ ACCEL *pszAccel = NULL;
+ INT c = 0;
+
+ if (!pszItemText) return FALSE;
+
+ if (phAccel)
+ {
+ for (int i = 0; i < count; i++)
+ {
+ c += CopyAcceleratorTable(phAccel[i], NULL, 0);
+ }
+ if (c)
+ {
+ pszAccel = (ACCEL*)calloc(c, sizeof(ACCEL));
+ if (!pszAccel) return FALSE;
+ }
+ int k = 0;
+ for (int i = 0; i < count; i++)
+ {
+ k += CopyAcceleratorTable(phAccel[i], &pszAccel[k], c - k);
+ }
+ }
+ BOOL r = AppendShortcutTextEx(pszItemText, cchTextMax, wID, pszAccel, c, uMode);
+ if (pszAccel) free(pszAccel);
+ return r;
+} \ No newline at end of file
diff --git a/Src/nu/menushortcuts.h b/Src/nu/menushortcuts.h
new file mode 100644
index 00000000..b9c9127a
--- /dev/null
+++ b/Src/nu/menushortcuts.h
@@ -0,0 +1,20 @@
+#ifndef NULLOSFT_MEDIALIBRARY_MENU_SHORTCUTS_HEADER
+#define NULLOSFT_MEDIALIBRARY_MENU_SHORTCUTS_HEADER
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+#pragma once
+#endif
+
+#include <windows.h>
+
+#define MSF_REPLACE 0x0000
+#define MSF_APPEND 0x0001
+#define MSF_WALKSUBMENU 0x0100
+
+BOOL AppendMenuShortcuts(HMENU hMenu, HACCEL *phAccel, INT count, UINT uMode);
+BOOL AppendMenuShortcutsEx(HMENU hMenu, ACCEL *pszAccel, INT count, UINT uMode);
+BOOL AppendShortcutText(LPWSTR pszItemText, INT cchTextMax, WORD wID, HACCEL *phAccel, INT count, UINT uMode);
+BOOL AppendShortcutTextEx(LPWSTR pszItemText, INT cchTextMax, WORD wID, ACCEL *pszAccel, INT cchAccel, UINT uMode);
+
+
+#endif // NULLOSFT_MEDIALIBRARY_MENU_SHORTCUTS_HEADER \ No newline at end of file
diff --git a/Src/nu/mtbrowser.cpp b/Src/nu/mtbrowser.cpp
new file mode 100644
index 00000000..f4bd7ba0
--- /dev/null
+++ b/Src/nu/mtbrowser.cpp
@@ -0,0 +1,611 @@
+#include "./mtbrowser.h"
+#include <exdispid.h>
+
+
+#define QUIT_FORCE 0x00FF
+
+#define THREAD_QUITING 0x0001
+#define THREAD_COMINIT 0x1000
+
+#define MEMMMGR_RECALLOCSTEP 8
+
+#define GetThreadBrowserInstance() ((HTMLContainer2*)TlsGetValue(threadStorage))
+
+typedef struct _THREAD_START_PARAM
+{
+ HANDLE hEvent;
+ MTBROWSER *pmtb;
+} THREAD_START_PARAM;
+
+typedef struct _MEMREC
+{
+ HANDLE handle;
+ FREEPROC freeproc;
+} MEMREC;
+
+typedef struct _MEMMNGR
+{
+ CRITICAL_SECTION cs;
+ MEMREC *pRec;
+ INT cCount;
+ INT cAlloc;
+} MEMMNGR;
+
+typedef struct _APCPARAM
+{
+ MEMMNGR *pmm;
+ VARIANTARG *pArgs;
+ INT cArgs;
+ HWND hwndNotify;
+ UINT uMsgNotify;
+ INT nNotifyCode;
+ APCPROC fnAPC;
+ HANDLE hThread;
+} APCPARAM;
+
+typedef struct _CALLBACKPARAM
+{
+ HWND hwndNotify;
+ UINT uMsgNotify;
+ BOOL bDestroyed;
+ BOOL bQuiting;
+ BOOL bReady;
+} CALLBACKPARAM;
+
+// forward declarations
+
+
+
+static DWORD CALLBACK BrowserThread(LPVOID param);
+static HRESULT MTBrowser_Quit(HTMLContainer2 *pContainer);
+
+static BOOL MemRec_Add(MEMMNGR *pmm, void *pMem, FREEPROC fnFreeProc);
+static BOOL MemRec_Free(MEMMNGR *pmm, void *pMem);
+
+static APCPARAM *AllocAPCParam(MTBROWSER *pmtb, INT cArgs, INT nNotifyCode, APCPROC fnAPC);
+static void CALLBACK FreeAPCParam(void *pMem);
+// APC
+static void CALLBACK APC_FunctionCall(ULONG_PTR param);
+
+static void CALLBACK APC_NavigateToName(HTMLContainer2 *pContainer, VARIANTARG *pArgs, INT cArgs, LPARAM *pResult);
+static void CALLBACK APC_SetLocation(HTMLContainer2 *pContainer, VARIANTARG *pArgs, INT cArgs, LPARAM *pResult);
+static void CALLBACK APC_Refresh2(HTMLContainer2 *pContainer, VARIANTARG *pArgs, INT cArgs, LPARAM *pResult);
+
+
+
+static DWORD threadStorage = TLS_OUT_OF_INDEXES;
+
+BOOL MTBrowser_Init(MTBROWSER *pmtb)
+{
+ if (!pmtb) return FALSE;
+ ZeroMemory(pmtb, sizeof(MTBROWSER));
+
+ if (TLS_OUT_OF_INDEXES == threadStorage)
+ {
+ threadStorage = TlsAlloc();
+ if (TLS_OUT_OF_INDEXES == threadStorage) return FALSE;
+ }
+
+ return TRUE;
+}
+
+BOOL MTBrowser_Clear(MTBROWSER *pmtb)
+{
+ if (pmtb)
+ {
+ if (pmtb->hThread) CloseHandle(pmtb->hThread);
+ if (pmtb->hMemMngr)
+ {
+ MEMMNGR *pmm;
+ pmm = (MEMMNGR*)pmtb->hMemMngr;
+ EnterCriticalSection(&pmm->cs);
+ for(int i = 0; i < pmm->cCount; i++)
+ {
+ if (pmm->pRec[i].handle)
+ {
+ (pmm->pRec[i].freeproc) ? pmm->pRec[i].freeproc(pmm->pRec[i].handle) : free(pmm->pRec[i].handle);
+ }
+ }
+ pmm->cCount = 0;
+ LeaveCriticalSection(&pmm->cs);
+ DeleteCriticalSection(&pmm->cs);
+ free(pmtb->hMemMngr);
+ }
+ ZeroMemory(pmtb, sizeof(MTBROWSER));
+ }
+ return TRUE;
+}
+
+BOOL MTBrowser_Start(MTBROWSER *pmtb, HTMLContainer2 *pContainer, UINT uMsgNotify)
+{
+ THREAD_START_PARAM param = {0};
+ if (!pmtb || !pContainer || TLS_OUT_OF_INDEXES == threadStorage) return FALSE;
+
+ pmtb->hMemMngr = calloc(1, sizeof(MEMMNGR));
+ if (!pmtb->hMemMngr) return FALSE;
+
+ InitializeCriticalSection(&((MEMMNGR*)pmtb->hMemMngr)->cs);
+
+ param.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ param.pmtb = pmtb;
+
+ pmtb->uMsgNotify = uMsgNotify;
+ pmtb->pContainer = pContainer;
+ pmtb->hThread = CreateThread(NULL, 0, BrowserThread, (LPVOID)&param, 0, &pmtb->dwThreadId);
+
+ if (pmtb->hThread)
+ {
+ WaitForSingleObject(param.hEvent, INFINITE);
+ SetThreadPriority(pmtb->hThread, THREAD_PRIORITY_NORMAL);
+ }
+
+ CloseHandle(param.hEvent);
+
+ return (NULL != pmtb->hThread);
+}
+
+BOOL MTBrowser_Kill(MTBROWSER *pmtb, UINT nTerminateDelay)
+{
+ MSG msg;
+ if (pmtb)
+ {
+ pmtb->bQuiting = TRUE;
+ if (pmtb && pmtb->hThread)
+ {
+ PostThreadMessage(pmtb->dwThreadId, WM_QUIT, QUIT_FORCE, 0L);
+ while(WAIT_TIMEOUT == WaitForSingleObject(pmtb->hThread, 0))
+ {
+ DWORD dwStatus;
+ dwStatus = MsgWaitForMultipleObjectsEx(1, &pmtb->hThread, nTerminateDelay, QS_ALLINPUT, MWMO_ALERTABLE | MWMO_INPUTAVAILABLE);
+ if (WAIT_OBJECT_0 + 1 == dwStatus || WAIT_OBJECT_0 == dwStatus)
+ {
+ while (PeekMessageW(&msg, NULL, 0xc0d6, 0xc0d6, PM_NOREMOVE)) if (NULL == msg.hwnd) DispatchMessageW(&msg);
+ }
+ else if (WAIT_TIMEOUT == dwStatus)
+ {
+ TerminateThread(pmtb->hThread, 3);
+ break;
+ }
+ }
+ CloseHandle(pmtb->hThread);
+ pmtb->hThread = NULL;
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+BOOL MTBrowser_QuitAPC(MTBROWSER *pmtb)
+{
+ if (!pmtb) return FALSE;
+ if (pmtb->bQuiting) return TRUE;
+ pmtb->bQuiting = (pmtb->dwThreadId && PostThreadMessage(pmtb->dwThreadId, WM_QUIT, 0, 0L));
+ return pmtb->bQuiting;
+}
+BOOL MTBrowser_NavigateToNameAPC(MTBROWSER *pmtb, LPCWSTR pszURL, UINT fFlags)
+{
+ HAPC hAPC;
+ VARIANTARG *pArgs;
+
+ hAPC = MTBrowser_InitializeAPC(pmtb, 2, MTBC_APC_NAVIGATE, APC_NavigateToName, &pArgs);
+ if (!hAPC) return FALSE;
+
+ pArgs[0].vt = VT_BSTR;
+ pArgs[0].bstrVal= SysAllocString(pszURL);
+ pArgs[1].vt = VT_I4;
+ pArgs[1].intVal = fFlags;
+ return MTBrowser_CallAPC(hAPC);
+}
+
+BOOL MTBrowser_SetLocationAPC(MTBROWSER *pmtb, RECT *pRect)
+{
+ HAPC hAPC;
+ VARIANTARG *pArgs;
+
+ hAPC = MTBrowser_InitializeAPC(pmtb, 4, MTBC_APC_SETLOCATION, APC_SetLocation, &pArgs);
+ if (!hAPC) return FALSE;
+
+ pArgs[0].vt = VT_I4;
+ pArgs[0].intVal = pRect->left;
+ pArgs[1].vt = VT_I4;
+ pArgs[1].intVal = pRect->top;
+ pArgs[2].vt = VT_I4;
+ pArgs[2].intVal = pRect->right - pRect->left;
+ pArgs[3].vt = VT_I4;
+ pArgs[3].intVal = pRect->bottom - pRect->top;
+ return MTBrowser_CallAPC(hAPC);
+}
+
+BOOL MTBrowser_Refresh2APC(MTBROWSER *pmtb, INT nRefreshMode)
+{
+ HAPC hAPC;
+ VARIANTARG *pArgs;
+
+ hAPC = MTBrowser_InitializeAPC(pmtb, 1, MTBC_APC_REFRESH2, APC_Refresh2, &pArgs);
+ if (!hAPC) return FALSE;
+
+ pArgs[0].vt = VT_I4;
+ pArgs[0].intVal = nRefreshMode;
+ return MTBrowser_CallAPC(hAPC);
+}
+HAPC MTBrowser_InitializeAPC(MTBROWSER *pmtb, INT nCount, UINT nCmdCode, APCPROC fnAPC, VARIANTARG **pArgs)
+{
+ APCPARAM *pParam;
+ if (!pmtb || pmtb->bQuiting || !pmtb->hThread) return FALSE;
+ pParam = AllocAPCParam(pmtb, nCount, nCmdCode, fnAPC);
+ if (pParam && pArgs) *pArgs = pParam->pArgs;
+ return (HAPC) pParam;
+}
+
+BOOL MTBrowser_CallAPC(HAPC hAPC)
+{
+ BOOL result;
+ if (!hAPC) return FALSE;
+
+ result = QueueUserAPC(APC_FunctionCall, ((APCPARAM*)hAPC)->hThread, (ULONG_PTR)hAPC);
+ if (!result) MemRec_Free(((APCPARAM*)hAPC)->pmm, hAPC);
+
+ return result;
+}
+
+BOOL MTBrowser_AddMemRec(MTBROWSER *pmtb, void *pMem, FREEPROC fnFreeProc)
+{
+ return (pmtb && pmtb->hMemMngr) ? MemRec_Add((MEMMNGR*)pmtb->hMemMngr, pMem, fnFreeProc) : FALSE;
+}
+
+BOOL MTBrowser_FreeMemRec(MTBROWSER *pmtb, void *pMem)
+{
+ return (pmtb && pmtb->hMemMngr) ? MemRec_Free((MEMMNGR*)pmtb->hMemMngr, pMem) : FALSE;
+}
+
+
+static BOOL MemRec_Add(MEMMNGR *pmm, void *pMem, FREEPROC fnFreeProc)
+{
+ if (!pmm || !pMem) return FALSE;
+
+ EnterCriticalSection(&pmm->cs);
+
+ if (pmm->cCount == pmm->cAlloc)
+ {
+ LPVOID pData;
+ pData = realloc(pmm->pRec, sizeof(MEMREC)*(pmm->cCount + MEMMMGR_RECALLOCSTEP));
+ if (!pData)
+ {
+ LeaveCriticalSection(&pmm->cs);
+ return FALSE;
+ }
+ pmm->pRec = (MEMREC*)pData;
+ pmm->cAlloc = pmm->cCount + MEMMMGR_RECALLOCSTEP;
+ }
+ pmm->pRec[pmm->cCount].handle = pMem;
+ pmm->pRec[pmm->cCount].freeproc = fnFreeProc;
+ pmm->cCount++;
+
+ LeaveCriticalSection(&pmm->cs);
+ return TRUE;
+}
+
+static BOOL MemRec_Free(MEMMNGR *pmm, void *pMem)
+{
+ INT index;
+ MEMREC rec;
+
+ if (!pmm || !pMem) return FALSE;
+
+ EnterCriticalSection(&pmm->cs);
+
+ ZeroMemory(&rec, sizeof(MEMREC));
+
+ for(index = 0; index < pmm->cCount; index++)
+ {
+ if (pmm->pRec[index].handle == pMem)
+ {
+ rec.freeproc= pmm->pRec[index].freeproc;
+ rec.handle = pmm->pRec[index].handle;
+ break;
+ }
+ }
+
+ if (index < (pmm->cCount -1)) MoveMemory(&pmm->pRec[index], &pmm->pRec[index + 1], sizeof(MEMREC)*(pmm->cCount - index - 1));
+ if (index < pmm->cCount) pmm->cCount--;
+
+ if(rec.handle)
+ {
+ (rec.freeproc) ? rec.freeproc(rec.handle) : free(rec.handle);
+ }
+ LeaveCriticalSection(&pmm->cs);
+
+ return (NULL != rec.handle);
+}
+
+#define POSTNOTIFY(_hwnd, _msg, _code, _param) ((_msg && IsWindow(_hwnd)) ? PostMessageW(_hwnd, _msg, (WPARAM)_code, (LPARAM)_param) : FALSE)
+static BOOL CALLBACK OnBrowserEvent(HTMLContainer2 *pContainer, DISPID dispId, DISPPARAMS FAR *pDispParams, LPVOID pUser)
+{
+ CALLBACKPARAM *pcb;
+ pcb = (CALLBACKPARAM*)pUser;
+ if (!pcb) return FALSE;
+
+ switch(dispId)
+ {
+ case DISPID_DESTRUCTOR:
+ pcb->bDestroyed = TRUE;
+ PostThreadMessage(GetCurrentThreadId(), WM_NULL, 0, 0L);
+ break;
+ case DISPID_NAVIGATECOMPLETE2:
+ if (pcb->bReady)
+ {
+
+ }
+ else
+ {
+ DWORD_PTR dwRedrawOFF;
+ HWND hwndParent;
+ hwndParent = pContainer->GetParentHWND();
+ if (hwndParent) SendMessageTimeout(hwndParent, WM_SETREDRAW, FALSE, 0L, SMTO_NORMAL, 100, &dwRedrawOFF);
+ pContainer->SetLocation(-2000, 0, 100, 100);
+ if (hwndParent) SendMessageTimeout(hwndParent, WM_SETREDRAW, TRUE, 0L, SMTO_NORMAL, 100, &dwRedrawOFF);
+ }
+ break;
+ case DISPID_DOCUMENTCOMPLETE:
+ {
+ HRESULT hr;
+ IUnknown *pUnk, *pUnkDisp;
+ BOOL bDocReady;
+ bDocReady = FALSE;
+ hr = pContainer->GetIUnknown(&pUnk);
+ if (SUCCEEDED(hr))
+ {
+ hr = pDispParams->rgvarg[1].pdispVal->QueryInterface(IID_IUnknown, (void**)&pUnkDisp);
+ if (SUCCEEDED(hr)) pUnkDisp->Release();
+ if (pUnk == pUnkDisp) bDocReady = TRUE;
+ if (!pcb->bReady && pDispParams->rgvarg[0].pvarVal->bstrVal &&
+ 0 == lstrcmpW(L"about:blank", pDispParams->rgvarg[0].pvarVal->bstrVal))
+ {
+ pcb->bReady = TRUE;
+ POSTNOTIFY(pcb->hwndNotify, pcb->uMsgNotify, MTBC_READY, (LPARAM)pContainer);
+ break;
+ }
+ }
+ if (pcb->bReady) POSTNOTIFY(pcb->hwndNotify, pcb->uMsgNotify, MTBC_DOCUMENTCOMPLETE, bDocReady);
+ }
+
+ }
+ return pcb->bQuiting;
+}
+
+static DWORD CALLBACK BrowserThread(LPVOID param)
+{
+ HRESULT hr;
+ HTMLContainer2 *pInstance;
+ DWORD dwInterval;
+ UINT uState;
+ MSG msg;
+ CALLBACKPARAM cbParam;
+
+ uState = 0;
+ TlsSetValue(threadStorage, 0);
+
+ pInstance = ((THREAD_START_PARAM*)param)->pmtb->pContainer;
+
+ ZeroMemory(&cbParam, sizeof(CALLBACKPARAM));
+
+ cbParam.uMsgNotify = ((THREAD_START_PARAM*)param)->pmtb->uMsgNotify;
+ cbParam.hwndNotify = pInstance->GetParentHWND();
+
+ pInstance->RegisterBrowserEventCB(OnBrowserEvent, (LPVOID)&cbParam);
+
+ ((THREAD_START_PARAM*)param)->pmtb->hwndNotify = cbParam.hwndNotify;
+
+ SetEvent(((THREAD_START_PARAM*)param)->hEvent);
+
+ hr = OleInitialize(NULL);//hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
+ if (SUCCEEDED(hr))
+ {
+ uState |= THREAD_COMINIT;
+ hr = pInstance->Initialize();
+ if(SUCCEEDED(hr)) TlsSetValue(threadStorage, pInstance);
+ else
+ {
+ pInstance->Finish();
+ pInstance->Release();
+ }
+ }
+
+ // force message queue
+ PeekMessageW(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
+
+ if (FAILED(hr) || !IsWindow(cbParam.hwndNotify))
+ {
+ TlsSetValue(threadStorage, 0);
+ pInstance->Finish();
+ pInstance->Release();
+ }
+ else
+ {
+ pInstance->NavigateToName(L"about:blank", 0);
+ }
+
+
+ dwInterval = INFINITE;
+ while (!cbParam.bDestroyed)
+ {
+ DWORD dwStatus = MsgWaitForMultipleObjectsEx(0, NULL, dwInterval, QS_ALLINPUT, MWMO_ALERTABLE | MWMO_INPUTAVAILABLE);
+ if (WAIT_OBJECT_0 == dwStatus)
+ {
+ while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE))
+ {
+ if (WM_QUIT == msg.message)
+ {
+ cbParam.bQuiting = TRUE;
+ TlsSetValue(threadStorage, 0);
+ if (QUIT_FORCE == msg.wParam)
+ {
+ if (!cbParam.bDestroyed)
+ {
+ pInstance->Finish();
+ while(pInstance->Release() > 0);
+ }
+ break;
+ }
+ else if (0 == (THREAD_QUITING & uState) && SUCCEEDED(MTBrowser_Quit(pInstance)))
+ {
+ uState |= THREAD_QUITING;
+ dwInterval = 200;
+ }
+ POSTNOTIFY(cbParam.hwndNotify, cbParam.uMsgNotify, MTBC_APC_QUIT, (LPARAM)(THREAD_QUITING & uState));
+ }
+ else if ((WM_KEYFIRST > msg.message || WM_KEYLAST < msg.message ||
+ (THREAD_QUITING & uState) || !pInstance->TranslateKey(&msg)) &&
+ !IsDialogMessageW(cbParam.hwndNotify, &msg))
+ {
+ TranslateMessage(&msg);
+ DispatchMessageW(&msg);
+ }
+ }
+ }
+ else if (WAIT_TIMEOUT == dwStatus) // quiting - check readystatus
+ {
+ if (!cbParam.bDestroyed)
+ {
+ READYSTATE state;
+ IWebBrowser2 *pWeb2;
+ hr = pInstance->GetIWebBrowser2(&pWeb2);
+
+ if (SUCCEEDED(hr))
+ {
+ hr = pWeb2->get_ReadyState(&state);
+ pWeb2->Release();
+ }
+ else state = READYSTATE_UNINITIALIZED;
+ if (FAILED(hr) || READYSTATE_UNINITIALIZED == state || READYSTATE_INTERACTIVE <= state)
+ {
+ pInstance->Finish();
+ pInstance->Release();
+ }
+ }
+ }
+ }
+
+ if (THREAD_COMINIT & uState) OleUninitialize();//CoUninitialize();
+ POSTNOTIFY(cbParam.hwndNotify, cbParam.uMsgNotify, MTBC_DESTROYED, (LPARAM)pInstance);
+
+ return 0;
+}
+
+static HRESULT MTBrowser_Quit(HTMLContainer2 *pContainer)
+{
+ HRESULT hr;
+ IWebBrowser2 *pWeb2;
+
+ hr = pContainer->GetIWebBrowser2(&pWeb2);
+ if (SUCCEEDED(hr))
+ {
+ hr = pWeb2->Stop();
+ pWeb2->Release();
+ if (SUCCEEDED(hr)) hr = pContainer->NavigateToName(L"about:blank", navNoHistory | navNoReadFromCache | navNoWriteToCache);
+ }
+ return hr;
+}
+
+static APCPARAM* AllocAPCParam(MTBROWSER *pmtb, INT cArgs, INT nNotifyCode, APCPROC fnAPC)
+{
+ if (!pmtb || !pmtb->hMemMngr || !fnAPC) return NULL;
+
+ APCPARAM *pParam = (APCPARAM*)calloc(1, sizeof(APCPARAM));
+ if (!pParam) return NULL;
+
+ if (cArgs)
+ {
+ INT i;
+ pParam->pArgs = (VARIANTARG*)calloc(cArgs, sizeof(VARIANTARG));
+ if (!pParam->pArgs)
+ {
+ free(pParam);
+ return NULL;
+ }
+ for (i = 0; i < cArgs; i++) VariantInit(&pParam->pArgs[i]);
+ }
+ else pParam->pArgs = NULL;
+
+ pParam->cArgs = cArgs;
+ pParam->pmm = (MEMMNGR*)pmtb->hMemMngr;
+ pParam->hwndNotify = pmtb->hwndNotify;
+ pParam->uMsgNotify = pmtb->uMsgNotify;
+ pParam->nNotifyCode = nNotifyCode;
+ pParam->fnAPC = fnAPC;
+ pParam->hThread = pmtb->hThread;
+
+ if (!MemRec_Add(pParam->pmm, pParam, FreeAPCParam))
+ {
+ FreeAPCParam(pParam);
+ pParam = NULL;
+ }
+
+ return pParam;
+}
+
+static void CALLBACK FreeAPCParam(void *pMem)
+{
+ if (pMem)
+ {
+ APCPARAM* pParam;
+ pParam = (APCPARAM*)pMem;
+ if (pParam->pArgs)
+ {
+ for(INT i = 0; i < pParam->cArgs; i++) VariantClear(&pParam->pArgs[i]);
+ free(pParam->pArgs);
+ }
+ free(pMem);
+ }
+}
+
+static void CALLBACK APC_FunctionCall(ULONG_PTR param)
+{
+ LPARAM result;
+ APCPARAM *pParam;
+ pParam = (APCPARAM*)param;
+ if (!pParam) return;
+
+ HTMLContainer2 *pContainer;
+ pContainer = GetThreadBrowserInstance();
+ if (pContainer)
+ {
+ result = 0L;
+ if (pParam->fnAPC) pParam->fnAPC(pContainer, pParam->pArgs, pParam->cArgs, &result);
+ if (pParam->nNotifyCode && pParam->hwndNotify && IsWindow(pParam->hwndNotify))
+ {
+ PostMessageW(pParam->hwndNotify, pParam->uMsgNotify, pParam->nNotifyCode, result);
+ }
+ }
+ MemRec_Free(pParam->pmm, pParam);
+
+}
+
+static void CALLBACK APC_NavigateToName(HTMLContainer2 *pContainer, VARIANTARG *pArgs, INT cArgs, LPARAM *pResult)
+{
+ *pResult = (2 == cArgs) ? pContainer->NavigateToName(pArgs[0].bstrVal, pArgs[1].intVal) : E_INVALIDARG;
+}
+
+static void CALLBACK APC_SetLocation(HTMLContainer2 *pContainer, VARIANTARG *pArgs, INT cArgs, LPARAM *pResult)
+{
+ *pResult = (4 == cArgs) ? pContainer->SetLocation(pArgs[0].intVal, pArgs[1].intVal, pArgs[2].intVal, pArgs[3].intVal) : E_INVALIDARG;
+}
+
+
+static void CALLBACK APC_Refresh2(HTMLContainer2 *pContainer, VARIANTARG *pArgs, INT cArgs, LPARAM *pResult)
+{
+ if (1 != cArgs) *pResult = E_INVALIDARG;
+ else
+ {
+ HRESULT hr;
+ IWebBrowser2 *pWeb2;
+ hr = pContainer->GetIWebBrowser2(&pWeb2);
+ if (SUCCEEDED(hr))
+ {
+ hr = pWeb2->Refresh2(&pArgs[0]);
+ pWeb2->Release();
+ }
+ *pResult = (LPARAM)hr;
+ }
+
+} \ No newline at end of file
diff --git a/Src/nu/mtbrowser.h b/Src/nu/mtbrowser.h
new file mode 100644
index 00000000..de493bfd
--- /dev/null
+++ b/Src/nu/mtbrowser.h
@@ -0,0 +1,63 @@
+#ifndef NULLSOFT_MULTITHREADED_BROWSER_HEADER
+#define NULLSOFT_MULTITHREADED_BROWSER_HEADER
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+#pragma once
+#endif
+
+#include "../nu/HTMLContainer2.h"
+
+typedef struct _MTBROWSER
+{
+ HTMLContainer2 *pContainer; // HTMLContainer2 object
+ HANDLE hThread; // Browser object thread
+ DWORD dwThreadId; // Browser object thread Id
+ BOOL bQuiting; // Browser quiting (do not schedule any othe APC)
+ HANDLE hMemMngr; // handle to the browser memory meanger.
+ HWND hwndNotify; // set from pContainer->GetParentHWND();
+ UINT uMsgNotify; // if 0 != uMsgNotify you will be notified that browser finished operation with wParam set to cmd code, and lParam result.
+} MTBROWSER;
+
+typedef void* HAPC;
+typedef void (CALLBACK *FREEPROC)(void* pMemFree);
+typedef void (CALLBACK *APCPROC)(HTMLContainer2 *pContainer, VARIANTARG *pArgs, INT cArgs, LPARAM *pResult);
+
+// messages
+#define MTBC_FIRST (0x0000)
+#define MTBC_LAST (0x00FF)
+
+#define MTBC_READY (MTBC_FIRST) // Posted when browser thread and browser object initialization completed //lParam = (HTMLContainer2*)
+#define MTBC_DESTROYED (MTBC_LAST) // Posted when browser object destroyed itself and browser thread about to exit. //lParam = (HTMLContainer2*)- destroyed!!!
+
+#define MTBC_APC_QUIT (MTBC_FIRST + 20) // Posted when browser executed quit APC
+#define MTBC_APC_NAVIGATE (MTBC_FIRST + 21) // Posted when browser executed navigate APC. lParam = (HRESULT)
+#define MTBC_APC_SETLOCATION (MTBC_FIRST + 22) // Posted when browser executed SetLocation APC. lParam = (HRESULT)
+#define MTBC_APC_REFRESH2 (MTBC_FIRST + 23) // Posted when browser executed Refresh2 APC. lParam = (HRESULT)
+
+#define MTBC_DOCUMENTCOMPLETE (MTBC_FIRST + 41) // lParam = (BOOL)bDocumentReady
+
+
+
+BOOL MTBrowser_Init(MTBROWSER *pmtb);
+BOOL MTBrowser_Clear(MTBROWSER *pmtb);
+
+BOOL MTBrowser_Start(MTBROWSER *pmtb, HTMLContainer2 *pContainer, UINT uMsgNotify);
+BOOL MTBrowser_Kill(MTBROWSER *pmtb, UINT nTerminateDelay); // use in WM_DESTROY
+
+// Async calls
+HAPC MTBrowser_InitializeAPC(MTBROWSER *pmtb, INT nCount, UINT nCmdCode, APCPROC fnAPC, VARIANTARG **pArgs);
+BOOL MTBrowser_CallAPC(HAPC hAPC);
+
+BOOL MTBrowser_QuitAPC(MTBROWSER *pmtb);
+BOOL MTBrowser_NavigateToNameAPC(MTBROWSER *pmtb, LPCWSTR pszURL, UINT fFlags);
+BOOL MTBrowser_SetLocationAPC(MTBROWSER *pmtb, RECT *pRect);
+BOOL MTBrowser_SetVisibleAPC(MTBROWSER *pmtb, BOOL bVisible);
+
+BOOL MTBrowser_Refresh2APC(MTBROWSER *pmtb, INT nRefreshMode);
+
+
+/// Thread Memory manager
+BOOL MTBrowser_AddMemRec(MTBROWSER *pmtb, void *pMem, FREEPROC fnFreeProc); // if fnFreeProc == NULL - standart free() will be used
+BOOL MTBrowser_FreeMemRec(MTBROWSER *pmtb, void *pMem);
+
+#endif //NULLSOFT_MULTITHREADED_BROWSER_HEADER \ No newline at end of file
diff --git a/Src/nu/noarg.c b/Src/nu/noarg.c
new file mode 100644
index 00000000..61d6f8ae
--- /dev/null
+++ b/Src/nu/noarg.c
@@ -0,0 +1,28 @@
+/***
+*noarg.c - stub out CRT's processing of command line arguments
+*
+* Copyright (c) Microsoft Corporation. All rights reserved.
+*
+*Purpose:
+* Stub out the processing of the command line into argv[], normally
+* carried out at during startup. Note, the argc and argv arguments to
+* main are not meaningful if this object is used. Nor are __argc and
+* __argv.
+*
+*******************************************************************************/
+
+#include <tchar.h>
+#ifdef __cplusplus
+extern "C" {
+#endif
+int __cdecl _setargv() { return 0; }
+
+int __cdecl _wsetargv() { return 0; }
+
+_TUCHAR * __cdecl _wincmdln() { return NULL; }
+
+_TUCHAR * __cdecl _wwincmdln() { return NULL; }
+
+#ifdef __cplusplus
+}
+#endif \ No newline at end of file
diff --git a/Src/nu/nochkclr.c b/Src/nu/nochkclr.c
new file mode 100644
index 00000000..951e8fdf
--- /dev/null
+++ b/Src/nu/nochkclr.c
@@ -0,0 +1,26 @@
+/***
+* nochkclr.c - Dummy non-version-checking CLR call
+*
+* Copyright (c) Microsoft Corporation. All rights reserved.
+*
+*Purpose:
+*
+*******************************************************************************/
+
+
+/***
+*_check_commonlanguageruntime_version
+*
+*Purpose:
+* If you don't link to the CRT, you use this obj to fill the compiler's need for this symbol
+
+*******************************************************************************/
+#ifdef __cplusplus
+extern "C" {
+#endif
+void __cdecl _check_commonlanguageruntime_version()
+{
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/Src/nu/noenv.c b/Src/nu/noenv.c
new file mode 100644
index 00000000..06bf3bd7
--- /dev/null
+++ b/Src/nu/noenv.c
@@ -0,0 +1,27 @@
+/***
+*noenv.c - stub out CRT's environment string processing
+*
+* Copyright (c) Microsoft Corporation. All rights reserved.
+*
+*Purpose:
+* Stub out the environment string processing normally carried out at
+* during startup. Note, getenv, _putenv and _environ are not supported
+* if this object is used. Nor is the third argument to main.
+*
+*******************************************************************************/
+
+#include <stdlib.h>
+#ifdef __cplusplus
+extern "C" {
+#endif
+int __cdecl _setenvp(void) { return 0; }
+
+void * __cdecl __crtGetEnvironmentStringsA(void) { return NULL; }
+
+int __cdecl _wsetenvp(void) { return 0; }
+
+void * __cdecl __crtGetEnvironmentStringsW(void) { return NULL; }
+
+#ifdef __cplusplus
+}
+#endif \ No newline at end of file
diff --git a/Src/nu/nomsgs.c b/Src/nu/nomsgs.c
new file mode 100644
index 00000000..0d42b0da
--- /dev/null
+++ b/Src/nu/nomsgs.c
@@ -0,0 +1,17 @@
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ void __cdecl _FF_MSGBANNER (
+ void
+ )
+ {}
+
+
+void __cdecl _NMSG_WRITE (
+ int rterrnum
+ )
+{}
+#ifdef __cplusplus
+}
+#endif \ No newline at end of file
diff --git a/Src/nu/nonewthrow.c b/Src/nu/nonewthrow.c
new file mode 100644
index 00000000..9b4523ab
--- /dev/null
+++ b/Src/nu/nonewthrow.c
@@ -0,0 +1,18 @@
+void *operator new(size_t size)
+{
+ return malloc(size);
+}
+
+void *operator new[](size_t size)
+{
+ return malloc(size);
+}
+
+void operator delete(void *ptr)
+{
+ free(ptr);
+}
+void operator delete[](void *ptr)
+{
+ free(ptr);
+} \ No newline at end of file
diff --git a/Src/nu/nosec.c b/Src/nu/nosec.c
new file mode 100644
index 00000000..b6f9fc10
--- /dev/null
+++ b/Src/nu/nosec.c
@@ -0,0 +1,12 @@
+#ifdef __cplusplus
+extern "C" {
+#endif
+ void __cdecl __security_error_handler(
+ int code,
+ void *data)
+ {
+ _exit(3);
+ }
+#ifdef __cplusplus
+}
+#endif \ No newline at end of file
diff --git a/Src/nu/ns_wc.h b/Src/nu/ns_wc.h
new file mode 100644
index 00000000..5d3fecfd
--- /dev/null
+++ b/Src/nu/ns_wc.h
@@ -0,0 +1,67 @@
+#pragma once
+#include <windows.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+__inline int MultiByteToWideCharSZ(
+ UINT CodePage, // code page
+ DWORD dwFlags, // character-type options
+ LPCSTR lpMultiByteStr, // string to map
+ int cbMultiByte, // number of bytes in string
+ LPWSTR lpWideCharStr, // wide-character buffer
+ int cchWideChar // size of buffer
+)
+{
+ int converted=0;
+ if (cchWideChar == 0)
+ return MultiByteToWideChar(CodePage, dwFlags, lpMultiByteStr, cbMultiByte, lpWideCharStr, cchWideChar);
+ converted = MultiByteToWideChar(CodePage, dwFlags, lpMultiByteStr, cbMultiByte, lpWideCharStr, cchWideChar-1);
+ if (!converted)
+ {
+ if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
+ {
+ lpWideCharStr[cchWideChar-1]=0;
+ return cchWideChar;
+ }
+ else
+ return 0;
+ }
+ lpWideCharStr[converted]=0;
+ return converted+1;
+}
+
+__inline int WideCharToMultiByteSZ(
+ UINT CodePage, // code page
+ DWORD dwFlags, // performance and mapping flags
+ LPCWSTR lpWideCharStr, // wide-character string
+ int cchWideChar, // number of chars in string
+ LPSTR lpMultiByteStr, // buffer for new string
+ int cbMultiByte, // size of buffer
+ LPCSTR lpDefaultChar, // default for unmappable chars
+ LPBOOL lpUsedDefaultChar // set when default char used
+)
+{
+ int converted=0;
+ if (cbMultiByte == 0)
+ return WideCharToMultiByte(CodePage, dwFlags, lpWideCharStr, cchWideChar, lpMultiByteStr, cbMultiByte, lpDefaultChar, lpUsedDefaultChar);
+ converted= WideCharToMultiByte(CodePage, dwFlags, lpWideCharStr, cchWideChar, lpMultiByteStr, cbMultiByte-1, lpDefaultChar, lpUsedDefaultChar);
+ if (!converted)
+ {
+ if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
+ {
+ lpMultiByteStr[cbMultiByte-1]=0;
+ return cbMultiByte;
+ }
+ else
+ return 0;
+ }
+ lpMultiByteStr[converted]=0;
+ return converted+1;
+}
+
+#ifdef __cplusplus
+}
+#endif \ No newline at end of file
diff --git a/Src/nu/refcount.h b/Src/nu/refcount.h
new file mode 100644
index 00000000..9ab52f4e
--- /dev/null
+++ b/Src/nu/refcount.h
@@ -0,0 +1,21 @@
+#pragma once
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+template <class ifc_t>
+class Countable : public ifc_t
+{
+public:
+ Countable()
+ {
+ ref_count=1;
+ }
+
+ // this needs to be done like this otherwise the destructor doesn't get called properly (we don't want virtual destructor for various reasons)
+#define REFERENCE_COUNT_IMPLEMENTATION size_t AddRef() { return InterlockedIncrement((LONG*)&ref_count); }\
+ size_t Release() { if (!ref_count) return ref_count; LONG r = InterlockedDecrement((LONG*)&ref_count); if (!r) delete(this); return r; }
+protected:
+ size_t ref_count;
+};
+
+#define REFERENCE_COUNTED CB(ADDREF, AddRef); CB(RELEASE, Release);
+
diff --git a/Src/nu/regexp.cpp b/Src/nu/regexp.cpp
new file mode 100644
index 00000000..6908a49a
--- /dev/null
+++ b/Src/nu/regexp.cpp
@@ -0,0 +1,233 @@
+#include "regexp.h"
+// TODO: make a little more multi-byte safe
+
+
+
+// regexp match functions
+
+// A match means the entire string TEXT is used up in matching.
+// In the pattern string:
+// `*' matches any sequence of characters (zero or more)
+// `?' matches any character
+// [SET] matches any character in the specified set,
+// [!SET] or [^SET] matches any character not in the specified set.
+
+// A set is composed of characters or ranges; a range looks like
+// character hyphen character (as in 0-9 or A-Z). [0-9a-zA-Z_] is the
+// minimal set of characters allowed in the [..] pattern construct.
+// Other characters are allowed (ie. 8 bit characters) if your system
+// will support them.
+
+// To suppress the special syntactic significance of any of `[]*?!^-\',
+// and match the character exactly, precede it with a `\'.
+
+enum {
+ MATCH_VALID = 1, /* valid match */
+ MATCH_END, /* premature end of pattern string */
+ MATCH_ABORT, /* premature end of text string */
+ MATCH_RANGE, /* match failure on [..] construct */
+ MATCH_LITERAL, /* match failure on literal match */
+ MATCH_PATTERN, /* bad pattern */
+};
+
+enum {
+ PATTERN_VALID = 0, /* valid pattern */
+ PATTERN_ESC = -1, /* literal escape at end of pattern */
+ PATTERN_RANGE = -2, /* malformed range in [..] construct */
+ PATTERN_CLOSE = -3, /* no end bracket in [..] construct */
+ PATTERN_EMPTY = -4, /* [..] contstruct is empty */
+};
+
+int Matche(const regchar_t *p, const regchar_t *t);
+
+// TODO: make this multi-byte aware
+int matche_after_star(const regchar_t *p, const regchar_t *t)
+{
+ register int match = 0;
+ register regchar_t nextp;
+ /* pass over existing ? and * in pattern */
+ while ( *p == '?' || *p == '*' )
+ {
+ /* take one char for each ? and + */
+ if (*p == '?')
+ {
+ /* if end of text then no match */
+ if (!*t++) return MATCH_ABORT;
+ }
+ /* move to next char in pattern */
+ p++;
+ }
+ /* if end of pattern we have matched regardless of text left */
+ if (!*p) return MATCH_VALID;
+ /* get the next character to match which must be a literal or '[' */
+ nextp = *p;
+ if (nextp == '\\')
+ {
+ nextp = p[1];
+ /* if end of text then we have a bad pattern */
+ if (!nextp) return MATCH_PATTERN;
+ }
+ /* Continue until we run out of text or definite result seen */
+ do
+ {
+ /* a precondition for matching is that the next character
+ in the pattern match the next character in the text or that
+ the next pattern char is the beginning of a range. Increment
+ text pointer as we go here */
+ if (nextp == *t || nextp == '[') match = Matche(p, t);
+ /* if the end of text is reached then no match */
+ if (!*t++) match = MATCH_ABORT;
+ }
+ while ( match != MATCH_VALID && match != MATCH_ABORT && match != MATCH_PATTERN);
+ /* return result */
+ return match;
+}
+
+
+int Matche(const regchar_t *p, const regchar_t *t)
+{
+ regchar_t range_start, range_end; /* start and end in range */
+
+ bool invert; /* is this [..] or [!..] */
+ bool member_match; /* have I matched the [..] construct? */
+ bool loop; /* should I terminate? */
+
+ for ( ; *p; p++, t++)
+ {
+ /* if this is the end of the text then this is the end of the match */
+ if (!*t)
+ {
+ return (*p == '*' && *++p == '\0') ? MATCH_VALID : MATCH_ABORT;
+ }
+ /* determine and react to pattern type */
+ switch (*p)
+ {
+ case '?': /* single any character match */
+ break;
+ case '*': /* multiple any character match */
+ return matche_after_star (p, t);
+
+ /* [..] construct, single member/exclusion character match */
+ case '[':
+ {
+ /* move to beginning of range */
+ p++;
+ /* check if this is a member match or exclusion match */
+ invert = false;
+ if (*p == '!' || *p == '^')
+ {
+ invert = true;
+ p++;
+ }
+ /* if closing bracket here or at range start then we have a malformed pattern */
+ if (*p == ']')
+ return MATCH_PATTERN;
+
+ member_match = false;
+ loop = true;
+ while (loop)
+ {
+ /* if end of construct then loop is done */
+ if (*p == ']')
+ {
+ loop = false;
+ continue;
+ }
+ /* matching a '!', '^', '-', '\' or a ']' */
+ if (*p == '\\')
+ range_start = range_end = *++p;
+ else
+ range_start = range_end = *p;
+ /* if end of pattern then bad pattern (Missing ']') */
+ if (!*p)
+ return MATCH_PATTERN;
+ /* check for range bar */
+ if (*++p == '-')
+ {
+ /* get the range end */
+ range_end = *++p;
+ /* if end of pattern or construct then bad pattern */
+ if (range_end == '\0' || range_end == ']') return MATCH_PATTERN;
+ /* special character range end */
+ if (range_end == '\\')
+ {
+ range_end = *++p;
+ /* if end of text then we have a bad pattern */
+ if (!range_end) return MATCH_PATTERN;
+ }
+ /* move just beyond this range */
+ p++;
+ }
+ /* if the text character is in range then match found.
+ make sure the range letters have the proper
+ relationship to one another before comparison */
+ if (range_start < range_end)
+ {
+ if (*t >= range_start && *t <= range_end)
+ {
+ member_match = true;
+ loop = false;
+ }
+ }
+ else
+ {
+ if (*t >= range_end && *t <= range_start)
+ {
+ member_match = true;
+ loop = false;
+ }
+ }
+ }
+ /* if there was a match in an exclusion set then no match */
+ /* if there was no match in a member set then no match */
+ if ((invert && member_match) || !(invert || member_match))
+ return MATCH_RANGE;
+ /* if this is not an exclusion then skip the rest of the [...] construct that already matched. */
+ if (member_match)
+ {
+ while (p && *p != ']')
+ {
+ /* bad pattern (Missing ']') */
+ if (!*p)
+ return MATCH_PATTERN;
+ /* skip exact match */
+ if (*p == '\\')
+ {
+ p++;
+ /* if end of text then we have a bad pattern */
+ if (!*p)
+ return MATCH_PATTERN;
+ }
+ /* move to next pattern char */
+ p++;
+ }
+ }
+ break;
+ }
+ case '\\': /* next character is quoted and must match exactly */
+ /* move pattern pointer to quoted char and fall through */
+ p++;
+ /* if end of text then we have a bad pattern */
+ if (!*p)
+ return MATCH_PATTERN;
+ /* must match this character exactly */
+ default:
+ if (*p != *t)
+ return MATCH_LITERAL;
+ }
+ }
+ /* if end of text not reached then the pattern fails */
+ if (*t)
+ return MATCH_END;
+ else return MATCH_VALID;
+}
+
+bool Match(const regchar_t *match, const regchar_t *string)
+{
+ if (!match)
+ return true;
+ int error_type;
+
+ error_type = Matche(match, string);
+ return (error_type == MATCH_VALID);
+}
diff --git a/Src/nu/regexp.h b/Src/nu/regexp.h
new file mode 100644
index 00000000..a2c35af6
--- /dev/null
+++ b/Src/nu/regexp.h
@@ -0,0 +1,14 @@
+#ifndef NULLSOFT_UTILITY_REGEXP_H
+#define NULLSOFT_UTILITY_REGEXP_H
+#pragma once
+
+#ifdef _WIN32
+#include <wchar.h>
+typedef wchar_t regchar_t;
+#else
+typedef char regchar_t;
+#endif
+// strings must be in ALL CAPS. sorry.
+bool Match(const regchar_t *match, const regchar_t *string);
+
+#endif \ No newline at end of file
diff --git a/Src/nu/simple_rwlock.h b/Src/nu/simple_rwlock.h
new file mode 100644
index 00000000..f9952959
--- /dev/null
+++ b/Src/nu/simple_rwlock.h
@@ -0,0 +1,49 @@
+#pragma once
+
+/*
+Simple Reader/Writer lock. Lets unlimited readers through but a writer will lock exclusively
+not meant for high-throughput uses
+this is useful when writes are very infrequent
+*/
+#include <bfc/platform/types.h>
+#include <windows.h>
+
+typedef size_t simple_rwlock_t;
+static const size_t simple_rwlock_writer_active = 1; // writer active flag
+static const size_t simple_rwlock_reader_increment= 2; // to adjust reader count
+
+static inline void simple_rwlock_write_lock(simple_rwlock_t *lock)
+{
+ while (InterlockedCompareExchangePointer((PVOID volatile*)lock, (PVOID)simple_rwlock_writer_active, 0))
+ {
+ // nop
+ }
+}
+
+static inline void simple_rwlock_write_unlock(simple_rwlock_t *lock)
+{
+#ifdef _WIN64
+ InterlockedExchangeAdd64((LONGLONG volatile*)lock, -simple_rwlock_writer_active);
+#else
+ InterlockedExchangeAdd((LONG volatile*)lock, -simple_rwlock_writer_active);
+#endif
+}
+
+
+static inline void simple_rwlock_read_lock(simple_rwlock_t *lock)
+{
+ InterlockedExchangeAdd((LONG volatile*)lock, simple_rwlock_reader_increment);
+ while ((*lock & simple_rwlock_writer_active))
+ {
+ // nope
+ }
+}
+
+static inline void simple_rwlock_read_unlock(simple_rwlock_t *lock)
+{
+ #ifdef _WIN64
+ InterlockedExchangeAdd64((LONGLONG volatile*)lock, -simple_rwlock_reader_increment);
+#else
+ InterlockedExchangeAdd((LONG volatile*)lock, -simple_rwlock_reader_increment);
+#endif
+}
diff --git a/Src/nu/smalheap.c b/Src/nu/smalheap.c
new file mode 100644
index 00000000..de8a1bed
--- /dev/null
+++ b/Src/nu/smalheap.c
@@ -0,0 +1,182 @@
+/***
+*smalheap.c - small, simple heap manager
+*
+* Copyright (c) Microsoft Corporation. All rights reserved.
+*
+*Purpose:
+*
+*
+*******************************************************************************/
+#include <stdlib.h>
+#include <malloc.h>
+#include <windows.h>
+
+#define BYTES_PER_PARA 16
+#define DWORDS_PER_PARA 4
+
+#define PARAS_PER_PAGE 256 // tunable value
+#define PAGES_PER_GROUP 8 // tunable value
+#define GROUPS_PER_REGION 32 // tunable value (max 32)
+
+#define BYTES_PER_PAGE (BYTES_PER_PARA * PARAS_PER_PAGE)
+#define BYTES_PER_GROUP (BYTES_PER_PAGE * PAGES_PER_GROUP)
+#define BYTES_PER_REGION (BYTES_PER_GROUP * GROUPS_PER_REGION)
+#ifdef __cplusplus
+extern "C" {
+#endif
+HANDLE _crtheap;
+
+/*
+ * Primary heap routines (Initialization, termination, malloc and free).
+ */
+
+void __cdecl free (
+ void * pblock
+ )
+{
+ if ( pblock == NULL )
+ return;
+
+ HeapFree(_crtheap, 0, pblock);
+}
+
+
+int __cdecl _heap_init (
+ int mtflag
+ )
+{
+ if ( (_crtheap = HeapCreate( mtflag ? 0 : HEAP_NO_SERIALIZE,
+ BYTES_PER_PAGE, 0 )) == NULL )
+ return 0;
+
+ return 1;
+}
+
+
+void __cdecl _heap_term (
+ void
+ )
+{
+ HeapDestroy( _crtheap );
+}
+
+
+void * __cdecl _nh_malloc (
+ size_t size,
+ int nhFlag
+ )
+{
+ void * retp;
+
+ retp = HeapAlloc( _crtheap, 0, size );
+
+ /*
+ * if successful allocation, return pointer to memory
+ * if new handling turned off altogether, return NULL
+ */
+
+ return retp;
+
+
+}
+
+
+void * __cdecl malloc (
+ size_t size
+ )
+{
+ return _nh_malloc( size, 0 );
+}
+
+/*
+ * Secondary heap routines.
+ */
+
+void * __cdecl calloc (
+ size_t num,
+ size_t size
+ )
+{
+ void * retp;
+
+ size *= num;
+
+
+ retp = HeapAlloc( _crtheap, HEAP_ZERO_MEMORY, size );
+
+ return retp;
+
+ /* new handler was successful -- try to allocate again */
+
+}
+
+
+void * __cdecl _expand (
+ void * pblock,
+ size_t newsize
+ )
+{
+ return HeapReAlloc( _crtheap,
+ HEAP_REALLOC_IN_PLACE_ONLY,
+ pblock,
+ newsize );
+}
+
+
+int __cdecl _heapchk(void)
+{
+ int retcode = _HEAPOK;
+
+ if ( !HeapValidate( _crtheap, 0, NULL ) &&
+ (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED) )
+ retcode = _HEAPBADNODE;
+
+ return retcode;
+}
+
+
+int __cdecl _heapmin(void)
+{
+ if ( (HeapCompact( _crtheap, 0 ) == 0) &&
+ (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED) )
+ return -1;
+
+ return 0;
+}
+
+
+size_t __cdecl _msize (
+ void * pblock
+ )
+{
+ return (size_t)HeapSize( _crtheap, 0, pblock );
+}
+
+
+void * __cdecl realloc (
+ void * pblock,
+ size_t newsize
+ )
+{
+ void * retp;
+
+ /* if pblock is NULL, call malloc */
+ if ( pblock == (void *) NULL )
+ return malloc( newsize );
+
+ /* if pblock is !NULL and size is 0, call free and return NULL */
+ if ( newsize == 0 ) {
+ free( pblock );
+ return NULL;
+ }
+
+
+ retp = HeapReAlloc( _crtheap, 0, pblock, newsize );
+
+ return retp;
+
+}
+
+#ifdef __cplusplus
+}
+#endif \ No newline at end of file
diff --git a/Src/nu/sort.cpp b/Src/nu/sort.cpp
new file mode 100644
index 00000000..dea3dada
--- /dev/null
+++ b/Src/nu/sort.cpp
@@ -0,0 +1,361 @@
+#include <bfc/platform/types.h>
+#include "sort.h"
+#include <assert.h>
+/***
+*qsort.c - quicksort algorithm; qsort() library function for sorting arrays
+*
+* Copyright (c) Microsoft Corporation. All rights reserved.
+*
+*Purpose:
+* To implement the qsort() routine for sorting arrays.
+*
+*******************************************************************************/
+
+/* Always compile this module for speed, not size */
+#pragma optimize("t", on)
+
+/* prototypes for local routines */
+static void shortsort(uint8_t *lo, uint8_t *hi, size_t width, const void *context,
+ int (__fastcall *comp)(const void *, const void *, const void *));
+static void swap(uint8_t *p, uint8_t *q, size_t width);
+
+/* this parameter defines the cutoff between using quick sort and
+ insertion sort for arrays; arrays with lengths shorter or equal to the
+ below value use insertion sort */
+
+#define CUTOFF 8 /* testing shows that this is good value */
+
+/***
+*qsort(base, num, wid, context, comp) - quicksort function for sorting arrays
+*
+*Purpose:
+* quicksort the array of elements
+* side effects: sorts in place
+* maximum array size is number of elements times size of elements,
+* but is limited by the virtual address space of the processor
+*
+*Entry:
+* char *base = pointer to base of array
+* size_t num = number of elements in the array
+* size_t width = width in bytes of each array element
+* int (*comp)() = pointer to function returning analog of strcmp for
+* strings, but supplied by user for comparing the array elements.
+* it accepts 2 pointers to elements and returns neg if 1<2, 0 if
+* 1=2, pos if 1>2.
+*
+*Exit:
+* returns void
+*
+*Exceptions:
+*
+*******************************************************************************/
+
+/* sort the array between lo and hi (inclusive) */
+
+#define STKSIZ (8*sizeof(void*) - 2)
+
+void __cdecl nu::qsort (
+ void *base,
+ size_t num,
+ size_t width,
+ const void *context,
+ int (__fastcall *comp)(const void *, const void *, const void *)
+ )
+{
+ /* Note: the number of stack entries required is no more than
+ 1 + log2(num), so 30 is sufficient for any array */
+ uint8_t *lo, *hi; /* ends of sub-array currently sorting */
+ uint8_t *mid; /* points to middle of subarray */
+ uint8_t *loguy, *higuy; /* traveling pointers for partition step */
+ size_t size; /* size of the sub-array */
+ uint8_t *lostk[STKSIZ] = {0}, *histk[STKSIZ] = {0};
+ int stkptr; /* stack for saving sub-array to be processed */
+
+ assert((width % sizeof(void *)) == 0);
+ if (num < 2 || width == 0)
+ return; /* nothing to do */
+
+ stkptr = 0; /* initialize stack */
+
+ lo = static_cast<uint8_t *>(base);
+ hi = (uint8_t *)base + width * (num-1); /* initialize limits */
+
+ /* this entry point is for pseudo-recursion calling: setting
+ lo and hi and jumping to here is like recursion, but stkptr is
+ preserved, locals aren't, so we preserve stuff on the stack */
+recurse:
+
+ size = (hi - lo) / width + 1; /* number of el's to sort */
+
+ /* below a certain size, it is faster to use a O(n^2) sorting method */
+ if (size <= CUTOFF) {
+ shortsort(lo, hi, width, context, comp);
+ }
+ else {
+ /* First we pick a partitioning element. The efficiency of the
+ algorithm demands that we find one that is approximately the median
+ of the values, but also that we select one fast. We choose the
+ median of the first, middle, and last elements, to avoid bad
+ performance in the face of already sorted data, or data that is made
+ up of multiple sorted runs appended together. Testing shows that a
+ median-of-three algorithm provides better performance than simply
+ picking the middle element for the latter case. */
+
+ mid = lo + (size / 2) * width; /* find middle element */
+
+ /* Sort the first, middle, last elements into order */
+ if (comp(lo, mid, context) > 0) {
+ swap(lo, mid, width);
+ }
+ if (comp(lo, hi, context) > 0) {
+ swap(lo, hi, width);
+ }
+ if (comp(mid, hi, context) > 0) {
+ swap(mid, hi, width);
+ }
+
+ /* We now wish to partition the array into three pieces, one consisting
+ of elements <= partition element, one of elements equal to the
+ partition element, and one of elements > than it. This is done
+ below; comments indicate conditions established at every step. */
+
+ loguy = lo;
+ higuy = hi;
+
+ /* Note that higuy decreases and loguy increases on every iteration,
+ so loop must terminate. */
+ for (;;) {
+ /* lo <= loguy < hi, lo < higuy <= hi,
+ A[i] <= A[mid] for lo <= i <= loguy,
+ A[i] > A[mid] for higuy <= i < hi,
+ A[hi] >= A[mid] */
+
+ /* The doubled loop is to avoid calling comp(mid,mid), since some
+ existing comparison funcs don't work when passed the same
+ value for both pointers. */
+
+ if (mid > loguy) {
+ do {
+ loguy += width;
+ } while (loguy < mid && comp(loguy, mid, context) <= 0);
+ }
+ if (mid <= loguy) {
+ do {
+ loguy += width;
+ } while (loguy <= hi && comp(loguy, mid, context) <= 0);
+ }
+
+ /* lo < loguy <= hi+1, A[i] <= A[mid] for lo <= i < loguy,
+ either loguy > hi or A[loguy] > A[mid] */
+
+ do {
+ higuy -= width;
+ } while (higuy > mid && comp(higuy, mid, context) > 0);
+
+ /* lo <= higuy < hi, A[i] > A[mid] for higuy < i < hi,
+ either higuy == lo or A[higuy] <= A[mid] */
+
+ if (higuy < loguy)
+ break;
+
+ /* if loguy > hi or higuy == lo, then we would have exited, so
+ A[loguy] > A[mid], A[higuy] <= A[mid],
+ loguy <= hi, higuy > lo */
+
+ swap(loguy, higuy, width);
+
+ /* If the partition element was moved, follow it. Only need
+ to check for mid == higuy, since before the swap,
+ A[loguy] > A[mid] implies loguy != mid. */
+
+ if (mid == higuy)
+ mid = loguy;
+
+ /* A[loguy] <= A[mid], A[higuy] > A[mid]; so condition at top
+ of loop is re-established */
+ }
+
+ /* A[i] <= A[mid] for lo <= i < loguy,
+ A[i] > A[mid] for higuy < i < hi,
+ A[hi] >= A[mid]
+ higuy < loguy
+ implying:
+ higuy == loguy-1
+ or higuy == hi - 1, loguy == hi + 1, A[hi] == A[mid] */
+
+ /* Find adjacent elements equal to the partition element. The
+ doubled loop is to avoid calling comp(mid,mid), since some
+ existing comparison funcs don't work when passed the same value
+ for both pointers. */
+
+ higuy += width;
+ if (mid < higuy) {
+ do {
+ higuy -= width;
+ } while (higuy > mid && comp(higuy, mid, context) == 0);
+ }
+ if (mid >= higuy) {
+ do {
+ higuy -= width;
+ } while (higuy > lo && comp(higuy, mid, context) == 0);
+ }
+
+ /* OK, now we have the following:
+ higuy < loguy
+ lo <= higuy <= hi
+ A[i] <= A[mid] for lo <= i <= higuy
+ A[i] == A[mid] for higuy < i < loguy
+ A[i] > A[mid] for loguy <= i < hi
+ A[hi] >= A[mid] */
+
+ /* We've finished the partition, now we want to sort the subarrays
+ [lo, higuy] and [loguy, hi].
+ We do the smaller one first to minimize stack usage.
+ We only sort arrays of length 2 or more.*/
+
+ if ( higuy - lo >= hi - loguy ) {
+ if (lo < higuy) {
+ lostk[stkptr] = lo;
+ histk[stkptr] = higuy;
+ ++stkptr;
+ } /* save big recursion for later */
+
+ if (loguy < hi) {
+ lo = loguy;
+ goto recurse; /* do small recursion */
+ }
+ }
+ else {
+ if (loguy < hi) {
+ lostk[stkptr] = loguy;
+ histk[stkptr] = hi;
+ ++stkptr; /* save big recursion for later */
+ }
+
+ if (lo < higuy) {
+ hi = higuy;
+ goto recurse; /* do small recursion */
+ }
+ }
+ }
+
+ /* We have sorted the array, except for any pending sorts on the stack.
+ Check if there are any, and do them. */
+
+ --stkptr;
+ if (stkptr >= 0) {
+ lo = lostk[stkptr];
+ hi = histk[stkptr];
+ goto recurse; /* pop subarray from stack */
+ }
+ else
+ return; /* all subarrays done */
+}
+
+
+/***
+*shortsort(hi, lo, width, comp) - insertion sort for sorting short arrays
+*
+*Purpose:
+* sorts the sub-array of elements between lo and hi (inclusive)
+* side effects: sorts in place
+* assumes that lo < hi
+*
+*Entry:
+* char *lo = pointer to low element to sort
+* char *hi = pointer to high element to sort
+* size_t width = width in bytes of each array element
+* int (*comp)() = pointer to function returning analog of strcmp for
+* strings, but supplied by user for comparing the array elements.
+* it accepts 2 pointers to elements and returns neg if 1<2, 0 if
+* 1=2, pos if 1>2.
+*
+*Exit:
+* returns void
+*
+*Exceptions:
+*
+*******************************************************************************/
+
+static void __cdecl shortsort (
+ uint8_t *lo,
+ uint8_t *hi,
+ size_t width,
+ const void *context,
+ int (__fastcall *comp)(const void *, const void *, const void *)
+ )
+{
+ uint8_t *p;
+
+ /* Note: in assertions below, i and j are alway inside original bound of
+ array to sort. */
+
+ while (hi > lo) {
+ /* A[i] <= A[j] for i <= j, j > hi */
+ uint8_t *max = lo;
+ for (p = lo+width; p <= hi; p += width) {
+ /* A[i] <= A[max] for lo <= i < p */
+ if (comp(p, max, context) > 0) {
+ max = p;
+ }
+ /* A[i] <= A[max] for lo <= i <= p */
+ }
+
+ /* A[i] <= A[max] for lo <= i <= hi */
+
+ swap(max, hi, width);
+
+ /* A[i] <= A[hi] for i <= hi, so A[i] <= A[j] for i <= j, j >= hi */
+
+ hi -= width;
+
+ /* A[i] <= A[j] for i <= j, j > hi, loop top condition established */
+ }
+ /* A[i] <= A[j] for i <= j, j > lo, which implies A[i] <= A[j] for i < j,
+ so array is sorted */
+}
+
+
+/***
+*swap(a, b, width) - swap two elements
+*
+*Purpose:
+* swaps the two array elements of size width
+*
+*Entry:
+* char *a, *b = pointer to two elements to swap
+* size_t width = width in bytes of each array element
+*
+*Exit:
+* returns void
+*
+*Exceptions:
+*
+*******************************************************************************/
+
+static void swap (
+ uint8_t *_a,
+ uint8_t *_b,
+ size_t width
+ )
+{
+#if 1
+ void *tmp;
+ void **a = (void **)_a;
+ void **b = (void **)_b;
+ if ( a != b )
+ /* Do the swap one character at a time to avoid potential alignment
+ problems. */
+ do {
+ tmp = *a;
+ *a++ = *b;
+ *b++ = tmp;
+ width-=sizeof(void *);
+ } while (width);
+#else
+ //void *temp = alloca(width);
+ memcpy(temp, a, width);
+ memcpy(a, b, width);
+ memcpy(b, temp, width);
+ #endif
+}
diff --git a/Src/nu/sort.h b/Src/nu/sort.h
new file mode 100644
index 00000000..94816e75
--- /dev/null
+++ b/Src/nu/sort.h
@@ -0,0 +1,11 @@
+#pragma once
+
+namespace nu
+{
+ void __cdecl qsort (
+ void *base,
+ size_t num,
+ size_t width,
+ const void *context,
+ int (__fastcall *comp)(const void *, const void *, const void *));
+}; \ No newline at end of file
diff --git a/Src/nu/strsafe.c b/Src/nu/strsafe.c
new file mode 100644
index 00000000..d040533f
--- /dev/null
+++ b/Src/nu/strsafe.c
@@ -0,0 +1,2 @@
+#define STRSAFE_LIB_IMPL
+#include "strsafe.h" \ No newline at end of file
diff --git a/Src/nu/strsafe.h b/Src/nu/strsafe.h
new file mode 100644
index 00000000..39fae5ea
--- /dev/null
+++ b/Src/nu/strsafe.h
@@ -0,0 +1,6647 @@
+/******************************************************************
+* *
+* strsafe.h -- This module defines safer C library string *
+* routine replacements. These are meant to make C *
+* a bit more safe in reference to security and *
+* robustness *
+* *
+* Copyright (c) Microsoft Corp. All rights reserved. *
+* Ported to Unix by Ben Allison - Nullsoft, Inc. *
+* *
+******************************************************************/
+#ifndef _STRSAFE_H_INCLUDED_
+#define _STRSAFE_H_INCLUDED_
+#pragma once
+
+#include <stdio.h> // for _vsnprintf, _vsnwprintf, getc, getwc
+#include <string.h> // for memset
+#include <stdarg.h> // for va_start, etc.
+#include <wchar.h>
+
+#ifndef _WIN32
+#define STRSAFE_NO_DEPRECATE
+#endif
+
+#ifdef _WIN32
+#define vsnprintf _vsnprintf
+#endif
+
+#ifndef _SIZE_T_DEFINED
+#ifdef _WIN64
+typedef unsigned __int64 size_t;
+#elif defined(_WIN32)
+typedef __w64 unsigned int size_t;
+#else
+#include <stdint.h>
+#endif // !_WIN64
+#define _SIZE_T_DEFINED
+#endif // !_SIZE_T_DEFINED
+
+#if defined(_WIN32) && !defined(_WCHAR_T_DEFINED) && !defined(_NATIVE_WCHAR_T_DEFINED)
+typedef unsigned short wchar_t;
+#define _WCHAR_T_DEFINED
+#endif
+
+#ifndef _HRESULT_DEFINED
+#define _HRESULT_DEFINED
+typedef long HRESULT;
+#endif // !_HRESULT_DEFINED
+
+#ifndef SUCCEEDED
+#define SUCCEEDED(hr) ((HRESULT)(hr) >= 0)
+#endif
+
+#ifndef FAILED
+#define FAILED(hr) ((HRESULT)(hr) < 0)
+#endif
+
+#ifndef S_OK
+#define S_OK ((HRESULT)0x00000000L)
+#endif
+
+#ifdef __cplusplus
+#define _STRSAFE_EXTERN_C extern "C"
+#else
+#define _STRSAFE_EXTERN_C extern
+#endif
+
+// If you do not want to use these functions inline (and instead want to link w/ strsafe.lib), then
+// #define STRSAFE_LIB before including this header file.
+#ifndef _WIN32
+#define __inline inline
+#define __stdcall
+#endif
+
+#if defined(STRSAFE_LIB)
+#define STRSAFEAPI _STRSAFE_EXTERN_C HRESULT __stdcall
+#pragma comment(lib, "./strsafe.lib")
+#elif defined(STRSAFE_LIB_IMPL)
+#define STRSAFEAPI _STRSAFE_EXTERN_C __declspec(dllexport) HRESULT __stdcall
+#else
+#define STRSAFEAPI __inline HRESULT __stdcall
+#define STRSAFE_INLINE
+#endif
+
+// Some functions always run inline because they use stdin and we want to avoid building multiple
+// versions of strsafe lib depending on if you use msvcrt, libcmt, etc.
+#define STRSAFE_INLINE_API __inline HRESULT __stdcall
+
+// The user can request no "Cb" or no "Cch" fuctions, but not both!
+#if defined(STRSAFE_NO_CB_FUNCTIONS) && defined(STRSAFE_NO_CCH_FUNCTIONS)
+#error cannot specify both STRSAFE_NO_CB_FUNCTIONS and STRSAFE_NO_CCH_FUNCTIONS !!
+#endif
+
+// This should only be defined when we are building strsafe.lib
+#ifdef STRSAFE_LIB_IMPL
+#define STRSAFE_INLINE
+#endif
+
+
+// If both strsafe.h and ntstrsafe.h are included, only use definitions from one.
+#ifndef _NTSTRSAFE_H_INCLUDED_
+
+#define STRSAFE_MAX_CCH 2147483647 // max # of characters we support (same as INT_MAX)
+
+// Flags for controling the Ex functions
+//
+// STRSAFE_FILL_BYTE(0xFF) 0x000000FF // bottom byte specifies fill pattern
+#define STRSAFE_IGNORE_NULLS 0x00000100 // treat null as TEXT("") -- don't fault on NULL buffers
+#define STRSAFE_FILL_BEHIND_NULL 0x00000200 // fill in extra space behind the null terminator
+#define STRSAFE_FILL_ON_FAILURE 0x00000400 // on failure, overwrite pszDest with fill pattern and null terminate it
+#define STRSAFE_NULL_ON_FAILURE 0x00000800 // on failure, set *pszDest = TEXT('\0')
+#define STRSAFE_NO_TRUNCATION 0x00001000 // instead of returning a truncated result, copy/append nothing to pszDest and null terminate it
+
+#define STRSAFE_VALID_FLAGS (0x000000FF | STRSAFE_IGNORE_NULLS | STRSAFE_FILL_BEHIND_NULL | STRSAFE_FILL_ON_FAILURE | STRSAFE_NULL_ON_FAILURE | STRSAFE_NO_TRUNCATION)
+
+// helper macro to set the fill character and specify buffer filling
+#define STRSAFE_FILL_BYTE(x) ((unsigned long)((x & 0x000000FF) | STRSAFE_FILL_BEHIND_NULL))
+#define STRSAFE_FAILURE_BYTE(x) ((unsigned long)((x & 0x000000FF) | STRSAFE_FILL_ON_FAILURE))
+
+#define STRSAFE_GET_FILL_PATTERN(dwFlags) ((int)(dwFlags & 0x000000FF))
+
+#endif // _NTSTRSAFE_H_INCLUDED_
+
+// STRSAFE error return codes
+//
+#define STRSAFE_E_INSUFFICIENT_BUFFER ((HRESULT)0x8007007AL) // 0x7A = 122L = ERROR_INSUFFICIENT_BUFFER
+#define STRSAFE_E_INVALID_PARAMETER ((HRESULT)0x80070057L) // 0x57 = 87L = ERROR_INVALID_PARAMETER
+#define STRSAFE_E_END_OF_FILE ((HRESULT)0x80070026L) // 0x26 = 38L = ERROR_HANDLE_EOF
+
+// prototypes for the worker functions
+#ifdef STRSAFE_INLINE
+STRSAFEAPI StringCopyWorkerA(char* pszDest, size_t cchDest, const char* pszSrc);
+STRSAFEAPI StringCopyWorkerW(wchar_t* pszDest, size_t cchDest, const wchar_t* pszSrc);
+STRSAFEAPI StringCopyExWorkerA(char* pszDest, size_t cchDest, size_t cbDest, const char* pszSrc, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags);
+STRSAFEAPI StringCopyExWorkerW(wchar_t* pszDest, size_t cchDest, size_t cbDest, const wchar_t* pszSrc, wchar_t** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags);
+STRSAFEAPI StringCopyNWorkerA(char* pszDest, size_t cchDest, const char* pszSrc, size_t cchSrc);
+STRSAFEAPI StringCopyNWorkerW(wchar_t* pszDest, size_t cchDest, const wchar_t* pszSrc, size_t cchSrc);
+STRSAFEAPI StringCopyNExWorkerA(char* pszDest, size_t cchDest, size_t cbDest, const char* pszSrc, size_t cchSrc, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags);
+STRSAFEAPI StringCopyNExWorkerW(wchar_t* pszDest, size_t cchDest, size_t cbDest, const wchar_t* pszSrc, size_t cchSrc, wchar_t** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags);
+STRSAFEAPI StringCatWorkerA(char* pszDest, size_t cchDest, const char* pszSrc);
+STRSAFEAPI StringCatWorkerW(wchar_t* pszDest, size_t cchDest, const wchar_t* pszSrc);
+STRSAFEAPI StringCatExWorkerA(char* pszDest, size_t cchDest, size_t cbDest, const char* pszSrc, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags);
+STRSAFEAPI StringCatExWorkerW(wchar_t* pszDest, size_t cchDest, size_t cbDest, const wchar_t* pszSrc, wchar_t** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags);
+STRSAFEAPI StringCatNWorkerA(char* pszDest, size_t cchDest, const char* pszSrc, size_t cchMaxAppend);
+STRSAFEAPI StringCatNWorkerW(wchar_t* pszDest, size_t cchDest, const wchar_t* pszSrc, size_t cchMaxAppend);
+STRSAFEAPI StringCatNExWorkerA(char* pszDest, size_t cchDest, size_t cbDest, const char* pszSrc, size_t cchMaxAppend, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags);
+STRSAFEAPI StringCatNExWorkerW(wchar_t* pszDest, size_t cchDest, size_t cbDest, const wchar_t* pszSrc, size_t cchMaxAppend, wchar_t** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags);
+STRSAFEAPI StringVPrintfWorkerA(char* pszDest, size_t cchDest, const char* pszFormat, va_list argList);
+STRSAFEAPI StringVPrintfWorkerW(wchar_t* pszDest, size_t cchDest, const wchar_t* pszFormat, va_list argList);
+STRSAFEAPI StringVPrintfExWorkerA(char* pszDest, size_t cchDest, size_t cbDest, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags, const char* pszFormat, va_list argList);
+STRSAFEAPI StringVPrintfExWorkerW(wchar_t* pszDest, size_t cchDest, size_t cbDest, wchar_t** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags, const wchar_t* pszFormat, va_list argList);
+STRSAFEAPI StringLengthWorkerA(const char* psz, size_t cchMax, size_t* pcch);
+STRSAFEAPI StringLengthWorkerW(const wchar_t* psz, size_t cchMax, size_t* pcch);
+#endif // STRSAFE_INLINE
+
+#ifndef STRSAFE_LIB_IMPL
+// these functions are always inline
+STRSAFE_INLINE_API StringGetsExWorkerA(char* pszDest, size_t cchDest, size_t cbDest, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags);
+STRSAFE_INLINE_API StringGetsExWorkerW(wchar_t* pszDest, size_t cchDest, size_t cbDest, wchar_t** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags);
+#endif
+
+#ifdef _NTSTRSAFE_H_INCLUDED_
+#pragma warning(push)
+#pragma warning(disable : 4995)
+#endif // _NTSTRSAFE_H_INCLUDED_
+
+
+#ifndef STRSAFE_NO_CCH_FUNCTIONS
+/*++
+
+STDAPI
+StringCchCopy(
+ OUT LPTSTR pszDest,
+ IN size_t cchDest,
+ IN LPCTSTR pszSrc
+ );
+
+Routine Description:
+
+ This routine is a safer version of the C built-in function 'strcpy'.
+ The size of the destination buffer (in characters) is a parameter and
+ this function will not write past the end of this buffer and it will
+ ALWAYS null terminate the destination buffer (unless it is zero length).
+
+ This routine is not a replacement for strncpy. That function will pad the
+ destination string with extra null termination characters if the count is
+ greater than the length of the source string, and it will fail to null
+ terminate the destination string if the source string length is greater
+ than or equal to the count. You can not blindly use this instead of strncpy:
+ it is common for code to use it to "patch" strings and you would introduce
+ errors if the code started null terminating in the middle of the string.
+
+ This function returns a hresult, and not a pointer. It returns
+ S_OK if the string was copied without truncation and null terminated,
+ otherwise it will return a failure code. In failure cases as much of
+ pszSrc will be copied to pszDest as possible, and pszDest will be null
+ terminated.
+
+Arguments:
+
+ pszDest - destination string
+
+ cchDest - size of destination buffer in characters.
+ length must be = (_tcslen(src) + 1) to hold all of the
+ source including the null terminator
+
+ pszSrc - source string which must be null terminated
+
+Notes:
+ Behavior is undefined if source and destination strings overlap.
+
+ pszDest and pszSrc should not be NULL. See StringCchCopyEx if you require
+ the handling of NULL values.
+
+Return Value:
+
+ S_OK - if there was source data and it was all copied and the
+ resultant dest string was null terminated
+
+ failure - you can use the macro HRESULT_CODE() to get a win32
+ error code for all hresult failure cases
+
+ STRSAFE_E_INSUFFICIENT_BUFFER /
+ HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
+ - this return value is an indication that the copy
+ operation failed due to insufficient space. When this
+ error occurs, the destination buffer is modified to
+ contain a truncated version of the ideal result and is
+ null terminated. This is useful for situations where
+ truncation is ok
+
+ It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
+ return value of this function.
+
+--*/
+
+STRSAFEAPI StringCchCopyA(char* pszDest, size_t cchDest, const char* pszSrc);
+STRSAFEAPI StringCchCopyW(wchar_t* pszDest, size_t cchDest, const wchar_t* pszSrc);
+#ifdef UNICODE
+#define StringCchCopy StringCchCopyW
+#else
+#define StringCchCopy StringCchCopyA
+#endif // !UNICODE
+
+#ifdef STRSAFE_INLINE
+STRSAFEAPI StringCchCopyA(char* pszDest, size_t cchDest, const char* pszSrc)
+{
+ HRESULT hr;
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ hr = StringCopyWorkerA(pszDest, cchDest, pszSrc);
+ }
+
+ return hr;
+}
+
+STRSAFEAPI StringCchCopyW(wchar_t* pszDest, size_t cchDest, const wchar_t* pszSrc)
+{
+ HRESULT hr;
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ hr = StringCopyWorkerW(pszDest, cchDest, pszSrc);
+ }
+
+ return hr;
+}
+#endif // STRSAFE_INLINE
+#endif // !STRSAFE_NO_CCH_FUNCTIONS
+
+
+#ifndef STRSAFE_NO_CB_FUNCTIONS
+/*++
+
+STDAPI
+StringCbCopy(
+ OUT LPTSTR pszDest,
+ IN size_t cbDest,
+ IN LPCTSTR pszSrc
+ );
+
+Routine Description:
+
+ This routine is a safer version of the C built-in function 'strcpy'.
+ The size of the destination buffer (in bytes) is a parameter and this
+ function will not write past the end of this buffer and it will ALWAYS
+ null terminate the destination buffer (unless it is zero length).
+
+ This routine is not a replacement for strncpy. That function will pad the
+ destination string with extra null termination characters if the count is
+ greater than the length of the source string, and it will fail to null
+ terminate the destination string if the source string length is greater
+ than or equal to the count. You can not blindly use this instead of strncpy:
+ it is common for code to use it to "patch" strings and you would introduce
+ errors if the code started null terminating in the middle of the string.
+
+ This function returns a hresult, and not a pointer. It returns
+ S_OK if the string was copied without truncation and null terminated,
+ otherwise it will return a failure code. In failure cases as much of pszSrc
+ will be copied to pszDest as possible, and pszDest will be null terminated.
+
+Arguments:
+
+ pszDest - destination string
+
+ cbDest - size of destination buffer in bytes.
+ length must be = ((_tcslen(src) + 1) * sizeof(TCHAR)) to
+ hold all of the source including the null terminator
+
+ pszSrc - source string which must be null terminated
+
+Notes:
+ Behavior is undefined if source and destination strings overlap.
+
+ pszDest and pszSrc should not be NULL. See StringCbCopyEx if you require
+ the handling of NULL values.
+
+Return Value:
+
+ S_OK - if there was source data and it was all copied and the
+ resultant dest string was null terminated
+
+ failure - you can use the macro HRESULT_CODE() to get a win32
+ error code for all hresult failure cases
+
+ STRSAFE_E_INSUFFICIENT_BUFFER /
+ HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
+ - this return value is an indication that the copy
+ operation failed due to insufficient space. When this
+ error occurs, the destination buffer is modified to
+ contain a truncated version of the ideal result and is
+ null terminated. This is useful for situations where
+ truncation is ok
+
+ It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
+ return value of this function.
+
+--*/
+
+STRSAFEAPI StringCbCopyA(char* pszDest, size_t cbDest, const char* pszSrc);
+STRSAFEAPI StringCbCopyW(wchar_t* pszDest, size_t cbDest, const wchar_t* pszSrc);
+#ifdef UNICODE
+#define StringCbCopy StringCbCopyW
+#else
+#define StringCbCopy StringCbCopyA
+#endif // !UNICODE
+
+#ifdef STRSAFE_INLINE
+STRSAFEAPI StringCbCopyA(char* pszDest, size_t cbDest, const char* pszSrc)
+{
+ HRESULT hr;
+ size_t cchDest;
+
+ // convert to count of characters
+ cchDest = cbDest / sizeof(char);
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ hr = StringCopyWorkerA(pszDest, cchDest, pszSrc);
+ }
+
+ return hr;
+}
+
+STRSAFEAPI StringCbCopyW(wchar_t* pszDest, size_t cbDest, const wchar_t* pszSrc)
+{
+ HRESULT hr;
+ size_t cchDest;
+
+ // convert to count of characters
+ cchDest = cbDest / sizeof(wchar_t);
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ hr = StringCopyWorkerW(pszDest, cchDest, pszSrc);
+ }
+
+ return hr;
+}
+#endif // STRSAFE_INLINE
+#endif // !STRSAFE_NO_CB_FUNCTIONS
+
+
+#ifndef STRSAFE_NO_CCH_FUNCTIONS
+/*++
+
+STDAPI
+StringCchCopyEx(
+ OUT LPTSTR pszDest OPTIONAL,
+ IN size_t cchDest,
+ IN LPCTSTR pszSrc OPTIONAL,
+ OUT LPTSTR* ppszDestEnd OPTIONAL,
+ OUT size_t* pcchRemaining OPTIONAL,
+ IN DWORD dwFlags
+ );
+
+Routine Description:
+
+ This routine is a safer version of the C built-in function 'strcpy' with
+ some additional parameters. In addition to functionality provided by
+ StringCchCopy, this routine also returns a pointer to the end of the
+ destination string and the number of characters left in the destination string
+ including the null terminator. The flags parameter allows additional controls.
+
+Arguments:
+
+ pszDest - destination string
+
+ cchDest - size of destination buffer in characters.
+ length must be = (_tcslen(pszSrc) + 1) to hold all of
+ the source including the null terminator
+
+ pszSrc - source string which must be null terminated
+
+ ppszDestEnd - if ppszDestEnd is non-null, the function will return a
+ pointer to the end of the destination string. If the
+ function copied any data, the result will point to the
+ null termination character
+
+ pcchRemaining - if pcchRemaining is non-null, the function will return the
+ number of characters left in the destination string,
+ including the null terminator
+
+ dwFlags - controls some details of the string copy:
+
+ STRSAFE_FILL_BEHIND_NULL
+ if the function succeeds, the low byte of dwFlags will be
+ used to fill the uninitialize part of destination buffer
+ behind the null terminator
+
+ STRSAFE_IGNORE_NULLS
+ treat NULL string pointers like empty strings (TEXT("")).
+ this flag is useful for emulating functions like lstrcpy
+
+ STRSAFE_FILL_ON_FAILURE
+ if the function fails, the low byte of dwFlags will be
+ used to fill all of the destination buffer, and it will
+ be null terminated. This will overwrite any truncated
+ string returned when the failure is
+ STRSAFE_E_INSUFFICIENT_BUFFER
+
+ STRSAFE_NO_TRUNCATION /
+ STRSAFE_NULL_ON_FAILURE
+ if the function fails, the destination buffer will be set
+ to the empty string. This will overwrite any truncated string
+ returned when the failure is STRSAFE_E_INSUFFICIENT_BUFFER.
+
+Notes:
+ Behavior is undefined if source and destination strings overlap.
+
+ pszDest and pszSrc should not be NULL unless the STRSAFE_IGNORE_NULLS flag
+ is specified. If STRSAFE_IGNORE_NULLS is passed, both pszDest and pszSrc
+ may be NULL. An error may still be returned even though NULLS are ignored
+ due to insufficient space.
+
+Return Value:
+
+ S_OK - if there was source data and it was all copied and the
+ resultant dest string was null terminated
+
+ failure - you can use the macro HRESULT_CODE() to get a win32
+ error code for all hresult failure cases
+
+ STRSAFE_E_INSUFFICIENT_BUFFER /
+ HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
+ - this return value is an indication that the copy
+ operation failed due to insufficient space. When this
+ error occurs, the destination buffer is modified to
+ contain a truncated version of the ideal result and is
+ null terminated. This is useful for situations where
+ truncation is ok.
+
+ It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
+ return value of this function
+
+--*/
+
+STRSAFEAPI StringCchCopyExA(char* pszDest, size_t cchDest, const char* pszSrc, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags);
+STRSAFEAPI StringCchCopyExW(wchar_t* pszDest, size_t cchDest, const wchar_t* pszSrc, wchar_t** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags);
+#ifdef UNICODE
+#define StringCchCopyEx StringCchCopyExW
+#else
+#define StringCchCopyEx StringCchCopyExA
+#endif // !UNICODE
+
+#ifdef STRSAFE_INLINE
+STRSAFEAPI StringCchCopyExA(char* pszDest, size_t cchDest, const char* pszSrc, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags)
+{
+ HRESULT hr;
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ size_t cbDest;
+
+ // safe to multiply cchDest * sizeof(char) since cchDest < STRSAFE_MAX_CCH and sizeof(char) is 1
+ cbDest = cchDest * sizeof(char);
+
+ hr = StringCopyExWorkerA(pszDest, cchDest, cbDest, pszSrc, ppszDestEnd, pcchRemaining, dwFlags);
+ }
+
+ return hr;
+}
+
+STRSAFEAPI StringCchCopyExW(wchar_t* pszDest, size_t cchDest, const wchar_t* pszSrc, wchar_t** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags)
+{
+ HRESULT hr;
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ size_t cbDest;
+
+ // safe to multiply cchDest * sizeof(wchar_t) since cchDest < STRSAFE_MAX_CCH and sizeof(wchar_t) is 2
+ cbDest = cchDest * sizeof(wchar_t);
+
+ hr = StringCopyExWorkerW(pszDest, cchDest, cbDest, pszSrc, ppszDestEnd, pcchRemaining, dwFlags);
+ }
+
+ return hr;
+}
+#endif // STRSAFE_INLINE
+#endif // !STRSAFE_NO_CCH_FUNCTIONS
+
+
+#ifndef STRSAFE_NO_CB_FUNCTIONS
+/*++
+
+STDAPI
+StringCbCopyEx(
+ OUT LPTSTR pszDest OPTIONAL,
+ IN size_t cbDest,
+ IN LPCTSTR pszSrc OPTIONAL,
+ OUT LPTSTR* ppszDestEnd OPTIONAL,
+ OUT size_t* pcbRemaining OPTIONAL,
+ IN DWORD dwFlags
+ );
+
+Routine Description:
+
+ This routine is a safer version of the C built-in function 'strcpy' with
+ some additional parameters. In addition to functionality provided by
+ StringCbCopy, this routine also returns a pointer to the end of the
+ destination string and the number of bytes left in the destination string
+ including the null terminator. The flags parameter allows additional controls.
+
+Arguments:
+
+ pszDest - destination string
+
+ cbDest - size of destination buffer in bytes.
+ length must be ((_tcslen(pszSrc) + 1) * sizeof(TCHAR)) to
+ hold all of the source including the null terminator
+
+ pszSrc - source string which must be null terminated
+
+ ppszDestEnd - if ppszDestEnd is non-null, the function will return a
+ pointer to the end of the destination string. If the
+ function copied any data, the result will point to the
+ null termination character
+
+ pcbRemaining - pcbRemaining is non-null,the function will return the
+ number of bytes left in the destination string,
+ including the null terminator
+
+ dwFlags - controls some details of the string copy:
+
+ STRSAFE_FILL_BEHIND_NULL
+ if the function succeeds, the low byte of dwFlags will be
+ used to fill the uninitialize part of destination buffer
+ behind the null terminator
+
+ STRSAFE_IGNORE_NULLS
+ treat NULL string pointers like empty strings (TEXT("")).
+ this flag is useful for emulating functions like lstrcpy
+
+ STRSAFE_FILL_ON_FAILURE
+ if the function fails, the low byte of dwFlags will be
+ used to fill all of the destination buffer, and it will
+ be null terminated. This will overwrite any truncated
+ string returned when the failure is
+ STRSAFE_E_INSUFFICIENT_BUFFER
+
+ STRSAFE_NO_TRUNCATION /
+ STRSAFE_NULL_ON_FAILURE
+ if the function fails, the destination buffer will be set
+ to the empty string. This will overwrite any truncated string
+ returned when the failure is STRSAFE_E_INSUFFICIENT_BUFFER.
+
+Notes:
+ Behavior is undefined if source and destination strings overlap.
+
+ pszDest and pszSrc should not be NULL unless the STRSAFE_IGNORE_NULLS flag
+ is specified. If STRSAFE_IGNORE_NULLS is passed, both pszDest and pszSrc
+ may be NULL. An error may still be returned even though NULLS are ignored
+ due to insufficient space.
+
+Return Value:
+
+ S_OK - if there was source data and it was all copied and the
+ resultant dest string was null terminated
+
+ failure - you can use the macro HRESULT_CODE() to get a win32
+ error code for all hresult failure cases
+
+ STRSAFE_E_INSUFFICIENT_BUFFER /
+ HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
+ - this return value is an indication that the copy
+ operation failed due to insufficient space. When this
+ error occurs, the destination buffer is modified to
+ contain a truncated version of the ideal result and is
+ null terminated. This is useful for situations where
+ truncation is ok.
+
+ It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
+ return value of this function
+
+--*/
+
+STRSAFEAPI StringCbCopyExA(char* pszDest, size_t cbDest, const char* pszSrc, char** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags);
+STRSAFEAPI StringCbCopyExW(wchar_t* pszDest, size_t cbDest, const wchar_t* pszSrc, wchar_t** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags);
+#ifdef UNICODE
+#define StringCbCopyEx StringCbCopyExW
+#else
+#define StringCbCopyEx StringCbCopyExA
+#endif // !UNICODE
+
+#ifdef STRSAFE_INLINE
+STRSAFEAPI StringCbCopyExA(char* pszDest, size_t cbDest, const char* pszSrc, char** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags)
+{
+ HRESULT hr;
+ size_t cchDest;
+ size_t cchRemaining = 0;
+
+ cchDest = cbDest / sizeof(char);
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ hr = StringCopyExWorkerA(pszDest, cchDest, cbDest, pszSrc, ppszDestEnd, &cchRemaining, dwFlags);
+ }
+
+ if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
+ {
+ if (pcbRemaining)
+ {
+ // safe to multiply cchRemaining * sizeof(char) since cchRemaining < STRSAFE_MAX_CCH and sizeof(char) is 1
+ *pcbRemaining = (cchRemaining * sizeof(char)) + (cbDest % sizeof(char));
+ }
+ }
+
+ return hr;
+}
+
+STRSAFEAPI StringCbCopyExW(wchar_t* pszDest, size_t cbDest, const wchar_t* pszSrc, wchar_t** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags)
+{
+ HRESULT hr;
+ size_t cchDest;
+ size_t cchRemaining = 0;
+
+ cchDest = cbDest / sizeof(wchar_t);
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ hr = StringCopyExWorkerW(pszDest, cchDest, cbDest, pszSrc, ppszDestEnd, &cchRemaining, dwFlags);
+ }
+
+ if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
+ {
+ if (pcbRemaining)
+ {
+ // safe to multiply cchRemaining * sizeof(wchar_t) since cchRemaining < STRSAFE_MAX_CCH and sizeof(wchar_t) is 2
+ *pcbRemaining = (cchRemaining * sizeof(wchar_t)) + (cbDest % sizeof(wchar_t));
+ }
+ }
+
+ return hr;
+}
+#endif // STRSAFE_INLINE
+#endif // !STRSAFE_NO_CB_FUNCTIONS
+
+
+#ifndef STRSAFE_NO_CCH_FUNCTIONS
+/*++
+
+STDAPI
+StringCchCopyN(
+ OUT LPTSTR pszDest,
+ IN size_t cchDest,
+ IN LPCTSTR pszSrc,
+ IN size_t cchSrc
+ );
+
+Routine Description:
+
+ This routine is a safer version of the C built-in function 'strncpy'.
+ The size of the destination buffer (in characters) is a parameter and
+ this function will not write past the end of this buffer and it will
+ ALWAYS null terminate the destination buffer (unless it is zero length).
+
+ This routine is meant as a replacement for strncpy, but it does behave
+ differently. This function will not pad the destination buffer with extra
+ null termination characters if cchSrc is greater than the length of pszSrc.
+
+ This function returns a hresult, and not a pointer. It returns
+ S_OK if the entire string or the first cchSrc characters were copied
+ without truncation and the resultant destination string was null terminated,
+ otherwise it will return a failure code. In failure cases as much of pszSrc
+ will be copied to pszDest as possible, and pszDest will be null terminated.
+
+Arguments:
+
+ pszDest - destination string
+
+ cchDest - size of destination buffer in characters.
+ length must be = (_tcslen(src) + 1) to hold all of the
+ source including the null terminator
+
+ pszSrc - source string
+
+ cchSrc - maximum number of characters to copy from source string,
+ not including the null terminator.
+
+Notes:
+ Behavior is undefined if source and destination strings overlap.
+
+ pszDest and pszSrc should not be NULL. See StringCchCopyNEx if you require
+ the handling of NULL values.
+
+Return Value:
+
+ S_OK - if there was source data and it was all copied and the
+ resultant dest string was null terminated
+
+ failure - you can use the macro HRESULT_CODE() to get a win32
+ error code for all hresult failure cases
+
+ STRSAFE_E_INSUFFICIENT_BUFFER /
+ HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
+ - this return value is an indication that the copy
+ operation failed due to insufficient space. When this
+ error occurs, the destination buffer is modified to
+ contain a truncated version of the ideal result and is
+ null terminated. This is useful for situations where
+ truncation is ok
+
+ It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
+ return value of this function.
+
+--*/
+
+STRSAFEAPI StringCchCopyNA(char* pszDest, size_t cchDest, const char* pszSrc, size_t cchSrc);
+STRSAFEAPI StringCchCopyNW(wchar_t* pszDest, size_t cchDest, const wchar_t* pszSrc, size_t cchSrc);
+#ifdef UNICODE
+#define StringCchCopyN StringCchCopyNW
+#else
+#define StringCchCopyN StringCchCopyNA
+#endif // !UNICODE
+
+#ifdef STRSAFE_INLINE
+STRSAFEAPI StringCchCopyNA(char* pszDest, size_t cchDest, const char* pszSrc, size_t cchSrc)
+{
+ HRESULT hr;
+
+ if ((cchDest > STRSAFE_MAX_CCH) ||
+ (cchSrc > STRSAFE_MAX_CCH))
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ hr = StringCopyNWorkerA(pszDest, cchDest, pszSrc, cchSrc);
+ }
+
+ return hr;
+}
+
+STRSAFEAPI StringCchCopyNW(wchar_t* pszDest, size_t cchDest, const wchar_t* pszSrc, size_t cchSrc)
+{
+ HRESULT hr;
+
+ if ((cchDest > STRSAFE_MAX_CCH) ||
+ (cchSrc > STRSAFE_MAX_CCH))
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ hr = StringCopyNWorkerW(pszDest, cchDest, pszSrc, cchSrc);
+ }
+
+ return hr;
+}
+#endif // STRSAFE_INLINE
+#endif // !STRSAFE_NO_CCH_FUNCTIONS
+
+
+#ifndef STRSAFE_NO_CB_FUNCTIONS
+/*++
+
+STDAPI
+StringCbCopyN(
+ OUT LPTSTR pszDest,
+ IN size_t cbDest,
+ IN LPCTSTR pszSrc,
+ IN size_t cbSrc
+ );
+
+Routine Description:
+
+ This routine is a safer version of the C built-in function 'strncpy'.
+ The size of the destination buffer (in bytes) is a parameter and this
+ function will not write past the end of this buffer and it will ALWAYS
+ null terminate the destination buffer (unless it is zero length).
+
+ This routine is meant as a replacement for strncpy, but it does behave
+ differently. This function will not pad the destination buffer with extra
+ null termination characters if cbSrc is greater than the size of pszSrc.
+
+ This function returns a hresult, and not a pointer. It returns
+ S_OK if the entire string or the first cbSrc characters were
+ copied without truncation and the resultant destination string was null
+ terminated, otherwise it will return a failure code. In failure cases as
+ much of pszSrc will be copied to pszDest as possible, and pszDest will be
+ null terminated.
+
+Arguments:
+
+ pszDest - destination string
+
+ cbDest - size of destination buffer in bytes.
+ length must be = ((_tcslen(src) + 1) * sizeof(TCHAR)) to
+ hold all of the source including the null terminator
+
+ pszSrc - source string
+
+ cbSrc - maximum number of bytes to copy from source string,
+ not including the null terminator.
+
+Notes:
+ Behavior is undefined if source and destination strings overlap.
+
+ pszDest and pszSrc should not be NULL. See StringCbCopyEx if you require
+ the handling of NULL values.
+
+Return Value:
+
+ S_OK - if there was source data and it was all copied and the
+ resultant dest string was null terminated
+
+ failure - you can use the macro HRESULT_CODE() to get a win32
+ error code for all hresult failure cases
+
+ STRSAFE_E_INSUFFICIENT_BUFFER /
+ HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
+ - this return value is an indication that the copy
+ operation failed due to insufficient space. When this
+ error occurs, the destination buffer is modified to
+ contain a truncated version of the ideal result and is
+ null terminated. This is useful for situations where
+ truncation is ok
+
+ It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
+ return value of this function.
+
+--*/
+
+STRSAFEAPI StringCbCopyNA(char* pszDest, size_t cbDest, const char* pszSrc, size_t cbSrc);
+STRSAFEAPI StringCbCopyNW(wchar_t* pszDest, size_t cbDest, const wchar_t* pszSrc, size_t cbSrc);
+#ifdef UNICODE
+#define StringCbCopyN StringCbCopyNW
+#else
+#define StringCbCopyN StringCbCopyNA
+#endif // !UNICODE
+
+#ifdef STRSAFE_INLINE
+STRSAFEAPI StringCbCopyNA(char* pszDest, size_t cbDest, const char* pszSrc, size_t cbSrc)
+{
+ HRESULT hr;
+ size_t cchDest;
+ size_t cchSrc;
+
+ // convert to count of characters
+ cchDest = cbDest / sizeof(char);
+ cchSrc = cbSrc / sizeof(char);
+
+ if ((cchDest > STRSAFE_MAX_CCH) ||
+ (cchSrc > STRSAFE_MAX_CCH))
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ hr = StringCopyNWorkerA(pszDest, cchDest, pszSrc, cchSrc);
+ }
+
+ return hr;
+}
+
+STRSAFEAPI StringCbCopyNW(wchar_t* pszDest, size_t cbDest, const wchar_t* pszSrc, size_t cbSrc)
+{
+ HRESULT hr;
+ size_t cchDest;
+ size_t cchSrc;
+
+ // convert to count of characters
+ cchDest = cbDest / sizeof(wchar_t);
+ cchSrc = cbSrc / sizeof(wchar_t);
+
+ if ((cchDest > STRSAFE_MAX_CCH) ||
+ (cchSrc > STRSAFE_MAX_CCH))
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ hr = StringCopyNWorkerW(pszDest, cchDest, pszSrc, cchSrc);
+ }
+
+ return hr;
+}
+#endif // STRSAFE_INLINE
+#endif // !STRSAFE_NO_CB_FUNCTIONS
+
+
+#ifndef STRSAFE_NO_CCH_FUNCTIONS
+/*++
+
+STDAPI
+StringCchCopyNEx(
+ OUT LPTSTR pszDest OPTIONAL,
+ IN size_t cchDest,
+ IN LPCTSTR pszSrc OPTIONAL,
+ IN size_t cchSrc,
+ OUT LPTSTR* ppszDestEnd OPTIONAL,
+ OUT size_t* pcchRemaining OPTIONAL,
+ IN DWORD dwFlags
+ );
+
+Routine Description:
+
+ This routine is a safer version of the C built-in function 'strncpy' with
+ some additional parameters. In addition to functionality provided by
+ StringCchCopyN, this routine also returns a pointer to the end of the
+ destination string and the number of characters left in the destination
+ string including the null terminator. The flags parameter allows
+ additional controls.
+
+ This routine is meant as a replacement for strncpy, but it does behave
+ differently. This function will not pad the destination buffer with extra
+ null termination characters if cchSrc is greater than the length of pszSrc.
+
+Arguments:
+
+ pszDest - destination string
+
+ cchDest - size of destination buffer in characters.
+ length must be = (_tcslen(pszSrc) + 1) to hold all of
+ the source including the null terminator
+
+ pszSrc - source string
+
+ cchSrc - maximum number of characters to copy from the source
+ string
+
+ ppszDestEnd - if ppszDestEnd is non-null, the function will return a
+ pointer to the end of the destination string. If the
+ function copied any data, the result will point to the
+ null termination character
+
+ pcchRemaining - if pcchRemaining is non-null, the function will return the
+ number of characters left in the destination string,
+ including the null terminator
+
+ dwFlags - controls some details of the string copy:
+
+ STRSAFE_FILL_BEHIND_NULL
+ if the function succeeds, the low byte of dwFlags will be
+ used to fill the uninitialize part of destination buffer
+ behind the null terminator
+
+ STRSAFE_IGNORE_NULLS
+ treat NULL string pointers like empty strings (TEXT("")).
+ this flag is useful for emulating functions like lstrcpy
+
+ STRSAFE_FILL_ON_FAILURE
+ if the function fails, the low byte of dwFlags will be
+ used to fill all of the destination buffer, and it will
+ be null terminated. This will overwrite any truncated
+ string returned when the failure is
+ STRSAFE_E_INSUFFICIENT_BUFFER
+
+ STRSAFE_NO_TRUNCATION /
+ STRSAFE_NULL_ON_FAILURE
+ if the function fails, the destination buffer will be set
+ to the empty string. This will overwrite any truncated string
+ returned when the failure is STRSAFE_E_INSUFFICIENT_BUFFER.
+
+Notes:
+ Behavior is undefined if source and destination strings overlap.
+
+ pszDest and pszSrc should not be NULL unless the STRSAFE_IGNORE_NULLS flag
+ is specified. If STRSAFE_IGNORE_NULLS is passed, both pszDest and pszSrc
+ may be NULL. An error may still be returned even though NULLS are ignored
+ due to insufficient space.
+
+Return Value:
+
+ S_OK - if there was source data and it was all copied and the
+ resultant dest string was null terminated
+
+ failure - you can use the macro HRESULT_CODE() to get a win32
+ error code for all hresult failure cases
+
+ STRSAFE_E_INSUFFICIENT_BUFFER /
+ HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
+ - this return value is an indication that the copy
+ operation failed due to insufficient space. When this
+ error occurs, the destination buffer is modified to
+ contain a truncated version of the ideal result and is
+ null terminated. This is useful for situations where
+ truncation is ok.
+
+ It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
+ return value of this function
+
+--*/
+
+STRSAFEAPI StringCchCopyNExA(char* pszDest, size_t cchDest, const char* pszSrc, size_t cchSrc, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags);
+STRSAFEAPI StringCchCopyNExW(wchar_t* pszDest, size_t cchDest, const wchar_t* pszSrc, size_t cchSrc, wchar_t** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags);
+#ifdef UNICODE
+#define StringCchCopyNEx StringCchCopyNExW
+#else
+#define StringCchCopyNEx StringCchCopyNExA
+#endif // !UNICODE
+
+#ifdef STRSAFE_INLINE
+STRSAFEAPI StringCchCopyNExA(char* pszDest, size_t cchDest, const char* pszSrc, size_t cchSrc, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags)
+{
+ HRESULT hr;
+
+ if ((cchDest > STRSAFE_MAX_CCH) ||
+ (cchSrc > STRSAFE_MAX_CCH))
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ size_t cbDest;
+
+ // safe to multiply cchDest * sizeof(char) since cchDest < STRSAFE_MAX_CCH and sizeof(char) is 1
+ cbDest = cchDest * sizeof(char);
+
+ hr = StringCopyNExWorkerA(pszDest, cchDest, cbDest, pszSrc, cchSrc, ppszDestEnd, pcchRemaining, dwFlags);
+ }
+
+ return hr;
+}
+
+STRSAFEAPI StringCchCopyNExW(wchar_t* pszDest, size_t cchDest, const wchar_t* pszSrc, size_t cchSrc, wchar_t** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags)
+{
+ HRESULT hr;
+
+ if ((cchDest > STRSAFE_MAX_CCH) ||
+ (cchSrc > STRSAFE_MAX_CCH))
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ size_t cbDest;
+
+ // safe to multiply cchDest * sizeof(wchar_t) since cchDest < STRSAFE_MAX_CCH and sizeof(wchar_t) is 2
+ cbDest = cchDest * sizeof(wchar_t);
+
+ hr = StringCopyNExWorkerW(pszDest, cchDest, cbDest, pszSrc, cchSrc, ppszDestEnd, pcchRemaining, dwFlags);
+ }
+
+ return hr;
+}
+#endif // STRSAFE_INLINE
+#endif // !STRSAFE_NO_CCH_FUNCTIONS
+
+
+#ifndef STRSAFE_NO_CB_FUNCTIONS
+/*++
+
+STDAPI
+StringCbCopyNEx(
+ OUT LPTSTR pszDest OPTIONAL,
+ IN size_t cbDest,
+ IN LPCTSTR pszSrc OPTIONAL,
+ IN size_t cbSrc,
+ OUT LPTSTR* ppszDestEnd OPTIONAL,
+ OUT size_t* pcbRemaining OPTIONAL,
+ IN DWORD dwFlags
+ );
+
+Routine Description:
+
+ This routine is a safer version of the C built-in function 'strncpy' with
+ some additional parameters. In addition to functionality provided by
+ StringCbCopyN, this routine also returns a pointer to the end of the
+ destination string and the number of bytes left in the destination string
+ including the null terminator. The flags parameter allows additional controls.
+
+ This routine is meant as a replacement for strncpy, but it does behave
+ differently. This function will not pad the destination buffer with extra
+ null termination characters if cbSrc is greater than the size of pszSrc.
+
+Arguments:
+
+ pszDest - destination string
+
+ cbDest - size of destination buffer in bytes.
+ length must be ((_tcslen(pszSrc) + 1) * sizeof(TCHAR)) to
+ hold all of the source including the null terminator
+
+ pszSrc - source string
+
+ cbSrc - maximum number of bytes to copy from source string
+
+ ppszDestEnd - if ppszDestEnd is non-null, the function will return a
+ pointer to the end of the destination string. If the
+ function copied any data, the result will point to the
+ null termination character
+
+ pcbRemaining - pcbRemaining is non-null,the function will return the
+ number of bytes left in the destination string,
+ including the null terminator
+
+ dwFlags - controls some details of the string copy:
+
+ STRSAFE_FILL_BEHIND_NULL
+ if the function succeeds, the low byte of dwFlags will be
+ used to fill the uninitialize part of destination buffer
+ behind the null terminator
+
+ STRSAFE_IGNORE_NULLS
+ treat NULL string pointers like empty strings (TEXT("")).
+ this flag is useful for emulating functions like lstrcpy
+
+ STRSAFE_FILL_ON_FAILURE
+ if the function fails, the low byte of dwFlags will be
+ used to fill all of the destination buffer, and it will
+ be null terminated. This will overwrite any truncated
+ string returned when the failure is
+ STRSAFE_E_INSUFFICIENT_BUFFER
+
+ STRSAFE_NO_TRUNCATION /
+ STRSAFE_NULL_ON_FAILURE
+ if the function fails, the destination buffer will be set
+ to the empty string. This will overwrite any truncated string
+ returned when the failure is STRSAFE_E_INSUFFICIENT_BUFFER.
+
+Notes:
+ Behavior is undefined if source and destination strings overlap.
+
+ pszDest and pszSrc should not be NULL unless the STRSAFE_IGNORE_NULLS flag
+ is specified. If STRSAFE_IGNORE_NULLS is passed, both pszDest and pszSrc
+ may be NULL. An error may still be returned even though NULLS are ignored
+ due to insufficient space.
+
+Return Value:
+
+ S_OK - if there was source data and it was all copied and the
+ resultant dest string was null terminated
+
+ failure - you can use the macro HRESULT_CODE() to get a win32
+ error code for all hresult failure cases
+
+ STRSAFE_E_INSUFFICIENT_BUFFER /
+ HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
+ - this return value is an indication that the copy
+ operation failed due to insufficient space. When this
+ error occurs, the destination buffer is modified to
+ contain a truncated version of the ideal result and is
+ null terminated. This is useful for situations where
+ truncation is ok.
+
+ It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
+ return value of this function
+
+--*/
+
+STRSAFEAPI StringCbCopyNExA(char* pszDest, size_t cbDest, const char* pszSrc, size_t cbSrc, char** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags);
+STRSAFEAPI StringCbCopyNExW(wchar_t* pszDest, size_t cbDest, const wchar_t* pszSrc, size_t cbSrc, wchar_t** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags);
+#ifdef UNICODE
+#define StringCbCopyNEx StringCbCopyNExW
+#else
+#define StringCbCopyNEx StringCbCopyNExA
+#endif // !UNICODE
+
+#ifdef STRSAFE_INLINE
+STRSAFEAPI StringCbCopyNExA(char* pszDest, size_t cbDest, const char* pszSrc, size_t cbSrc, char** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags)
+{
+ HRESULT hr;
+ size_t cchDest;
+ size_t cchSrc;
+ size_t cchRemaining = 0;
+
+ cchDest = cbDest / sizeof(char);
+ cchSrc = cbSrc / sizeof(char);
+
+ if ((cchDest > STRSAFE_MAX_CCH) ||
+ (cchSrc > STRSAFE_MAX_CCH))
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ hr = StringCopyNExWorkerA(pszDest, cchDest, cbDest, pszSrc, cchSrc, ppszDestEnd, &cchRemaining, dwFlags);
+ }
+
+ if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
+ {
+ if (pcbRemaining)
+ {
+ // safe to multiply cchRemaining * sizeof(char) since cchRemaining < STRSAFE_MAX_CCH and sizeof(char) is 1
+ *pcbRemaining = (cchRemaining * sizeof(char)) + (cbDest % sizeof(char));
+ }
+ }
+
+ return hr;
+}
+
+STRSAFEAPI StringCbCopyNExW(wchar_t* pszDest, size_t cbDest, const wchar_t* pszSrc, size_t cbSrc, wchar_t** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags)
+{
+ HRESULT hr;
+ size_t cchDest;
+ size_t cchSrc;
+ size_t cchRemaining = 0;
+
+ cchDest = cbDest / sizeof(wchar_t);
+ cchSrc = cbSrc / sizeof(wchar_t);
+
+ if ((cchDest > STRSAFE_MAX_CCH) ||
+ (cchSrc > STRSAFE_MAX_CCH))
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ hr = StringCopyNExWorkerW(pszDest, cchDest, cbDest, pszSrc, cchSrc, ppszDestEnd, &cchRemaining, dwFlags);
+ }
+
+ if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
+ {
+ if (pcbRemaining)
+ {
+ // safe to multiply cchRemaining * sizeof(wchar_t) since cchRemaining < STRSAFE_MAX_CCH and sizeof(wchar_t) is 2
+ *pcbRemaining = (cchRemaining * sizeof(wchar_t)) + (cbDest % sizeof(wchar_t));
+ }
+ }
+
+ return hr;
+}
+#endif // STRSAFE_INLINE
+#endif // !STRSAFE_NO_CB_FUNCTIONS
+
+
+#ifndef STRSAFE_NO_CCH_FUNCTIONS
+/*++
+
+STDAPI
+StringCchCat(
+ IN OUT LPTSTR pszDest,
+ IN size_t cchDest,
+ IN LPCTSTR pszSrc
+ );
+
+Routine Description:
+
+ This routine is a safer version of the C built-in function 'strcat'.
+ The size of the destination buffer (in characters) is a parameter and this
+ function will not write past the end of this buffer and it will ALWAYS
+ null terminate the destination buffer (unless it is zero length).
+
+ This function returns a hresult, and not a pointer. It returns
+ S_OK if the string was concatenated without truncation and null terminated,
+ otherwise it will return a failure code. In failure cases as much of pszSrc
+ will be appended to pszDest as possible, and pszDest will be null
+ terminated.
+
+Arguments:
+
+ pszDest - destination string which must be null terminated
+
+ cchDest - size of destination buffer in characters.
+ length must be = (_tcslen(pszDest) + _tcslen(pszSrc) + 1)
+ to hold all of the combine string plus the null
+ terminator
+
+ pszSrc - source string which must be null terminated
+
+Notes:
+ Behavior is undefined if source and destination strings overlap.
+
+ pszDest and pszSrc should not be NULL. See StringCchCatEx if you require
+ the handling of NULL values.
+
+Return Value:
+
+ S_OK - if there was source data and it was all concatenated and
+ the resultant dest string was null terminated
+
+ failure - you can use the macro HRESULT_CODE() to get a win32
+ error code for all hresult failure cases
+
+ STRSAFE_E_INSUFFICIENT_BUFFER /
+ HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
+ - this return value is an indication that the operation
+ failed due to insufficient space. When this error occurs,
+ the destination buffer is modified to contain a truncated
+ version of the ideal result and is null terminated. This
+ is useful for situations where truncation is ok.
+
+ It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
+ return value of this function
+
+--*/
+
+STRSAFEAPI StringCchCatA(char* pszDest, size_t cchDest, const char* pszSrc);
+STRSAFEAPI StringCchCatW(wchar_t* pszDest, size_t cchDest, const wchar_t* pszSrc);
+#ifdef UNICODE
+#define StringCchCat StringCchCatW
+#else
+#define StringCchCat StringCchCatA
+#endif // !UNICODE
+
+#ifdef STRSAFE_INLINE
+STRSAFEAPI StringCchCatA(char* pszDest, size_t cchDest, const char* pszSrc)
+{
+ HRESULT hr;
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ hr = StringCatWorkerA(pszDest, cchDest, pszSrc);
+ }
+
+ return hr;
+}
+
+STRSAFEAPI StringCchCatW(wchar_t* pszDest, size_t cchDest, const wchar_t* pszSrc)
+{
+ HRESULT hr;
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ hr = StringCatWorkerW(pszDest, cchDest, pszSrc);
+ }
+
+ return hr;
+}
+#endif // STRSAFE_INLINE
+#endif // !STRSAFE_NO_CCH_FUNCTIONS
+
+
+#ifndef STRSAFE_NO_CB_FUNCTIONS
+/*++
+
+STDAPI
+StringCbCat(
+ IN OUT LPTSTR pszDest,
+ IN size_t cbDest,
+ IN LPCTSTR pszSrc
+ );
+
+Routine Description:
+
+ This routine is a safer version of the C built-in function 'strcat'.
+ The size of the destination buffer (in bytes) is a parameter and this
+ function will not write past the end of this buffer and it will ALWAYS
+ null terminate the destination buffer (unless it is zero length).
+
+ This function returns a hresult, and not a pointer. It returns
+ S_OK if the string was concatenated without truncation and null terminated,
+ otherwise it will return a failure code. In failure cases as much of pszSrc
+ will be appended to pszDest as possible, and pszDest will be null
+ terminated.
+
+Arguments:
+
+ pszDest - destination string which must be null terminated
+
+ cbDest - size of destination buffer in bytes.
+ length must be = ((_tcslen(pszDest) + _tcslen(pszSrc) + 1) * sizeof(TCHAR)
+ to hold all of the combine string plus the null
+ terminator
+
+ pszSrc - source string which must be null terminated
+
+Notes:
+ Behavior is undefined if source and destination strings overlap.
+
+ pszDest and pszSrc should not be NULL. See StringCbCatEx if you require
+ the handling of NULL values.
+
+Return Value:
+
+ S_OK - if there was source data and it was all concatenated and
+ the resultant dest string was null terminated
+
+ failure - you can use the macro HRESULT_CODE() to get a win32
+ error code for all hresult failure cases
+
+ STRSAFE_E_INSUFFICIENT_BUFFER /
+ HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
+ - this return value is an indication that the operation
+ failed due to insufficient space. When this error occurs,
+ the destination buffer is modified to contain a truncated
+ version of the ideal result and is null terminated. This
+ is useful for situations where truncation is ok.
+
+ It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
+ return value of this function
+
+--*/
+
+STRSAFEAPI StringCbCatA(char* pszDest, size_t cbDest, const char* pszSrc);
+STRSAFEAPI StringCbCatW(wchar_t* pszDest, size_t cbDest, const wchar_t* pszSrc);
+#ifdef UNICODE
+#define StringCbCat StringCbCatW
+#else
+#define StringCbCat StringCbCatA
+#endif // !UNICODE
+
+#ifdef STRSAFE_INLINE
+STRSAFEAPI StringCbCatA(char* pszDest, size_t cbDest, const char* pszSrc)
+{
+ HRESULT hr;
+ size_t cchDest;
+
+ cchDest = cbDest / sizeof(char);
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ hr = StringCatWorkerA(pszDest, cchDest, pszSrc);
+ }
+
+ return hr;
+}
+
+STRSAFEAPI StringCbCatW(wchar_t* pszDest, size_t cbDest, const wchar_t* pszSrc)
+{
+ HRESULT hr;
+ size_t cchDest;
+
+ cchDest = cbDest / sizeof(wchar_t);
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ hr = StringCatWorkerW(pszDest, cchDest, pszSrc);
+ }
+
+ return hr;
+}
+#endif // STRSAFE_INLINE
+#endif // !STRSAFE_NO_CB_FUNCTIONS
+
+
+#ifndef STRSAFE_NO_CCH_FUNCTIONS
+/*++
+
+STDAPI
+StringCchCatEx(
+ IN OUT LPTSTR pszDest OPTIONAL,
+ IN size_t cchDest,
+ IN LPCTSTR pszSrc OPTIONAL,
+ OUT LPTSTR* ppszDestEnd OPTIONAL,
+ OUT size_t* pcchRemaining OPTIONAL,
+ IN DWORD dwFlags
+ );
+
+Routine Description:
+
+ This routine is a safer version of the C built-in function 'strcat' with
+ some additional parameters. In addition to functionality provided by
+ StringCchCat, this routine also returns a pointer to the end of the
+ destination string and the number of characters left in the destination string
+ including the null terminator. The flags parameter allows additional controls.
+
+Arguments:
+
+ pszDest - destination string which must be null terminated
+
+ cchDest - size of destination buffer in characters
+ length must be (_tcslen(pszDest) + _tcslen(pszSrc) + 1)
+ to hold all of the combine string plus the null
+ terminator.
+
+ pszSrc - source string which must be null terminated
+
+ ppszDestEnd - if ppszDestEnd is non-null, the function will return a
+ pointer to the end of the destination string. If the
+ function appended any data, the result will point to the
+ null termination character
+
+ pcchRemaining - if pcchRemaining is non-null, the function will return the
+ number of characters left in the destination string,
+ including the null terminator
+
+ dwFlags - controls some details of the string copy:
+
+ STRSAFE_FILL_BEHIND_NULL
+ if the function succeeds, the low byte of dwFlags will be
+ used to fill the uninitialize part of destination buffer
+ behind the null terminator
+
+ STRSAFE_IGNORE_NULLS
+ treat NULL string pointers like empty strings (TEXT("")).
+ this flag is useful for emulating functions like lstrcat
+
+ STRSAFE_FILL_ON_FAILURE
+ if the function fails, the low byte of dwFlags will be
+ used to fill all of the destination buffer, and it will
+ be null terminated. This will overwrite any pre-existing
+ or truncated string
+
+ STRSAFE_NULL_ON_FAILURE
+ if the function fails, the destination buffer will be set
+ to the empty string. This will overwrite any pre-existing or
+ truncated string
+
+ STRSAFE_NO_TRUNCATION
+ if the function returns STRSAFE_E_INSUFFICIENT_BUFFER, pszDest
+ will not contain a truncated string, it will remain unchanged.
+
+Notes:
+ Behavior is undefined if source and destination strings overlap.
+
+ pszDest and pszSrc should not be NULL unless the STRSAFE_IGNORE_NULLS flag
+ is specified. If STRSAFE_IGNORE_NULLS is passed, both pszDest and pszSrc
+ may be NULL. An error may still be returned even though NULLS are ignored
+ due to insufficient space.
+
+Return Value:
+
+ S_OK - if there was source data and it was all concatenated and
+ the resultant dest string was null terminated
+
+ failure - you can use the macro HRESULT_CODE() to get a win32
+ error code for all hresult failure cases
+
+ STRSAFE_E_INSUFFICIENT_BUFFER /
+ HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
+ - this return value is an indication that the operation
+ failed due to insufficient space. When this error
+ occurs, the destination buffer is modified to contain
+ a truncated version of the ideal result and is null
+ terminated. This is useful for situations where
+ truncation is ok.
+
+ It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
+ return value of this function
+
+--*/
+
+STRSAFEAPI StringCchCatExA(char* pszDest, size_t cchDest, const char* pszSrc, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags);
+STRSAFEAPI StringCchCatExW(wchar_t* pszDest, size_t cchDest, const wchar_t* pszSrc, wchar_t** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags);
+#ifdef UNICODE
+#define StringCchCatEx StringCchCatExW
+#else
+#define StringCchCatEx StringCchCatExA
+#endif // !UNICODE
+
+#ifdef STRSAFE_INLINE
+STRSAFEAPI StringCchCatExA(char* pszDest, size_t cchDest, const char* pszSrc, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags)
+{
+ HRESULT hr;
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ size_t cbDest;
+
+ // safe to multiply cchDest * sizeof(char) since cchDest < STRSAFE_MAX_CCH and sizeof(char) is 1
+ cbDest = cchDest * sizeof(char);
+
+ hr = StringCatExWorkerA(pszDest, cchDest, cbDest, pszSrc, ppszDestEnd, pcchRemaining, dwFlags);
+ }
+
+ return hr;
+}
+
+STRSAFEAPI StringCchCatExW(wchar_t* pszDest, size_t cchDest, const wchar_t* pszSrc, wchar_t** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags)
+{
+ HRESULT hr;
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ size_t cbDest;
+
+ // safe to multiply cchDest * sizeof(wchar_t) since cchDest < STRSAFE_MAX_CCH and sizeof(wchar_t) is 2
+ cbDest = cchDest * sizeof(wchar_t);
+
+ hr = StringCatExWorkerW(pszDest, cchDest, cbDest, pszSrc, ppszDestEnd, pcchRemaining, dwFlags);
+ }
+
+ return hr;
+}
+#endif // STRSAFE_INLINE
+#endif // !STRSAFE_NO_CCH_FUNCTIONS
+
+
+#ifndef STRSAFE_NO_CB_FUNCTIONS
+/*++
+
+STDAPI
+StringCbCatEx(
+ IN OUT LPTSTR pszDest OPTIONAL,
+ IN size_t cbDest,
+ IN LPCTSTR pszSrc OPTIONAL,
+ OUT LPTSTR* ppszDestEnd OPTIONAL,
+ OUT size_t* pcbRemaining OPTIONAL,
+ IN DWORD dwFlags
+ );
+
+Routine Description:
+
+ This routine is a safer version of the C built-in function 'strcat' with
+ some additional parameters. In addition to functionality provided by
+ StringCbCat, this routine also returns a pointer to the end of the
+ destination string and the number of bytes left in the destination string
+ including the null terminator. The flags parameter allows additional controls.
+
+Arguments:
+
+ pszDest - destination string which must be null terminated
+
+ cbDest - size of destination buffer in bytes.
+ length must be ((_tcslen(pszDest) + _tcslen(pszSrc) + 1) * sizeof(TCHAR)
+ to hold all of the combine string plus the null
+ terminator.
+
+ pszSrc - source string which must be null terminated
+
+ ppszDestEnd - if ppszDestEnd is non-null, the function will return a
+ pointer to the end of the destination string. If the
+ function appended any data, the result will point to the
+ null termination character
+
+ pcbRemaining - if pcbRemaining is non-null, the function will return
+ the number of bytes left in the destination string,
+ including the null terminator
+
+ dwFlags - controls some details of the string copy:
+
+ STRSAFE_FILL_BEHIND_NULL
+ if the function succeeds, the low byte of dwFlags will be
+ used to fill the uninitialize part of destination buffer
+ behind the null terminator
+
+ STRSAFE_IGNORE_NULLS
+ treat NULL string pointers like empty strings (TEXT("")).
+ this flag is useful for emulating functions like lstrcat
+
+ STRSAFE_FILL_ON_FAILURE
+ if the function fails, the low byte of dwFlags will be
+ used to fill all of the destination buffer, and it will
+ be null terminated. This will overwrite any pre-existing
+ or truncated string
+
+ STRSAFE_NULL_ON_FAILURE
+ if the function fails, the destination buffer will be set
+ to the empty string. This will overwrite any pre-existing or
+ truncated string
+
+ STRSAFE_NO_TRUNCATION
+ if the function returns STRSAFE_E_INSUFFICIENT_BUFFER, pszDest
+ will not contain a truncated string, it will remain unchanged.
+
+Notes:
+ Behavior is undefined if source and destination strings overlap.
+
+ pszDest and pszSrc should not be NULL unless the STRSAFE_IGNORE_NULLS flag
+ is specified. If STRSAFE_IGNORE_NULLS is passed, both pszDest and pszSrc
+ may be NULL. An error may still be returned even though NULLS are ignored
+ due to insufficient space.
+
+Return Value:
+
+ S_OK - if there was source data and it was all concatenated
+ and the resultant dest string was null terminated
+
+ failure - you can use the macro HRESULT_CODE() to get a win32
+ error code for all hresult failure cases
+
+ STRSAFE_E_INSUFFICIENT_BUFFER /
+ HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
+ - this return value is an indication that the operation
+ failed due to insufficient space. When this error
+ occurs, the destination buffer is modified to contain
+ a truncated version of the ideal result and is null
+ terminated. This is useful for situations where
+ truncation is ok.
+
+ It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
+ return value of this function
+
+--*/
+
+STRSAFEAPI StringCbCatExA(char* pszDest, size_t cbDest, const char* pszSrc, char** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags);
+STRSAFEAPI StringCbCatExW(wchar_t* pszDest, size_t cbDest, const wchar_t* pszSrc, wchar_t** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags);
+#ifdef UNICODE
+#define StringCbCatEx StringCbCatExW
+#else
+#define StringCbCatEx StringCbCatExA
+#endif // !UNICODE
+
+#ifdef STRSAFE_INLINE
+STRSAFEAPI StringCbCatExA(char* pszDest, size_t cbDest, const char* pszSrc, char** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags)
+{
+ HRESULT hr;
+ size_t cchDest;
+ size_t cchRemaining = 0;
+
+ cchDest = cbDest / sizeof(char);
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ hr = StringCatExWorkerA(pszDest, cchDest, cbDest, pszSrc, ppszDestEnd, &cchRemaining, dwFlags);
+ }
+
+ if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
+ {
+ if (pcbRemaining)
+ {
+ // safe to multiply cchRemaining * sizeof(char) since cchRemaining < STRSAFE_MAX_CCH and sizeof(char) is 1
+ *pcbRemaining = (cchRemaining * sizeof(char)) + (cbDest % sizeof(char));
+ }
+ }
+
+ return hr;
+}
+
+STRSAFEAPI StringCbCatExW(wchar_t* pszDest, size_t cbDest, const wchar_t* pszSrc, wchar_t** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags)
+{
+ HRESULT hr;
+ size_t cchDest;
+ size_t cchRemaining = 0;
+
+ cchDest = cbDest / sizeof(wchar_t);
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ hr = StringCatExWorkerW(pszDest, cchDest, cbDest, pszSrc, ppszDestEnd, &cchRemaining, dwFlags);
+ }
+
+ if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
+ {
+ if (pcbRemaining)
+ {
+ // safe to multiply cchRemaining * sizeof(wchar_t) since cchRemaining < STRSAFE_MAX_CCH and sizeof(wchar_t) is 2
+ *pcbRemaining = (cchRemaining * sizeof(wchar_t)) + (cbDest % sizeof(wchar_t));
+ }
+ }
+
+ return hr;
+}
+#endif // STRSAFE_INLINE
+#endif // !STRSAFE_NO_CB_FUNCTIONS
+
+
+#ifndef STRSAFE_NO_CCH_FUNCTIONS
+/*++
+
+STDAPI
+StringCchCatN(
+ IN OUT LPTSTR pszDest,
+ IN size_t cchDest,
+ IN LPCTSTR pszSrc,
+ IN size_t cchMaxAppend
+ );
+
+Routine Description:
+
+ This routine is a safer version of the C built-in function 'strncat'.
+ The size of the destination buffer (in characters) is a parameter as well as
+ the maximum number of characters to append, excluding the null terminator.
+ This function will not write past the end of the destination buffer and it will
+ ALWAYS null terminate pszDest (unless it is zero length).
+
+ This function returns a hresult, and not a pointer. It returns
+ S_OK if all of pszSrc or the first cchMaxAppend characters were appended
+ to the destination string and it was null terminated, otherwise it will
+ return a failure code. In failure cases as much of pszSrc will be appended
+ to pszDest as possible, and pszDest will be null terminated.
+
+Arguments:
+
+ pszDest - destination string which must be null terminated
+
+ cchDest - size of destination buffer in characters.
+ length must be (_tcslen(pszDest) + min(cchMaxAppend, _tcslen(pszSrc)) + 1)
+ to hold all of the combine string plus the null
+ terminator.
+
+ pszSrc - source string
+
+ cchMaxAppend - maximum number of characters to append
+
+Notes:
+ Behavior is undefined if source and destination strings overlap.
+
+ pszDest and pszSrc should not be NULL. See StringCchCatNEx if you require
+ the handling of NULL values.
+
+Return Value:
+
+ S_OK - if all of pszSrc or the first cchMaxAppend characters
+ were concatenated to pszDest and the resultant dest
+ string was null terminated
+
+ failure - you can use the macro HRESULT_CODE() to get a win32
+ error code for all hresult failure cases
+
+ STRSAFE_E_INSUFFICIENT_BUFFER /
+ HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
+ - this return value is an indication that the operation
+ failed due to insufficient space. When this error
+ occurs, the destination buffer is modified to contain
+ a truncated version of the ideal result and is null
+ terminated. This is useful for situations where
+ truncation is ok.
+
+ It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
+ return value of this function
+
+--*/
+
+STRSAFEAPI StringCchCatNA(char* pszDest, size_t cchDest, const char* pszSrc, size_t cchMaxAppend);
+STRSAFEAPI StringCchCatNW(wchar_t* pszDest, size_t cchDest, const wchar_t* pszSrc, size_t cchMaxAppend);
+#ifdef UNICODE
+#define StringCchCatN StringCchCatNW
+#else
+#define StringCchCatN StringCchCatNA
+#endif // !UNICODE
+
+#ifdef STRSAFE_INLINE
+STRSAFEAPI StringCchCatNA(char* pszDest, size_t cchDest, const char* pszSrc, size_t cchMaxAppend)
+{
+ HRESULT hr;
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ hr = StringCatNWorkerA(pszDest, cchDest, pszSrc, cchMaxAppend);
+ }
+
+ return hr;
+}
+
+STRSAFEAPI StringCchCatNW(wchar_t* pszDest, size_t cchDest, const wchar_t* pszSrc, size_t cchMaxAppend)
+{
+ HRESULT hr;
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ hr = StringCatNWorkerW(pszDest, cchDest, pszSrc, cchMaxAppend);
+ }
+
+ return hr;
+}
+#endif // STRSAFE_INLINE
+#endif // !STRSAFE_NO_CCH_FUNCTIONS
+
+
+#ifndef STRSAFE_NO_CB_FUNCTIONS
+/*++
+
+STDAPI
+StringCbCatN(
+ IN OUT LPTSTR pszDest,
+ IN size_t cbDest,
+ IN LPCTSTR pszSrc,
+ IN size_t cbMaxAppend
+ );
+
+Routine Description:
+
+ This routine is a safer version of the C built-in function 'strncat'.
+ The size of the destination buffer (in bytes) is a parameter as well as
+ the maximum number of bytes to append, excluding the null terminator.
+ This function will not write past the end of the destination buffer and it will
+ ALWAYS null terminate pszDest (unless it is zero length).
+
+ This function returns a hresult, and not a pointer. It returns
+ S_OK if all of pszSrc or the first cbMaxAppend bytes were appended
+ to the destination string and it was null terminated, otherwise it will
+ return a failure code. In failure cases as much of pszSrc will be appended
+ to pszDest as possible, and pszDest will be null terminated.
+
+Arguments:
+
+ pszDest - destination string which must be null terminated
+
+ cbDest - size of destination buffer in bytes.
+ length must be ((_tcslen(pszDest) + min(cbMaxAppend / sizeof(TCHAR), _tcslen(pszSrc)) + 1) * sizeof(TCHAR)
+ to hold all of the combine string plus the null
+ terminator.
+
+ pszSrc - source string
+
+ cbMaxAppend - maximum number of bytes to append
+
+Notes:
+ Behavior is undefined if source and destination strings overlap.
+
+ pszDest and pszSrc should not be NULL. See StringCbCatNEx if you require
+ the handling of NULL values.
+
+Return Value:
+
+ S_OK - if all of pszSrc or the first cbMaxAppend bytes were
+ concatenated to pszDest and the resultant dest string
+ was null terminated
+
+ failure - you can use the macro HRESULT_CODE() to get a win32
+ error code for all hresult failure cases
+
+ STRSAFE_E_INSUFFICIENT_BUFFER /
+ HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
+ - this return value is an indication that the operation
+ failed due to insufficient space. When this error
+ occurs, the destination buffer is modified to contain
+ a truncated version of the ideal result and is null
+ terminated. This is useful for situations where
+ truncation is ok.
+
+ It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
+ return value of this function
+
+--*/
+
+STRSAFEAPI StringCbCatNA(char* pszDest, size_t cbDest, const char* pszSrc, size_t cbMaxAppend);
+STRSAFEAPI StringCbCatNW(wchar_t* pszDest, size_t cbDest, const wchar_t* pszSrc, size_t cbMaxAppend);
+#ifdef UNICODE
+#define StringCbCatN StringCbCatNW
+#else
+#define StringCbCatN StringCbCatNA
+#endif // !UNICODE
+
+#ifdef STRSAFE_INLINE
+STRSAFEAPI StringCbCatNA(char* pszDest, size_t cbDest, const char* pszSrc, size_t cbMaxAppend)
+{
+ HRESULT hr;
+ size_t cchDest;
+
+ cchDest = cbDest / sizeof(char);
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ size_t cchMaxAppend;
+
+ cchMaxAppend = cbMaxAppend / sizeof(char);
+
+ hr = StringCatNWorkerA(pszDest, cchDest, pszSrc, cchMaxAppend);
+ }
+
+ return hr;
+}
+
+STRSAFEAPI StringCbCatNW(wchar_t* pszDest, size_t cbDest, const wchar_t* pszSrc, size_t cbMaxAppend)
+{
+ HRESULT hr;
+ size_t cchDest;
+
+ cchDest = cbDest / sizeof(wchar_t);
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ size_t cchMaxAppend;
+
+ cchMaxAppend = cbMaxAppend / sizeof(wchar_t);
+
+ hr = StringCatNWorkerW(pszDest, cchDest, pszSrc, cchMaxAppend);
+ }
+
+ return hr;
+}
+#endif // STRSAFE_INLINE
+#endif // !STRSAFE_NO_CB_FUNCTIONS
+
+
+#ifndef STRSAFE_NO_CCH_FUNCTIONS
+/*++
+
+STDAPI
+StringCchCatNEx(
+ IN OUT LPTSTR pszDest OPTIONAL,
+ IN size_t cchDest,
+ IN LPCTSTR pszSrc OPTIONAL,
+ IN size_t cchMaxAppend,
+ OUT LPTSTR* ppszDestEnd OPTIONAL,
+ OUT size_t* pcchRemaining OPTIONAL,
+ IN DWORD dwFlags
+ );
+
+Routine Description:
+
+ This routine is a safer version of the C built-in function 'strncat', with
+ some additional parameters. In addition to functionality provided by
+ StringCchCatN, this routine also returns a pointer to the end of the
+ destination string and the number of characters left in the destination string
+ including the null terminator. The flags parameter allows additional controls.
+
+Arguments:
+
+ pszDest - destination string which must be null terminated
+
+ cchDest - size of destination buffer in characters.
+ length must be (_tcslen(pszDest) + min(cchMaxAppend, _tcslen(pszSrc)) + 1)
+ to hold all of the combine string plus the null
+ terminator.
+
+ pszSrc - source string
+
+ cchMaxAppend - maximum number of characters to append
+
+ ppszDestEnd - if ppszDestEnd is non-null, the function will return a
+ pointer to the end of the destination string. If the
+ function appended any data, the result will point to the
+ null termination character
+
+ pcchRemaining - if pcchRemaining is non-null, the function will return the
+ number of characters left in the destination string,
+ including the null terminator
+
+ dwFlags - controls some details of the string copy:
+
+ STRSAFE_FILL_BEHIND_NULL
+ if the function succeeds, the low byte of dwFlags will be
+ used to fill the uninitialize part of destination buffer
+ behind the null terminator
+
+ STRSAFE_IGNORE_NULLS
+ treat NULL string pointers like empty strings (TEXT(""))
+
+ STRSAFE_FILL_ON_FAILURE
+ if the function fails, the low byte of dwFlags will be
+ used to fill all of the destination buffer, and it will
+ be null terminated. This will overwrite any pre-existing
+ or truncated string
+
+ STRSAFE_NULL_ON_FAILURE
+ if the function fails, the destination buffer will be set
+ to the empty string. This will overwrite any pre-existing or
+ truncated string
+
+ STRSAFE_NO_TRUNCATION
+ if the function returns STRSAFE_E_INSUFFICIENT_BUFFER, pszDest
+ will not contain a truncated string, it will remain unchanged.
+
+Notes:
+ Behavior is undefined if source and destination strings overlap.
+
+ pszDest and pszSrc should not be NULL unless the STRSAFE_IGNORE_NULLS flag
+ is specified. If STRSAFE_IGNORE_NULLS is passed, both pszDest and pszSrc
+ may be NULL. An error may still be returned even though NULLS are ignored
+ due to insufficient space.
+
+Return Value:
+
+ S_OK - if all of pszSrc or the first cchMaxAppend characters
+ were concatenated to pszDest and the resultant dest
+ string was null terminated
+
+ failure - you can use the macro HRESULT_CODE() to get a win32
+ error code for all hresult failure cases
+
+ STRSAFE_E_INSUFFICIENT_BUFFER /
+ HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
+ - this return value is an indication that the operation
+ failed due to insufficient space. When this error
+ occurs, the destination buffer is modified to contain
+ a truncated version of the ideal result and is null
+ terminated. This is useful for situations where
+ truncation is ok.
+
+ It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
+ return value of this function
+
+--*/
+
+STRSAFEAPI StringCchCatNExA(char* pszDest, size_t cchDest, const char* pszSrc, size_t cchMaxAppend, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags);
+STRSAFEAPI StringCchCatNExW(wchar_t* pszDest, size_t cchDest, const wchar_t* pszSrc, size_t cchMaxAppend, wchar_t** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags);
+#ifdef UNICODE
+#define StringCchCatNEx StringCchCatNExW
+#else
+#define StringCchCatNEx StringCchCatNExA
+#endif // !UNICODE
+
+#ifdef STRSAFE_INLINE
+STRSAFEAPI StringCchCatNExA(char* pszDest, size_t cchDest, const char* pszSrc, size_t cchMaxAppend, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags)
+{
+ HRESULT hr;
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ size_t cbDest;
+
+ // safe to multiply cchDest * sizeof(char) since cchDest < STRSAFE_MAX_CCH and sizeof(char) is 1
+ cbDest = cchDest * sizeof(char);
+
+ hr = StringCatNExWorkerA(pszDest, cchDest, cbDest, pszSrc, cchMaxAppend, ppszDestEnd, pcchRemaining, dwFlags);
+ }
+
+ return hr;
+}
+
+STRSAFEAPI StringCchCatNExW(wchar_t* pszDest, size_t cchDest, const wchar_t* pszSrc, size_t cchMaxAppend, wchar_t** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags)
+{
+ HRESULT hr;
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ size_t cbDest;
+
+ // safe to multiply cchDest * sizeof(wchar_t) since cchDest < STRSAFE_MAX_CCH and sizeof(wchar_t) is 2
+ cbDest = cchDest * sizeof(wchar_t);
+
+ hr = StringCatNExWorkerW(pszDest, cchDest, cbDest, pszSrc, cchMaxAppend, ppszDestEnd, pcchRemaining, dwFlags);
+ }
+
+ return hr;
+}
+#endif // STRSAFE_INLINE
+#endif // !STRSAFE_NO_CCH_FUNCTIONS
+
+
+#ifndef STRSAFE_NO_CB_FUNCTIONS
+/*++
+
+STDAPI
+StringCbCatNEx(
+ IN OUT LPTSTR pszDest OPTIONAL,
+ IN size_t cbDest,
+ IN LPCTSTR pszSrc OPTIONAL,
+ IN size_t cbMaxAppend,
+ OUT LPTSTR* ppszDestEnd OPTIONAL,
+ OUT size_t* pcchRemaining OPTIONAL,
+ IN DWORD dwFlags
+ );
+
+Routine Description:
+
+ This routine is a safer version of the C built-in function 'strncat', with
+ some additional parameters. In addition to functionality provided by
+ StringCbCatN, this routine also returns a pointer to the end of the
+ destination string and the number of bytes left in the destination string
+ including the null terminator. The flags parameter allows additional controls.
+
+Arguments:
+
+ pszDest - destination string which must be null terminated
+
+ cbDest - size of destination buffer in bytes.
+ length must be ((_tcslen(pszDest) + min(cbMaxAppend / sizeof(TCHAR), _tcslen(pszSrc)) + 1) * sizeof(TCHAR)
+ to hold all of the combine string plus the null
+ terminator.
+
+ pszSrc - source string
+
+ cbMaxAppend - maximum number of bytes to append
+
+ ppszDestEnd - if ppszDestEnd is non-null, the function will return a
+ pointer to the end of the destination string. If the
+ function appended any data, the result will point to the
+ null termination character
+
+ pcbRemaining - if pcbRemaining is non-null, the function will return the
+ number of bytes left in the destination string,
+ including the null terminator
+
+ dwFlags - controls some details of the string copy:
+
+ STRSAFE_FILL_BEHIND_NULL
+ if the function succeeds, the low byte of dwFlags will be
+ used to fill the uninitialize part of destination buffer
+ behind the null terminator
+
+ STRSAFE_IGNORE_NULLS
+ treat NULL string pointers like empty strings (TEXT(""))
+
+ STRSAFE_FILL_ON_FAILURE
+ if the function fails, the low byte of dwFlags will be
+ used to fill all of the destination buffer, and it will
+ be null terminated. This will overwrite any pre-existing
+ or truncated string
+
+ STRSAFE_NULL_ON_FAILURE
+ if the function fails, the destination buffer will be set
+ to the empty string. This will overwrite any pre-existing or
+ truncated string
+
+ STRSAFE_NO_TRUNCATION
+ if the function returns STRSAFE_E_INSUFFICIENT_BUFFER, pszDest
+ will not contain a truncated string, it will remain unchanged.
+
+Notes:
+ Behavior is undefined if source and destination strings overlap.
+
+ pszDest and pszSrc should not be NULL unless the STRSAFE_IGNORE_NULLS flag
+ is specified. If STRSAFE_IGNORE_NULLS is passed, both pszDest and pszSrc
+ may be NULL. An error may still be returned even though NULLS are ignored
+ due to insufficient space.
+
+Return Value:
+
+ S_OK - if all of pszSrc or the first cbMaxAppend bytes were
+ concatenated to pszDest and the resultant dest string
+ was null terminated
+
+ failure - you can use the macro HRESULT_CODE() to get a win32
+ error code for all hresult failure cases
+
+ STRSAFE_E_INSUFFICIENT_BUFFER /
+ HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
+ - this return value is an indication that the operation
+ failed due to insufficient space. When this error
+ occurs, the destination buffer is modified to contain
+ a truncated version of the ideal result and is null
+ terminated. This is useful for situations where
+ truncation is ok.
+
+ It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
+ return value of this function
+
+--*/
+
+STRSAFEAPI StringCbCatNExA(char* pszDest, size_t cbDest, const char* pszSrc, size_t cbMaxAppend, char** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags);
+STRSAFEAPI StringCbCatNExW(wchar_t* pszDest, size_t cbDest, const wchar_t* pszSrc, size_t cbMaxAppend, wchar_t** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags);
+#ifdef UNICODE
+#define StringCbCatNEx StringCbCatNExW
+#else
+#define StringCbCatNEx StringCbCatNExA
+#endif // !UNICODE
+
+#ifdef STRSAFE_INLINE
+STRSAFEAPI StringCbCatNExA(char* pszDest, size_t cbDest, const char* pszSrc, size_t cbMaxAppend, char** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags)
+{
+ HRESULT hr;
+ size_t cchDest;
+ size_t cchRemaining = 0;
+
+ cchDest = cbDest / sizeof(char);
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ size_t cchMaxAppend;
+
+ cchMaxAppend = cbMaxAppend / sizeof(char);
+
+ hr = StringCatNExWorkerA(pszDest, cchDest, cbDest, pszSrc, cchMaxAppend, ppszDestEnd, &cchRemaining, dwFlags);
+ }
+
+ if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
+ {
+ if (pcbRemaining)
+ {
+ // safe to multiply cchRemaining * sizeof(char) since cchRemaining < STRSAFE_MAX_CCH and sizeof(char) is 1
+ *pcbRemaining = (cchRemaining * sizeof(char)) + (cbDest % sizeof(char));
+ }
+ }
+
+ return hr;
+}
+
+STRSAFEAPI StringCbCatNExW(wchar_t* pszDest, size_t cbDest, const wchar_t* pszSrc, size_t cbMaxAppend, wchar_t** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags)
+{
+ HRESULT hr;
+ size_t cchDest;
+ size_t cchRemaining = 0;
+
+ cchDest = cbDest / sizeof(wchar_t);
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ size_t cchMaxAppend;
+
+ cchMaxAppend = cbMaxAppend / sizeof(wchar_t);
+
+ hr = StringCatNExWorkerW(pszDest, cchDest, cbDest, pszSrc, cchMaxAppend, ppszDestEnd, &cchRemaining, dwFlags);
+ }
+
+ if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
+ {
+ if (pcbRemaining)
+ {
+ // safe to multiply cchRemaining * sizeof(wchar_t) since cchRemaining < STRSAFE_MAX_CCH and sizeof(wchar_t) is 2
+ *pcbRemaining = (cchRemaining * sizeof(wchar_t)) + (cbDest % sizeof(wchar_t));
+ }
+ }
+
+ return hr;
+}
+#endif // STRSAFE_INLINE
+#endif // !STRSAFE_NO_CB_FUNCTIONS
+
+
+#ifndef STRSAFE_NO_CCH_FUNCTIONS
+/*++
+
+STDAPI
+StringCchVPrintf(
+ OUT LPTSTR pszDest,
+ IN size_t cchDest,
+ IN LPCTSTR pszFormat,
+ IN va_list argList
+ );
+
+Routine Description:
+
+ This routine is a safer version of the C built-in function 'vsprintf'.
+ The size of the destination buffer (in characters) is a parameter and
+ this function will not write past the end of this buffer and it will
+ ALWAYS null terminate the destination buffer (unless it is zero length).
+
+ This function returns a hresult, and not a pointer. It returns
+ S_OK if the string was printed without truncation and null terminated,
+ otherwise it will return a failure code. In failure cases it will return
+ a truncated version of the ideal result.
+
+Arguments:
+
+ pszDest - destination string
+
+ cchDest - size of destination buffer in characters
+ length must be sufficient to hold the resulting formatted
+ string, including the null terminator.
+
+ pszFormat - format string which must be null terminated
+
+ argList - va_list from the variable arguments according to the
+ stdarg.h convention
+
+Notes:
+ Behavior is undefined if destination, format strings or any arguments
+ strings overlap.
+
+ pszDest and pszFormat should not be NULL. See StringCchVPrintfEx if you
+ require the handling of NULL values.
+
+Return Value:
+
+ S_OK - if there was sufficient space in the dest buffer for
+ the resultant string and it was null terminated.
+
+ failure - you can use the macro HRESULT_CODE() to get a win32
+ error code for all hresult failure cases
+
+ STRSAFE_E_INSUFFICIENT_BUFFER /
+ HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
+ - this return value is an indication that the print
+ operation failed due to insufficient space. When this
+ error occurs, the destination buffer is modified to
+ contain a truncated version of the ideal result and is
+ null terminated. This is useful for situations where
+ truncation is ok.
+
+ It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
+ return value of this function
+
+--*/
+
+STRSAFEAPI StringCchVPrintfA(char* pszDest, size_t cchDest, const char* pszFormat, va_list argList);
+STRSAFEAPI StringCchVPrintfW(wchar_t* pszDest, size_t cchDest, const wchar_t* pszFormat, va_list argList);
+#ifdef UNICODE
+#define StringCchVPrintf StringCchVPrintfW
+#else
+#define StringCchVPrintf StringCchVPrintfA
+#endif // !UNICODE
+
+#ifdef STRSAFE_INLINE
+STRSAFEAPI StringCchVPrintfA(char* pszDest, size_t cchDest, const char* pszFormat, va_list argList)
+{
+ HRESULT hr;
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ hr = StringVPrintfWorkerA(pszDest, cchDest, pszFormat, argList);
+ }
+
+ return hr;
+}
+
+#ifdef _WIN32 // TODO: benski> port to BSD
+STRSAFEAPI StringCchVPrintfW(wchar_t* pszDest, size_t cchDest, const wchar_t* pszFormat, va_list argList)
+{
+ HRESULT hr;
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ hr = StringVPrintfWorkerW(pszDest, cchDest, pszFormat, argList);
+ }
+
+ return hr;
+}
+#endif
+
+#endif // STRSAFE_INLINE
+#endif // !STRSAFE_NO_CCH_FUNCTIONS
+
+
+#ifndef STRSAFE_NO_CB_FUNCTIONS
+/*++
+
+STDAPI
+StringCbVPrintf(
+ OUT LPTSTR pszDest,
+ IN size_t cbDest,
+ IN LPCTSTR pszFormat,
+ IN va_list argList
+ );
+
+Routine Description:
+
+ This routine is a safer version of the C built-in function 'vsprintf'.
+ The size of the destination buffer (in bytes) is a parameter and
+ this function will not write past the end of this buffer and it will
+ ALWAYS null terminate the destination buffer (unless it is zero length).
+
+ This function returns a hresult, and not a pointer. It returns
+ S_OK if the string was printed without truncation and null terminated,
+ otherwise it will return a failure code. In failure cases it will return
+ a truncated version of the ideal result.
+
+Arguments:
+
+ pszDest - destination string
+
+ cbDest - size of destination buffer in bytes
+ length must be sufficient to hold the resulting formatted
+ string, including the null terminator.
+
+ pszFormat - format string which must be null terminated
+
+ argList - va_list from the variable arguments according to the
+ stdarg.h convention
+
+Notes:
+ Behavior is undefined if destination, format strings or any arguments
+ strings overlap.
+
+ pszDest and pszFormat should not be NULL. See StringCbVPrintfEx if you
+ require the handling of NULL values.
+
+
+Return Value:
+
+ S_OK - if there was sufficient space in the dest buffer for
+ the resultant string and it was null terminated.
+
+ failure - you can use the macro HRESULT_CODE() to get a win32
+ error code for all hresult failure cases
+
+ STRSAFE_E_INSUFFICIENT_BUFFER /
+ HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
+ - this return value is an indication that the print
+ operation failed due to insufficient space. When this
+ error occurs, the destination buffer is modified to
+ contain a truncated version of the ideal result and is
+ null terminated. This is useful for situations where
+ truncation is ok.
+
+ It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
+ return value of this function
+
+--*/
+
+STRSAFEAPI StringCbVPrintfA(char* pszDest, size_t cbDest, const char* pszFormat, va_list argList);
+STRSAFEAPI StringCbVPrintfW(wchar_t* pszDest, size_t cbDest, const wchar_t* pszFormat, va_list argList);
+#ifdef UNICODE
+#define StringCbVPrintf StringCbVPrintfW
+#else
+#define StringCbVPrintf StringCbVPrintfA
+#endif // !UNICODE
+
+#ifdef STRSAFE_INLINE
+STRSAFEAPI StringCbVPrintfA(char* pszDest, size_t cbDest, const char* pszFormat, va_list argList)
+{
+ HRESULT hr;
+ size_t cchDest;
+
+ cchDest = cbDest / sizeof(char);
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ hr = StringVPrintfWorkerA(pszDest, cchDest, pszFormat, argList);
+ }
+
+ return hr;
+}
+
+#ifdef _WIN32 // TODO: benski> port to BSD
+STRSAFEAPI StringCbVPrintfW(wchar_t* pszDest, size_t cbDest, const wchar_t* pszFormat, va_list argList)
+{
+ HRESULT hr;
+ size_t cchDest;
+
+ cchDest = cbDest / sizeof(wchar_t);
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ hr = StringVPrintfWorkerW(pszDest, cchDest, pszFormat, argList);
+ }
+
+ return hr;
+}
+#endif
+
+#endif // STRSAFE_INLINE
+#endif // !STRSAFE_NO_CB_FUNCTIONS
+
+
+#ifndef STRSAFE_NO_CCH_FUNCTIONS
+/*++
+
+STDAPI
+StringCchPrintf(
+ OUT LPTSTR pszDest,
+ IN size_t cchDest,
+ IN LPCTSTR pszFormat,
+ ...
+ );
+
+Routine Description:
+
+ This routine is a safer version of the C built-in function 'sprintf'.
+ The size of the destination buffer (in characters) is a parameter and
+ this function will not write past the end of this buffer and it will
+ ALWAYS null terminate the destination buffer (unless it is zero length).
+
+ This function returns a hresult, and not a pointer. It returns
+ S_OK if the string was printed without truncation and null terminated,
+ otherwise it will return a failure code. In failure cases it will return
+ a truncated version of the ideal result.
+
+Arguments:
+
+ pszDest - destination string
+
+ cchDest - size of destination buffer in characters
+ length must be sufficient to hold the resulting formatted
+ string, including the null terminator.
+
+ pszFormat - format string which must be null terminated
+
+ ... - additional parameters to be formatted according to
+ the format string
+
+Notes:
+ Behavior is undefined if destination, format strings or any arguments
+ strings overlap.
+
+ pszDest and pszFormat should not be NULL. See StringCchPrintfEx if you
+ require the handling of NULL values.
+
+Return Value:
+
+ S_OK - if there was sufficient space in the dest buffer for
+ the resultant string and it was null terminated.
+
+ failure - you can use the macro HRESULT_CODE() to get a win32
+ error code for all hresult failure cases
+
+ STRSAFE_E_INSUFFICIENT_BUFFER /
+ HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
+ - this return value is an indication that the print
+ operation failed due to insufficient space. When this
+ error occurs, the destination buffer is modified to
+ contain a truncated version of the ideal result and is
+ null terminated. This is useful for situations where
+ truncation is ok.
+
+ It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
+ return value of this function
+
+--*/
+
+STRSAFEAPI StringCchPrintfA(char* pszDest, size_t cchDest, const char* pszFormat, ...);
+STRSAFEAPI StringCchPrintfW(wchar_t* pszDest, size_t cchDest, const wchar_t* pszFormat, ...);
+#ifdef UNICODE
+#define StringCchPrintf StringCchPrintfW
+#else
+#define StringCchPrintf StringCchPrintfA
+#endif // !UNICODE
+
+#ifdef STRSAFE_INLINE
+STRSAFEAPI StringCchPrintfA(char* pszDest, size_t cchDest, const char* pszFormat, ...)
+{
+ HRESULT hr;
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ va_list argList;
+
+ va_start(argList, pszFormat);
+
+ hr = StringVPrintfWorkerA(pszDest, cchDest, pszFormat, argList);
+
+ va_end(argList);
+ }
+
+ return hr;
+}
+
+#ifdef _WIN32 // TODO: benski> port to BSD
+STRSAFEAPI StringCchPrintfW(wchar_t* pszDest, size_t cchDest, const wchar_t* pszFormat, ...)
+{
+ HRESULT hr;
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ va_list argList;
+
+ va_start(argList, pszFormat);
+
+ hr = StringVPrintfWorkerW(pszDest, cchDest, pszFormat, argList);
+
+ va_end(argList);
+ }
+
+ return hr;
+}
+#endif
+#endif // STRSAFE_INLINE
+#endif // !STRSAFE_NO_CCH_FUNCTIONS
+
+
+#ifndef STRSAFE_NO_CB_FUNCTIONS
+/*++
+
+STDAPI
+StringCbPrintf(
+ OUT LPTSTR pszDest,
+ IN size_t cbDest,
+ IN LPCTSTR pszFormat,
+ ...
+ );
+
+Routine Description:
+
+ This routine is a safer version of the C built-in function 'sprintf'.
+ The size of the destination buffer (in bytes) is a parameter and
+ this function will not write past the end of this buffer and it will
+ ALWAYS null terminate the destination buffer (unless it is zero length).
+
+ This function returns a hresult, and not a pointer. It returns
+ S_OK if the string was printed without truncation and null terminated,
+ otherwise it will return a failure code. In failure cases it will return
+ a truncated version of the ideal result.
+
+Arguments:
+
+ pszDest - destination string
+
+ cbDest - size of destination buffer in bytes
+ length must be sufficient to hold the resulting formatted
+ string, including the null terminator.
+
+ pszFormat - format string which must be null terminated
+
+ ... - additional parameters to be formatted according to
+ the format string
+
+Notes:
+ Behavior is undefined if destination, format strings or any arguments
+ strings overlap.
+
+ pszDest and pszFormat should not be NULL. See StringCbPrintfEx if you
+ require the handling of NULL values.
+
+
+Return Value:
+
+ S_OK - if there was sufficient space in the dest buffer for
+ the resultant string and it was null terminated.
+
+ failure - you can use the macro HRESULT_CODE() to get a win32
+ error code for all hresult failure cases
+
+ STRSAFE_E_INSUFFICIENT_BUFFER /
+ HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
+ - this return value is an indication that the print
+ operation failed due to insufficient space. When this
+ error occurs, the destination buffer is modified to
+ contain a truncated version of the ideal result and is
+ null terminated. This is useful for situations where
+ truncation is ok.
+
+ It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
+ return value of this function
+
+--*/
+
+STRSAFEAPI StringCbPrintfA(char* pszDest, size_t cbDest, const char* pszFormat, ...);
+STRSAFEAPI StringCbPrintfW(wchar_t* pszDest, size_t cbDest, const wchar_t* pszFormat, ...);
+#ifdef UNICODE
+#define StringCbPrintf StringCbPrintfW
+#else
+#define StringCbPrintf StringCbPrintfA
+#endif // !UNICODE
+
+#ifdef STRSAFE_INLINE
+STRSAFEAPI StringCbPrintfA(char* pszDest, size_t cbDest, const char* pszFormat, ...)
+{
+ HRESULT hr;
+ size_t cchDest;
+
+ cchDest = cbDest / sizeof(char);
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ va_list argList;
+
+ va_start(argList, pszFormat);
+
+ hr = StringVPrintfWorkerA(pszDest, cchDest, pszFormat, argList);
+
+ va_end(argList);
+ }
+
+ return hr;
+}
+
+#ifdef _WIN32 // TODO: benski> port to BSD
+STRSAFEAPI StringCbPrintfW(wchar_t* pszDest, size_t cbDest, const wchar_t* pszFormat, ...)
+{
+ HRESULT hr;
+ size_t cchDest;
+
+ cchDest = cbDest / sizeof(wchar_t);
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ va_list argList;
+
+ va_start(argList, pszFormat);
+
+ hr = StringVPrintfWorkerW(pszDest, cchDest, pszFormat, argList);
+
+ va_end(argList);
+ }
+
+ return hr;
+}
+#endif
+#endif // STRSAFE_INLINE
+#endif // !STRSAFE_NO_CB_FUNCTIONS
+
+
+#ifndef STRSAFE_NO_CCH_FUNCTIONS
+/*++
+
+STDAPI
+StringCchPrintfEx(
+ OUT LPTSTR pszDest OPTIONAL,
+ IN size_t cchDest,
+ OUT LPTSTR* ppszDestEnd OPTIONAL,
+ OUT size_t* pcchRemaining OPTIONAL,
+ IN DWORD dwFlags,
+ IN LPCTSTR pszFormat OPTIONAL,
+ ...
+ );
+
+Routine Description:
+
+ This routine is a safer version of the C built-in function 'sprintf' with
+ some additional parameters. In addition to functionality provided by
+ StringCchPrintf, this routine also returns a pointer to the end of the
+ destination string and the number of characters left in the destination string
+ including the null terminator. The flags parameter allows additional controls.
+
+Arguments:
+
+ pszDest - destination string
+
+ cchDest - size of destination buffer in characters.
+ length must be sufficient to contain the resulting
+ formatted string plus the null terminator.
+
+ ppszDestEnd - if ppszDestEnd is non-null, the function will return a
+ pointer to the end of the destination string. If the
+ function printed any data, the result will point to the
+ null termination character
+
+ pcchRemaining - if pcchRemaining is non-null, the function will return
+ the number of characters left in the destination string,
+ including the null terminator
+
+ dwFlags - controls some details of the string copy:
+
+ STRSAFE_FILL_BEHIND_NULL
+ if the function succeeds, the low byte of dwFlags will be
+ used to fill the uninitialize part of destination buffer
+ behind the null terminator
+
+ STRSAFE_IGNORE_NULLS
+ treat NULL string pointers like empty strings (TEXT(""))
+
+ STRSAFE_FILL_ON_FAILURE
+ if the function fails, the low byte of dwFlags will be
+ used to fill all of the destination buffer, and it will
+ be null terminated. This will overwrite any truncated
+ string returned when the failure is
+ STRSAFE_E_INSUFFICIENT_BUFFER
+
+ STRSAFE_NO_TRUNCATION /
+ STRSAFE_NULL_ON_FAILURE
+ if the function fails, the destination buffer will be set
+ to the empty string. This will overwrite any truncated string
+ returned when the failure is STRSAFE_E_INSUFFICIENT_BUFFER.
+
+ pszFormat - format string which must be null terminated
+
+ ... - additional parameters to be formatted according to
+ the format string
+
+Notes:
+ Behavior is undefined if destination, format strings or any arguments
+ strings overlap.
+
+ pszDest and pszFormat should not be NULL unless the STRSAFE_IGNORE_NULLS
+ flag is specified. If STRSAFE_IGNORE_NULLS is passed, both pszDest and
+ pszFormat may be NULL. An error may still be returned even though NULLS
+ are ignored due to insufficient space.
+
+Return Value:
+
+ S_OK - if there was source data and it was all concatenated and
+ the resultant dest string was null terminated
+
+ failure - you can use the macro HRESULT_CODE() to get a win32
+ error code for all hresult failure cases
+
+ STRSAFE_E_INSUFFICIENT_BUFFER /
+ HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
+ - this return value is an indication that the print
+ operation failed due to insufficient space. When this
+ error occurs, the destination buffer is modified to
+ contain a truncated version of the ideal result and is
+ null terminated. This is useful for situations where
+ truncation is ok.
+
+ It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
+ return value of this function
+
+--*/
+
+STRSAFEAPI StringCchPrintfExA(char* pszDest, size_t cchDest, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags, const char* pszFormat, ...);
+STRSAFEAPI StringCchPrintfExW(wchar_t* pszDest, size_t cchDest, wchar_t** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags, const wchar_t* pszFormat, ...);
+#ifdef UNICODE
+#define StringCchPrintfEx StringCchPrintfExW
+#else
+#define StringCchPrintfEx StringCchPrintfExA
+#endif // !UNICODE
+
+#ifdef STRSAFE_INLINE
+STRSAFEAPI StringCchPrintfExA(char* pszDest, size_t cchDest, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags, const char* pszFormat, ...)
+{
+ HRESULT hr;
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ size_t cbDest;
+ va_list argList;
+
+ // safe to multiply cchDest * sizeof(char) since cchDest < STRSAFE_MAX_CCH and sizeof(char) is 1
+ cbDest = cchDest * sizeof(char);
+ va_start(argList, pszFormat);
+
+ hr = StringVPrintfExWorkerA(pszDest, cchDest, cbDest, ppszDestEnd, pcchRemaining, dwFlags, pszFormat, argList);
+
+ va_end(argList);
+ }
+
+ return hr;
+}
+
+#ifdef _WIN32 // TODO: benski> port to BSD
+STRSAFEAPI StringCchPrintfExW(wchar_t* pszDest, size_t cchDest, wchar_t** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags, const wchar_t* pszFormat, ...)
+{
+ HRESULT hr;
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ size_t cbDest;
+ va_list argList;
+
+ // safe to multiply cchDest * sizeof(wchar_t) since cchDest < STRSAFE_MAX_CCH and sizeof(wchar_t) is 2
+ cbDest = cchDest * sizeof(wchar_t);
+ va_start(argList, pszFormat);
+
+ hr = StringVPrintfExWorkerW(pszDest, cchDest, cbDest, ppszDestEnd, pcchRemaining, dwFlags, pszFormat, argList);
+
+ va_end(argList);
+ }
+
+ return hr;
+}
+#endif
+#endif // STRSAFE_INLINE
+#endif // !STRSAFE_NO_CCH_FUNCTIONS
+
+
+#ifndef STRSAFE_NO_CB_FUNCTIONS
+/*++
+
+STDAPI
+StringCbPrintfEx(
+ OUT LPTSTR pszDest OPTIONAL,
+ IN size_t cbDest,
+ OUT LPTSTR* ppszDestEnd OPTIONAL,
+ OUT size_t* pcbRemaining OPTIONAL,
+ IN DWORD dwFlags,
+ IN LPCTSTR pszFormat OPTIONAL,
+ ...
+ );
+
+Routine Description:
+
+ This routine is a safer version of the C built-in function 'sprintf' with
+ some additional parameters. In addition to functionality provided by
+ StringCbPrintf, this routine also returns a pointer to the end of the
+ destination string and the number of bytes left in the destination string
+ including the null terminator. The flags parameter allows additional controls.
+
+Arguments:
+
+ pszDest - destination string
+
+ cbDest - size of destination buffer in bytes.
+ length must be sufficient to contain the resulting
+ formatted string plus the null terminator.
+
+ ppszDestEnd - if ppszDestEnd is non-null, the function will return a
+ pointer to the end of the destination string. If the
+ function printed any data, the result will point to the
+ null termination character
+
+ pcbRemaining - if pcbRemaining is non-null, the function will return
+ the number of bytes left in the destination string,
+ including the null terminator
+
+ dwFlags - controls some details of the string copy:
+
+ STRSAFE_FILL_BEHIND_NULL
+ if the function succeeds, the low byte of dwFlags will be
+ used to fill the uninitialize part of destination buffer
+ behind the null terminator
+
+ STRSAFE_IGNORE_NULLS
+ treat NULL string pointers like empty strings (TEXT(""))
+
+ STRSAFE_FILL_ON_FAILURE
+ if the function fails, the low byte of dwFlags will be
+ used to fill all of the destination buffer, and it will
+ be null terminated. This will overwrite any truncated
+ string returned when the failure is
+ STRSAFE_E_INSUFFICIENT_BUFFER
+
+ STRSAFE_NO_TRUNCATION /
+ STRSAFE_NULL_ON_FAILURE
+ if the function fails, the destination buffer will be set
+ to the empty string. This will overwrite any truncated string
+ returned when the failure is STRSAFE_E_INSUFFICIENT_BUFFER.
+
+ pszFormat - format string which must be null terminated
+
+ ... - additional parameters to be formatted according to
+ the format string
+
+Notes:
+ Behavior is undefined if destination, format strings or any arguments
+ strings overlap.
+
+ pszDest and pszFormat should not be NULL unless the STRSAFE_IGNORE_NULLS
+ flag is specified. If STRSAFE_IGNORE_NULLS is passed, both pszDest and
+ pszFormat may be NULL. An error may still be returned even though NULLS
+ are ignored due to insufficient space.
+
+Return Value:
+
+ S_OK - if there was source data and it was all concatenated and
+ the resultant dest string was null terminated
+
+ failure - you can use the macro HRESULT_CODE() to get a win32
+ error code for all hresult failure cases
+
+ STRSAFE_E_INSUFFICIENT_BUFFER /
+ HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
+ - this return value is an indication that the print
+ operation failed due to insufficient space. When this
+ error occurs, the destination buffer is modified to
+ contain a truncated version of the ideal result and is
+ null terminated. This is useful for situations where
+ truncation is ok.
+
+ It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
+ return value of this function
+
+--*/
+
+STRSAFEAPI StringCbPrintfExA(char* pszDest, size_t cbDest, char** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags, const char* pszFormat, ...);
+STRSAFEAPI StringCbPrintfExW(wchar_t* pszDest, size_t cbDest, wchar_t** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags, const wchar_t* pszFormat, ...);
+#ifdef UNICODE
+#define StringCbPrintfEx StringCbPrintfExW
+#else
+#define StringCbPrintfEx StringCbPrintfExA
+#endif // !UNICODE
+
+#ifdef STRSAFE_INLINE
+STRSAFEAPI StringCbPrintfExA(char* pszDest, size_t cbDest, char** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags, const char* pszFormat, ...)
+{
+ HRESULT hr;
+ size_t cchDest;
+ size_t cchRemaining = 0;
+
+ cchDest = cbDest / sizeof(char);
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ va_list argList;
+
+ va_start(argList, pszFormat);
+
+ hr = StringVPrintfExWorkerA(pszDest, cchDest, cbDest, ppszDestEnd, &cchRemaining, dwFlags, pszFormat, argList);
+
+ va_end(argList);
+ }
+
+ if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
+ {
+ if (pcbRemaining)
+ {
+ // safe to multiply cchRemaining * sizeof(char) since cchRemaining < STRSAFE_MAX_CCH and sizeof(char) is 1
+ *pcbRemaining = (cchRemaining * sizeof(char)) + (cbDest % sizeof(char));
+ }
+ }
+
+ return hr;
+}
+
+#ifdef _WIN32 // TODO: benski> port to BSD
+STRSAFEAPI StringCbPrintfExW(wchar_t* pszDest, size_t cbDest, wchar_t** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags, const wchar_t* pszFormat, ...)
+{
+ HRESULT hr;
+ size_t cchDest;
+ size_t cchRemaining = 0;
+
+ cchDest = cbDest / sizeof(wchar_t);
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ va_list argList;
+
+ va_start(argList, pszFormat);
+
+ hr = StringVPrintfExWorkerW(pszDest, cchDest, cbDest, ppszDestEnd, &cchRemaining, dwFlags, pszFormat, argList);
+
+ va_end(argList);
+ }
+
+ if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
+ {
+ if (pcbRemaining)
+ {
+ // safe to multiply cchRemaining * sizeof(wchar_t) since cchRemaining < STRSAFE_MAX_CCH and sizeof(wchar_t) is 2
+ *pcbRemaining = (cchRemaining * sizeof(wchar_t)) + (cbDest % sizeof(wchar_t));
+ }
+ }
+
+ return hr;
+}
+#endif
+#endif // STRSAFE_INLINE
+#endif // !STRSAFE_NO_CB_FUNCTIONS
+
+
+#ifndef STRSAFE_NO_CCH_FUNCTIONS
+/*++
+
+STDAPI
+StringCchVPrintfEx(
+ OUT LPTSTR pszDest OPTIONAL,
+ IN size_t cchDest,
+ OUT LPTSTR* ppszDestEnd OPTIONAL,
+ OUT size_t* pcchRemaining OPTIONAL,
+ IN DWORD dwFlags,
+ IN LPCTSTR pszFormat OPTIONAL,
+ IN va_list argList
+ );
+
+Routine Description:
+
+ This routine is a safer version of the C built-in function 'vsprintf' with
+ some additional parameters. In addition to functionality provided by
+ StringCchVPrintf, this routine also returns a pointer to the end of the
+ destination string and the number of characters left in the destination string
+ including the null terminator. The flags parameter allows additional controls.
+
+Arguments:
+
+ pszDest - destination string
+
+ cchDest - size of destination buffer in characters.
+ length must be sufficient to contain the resulting
+ formatted string plus the null terminator.
+
+ ppszDestEnd - if ppszDestEnd is non-null, the function will return a
+ pointer to the end of the destination string. If the
+ function printed any data, the result will point to the
+ null termination character
+
+ pcchRemaining - if pcchRemaining is non-null, the function will return
+ the number of characters left in the destination string,
+ including the null terminator
+
+ dwFlags - controls some details of the string copy:
+
+ STRSAFE_FILL_BEHIND_NULL
+ if the function succeeds, the low byte of dwFlags will be
+ used to fill the uninitialize part of destination buffer
+ behind the null terminator
+
+ STRSAFE_IGNORE_NULLS
+ treat NULL string pointers like empty strings (TEXT(""))
+
+ STRSAFE_FILL_ON_FAILURE
+ if the function fails, the low byte of dwFlags will be
+ used to fill all of the destination buffer, and it will
+ be null terminated. This will overwrite any truncated
+ string returned when the failure is
+ STRSAFE_E_INSUFFICIENT_BUFFER
+
+ STRSAFE_NO_TRUNCATION /
+ STRSAFE_NULL_ON_FAILURE
+ if the function fails, the destination buffer will be set
+ to the empty string. This will overwrite any truncated string
+ returned when the failure is STRSAFE_E_INSUFFICIENT_BUFFER.
+
+ pszFormat - format string which must be null terminated
+
+ argList - va_list from the variable arguments according to the
+ stdarg.h convention
+
+Notes:
+ Behavior is undefined if destination, format strings or any arguments
+ strings overlap.
+
+ pszDest and pszFormat should not be NULL unless the STRSAFE_IGNORE_NULLS
+ flag is specified. If STRSAFE_IGNORE_NULLS is passed, both pszDest and
+ pszFormat may be NULL. An error may still be returned even though NULLS
+ are ignored due to insufficient space.
+
+Return Value:
+
+ S_OK - if there was source data and it was all concatenated and
+ the resultant dest string was null terminated
+
+ failure - you can use the macro HRESULT_CODE() to get a win32
+ error code for all hresult failure cases
+
+ STRSAFE_E_INSUFFICIENT_BUFFER /
+ HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
+ - this return value is an indication that the print
+ operation failed due to insufficient space. When this
+ error occurs, the destination buffer is modified to
+ contain a truncated version of the ideal result and is
+ null terminated. This is useful for situations where
+ truncation is ok.
+
+ It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
+ return value of this function
+
+--*/
+
+STRSAFEAPI StringCchVPrintfExA(char* pszDest, size_t cchDest, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags, const char* pszFormat, va_list argList);
+STRSAFEAPI StringCchVPrintfExW(wchar_t* pszDest, size_t cchDest, wchar_t** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags, const wchar_t* pszFormat, va_list argList);
+#ifdef UNICODE
+#define StringCchVPrintfEx StringCchVPrintfExW
+#else
+#define StringCchVPrintfEx StringCchVPrintfExA
+#endif // !UNICODE
+
+#ifdef STRSAFE_INLINE
+STRSAFEAPI StringCchVPrintfExA(char* pszDest, size_t cchDest, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags, const char* pszFormat, va_list argList)
+{
+ HRESULT hr;
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ size_t cbDest;
+
+ // safe to multiply cchDest * sizeof(char) since cchDest < STRSAFE_MAX_CCH and sizeof(char) is 1
+ cbDest = cchDest * sizeof(char);
+
+ hr = StringVPrintfExWorkerA(pszDest, cchDest, cbDest, ppszDestEnd, pcchRemaining, dwFlags, pszFormat, argList);
+ }
+
+ return hr;
+}
+
+#ifdef _WIN32 // TODO: benski> port to BSD
+STRSAFEAPI StringCchVPrintfExW(wchar_t* pszDest, size_t cchDest, wchar_t** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags, const wchar_t* pszFormat, va_list argList)
+{
+ HRESULT hr;
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ size_t cbDest;
+
+ // safe to multiply cchDest * sizeof(wchar_t) since cchDest < STRSAFE_MAX_CCH and sizeof(wchar_t) is 2
+ cbDest = cchDest * sizeof(wchar_t);
+
+ hr = StringVPrintfExWorkerW(pszDest, cchDest, cbDest, ppszDestEnd, pcchRemaining, dwFlags, pszFormat, argList);
+ }
+
+ return hr;
+}
+#endif
+#endif // STRSAFE_INLINE
+#endif // !STRSAFE_NO_CCH_FUNCTIONS
+
+
+#ifndef STRSAFE_NO_CB_FUNCTIONS
+/*++
+
+STDAPI
+StringCbVPrintfEx(
+ OUT LPTSTR pszDest OPTIONAL,
+ IN size_t cbDest,
+ OUT LPTSTR* ppszDestEnd OPTIONAL,
+ OUT size_t* pcbRemaining OPTIONAL,
+ IN DWORD dwFlags,
+ IN LPCTSTR pszFormat OPTIONAL,
+ IN va_list argList
+ );
+
+Routine Description:
+
+ This routine is a safer version of the C built-in function 'vsprintf' with
+ some additional parameters. In addition to functionality provided by
+ StringCbVPrintf, this routine also returns a pointer to the end of the
+ destination string and the number of characters left in the destination string
+ including the null terminator. The flags parameter allows additional controls.
+
+Arguments:
+
+ pszDest - destination string
+
+ cbDest - size of destination buffer in bytes.
+ length must be sufficient to contain the resulting
+ formatted string plus the null terminator.
+
+ ppszDestEnd - if ppszDestEnd is non-null, the function will return
+ a pointer to the end of the destination string. If the
+ function printed any data, the result will point to the
+ null termination character
+
+ pcbRemaining - if pcbRemaining is non-null, the function will return
+ the number of bytes left in the destination string,
+ including the null terminator
+
+ dwFlags - controls some details of the string copy:
+
+ STRSAFE_FILL_BEHIND_NULL
+ if the function succeeds, the low byte of dwFlags will be
+ used to fill the uninitialize part of destination buffer
+ behind the null terminator
+
+ STRSAFE_IGNORE_NULLS
+ treat NULL string pointers like empty strings (TEXT(""))
+
+ STRSAFE_FILL_ON_FAILURE
+ if the function fails, the low byte of dwFlags will be
+ used to fill all of the destination buffer, and it will
+ be null terminated. This will overwrite any truncated
+ string returned when the failure is
+ STRSAFE_E_INSUFFICIENT_BUFFER
+
+ STRSAFE_NO_TRUNCATION /
+ STRSAFE_NULL_ON_FAILURE
+ if the function fails, the destination buffer will be set
+ to the empty string. This will overwrite any truncated string
+ returned when the failure is STRSAFE_E_INSUFFICIENT_BUFFER.
+
+ pszFormat - format string which must be null terminated
+
+ argList - va_list from the variable arguments according to the
+ stdarg.h convention
+
+Notes:
+ Behavior is undefined if destination, format strings or any arguments
+ strings overlap.
+
+ pszDest and pszFormat should not be NULL unless the STRSAFE_IGNORE_NULLS
+ flag is specified. If STRSAFE_IGNORE_NULLS is passed, both pszDest and
+ pszFormat may be NULL. An error may still be returned even though NULLS
+ are ignored due to insufficient space.
+
+Return Value:
+
+ S_OK - if there was source data and it was all concatenated and
+ the resultant dest string was null terminated
+
+ failure - you can use the macro HRESULT_CODE() to get a win32
+ error code for all hresult failure cases
+
+ STRSAFE_E_INSUFFICIENT_BUFFER /
+ HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
+ - this return value is an indication that the print
+ operation failed due to insufficient space. When this
+ error occurs, the destination buffer is modified to
+ contain a truncated version of the ideal result and is
+ null terminated. This is useful for situations where
+ truncation is ok.
+
+ It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
+ return value of this function
+
+--*/
+
+STRSAFEAPI StringCbVPrintfExA(char* pszDest, size_t cbDest, char** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags, const char* pszFormat, va_list argList);
+STRSAFEAPI StringCbVPrintfExW(wchar_t* pszDest, size_t cbDest, wchar_t** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags, const wchar_t* pszFormat, va_list argList);
+#ifdef UNICODE
+#define StringCbVPrintfEx StringCbVPrintfExW
+#else
+#define StringCbVPrintfEx StringCbVPrintfExA
+#endif // !UNICODE
+
+#ifdef STRSAFE_INLINE
+STRSAFEAPI StringCbVPrintfExA(char* pszDest, size_t cbDest, char** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags, const char* pszFormat, va_list argList)
+{
+ HRESULT hr;
+ size_t cchDest;
+ size_t cchRemaining = 0;
+
+ cchDest = cbDest / sizeof(char);
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ hr = StringVPrintfExWorkerA(pszDest, cchDest, cbDest, ppszDestEnd, &cchRemaining, dwFlags, pszFormat, argList);
+ }
+
+ if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
+ {
+ if (pcbRemaining)
+ {
+ // safe to multiply cchRemaining * sizeof(char) since cchRemaining < STRSAFE_MAX_CCH and sizeof(char) is 1
+ *pcbRemaining = (cchRemaining * sizeof(char)) + (cbDest % sizeof(char));
+ }
+ }
+
+ return hr;
+}
+
+#ifdef _WIN32 // TODO: benski> port to BSD
+STRSAFEAPI StringCbVPrintfExW(wchar_t* pszDest, size_t cbDest, wchar_t** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags, const wchar_t* pszFormat, va_list argList)
+{
+ HRESULT hr;
+ size_t cchDest;
+ size_t cchRemaining = 0;
+
+ cchDest = cbDest / sizeof(wchar_t);
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ hr = StringVPrintfExWorkerW(pszDest, cchDest, cbDest, ppszDestEnd, &cchRemaining, dwFlags, pszFormat, argList);
+ }
+
+ if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
+ {
+ if (pcbRemaining)
+ {
+ // safe to multiply cchRemaining * sizeof(wchar_t) since cchRemaining < STRSAFE_MAX_CCH and sizeof(wchar_t) is 2
+ *pcbRemaining = (cchRemaining * sizeof(wchar_t)) + (cbDest % sizeof(wchar_t));
+ }
+ }
+
+ return hr;
+}
+#endif
+#endif // STRSAFE_INLINE
+#endif // !STRSAFE_NO_CB_FUNCTIONS
+
+
+#ifndef STRSAFE_NO_CCH_FUNCTIONS
+/*++
+
+STDAPI
+StringCchGets(
+ OUT LPTSTR pszDest,
+ IN size_t cchDest
+ );
+
+Routine Description:
+
+ This routine is a safer version of the C built-in function 'gets'.
+ The size of the destination buffer (in characters) is a parameter and
+ this function will not write past the end of this buffer and it will
+ ALWAYS null terminate the destination buffer (unless it is zero length).
+
+ This routine is not a replacement for fgets. That function does not replace
+ newline characters with a null terminator.
+
+ This function returns a hresult, and not a pointer. It returns
+ S_OK if any characters were read from stdin and copied to pszDest and
+ pszDest was null terminated, otherwise it will return a failure code.
+
+Arguments:
+
+ pszDest - destination string
+
+ cchDest - size of destination buffer in characters.
+
+Notes:
+ pszDest should not be NULL. See StringCchGetsEx if you require the handling
+ of NULL values.
+
+ cchDest must be > 1 for this function to succeed.
+
+Return Value:
+
+ S_OK - data was read from stdin and copied, and the resultant
+ dest string was null terminated
+
+ failure - you can use the macro HRESULT_CODE() to get a win32
+ error code for all hresult failure cases
+
+ STRSAFE_E_END_OF_FILE /
+ HRESULT_CODE(hr) == ERROR_HANDLE_EOF
+ - this return value indicates an error or end-of-file
+ condition, use feof or ferror to determine which one has
+ occured.
+
+ STRSAFE_E_INSUFFICIENT_BUFFER /
+ HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
+ - this return value is an indication that there was
+ insufficient space in the destination buffer to copy any
+ data
+
+ It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
+ return value of this function.
+
+--*/
+
+#ifndef STRSAFE_LIB_IMPL
+STRSAFE_INLINE_API StringCchGetsA(char* pszDest, size_t cchDest);
+STRSAFE_INLINE_API StringCchGetsW(wchar_t* pszDest, size_t cchDest);
+#ifdef UNICODE
+#define StringCchGets StringCchGetsW
+#else
+#define StringCchGets StringCchGetsA
+#endif // !UNICODE
+
+STRSAFE_INLINE_API StringCchGetsA(char* pszDest, size_t cchDest)
+{
+ HRESULT hr;
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ size_t cbDest;
+
+ // safe to multiply cchDest * sizeof(char) since cchDest < STRSAFE_MAX_CCH and sizeof(char) is 1
+ cbDest = cchDest * sizeof(char);
+
+ hr = StringGetsExWorkerA(pszDest, cchDest, cbDest, NULL, NULL, 0);
+ }
+
+ return hr;
+}
+
+STRSAFE_INLINE_API StringCchGetsW(wchar_t* pszDest, size_t cchDest)
+{
+ HRESULT hr;
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ size_t cbDest;
+
+ // safe to multiply cchDest * sizeof(wchar_t) since cchDest < STRSAFE_MAX_CCH and sizeof(wchar_t) is 2
+ cbDest = cchDest * sizeof(wchar_t);
+
+ hr = StringGetsExWorkerW(pszDest, cchDest, cbDest, NULL, NULL, 0);
+ }
+
+ return hr;
+}
+#endif // !STRSAFE_NO_CCH_FUNCTIONS
+#endif // !STRSAFE_LIB_IMPL
+
+#ifndef STRSAFE_NO_CB_FUNCTIONS
+/*++
+
+STDAPI
+StringCbGets(
+ OUT LPTSTR pszDest,
+ IN size_t cbDest
+ );
+
+Routine Description:
+
+ This routine is a safer version of the C built-in function 'gets'.
+ The size of the destination buffer (in bytes) is a parameter and
+ this function will not write past the end of this buffer and it will
+ ALWAYS null terminate the destination buffer (unless it is zero length).
+
+ This routine is not a replacement for fgets. That function does not replace
+ newline characters with a null terminator.
+
+ This function returns a hresult, and not a pointer. It returns
+ S_OK if any characters were read from stdin and copied to pszDest
+ and pszDest was null terminated, otherwise it will return a failure code.
+
+Arguments:
+
+ pszDest - destination string
+
+ cbDest - size of destination buffer in bytes.
+
+Notes:
+ pszDest should not be NULL. See StringCbGetsEx if you require the handling
+ of NULL values.
+
+ cbDest must be > sizeof(TCHAR) for this function to succeed.
+
+Return Value:
+
+ S_OK - data was read from stdin and copied, and the resultant
+ dest string was null terminated
+
+ failure - you can use the macro HRESULT_CODE() to get a win32
+ error code for all hresult failure cases
+
+ STRSAFE_E_END_OF_FILE /
+ HRESULT_CODE(hr) == ERROR_HANDLE_EOF
+ - this return value indicates an error or end-of-file
+ condition, use feof or ferror to determine which one has
+ occured.
+
+ STRSAFE_E_INSUFFICIENT_BUFFER /
+ HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
+ - this return value is an indication that there was
+ insufficient space in the destination buffer to copy any
+ data
+
+ It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
+ return value of this function.
+
+--*/
+
+#ifndef STRSAFE_LIB_IMPL
+STRSAFE_INLINE_API StringCbGetsA(char* pszDest, size_t cbDest);
+STRSAFE_INLINE_API StringCbGetsW(wchar_t* pszDest, size_t cbDest);
+#ifdef UNICODE
+#define StringCbGets StringCbGetsW
+#else
+#define StringCbGets StringCbGetsA
+#endif // !UNICODE
+
+STRSAFE_INLINE_API StringCbGetsA(char* pszDest, size_t cbDest)
+{
+ HRESULT hr;
+ size_t cchDest;
+
+ // convert to count of characters
+ cchDest = cbDest / sizeof(char);
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ hr = StringGetsExWorkerA(pszDest, cchDest, cbDest, NULL, NULL, 0);
+ }
+
+ return hr;
+}
+
+STRSAFE_INLINE_API StringCbGetsW(wchar_t* pszDest, size_t cbDest)
+{
+ HRESULT hr;
+ size_t cchDest;
+
+ // convert to count of characters
+ cchDest = cbDest / sizeof(wchar_t);
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ hr = StringGetsExWorkerW(pszDest, cchDest, cbDest, NULL, NULL, 0);
+ }
+
+ return hr;
+}
+#endif // !STRSAFE_NO_CB_FUNCTIONS
+#endif // !STRSAFE_LIB_IMPL
+
+#ifndef STRSAFE_NO_CCH_FUNCTIONS
+/*++
+
+STDAPI
+StringCchGetsEx(
+ OUT LPTSTR pszDest OPTIONAL,
+ IN size_t cchDest,
+ OUT LPTSTR* ppszDestEnd OPTIONAL,
+ OUT size_t* pcchRemaining OPTIONAL,
+ IN DWORD dwFlags
+ );
+
+Routine Description:
+
+ This routine is a safer version of the C built-in function 'gets' with
+ some additional parameters. In addition to functionality provided by
+ StringCchGets, this routine also returns a pointer to the end of the
+ destination string and the number of characters left in the destination string
+ including the null terminator. The flags parameter allows additional controls.
+
+Arguments:
+
+ pszDest - destination string
+
+ cchDest - size of destination buffer in characters.
+
+ ppszDestEnd - if ppszDestEnd is non-null, the function will return a
+ pointer to the end of the destination string. If the
+ function copied any data, the result will point to the
+ null termination character
+
+ pcchRemaining - if pcchRemaining is non-null, the function will return the
+ number of characters left in the destination string,
+ including the null terminator
+
+ dwFlags - controls some details of the string copy:
+
+ STRSAFE_FILL_BEHIND_NULL
+ if the function succeeds, the low byte of dwFlags will be
+ used to fill the uninitialize part of destination buffer
+ behind the null terminator
+
+ STRSAFE_IGNORE_NULLS
+ treat NULL string pointers like empty strings (TEXT("")).
+
+ STRSAFE_FILL_ON_FAILURE
+ if the function fails, the low byte of dwFlags will be
+ used to fill all of the destination buffer, and it will
+ be null terminated.
+
+ STRSAFE_NO_TRUNCATION /
+ STRSAFE_NULL_ON_FAILURE
+ if the function fails, the destination buffer will be set
+ to the empty string.
+
+Notes:
+ pszDest should not be NULL unless the STRSAFE_IGNORE_NULLS flag is specified.
+ If STRSAFE_IGNORE_NULLS is passed and pszDest is NULL, an error may still be
+ returned even though NULLS are ignored
+
+ cchDest must be > 1 for this function to succeed.
+
+Return Value:
+
+ S_OK - data was read from stdin and copied, and the resultant
+ dest string was null terminated
+
+ failure - you can use the macro HRESULT_CODE() to get a win32
+ error code for all hresult failure cases
+
+ STRSAFE_E_END_OF_FILE /
+ HRESULT_CODE(hr) == ERROR_HANDLE_EOF
+ - this return value indicates an error or end-of-file
+ condition, use feof or ferror to determine which one has
+ occured.
+
+ STRSAFE_E_INSUFFICIENT_BUFFER /
+ HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
+ - this return value is an indication that there was
+ insufficient space in the destination buffer to copy any
+ data
+
+ It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
+ return value of this function.
+
+--*/
+
+#ifndef STRSAFE_LIB_IMPL
+STRSAFE_INLINE_API StringCchGetsExA(char* pszDest, size_t cchDest, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags);
+STRSAFE_INLINE_API StringCchGetsExW(wchar_t* pszDest, size_t cchDest, wchar_t** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags);
+#ifdef UNICODE
+#define StringCchGetsEx StringCchGetsExW
+#else
+#define StringCchGetsEx StringCchGetsExA
+#endif // !UNICODE
+
+STRSAFE_INLINE_API StringCchGetsExA(char* pszDest, size_t cchDest, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags)
+{
+ HRESULT hr;
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ size_t cbDest;
+
+ // safe to multiply cchDest * sizeof(char) since cchDest < STRSAFE_MAX_CCH and sizeof(char) is 1
+ cbDest = cchDest * sizeof(char);
+
+ hr = StringGetsExWorkerA(pszDest, cchDest, cbDest, ppszDestEnd, pcchRemaining, dwFlags);
+ }
+
+ return hr;
+}
+
+STRSAFE_INLINE_API StringCchGetsExW(wchar_t* pszDest, size_t cchDest, wchar_t** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags)
+{
+ HRESULT hr;
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ size_t cbDest;
+
+ // safe to multiply cchDest * sizeof(wchar_t) since cchDest < STRSAFE_MAX_CCH and sizeof(wchar_t) is 2
+ cbDest = cchDest * sizeof(wchar_t);
+
+ hr = StringGetsExWorkerW(pszDest, cchDest, cbDest, ppszDestEnd, pcchRemaining, dwFlags);
+ }
+
+ return hr;
+}
+#endif // !STRSAFE_NO_CCH_FUNCTIONS
+#endif // !STRSAFE_LIB_IMPL
+
+#ifndef STRSAFE_NO_CB_FUNCTIONS
+/*++
+
+STDAPI
+StringCbGetsEx(
+ OUT LPTSTR pszDest OPTIONAL,
+ IN size_t cbDest,
+ OUT LPTSTR* ppszDestEnd OPTIONAL,
+ OUT size_t* pcbRemaining OPTIONAL,
+ IN DWORD dwFlags
+ );
+
+Routine Description:
+
+ This routine is a safer version of the C built-in function 'gets' with
+ some additional parameters. In addition to functionality provided by
+ StringCbGets, this routine also returns a pointer to the end of the
+ destination string and the number of characters left in the destination string
+ including the null terminator. The flags parameter allows additional controls.
+
+Arguments:
+
+ pszDest - destination string
+
+ cbDest - size of destination buffer in bytes.
+
+ ppszDestEnd - if ppszDestEnd is non-null, the function will return a
+ pointer to the end of the destination string. If the
+ function copied any data, the result will point to the
+ null termination character
+
+ pcbRemaining - if pbRemaining is non-null, the function will return the
+ number of bytes left in the destination string,
+ including the null terminator
+
+ dwFlags - controls some details of the string copy:
+
+ STRSAFE_FILL_BEHIND_NULL
+ if the function succeeds, the low byte of dwFlags will be
+ used to fill the uninitialize part of destination buffer
+ behind the null terminator
+
+ STRSAFE_IGNORE_NULLS
+ treat NULL string pointers like empty strings (TEXT("")).
+
+ STRSAFE_FILL_ON_FAILURE
+ if the function fails, the low byte of dwFlags will be
+ used to fill all of the destination buffer, and it will
+ be null terminated.
+
+ STRSAFE_NO_TRUNCATION /
+ STRSAFE_NULL_ON_FAILURE
+ if the function fails, the destination buffer will be set
+ to the empty string.
+
+Notes:
+ pszDest should not be NULL unless the STRSAFE_IGNORE_NULLS flag is specified.
+ If STRSAFE_IGNORE_NULLS is passed and pszDest is NULL, an error may still be
+ returned even though NULLS are ignored
+
+ cbDest must be > sizeof(TCHAR) for this function to succeed
+
+Return Value:
+
+ S_OK - data was read from stdin and copied, and the resultant
+ dest string was null terminated
+
+ failure - you can use the macro HRESULT_CODE() to get a win32
+ error code for all hresult failure cases
+
+ STRSAFE_E_END_OF_FILE /
+ HRESULT_CODE(hr) == ERROR_HANDLE_EOF
+ - this return value indicates an error or end-of-file
+ condition, use feof or ferror to determine which one has
+ occured.
+
+ STRSAFE_E_INSUFFICIENT_BUFFER /
+ HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
+ - this return value is an indication that there was
+ insufficient space in the destination buffer to copy any
+ data
+
+ It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
+ return value of this function.
+
+--*/
+
+#ifndef STRSAFE_LIB_IMPL
+STRSAFE_INLINE_API StringCbGetsExA(char* pszDest, size_t cbDest, char** ppszDestEnd, size_t* pbRemaining, unsigned long dwFlags);
+STRSAFE_INLINE_API StringCbGetsExW(wchar_t* pszDest, size_t cbDest, wchar_t** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags);
+#ifdef UNICODE
+#define StringCbGetsEx StringCbGetsExW
+#else
+#define StringCbGetsEx StringCbGetsExA
+#endif // !UNICODE
+
+STRSAFE_INLINE_API StringCbGetsExA(char* pszDest, size_t cbDest, char** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags)
+{
+ HRESULT hr;
+ size_t cchDest;
+ size_t cchRemaining = 0;
+
+ cchDest = cbDest / sizeof(char);
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ hr = StringGetsExWorkerA(pszDest, cchDest, cbDest, ppszDestEnd, &cchRemaining, dwFlags);
+ }
+
+ if (SUCCEEDED(hr) ||
+ (hr == STRSAFE_E_INSUFFICIENT_BUFFER) ||
+ (hr == STRSAFE_E_END_OF_FILE))
+ {
+ if (pcbRemaining)
+ {
+ // safe to multiply cchRemaining * sizeof(char) since cchRemaining < STRSAFE_MAX_CCH and sizeof(char) is 1
+ *pcbRemaining = (cchRemaining * sizeof(char)) + (cbDest % sizeof(char));
+ }
+ }
+
+ return hr;
+}
+
+STRSAFE_INLINE_API StringCbGetsExW(wchar_t* pszDest, size_t cbDest, wchar_t** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags)
+{
+ HRESULT hr;
+ size_t cchDest;
+ size_t cchRemaining = 0;
+
+ cchDest = cbDest / sizeof(wchar_t);
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ hr = StringGetsExWorkerW(pszDest, cchDest, cbDest, ppszDestEnd, &cchRemaining, dwFlags);
+ }
+
+ if (SUCCEEDED(hr) ||
+ (hr == STRSAFE_E_INSUFFICIENT_BUFFER) ||
+ (hr == STRSAFE_E_END_OF_FILE))
+ {
+ if (pcbRemaining)
+ {
+ // safe to multiply cchRemaining * sizeof(wchar_t) since cchRemaining < STRSAFE_MAX_CCH and sizeof(wchar_t) is 2
+ *pcbRemaining = (cchRemaining * sizeof(wchar_t)) + (cbDest % sizeof(wchar_t));
+ }
+ }
+
+ return hr;
+}
+#endif // !STRSAFE_NO_CB_FUNCTIONS
+#endif // !STRSAFE_LIB_IMPL
+
+#ifndef STRSAFE_NO_CCH_FUNCTIONS
+/*++
+
+STDAPI
+StringCchLength(
+ IN LPCTSTR psz,
+ IN size_t cchMax,
+ OUT size_t* pcch OPTIONAL
+ );
+
+Routine Description:
+
+ This routine is a safer version of the C built-in function 'strlen'.
+ It is used to make sure a string is not larger than a given length, and
+ it optionally returns the current length in characters not including
+ the null terminator.
+
+ This function returns a hresult, and not a pointer. It returns
+ S_OK if the string is non-null and the length including the null
+ terminator is less than or equal to cchMax characters.
+
+Arguments:
+
+ psz - string to check the length of
+
+ cchMax - maximum number of characters including the null terminator
+ that psz is allowed to contain
+
+ pcch - if the function succeeds and pcch is non-null, the current length
+ in characters of psz excluding the null terminator will be returned.
+ This out parameter is equivalent to the return value of strlen(psz)
+
+Notes:
+ psz can be null but the function will fail
+
+ cchMax should be greater than zero or the function will fail
+
+Return Value:
+
+ S_OK - psz is non-null and the length including the null
+ terminator is less than or equal to cchMax characters
+
+ failure - you can use the macro HRESULT_CODE() to get a win32
+ error code for all hresult failure cases
+
+ It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
+ return value of this function.
+
+--*/
+
+STRSAFEAPI StringCchLengthA(const char* psz, size_t cchMax, size_t* pcch);
+STRSAFEAPI StringCchLengthW(const wchar_t* psz, size_t cchMax, size_t* pcch);
+#ifdef UNICODE
+#define StringCchLength StringCchLengthW
+#else
+#define StringCchLength StringCchLengthA
+#endif // !UNICODE
+
+#ifdef STRSAFE_INLINE
+STRSAFEAPI StringCchLengthA(const char* psz, size_t cchMax, size_t* pcch)
+{
+ HRESULT hr;
+
+ if ((psz == NULL) || (cchMax > STRSAFE_MAX_CCH))
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ hr = StringLengthWorkerA(psz, cchMax, pcch);
+ }
+
+ return hr;
+}
+
+STRSAFEAPI StringCchLengthW(const wchar_t* psz, size_t cchMax, size_t* pcch)
+{
+ HRESULT hr;
+
+ if ((psz == NULL) || (cchMax > STRSAFE_MAX_CCH))
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ hr = StringLengthWorkerW(psz, cchMax, pcch);
+ }
+
+ return hr;
+}
+#endif // STRSAFE_INLINE
+#endif // !STRSAFE_NO_CCH_FUNCTIONS
+
+
+#ifndef STRSAFE_NO_CB_FUNCTIONS
+/*++
+
+STDAPI
+StringCbLength(
+ IN LPCTSTR psz,
+ IN size_t cbMax,
+ OUT size_t* pcb OPTIONAL
+ );
+
+Routine Description:
+
+ This routine is a safer version of the C built-in function 'strlen'.
+ It is used to make sure a string is not larger than a given length, and
+ it optionally returns the current length in bytes not including
+ the null terminator.
+
+ This function returns a hresult, and not a pointer. It returns
+ S_OK if the string is non-null and the length including the null
+ terminator is less than or equal to cbMax bytes.
+
+Arguments:
+
+ psz - string to check the length of
+
+ cbMax - maximum number of bytes including the null terminator
+ that psz is allowed to contain
+
+ pcb - if the function succeeds and pcb is non-null, the current length
+ in bytes of psz excluding the null terminator will be returned.
+ This out parameter is equivalent to the return value of strlen(psz) * sizeof(TCHAR)
+
+Notes:
+ psz can be null but the function will fail
+
+ cbMax should be greater than or equal to sizeof(TCHAR) or the function will fail
+
+Return Value:
+
+ S_OK - psz is non-null and the length including the null
+ terminator is less than or equal to cbMax bytes
+
+ failure - you can use the macro HRESULT_CODE() to get a win32
+ error code for all hresult failure cases
+
+ It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
+ return value of this function.
+
+--*/
+
+STRSAFEAPI StringCbLengthA(const char* psz, size_t cchMax, size_t* pcch);
+STRSAFEAPI StringCbLengthW(const wchar_t* psz, size_t cchMax, size_t* pcch);
+#ifdef UNICODE
+#define StringCbLength StringCbLengthW
+#else
+#define StringCbLength StringCbLengthA
+#endif // !UNICODE
+
+#ifdef STRSAFE_INLINE
+STRSAFEAPI StringCbLengthA(const char* psz, size_t cbMax, size_t* pcb)
+{
+ HRESULT hr;
+ size_t cchMax;
+ size_t cch = 0;
+
+ cchMax = cbMax / sizeof(char);
+
+ if ((psz == NULL) || (cchMax > STRSAFE_MAX_CCH))
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ hr = StringLengthWorkerA(psz, cchMax, &cch);
+ }
+
+ if (SUCCEEDED(hr) && pcb)
+ {
+ // safe to multiply cch * sizeof(char) since cch < STRSAFE_MAX_CCH and sizeof(char) is 1
+ *pcb = cch * sizeof(char);
+ }
+
+ return hr;
+}
+
+STRSAFEAPI StringCbLengthW(const wchar_t* psz, size_t cbMax, size_t* pcb)
+{
+ HRESULT hr;
+ size_t cchMax;
+ size_t cch = 0;
+
+ cchMax = cbMax / sizeof(wchar_t);
+
+ if ((psz == NULL) || (cchMax > STRSAFE_MAX_CCH))
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ hr = StringLengthWorkerW(psz, cchMax, &cch);
+ }
+
+ if (SUCCEEDED(hr) && pcb)
+ {
+ // safe to multiply cch * sizeof(wchar_t) since cch < STRSAFE_MAX_CCH and sizeof(wchar_t) is 2
+ *pcb = cch * sizeof(wchar_t);
+ }
+
+ return hr;
+}
+#endif // STRSAFE_INLINE
+#endif // !STRSAFE_NO_CB_FUNCTIONS
+
+
+// these are the worker functions that actually do the work
+#ifdef STRSAFE_INLINE
+STRSAFEAPI StringCopyWorkerA(char* pszDest, size_t cchDest, const char* pszSrc)
+{
+ HRESULT hr = S_OK;
+
+ if (cchDest == 0)
+ {
+ // can not null terminate a zero-byte dest buffer
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ while (cchDest && (*pszSrc != '\0'))
+ {
+ *pszDest++ = *pszSrc++;
+ cchDest--;
+ }
+
+ if (cchDest == 0)
+ {
+ // we are going to truncate pszDest
+ pszDest--;
+ hr = STRSAFE_E_INSUFFICIENT_BUFFER;
+ }
+
+ *pszDest= '\0';
+ }
+
+ return hr;
+}
+
+STRSAFEAPI StringCopyWorkerW(wchar_t* pszDest, size_t cchDest, const wchar_t* pszSrc)
+{
+ HRESULT hr = S_OK;
+
+ if (cchDest == 0)
+ {
+ // can not null terminate a zero-byte dest buffer
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ while (cchDest && (*pszSrc != L'\0'))
+ {
+ *pszDest++ = *pszSrc++;
+ cchDest--;
+ }
+
+ if (cchDest == 0)
+ {
+ // we are going to truncate pszDest
+ pszDest--;
+ hr = STRSAFE_E_INSUFFICIENT_BUFFER;
+ }
+
+ *pszDest= L'\0';
+ }
+
+ return hr;
+}
+
+STRSAFEAPI StringCopyExWorkerA(char* pszDest, size_t cchDest, size_t cbDest, const char* pszSrc, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags)
+{
+ HRESULT hr = S_OK;
+ char* pszDestEnd = pszDest;
+ size_t cchRemaining = 0;
+
+ // ASSERT(cbDest == (cchDest * sizeof(char)) ||
+ // cbDest == (cchDest * sizeof(char)) + (cbDest % sizeof(char)));
+
+ // only accept valid flags
+ if (dwFlags & (~STRSAFE_VALID_FLAGS))
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ if (dwFlags & STRSAFE_IGNORE_NULLS)
+ {
+ if (pszDest == NULL)
+ {
+ if ((cchDest != 0) || (cbDest != 0))
+ {
+ // NULL pszDest and non-zero cchDest/cbDest is invalid
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ }
+
+ if (pszSrc == NULL)
+ {
+ pszSrc = "";
+ }
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ if (cchDest == 0)
+ {
+ pszDestEnd = pszDest;
+ cchRemaining = 0;
+
+ // only fail if there was actually src data to copy
+ if (*pszSrc != '\0')
+ {
+ if (pszDest == NULL)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ hr = STRSAFE_E_INSUFFICIENT_BUFFER;
+ }
+ }
+ }
+ else
+ {
+ pszDestEnd = pszDest;
+ cchRemaining = cchDest;
+
+ while (cchRemaining && (*pszSrc != '\0'))
+ {
+ *pszDestEnd++= *pszSrc++;
+ cchRemaining--;
+ }
+
+ if (cchRemaining > 0)
+ {
+ if (dwFlags & STRSAFE_FILL_BEHIND_NULL)
+ {
+ memset(pszDestEnd + 1, STRSAFE_GET_FILL_PATTERN(dwFlags), ((cchRemaining - 1) * sizeof(char)) + (cbDest % sizeof(char)));
+ }
+ }
+ else
+ {
+ // we are going to truncate pszDest
+ pszDestEnd--;
+ cchRemaining++;
+
+ hr = STRSAFE_E_INSUFFICIENT_BUFFER;
+ }
+
+ *pszDestEnd = '\0';
+ }
+ }
+ }
+
+ if (FAILED(hr))
+ {
+ if (pszDest)
+ {
+ if (dwFlags & STRSAFE_FILL_ON_FAILURE)
+ {
+ memset(pszDest, STRSAFE_GET_FILL_PATTERN(dwFlags), cbDest);
+
+ if (STRSAFE_GET_FILL_PATTERN(dwFlags) == 0)
+ {
+ pszDestEnd = pszDest;
+ cchRemaining = cchDest;
+ }
+ else if (cchDest > 0)
+ {
+ pszDestEnd = pszDest + cchDest - 1;
+ cchRemaining = 1;
+
+ // null terminate the end of the string
+ *pszDestEnd = '\0';
+ }
+ }
+
+ if (dwFlags & (STRSAFE_NULL_ON_FAILURE | STRSAFE_NO_TRUNCATION))
+ {
+ if (cchDest > 0)
+ {
+ pszDestEnd = pszDest;
+ cchRemaining = cchDest;
+
+ // null terminate the beginning of the string
+ *pszDestEnd = '\0';
+ }
+ }
+ }
+ }
+
+ if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
+ {
+ if (ppszDestEnd)
+ {
+ *ppszDestEnd = pszDestEnd;
+ }
+
+ if (pcchRemaining)
+ {
+ *pcchRemaining = cchRemaining;
+ }
+ }
+
+ return hr;
+}
+
+STRSAFEAPI StringCopyExWorkerW(wchar_t* pszDest, size_t cchDest, size_t cbDest, const wchar_t* pszSrc, wchar_t** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags)
+{
+ HRESULT hr = S_OK;
+ wchar_t* pszDestEnd = pszDest;
+ size_t cchRemaining = 0;
+
+ // ASSERT(cbDest == (cchDest * sizeof(wchar_t)) ||
+ // cbDest == (cchDest * sizeof(wchar_t)) + (cbDest % sizeof(wchar_t)));
+
+ // only accept valid flags
+ if (dwFlags & (~STRSAFE_VALID_FLAGS))
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ if (dwFlags & STRSAFE_IGNORE_NULLS)
+ {
+ if (pszDest == NULL)
+ {
+ if ((cchDest != 0) || (cbDest != 0))
+ {
+ // NULL pszDest and non-zero cchDest/cbDest is invalid
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ }
+
+ if (pszSrc == NULL)
+ {
+ pszSrc = L"";
+ }
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ if (cchDest == 0)
+ {
+ pszDestEnd = pszDest;
+ cchRemaining = 0;
+
+ // only fail if there was actually src data to copy
+ if (*pszSrc != L'\0')
+ {
+ if (pszDest == NULL)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ hr = STRSAFE_E_INSUFFICIENT_BUFFER;
+ }
+ }
+ }
+ else
+ {
+ pszDestEnd = pszDest;
+ cchRemaining = cchDest;
+
+ while (cchRemaining && (*pszSrc != L'\0'))
+ {
+ *pszDestEnd++= *pszSrc++;
+ cchRemaining--;
+ }
+
+ if (cchRemaining > 0)
+ {
+ if (dwFlags & STRSAFE_FILL_BEHIND_NULL)
+ {
+ memset(pszDestEnd + 1, STRSAFE_GET_FILL_PATTERN(dwFlags), ((cchRemaining - 1) * sizeof(wchar_t)) + (cbDest % sizeof(wchar_t)));
+ }
+ }
+ else
+ {
+ // we are going to truncate pszDest
+ pszDestEnd--;
+ cchRemaining++;
+
+ hr = STRSAFE_E_INSUFFICIENT_BUFFER;
+ }
+
+ *pszDestEnd = L'\0';
+ }
+ }
+ }
+
+ if (FAILED(hr))
+ {
+ if (pszDest)
+ {
+ if (dwFlags & STRSAFE_FILL_ON_FAILURE)
+ {
+ memset(pszDest, STRSAFE_GET_FILL_PATTERN(dwFlags), cbDest);
+
+ if (STRSAFE_GET_FILL_PATTERN(dwFlags) == 0)
+ {
+ pszDestEnd = pszDest;
+ cchRemaining = cchDest;
+ }
+ else if (cchDest > 0)
+ {
+ pszDestEnd = pszDest + cchDest - 1;
+ cchRemaining = 1;
+
+ // null terminate the end of the string
+ *pszDestEnd = L'\0';
+ }
+ }
+
+ if (dwFlags & (STRSAFE_NULL_ON_FAILURE | STRSAFE_NO_TRUNCATION))
+ {
+ if (cchDest > 0)
+ {
+ pszDestEnd = pszDest;
+ cchRemaining = cchDest;
+
+ // null terminate the beginning of the string
+ *pszDestEnd = L'\0';
+ }
+ }
+ }
+ }
+
+ if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
+ {
+ if (ppszDestEnd)
+ {
+ *ppszDestEnd = pszDestEnd;
+ }
+
+ if (pcchRemaining)
+ {
+ *pcchRemaining = cchRemaining;
+ }
+ }
+
+ return hr;
+}
+
+STRSAFEAPI StringCopyNWorkerA(char* pszDest, size_t cchDest, const char* pszSrc, size_t cchSrc)
+{
+ HRESULT hr = S_OK;
+
+ if (cchDest == 0)
+ {
+ // can not null terminate a zero-byte dest buffer
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ while (cchDest && cchSrc && (*pszSrc != '\0'))
+ {
+ *pszDest++= *pszSrc++;
+ cchDest--;
+ cchSrc--;
+ }
+
+ if (cchDest == 0)
+ {
+ // we are going to truncate pszDest
+ pszDest--;
+ hr = STRSAFE_E_INSUFFICIENT_BUFFER;
+ }
+
+ *pszDest= '\0';
+ }
+
+ return hr;
+}
+
+STRSAFEAPI StringCopyNWorkerW(wchar_t* pszDest, size_t cchDest, const wchar_t* pszSrc, size_t cchSrc)
+{
+ HRESULT hr = S_OK;
+
+ if (cchDest == 0)
+ {
+ // can not null terminate a zero-byte dest buffer
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ while (cchDest && cchSrc && (*pszSrc != L'\0'))
+ {
+ *pszDest++= *pszSrc++;
+ cchDest--;
+ cchSrc--;
+ }
+
+ if (cchDest == 0)
+ {
+ // we are going to truncate pszDest
+ pszDest--;
+ hr = STRSAFE_E_INSUFFICIENT_BUFFER;
+ }
+
+ *pszDest= L'\0';
+ }
+
+ return hr;
+}
+
+STRSAFEAPI StringCopyNExWorkerA(char* pszDest, size_t cchDest, size_t cbDest, const char* pszSrc, size_t cchSrc, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags)
+{
+ HRESULT hr = S_OK;
+ char* pszDestEnd = pszDest;
+ size_t cchRemaining = 0;
+
+ // ASSERT(cbDest == (cchDest * sizeof(char)) ||
+ // cbDest == (cchDest * sizeof(char)) + (cbDest % sizeof(char)));
+
+ // only accept valid flags
+ if (dwFlags & (~STRSAFE_VALID_FLAGS))
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ if (dwFlags & STRSAFE_IGNORE_NULLS)
+ {
+ if (pszDest == NULL)
+ {
+ if ((cchDest != 0) || (cbDest != 0))
+ {
+ // NULL pszDest and non-zero cchDest/cbDest is invalid
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ }
+
+ if (pszSrc == NULL)
+ {
+ pszSrc = "";
+ }
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ if (cchDest == 0)
+ {
+ pszDestEnd = pszDest;
+ cchRemaining = 0;
+
+ // only fail if there was actually src data to copy
+ if (*pszSrc != '\0')
+ {
+ if (pszDest == NULL)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ hr = STRSAFE_E_INSUFFICIENT_BUFFER;
+ }
+ }
+ }
+ else
+ {
+ pszDestEnd = pszDest;
+ cchRemaining = cchDest;
+
+ while (cchRemaining && cchSrc && (*pszSrc != '\0'))
+ {
+ *pszDestEnd++= *pszSrc++;
+ cchRemaining--;
+ cchSrc--;
+ }
+
+ if (cchRemaining > 0)
+ {
+ if (dwFlags & STRSAFE_FILL_BEHIND_NULL)
+ {
+ memset(pszDestEnd + 1, STRSAFE_GET_FILL_PATTERN(dwFlags), ((cchRemaining - 1) * sizeof(char)) + (cbDest % sizeof(char)));
+ }
+ }
+ else
+ {
+ // we are going to truncate pszDest
+ pszDestEnd--;
+ cchRemaining++;
+
+ hr = STRSAFE_E_INSUFFICIENT_BUFFER;
+ }
+
+ *pszDestEnd = '\0';
+ }
+ }
+ }
+
+ if (FAILED(hr))
+ {
+ if (pszDest)
+ {
+ if (dwFlags & STRSAFE_FILL_ON_FAILURE)
+ {
+ memset(pszDest, STRSAFE_GET_FILL_PATTERN(dwFlags), cbDest);
+
+ if (STRSAFE_GET_FILL_PATTERN(dwFlags) == 0)
+ {
+ pszDestEnd = pszDest;
+ cchRemaining = cchDest;
+ }
+ else if (cchDest > 0)
+ {
+ pszDestEnd = pszDest + cchDest - 1;
+ cchRemaining = 1;
+
+ // null terminate the end of the string
+ *pszDestEnd = '\0';
+ }
+ }
+
+ if (dwFlags & (STRSAFE_NULL_ON_FAILURE | STRSAFE_NO_TRUNCATION))
+ {
+ if (cchDest > 0)
+ {
+ pszDestEnd = pszDest;
+ cchRemaining = cchDest;
+
+ // null terminate the beginning of the string
+ *pszDestEnd = '\0';
+ }
+ }
+ }
+ }
+
+ if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
+ {
+ if (ppszDestEnd)
+ {
+ *ppszDestEnd = pszDestEnd;
+ }
+
+ if (pcchRemaining)
+ {
+ *pcchRemaining = cchRemaining;
+ }
+ }
+
+ return hr;
+}
+
+STRSAFEAPI StringCopyNExWorkerW(wchar_t* pszDest, size_t cchDest, size_t cbDest, const wchar_t* pszSrc, size_t cchSrc, wchar_t** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags)
+{
+ HRESULT hr = S_OK;
+ wchar_t* pszDestEnd = pszDest;
+ size_t cchRemaining = 0;
+
+ // ASSERT(cbDest == (cchDest * sizeof(wchar_t)) ||
+ // cbDest == (cchDest * sizeof(wchar_t)) + (cbDest % sizeof(wchar_t)));
+
+ // only accept valid flags
+ if (dwFlags & (~STRSAFE_VALID_FLAGS))
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ if (dwFlags & STRSAFE_IGNORE_NULLS)
+ {
+ if (pszDest == NULL)
+ {
+ if ((cchDest != 0) || (cbDest != 0))
+ {
+ // NULL pszDest and non-zero cchDest/cbDest is invalid
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ }
+
+ if (pszSrc == NULL)
+ {
+ pszSrc = L"";
+ }
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ if (cchDest == 0)
+ {
+ pszDestEnd = pszDest;
+ cchRemaining = 0;
+
+ // only fail if there was actually src data to copy
+ if (*pszSrc != L'\0')
+ {
+ if (pszDest == NULL)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ hr = STRSAFE_E_INSUFFICIENT_BUFFER;
+ }
+ }
+ }
+ else
+ {
+ pszDestEnd = pszDest;
+ cchRemaining = cchDest;
+
+ while (cchRemaining && cchSrc && (*pszSrc != L'\0'))
+ {
+ *pszDestEnd++= *pszSrc++;
+ cchRemaining--;
+ cchSrc--;
+ }
+
+ if (cchRemaining > 0)
+ {
+ if (dwFlags & STRSAFE_FILL_BEHIND_NULL)
+ {
+ memset(pszDestEnd + 1, STRSAFE_GET_FILL_PATTERN(dwFlags), ((cchRemaining - 1) * sizeof(wchar_t)) + (cbDest % sizeof(wchar_t)));
+ }
+ }
+ else
+ {
+ // we are going to truncate pszDest
+ pszDestEnd--;
+ cchRemaining++;
+
+ hr = STRSAFE_E_INSUFFICIENT_BUFFER;
+ }
+
+ *pszDestEnd = L'\0';
+ }
+ }
+ }
+
+ if (FAILED(hr))
+ {
+ if (pszDest)
+ {
+ if (dwFlags & STRSAFE_FILL_ON_FAILURE)
+ {
+ memset(pszDest, STRSAFE_GET_FILL_PATTERN(dwFlags), cbDest);
+
+ if (STRSAFE_GET_FILL_PATTERN(dwFlags) == 0)
+ {
+ pszDestEnd = pszDest;
+ cchRemaining = cchDest;
+ }
+ else if (cchDest > 0)
+ {
+ pszDestEnd = pszDest + cchDest - 1;
+ cchRemaining = 1;
+
+ // null terminate the end of the string
+ *pszDestEnd = L'\0';
+ }
+ }
+
+ if (dwFlags & (STRSAFE_NULL_ON_FAILURE | STRSAFE_NO_TRUNCATION))
+ {
+ if (cchDest > 0)
+ {
+ pszDestEnd = pszDest;
+ cchRemaining = cchDest;
+
+ // null terminate the beginning of the string
+ *pszDestEnd = L'\0';
+ }
+ }
+ }
+ }
+
+ if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
+ {
+ if (ppszDestEnd)
+ {
+ *ppszDestEnd = pszDestEnd;
+ }
+
+ if (pcchRemaining)
+ {
+ *pcchRemaining = cchRemaining;
+ }
+ }
+
+ return hr;
+}
+
+STRSAFEAPI StringCatWorkerA(char* pszDest, size_t cchDest, const char* pszSrc)
+{
+ HRESULT hr;
+ size_t cchDestCurrent;
+
+ hr = StringLengthWorkerA(pszDest, cchDest, &cchDestCurrent);
+
+ if (SUCCEEDED(hr))
+ {
+ hr = StringCopyWorkerA(pszDest + cchDestCurrent,
+ cchDest - cchDestCurrent,
+ pszSrc);
+ }
+
+ return hr;
+}
+
+STRSAFEAPI StringCatWorkerW(wchar_t* pszDest, size_t cchDest, const wchar_t* pszSrc)
+{
+ HRESULT hr;
+ size_t cchDestCurrent;
+
+ hr = StringLengthWorkerW(pszDest, cchDest, &cchDestCurrent);
+
+ if (SUCCEEDED(hr))
+ {
+ hr = StringCopyWorkerW(pszDest + cchDestCurrent,
+ cchDest - cchDestCurrent,
+ pszSrc);
+ }
+
+ return hr;
+}
+
+STRSAFEAPI StringCatExWorkerA(char* pszDest, size_t cchDest, size_t cbDest, const char* pszSrc, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags)
+{
+ HRESULT hr = S_OK;
+ char* pszDestEnd = pszDest;
+ size_t cchRemaining = 0;
+
+ // ASSERT(cbDest == (cchDest * sizeof(char)) ||
+ // cbDest == (cchDest * sizeof(char)) + (cbDest % sizeof(char)));
+
+ // only accept valid flags
+ if (dwFlags & (~STRSAFE_VALID_FLAGS))
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ size_t cchDestCurrent;
+
+ if (dwFlags & STRSAFE_IGNORE_NULLS)
+ {
+ if (pszDest == NULL)
+ {
+ if ((cchDest == 0) && (cbDest == 0))
+ {
+ cchDestCurrent = 0;
+ }
+ else
+ {
+ // NULL pszDest and non-zero cchDest/cbDest is invalid
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ }
+ else
+ {
+ hr = StringLengthWorkerA(pszDest, cchDest, &cchDestCurrent);
+
+ if (SUCCEEDED(hr))
+ {
+ pszDestEnd = pszDest + cchDestCurrent;
+ cchRemaining = cchDest - cchDestCurrent;
+ }
+ }
+
+ if (pszSrc == NULL)
+ {
+ pszSrc = "";
+ }
+ }
+ else
+ {
+ hr = StringLengthWorkerA(pszDest, cchDest, &cchDestCurrent);
+
+ if (SUCCEEDED(hr))
+ {
+ pszDestEnd = pszDest + cchDestCurrent;
+ cchRemaining = cchDest - cchDestCurrent;
+ }
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ if (cchDest == 0)
+ {
+ // only fail if there was actually src data to append
+ if (*pszSrc != '\0')
+ {
+ if (pszDest == NULL)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ hr = STRSAFE_E_INSUFFICIENT_BUFFER;
+ }
+ }
+ }
+ else
+ {
+ // we handle the STRSAFE_FILL_ON_FAILURE and STRSAFE_NULL_ON_FAILURE cases below, so do not pass
+ // those flags through
+ hr = StringCopyExWorkerA(pszDestEnd,
+ cchRemaining,
+ (cchRemaining * sizeof(char)) + (cbDest % sizeof(char)),
+ pszSrc,
+ &pszDestEnd,
+ &cchRemaining,
+ dwFlags & (~(STRSAFE_FILL_ON_FAILURE | STRSAFE_NULL_ON_FAILURE)));
+ }
+ }
+ }
+
+ if (FAILED(hr))
+ {
+ if (pszDest)
+ {
+ // STRSAFE_NO_TRUNCATION is taken care of by StringCopyExWorkerA()
+
+ if (dwFlags & STRSAFE_FILL_ON_FAILURE)
+ {
+ memset(pszDest, STRSAFE_GET_FILL_PATTERN(dwFlags), cbDest);
+
+ if (STRSAFE_GET_FILL_PATTERN(dwFlags) == 0)
+ {
+ pszDestEnd = pszDest;
+ cchRemaining = cchDest;
+ }
+ else
+ if (cchDest > 0)
+ {
+ pszDestEnd = pszDest + cchDest - 1;
+ cchRemaining = 1;
+
+ // null terminate the end of the string
+ *pszDestEnd = '\0';
+ }
+ }
+
+ if (dwFlags & STRSAFE_NULL_ON_FAILURE)
+ {
+ if (cchDest > 0)
+ {
+ pszDestEnd = pszDest;
+ cchRemaining = cchDest;
+
+ // null terminate the beginning of the string
+ *pszDestEnd = '\0';
+ }
+ }
+ }
+ }
+
+ if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
+ {
+ if (ppszDestEnd)
+ {
+ *ppszDestEnd = pszDestEnd;
+ }
+
+ if (pcchRemaining)
+ {
+ *pcchRemaining = cchRemaining;
+ }
+ }
+
+ return hr;
+}
+
+STRSAFEAPI StringCatExWorkerW(wchar_t* pszDest, size_t cchDest, size_t cbDest, const wchar_t* pszSrc, wchar_t** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags)
+{
+ HRESULT hr = S_OK;
+ wchar_t* pszDestEnd = pszDest;
+ size_t cchRemaining = 0;
+
+ // ASSERT(cbDest == (cchDest * sizeof(wchar_t)) ||
+ // cbDest == (cchDest * sizeof(wchar_t)) + (cbDest % sizeof(wchar_t)));
+
+ // only accept valid flags
+ if (dwFlags & (~STRSAFE_VALID_FLAGS))
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ size_t cchDestCurrent;
+
+ if (dwFlags & STRSAFE_IGNORE_NULLS)
+ {
+ if (pszDest == NULL)
+ {
+ if ((cchDest == 0) && (cbDest == 0))
+ {
+ cchDestCurrent = 0;
+ }
+ else
+ {
+ // NULL pszDest and non-zero cchDest/cbDest is invalid
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ }
+ else
+ {
+ hr = StringLengthWorkerW(pszDest, cchDest, &cchDestCurrent);
+
+ if (SUCCEEDED(hr))
+ {
+ pszDestEnd = pszDest + cchDestCurrent;
+ cchRemaining = cchDest - cchDestCurrent;
+ }
+ }
+
+ if (pszSrc == NULL)
+ {
+ pszSrc = L"";
+ }
+ }
+ else
+ {
+ hr = StringLengthWorkerW(pszDest, cchDest, &cchDestCurrent);
+
+ if (SUCCEEDED(hr))
+ {
+ pszDestEnd = pszDest + cchDestCurrent;
+ cchRemaining = cchDest - cchDestCurrent;
+ }
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ if (cchDest == 0)
+ {
+ // only fail if there was actually src data to append
+ if (*pszSrc != L'\0')
+ {
+ if (pszDest == NULL)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ hr = STRSAFE_E_INSUFFICIENT_BUFFER;
+ }
+ }
+ }
+ else
+ {
+ // we handle the STRSAFE_FILL_ON_FAILURE and STRSAFE_NULL_ON_FAILURE cases below, so do not pass
+ // those flags through
+ hr = StringCopyExWorkerW(pszDestEnd,
+ cchRemaining,
+ (cchRemaining * sizeof(wchar_t)) + (cbDest % sizeof(wchar_t)),
+ pszSrc,
+ &pszDestEnd,
+ &cchRemaining,
+ dwFlags & (~(STRSAFE_FILL_ON_FAILURE | STRSAFE_NULL_ON_FAILURE)));
+ }
+ }
+ }
+
+ if (FAILED(hr))
+ {
+ if (pszDest)
+ {
+ // STRSAFE_NO_TRUNCATION is taken care of by StringCopyExWorkerW()
+
+ if (dwFlags & STRSAFE_FILL_ON_FAILURE)
+ {
+ memset(pszDest, STRSAFE_GET_FILL_PATTERN(dwFlags), cbDest);
+
+ if (STRSAFE_GET_FILL_PATTERN(dwFlags) == 0)
+ {
+ pszDestEnd = pszDest;
+ cchRemaining = cchDest;
+ }
+ else if (cchDest > 0)
+ {
+ pszDestEnd = pszDest + cchDest - 1;
+ cchRemaining = 1;
+
+ // null terminate the end of the string
+ *pszDestEnd = L'\0';
+ }
+ }
+
+ if (dwFlags & STRSAFE_NULL_ON_FAILURE)
+ {
+ if (cchDest > 0)
+ {
+ pszDestEnd = pszDest;
+ cchRemaining = cchDest;
+
+ // null terminate the beginning of the string
+ *pszDestEnd = L'\0';
+ }
+ }
+ }
+ }
+
+ if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
+ {
+ if (ppszDestEnd)
+ {
+ *ppszDestEnd = pszDestEnd;
+ }
+
+ if (pcchRemaining)
+ {
+ *pcchRemaining = cchRemaining;
+ }
+ }
+
+ return hr;
+}
+
+STRSAFEAPI StringCatNWorkerA(char* pszDest, size_t cchDest, const char* pszSrc, size_t cchMaxAppend)
+{
+ HRESULT hr;
+ size_t cchDestCurrent;
+
+ hr = StringLengthWorkerA(pszDest, cchDest, &cchDestCurrent);
+
+ if (SUCCEEDED(hr))
+ {
+ hr = StringCopyNWorkerA(pszDest + cchDestCurrent,
+ cchDest - cchDestCurrent,
+ pszSrc,
+ cchMaxAppend);
+ }
+
+ return hr;
+}
+
+STRSAFEAPI StringCatNWorkerW(wchar_t* pszDest, size_t cchDest, const wchar_t* pszSrc, size_t cchMaxAppend)
+{
+ HRESULT hr;
+ size_t cchDestCurrent;
+
+ hr = StringLengthWorkerW(pszDest, cchDest, &cchDestCurrent);
+
+ if (SUCCEEDED(hr))
+ {
+ hr = StringCopyNWorkerW(pszDest + cchDestCurrent,
+ cchDest - cchDestCurrent,
+ pszSrc,
+ cchMaxAppend);
+ }
+
+ return hr;
+}
+
+STRSAFEAPI StringCatNExWorkerA(char* pszDest, size_t cchDest, size_t cbDest, const char* pszSrc, size_t cchMaxAppend, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags)
+{
+ HRESULT hr = S_OK;
+ char* pszDestEnd = pszDest;
+ size_t cchRemaining = 0;
+ size_t cchDestCurrent = 0;
+
+ // ASSERT(cbDest == (cchDest * sizeof(char)) ||
+ // cbDest == (cchDest * sizeof(char)) + (cbDest % sizeof(char)));
+
+ // only accept valid flags
+ if (dwFlags & (~STRSAFE_VALID_FLAGS))
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ if (dwFlags & STRSAFE_IGNORE_NULLS)
+ {
+ if (pszDest == NULL)
+ {
+ if ((cchDest == 0) && (cbDest == 0))
+ {
+ cchDestCurrent = 0;
+ }
+ else
+ {
+ // NULL pszDest and non-zero cchDest/cbDest is invalid
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ }
+ else
+ {
+ hr = StringLengthWorkerA(pszDest, cchDest, &cchDestCurrent);
+
+ if (SUCCEEDED(hr))
+ {
+ pszDestEnd = pszDest + cchDestCurrent;
+ cchRemaining = cchDest - cchDestCurrent;
+ }
+ }
+
+ if (pszSrc == NULL)
+ {
+ pszSrc = "";
+ }
+ }
+ else
+ {
+ hr = StringLengthWorkerA(pszDest, cchDest, &cchDestCurrent);
+
+ if (SUCCEEDED(hr))
+ {
+ pszDestEnd = pszDest + cchDestCurrent;
+ cchRemaining = cchDest - cchDestCurrent;
+ }
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ if (cchDest == 0)
+ {
+ // only fail if there was actually src data to append
+ if (*pszSrc != '\0')
+ {
+ if (pszDest == NULL)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ hr = STRSAFE_E_INSUFFICIENT_BUFFER;
+ }
+ }
+ }
+ else
+ {
+ // we handle the STRSAFE_FILL_ON_FAILURE and STRSAFE_NULL_ON_FAILURE cases below, so do not pass
+ // those flags through
+ hr = StringCopyNExWorkerA(pszDestEnd,
+ cchRemaining,
+ (cchRemaining * sizeof(char)) + (cbDest % sizeof(char)),
+ pszSrc,
+ cchMaxAppend,
+ &pszDestEnd,
+ &cchRemaining,
+ dwFlags & (~(STRSAFE_FILL_ON_FAILURE | STRSAFE_NULL_ON_FAILURE)));
+ }
+ }
+ }
+
+ if (FAILED(hr))
+ {
+ if (pszDest)
+ {
+ // STRSAFE_NO_TRUNCATION is taken care of by StringCopyNExWorkerA()
+
+ if (dwFlags & STRSAFE_FILL_ON_FAILURE)
+ {
+ memset(pszDest, STRSAFE_GET_FILL_PATTERN(dwFlags), cbDest);
+
+ if (STRSAFE_GET_FILL_PATTERN(dwFlags) == 0)
+ {
+ pszDestEnd = pszDest;
+ cchRemaining = cchDest;
+ }
+ else if (cchDest > 0)
+ {
+ pszDestEnd = pszDest + cchDest - 1;
+ cchRemaining = 1;
+
+ // null terminate the end of the string
+ *pszDestEnd = '\0';
+ }
+ }
+
+ if (dwFlags & (STRSAFE_NULL_ON_FAILURE))
+ {
+ if (cchDest > 0)
+ {
+ pszDestEnd = pszDest;
+ cchRemaining = cchDest;
+
+ // null terminate the beginning of the string
+ *pszDestEnd = '\0';
+ }
+ }
+ }
+ }
+
+ if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
+ {
+ if (ppszDestEnd)
+ {
+ *ppszDestEnd = pszDestEnd;
+ }
+
+ if (pcchRemaining)
+ {
+ *pcchRemaining = cchRemaining;
+ }
+ }
+
+ return hr;
+}
+
+STRSAFEAPI StringCatNExWorkerW(wchar_t* pszDest, size_t cchDest, size_t cbDest, const wchar_t* pszSrc, size_t cchMaxAppend, wchar_t** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags)
+{
+ HRESULT hr = S_OK;
+ wchar_t* pszDestEnd = pszDest;
+ size_t cchRemaining = 0;
+ size_t cchDestCurrent = 0;
+
+
+ // ASSERT(cbDest == (cchDest * sizeof(wchar_t)) ||
+ // cbDest == (cchDest * sizeof(wchar_t)) + (cbDest % sizeof(wchar_t)));
+
+ // only accept valid flags
+ if (dwFlags & (~STRSAFE_VALID_FLAGS))
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ if (dwFlags & STRSAFE_IGNORE_NULLS)
+ {
+ if (pszDest == NULL)
+ {
+ if ((cchDest == 0) && (cbDest == 0))
+ {
+ cchDestCurrent = 0;
+ }
+ else
+ {
+ // NULL pszDest and non-zero cchDest/cbDest is invalid
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ }
+ else
+ {
+ hr = StringLengthWorkerW(pszDest, cchDest, &cchDestCurrent);
+
+ if (SUCCEEDED(hr))
+ {
+ pszDestEnd = pszDest + cchDestCurrent;
+ cchRemaining = cchDest - cchDestCurrent;
+ }
+ }
+
+ if (pszSrc == NULL)
+ {
+ pszSrc = L"";
+ }
+ }
+ else
+ {
+ hr = StringLengthWorkerW(pszDest, cchDest, &cchDestCurrent);
+
+ if (SUCCEEDED(hr))
+ {
+ pszDestEnd = pszDest + cchDestCurrent;
+ cchRemaining = cchDest - cchDestCurrent;
+ }
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ if (cchDest == 0)
+ {
+ // only fail if there was actually src data to append
+ if (*pszSrc != L'\0')
+ {
+ if (pszDest == NULL)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ hr = STRSAFE_E_INSUFFICIENT_BUFFER;
+ }
+ }
+ }
+ else
+ {
+ // we handle the STRSAFE_FILL_ON_FAILURE and STRSAFE_NULL_ON_FAILURE cases below, so do not pass
+ // those flags through
+ hr = StringCopyNExWorkerW(pszDestEnd,
+ cchRemaining,
+ (cchRemaining * sizeof(wchar_t)) + (cbDest % sizeof(wchar_t)),
+ pszSrc,
+ cchMaxAppend,
+ &pszDestEnd,
+ &cchRemaining,
+ dwFlags & (~(STRSAFE_FILL_ON_FAILURE | STRSAFE_NULL_ON_FAILURE)));
+ }
+ }
+ }
+
+ if (FAILED(hr))
+ {
+ if (pszDest)
+ {
+ // STRSAFE_NO_TRUNCATION is taken care of by StringCopyNExWorkerW()
+
+ if (dwFlags & STRSAFE_FILL_ON_FAILURE)
+ {
+ memset(pszDest, STRSAFE_GET_FILL_PATTERN(dwFlags), cbDest);
+
+ if (STRSAFE_GET_FILL_PATTERN(dwFlags) == 0)
+ {
+ pszDestEnd = pszDest;
+ cchRemaining = cchDest;
+ }
+ else if (cchDest > 0)
+ {
+ pszDestEnd = pszDest + cchDest - 1;
+ cchRemaining = 1;
+
+ // null terminate the end of the string
+ *pszDestEnd = L'\0';
+ }
+ }
+
+ if (dwFlags & (STRSAFE_NULL_ON_FAILURE))
+ {
+ if (cchDest > 0)
+ {
+ pszDestEnd = pszDest;
+ cchRemaining = cchDest;
+
+ // null terminate the beginning of the string
+ *pszDestEnd = L'\0';
+ }
+ }
+ }
+ }
+
+ if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
+ {
+ if (ppszDestEnd)
+ {
+ *ppszDestEnd = pszDestEnd;
+ }
+
+ if (pcchRemaining)
+ {
+ *pcchRemaining = cchRemaining;
+ }
+ }
+
+ return hr;
+}
+
+STRSAFEAPI StringVPrintfWorkerA(char* pszDest, size_t cchDest, const char* pszFormat, va_list argList)
+{
+ HRESULT hr = S_OK;
+
+ if (cchDest == 0)
+ {
+ // can not null terminate a zero-byte dest buffer
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ int iRet;
+ size_t cchMax;
+
+ // leave the last space for the null terminator
+ cchMax = cchDest - 1;
+
+ iRet = vsnprintf(pszDest, cchMax, pszFormat, argList);
+ // ASSERT((iRet < 0) || (((size_t)iRet) <= cchMax));
+
+ if ((iRet < 0) || (((size_t)iRet) > cchMax))
+ {
+ // need to null terminate the string
+ pszDest += cchMax;
+ *pszDest = '\0';
+
+ // we have truncated pszDest
+ hr = STRSAFE_E_INSUFFICIENT_BUFFER;
+ }
+ else if (((size_t)iRet) == cchMax)
+ {
+ // need to null terminate the string
+ pszDest += cchMax;
+ *pszDest = '\0';
+ }
+ }
+
+ return hr;
+}
+#ifdef _WIN32 // TOOD: find BSD replacement for _vsnwprintf
+STRSAFEAPI StringVPrintfWorkerW(wchar_t* pszDest, size_t cchDest, const wchar_t* pszFormat, va_list argList)
+{
+ HRESULT hr = S_OK;
+
+ if (cchDest == 0)
+ {
+ // can not null terminate a zero-byte dest buffer
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ int iRet;
+ size_t cchMax;
+
+ // leave the last space for the null terminator
+ cchMax = cchDest - 1;
+
+ iRet = _vsnwprintf(pszDest, cchMax, pszFormat, argList);
+ // ASSERT((iRet < 0) || (((size_t)iRet) <= cchMax));
+
+ if ((iRet < 0) || (((size_t)iRet) > cchMax))
+ {
+ // need to null terminate the string
+ pszDest += cchMax;
+ *pszDest = L'\0';
+
+ // we have truncated pszDest
+ hr = STRSAFE_E_INSUFFICIENT_BUFFER;
+ }
+ else if (((size_t)iRet) == cchMax)
+ {
+ // need to null terminate the string
+ pszDest += cchMax;
+ *pszDest = L'\0';
+ }
+ }
+
+ return hr;
+}
+#endif
+
+STRSAFEAPI StringVPrintfExWorkerA(char* pszDest, size_t cchDest, size_t cbDest, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags, const char* pszFormat, va_list argList)
+{
+ HRESULT hr = S_OK;
+ char* pszDestEnd = pszDest;
+ size_t cchRemaining = 0;
+
+ // ASSERT(cbDest == (cchDest * sizeof(char)) ||
+ // cbDest == (cchDest * sizeof(char)) + (cbDest % sizeof(char)));
+
+ // only accept valid flags
+ if (dwFlags & (~STRSAFE_VALID_FLAGS))
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ if (dwFlags & STRSAFE_IGNORE_NULLS)
+ {
+ if (pszDest == NULL)
+ {
+ if ((cchDest != 0) || (cbDest != 0))
+ {
+ // NULL pszDest and non-zero cchDest/cbDest is invalid
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ }
+
+ if (pszFormat == NULL)
+ {
+ pszFormat = "";
+ }
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ if (cchDest == 0)
+ {
+ pszDestEnd = pszDest;
+ cchRemaining = 0;
+
+ // only fail if there was actually a non-empty format string
+ if (*pszFormat != '\0')
+ {
+ if (pszDest == NULL)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ hr = STRSAFE_E_INSUFFICIENT_BUFFER;
+ }
+ }
+ }
+ else
+ {
+ int iRet;
+ size_t cchMax;
+
+ // leave the last space for the null terminator
+ cchMax = cchDest - 1;
+
+ iRet = vsnprintf(pszDest, cchMax, pszFormat, argList);
+ // ASSERT((iRet < 0) || (((size_t)iRet) <= cchMax));
+
+ if ((iRet < 0) || (((size_t)iRet) > cchMax))
+ {
+ // we have truncated pszDest
+ pszDestEnd = pszDest + cchMax;
+ cchRemaining = 1;
+
+ // need to null terminate the string
+ *pszDestEnd = '\0';
+
+ hr = STRSAFE_E_INSUFFICIENT_BUFFER;
+ }
+ else if (((size_t)iRet) == cchMax)
+ {
+ // string fit perfectly
+ pszDestEnd = pszDest + cchMax;
+ cchRemaining = 1;
+
+ // need to null terminate the string
+ *pszDestEnd = '\0';
+ }
+ else if (((size_t)iRet) < cchMax)
+ {
+ // there is extra room
+ pszDestEnd = pszDest + iRet;
+ cchRemaining = cchDest - iRet;
+
+ if (dwFlags & STRSAFE_FILL_BEHIND_NULL)
+ {
+ memset(pszDestEnd + 1, STRSAFE_GET_FILL_PATTERN(dwFlags), ((cchRemaining - 1) * sizeof(char)) + (cbDest % sizeof(char)));
+ }
+ }
+ }
+ }
+ }
+
+ if (FAILED(hr))
+ {
+ if (pszDest)
+ {
+ if (dwFlags & STRSAFE_FILL_ON_FAILURE)
+ {
+ memset(pszDest, STRSAFE_GET_FILL_PATTERN(dwFlags), cbDest);
+
+ if (STRSAFE_GET_FILL_PATTERN(dwFlags) == 0)
+ {
+ pszDestEnd = pszDest;
+ cchRemaining = cchDest;
+ }
+ else if (cchDest > 0)
+ {
+ pszDestEnd = pszDest + cchDest - 1;
+ cchRemaining = 1;
+
+ // null terminate the end of the string
+ *pszDestEnd = '\0';
+ }
+ }
+
+ if (dwFlags & (STRSAFE_NULL_ON_FAILURE | STRSAFE_NO_TRUNCATION))
+ {
+ if (cchDest > 0)
+ {
+ pszDestEnd = pszDest;
+ cchRemaining = cchDest;
+
+ // null terminate the beginning of the string
+ *pszDestEnd = '\0';
+ }
+ }
+ }
+ }
+
+ if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
+ {
+ if (ppszDestEnd)
+ {
+ *ppszDestEnd = pszDestEnd;
+ }
+
+ if (pcchRemaining)
+ {
+ *pcchRemaining = cchRemaining;
+ }
+ }
+
+ return hr;
+}
+#ifdef _WIN32
+STRSAFEAPI StringVPrintfExWorkerW(wchar_t* pszDest, size_t cchDest, size_t cbDest, wchar_t** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags, const wchar_t* pszFormat, va_list argList)
+{
+ HRESULT hr = S_OK;
+ wchar_t* pszDestEnd = pszDest;
+ size_t cchRemaining = 0;
+
+ // ASSERT(cbDest == (cchDest * sizeof(wchar_t)) ||
+ // cbDest == (cchDest * sizeof(wchar_t)) + (cbDest % sizeof(wchar_t)));
+
+ // only accept valid flags
+ if (dwFlags & (~STRSAFE_VALID_FLAGS))
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ if (dwFlags & STRSAFE_IGNORE_NULLS)
+ {
+ if (pszDest == NULL)
+ {
+ if ((cchDest != 0) || (cbDest != 0))
+ {
+ // NULL pszDest and non-zero cchDest/cbDest is invalid
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ }
+
+ if (pszFormat == NULL)
+ {
+ pszFormat = L"";
+ }
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ if (cchDest == 0)
+ {
+ pszDestEnd = pszDest;
+ cchRemaining = 0;
+
+ // only fail if there was actually a non-empty format string
+ if (*pszFormat != L'\0')
+ {
+ if (pszDest == NULL)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ hr = STRSAFE_E_INSUFFICIENT_BUFFER;
+ }
+ }
+ }
+ else
+ {
+ int iRet;
+ size_t cchMax;
+
+ // leave the last space for the null terminator
+ cchMax = cchDest - 1;
+
+ iRet = _vsnwprintf(pszDest, cchMax, pszFormat, argList);
+ // ASSERT((iRet < 0) || (((size_t)iRet) <= cchMax));
+
+ if ((iRet < 0) || (((size_t)iRet) > cchMax))
+ {
+ // we have truncated pszDest
+ pszDestEnd = pszDest + cchMax;
+ cchRemaining = 1;
+
+ // need to null terminate the string
+ *pszDestEnd = L'\0';
+
+ hr = STRSAFE_E_INSUFFICIENT_BUFFER;
+ }
+ else if (((size_t)iRet) == cchMax)
+ {
+ // string fit perfectly
+ pszDestEnd = pszDest + cchMax;
+ cchRemaining = 1;
+
+ // need to null terminate the string
+ *pszDestEnd = L'\0';
+ }
+ else if (((size_t)iRet) < cchMax)
+ {
+ // there is extra room
+ pszDestEnd = pszDest + iRet;
+ cchRemaining = cchDest - iRet;
+
+ if (dwFlags & STRSAFE_FILL_BEHIND_NULL)
+ {
+ memset(pszDestEnd + 1, STRSAFE_GET_FILL_PATTERN(dwFlags), ((cchRemaining - 1) * sizeof(wchar_t)) + (cbDest % sizeof(wchar_t)));
+ }
+ }
+ }
+ }
+ }
+
+ if (FAILED(hr))
+ {
+ if (pszDest)
+ {
+ if (dwFlags & STRSAFE_FILL_ON_FAILURE)
+ {
+ memset(pszDest, STRSAFE_GET_FILL_PATTERN(dwFlags), cbDest);
+
+ if (STRSAFE_GET_FILL_PATTERN(dwFlags) == 0)
+ {
+ pszDestEnd = pszDest;
+ cchRemaining = cchDest;
+ }
+ else if (cchDest > 0)
+ {
+ pszDestEnd = pszDest + cchDest - 1;
+ cchRemaining = 1;
+
+ // null terminate the end of the string
+ *pszDestEnd = L'\0';
+ }
+ }
+
+ if (dwFlags & (STRSAFE_NULL_ON_FAILURE | STRSAFE_NO_TRUNCATION))
+ {
+ if (cchDest > 0)
+ {
+ pszDestEnd = pszDest;
+ cchRemaining = cchDest;
+
+ // null terminate the beginning of the string
+ *pszDestEnd = L'\0';
+ }
+ }
+ }
+ }
+
+ if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
+ {
+ if (ppszDestEnd)
+ {
+ *ppszDestEnd = pszDestEnd;
+ }
+
+ if (pcchRemaining)
+ {
+ *pcchRemaining = cchRemaining;
+ }
+ }
+
+ return hr;
+}
+#endif
+
+STRSAFEAPI StringLengthWorkerA(const char* psz, size_t cchMax, size_t* pcch)
+{
+ HRESULT hr = S_OK;
+ size_t cchMaxPrev = cchMax;
+
+ while (cchMax && (*psz != '\0'))
+ {
+ psz++;
+ cchMax--;
+ }
+
+ if (cchMax == 0)
+ {
+ // the string is longer than cchMax
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+
+ if (SUCCEEDED(hr) && pcch)
+ {
+ *pcch = cchMaxPrev - cchMax;
+ }
+
+ return hr;
+}
+
+STRSAFEAPI StringLengthWorkerW(const wchar_t* psz, size_t cchMax, size_t* pcch)
+{
+ HRESULT hr = S_OK;
+ size_t cchMaxPrev = cchMax;
+
+ while (cchMax && (*psz != L'\0'))
+ {
+ psz++;
+ cchMax--;
+ }
+
+ if (cchMax == 0)
+ {
+ // the string is longer than cchMax
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+
+ if (SUCCEEDED(hr) && pcch)
+ {
+ *pcch = cchMaxPrev - cchMax;
+ }
+
+ return hr;
+}
+#endif // STRSAFE_INLINE
+
+#ifndef STRSAFE_LIB_IMPL
+STRSAFE_INLINE_API StringGetsExWorkerA(char* pszDest, size_t cchDest, size_t cbDest, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags)
+{
+ HRESULT hr = S_OK;
+ char* pszDestEnd = pszDest;
+ size_t cchRemaining = 0;
+
+ // ASSERT(cbDest == (cchDest * sizeof(char)) ||
+ // cbDest == (cchDest * sizeof(char)) + (cbDest % sizeof(char)));
+
+ // only accept valid flags
+ if (dwFlags & (~STRSAFE_VALID_FLAGS))
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ if (dwFlags & STRSAFE_IGNORE_NULLS)
+ {
+ if (pszDest == NULL)
+ {
+ if ((cchDest != 0) || (cbDest != 0))
+ {
+ // NULL pszDest and non-zero cchDest/cbDest is invalid
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ }
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ if (cchDest <= 1)
+ {
+ pszDestEnd = pszDest;
+ cchRemaining = cchDest;
+
+ if (cchDest == 1)
+ {
+ *pszDestEnd = '\0';
+ }
+
+ hr = STRSAFE_E_INSUFFICIENT_BUFFER;
+ }
+ else
+ {
+ int ch;
+
+ pszDestEnd = pszDest;
+ cchRemaining = cchDest;
+
+ while ((cchRemaining > 1) && (ch = getc(stdin)) != (int)'\n')
+ {
+ if (ch == EOF)
+ {
+ if (pszDestEnd == pszDest)
+ {
+ // we failed to read anything from stdin
+ hr = STRSAFE_E_END_OF_FILE;
+ }
+ break;
+ }
+
+ *pszDestEnd = ch;
+
+ pszDestEnd++;
+ cchRemaining--;
+ }
+
+ if (cchRemaining > 0)
+ {
+ // there is extra room
+ if (dwFlags & STRSAFE_FILL_BEHIND_NULL)
+ {
+ memset(pszDestEnd + 1, STRSAFE_GET_FILL_PATTERN(dwFlags), ((cchRemaining - 1) * sizeof(char)) + (cbDest % sizeof(char)));
+ }
+ }
+
+ *pszDestEnd = '\0';
+ }
+ }
+ }
+
+ if (FAILED(hr))
+ {
+ if (pszDest)
+ {
+ if (dwFlags & STRSAFE_FILL_ON_FAILURE)
+ {
+ memset(pszDest, STRSAFE_GET_FILL_PATTERN(dwFlags), cbDest);
+
+ if (STRSAFE_GET_FILL_PATTERN(dwFlags) == 0)
+ {
+ pszDestEnd = pszDest;
+ cchRemaining = cchDest;
+ }
+ else if (cchDest > 0)
+ {
+ pszDestEnd = pszDest + cchDest - 1;
+ cchRemaining = 1;
+
+ // null terminate the end of the string
+ *pszDestEnd = '\0';
+ }
+ }
+
+ if (dwFlags & (STRSAFE_NULL_ON_FAILURE | STRSAFE_NO_TRUNCATION))
+ {
+ if (cchDest > 0)
+ {
+ pszDestEnd = pszDest;
+ cchRemaining = cchDest;
+
+ // null terminate the beginning of the string
+ *pszDestEnd = '\0';
+ }
+ }
+ }
+ }
+
+ if (SUCCEEDED(hr) ||
+ (hr == STRSAFE_E_INSUFFICIENT_BUFFER) ||
+ (hr == STRSAFE_E_END_OF_FILE))
+ {
+ if (ppszDestEnd)
+ {
+ *ppszDestEnd = pszDestEnd;
+ }
+
+ if (pcchRemaining)
+ {
+ *pcchRemaining = cchRemaining;
+ }
+ }
+
+ return hr;
+}
+
+STRSAFE_INLINE_API StringGetsExWorkerW(wchar_t* pszDest, size_t cchDest, size_t cbDest, wchar_t** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags)
+{
+ HRESULT hr = S_OK;
+ wchar_t* pszDestEnd = pszDest;
+ size_t cchRemaining = 0;
+
+ // ASSERT(cbDest == (cchDest * sizeof(char)) ||
+ // cbDest == (cchDest * sizeof(char)) + (cbDest % sizeof(char)));
+
+ // only accept valid flags
+ if (dwFlags & (~STRSAFE_VALID_FLAGS))
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ if (dwFlags & STRSAFE_IGNORE_NULLS)
+ {
+ if (pszDest == NULL)
+ {
+ if ((cchDest != 0) || (cbDest != 0))
+ {
+ // NULL pszDest and non-zero cchDest/cbDest is invalid
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ }
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ if (cchDest <= 1)
+ {
+ pszDestEnd = pszDest;
+ cchRemaining = cchDest;
+
+ if (cchDest == 1)
+ {
+ *pszDestEnd = L'\0';
+ }
+
+ hr = STRSAFE_E_INSUFFICIENT_BUFFER;
+ }
+ else
+ {
+ wchar_t ch;
+
+ pszDestEnd = pszDest;
+ cchRemaining = cchDest;
+
+ while ((cchRemaining > 1) && (ch = (wchar_t)getwc(stdin)) != L'\n')
+ {
+ if (ch == EOF)
+ {
+ if (pszDestEnd == pszDest)
+ {
+ // we failed to read anything from stdin
+ hr = STRSAFE_E_END_OF_FILE;
+ }
+ break;
+ }
+
+ *pszDestEnd = ch;
+
+ pszDestEnd++;
+ cchRemaining--;
+ }
+
+ if (cchRemaining > 0)
+ {
+ // there is extra room
+ if (dwFlags & STRSAFE_FILL_BEHIND_NULL)
+ {
+ memset(pszDestEnd + 1, STRSAFE_GET_FILL_PATTERN(dwFlags), ((cchRemaining - 1) * sizeof(wchar_t)) + (cbDest % sizeof(wchar_t)));
+ }
+ }
+
+ *pszDestEnd = L'\0';
+ }
+ }
+ }
+
+ if (FAILED(hr))
+ {
+ if (pszDest)
+ {
+ if (dwFlags & STRSAFE_FILL_ON_FAILURE)
+ {
+ memset(pszDest, STRSAFE_GET_FILL_PATTERN(dwFlags), cbDest);
+
+ if (STRSAFE_GET_FILL_PATTERN(dwFlags) == 0)
+ {
+ pszDestEnd = pszDest;
+ cchRemaining = cchDest;
+ }
+ else if (cchDest > 0)
+ {
+ pszDestEnd = pszDest + cchDest - 1;
+ cchRemaining = 1;
+
+ // null terminate the end of the string
+ *pszDestEnd = L'\0';
+ }
+ }
+
+ if (dwFlags & (STRSAFE_NULL_ON_FAILURE | STRSAFE_NO_TRUNCATION))
+ {
+ if (cchDest > 0)
+ {
+ pszDestEnd = pszDest;
+ cchRemaining = cchDest;
+
+ // null terminate the beginning of the string
+ *pszDestEnd = L'\0';
+ }
+ }
+ }
+ }
+
+ if (SUCCEEDED(hr) ||
+ (hr == STRSAFE_E_INSUFFICIENT_BUFFER) ||
+ (hr == STRSAFE_E_END_OF_FILE))
+ {
+ if (ppszDestEnd)
+ {
+ *ppszDestEnd = pszDestEnd;
+ }
+
+ if (pcchRemaining)
+ {
+ *pcchRemaining = cchRemaining;
+ }
+ }
+
+ return hr;
+}
+#endif // !STRSAFE_LIB_IMPL
+
+
+// Do not call these functions, they are worker functions for internal use within this file
+#ifdef DEPRECATE_SUPPORTED
+#pragma deprecated(StringCopyWorkerA)
+#pragma deprecated(StringCopyWorkerW)
+#pragma deprecated(StringCopyExWorkerA)
+#pragma deprecated(StringCopyExWorkerW)
+#pragma deprecated(StringCatWorkerA)
+#pragma deprecated(StringCatWorkerW)
+#pragma deprecated(StringCatExWorkerA)
+#pragma deprecated(StringCatExWorkerW)
+#pragma deprecated(StringCatNWorkerA)
+#pragma deprecated(StringCatNWorkerW)
+#pragma deprecated(StringCatNExWorkerA)
+#pragma deprecated(StringCatNExWorkerW)
+#pragma deprecated(StringVPrintfWorkerA)
+#pragma deprecated(StringVPrintfWorkerW)
+#pragma deprecated(StringVPrintfExWorkerA)
+#pragma deprecated(StringVPrintfExWorkerW)
+#pragma deprecated(StringLengthWorkerA)
+#pragma deprecated(StringLengthWorkerW)
+#else
+#define StringCopyWorkerA StringCopyWorkerA_instead_use_StringCchCopyA_or_StringCchCopyExA;
+#define StringCopyWorkerW StringCopyWorkerW_instead_use_StringCchCopyW_or_StringCchCopyExW;
+#define StringCopyExWorkerA StringCopyExWorkerA_instead_use_StringCchCopyA_or_StringCchCopyExA;
+#define StringCopyExWorkerW StringCopyExWorkerW_instead_use_StringCchCopyW_or_StringCchCopyExW;
+#define StringCatWorkerA StringCatWorkerA_instead_use_StringCchCatA_or_StringCchCatExA;
+#define StringCatWorkerW StringCatWorkerW_instead_use_StringCchCatW_or_StringCchCatExW;
+#define StringCatExWorkerA StringCatExWorkerA_instead_use_StringCchCatA_or_StringCchCatExA;
+#define StringCatExWorkerW StringCatExWorkerW_instead_use_StringCchCatW_or_StringCchCatExW;
+#define StringCatNWorkerA StringCatNWorkerA_instead_use_StringCchCatNA_or_StrincCbCatNA;
+#define StringCatNWorkerW StringCatNWorkerW_instead_use_StringCchCatNW_or_StringCbCatNW;
+#define StringCatNExWorkerA StringCatNExWorkerA_instead_use_StringCchCatNExA_or_StringCbCatNExA;
+#define StringCatNExWorkerW StringCatNExWorkerW_instead_use_StringCchCatNExW_or_StringCbCatNExW;
+#define StringVPrintfWorkerA StringVPrintfWorkerA_instead_use_StringCchVPrintfA_or_StringCchVPrintfExA;
+#define StringVPrintfWorkerW StringVPrintfWorkerW_instead_use_StringCchVPrintfW_or_StringCchVPrintfExW;
+#define StringVPrintfExWorkerA StringVPrintfExWorkerA_instead_use_StringCchVPrintfA_or_StringCchVPrintfExA;
+#define StringVPrintfExWorkerW StringVPrintfExWorkerW_instead_use_StringCchVPrintfW_or_StringCchVPrintfExW;
+#define StringLengthWorkerA StringLengthWorkerA_instead_use_StringCchLengthA_or_StringCbLengthA;
+#define StringLengthWorkerW StringLengthWorkerW_instead_use_StringCchLengthW_or_StringCbLengthW;
+#endif // !DEPRECATE_SUPPORTED
+
+
+#ifndef STRSAFE_NO_DEPRECATE
+// Deprecate all of the unsafe functions to generate compiletime errors. If you do not want
+// this then you can #define STRSAFE_NO_DEPRECATE before including this file.
+#ifdef DEPRECATE_SUPPORTED
+
+// First all the names that are a/w variants (or shouldn't be #defined by now anyway).
+#pragma deprecated(lstrcpyA)
+#pragma deprecated(lstrcpyW)
+#pragma deprecated(lstrcatA)
+#pragma deprecated(lstrcatW)
+#pragma deprecated(wsprintfA)
+#pragma deprecated(wsprintfW)
+
+#pragma deprecated(StrCpyW)
+#pragma deprecated(StrCatW)
+#pragma deprecated(StrNCatA)
+#pragma deprecated(StrNCatW)
+#pragma deprecated(StrCatNA)
+#pragma deprecated(StrCatNW)
+#pragma deprecated(wvsprintfA)
+#pragma deprecated(wvsprintfW)
+
+#pragma deprecated(strcpy)
+#pragma deprecated(wcscpy)
+#pragma deprecated(strcat)
+#pragma deprecated(wcscat)
+#pragma deprecated(sprintf)
+#pragma deprecated(swprintf)
+#pragma deprecated(vsprintf)
+#pragma deprecated(vswprintf)
+#pragma deprecated(_snprintf)
+#pragma deprecated(_snwprintf)
+#pragma deprecated(_vsnprintf)
+#pragma deprecated(_vsnwprintf)
+#pragma deprecated(gets)
+#pragma deprecated(_getws)
+
+// Then all the windows.h names - we need to undef and redef based on UNICODE setting
+#undef lstrcpy
+#undef lstrcat
+#undef wsprintf
+#undef wvsprintf
+#pragma deprecated(lstrcpy)
+#pragma deprecated(lstrcat)
+#pragma deprecated(wsprintf)
+#pragma deprecated(wvsprintf)
+#ifdef UNICODE
+#define lstrcpy lstrcpyW
+#define lstrcat lstrcatW
+#define wsprintf wsprintfW
+#define wvsprintf wvsprintfW
+#else
+#define lstrcpy lstrcpyA
+#define lstrcat lstrcatA
+#define wsprintf wsprintfA
+#define wvsprintf wvsprintfA
+#endif
+
+// Then the shlwapi names - they key off UNICODE also.
+#undef StrCpyA
+#undef StrCpy
+#undef StrCatA
+#undef StrCat
+#undef StrNCat
+#undef StrCatN
+#pragma deprecated(StrCpyA)
+#pragma deprecated(StrCatA)
+#pragma deprecated(StrCatN)
+#pragma deprecated(StrCpy)
+#pragma deprecated(StrCat)
+#pragma deprecated(StrNCat)
+#define StrCpyA lstrcpyA
+#define StrCatA lstrcatA
+#define StrCatN StrNCat
+#ifdef UNICODE
+#define StrCpy StrCpyW
+#define StrCat StrCatW
+#define StrNCat StrNCatW
+#else
+#define StrCpy lstrcpyA
+#define StrCat lstrcatA
+#define StrNCat StrNCatA
+#endif
+
+// Then all the CRT names - we need to undef/redef based on _UNICODE value.
+#undef _tcscpy
+#undef _ftcscpy
+#undef _tcscat
+#undef _ftcscat
+#undef _stprintf
+#undef _sntprintf
+#undef _vstprintf
+#undef _vsntprintf
+#undef _getts
+#pragma deprecated(_tcscpy)
+#pragma deprecated(_ftcscpy)
+#pragma deprecated(_tcscat)
+#pragma deprecated(_ftcscat)
+#pragma deprecated(_stprintf)
+#pragma deprecated(_sntprintf)
+#pragma deprecated(_vstprintf)
+#pragma deprecated(_vsntprintf)
+#pragma deprecated(_getts)
+#ifdef _UNICODE
+#define _tcscpy wcscpy
+#define _ftcscpy wcscpy
+#define _tcscat wcscat
+#define _ftcscat wcscat
+#define _stprintf swprintf
+#define _sntprintf _snwprintf
+#define _vstprintf vswprintf
+#define _vsntprintf _vsnwprintf
+#define _getts _getws
+#else
+#define _tcscpy strcpy
+#define _ftcscpy strcpy
+#define _tcscat strcat
+#define _ftcscat strcat
+#define _stprintf sprintf
+#define _sntprintf _snprintf
+#define _vstprintf vsprintf
+#define _vsntprintf _vsnprintf
+#define _getts gets
+#endif
+
+#else // DEPRECATE_SUPPORTED
+
+#undef strcpy
+#define strcpy strcpy_instead_use_StringCbCopyA_or_StringCchCopyA;
+
+#undef wcscpy
+#define wcscpy wcscpy_instead_use_StringCbCopyW_or_StringCchCopyW;
+
+#undef strcat
+#define strcat strcat_instead_use_StringCbCatA_or_StringCchCatA;
+
+#undef wcscat
+#define wcscat wcscat_instead_use_StringCbCatW_or_StringCchCatW;
+
+#undef sprintf
+#define sprintf sprintf_instead_use_StringCbPrintfA_or_StringCchPrintfA;
+
+#undef swprintf
+#define swprintf swprintf_instead_use_StringCbPrintfW_or_StringCchPrintfW;
+
+#undef vsprintf
+#define vsprintf vsprintf_instead_use_StringCbVPrintfA_or_StringCchVPrintfA;
+
+#undef vswprintf
+#define vswprintf vswprintf_instead_use_StringCbVPrintfW_or_StringCchVPrintfW;
+
+#undef _snprintf
+#define _snprintf _snprintf_instead_use_StringCbPrintfA_or_StringCchPrintfA;
+
+#undef _snwprintf
+#define _snwprintf _snwprintf_instead_use_StringCbPrintfW_or_StringCchPrintfW;
+
+#undef _vsnprintf
+#define _vsnprintf _vsnprintf_instead_use_StringCbVPrintfA_or_StringCchVPrintfA;
+
+#undef _vsnwprintf
+#define _vsnwprintf _vsnwprintf_instead_use_StringCbVPrintfW_or_StringCchVPrintfW;
+
+#undef strcpyA
+#define strcpyA strcpyA_instead_use_StringCbCopyA_or_StringCchCopyA;
+
+#undef strcpyW
+#define strcpyW strcpyW_instead_use_StringCbCopyW_or_StringCchCopyW;
+
+#undef lstrcpy
+#define lstrcpy lstrcpy_instead_use_StringCbCopy_or_StringCchCopy;
+
+#undef lstrcpyA
+#define lstrcpyA lstrcpyA_instead_use_StringCbCopyA_or_StringCchCopyA;
+
+#undef lstrcpyW
+#define lstrcpyW lstrcpyW_instead_use_StringCbCopyW_or_StringCchCopyW;
+
+#undef StrCpy
+#define StrCpy StrCpy_instead_use_StringCbCopy_or_StringCchCopy;
+
+#undef StrCpyA
+#define StrCpyA StrCpyA_instead_use_StringCbCopyA_or_StringCchCopyA;
+
+#undef StrCpyW
+#define StrCpyW StrCpyW_instead_use_StringCbCopyW_or_StringCchCopyW;
+
+#undef _tcscpy
+#define _tcscpy _tcscpy_instead_use_StringCbCopy_or_StringCchCopy;
+
+#undef _ftcscpy
+#define _ftcscpy _ftcscpy_instead_use_StringCbCopy_or_StringCchCopy;
+
+#undef lstrcat
+#define lstrcat lstrcat_instead_use_StringCbCat_or_StringCchCat;
+
+#undef lstrcatA
+#define lstrcatA lstrcatA_instead_use_StringCbCatA_or_StringCchCatA;
+
+#undef lstrcatW
+#define lstrcatW lstrcatW_instead_use_StringCbCatW_or_StringCchCatW;
+
+#undef StrCat
+#define StrCat StrCat_instead_use_StringCbCat_or_StringCchCat;
+
+#undef StrCatA
+#define StrCatA StrCatA_instead_use_StringCbCatA_or_StringCchCatA;
+
+#undef StrCatW
+#define StrCatW StrCatW_instead_use_StringCbCatW_or_StringCchCatW;
+
+#undef StrNCat
+#define StrNCat StrNCat_instead_use_StringCbCatN_or_StringCchCatN;
+
+#undef StrNCatA
+#define StrNCatA StrNCatA_instead_use_StringCbCatNA_or_StringCchCatNA;
+
+#undef StrNCatW
+#define StrNCatW StrNCatW_instead_use_StringCbCatNW_or_StringCchCatNW;
+
+#undef StrCatN
+#define StrCatN StrCatN_instead_use_StringCbCatN_or_StringCchCatN;
+
+#undef StrCatNA
+#define StrCatNA StrCatNA_instead_use_StringCbCatNA_or_StringCchCatNA;
+
+#undef StrCatNW
+#define StrCatNW StrCatNW_instead_use_StringCbCatNW_or_StringCchCatNW;
+
+#undef _tcscat
+#define _tcscat _tcscat_instead_use_StringCbCat_or_StringCchCat;
+
+#undef _ftcscat
+#define _ftcscat _ftcscat_instead_use_StringCbCat_or_StringCchCat;
+
+#undef wsprintf
+#define wsprintf wsprintf_instead_use_StringCbPrintf_or_StringCchPrintf;
+
+#undef wsprintfA
+#define wsprintfA wsprintfA_instead_use_StringCbPrintfA_or_StringCchPrintfA;
+
+#undef wsprintfW
+#define wsprintfW wsprintfW_instead_use_StringCbPrintfW_or_StringCchPrintfW;
+
+#undef wvsprintf
+#define wvsprintf wvsprintf_instead_use_StringCbVPrintf_or_StringCchVPrintf;
+
+#undef wvsprintfA
+#define wvsprintfA wvsprintfA_instead_use_StringCbVPrintfA_or_StringCchVPrintfA;
+
+#undef wvsprintfW
+#define wvsprintfW wvsprintfW_instead_use_StringCbVPrintfW_or_StringCchVPrintfW;
+
+#undef _vstprintf
+#define _vstprintf _vstprintf_instead_use_StringCbVPrintf_or_StringCchVPrintf;
+
+#undef _vsntprintf
+#define _vsntprintf _vsntprintf_instead_use_StringCbVPrintf_or_StringCchVPrintf;
+
+#undef _stprintf
+#define _stprintf _stprintf_instead_use_StringCbPrintf_or_StringCchPrintf;
+
+#undef _sntprintf
+#define _sntprintf _sntprintf_instead_use_StringCbPrintf_or_StringCchPrintf;
+
+#undef _getts
+#define _getts _getts_instead_use_StringCbGets_or_StringCchGets;
+
+#undef gets
+#define gets _gets_instead_use_StringCbGetsA_or_StringCchGetsA;
+
+#undef _getws
+#define _getws _getws_instead_use_StringCbGetsW_or_StringCchGetsW;
+
+#endif // !DEPRECATE_SUPPORTED
+#endif // !STRSAFE_NO_DEPRECATE
+
+#ifdef _NTSTRSAFE_H_INCLUDED_
+#pragma warning(pop)
+#endif // _NTSTRSAFE_H_INCLUDED_
+
+#endif // _STRSAFE_H_INCLUDED_
diff --git a/Src/nu/strsafe.sln b/Src/nu/strsafe.sln
new file mode 100644
index 00000000..151a3a9a
--- /dev/null
+++ b/Src/nu/strsafe.sln
@@ -0,0 +1,23 @@
+Microsoft Visual Studio Solution File, Format Version 8.00
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "strsafe", "strsafe.vcproj", "{8402F30F-5EFE-4066-9D94-0B47B6C83D43}"
+ ProjectSection(ProjectDependencies) = postProject
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfiguration) = preSolution
+ Debug = Debug
+ Release = Release
+ EndGlobalSection
+ GlobalSection(ProjectDependencies) = postSolution
+ EndGlobalSection
+ GlobalSection(ProjectConfiguration) = postSolution
+ {8402F30F-5EFE-4066-9D94-0B47B6C83D43}.Debug.ActiveCfg = Debug|Win32
+ {8402F30F-5EFE-4066-9D94-0B47B6C83D43}.Debug.Build.0 = Debug|Win32
+ {8402F30F-5EFE-4066-9D94-0B47B6C83D43}.Release.ActiveCfg = Release|Win32
+ {8402F30F-5EFE-4066-9D94-0B47B6C83D43}.Release.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ EndGlobalSection
+ GlobalSection(ExtensibilityAddIns) = postSolution
+ EndGlobalSection
+EndGlobal
diff --git a/Src/nu/strsafe.vcproj b/Src/nu/strsafe.vcproj
new file mode 100644
index 00000000..dff735c0
--- /dev/null
+++ b/Src/nu/strsafe.vcproj
@@ -0,0 +1,150 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="7.10"
+ Name="strsafe"
+ ProjectGUID="{8402F30F-5EFE-4066-9D94-0B47B6C83D43}"
+ Keyword="Win32Proj">
+ <Platforms>
+ <Platform
+ Name="Win32"/>
+ </Platforms>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="Debug"
+ IntermediateDirectory="Debug"
+ ConfigurationType="2"
+ CharacterSet="2">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;STRSAFE_EXPORTS"
+ MinimalRebuild="TRUE"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="1"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="TRUE"
+ DebugInformationFormat="4"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLinkerTool"
+ OutputFile="$(OutDir)/strsafe.dll"
+ LinkIncremental="2"
+ ModuleDefinitionFile="strsafe.def"
+ GenerateDebugInformation="TRUE"
+ ProgramDatabaseFile="$(OutDir)/strsafe.pdb"
+ SubSystem="2"
+ ImportLibrary="$(OutDir)/strsafe.lib"
+ TargetMachine="1"/>
+ <Tool
+ Name="VCMIDLTool"/>
+ <Tool
+ Name="VCPostBuildEventTool"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCXMLDataGeneratorTool"/>
+ <Tool
+ Name="VCWebDeploymentTool"/>
+ <Tool
+ Name="VCManagedWrapperGeneratorTool"/>
+ <Tool
+ Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="Release"
+ IntermediateDirectory="Release"
+ ConfigurationType="2"
+ CharacterSet="2">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ GlobalOptimizations="TRUE"
+ EnableIntrinsicFunctions="TRUE"
+ FavorSizeOrSpeed="1"
+ OmitFramePointers="TRUE"
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;STRSAFE_EXPORTS"
+ StringPooling="TRUE"
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="TRUE"
+ TreatWChar_tAsBuiltInType="TRUE"
+ ForceConformanceInForLoopScope="TRUE"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="TRUE"
+ DebugInformationFormat="3"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="unicows.lib"
+ OutputFile="$(ProgramFiles)/Winamp/strsafe.dll"
+ LinkIncremental="1"
+ ModuleDefinitionFile="strsafe.def"
+ GenerateDebugInformation="TRUE"
+ SubSystem="2"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ OptimizeForWindows98="1"
+ ImportLibrary="$(OutDir)/strsafe.lib"
+ TargetMachine="1"/>
+ <Tool
+ Name="VCMIDLTool"/>
+ <Tool
+ Name="VCPostBuildEventTool"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCXMLDataGeneratorTool"/>
+ <Tool
+ Name="VCWebDeploymentTool"/>
+ <Tool
+ Name="VCManagedWrapperGeneratorTool"/>
+ <Tool
+ Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}">
+ <File
+ RelativePath=".\strsafe.c">
+ </File>
+ <File
+ RelativePath=".\strsafe.h">
+ </File>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}">
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx"
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}">
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/Src/nu/threadname.h b/Src/nu/threadname.h
new file mode 100644
index 00000000..d89b5bea
--- /dev/null
+++ b/Src/nu/threadname.h
@@ -0,0 +1,36 @@
+#ifndef NULLSOFT_UTILITY_THREADNAME_H
+#define NULLSOFT_UTILITY_THREADNAME_H
+
+#ifdef _DEBUG
+#include <windows.h>
+
+typedef struct tagTHREADNAME_INFO
+{
+ DWORD dwType; // must be 0x1000
+ LPCSTR szName; // pointer to name (in user addr space)
+ DWORD dwThreadID; // thread ID (-1=caller thread)
+ DWORD dwFlags; // reserved for future use, must be zero
+}
+THREADNAME_INFO;
+
+__inline void SetThreadName(DWORD dwThreadID, const char *szThreadName)
+{
+ THREADNAME_INFO info;
+ info.dwType = 0x1000;
+ info.szName = szThreadName;
+ info.dwThreadID = dwThreadID;
+ info.dwFlags = 0;
+
+ __try
+ {
+ RaiseException( 0x406D1388, 0, sizeof(info) / sizeof(DWORD), (const ULONG_PTR *)&info );
+ }
+ __except(EXCEPTION_CONTINUE_EXECUTION)
+ {}
+}
+
+#else
+#define SetThreadName(x,y)
+#endif
+
+#endif
diff --git a/Src/nu/threadpool/ThreadFunctions.cpp b/Src/nu/threadpool/ThreadFunctions.cpp
new file mode 100644
index 00000000..0ae23730
--- /dev/null
+++ b/Src/nu/threadpool/ThreadFunctions.cpp
@@ -0,0 +1,79 @@
+#include "ThreadFunctions.h"
+#include "threadpool_types.h"
+
+ThreadFunctions::ThreadFunctions(int create_function_list)
+{
+ if (create_function_list)
+ {
+ functions_semaphore = CreateSemaphore(0, 0, ThreadPoolTypes::MAX_SEMAPHORE_VALUE, 0);
+ InitializeCriticalSectionAndSpinCount(&functions_guard, 200);
+ }
+ else
+ functions_semaphore = 0;
+}
+
+ThreadFunctions::~ThreadFunctions()
+{
+ if (functions_semaphore)
+ {
+ CloseHandle(functions_semaphore);
+ DeleteCriticalSection(&functions_guard);
+ }
+}
+
+void ThreadFunctions::Add(HANDLE handle, api_threadpool::ThreadPoolFunc func, void *user_data, intptr_t id)
+{
+ Nullsoft::Utility::AutoLock l(guard);
+ Data *new_data = (Data *)calloc(1, sizeof(Data));
+ new_data->func = func;
+ new_data->user_data = user_data;
+ new_data->id = id;
+ data[handle] = new_data;
+}
+
+bool ThreadFunctions::Get(HANDLE handle, api_threadpool::ThreadPoolFunc *func, void **user_data, intptr_t *id)
+{
+ Nullsoft::Utility::AutoLock l(guard);
+ DataMap::iterator found = data.find(handle);
+ if (found == data.end())
+ return false;
+
+ const Data *d = found->second;
+ *func = d->func;
+ *user_data = d->user_data;
+ *id = d->id;
+ return true;
+}
+
+void ThreadFunctions::QueueFunction(api_threadpool::ThreadPoolFunc func, void *user_data, intptr_t id)
+{
+ Data *new_data = (Data *)calloc(1, sizeof(Data));
+ new_data->func = func;
+ new_data->user_data = user_data;
+ new_data->id = id;
+ EnterCriticalSection(&functions_guard);
+ functions_list.push_front(new_data);
+ LeaveCriticalSection(&functions_guard); // unlock before releasing the semaphore early so we don't lock convoy
+ ReleaseSemaphore(functions_semaphore, 1, 0);
+}
+
+bool ThreadFunctions::PopFunction(api_threadpool::ThreadPoolFunc *func, void **user_data, intptr_t *id)
+{
+ EnterCriticalSection(&functions_guard);
+ if (!functions_list.empty())
+ {
+ ThreadFunctions::Data *data = functions_list.back();
+ functions_list.pop_back();
+ LeaveCriticalSection(&functions_guard);
+ *func = data->func;
+ *user_data = data->user_data;
+ *id = data->id;
+ free(data);
+ return true;
+ }
+ else
+ {
+ LeaveCriticalSection(&functions_guard);
+ return false;
+ }
+}
diff --git a/Src/nu/threadpool/ThreadFunctions.h b/Src/nu/threadpool/ThreadFunctions.h
new file mode 100644
index 00000000..bbc5e6c6
--- /dev/null
+++ b/Src/nu/threadpool/ThreadFunctions.h
@@ -0,0 +1,31 @@
+#pragma once
+#include "api_threadpool.h"
+#include <map>
+#include <deque>
+#include "../AutoLock.h"
+
+class ThreadFunctions
+{
+public:
+ struct Data
+ {
+ api_threadpool::ThreadPoolFunc func;
+ void *user_data;
+ intptr_t id;
+ };
+ ThreadFunctions(int create_function_list=1);
+ ~ThreadFunctions();
+ void Add(HANDLE handle, api_threadpool::ThreadPoolFunc func, void *user_data, intptr_t id);
+ bool Get(HANDLE handle, api_threadpool::ThreadPoolFunc *func, void **user_data, intptr_t *id);
+ void QueueFunction(api_threadpool::ThreadPoolFunc func, void *user_data, intptr_t id);
+ bool PopFunction(api_threadpool::ThreadPoolFunc *func, void **user_data, intptr_t *id);
+
+ typedef std::map<HANDLE, const ThreadFunctions::Data*> DataMap;
+ DataMap data;
+ Nullsoft::Utility::LockGuard guard;
+
+ typedef std::deque<ThreadFunctions::Data*> FuncList;
+ FuncList functions_list;
+ CRITICAL_SECTION functions_guard;
+ HANDLE functions_semaphore;
+};
diff --git a/Src/nu/threadpool/ThreadID.cpp b/Src/nu/threadpool/ThreadID.cpp
new file mode 100644
index 00000000..28d6f1ca
--- /dev/null
+++ b/Src/nu/threadpool/ThreadID.cpp
@@ -0,0 +1,274 @@
+#include "ThreadID.h"
+
+DWORD ThreadID::thread_func_stub(LPVOID param)
+{
+ ThreadID *t = static_cast<ThreadID*>(param);
+ if (t != NULL)
+ {
+ return t->ThreadFunction();
+ }
+ else return 0;
+}
+
+void ThreadID::Kill()
+{
+ if (threadHandle && threadHandle != INVALID_HANDLE_VALUE)
+ {
+ //cut: WaitForSingleObject(threadHandle, INFINITE);
+ while (WaitForMultipleObjectsEx(1, &threadHandle, FALSE, INFINITE, TRUE) != WAIT_OBJECT_0)
+ {
+ }
+ }
+
+}
+
+ThreadID::ThreadID(ThreadFunctions *t_f, HANDLE killswitch, HANDLE global_functions_semaphore,
+ ThreadPoolTypes::HandleList &inherited_handles,
+ volatile LONG *thread_count, HANDLE _max_load_event,
+ int _reserved, int _com_type) : ThreadFunctions(_reserved)
+{
+ /* initialize values */
+ released = false;
+ InitializeCriticalSection(&handle_lock);
+
+ /* grab values passed to us */
+ reserved = _reserved;
+ com_type = _com_type;
+ max_load_event = _max_load_event;
+ global_functions = t_f;
+ num_threads_available = thread_count;
+
+ /* wait_handles[0] is kill switch */
+ wait_handles.push_back(killswitch);
+
+ /* wait_handles[1] is wake switch */
+ wakeHandle = CreateSemaphore(0, 0, ThreadPoolTypes::MAX_SEMAPHORE_VALUE, 0);
+ wait_handles.push_back(wakeHandle);
+
+ if (reserved)
+ {
+ /* if thread is reserved,
+ wait_handles[2] is a Funcion Call wake semaphore
+ for this thread only. */
+ wait_handles.push_back(functions_semaphore); // WAIT_OBJECT_0+1 == per-thread queued functions
+ }
+ else
+ {
+ /* if thread is not reserved,
+ wait_handles[2] is a Function Call wake semaphore
+ global to all threads */
+ wait_handles.push_back(global_functions_semaphore); // WAIT_OBJECT_0+2 == any-thread queued functions
+ }
+
+ /* add inherited handles
+ (handles added to thread pool before this thread was created) */
+ for ( ThreadPoolTypes::HandleList::iterator itr = inherited_handles.begin(); itr != inherited_handles.end(); itr++ )
+ {
+ wait_handles.push_back( *itr );
+ }
+
+ /* start thread */
+ threadHandle = CreateThread(0, 0, thread_func_stub, this, 0, 0);
+}
+
+ThreadID::~ThreadID()
+{
+ CloseHandle(threadHandle);
+ CloseHandle(wakeHandle);
+ DeleteCriticalSection(&handle_lock);
+}
+
+bool ThreadID::TryAddHandle(HANDLE new_handle)
+{
+ // let's see if we get lucky and can access the handle list directly
+ if (TryEnterCriticalSection(&handle_lock))
+ {
+ // made it
+ wait_handles.push_back(new_handle);
+ LeaveCriticalSection(&handle_lock);
+ return true;
+ }
+ else
+ {
+ ReleaseSemaphore(wakeHandle, 1, 0); // kick the thread out of WaitForMultiple...
+ return false;
+ }
+}
+
+void ThreadID::WaitAddHandle(HANDLE handle)
+{
+ // wakeHandle already got released once by nature of this function being called
+ EnterCriticalSection(&handle_lock);
+ wait_handles.push_back(handle);
+ LeaveCriticalSection(&handle_lock);
+ ReleaseSemaphore(wakeHandle, 1, 0); // kick out the second wait
+}
+
+void ThreadID::AddHandle(HANDLE new_handle)
+{
+ if (!TryAddHandle(new_handle))
+ WaitAddHandle(new_handle);
+}
+
+bool ThreadID::TryRemoveHandle(HANDLE handle)
+{
+ // let's see if we get lucky and can access the handle list directly
+ if (TryEnterCriticalSection(&handle_lock))
+ {
+ RemoveHandle_Internal(handle);
+ LeaveCriticalSection(&handle_lock);
+ return true;
+ }
+ else
+ {
+ ReleaseSemaphore(wakeHandle, 1, 0); // kick the thread out of WaitForMultiple...
+ return false;
+ }
+ return false;
+}
+
+void ThreadID::WaitRemoveHandle(HANDLE handle)
+{
+ // wakeHandle already got released once by nature of this function being called
+ EnterCriticalSection(&handle_lock);
+ RemoveHandle_Internal(handle);
+ LeaveCriticalSection(&handle_lock);
+ ReleaseSemaphore(wakeHandle, 1, 0); // kick out the second wait
+}
+
+void ThreadID::RemoveHandle(HANDLE handle)
+{
+ if (!TryRemoveHandle(handle))
+ WaitRemoveHandle(handle);
+}
+
+void ThreadID::RemoveHandle_Internal(HANDLE handle)
+{
+ // first three handles are reserved, so start after that
+ for (size_t i=3;i<wait_handles.size();i++)
+ {
+ if (wait_handles[i] == handle)
+ {
+ wait_handles.erase(wait_handles.begin() + i);
+ i--;
+ }
+ }
+}
+
+bool ThreadID::IsReserved() const
+{
+ return !!reserved;
+}
+
+DWORD CALLBACK ThreadID::ThreadFunction()
+{
+ switch(com_type)
+ {
+ case api_threadpool::FLAG_REQUIRE_COM_MT:
+ CoInitializeEx(0, COINIT_MULTITHREADED);
+ break;
+ case api_threadpool::FLAG_REQUIRE_COM_STA:
+ CoInitialize(0);
+ break;
+ }
+
+ while (1)
+ {
+ InterlockedIncrement(num_threads_available);
+ EnterCriticalSection(&handle_lock);
+ DWORD ret = WaitForMultipleObjectsEx((DWORD)wait_handles.size(), wait_handles.data(), FALSE, INFINITE, TRUE);
+ // cut: LeaveCriticalSection(&handle_lock);
+ if (InterlockedDecrement(num_threads_available) == 0 && !reserved)
+ SetEvent(max_load_event); // notify the watch dog if all the threads are used up
+
+ if (ret == WAIT_OBJECT_0)
+ {
+ // killswitch
+ LeaveCriticalSection(&handle_lock);
+ break;
+ }
+ else if (ret == WAIT_OBJECT_0 + 1)
+ {
+ // we got woken up to release the handles lock
+ // wait for the second signal
+ LeaveCriticalSection(&handle_lock);
+ InterlockedIncrement(num_threads_available);
+ WaitForSingleObject(wakeHandle, INFINITE);
+ InterlockedDecrement(num_threads_available);
+ }
+ else if (ret == WAIT_OBJECT_0 + 2)
+ {
+ LeaveCriticalSection(&handle_lock);
+ api_threadpool::ThreadPoolFunc func;
+ void *user_data;
+ intptr_t id;
+ if (reserved)
+ {
+ // per-thread queued functions
+ if (PopFunction(&func, &user_data, &id))
+ {
+ func(0, user_data, id);
+ }
+ }
+ else
+ {
+ // global queued functions
+ if (global_functions->PopFunction(&func, &user_data, &id))
+ {
+ func(0, user_data, id);
+ }
+ }
+ }
+ else if (ret > WAIT_OBJECT_0 && ret < (WAIT_OBJECT_0 + wait_handles.size()))
+ {
+ DWORD index = ret - WAIT_OBJECT_0;
+ HANDLE handle = wait_handles[index];
+ LeaveCriticalSection(&handle_lock);
+ /* !!! race condition here if someone calls ThreadPool::RemoveHandle and then CloseHandle() !!!
+ before calling RemoveHandle, caller needs to either
+ ensure that Event is unsignalled (And won't be signalled)
+ or call RemoveHandle from within the function callback */
+ api_threadpool::ThreadPoolFunc func;
+ void *user_data;
+ intptr_t id;
+ if (global_functions->Get(handle, &func, &user_data, &id))
+ {
+ func(handle, user_data, id);
+ }
+ }
+ else
+ {
+ LeaveCriticalSection(&handle_lock);
+ }
+ }
+ if (com_type & api_threadpool::MASK_COM_FLAGS)
+ CoUninitialize();
+ return 0;
+}
+
+bool ThreadID::CanRunCOM(int flags) const
+{
+ switch(com_type)
+ {
+ case api_threadpool::FLAG_REQUIRE_COM_MT: // if we're a CONIT_MULTITHREADEX thread (default)
+ return !(flags & api_threadpool::FLAG_REQUIRE_COM_STA); // don't let STA stuff run
+ case api_threadpool::FLAG_REQUIRE_COM_STA: // if we're a CoInitialize(0) thread
+ return !(flags & api_threadpool::FLAG_REQUIRE_COM_MT); // don't let MT stuff run
+ }
+ return false; // shouldn't get here
+}
+
+bool ThreadID::IsReleased() const
+{
+ return released;
+}
+
+void ThreadID::Reserve()
+{
+ released=false;
+}
+
+void ThreadID::Release()
+{
+ released=true;
+} \ No newline at end of file
diff --git a/Src/nu/threadpool/ThreadID.h b/Src/nu/threadpool/ThreadID.h
new file mode 100644
index 00000000..e5d1e09e
--- /dev/null
+++ b/Src/nu/threadpool/ThreadID.h
@@ -0,0 +1,56 @@
+#pragma once
+#include <windows.h>
+#include "ThreadFunctions.h"
+#include "threadpool_types.h"
+#include <vector>
+
+
+class ThreadID : private ThreadFunctions
+{
+public:
+ static DWORD CALLBACK thread_func_stub(LPVOID param);
+ ThreadID(ThreadFunctions *t_f, HANDLE killswitch, HANDLE global_functions_semaphore, ThreadPoolTypes::HandleList &inherited_handles, volatile LONG *thread_count, HANDLE _max_load_event, int _reserved, int _com_type);
+ ~ThreadID();
+ void Kill();
+
+ /* Try and Wait must be paired!!! */
+ bool TryAddHandle(HANDLE new_handle);
+ void WaitAddHandle(HANDLE new_handle);
+ void AddHandle(HANDLE new_handle);
+
+ /* Try and Wait must be paired!!! */
+ bool TryRemoveHandle(HANDLE handle);
+ void WaitRemoveHandle(HANDLE handle);
+ void RemoveHandle(HANDLE handle);
+
+ using ThreadFunctions::QueueFunction;
+ bool IsReserved() const;
+ bool IsReleased() const;
+ bool CanRunCOM(int flags) const;
+ void Reserve(); // re-reserves a released thread
+ void Release(); // release a reversed thread
+private:
+ void RemoveHandle_Internal(HANDLE handle);
+ DWORD CALLBACK ThreadFunction();
+
+ int reserved;
+ ThreadFunctions *global_functions;
+ volatile LONG *num_threads_available;
+ int com_type;
+ bool released;
+
+ ThreadFunctions local_functions;
+
+ // list of handles we're waiting on
+ typedef std::vector<HANDLE> HandleList;
+ HandleList wait_handles;
+ CRITICAL_SECTION handle_lock;
+
+ // handles we create/own
+ HANDLE threadHandle;
+ HANDLE wakeHandle;
+
+ // handles given to us
+ HANDLE max_load_event;
+
+};
diff --git a/Src/nu/threadpool/ThreadPool.cpp b/Src/nu/threadpool/ThreadPool.cpp
new file mode 100644
index 00000000..950df5d1
--- /dev/null
+++ b/Src/nu/threadpool/ThreadPool.cpp
@@ -0,0 +1,365 @@
+#include "ThreadPool.h"
+
+ThreadPool::ThreadPool()
+{
+ for ( int i = 0; i < THREAD_TYPES; i++ )
+ {
+ num_threads_available[ i ] = 0;
+ max_load_event[ i ] = CreateEvent( NULL, TRUE, FALSE, NULL );
+ }
+
+ killswitch = CreateEvent( NULL, TRUE, FALSE, NULL );
+
+ // one thread of each type to start
+ for ( int i = 0; i < 2; i++ )
+ CreateNewThread_Internal( i );
+
+ watchdog_thread_handle = CreateThread( 0, 0, WatchDogThreadProcedure_stub, this, 0, 0 );
+}
+
+void ThreadPool::Kill()
+{
+ SetEvent( killswitch );
+ WaitForSingleObject( watchdog_thread_handle, INFINITE );
+ CloseHandle( watchdog_thread_handle );
+
+ for ( ThreadID *l_thread : threads )
+ {
+ l_thread->Kill();
+ delete l_thread;
+ }
+
+ CloseHandle( killswitch );
+
+ for ( int i = 0; i < THREAD_TYPES; i++ )
+ CloseHandle( max_load_event[ i ] );
+}
+
+DWORD ThreadPool::WatchDogThreadProcedure_stub( LPVOID param )
+{
+ ThreadPool *_this = (ThreadPool *)param;
+ return _this->WatchDogThreadProcedure();
+}
+
+
+/*
+watchdog will get woken up when number of available threads hits zero
+it creates a new thread, sleeps for a bit to let things "settle" and then reset the event
+it will need a copy of all "any-thread" handles to build the new thread, and will need to manage in a thread safe way
+(so a new thread doesn't "miss" a handle that is added in the interim)
+*/
+DWORD CALLBACK ThreadPool::WatchDogThreadProcedure()
+{
+ // we ignore the max load event for reserved threads
+ HANDLE events[ 3 ] = { killswitch, max_load_event[ TYPE_MT ], max_load_event[ TYPE_STA ] };
+
+ while ( 1 )
+ {
+ DWORD ret = WaitForMultipleObjects( 3, events, FALSE, INFINITE );
+ if ( ret == WAIT_OBJECT_0 )
+ {
+ break;
+ }
+ else if ( ret == WAIT_OBJECT_0 + 1 || ret == WAIT_OBJECT_0 + 2 )
+ {
+ int thread_type = ret - ( WAIT_OBJECT_0 + 1 );
+ // this signal is for "full thread load reached"
+
+ // lets make sure we're actually at max capacity
+ Sleep( 10 ); // sleep a bit
+ if ( num_threads_available[ thread_type ] != 0 ) // see if we're still fully-loaded
+ continue;
+
+ guard.Lock();
+ CreateNewThread_Internal( thread_type );
+ guard.Unlock();
+
+ Sleep( 250 ); // give the system time to 'settle down' so we don't spawn a ton of threads in a row
+
+ ResetEvent( max_load_event[ thread_type ] );
+ }
+ }
+
+ return 0;
+}
+
+ThreadID *ThreadPool::ReserveThread( int flags )
+{
+
+ // first, check to see if there's any released threads we can grab
+ Nullsoft::Utility::AutoLock threadlock( guard );
+ for ( ThreadID *t : threads )
+ {
+ if ( t->IsReserved() && t->IsReleased() && t->CanRunCOM( flags ) )
+ {
+ t->Reserve();
+ return t;
+ }
+ }
+
+ // TODO: if there are enough free threads available, mark one as reserved
+// this will involve signalling the thread to switch to 'reserved' mode
+// swapping out the 'function list' semaphore with a local one
+// and removing all 'busy handles' from the queue
+// can probably use the 'wake' handle to synchronize this
+
+/*
+int thread_type = GetThreadType(flags);
+if (num_threads_available[thread_type > 2])
+{
+ for (size_t i=0;i!=threads.size();i++)
+ {
+ if (threads[i]->IsReserved() == false && threads[i]->CanRunCOM(flags))
+ {
+
+ }
+ }
+}
+*/
+
+ ThreadID *new_thread = CreateNewThread_Internal( GetThreadType( flags, 1 ) );
+
+ return new_thread;
+}
+
+void ThreadPool::ReleaseThread( ThreadID *thread_id )
+{
+ if ( thread_id )
+ {
+ // lock so there's no race condition between ReserveThread() and ReleaseThread()
+ Nullsoft::Utility::AutoLock threadlock( guard );
+ thread_id->Release();
+ }
+}
+
+
+int ThreadPool::AddHandle( ThreadID *thread_id, HANDLE handle, api_threadpool::ThreadPoolFunc func, void *user_data, intptr_t id, int flags )
+{
+ // TODO: need to ensure that handles are not duplicated
+ thread_functions.Add( handle, func, user_data, id );
+
+ if ( thread_id )
+ {
+ if ( thread_id->CanRunCOM( flags ) )
+ thread_id->AddHandle( handle );
+ else
+ return 1;
+ return 0;
+ }
+ else
+ {
+ /* increment thread counts temporarily - because the massive wake-up
+ causes thread counts to go to 0 */
+ for ( int i = 0; i < THREAD_TYPES; i++ )
+ InterlockedIncrement( &num_threads_available[ i ] );
+
+ guard.Lock();
+
+ AddHandle_Internal( 0, handle, flags );
+
+ bool thread_types[ THREAD_TYPES ];
+ GetThreadTypes( flags, thread_types );
+
+ for ( int i = 0; i < THREAD_TYPES; i++ )
+ {
+ if ( thread_types[ i ] )
+ any_thread_handles[ i ].push_back( handle );
+ }
+
+ guard.Unlock();
+
+ for ( int i = 0; i < THREAD_TYPES; i++ )
+ InterlockedDecrement( &num_threads_available[ i ] );
+
+
+ }
+
+ return 0;
+}
+
+/* helper functions for adding/removing handles,
+we keep going through the list as long as we can add/remove immediately.
+once we have to block, we recurse the function starting at the next handle
+when the function returns, we wait.
+this lets us do some work rather than sit and wait for each thread's lock */
+void ThreadPool::RemoveHandle_Internal(size_t start, HANDLE handle)
+{
+ for (;start!=threads.size();start++)
+ {
+ ThreadID *t = threads[start];
+ if (!t->TryRemoveHandle(handle)) // try to remove
+ {
+ // have to wait
+ RemoveHandle_Internal(start+1, handle); // recurse start with the next thread
+ t->WaitRemoveHandle(handle); // finish the job
+ return;
+ }
+ }
+}
+
+void ThreadPool::AddHandle_Internal(size_t start, HANDLE handle, int flags)
+{
+ for (;start<threads.size();start++)
+ {
+ ThreadID *t = threads[start];
+ if ((flags & api_threadpool::FLAG_LONG_EXECUTION) && t->IsReserved())
+ continue;
+
+ if (!t->CanRunCOM(flags))
+ continue;
+
+ if (!t->TryAddHandle(handle)) // try to add
+ {
+ // have to wait,
+ AddHandle_Internal(start+1, handle, flags); // recurse start with the next thread
+ t->WaitAddHandle(handle); // finish the job
+ return;
+ }
+ }
+}
+
+void ThreadPool::RemoveHandle(ThreadID *thread_id, HANDLE handle)
+{
+ if (thread_id)
+ {
+ thread_id->RemoveHandle(handle);
+ }
+ else
+ {
+ /* increment thread counts temporarily - because the massive wake-up
+ causes thread counts to go to 0 */
+ for (int i=0;i<THREAD_TYPES;i++)
+ InterlockedIncrement(&num_threads_available[i]);
+ guard.Lock();
+ RemoveHandle_Internal(0, handle);
+
+ for (int j=0;j<THREAD_TYPES;j++)
+ {
+ //for (ThreadPoolTypes::HandleList::iterator itr = any_thread_handles[j].begin();
+ // itr != any_thread_handles[j].end();
+ // itr++)
+
+ ThreadPoolTypes::HandleList::iterator itr = any_thread_handles[j].begin();
+ while(itr != any_thread_handles[j].end())
+ {
+ if (*itr == handle)
+ {
+ itr = any_thread_handles[j].erase(itr);
+ }
+ else
+ {
+ itr++;
+ }
+ }
+ }
+ guard.Unlock();
+ for (int i=0;i<THREAD_TYPES;i++)
+ InterlockedDecrement(&num_threads_available[i]);
+ }
+}
+
+int ThreadPool::RunFunction(ThreadID *threadid, api_threadpool::ThreadPoolFunc func, void *user_data, intptr_t id, int flags)
+{
+ if (threadid)
+ threadid->QueueFunction(func, user_data, id);
+ else
+ thread_functions.QueueFunction(func, user_data, id);
+ return 0;
+}
+
+ThreadID *ThreadPool::CreateNewThread_Internal(int thread_type)
+{
+ int reserved=0;
+ int com_type = api_threadpool::FLAG_REQUIRE_COM_MT; // default
+ switch(thread_type)
+ {
+ case TYPE_STA_RESERVED:
+ reserved=1;
+ case TYPE_STA:
+ com_type = api_threadpool::FLAG_REQUIRE_COM_STA;
+ break;
+ case TYPE_MT_RESERVED:
+ reserved=1;
+ case TYPE_MT:
+ com_type = api_threadpool::FLAG_REQUIRE_COM_MT;
+ break;
+ }
+
+ Nullsoft::Utility::AutoLock threadlock(guard); // lock here (rather than after new ThreadID) to protect any_thread_handles
+ ThreadID *new_thread = new ThreadID(&thread_functions, killswitch, thread_functions.functions_semaphore,
+ any_thread_handles[thread_type],
+ &num_threads_available[thread_type], max_load_event[thread_type],
+ reserved, com_type);
+ threads.push_back(new_thread);
+ return new_thread;
+}
+
+size_t ThreadPool::GetNumberOfThreads()
+{
+ Nullsoft::Utility::AutoLock threadlock(guard);
+ return threads.size();
+}
+
+size_t ThreadPool::GetNumberOfActiveThreads()
+{
+ size_t numThreads = GetNumberOfThreads();
+ for (int i=0;i<THREAD_TYPES;i++)
+ numThreads -= num_threads_available[i];
+ return numThreads;
+}
+
+int ThreadPool::GetThreadType(int flags, int reserved)
+{
+ flags &= api_threadpool::MASK_COM_FLAGS;
+ int thread_type=TYPE_MT;
+ switch(flags)
+ {
+ case api_threadpool::FLAG_REQUIRE_COM_STA:
+ thread_type = reserved?TYPE_STA_RESERVED:TYPE_STA;
+ break;
+ case 0: // default
+ case api_threadpool::FLAG_REQUIRE_COM_MT:
+ thread_type = reserved?TYPE_MT_RESERVED:TYPE_MT;
+ break;
+ }
+
+ return thread_type;
+}
+
+void ThreadPool::GetThreadTypes(int flags, bool types[THREAD_TYPES])
+{
+ for (int i=0;i<THREAD_TYPES;i++)
+ {
+ types[i]=true;
+ }
+
+ if (flags & api_threadpool::FLAG_REQUIRE_COM_STA)
+ {
+ types[TYPE_MT] = false;
+ types[TYPE_MT] = false;
+ }
+
+ if (flags & api_threadpool::FLAG_REQUIRE_COM_STA)
+ {
+ types[TYPE_STA] = false;
+ types[TYPE_STA_RESERVED] = false;
+ }
+
+ if (flags & api_threadpool::FLAG_LONG_EXECUTION)
+ {
+ types[TYPE_STA_RESERVED] = false;
+ types[TYPE_MT_RESERVED] = false;
+ }
+
+}
+#define CBCLASS ThreadPool
+START_DISPATCH;
+CB(RESERVETHREAD, ReserveThread)
+VCB(RELEASETHREAD, ReleaseThread)
+CB(ADDHANDLE, AddHandle)
+VCB(REMOVEHANDLE, RemoveHandle)
+CB(RUNFUNCTION, RunFunction)
+CB(GETNUMBEROFTHREADS, GetNumberOfThreads)
+CB(GETNUMBEROFACTIVETHREADS, GetNumberOfActiveThreads)
+END_DISPATCH;
+#undef CBCLASS
diff --git a/Src/nu/threadpool/ThreadPool.h b/Src/nu/threadpool/ThreadPool.h
new file mode 100644
index 00000000..f68efc12
--- /dev/null
+++ b/Src/nu/threadpool/ThreadPool.h
@@ -0,0 +1,98 @@
+#pragma once
+
+#include <windows.h>
+#include <bfc/platform/types.h>
+#include <vector>
+#include "../autolock.h"
+#include "ThreadID.h"
+#include "ThreadFunctions.h"
+#include "threadpool_types.h"
+/* random notes
+
+HANDLEs common to all threads
+
+WaitForMultipleObjectsEx() around these
+0 - killswitch
+1 - shared APC event. since threads might want to use APCs themselves, we'll use a different mechanism (thread-safe FIFO and an event). the intention is that APCs that can go on any thread will use this handle
+2 - per thread APC event.
+
+
+parameters for "run my function" method
+function pointer, user data, flags
+flags:
+interrupt - for very short non-locking functions where it is safe to interrupt another thread, uses QueueUserAPC
+no_wait - spawn a new thread if all threads are busy
+com_multithreaded - all threads are created with CoInitialize(0), if you need a COINIT_MULTITHREADED thread, use this flag
+
+parameters for "add my handle" method
+handle, function pointer, user data, flags
+flags:
+single_thread - only one thread in the pool will wait on your object, useful if your handle is not auto-reset
+
+parameters for "function call repeat" - calls your function until you return 0
+function pointer, user data, flags
+flags:
+single_thread - keep calling on the same thread
+*/
+
+
+class ThreadPool : public api_threadpool
+{
+public:
+ static const char *getServiceName() { return "Thread Pool API"; }
+ static const GUID getServiceGuid() { return ThreadPoolGUID; }
+public:
+ // Owner API:
+ ThreadPool();
+ void Kill();
+
+ // User API:
+ /* If you have multiple events, APCs, etc and you need them to always run on the same thread
+ you can reserve one */
+ ThreadID *ReserveThread(int flags);
+ /* Release a thread you've previously reserved */
+ void ReleaseThread(ThreadID *thread_id);
+
+ /* adds a waitable handle to the thread pool. when the event is signalled, your function ptr will get called
+ user_data and id values get passed to your function.
+ your function should return 1 to indicate that it can be removed
+ flags, see api_threadpool */
+ int AddHandle(ThreadID *threadid, HANDLE handle, api_threadpool::ThreadPoolFunc func, void *user_data, intptr_t id, int flags);
+ void RemoveHandle(ThreadID *threadid, HANDLE handle);
+ int RunFunction(ThreadID *threadid, api_threadpool::ThreadPoolFunc func, void *user_data, intptr_t id, int flags);
+
+ size_t GetNumberOfThreads(); // total number of threads in the threadpool
+ size_t GetNumberOfActiveThreads(); // number of threads that are currently being used (inside user function but not necessarily busy)
+
+private:
+ enum
+ {
+ TYPE_MT = 0,
+ TYPE_STA = 1,
+ TYPE_MT_RESERVED = 2,
+ TYPE_STA_RESERVED = 3,
+
+ THREAD_TYPES = 4, // two thread types, single threaded apartment COM and multithreaded COM
+ };
+private:
+ static DWORD CALLBACK WatchDogThreadProcedure_stub(LPVOID param);
+ ThreadID *CreateNewThread_Internal(int thread_type = 0);
+ DWORD CALLBACK WatchDogThreadProcedure();
+ static int GetThreadType(int flags, int reserved = 0);
+ static void GetThreadTypes(int flags, bool types[THREAD_TYPES]);
+ void RemoveHandle_Internal(size_t start, HANDLE handle); // recursive helper function for RemoveHandle()
+ void AddHandle_Internal(size_t start, HANDLE handle, int flags); // recursive helper function for RemoveHandle()
+
+ Nullsoft::Utility::LockGuard guard; // guards threads, any_thread_handles, and non_reserved_handles data structures
+ typedef std::vector<ThreadID*> ThreadList;
+ ThreadList threads;
+ ThreadPoolTypes::HandleList any_thread_handles[THREAD_TYPES];
+ HANDLE killswitch;
+ HANDLE watchdog_thread_handle;
+ volatile LONG num_threads_available[THREAD_TYPES];
+ ThreadFunctions thread_functions;
+ HANDLE max_load_event[THREAD_TYPES];
+protected:
+ RECVS_DISPATCH;
+};
+
diff --git a/Src/nu/threadpool/TimerHandle.hpp b/Src/nu/threadpool/TimerHandle.hpp
new file mode 100644
index 00000000..ccbb6ef6
--- /dev/null
+++ b/Src/nu/threadpool/TimerHandle.hpp
@@ -0,0 +1,54 @@
+#ifndef NU_THREADPOOL_TIMERHANDLE_H
+#define NU_THREADPOOL_TIMERHANDLE_H
+
+#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x400)
+#error Must define _WIN32_WINNT >= 0x400 to use TimerHandle
+#endif
+
+#include <windows.h>
+#include <bfc/platform/types.h>
+/*
+TimerHandle() constructor will make a new timer handle
+TimerHandle(existing_handle) will "take over" an existing handle
+~TimerHandle() DOES NOT CloseHandle as this object is meant as a helper
+call Close() to kill the timer handle
+
+The timer will be "one shot" auto-reset.
+Because it is meant to be compatible with the threadpool, manual-reset timers and periodic timers
+are not recommended!! You will have re-entrancy problems
+If you want "periodic" behavior, call Wait() at the end of your ThreadPoolFunc
+*/
+class TimerHandle
+{
+public:
+ TimerHandle() { timerHandle = CreateWaitableTimer( 0, FALSE, 0 ); }
+ TimerHandle( HANDLE p_handle ) { timerHandle = p_handle; }
+
+ void Close() { CloseHandle( timerHandle ); }
+
+ void Wait( uint64_t p_milliseconds )
+ {
+ /* MSDN notes about SetWaitableTimer: 100 nanosecond resolution, Negative values indicate relative time*/
+ LARGE_INTEGER timeout = { 0 };
+ timeout.QuadPart = -( (int64_t)p_milliseconds * 1000LL /*to microseconds*/ * 10LL /* to 100 nanoseconds */ );
+ SetWaitableTimer( timerHandle, &timeout, 0, 0, 0, FALSE );
+ }
+
+ void Poll( uint64_t p_milliseconds ) // only use on a reserved thread!!!
+ {
+ /* MSDN notes about SetWaitableTimer: 100 nanosecond resolution, Negative values indicate relative time*/
+ LARGE_INTEGER timeout = { 0 };
+ timeout.QuadPart = -( (int64_t)p_milliseconds * 1000LL /*to microseconds*/ * 10LL /* to 100 nanoseconds */ );
+ SetWaitableTimer( timerHandle, &timeout, (LONG)p_milliseconds, 0, 0, FALSE );
+ }
+
+ /* TODO: WaitUntil method for absolute times */
+
+ void Cancel() { CancelWaitableTimer( timerHandle ); }
+ operator HANDLE() { return timerHandle; }
+
+private:
+ HANDLE timerHandle;
+};
+
+#endif // !NU_THREADPOOL_TIMERHANDLE_H \ No newline at end of file
diff --git a/Src/nu/threadpool/api_threadpool.h b/Src/nu/threadpool/api_threadpool.h
new file mode 100644
index 00000000..4cf5ab4f
--- /dev/null
+++ b/Src/nu/threadpool/api_threadpool.h
@@ -0,0 +1,92 @@
+#pragma once
+
+#include <windows.h>
+#include <bfc/platform/types.h>
+#include <bfc/dispatch.h>
+
+class ThreadID;
+
+class api_threadpool : public Dispatchable
+{
+protected:
+ api_threadpool() {}
+ ~api_threadpool() {}
+public:
+ typedef int (*ThreadPoolFunc)(HANDLE handle, void *user_data, intptr_t id);
+ enum
+ {
+ // pass this flag to AddHandle or RunFunction indicate that your thread function
+ // might run for a long time
+ FLAG_LONG_EXECUTION = 0x1,
+ FLAG_REQUIRE_COM_STA = 0x2,
+ FLAG_REQUIRE_COM_MT = 0x4,
+ MASK_COM_FLAGS = 0x6,
+ };
+
+public:
+ ThreadID *ReserveThread(int flags);
+ /* Release a thread you've previously reserved */
+ void ReleaseThread(ThreadID *thread_id);
+
+ /* adds a waitable handle to the thread pool. when the event is signalled, your function ptr will get called
+ user_data and id values get passed to your function.
+ your function should return 1 to indicate that it can be removed
+ flags, see api_threadpool */
+ int AddHandle(ThreadID *threadid, HANDLE handle, api_threadpool::ThreadPoolFunc func, void *user_data, intptr_t id, int flags);
+ void RemoveHandle(ThreadID *threadid, HANDLE handle);
+ int RunFunction(ThreadID *threadid, api_threadpool::ThreadPoolFunc func, void *user_data, intptr_t id, int flags);
+
+ size_t GetNumberOfThreads(); // total number of threads in the threadpool
+ size_t GetNumberOfActiveThreads(); // number of threads that are currently being used (inside user function but not necessarily busy)
+
+ enum
+ {
+ RESERVETHREAD = 0,
+ RELEASETHREAD = 1,
+ ADDHANDLE = 2,
+ REMOVEHANDLE = 3,
+ RUNFUNCTION = 4,
+ GETNUMBEROFTHREADS = 5,
+ GETNUMBEROFACTIVETHREADS = 6,
+ };
+};
+
+inline ThreadID *api_threadpool::ReserveThread(int flags)
+{
+ return _call(RESERVETHREAD, (ThreadID *)0, flags);
+}
+
+inline void api_threadpool::ReleaseThread(ThreadID *thread_id)
+{
+ _voidcall(RELEASETHREAD, thread_id);
+}
+
+inline int api_threadpool::AddHandle(ThreadID *threadid, HANDLE handle, api_threadpool::ThreadPoolFunc func, void *user_data, intptr_t id, int flags)
+{
+ return _call(ADDHANDLE, (int)1, threadid, handle, func, user_data, id, flags);
+}
+
+inline void api_threadpool::RemoveHandle(ThreadID *threadid, HANDLE handle)
+{
+ _voidcall(REMOVEHANDLE, threadid, handle);
+}
+
+inline int api_threadpool::RunFunction(ThreadID *threadid, api_threadpool::ThreadPoolFunc func, void *user_data, intptr_t id, int flags)
+{
+ return _call(RUNFUNCTION, (int)1, threadid, func, user_data, id, flags);
+}
+
+inline size_t api_threadpool::GetNumberOfThreads()
+{
+ return _call(GETNUMBEROFTHREADS, (size_t)0);
+}
+
+inline size_t api_threadpool::GetNumberOfActiveThreads()
+{
+ return _call(GETNUMBEROFACTIVETHREADS, (size_t)0);
+}
+
+// {4DE015D3-11D8-4ac6-A3E6-216DF5252107}
+static const GUID ThreadPoolGUID =
+{ 0x4de015d3, 0x11d8, 0x4ac6, { 0xa3, 0xe6, 0x21, 0x6d, 0xf5, 0x25, 0x21, 0x7 } };
+
diff --git a/Src/nu/threadpool/threadpool.sln b/Src/nu/threadpool/threadpool.sln
new file mode 100644
index 00000000..790cf5e9
--- /dev/null
+++ b/Src/nu/threadpool/threadpool.sln
@@ -0,0 +1,23 @@
+Microsoft Visual Studio Solution File, Format Version 8.00
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "threadpool", "threadpool.vcproj", "{12CC5DA2-87FF-456A-AF20-A243F168EFE8}"
+ ProjectSection(ProjectDependencies) = postProject
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfiguration) = preSolution
+ Debug = Debug
+ Release = Release
+ EndGlobalSection
+ GlobalSection(ProjectDependencies) = postSolution
+ EndGlobalSection
+ GlobalSection(ProjectConfiguration) = postSolution
+ {12CC5DA2-87FF-456A-AF20-A243F168EFE8}.Debug.ActiveCfg = Debug|Win32
+ {12CC5DA2-87FF-456A-AF20-A243F168EFE8}.Debug.Build.0 = Debug|Win32
+ {12CC5DA2-87FF-456A-AF20-A243F168EFE8}.Release.ActiveCfg = Release|Win32
+ {12CC5DA2-87FF-456A-AF20-A243F168EFE8}.Release.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ EndGlobalSection
+ GlobalSection(ExtensibilityAddIns) = postSolution
+ EndGlobalSection
+EndGlobal
diff --git a/Src/nu/threadpool/threadpool.vcproj b/Src/nu/threadpool/threadpool.vcproj
new file mode 100644
index 00000000..c526cdaa
--- /dev/null
+++ b/Src/nu/threadpool/threadpool.vcproj
@@ -0,0 +1,137 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="7.10"
+ Name="threadpool"
+ ProjectGUID="{12CC5DA2-87FF-456A-AF20-A243F168EFE8}"
+ Keyword="Win32Proj">
+ <Platforms>
+ <Platform
+ Name="Win32"/>
+ </Platforms>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="Debug"
+ IntermediateDirectory="Debug"
+ ConfigurationType="4"
+ CharacterSet="2">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="../../Wasabi"
+ PreprocessorDefinitions="WIN32;_DEBUG;_LIB;_WIN32_WINNT=0x400"
+ MinimalRebuild="TRUE"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="5"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="TRUE"
+ DebugInformationFormat="4"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile="$(OutDir)/threadpool.lib"/>
+ <Tool
+ Name="VCMIDLTool"/>
+ <Tool
+ Name="VCPostBuildEventTool"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCXMLDataGeneratorTool"/>
+ <Tool
+ Name="VCManagedWrapperGeneratorTool"/>
+ <Tool
+ Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="Release"
+ IntermediateDirectory="Release"
+ ConfigurationType="4"
+ CharacterSet="2">
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="../../Wasabi"
+ PreprocessorDefinitions="WIN32;NDEBUG;_LIB;_WIN32_WINNT=0x400"
+ RuntimeLibrary="4"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="TRUE"
+ DebugInformationFormat="3"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile="$(OutDir)/threadpool.lib"/>
+ <Tool
+ Name="VCMIDLTool"/>
+ <Tool
+ Name="VCPostBuildEventTool"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCXMLDataGeneratorTool"/>
+ <Tool
+ Name="VCManagedWrapperGeneratorTool"/>
+ <Tool
+ Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}">
+ <File
+ RelativePath=".\ThreadFunctions.cpp">
+ </File>
+ <File
+ RelativePath=".\ThreadID.cpp">
+ </File>
+ <File
+ RelativePath=".\ThreadPool.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}">
+ <File
+ RelativePath=".\api_threadpool.h">
+ </File>
+ <File
+ RelativePath=".\ThreadFunctions.h">
+ </File>
+ <File
+ RelativePath=".\ThreadID.h">
+ </File>
+ <File
+ RelativePath=".\ThreadPool.h">
+ </File>
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx"
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}">
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/Src/nu/threadpool/threadpool_types.h b/Src/nu/threadpool/threadpool_types.h
new file mode 100644
index 00000000..9a23e833
--- /dev/null
+++ b/Src/nu/threadpool/threadpool_types.h
@@ -0,0 +1,8 @@
+#pragma once
+#include <deque>
+#include <windows.h>
+namespace ThreadPoolTypes
+{
+ typedef std::deque<HANDLE> HandleList;
+ const int MAX_SEMAPHORE_VALUE = 1024; //some arbitrarily high amount*
+} \ No newline at end of file
diff --git a/Src/nu/trace.cpp b/Src/nu/trace.cpp
new file mode 100644
index 00000000..77faf092
--- /dev/null
+++ b/Src/nu/trace.cpp
@@ -0,0 +1,86 @@
+#include "./trace.h"
+#include <strsafe.h>
+
+#ifdef _DEBUG
+
+#ifdef __cplusplus
+extern "C" {
+#endif /*__cplusplus*/
+
+void DebugPrintfW(LPCWSTR format, ...)
+{
+ va_list argList;
+ wchar_t *pstr(NULL);
+ void *buffer;
+ size_t allocated(0), remaining(0);
+ int attempt(0);
+
+ va_start(argList, format);
+
+ do
+ {
+ attempt++;
+ if(attempt)
+ {
+ allocated += (512 * attempt);
+ buffer = realloc(pstr, allocated * sizeof(wchar_t));
+ if (NULL == buffer)
+ break;
+
+ pstr = (wchar_t*)buffer;
+ }
+
+ }
+ while (STRSAFE_E_INSUFFICIENT_BUFFER == StringCchVPrintfExW(pstr, allocated, NULL, &remaining,
+ STRSAFE_NULL_ON_FAILURE, format, argList));
+
+ OutputDebugStringW(pstr);
+
+ if (NULL != pstr)
+ free(pstr);
+
+ va_end(argList);
+
+}
+
+void DebugPrintfA(LPCSTR format, ...)
+{
+ va_list argList;
+ char *pstr(NULL);
+ void *buffer;
+ size_t allocated(0), remaining(0);
+ int attempt(0);
+
+ va_start(argList, format);
+
+ do
+ {
+ attempt++;
+ if(attempt)
+ {
+ allocated += (512 * attempt);
+ buffer = realloc(pstr, allocated * sizeof(char));
+ if (NULL == buffer)
+ break;
+
+ pstr = (char*)buffer;
+ }
+
+ }
+ while (STRSAFE_E_INSUFFICIENT_BUFFER == StringCchVPrintfExA(pstr, allocated, NULL, &remaining,
+ STRSAFE_NULL_ON_FAILURE, format, argList));
+
+ OutputDebugStringA(pstr);
+
+ if (NULL != pstr)
+ free(pstr);
+
+ va_end(argList);
+}
+
+
+#ifdef __cplusplus
+}
+#endif /*__cplusplus*/
+
+#endif /*_DEBUG*/ \ No newline at end of file
diff --git a/Src/nu/trace.h b/Src/nu/trace.h
new file mode 100644
index 00000000..9cb29462
--- /dev/null
+++ b/Src/nu/trace.h
@@ -0,0 +1,56 @@
+#ifndef NULLOSFT_MEDIALIBRARY_TRACE_HEADER
+#define NULLOSFT_MEDIALIBRARY_TRACE_HEADER
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+#pragma once
+#endif
+
+#ifdef _DEBUG
+
+#include <wtypes.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void DebugPrintfA(LPCSTR format, ...);
+void DebugPrintfW(LPCWSTR format, ...);
+
+#ifdef __cplusplus
+}
+#endif
+
+#ifdef UNICODE
+#define DebugPrintf DebugPrintfW
+#else
+#define DebugPrintf DebugPrintfA
+#endif // !UNICODE
+
+#define aTRACE OutputDebugStringA
+#define aTRACE_FMT DebugPrintfA
+#define aTRACE_LINE(x) aTRACE_FMT("%s\n", (x))
+
+#define wTRACE OutputDebugStringW
+#define wTRACE_FMT DebugPrintfW
+#define wTRACE_LINE(x) wTRACE_FMT(L"%s\n", (x))
+
+#define TRACE OutputDebugString
+#define TRACE_FMT DebugPrintf
+#define TRACE_LINE(x) TRACE_FMT(TEXT("%s\n"), (x))
+
+#else // _DEBUG
+
+#define aTRACE
+#define aTRACE_FMT
+#define aTRACE_LINE
+
+#define wTRACE
+#define wTRACE_FMT
+#define wTRACE_LINE
+
+#define TRACE
+#define TRACE_FMT
+#define TRACE_LINE
+
+#endif // _DEBUG
+#endif // NULLOSFT_MEDIALIBRARY_TRACE_HEADER \ No newline at end of file
diff --git a/Src/nu/wa_str.h b/Src/nu/wa_str.h
new file mode 100644
index 00000000..e8ba98f0
--- /dev/null
+++ b/Src/nu/wa_str.h
@@ -0,0 +1,61 @@
+#pragma once
+
+#ifdef WA_STR_EXPORT
+#include <bfc/platform/export.h>
+#define DLLIMPORT DLLEXPORT
+#else
+#define DLLIMPORT
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* if you need a temporary ref counted string to pass INTO a function
+ and have a hardcoded string you want to define on the stack,
+ you can use this macro.
+ e.g. wa_str myStr = WA_STR_STATIC("test"); */
+#define WA_STR_STATIC(x) ((wa_str)"\0\0\0\0" ## x);
+
+typedef void *wa_str;
+
+/* convert a C string into a ref counted string
+ if you own the string, try using wa_str_own instead */
+DLLIMPORT wa_str wa_strdup(const char *);
+
+/* add a reference to a string
+ returns the new pointer to use!!!
+ in some cases (e.g. WA_STR_STATIC strings) the string must be re-malloc'd
+ so be sure to assign the return value to your string
+ e.g. wa_str myCopy = wa_str_addref(str); */
+DLLIMPORT wa_str wa_str_retain(wa_str str);
+
+/* release a reference to a string */
+DLLIMPORT void wa_str_release(wa_str str);
+
+/* gets a C-style string from a ref counted string.
+ only valid for as long as you hold a reference! */
+DLLIMPORT const char *wa_str_get(wa_str str);
+
+/* copies the contents of a ref counted string into the passed buffer
+ *dest = 0 on empty string */
+DLLIMPORT void wa_str_strcpy(wa_str str, char *dest, size_t destlen);
+
+/* allocates a reference counted string large enough to hold the given character count
+ len MUST include null terminator (e.g. pass 5 to malloc enough for "test")
+ data is set to where the character data can be written to.
+ you'll need to null terminate the string you write */
+DLLIMPORT wa_str wa_str_malloc(size_t len, char **data);
+
+/* allocates a reference counted string using a C string you already have
+ frees your string with the supplied free_func when reference count reaches 0 */
+DLLIMPORT wa_str wa_str_own(char *ptr, void (*free_func)(char *));
+
+/* a convenient typedef for the above function.
+ if you want to pass the standard C free() function
+ use (WA_STR_FREE_FUNC)free */
+typedef void (*WA_STR_FREE_FUNC)(char *);
+
+#ifdef __cplusplus
+}
+#endif \ No newline at end of file
diff --git a/Src/nu/windowsTheme.cpp b/Src/nu/windowsTheme.cpp
new file mode 100644
index 00000000..2085e30c
--- /dev/null
+++ b/Src/nu/windowsTheme.cpp
@@ -0,0 +1,266 @@
+#include "./windowsTheme.h"
+#include <shlwapi.h>
+
+
+typedef UXTHEME (WINAPI *UXOPENTHEMEDATA)(HWND hwnd, LPCWSTR /*pszClassList*/);
+typedef HRESULT (WINAPI *UXCLOSETHEMEDATA)(UXTHEME /*hTheme*/);
+typedef HRESULT (WINAPI *UXSETWINDOWTHEME)(HWND /*hwnd*/, LPCWSTR /*pszSubAppName*/, LPCWSTR /*pszSubIdList*/);
+typedef HRESULT (WINAPI *UXDRAWTHEMEBACKGROUND)(UXTHEME /*hTheme*/, HDC /*hdc*/, INT /*iPartId*/, INT /*iStateId*/,
+ LPCRECT /*pRect*/, LPCRECT /*pClipRect*/);
+typedef HRESULT (WINAPI *UXDRAWTHEMEPARENTBACKGROUND)(HWND /*hwnd*/, HDC /*hdc*/, const RECT * /*prc*/);
+typedef HRESULT (WINAPI *UXDRAWTHEMEPARENTBACKGROUNDEX)(HWND /*hwnd*/, HDC /*hdc*/, DWORD /*dwFlags*/, const RECT * /*prc*/);
+typedef HRESULT (WINAPI *UXDRAWTHEMETEXT)(UXTHEME /*hTheme*/, HDC /*hdc*/, INT /*iPartId*/, INT /*iStateId*/,
+ LPCWSTR /*pszText*/, INT /*iCharCount*/, DWORD /*dwTextFlags*/,
+ DWORD /*dwTextFlags2*/, LPCRECT /*pRect*/);
+typedef COLORREF (WINAPI *UXGETTHEMESYSCOLOR)(UXTHEME /*hTheme*/, INT /*iColorID*/);
+
+typedef HRESULT (WINAPI *UXGETTHEMECOLOR)(UXTHEME /*hTheme*/, int /*iPartId*/, int /*iStateId*/, int /*iPropId*/, LPCOLORREF /*pColor*/);
+typedef HRESULT (WINAPI *UXGETTHEMEINT)(UXTHEME /*hTheme*/, int /*iPartId*/, int /*iStateId*/, int /*iPropId*/, LPINT /*piVal*/);
+typedef HRESULT (WINAPI *UXGETTHEMEMETRIC)(UXTHEME /*hTheme*/, HDC /*hdc*/, int /*iPartId*/, int /*iStateId*/, int /*iPropId*/, LPINT /*piVal*/);
+typedef HRESULT (WINAPI *UXGETTHEMEPARTSIZE)(UXTHEME /*hTheme*/, HDC /*hdc*/, int /*iPartId*/, int /*iStateId*/, LPCRECT /*prc*/, UXTHEMESIZE /*eSize*/, LPSIZE /*psz*/);
+typedef HRESULT (WINAPI *UXGETTHEMEBACKGROUNDCONTENTRECT)(UXTHEME /*hTheme*/, HDC /*hdc*/, int /*iPartId*/, int /*iStateId*/, LPCRECT /*pBoundingRect*/, LPRECT /*pContentRect*/);
+typedef HRESULT (WINAPI *UXENABLETHEMEDIALOGTEXTURE)(HWND /*hwnd*/, DWORD /*dwFlags*/);
+
+typedef BOOL (WINAPI *UXISAPPTHEMED)(void);
+typedef BOOL (WINAPI *UXISTHEMEACTIVE)(void);
+typedef BOOL (WINAPI *UXISTHEMEBACKGROUNDPARTIALLYTRANSPARENT)(UXTHEME /*hTheme*/, int /*iPartId*/, int /*iStateId*/);
+
+
+
+static UXOPENTHEMEDATA uxOpenThemeData = NULL;
+static UXCLOSETHEMEDATA uxCloseThemeData = NULL;
+static UXSETWINDOWTHEME uxSetWindowTheme = NULL;
+static UXISAPPTHEMED uxIsAppThemed = NULL;
+static UXISTHEMEACTIVE uxIsThemeActive = NULL;
+static UXDRAWTHEMEBACKGROUND uxDrawThemeBackground = NULL;
+static UXDRAWTHEMEPARENTBACKGROUND uxDrawThemeParentBackground = NULL;
+static UXDRAWTHEMEPARENTBACKGROUNDEX uxDrawThemeParentBackgroundEx = NULL;
+static UXDRAWTHEMETEXT uxDrawThemeText = NULL;
+static UXGETTHEMESYSCOLOR uxGetThemeSysColor = NULL;
+static UXGETTHEMECOLOR uxGetThemeColor = NULL;
+static UXGETTHEMEINT uxGetThemeInt = NULL;
+static UXGETTHEMEMETRIC uxGetThemeMetric = NULL;
+static UXGETTHEMEPARTSIZE uxGetThemePartSize = NULL;
+static UXGETTHEMEBACKGROUNDCONTENTRECT uxGetThemeBackgroundContentRect = NULL;
+static UXISTHEMEBACKGROUNDPARTIALLYTRANSPARENT uxIsThemeBackgroundPartiallyTransparent = NULL;
+static UXENABLETHEMEDIALOGTEXTURE uxEnableThemeDialogTexture = NULL;
+
+static HMODULE uxModule = NULL;
+static HRESULT uxLoadResult = E_WINTHEME_NOTLOADED;
+static BOOL fSysCheckOk = -1;
+static BOOL fComCtlCheckOk = -1;
+
+
+#define UXTHEME_LOADCHECK(__uxFunction){\
+ if (NULL == uxModule && FAILED(UxTheme_LoadLibrary())) return uxLoadResult;\
+ if (NULL == (__uxFunction)) return E_WINTHEME_BADFUNCTION;}
+
+#define UXTHEME_LOADCHECK_RESULT(__uxFunction, __failResult){\
+ if ((NULL == uxModule && FAILED(UxTheme_LoadLibrary())) || NULL == (__uxFunction))\
+ return (__failResult);}
+
+HRESULT STDAPICALLTYPE UxTheme_LoadLibrary(void)
+{
+ if (E_WINTHEME_NOTLOADED == uxLoadResult)
+ {
+ uxModule = LoadLibraryW(L"uxtheme.dll");
+ if (NULL == uxModule)
+ {
+ uxLoadResult = E_WINTHEME_LOADFAILED;
+ }
+ else
+ {
+ uxSetWindowTheme = (UXSETWINDOWTHEME)GetProcAddress(uxModule, "SetWindowTheme");
+ uxIsAppThemed = (UXISAPPTHEMED)GetProcAddress(uxModule, "IsAppThemed");
+ uxIsThemeActive = (UXISTHEMEACTIVE)GetProcAddress(uxModule, "IsThemeActive");
+ uxOpenThemeData = (UXOPENTHEMEDATA)GetProcAddress(uxModule, "OpenThemeData");
+ uxCloseThemeData = (UXCLOSETHEMEDATA)GetProcAddress(uxModule, "CloseThemeData");
+ uxDrawThemeBackground = (UXDRAWTHEMEBACKGROUND)GetProcAddress(uxModule, "DrawThemeBackground");
+ uxDrawThemeParentBackground = (UXDRAWTHEMEPARENTBACKGROUND)GetProcAddress(uxModule, "DrawThemeParentBackground");
+ uxDrawThemeParentBackgroundEx = (UXDRAWTHEMEPARENTBACKGROUNDEX)GetProcAddress(uxModule, "DrawThemeParentBackgroundEx");
+ uxDrawThemeText = (UXDRAWTHEMETEXT)GetProcAddress(uxModule, "DrawThemeText");
+ uxGetThemeSysColor = (UXGETTHEMESYSCOLOR)GetProcAddress(uxModule, "GetThemeSysColor");
+ uxGetThemeColor = (UXGETTHEMECOLOR)GetProcAddress(uxModule, "GetThemeColor");
+ uxGetThemeInt = (UXGETTHEMEINT)GetProcAddress(uxModule, "GetThemeInt");
+ uxGetThemeMetric = (UXGETTHEMEMETRIC)GetProcAddress(uxModule, "GetThemeMetric");
+ uxGetThemePartSize = (UXGETTHEMEPARTSIZE)GetProcAddress(uxModule, "GetThemePartSize");
+ uxGetThemeBackgroundContentRect = (UXGETTHEMEBACKGROUNDCONTENTRECT)GetProcAddress(uxModule, "GetThemeBackgroundContentRect");
+ uxIsThemeBackgroundPartiallyTransparent = (UXISTHEMEBACKGROUNDPARTIALLYTRANSPARENT)GetProcAddress(uxModule, "IsThemeBackgroundPartiallyTransparent");
+ uxEnableThemeDialogTexture = (UXENABLETHEMEDIALOGTEXTURE)GetProcAddress(uxModule, "EnableThemeDialogTexture");
+
+ uxLoadResult = S_OK;
+ }
+ }
+
+ if (-1 == fSysCheckOk)
+ {
+ OSVERSIONINFOW vi;
+ vi.dwOSVersionInfoSize = sizeof(vi);
+ if (FALSE != GetVersionEx(&vi) &&
+ (vi.dwMajorVersion > 5 || (vi.dwMajorVersion == 5 && vi.dwMinorVersion > 0)))
+ {
+ fSysCheckOk = TRUE;
+ }
+ else
+ {
+ fSysCheckOk = FALSE;
+ }
+ }
+
+ if (-1 == fComCtlCheckOk)
+ {
+ fComCtlCheckOk = FALSE;
+
+ HMODULE hComCtl = LoadLibraryW(L"comctl32.dll");
+ if (NULL != hComCtl)
+ {
+ HRESULT (CALLBACK *waDllGetVersion)(DLLVERSIONINFO*) = (HRESULT (CALLBACK *)(DLLVERSIONINFO*))GetProcAddress(hComCtl, "DllGetVersion");
+ if (NULL != waDllGetVersion)
+ {
+ DLLVERSIONINFO dllVer;
+ dllVer.cbSize = sizeof(DLLVERSIONINFO);
+ if (S_OK == waDllGetVersion(&dllVer) && dllVer.dwMajorVersion >= 6)
+ {
+ fComCtlCheckOk = TRUE;
+ }
+ FreeLibrary(hComCtl);
+ }
+ }
+ }
+
+
+ return uxLoadResult;
+}
+
+HRESULT STDAPICALLTYPE UxTheme_IsThemeActive(void)
+{
+ if (NULL == uxIsAppThemed)
+ {
+ if(FAILED(UxTheme_LoadLibrary()))
+ return uxLoadResult;
+ if (NULL == uxIsAppThemed) return E_WINTHEME_BADFUNCTION;
+ }
+
+ if (NULL == uxIsThemeActive)
+ {
+ if(FAILED(UxTheme_LoadLibrary()))
+ return uxLoadResult;
+ if (NULL == uxIsThemeActive) return E_WINTHEME_BADFUNCTION;
+ }
+
+ if (TRUE != fSysCheckOk || TRUE != fComCtlCheckOk ||
+ FALSE == uxIsAppThemed() || FALSE == uxIsThemeActive())
+ {
+ return S_FALSE;
+ }
+
+ return S_OK;
+
+
+}
+
+HRESULT STDAPICALLTYPE UxTheme_GetLoadResult(void)
+{
+ return uxLoadResult;
+}
+
+UXTHEME STDAPICALLTYPE UxOpenThemeData(HWND hwnd, LPCWSTR pszClassList)
+{
+ UXTHEME_LOADCHECK_RESULT(uxOpenThemeData, NULL);
+ return uxOpenThemeData(hwnd, pszClassList);
+}
+
+HRESULT STDAPICALLTYPE UxCloseThemeData(UXTHEME hTheme)
+{
+ UXTHEME_LOADCHECK(uxCloseThemeData);
+ return uxCloseThemeData(hTheme);
+}
+
+HRESULT STDAPICALLTYPE UxSetWindowTheme(HWND hwnd, LPCWSTR pszSubAppName, LPCWSTR pszSubIdList)
+{
+ UXTHEME_LOADCHECK(uxSetWindowTheme);
+ return uxSetWindowTheme(hwnd, pszSubAppName, pszSubIdList);
+}
+
+BOOL STDAPICALLTYPE UxIsAppThemed(void)
+{
+ UXTHEME_LOADCHECK_RESULT(uxIsAppThemed, FALSE);
+ return uxIsAppThemed();
+}
+
+BOOL STDAPICALLTYPE UxIsThemeActive(void)
+{
+ UXTHEME_LOADCHECK_RESULT(UxIsThemeActive, FALSE);
+ return UxIsThemeActive();
+}
+
+HRESULT STDAPICALLTYPE UxDrawThemeBackground(UXTHEME hTheme, HDC hdc, int iPartId, int iStateId,
+ const RECT *pRect, OPTIONAL const RECT *pClipRect)
+{
+ UXTHEME_LOADCHECK(uxDrawThemeBackground);
+ return uxDrawThemeBackground(hTheme, hdc, iPartId, iStateId, pRect, pClipRect);
+}
+
+HRESULT STDAPICALLTYPE UxDrawThemeParentBackground(HWND hwnd, HDC hdc, const RECT *prc)
+{
+ UXTHEME_LOADCHECK(uxDrawThemeParentBackground);
+ return uxDrawThemeParentBackground(hwnd, hdc, prc);
+}
+
+HRESULT STDAPICALLTYPE UxDrawThemeParentBackgroundEx(HWND hwnd, HDC hdc, DWORD dwFlags, const RECT *prc)
+{
+ UXTHEME_LOADCHECK(uxDrawThemeParentBackgroundEx);
+ return uxDrawThemeParentBackgroundEx(hwnd, hdc, dwFlags, prc);
+}
+
+HRESULT STDAPICALLTYPE UxDrawThemeText(UXTHEME hTheme, HDC hdc, int iPartId, int iStateId, LPCWSTR pszText,
+ int iCharCount, DWORD dwTextFlags, DWORD dwTextFlags2, const RECT *pRect)
+{
+ UXTHEME_LOADCHECK(uxDrawThemeText);
+ return uxDrawThemeText(hTheme, hdc, iPartId, iStateId, pszText, iCharCount, dwTextFlags, dwTextFlags2, pRect);
+}
+
+COLORREF STDAPICALLTYPE UxGetThemeSysColor(UXTHEME hTheme, int iColorID)
+{
+ UXTHEME_LOADCHECK_RESULT(uxGetThemeSysColor, RGB(255, 0, 255));
+ return uxGetThemeSysColor(hTheme, iColorID);
+}
+
+HRESULT STDAPICALLTYPE UxGetThemeColor(UXTHEME hTheme, int iPartId, int iStateId, int iPropId, COLORREF *pColor)
+{
+ UXTHEME_LOADCHECK(uxGetThemeColor);
+ return uxGetThemeColor(hTheme, iPartId, iStateId, iPropId, pColor);
+}
+HRESULT STDAPICALLTYPE UxGetThemeInt(UXTHEME hTheme, int iPartId, int iStateId, int iPropId, int *piVal)
+{
+ UXTHEME_LOADCHECK(uxGetThemeInt);
+ return uxGetThemeInt(hTheme, iPartId, iStateId, iPropId, piVal);
+}
+HRESULT STDAPICALLTYPE UxGetThemeMetric(UXTHEME hTheme, HDC hdc, int iPartId, int iStateId, int iPropId, int *piVal)
+{
+ UXTHEME_LOADCHECK(uxGetThemeMetric);
+ return uxGetThemeMetric(hTheme, hdc, iPartId, iStateId, iPropId, piVal);
+}
+HRESULT STDAPICALLTYPE UxGetThemePartSize(UXTHEME hTheme, HDC hdc, int iPartId, int iStateId, LPCRECT prc, UXTHEMESIZE eSize, SIZE *psz)
+{
+ UXTHEME_LOADCHECK(uxGetThemePartSize);
+ return uxGetThemePartSize(hTheme, hdc, iPartId, iStateId, prc, eSize, psz);
+}
+
+HRESULT STDAPICALLTYPE UxGetThemeBackgroundContentRect(UXTHEME hTheme, HDC hdc, int iPartId, int iStateId, LPCRECT pBoundingRect, LPRECT pContentRect)
+{
+ UXTHEME_LOADCHECK(uxGetThemeBackgroundContentRect);
+ return uxGetThemeBackgroundContentRect(hTheme, hdc, iPartId, iStateId, pBoundingRect, pContentRect);
+}
+
+BOOL STDAPICALLTYPE UxIsThemeBackgroundPartiallyTransparent(UXTHEME hTheme, int iPartId, int iStateId)
+{
+ UXTHEME_LOADCHECK_RESULT(uxIsThemeBackgroundPartiallyTransparent, FALSE);
+ return uxIsThemeBackgroundPartiallyTransparent(hTheme, iPartId, iStateId);
+}
+
+HRESULT STDAPICALLTYPE UxEnableThemeDialogTexture(HWND hwnd, DWORD dwFlags)
+{
+ UXTHEME_LOADCHECK(uxEnableThemeDialogTexture);
+ return uxEnableThemeDialogTexture(hwnd, dwFlags);
+}
diff --git a/Src/nu/windowsTheme.h b/Src/nu/windowsTheme.h
new file mode 100644
index 00000000..24ff54b1
--- /dev/null
+++ b/Src/nu/windowsTheme.h
@@ -0,0 +1,65 @@
+#ifndef NULLOSFT_WINAMP_WINDOWTHEME_HEADER
+#define NULLOSFT_WINAMP_WINDOWTHEME_HEADER
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+#pragma once
+#endif
+
+#include <wtypes.h>
+
+#define E_WINTHEME_NOTLOADED MAKE_HRESULT(0, FACILITY_ITF, 0x0201) // library not loaded
+#define E_WINTHEME_LOADFAILED MAKE_HRESULT(1, FACILITY_ITF, 0x0202) // library load failed
+#define E_WINTHEME_BADFUNCTION MAKE_HRESULT(1, FACILITY_ITF, 0x0203) // function was not loaded
+
+//---------------------------------------------------------------------------
+// NOTE: PartId's and StateId's used in the theme API are defined in the
+// hdr file <tmschema.h> using the TM_PART and TM_STATE macros. For
+// example, "TM_PART(BP, PUSHBUTTON)" defines the PartId "BP_PUSHBUTTON".
+
+// UxTheme
+
+typedef HANDLE UXTHEME;
+
+typedef enum UXTHEMESIZE
+{
+ TS_MIN, // minimum size
+ TS_TRUE, // size without stretching
+ TS_DRAW, // size that theme mgr will use to draw part
+};
+
+#ifndef ETDT_ENABLE
+#define ETDT_DISABLE 0x00000001
+#define ETDT_ENABLE 0x00000002
+#define ETDT_USETABTEXTURE 0x00000004
+#define ETDT_USEAEROWIZARDTABTEXTURE 0x00000008
+#define ETDT_ENABLETAB (ETDT_ENABLE | ETDT_USETABTEXTURE)
+#define ETDT_ENABLEAEROWIZARDTAB (ETDT_ENABLE | ETDT_USEAEROWIZARDTABTEXTURE)
+#define ETDT_VALIDBITS (ETDT_DISABLE | ETDT_ENABLE | ETDT_USETABTEXTURE | ETDT_USEAEROWIZARDTABTEXTURE)
+#endif //ETDT_ENABLE
+
+HRESULT STDAPICALLTYPE UxTheme_LoadLibrary(void);
+HRESULT STDAPICALLTYPE UxTheme_GetLoadResult(void);
+HRESULT STDAPICALLTYPE UxTheme_IsThemeActive(void);
+
+UXTHEME STDAPICALLTYPE UxOpenThemeData(HWND hwnd, LPCWSTR pszClassList);
+HRESULT STDAPICALLTYPE UxCloseThemeData(UXTHEME hTheme);
+HRESULT STDAPICALLTYPE UxDrawThemeBackground(UXTHEME hTheme, HDC hdc, int iPartId, int iStateId,
+ const RECT *pRect, OPTIONAL const RECT *pClipRect);
+HRESULT STDAPICALLTYPE UxDrawThemeParentBackground(HWND hwnd, HDC hdc, const RECT *prc);
+HRESULT STDAPICALLTYPE UxDrawThemeParentBackgroundEx(HWND hwnd, HDC hdc, DWORD dwFlags, const RECT *prc);
+HRESULT STDAPICALLTYPE UxDrawThemeText(UXTHEME hTheme, HDC hdc, int iPartId, int iStateId, LPCWSTR pszText,
+ int iCharCount, DWORD dwTextFlags, DWORD dwTextFlags2, const RECT *pRect);
+COLORREF STDAPICALLTYPE UxGetThemeSysColor(UXTHEME hTheme, int iColorID);
+HRESULT STDAPICALLTYPE UxGetThemeColor(UXTHEME hTheme, int iPartId, int iStateId, int iPropId, COLORREF *pColor);
+HRESULT STDAPICALLTYPE UxGetThemeInt(UXTHEME hTheme, int iPartId, int iStateId, int iPropId, int *piVal);
+HRESULT STDAPICALLTYPE UxGetThemeMetric(UXTHEME hTheme, HDC hdc, int iPartId, int iStateId, int iPropId, int *piVal);
+HRESULT STDAPICALLTYPE UxGetThemePartSize(UXTHEME hTheme, HDC hdc, int iPartId, int iStateId, LPCRECT prc, UXTHEMESIZE eSize, SIZE *psz);
+HRESULT STDAPICALLTYPE UxGetThemeBackgroundContentRect(UXTHEME hTheme, HDC hdc, int iPartId, int iStateId, LPCRECT pBoundingRect, LPRECT pContentRect);
+HRESULT STDAPICALLTYPE UxSetWindowTheme(HWND hwnd, LPCWSTR pszSubAppName, LPCWSTR pszSubIdList);
+BOOL STDAPICALLTYPE UxIsAppThemed(void);
+BOOL STDAPICALLTYPE UxIsThemeActive(void);
+BOOL STDAPICALLTYPE UxIsThemeBackgroundPartiallyTransparent(UXTHEME hTheme, int iPartId, int iStateId);
+HRESULT STDAPICALLTYPE UxEnableThemeDialogTexture(HWND hwnd, DWORD dwFlags);
+
+
+#endif //NULLOSFT_WINAMP_WINDOWTHEME_HEADER \ No newline at end of file
diff --git a/Src/nu/winstr.c b/Src/nu/winstr.c
new file mode 100644
index 00000000..1a97df28
--- /dev/null
+++ b/Src/nu/winstr.c
@@ -0,0 +1,17 @@
+#include <windows.h>
+#ifdef __cplusplus
+extern "C" {
+#endif
+size_t __cdecl strlen(const char *s)
+{
+ return lstrlenA(s);
+}
+
+int __cdecl strcmp(const char *a, const char *b)
+{
+ return lstrcmpA(a,b);
+}
+
+#ifdef __cplusplus
+}
+#endif \ No newline at end of file