4#include "Stroika/Frameworks/StroikaPreComp.h"
15#include "Stroika/Foundation/Execution/Throw.h"
18#include "Stroika/Frameworks/Led/Config.h"
20#if qStroika_Platform_Windows
27#if qStroika_Frameworks_Led_UseActiveXToOpenURLs
33using namespace Stroika::Frameworks;
34using namespace Stroika::Frameworks::Led;
36#if !qStroika_Foundation_Characters_SDKUseswchar_t
37SDKString Led::Led_tString2SDKString (
const Led_tString& s)
47Led_tString Led::Led_ANSIString2tString (
const string& s)
58string Led::Led_tString2ANSIString (
const Led_tString& s)
70void Led::Led_BeepNotify ()
72#if qStroika_Platform_Windows
73 ::MessageBeep (MB_OK);
84#if qStroika_Platform_Windows
91int Led::Led_tStrniCmp (
const Led_tChar* l,
const Led_tChar* r,
size_t n)
98 if (result == strong_ordering::equal)
100 if (result == strong_ordering::less)
102 if (result == strong_ordering::greater)
108int Led::Led_tStriCmp (
const Led_tChar* l,
const Led_tChar* r)
113 if (result == strong_ordering::equal)
115 if (result == strong_ordering::less)
117 if (result == strong_ordering::greater)
129size_t Led::Led_SkrunchOutSpecialChars (Led_tChar* text,
size_t textLen, Led_tChar charToRemove)
131 size_t charsSkipped = 0;
132 Led_tChar* end = text + textLen;
133 for (Led_tChar* p = text; p + charsSkipped < end;) {
134 if (*(p + charsSkipped) == charToRemove) {
138 *p = *(p + charsSkipped);
141 return textLen - charsSkipped;
144#if qStroika_Frameworks_Led_SupportClipboard
151Led_ClipboardObjectAcquire::Led_ClipboardObjectAcquire (Led_ClipFormat clipType)
153#if qStroika_Platform_Windows
154 fOSClipHandle (nullptr)
157 fLockedData (nullptr)
159#if qStroika_Platform_Windows
162 fOSClipHandle = ::GetClipboardData (clipType);
164 DWORD errResult = ::GetLastError ();
166 if (fOSClipHandle !=
nullptr) {
167 fLockedData = ::GlobalLock (fOSClipHandle);
173#if qStroika_Platform_Windows
179VariantArrayPacker::VariantArrayPacker (VARIANT* v, VARTYPE vt,
size_t nElts)
180 : fSafeArrayVariant{v}
184 ::VariantClear (fSafeArrayVariant);
185 fSafeArrayVariant->parray = ::SafeArrayCreateVector (vt, 0,
static_cast<ULONG
> (nElts));
186 Execution::ThrowIfNull (fSafeArrayVariant->parray);
187 fSafeArrayVariant->vt = VT_ARRAY | vt;
191VariantArrayPacker::~VariantArrayPacker ()
195 ::SafeArrayUnaccessData (fSafeArrayVariant->parray);
198void* VariantArrayPacker::PokeAtData ()
const
208VariantArrayUnpacker::VariantArrayUnpacker (
const VARIANT& v)
209 : fSafeArray{v.parray}
211 if (not(v.vt & VT_ARRAY) or fSafeArray ==
nullptr) {
214 if ((v.vt & ~VT_ARRAY) != GetArrayElementType ()) {
218 void* useP =
nullptr;
223VariantArrayUnpacker::~VariantArrayUnpacker ()
226 ::SafeArrayUnaccessData (fSafeArray);
229const void* VariantArrayUnpacker::PeekAtData ()
const
234VARTYPE VariantArrayUnpacker::GetArrayElementType ()
const
237 VARTYPE vt = VT_EMPTY;
242size_t VariantArrayUnpacker::GetLength ()
const
253#if qStroika_Platform_Windows
264VARIANT Led::CreateSafeArrayOfBSTR (
const wchar_t*
const* strsStart,
const wchar_t*
const* strsEnd)
266 size_t nElts = strsEnd - strsStart;
268 ::VariantInit (&result);
270 const bool kEncodeAsVariant =
false;
272 if (kEncodeAsVariant) {
273 VariantArrayPacker packer (&result, VT_VARIANT, nElts);
274 VARIANT* vptr =
reinterpret_cast<VARIANT*
> (packer.PokeAtData ());
275 for (
size_t i = 0; i < nElts; ++i) {
276 ::VariantInit (&vptr[i]);
277 Execution::ThrowIfNull (vptr[i].bstrVal = ::SysAllocString (strsStart[i]));
278 vptr[i].vt = VT_VARIANT;
282 VariantArrayPacker packer (&result, VT_BSTR, nElts);
283 BSTR* bptr =
reinterpret_cast<BSTR*
> (packer.PokeAtData ());
284 for (
size_t i = 0; i < nElts; ++i) {
285 Execution::ThrowIfNull (bptr[i] = ::SysAllocString (strsStart[i]));
290 ::VariantClear (&result);
296VARIANT Led::CreateSafeArrayOfBSTR (
const vector<const wchar_t*>& v)
298 return CreateSafeArrayOfBSTR (Traversal::Iterator2Pointer (v.begin ()), Traversal::Iterator2Pointer (v.end ()));
301VARIANT Led::CreateSafeArrayOfBSTR (
const vector<wstring>& v)
303 vector<const wchar_t*> tmp;
304 for (
auto i = v.begin (); i != v.end (); ++i) {
306 tmp.push_back ((*i).c_str ());
308 return CreateSafeArrayOfBSTR (tmp);
316vector<wstring> Led::UnpackVectorOfStringsFromVariantArray (
const VARIANT& v)
318 vector<wstring> result;
319 VariantArrayUnpacker up (v);
320 size_t nElts = up.GetLength ();
321 switch (up.GetArrayElementType ()) {
323 const BSTR* bArray =
reinterpret_cast<const BSTR*
> (up.PeekAtData ());
324 for (
size_t i = 0; i < nElts; ++i) {
325 result.push_back (bArray[i]);
329 const VARIANT* vArray =
reinterpret_cast<const VARIANT*
> (up.PeekAtData ());
330 for (
size_t i = 0; i < nElts; ++i) {
331 result.push_back (vArray[i].bstrVal);
335 throw (DISP_E_BADVARTYPE);
341#if qStroika_Platform_Windows
349void Led::DumpSupportedInterfaces (IUnknown* obj,
const char* objectName,
const char* levelPrefix)
352 string label = objectName ==
nullptr ?
string{} : Characters::CString::Format (
" (named '%s')", objectName);
353 string msg = Characters::CString::Format (
"Dumping interfaces for object%s at %p:\n", label.c_str (), obj);
354 ::OutputDebugStringA (msg.c_str ());
356 if (levelPrefix ==
nullptr) {
365 HKEY interfaceKey =
nullptr;
367 BSTR tmpStr =
nullptr;
368 IUnknown* test =
nullptr;
370 if (::RegOpenKeyEx (HKEY_CLASSES_ROOT, _T (
"Interface"), 0, KEY_READ, &interfaceKey) != ERROR_SUCCESS) {
371 throw GetLastError ();
373 for (DWORD i = 0;; ++i) {
374 char subKey[MAX_PATH];
375 DWORD subKeySize =
sizeof (subKey);
376 if (::RegEnumKeyExA (interfaceKey, i, subKey, &subKeySize, 0, 0, 0, 0) == ERROR_SUCCESS) {
379 (void)::memset (&iid, 0,
sizeof (iid));
380 tmpStr = ::SysAllocString (String::FromNarrowSDKString (subKey).As<wstring> ().c_str ());
382 if (tmpStr !=
nullptr) {
383 ::SysFreeString (tmpStr);
386 if (SUCCEEDED (obj->QueryInterface (iid,
reinterpret_cast<void**
> (&test)))) {
388 Assert (iKey ==
nullptr);
389 if (::RegOpenKeyExA (interfaceKey, subKey, 0, KEY_READ, &iKey) != ERROR_SUCCESS) {
390 throw GetLastError ();
393 TCHAR interfaceNameBuf[1024];
394 DWORD interfaceNameBufSize =
sizeof (interfaceNameBuf);
395 if (::RegQueryValueEx (iKey, _T(
""),
nullptr, &dwType,
reinterpret_cast<LPBYTE
> (interfaceNameBuf),
396 &interfaceNameBufSize) == ERROR_SUCCESS) {
397 OutputDebugStringA (levelPrefix);
398 OutputDebugString (interfaceNameBuf);
399 OutputDebugStringA (
"\n");
401 if (iKey !=
nullptr) {
402 ::CloseHandle (iKey);
406 if (test !=
nullptr) {
412 OutputDebugStringA (levelPrefix);
413 OutputDebugStringA (
"ERROR LOOKING UP INTERFACE - IGNORING");
414 OutputDebugStringA (
"\n");
421 if (test !=
nullptr) {
425 if (tmpStr !=
nullptr) {
426 ::SysFreeString (tmpStr);
429 if (interfaceKey !=
nullptr) {
430 ::CloseHandle (interfaceKey);
431 interfaceKey =
nullptr;
435 if (iKey !=
nullptr) {
436 ::CloseHandle (iKey);
439 if (interfaceKey !=
nullptr) {
440 ::CloseHandle (interfaceKey);
441 interfaceKey =
nullptr;
443 if (tmpStr !=
nullptr) {
444 ::SysFreeString (tmpStr);
447 if (test !=
nullptr) {
462void Led::DumpObjectsInIterator (IEnumUnknown* iter,
const char* iteratorName,
const char* levelPrefix)
465 string label = iteratorName ==
nullptr ?
string{} : Characters::CString::Format (
" (named '%s')", iteratorName);
466 string msg = Characters::CString::Format (
"Dumping objects (and their interface names) for iterator%s at %p\n", label.c_str (), iter);
467 ::OutputDebugStringA (msg.c_str ());
468 if (levelPrefix ==
nullptr) {
472 if (iter ==
nullptr) {
473 ::OutputDebugStringA (levelPrefix);
474 ::OutputDebugStringA (
"<<nullptr ENUM POINTER>>\n");
477 IUnknown* nextObj =
nullptr;
478 for (
size_t i = 0; SUCCEEDED (iter->Next (1, &nextObj,
nullptr)); ++i) {
480 (void)snprintf (nameBuf, std::size (nameBuf),
"obj#%d",
static_cast<int> (i));
481 char levelPrefixBuf[1024];
482 Assert (::strlen (levelPrefix) <
sizeof (levelPrefixBuf) / 2);
483 Characters::CString::Copy (levelPrefixBuf, std::size (levelPrefixBuf), levelPrefix);
484 CString::Cat (levelPrefixBuf, std::size (levelPrefixBuf),
"\t");
485 DumpSupportedInterfaces (nextObj, nameBuf, levelPrefixBuf);
490 Assert (nextObj ==
nullptr);
499Led_URLD::Led_URLD (
const char* url,
const char* title)
504 size_t urlLen = ::strlen (url);
505 size_t titleLen = ::strlen (title);
507 size_t resultURLDLen = (urlLen + 1 + titleLen + 1);
508 fData.resize (resultURLDLen,
'\0');
510 char* data = &fData.front ();
511 Characters::CString::Copy (data, resultURLDLen, url);
512 CString::Cat (data, fData.size () - 1,
"\r");
513 CString::Cat (data, fData.size () - 1, title);
516Led_URLD::Led_URLD (
const void* urlData,
size_t nBytes)
520 for (
size_t i = 0; i < nBytes; i++) {
521 char c = ((
char*)urlData)[i];
528 if (fData.size () == 0 or fData.back () !=
'\0') {
529 fData.push_back (
'\0');
533size_t Led_URLD::GetURLDLength ()
const
536 return fData.size () - 1;
539size_t Led_URLD::GetURLLength ()
const
541 size_t len = fData.size ();
542 for (
size_t i = 0; i < len; i++) {
543 if (fData[i] ==
'\r') {
550size_t Led_URLD::GetTitleLength ()
const
552 size_t len = fData.size ();
553 for (
size_t i = 0; i < len; i++) {
554 if (fData[i] ==
'\r') {
555 Assert (len >= (i + 1 + 1));
556 return len - (i + 1 + 1);
562char* Led_URLD::PeekAtURLD ()
const
564 return (&
const_cast<Led_URLD*
> (
this)->fData.front ());
567char* Led_URLD::PeekAtURL ()
const
569 return (&
const_cast<Led_URLD*
> (
this)->fData.front ());
572char* Led_URLD::PeekAtTitle ()
const
574 return PeekAtURL () + GetURLLength () + 1;
577string Led_URLD::GetTitle ()
const
579 return string{PeekAtTitle (), GetTitleLength ()};
582string Led_URLD::GetURL ()
const
584 return string{PeekAtURL (), GetURLLength ()};
593 Led_URLManager* sTheURLMgr_;
595Led_URLManager& Led_URLManager::Get ()
597 if (sTheURLMgr_ ==
nullptr) {
598 sTheURLMgr_ =
new Led_URLManager ();
603void Led_URLManager::Set (Led_URLManager* newURLMgr)
605 if (sTheURLMgr_ != newURLMgr) {
607 sTheURLMgr_ = newURLMgr;
611void Led_URLManager::Open (
const string& url)
613#if qStroika_Platform_Windows
614#if qStroika_Frameworks_Led_UseActiveXToOpenURLs
620string Led_URLManager::FileSpecToURL ([[maybe_unused]]
const filesystem::path& p)
626#if qStroika_Frameworks_Led_UseActiveXToOpenURLs
627void Led_URLManager::Open_ActiveX (
const string& url)
631 int nWideChars = ::MultiByteToWideChar (CP_ACP, 0, url.c_str (),
static_cast<int> (url.length ()), wideURLBuf.data (),
632 static_cast<int> (url.length ()));
633 wideURLBuf[nWideChars] =
'\0';
635 IUnknown* pUnk =
nullptr;
646 static HINSTANCE urlmonLibrary = ::LoadLibrary (_T (
"urlmon"));
647 if (urlmonLibrary ==
nullptr) {
648 Execution::Throw (bad_alloc{});
650 static HRESULT (WINAPI * hlinkNavigateString) (IUnknown*, LPCWSTR) =
651 (HRESULT (WINAPI*) (IUnknown*, LPCWSTR))::GetProcAddress (urlmonLibrary,
"HlinkNavigateString");
652 if (hlinkNavigateString ==
nullptr) {
653 Execution::Throw (bad_alloc{});
655 HRESULT result = (*hlinkNavigateString) (pUnk, wideURLBuf.data ());
657 HRESULT result = ::HlinkNavigateString (pUnk, wideURLBuf.data ());
659 if (not SUCCEEDED (result)) {
670string Led::MakeSophistsAppNameVersionURL (
const string& relURL,
const string& appName,
const string& extraArgs)
672 Require (relURL.length () > 0 and relURL[0] ==
'/');
673 char fullVersionBuf[1024];
674 (void)snprintf (fullVersionBuf, std::size (fullVersionBuf),
"%d", qStroika_Version_FullVersion);
675 string fullURL =
"http://www.sophists.com" + relURL +
"?AppName=" + appName +
676#if qStroika_Platform_Windows
677 string{
"&Platform=Windows"} +
679 "&MajorMinorVersion=" + qStroika_Version_MajorMinorVersionString +
"&LedFullVersion=" + fullVersionBuf +
680 "&ShortVersionString=" + qStroika_Version_ShortVersionString + extraArgs;
689bool Led::Led_CasedCharsEqual (
char lhs,
char rhs,
bool ignoreCase)
694 if (ignoreCase and isascii (lhs) and isalpha (lhs) and isascii (rhs) and isalpha (rhs) and toupper (lhs) == toupper (rhs)) {
700CompileTimeFlagChecker_SOURCE (Stroika::Frameworks::Led, qStroika_Frameworks_Led_ProvideIMESupport, qStroika_Frameworks_Led_ProvideIMESupport);
#define AssertNotImplemented()
#define RequireNotNull(p)
#define AssertNotReached()
#define CompileTimeFlagChecker_SOURCE(NS_PREFIX, NAME, VALUE)
chrono::duration< double > DurationSeconds
chrono::duration<double> - a time span (length of time) measured in seconds, but high precision.
CodeCvt unifies byte <-> unicode conversions, vaguely inspired by (and wraps) std::codecvt,...
nonvirtual STRINGISH Bytes2String(span< const byte > from) const
nonvirtual BLOBISH String2Bytes(span< const CHAR_T > from) const
String is like std::u32string, except it is much easier to use, often much more space efficient,...
nonvirtual String SafeSubString(SZ from) const
Logically halfway between std::array and std::vector; Smart 'direct memory array' - which when needed...
basic_string< SDKChar > SDKString