diff options
Diffstat (limited to 'Src/nu')
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> ©) + { + 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 ©) { } // make copy constructor private so it can't be used + LockGuard &operator =(const LockGuard ©) {} // 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(¶m->URL); + VariantInit(¶m->Flags); + VariantInit(¶m->TargetFrameName); + VariantInit(¶m->PostData); + VariantInit(¶m->Headers); + + if (NULL != URL) VariantCopyInd(¶m->URL, URL); + if (NULL != Flags) VariantCopyInd(¶m->Flags, Flags); + if (NULL != TargetFrameName) VariantCopyInd(¶m->TargetFrameName, TargetFrameName); + if (NULL != PostData) VariantCopyInd(¶m->PostData, PostData); + if (NULL != Headers) VariantCopyInd(¶m->Headers, Headers); + + HRESULT hr = PostRedirectMessage(HTMLContainer2::msgNavigate2, (LPARAM)param); + if (FAILED(hr)) + { + VariantClear(¶m->URL); + VariantClear(¶m->Flags); + VariantClear(¶m->TargetFrameName); + VariantClear(¶m->PostData); + VariantClear(¶m->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> ©) + { + 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> ©) + { + 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)¶m, 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 |