JsValueRefはvoid* のtypedefなので、何者なのか掴みにくい。また、スタック上でない場所でキープする場合は、AddRefしなければならない。多分、GarvageCollectが働いた時に回収されてしまうのを避けるため。
そこで、CJsValueRefというラッパークラスを作ってみた。スクリプト上での型が分かっている場合に使える。
(少なくともWM_DESTROYのタイミングで、使用したすべてのCJsValueRefをReleaseして下さい。)

#include "stdafx.h"
#define _JSRT_
#include "ChakraCore.h"

#include
#include
#include
#include

#pragma comment (lib, "chakracore")
using namespace std;

class CJsValueRef
{
public:
CJsValueRef() :ref_(nullptr) {}
explicit CJsValueRef(JsValueRef ref);
CJsValueRef(const CJsValueRef& r);
~CJsValueRef() { Release(); }

CJsValueRef(int val);
CJsValueRef(double val);
CJsValueRef(LPCWSTR str);
CJsValueRef(bool bl);
CJsValueRef(const std::vector& ar);
CJsValueRef(const std::map& m);

UINT AddRef();
UINT Release();

// Convert functions
std::vector ToArray() const;
int ToInt() const;
double ToDouble() const;
std::wstring ToString() const;
std::map ToMap() const;
bool ToBool() const;

bool empty() { return (ref_ == nullptr); }

CJsValueRef& operator=(const CJsValueRef& x);
private:
JsValueRef ref_;

};

int main()
{
JsRuntimeHandle runtime;
JsContextRef context;
JsValueRef result;
unsigned currentSourceContext = 0;

wstring script = L"(()=>{ var a = { 'tokyo':'03', 'yokohama':'045', 'oosaka':'06'}; return a;})()"; // <-- map
JsCreateRuntime(JsRuntimeAttributeNone, nullptr, &runtime);

// Create an execution context.
JsCreateContext(runtime, &context);

// Now set the execution context as being the current one on this thread.
JsSetCurrentContext(context);

// Run the script.
JsRunScript(script.c_str(), currentSourceContext++, L"", &result);

{
CJsValueRef x = result;
auto m = x.ToMap();

wcout << L'{';

for( auto& it : m )
{
wcout << it.first << L':' << it.second.ToString();
}

wcout << L'}' << endl;

// output is "{tokyo:03,yokohama:045,oosaka:06,}"
}
system("pause");

// Dispose runtime
JsSetCurrentContext(JS_INVALID_REFERENCE);
JsDisposeRuntime(runtime);
return 0;
}

///////////////////////////////////////////////////
CJsValueRef::CJsValueRef(JsValueRef ref)
{
ref_ = ref;
AddRef();
}
CJsValueRef::CJsValueRef(const CJsValueRef& r)
{
ref_ = r.ref_;
AddRef();
}
CJsValueRef::CJsValueRef(int val)
{
JsIntToNumber(val,&ref_);
//AddRef(); 不要
}
CJsValueRef::CJsValueRef(double val)
{
JsDoubleToNumber(val,&ref_);
//AddRef();
}
CJsValueRef::CJsValueRef(LPCWSTR str)
{
JsPointerToString(str,lstrlen(str), &ref_);
AddRef();
}
CJsValueRef::CJsValueRef(bool bl)
{
JsBoolToBoolean( bl, &ref_ );
//AddRef();
}
CJsValueRef::CJsValueRef(const std::vector& ar)
{
JsCreateArray( ar.size(), &ref_ );
AddRef();

int i = 0;
for( auto& it : ar )
{
JsValueRef index;
JsIntToNumber(i++, &index );
JsSetIndexedProperty( ref_, index, it.ref_ );
}

}
CJsValueRef::CJsValueRef( const std::map& m)
{
JsCreateArray( m.size(), &ref_ );
AddRef();

JsValueRef jsobj;
JsConvertValueToObject(ref_, &jsobj );
JsValueRef xnm;
JsGetOwnPropertyNames( jsobj, &xnm );

int i = 0;
for( auto& it : m )
{
auto& key = it.first;

JsValueRef index;
JsIntToNumber(i++, &index);

JsValueRef ck;
JsPointerToString( key.c_str(),key.length(), &ck );

auto er = JsSetIndexedProperty(xnm, index, ck); // set xnm.
_ASSERT( er == JsNoError);

JsValueRef IndexedProperty;
er = JsGetIndexedProperty(xnm, index, &IndexedProperty);
_ASSERT( er == JsNoError);

er = JsSetIndexedProperty( jsobj, IndexedProperty, it.second.ref_ ); // set jsobj.
_ASSERT( er == JsNoError);
}
}
UINT CJsValueRef::AddRef()
{
UINT refcnt = 0;
JsAddRef(ref_, &refcnt);
return refcnt;
}

UINT CJsValueRef::Release()
{
UINT refcnt = 0;
if (ref_)
JsRelease(ref_, &refcnt);

ref_ = nullptr;
return refcnt;
}
CJsValueRef& CJsValueRef::operator=(const CJsValueRef& x)
{
if (this == &x)
return *this;

Release();
ref_ = x.ref_;
AddRef();

return *this;
}

std::vector CJsValueRef::ToArray() const
{
std::vector ar;
JsValueRef jsobj;
if ( JsNoError == JsConvertValueToObject(ref_, &jsobj))
{
JsValueRef xnm;
JsGetOwnPropertyNames(jsobj, &xnm);
JsValueRef lengthName, lengthValue;
ret = JsGetPropertyIdFromName(L"length", &lengthName);
ret = JsGetProperty(xnm, lengthName, &lengthValue);
int count;
JsNumberToInt(lengthValue, &count);
if (count)
{

ar.resize(count - 1);

for (int i = 0; i < count - 1; i++)
{
JsValueRef index;
JsIntToNumber(i, &index);

JsValueRef IndexedProperty;
JsGetIndexedProperty(jsobj, index, &IndexedProperty);

ar[i] = CJsValueRef(IndexedProperty);
}
}
}
return ar;

}
bool CJsValueRef::ToBool() const
{
bool ret = false;
JsValueRef a;
JsConvertValueToBoolean(ref_,&a);
JsBooleanToBool(a,&ret);
return ret;
}
int CJsValueRef::ToInt() const
{
int ret = 0;
JsValueRef a;
JsConvertValueToNumber(ref_, &a);
JsNumberToInt(a, &ret);

return ret;
}
double CJsValueRef::ToDouble() const
{
double ret = 0;
JsValueRef a;
JsConvertValueToNumber(ref_, &a);
JsNumberToDouble(a, &ret);

return ret;
}
std::wstring CJsValueRef::ToString() const
{
JsValueRef a;
JsConvertValueToString(ref_, &a);

const wchar_t* cb; size_t len;
JsStringToPointer(a, &cb, &len);

return std::wstring(cb, len);
}
std::map CJsValueRef::ToMap() const
{
std::map map;
JsValueRef jsobj;
if ( JsNoError == JsConvertValueToObject(ref_, &jsobj))
{
JsValueRef xnm;
JsGetOwnPropertyNames(jsobj, &xnm);
JsValueRef lengthName, lengthValue;
ret = JsGetPropertyIdFromName(L"length", &lengthName);
ret = JsGetProperty(xnm, lengthName, &lengthValue);
int count;
JsNumberToInt(lengthValue, &count);

std::vector si;

for (int i = 0; i < count; i++)
{
JsValueRef index;
JsIntToNumber(i, &index);
JsValueRef IndexedProperty;
ret = JsGetIndexedProperty(xnm, index, &IndexedProperty);

JsValueRef itemref;
JsConvertValueToString(IndexedProperty, &itemref);

WCHAR* cb; UINT len;
JsStringToPointer(itemref, (const wchar_t**) &cb, &len);

si.push_back(cb);
}

for (int i = 0; i < count; i++)
{
JsValueRef index;
JsPointerToString(si[i].c_str(), si[i].length(), &index);

JsValueRef IndexedProperty;
JsGetIndexedProperty(jsobj, index, &IndexedProperty);

map[ si[i] ] = CJsValueRef(IndexedProperty);
}
}
return map;
}



ChakraからCOMやVARIANTが消えたために、またやり直しだ。こちらが主流になると思われる。
以下はC++によるArrayとMapへの操作例。

#include "stdafx.h"
#define _JSRT_
#include "ChakraCore.h"

#include
#include
#include

#pragma comment (lib, "chakracore")
using namespace std;

// array
int main()
{
JsRuntimeHandle runtime;
JsContextRef context;
JsValueRef result;
unsigned currentSourceContext = 0;

// Your script, try replace the basic hello world with something else
wstring script = L"(()=>{ var a = [1,2,3]; return a;})()"; // <-- array

// Create a runtime.
JsCreateRuntime(JsRuntimeAttributeNone, nullptr, &runtime);

// Create an execution context.
JsCreateContext(runtime, &context);

// Now set the execution context as being the current one on this thread.
JsSetCurrentContext(context);

// Run the script.
JsRunScript(script.c_str(), currentSourceContext++, L"", &result);

JsValueType ty;
auto ret = JsGetValueType(result, &ty ); // 8: JsArray

JsValueRef jsobj;
ret = JsConvertValueToObject(result, &jsobj );


JsValueRef xnm;
JsGetOwnPropertyNames( jsobj, &xnm );
JsValueRef lengthName,lengthValue;
ret = JsGetPropertyIdFromName(L"length", &lengthName);
ret = JsGetProperty(xnm, lengthName, &lengthValue);
int count;
JsNumberToInt(lengthValue, &count);

wcout << L'[';
for( int i = 0; i < count-1; i++ )
{
JsValueRef index;
JsIntToNumber(i, &index );

JsValueRef IndexedProperty;
JsGetIndexedProperty(jsobj, index, &IndexedProperty );

JsValueRef itemref;
JsConvertValueToString( IndexedProperty, &itemref );

WCHAR* cb; UINT len;
ret = JsStringToPointer( itemref, (const wchar_t**)&cb, &len );

wcout << cb << L',';

}
wcout << L']' << endl;

// output is [1,2,3].

system("pause");

// Dispose runtime
JsSetCurrentContext(JS_INVALID_REFERENCE);
JsDisposeRuntime(runtime);



return 0;
}


// map
int main()
{
JsRuntimeHandle runtime;
JsContextRef context;
JsValueRef result;
unsigned currentSourceContext = 0;

// Your script, try replace the basic hello world with something else
wstring script = L"(()=>{ var a = { 'tokyo':'03', 'yokohama':'045', 'oosaka':'06'}; return a;})()"; // <-- map
// Create a runtime.
JsCreateRuntime(JsRuntimeAttributeNone, nullptr, &runtime);

// Create an execution context.
JsCreateContext(runtime, &context);

// Now set the execution context as being the current one on this thread.
JsSetCurrentContext(context);

// Run the script.
JsRunScript(script.c_str(), currentSourceContext++, L"", &result);

JsValueRef jsobj;
JsValueType ty;
auto ret = JsGetValueType(result, &ty ); // 5: JsObject

ret = JsConvertValueToObject(result, &jsobj );

JsValueRef xnm;
JsGetOwnPropertyNames( jsobj, &xnm );
JsValueRef lengthName,lengthValue;
ret = JsGetPropertyIdFromName(L"length", &lengthName);
ret = JsGetProperty(xnm, lengthName, &lengthValue);
int count;
JsNumberToInt(lengthValue, &count);

//wstring si [] = { L"tokyo",L"yokohama",L"oosaka" };

std::vector si;
for (int i = 0; i < count; i++)
{
JsValueRef index;
JsIntToNumber(i, &index);
JsValueRef IndexedProperty;
ret = JsGetIndexedProperty(xnm, index, &IndexedProperty);

JsValueRef itemref;
JsConvertValueToString(IndexedProperty, &itemref);

WCHAR* cb; UINT len;
JsStringToPointer(itemref, (const wchar_t**) &cb, &len);

si.push_back(cb); // tokyo,yokohama,oosaka
}

wcout << L'{';
for (int i = 0; i < count; i++)
{
JsValueRef index;
JsPointerToString(si[i].c_str(), si[i].length(), &index);

JsValueRef IndexedProperty;
JsGetIndexedProperty(jsobj, index, &IndexedProperty );

JsValueRef itemref;
JsConvertValueToString( IndexedProperty, &itemref );

WCHAR* cb; UINT len;
ret = JsStringToPointer( itemref, (const wchar_t**)&cb, &len );

wcout << si[i] << ':' << cb << L',';
}
wcout << L'}' << endl;

// output is "tokyo:03,yokohama:045,oosaka:06,"

system("pause");

// Dispose runtime
JsSetCurrentContext(JS_INVALID_REFERENCE);
JsDisposeRuntime(runtime);



return 0;
}

Dispatcher


スレッドは常時3つあります。

1 呼び出し側のスレッド
2 呼び出される側のスレッド
3 2の結果を返すスレッド
 
1のスレッドを起こす呼び出し側は大抵WindowMessageと同じGUIスレッド。
2はGUIと異なるスレッド。独自のスタックを持ち重い関数を処理します。
3は何もしなければ2と同じスレッド上になりますが、GUIでスレッドの終了を示すためにはスレッド変換(Dispatcher)してGUIスレッドにしなければなりません。(3を意識しないとC++/CXでハマります)

このDispatcherを作成します。
WndProcにWM_MY_DISPATCHER処理を下のように加えました。汎用的に使えます。


#define WM_MY_DISPATCHER (WM_APP+100)
typedef std::function DisptcherDelegate;

WndProc内
switch(message) {
...
case WM_MY_DISPATCHER:
{
DisptcherDelegate& f = *(DisptcherDelegate*)wParam;

f(hWnd, (void*)lParam);
}
break;
...
return 0;
}

上の関数fはメッセージ処理で実行されるのでGUIスレッドになります。
スレッドを作成実行は以下。concurrency::create_taskを使用してます。
(concurrency::create_task、実はなんだかよくわかないので無視されがちな関数ではないかと思うのですが、
知らなくてもスレッドは作成できますし)


#include
void FooFunc(HWND hWnd)
{
DWORD gui_thread_id = ::GetCurrentThreadId(); // スレッドIDの取得

concurrency::create_task([hWnd,gui_thread_id](){

Sleep(1000);

// ... 重い処理


}).then( [hWnd,gui_thread_id](){

static DisptcherDelegate Complete;

struct Temp
{
DWORD gid;
DWORD tid;
};

Temp* t = new Temp();
t->gid = gui_thread_id;
t->tid = ::GetCurrentThreadId();

Complete = [](HWND,void* p){

Temp* t = (Temp*)p;
_ASSERT( t->gid == ::GetCurrentThreadId() );
_ASSERT( t->tid != ::GetCurrentThreadId() );

// ここでメッセージボックスなどのGUIに終了メッセージを出す。

delete t;
};

PostMessage( hWnd, WM_MY_DISPATCHER, (WPARAM)&Complete, (LPARAM)t );

});
}

重い処理とthenは独自のスレッドで実行され、スレッドIDはtidとなります。
Complete内はWndProcのGUIスレッドで走るためgidと同じになります。
_ASSERTのValidationは無事通過します。
C++/CXのスレッド処理スタイルを古いスタイルで書いてみました。


方法1:
Direct2Dで色を塗るとこうなります。ブラシというリソース(作成時にRenderTargetを必要とする)を作成して、描画後に開放します。
GDI32のSelectObjectに似た感じですが、リソースを描画単位で作成する方法はメモリリークの点で確実ですが、動作が遅くなります。
アニメのように1秒間に数十回描画する場合、作成開放も数十回実行されますから非効率です。


case WM_PAINT:
{
...

ID2D1DeviceContext* cxt = GetTarget();

ID2D1SolidColorBrush* red;
cxt->CreateSolidColorBrush( D2RGB(255,0,0), &red );
cxt->FillRectangle( rc, red );
red->Release();

...
}
break;

方法2:
そこで、リソースをまとめてどこかで作成して描画するとこのようになります。
WM_SIZEで使用するリソースを作成します。
リソースがメモリ上に置かれたままになるため、数が多いと問題になります。
速度は速いですが、RenderTargetが壊れた時のことを配慮しなければなりません。GDI32にはないポイントです。


case WM_PAINT:
{
...

ID2D1DeviceContext* cxt = GetTarget();

ID2D1SolidColorBrush* red = RESOURCE_GetSolidBrush(cxt, D2RGB(255,0,0));
cxt->FillRectangle( rc, red );
...
}
break;


case WM_SIZE:
{
...

ID2D1DeviceContext* cxt = GetTarget();

if ( cxt )
{
RESOURCE_DestroyBrsuhResource();
DestroyRenderTarget(cxt);
}

cxt = CreateRenderTarget( 幅, 高さ );

RESOURCE_CreateBrsuhResource(cxt);

}
break;

MSはRenderTargetが壊れた時にどうにかしろという説明をしていますが、あれ、よくわかりません。
逆に、積極的に壊すようなプログラムにしないと問題を把握できません。
そこで、画面サイズが変更になった時、RenderTargetを壊して作り直しています。

ちなみに、
一度作成したリソースをすべて綺麗に開放しないと、ID2D1DeviceContextの再作成は失敗するため、RESOURCE_DestroyBrsuhResourceは重要です。

以上、GDI32(HDCを使う昔ながらのプログラミング)にはない考慮すべき点でした。


HWNDレスを目的にしたWindowsコントロール フレームワークを作成しました。

*コントロール類をDirect2Dだけで描画しています。
*C関数として呼び出せるようにDLLにまとめました。

少なくとも最低これだけは作っておかないとマズイと思われるコントロールを作りましたが、
難解になってしまいまいました。

github にアップしてあります。試しにJavascriptを絡めたサンプルもあります。ビルドにはVisualStudio2015が必要です。


一時的な文字バッファにstd::stringクラスは使わない、ただそれだけで速くなった。


#pragma once

namespace V4 {

class SplitString
{
public :
SplitString(){}

~SplitString(){ clear(); }

USHORT Split( LPCWSTR str, WCHAR ch )
{
USHORT s = 0, e=0;
std::vector> ar;

int total_len = 0;

for( int i =0; i < lstrlen(str); i++ )
{
if ( str[i] == ch )
{
std::pair s1( s,e );
ar.push_back(s1);

total_len += (e-s+1);
s = e + 1;
}
e++;
}

if ( s!=e )
{
std::pair s1( s,e );
ar.push_back(s1);
total_len += (e-s+1);
}

WCHAR* wx = new WCHAR[ total_len ];
LPWSTR sb = wx;

for(auto& it : ar )
{
int len = it.second-it.first;

memcpy(sb, str+it.first, sizeof(WCHAR)*len);

sb[len] = 0;
sb += len+1;
}


cxt_.buf = (LPWSTR*)wx;
cxt_.total_len = total_len;
cxt_.cnt = (USHORT)ar.size();
cxt_.idx = new USHORT[cxt_.cnt];
int j = 0;
for(auto& it : ar )
cxt_.idx[j++] = it.first;

return cxt_.cnt;

}

LPCWSTR operator[] (USHORT idx )
{
if ( idx >= cxt_.cnt )
return NULL;

LPWSTR p = (LPWSTR) cxt_.buf;
return (LPCWSTR)&p[cxt_.idx[idx]];
}

USHORT size() const {cxt_.cnt; }

void clear(){ cxt_.Clear(); }

private :
struct Context
{
Context()
{
idx = nullptr;
buf = nullptr;
total_len = 0;
cnt = 0;
}

void Clear()
{
delete [](WCHAR*)buf;
delete [] idx;
buf = nullptr;
idx = nullptr;
total_len = 0;
cnt = 0;
}

LPWSTR* buf;
USHORT* idx;
USHORT total_len;
USHORT cnt;
};

Context cxt_;
};

};


C++には文字の分割で気の利いた方法がない。vectorで結果を受け取る方法は、その過程でメモリの
やり取りに無駄が多い。
そこで、SplitStringというクラスを作成して、試してみるとなんと3倍も速くなった。


#include "stdafx.h"
#include
#include
#include
#include

using namespace std;

typedef void (*Func1)();

double Task1( Func1 f );

void func1();
void func2();

int _tmain(int argc, _TCHAR* argv[])
{
_tsetlocale(0,L"");

std::wcout << L". 経過時間 " << Task1( func1 ) << L"s \n"; // 0.21s
std::wcout << L". 経過時間 " << Task1( func2 ) << L"s \n"; // 0.07s

return 0;
}
double Task1( Func1 f )
{
LARGE_INTEGER ntime,freq;
QueryPerformanceFrequency(&freq);
QueryPerformanceCounter(&ntime);

f();

LARGE_INTEGER ntime2;
QueryPerformanceCounter(&ntime2);
double duration =(((double )( ntime2.QuadPart-ntime.QuadPart) / ( double)freq.QuadPart)); // 経過時間 秒単位
return duration;
}

// ありがちな文字の分割方法
vector split(const wstring &str, WCHAR delim){
vector res;
size_t current=0, found;
while( (found=str.find_first_of(delim, current)) != wstring::npos)
 {
res.push_back(wstring(str, current, found - current));
current = found + 1;
}
res.push_back(wstring(str, current, str.size() - current));
return res;
}

void func1()
{
std::vector ar;
for( int i = 0; i < 1000; i++ )
{
ar.clear();
ar = split( L"If you say you need it, It's all for you, If you gotta have it, It's all for you", L' ');
}

std::wcout << ar[6].c_str() << L" " << ar[16].c_str()<< L" " << ar[8].c_str()<< L" " << ar[18].c_str();
}
void func2()
{
V4::SplitString cc;
for( int i = 0; i < 1000; i++ )
{
cc.clear();
int cnt = cc.Split( L"If you say you need it, It's all for you, If you gotta have it, It's all for you", L' ');
}

std::wcout << cc[6] << L" " << cc[16]<< L" "<< cc[8]<< L" "<< cc[18] ;
}