2008/03/25


たしかにコレ、やばいっす。
ku spreadsheetとつながってるってやばいじゃんこれspreadsheetをバックエンドdbにしていろいろできるってことでしょ
ku's post on twitter
使い方次第では、結構強力な物になりそうな気がする。
ちなみに
F's Garage:iPhone SDKを読み解くのに必須! Google AJAX Language APIを使ったブックマークWidget作った。
Google AJAX Language APIは、document.writeで翻訳機能読み込みのscript要素コードを出力するようにできており、ブックマークレットには必須の遅延ロードができないようなので、なんだかいろいろ苦労しちゃいました。
多分以下の様にすれば行けるはず。(jsapiのクエリパラメータ「callback」と、loadの第三引数「callback」)
wikiも確かに管理しやすいかもしれないけど、Google SpreadSheetも可能性があると思う。
ちなみに今回作ったSITEINFOは簡単な物なので、「ネタフル」と「IDEA * IDEA」くらいしかない。
google-spreadsheets-siteinfo
あと、クエリのfrom区には「sheet1」とか使えるのが分かった。
以下、Google SpreadSheetからSITEINFOを読み込むサンプル
var siteinfo = [];
function handleQueryResponse(response) {
    var data = response.getDataTable();
    if (!data || response.isError()) {
        alert(response.getMessage() + ':' + response.getDetailedMessage());
        return;
    }
    for (var row = 0; row < data.getNumberOfRows(); row++) {
        siteinfo.push({
            'name'  : data.getFormattedValue(row, 0),
            'link'  : data.getFormattedValue(row, 1),
            'url'   : data.getFormattedValue(row, 2),
            'xpath' : data.getFormattedValue(row, 3),
            'base'  : data.getFormattedValue(row, 4)
        });
    }
    var html = '';
    for (var n = 0; n < siteinfo.length; n++) {
        html += '<b><a href="' + siteinfo[n].link + '">' + siteinfo[n].name + '</a></b><br />'
            html += '<blockquote' + '><' + 'pre>';
        html += '<b>url</b>:' + siteinfo[n].url + '<br />';
        html += '<b>xpath</b>:' + siteinfo[n].xpath + '<br />';
        html += '<b>base</b>:' + siteinfo[n].base + '<br />';
        html += '<' + '/pre></' + 'blockquote>';
    }
    document.getElementById('ExampleSITEINFO').innerHTML = html;
}

function querySITEINFO() {
    var query = new google.visualization.Query(
            "http://spreadsheets.google.com/tq?key=psdg8ZffuCtcsEuIuzzd1Mw&pub=1");
    query.setQuery("select A, B, C, D, E from sheet1 order by A desc");
    query.send(handleQueryResponse);
}

function jsapi_loaded() {
    google.load("visualization", "1", {"callback" : querySITEINFO});
}

function loadExampleSITEINFO() {
    var s = document.createElement('script');
    s.charset = 'utf-8';
    s.src = 'http://www.google.com/jsapi?callback=jsapi_loaded';
    document.body.appendChild(s);
}
とその実行結果。
IE6、Firefox、Safari3、Opera9.50bで動作確認。

続きを読む...

Posted at by



2008/03/21


C言語される方は見ておいた方が良いかもしれない。
ロベールのC++入門講座を読んで C++ を初歩の初歩から再入門するよ - 前編 - ひげぽん OSとか作っちゃうかMona-
[] 演算子は a[b] と b[a] はおなじ意味らしい。なんと!
それぞれ *(a + b) 、*(b + a) になるので等しいのですね。
うむ。しらんかった。確かに出力されるアセンブリも
c-code-1
c-code-2
となり、結果同じ操作なのだけれど、まさか文法的にコレが通るとは思ってなかった。
int main(void) {
    int a[3];
    0[a] = 1;
    1[a] = 2;
    printf("%d,%d,%d,%d\n", a[0], a[1], 0[a], 1[a]);
    return 0;
}
知らない事だらけだ...
Posted at by



2008/03/19


私もこれまで色々なWindowsアプリケーションを作ってきましたが、それらの多くはデスクトップ上で目的の動作だけを実行する単純なアプリケーションだったりします。
最近のテキストエディタ等では、マクロ等と呼ばれる拡張言語を使用してエディタ本来の動作では実現出来ない色々な追加機能を実行する事が出来る様になっています。
今日は、既存のWin32アプリケーションにJavaScriptでマクロが実行出来る様にする為のtipsをご紹介。
拡張言語といってもJavaScriptの様に柔軟性のある言語を作り直すとなると程遠い工数を掛けてしまう事になりますが、Windowsには「ScriptControl」というスクリプト実行コンポーネントが用意されています。
今回はこれを使って外部にあるJavaScriptファイルを実行し、かつそのJavaScriptからアプリケーション内のオブジェクトを操作するまでを説明します。ScriptControlはCOMで実装されており、以下の様にインスタンスを生成します。     hr = CoCreateInstance(
            CLSID_ScriptControl,
            NULL,
            CLSCTX_ALL,
            IID_IScriptControl,
            (void**)&pScriptCtrl);
そしてJavaScript(JScript)を実行させる為にLanguageプロパティを設定してExecuteStatementを実行します。
    hr = pScriptCtrl->put_Language(_bstr_t("JScript"));
    hr = pScriptCtrl->put_Timeout(-1);
    hr = pScriptCtrl->put_AllowUI(VARIANT_FALSE);
    hr = pScriptCtrl->ExecuteStatement(A2BSTR("var a = 'test'"));
これだけで既存のアプリケーションからJavaScriptが実行出来るようになります。
ただこれだけでは既存アプリケーションとの連携はまったく無く、面白味がありませんし単なる計算言語にしか成り得ません。
ブラウザ上で実行されるJavaScriptの様にwindowオブジェクトも無ければdocumentオブジェクトもありません。
つまりalertは使えません
このオブジェクトをJavaScript上に追加するのがAddObjectメソッドです。
AddObjectメソッドは名称指定でIUnknownオブジェクトをJavaScriptの実行スコープに追加出来ます。
ここに既存アプリケーションのオブジェクトを差し込む事になります。JavaScriptではメソッドを呼び出そうとする際にそのオブジェクトに対してIDispatchへのQueryInterfaceを試み、GetIDsOfNamesでdispIDを取得した後にInvokeメソッドを呼び出します。
この辺は、COMの知識のある方ならば既にご存知ですね。
で実装したIDispatchは以下の様なコードになりました。
class MyObject : public IDispatch
{
private:
    LONG m_cRef;

    // method type
    typedef HRESULT (MyObject::*Func)(DISPPARAMS*, VARIANT*);

    // method structure
    typedef struct _MY_OBJECT_METHOD_TABLE {
        _MY_OBJECT_METHOD_TABLE(DISPID dispid, const char* name, Func fn) {
            this->dispid = dispid;
            this->name = name;
            this->fn = fn;
        }
        DISPID dispid;
        const char* name;
        Func fn;
    } MY_OBJECT_METHOD_TABLE;

    // method table
    std::vector<MY_OBJECT_METHOD_TABLE> myObjectMethodTable;

public:
    // method: say
    //  show MessageBox
    HRESULT say(DISPPARAMS* pDispParams, VARIANT* ret) {
        USES_CONVERSION;

        for(int n = 0; n < pDispParams->cArgs; n++) {
            HRESULT hr;
            VARIANT arg;
            VariantInit(&arg);
            hr = VariantChangeType(&arg, &pDispParams->rgvarg[n], 0, VT_BSTR);
            MessageBox(0, OLE2T(arg.bstrVal), _T("MyObject"), MB_OK);
        }
        if (ret) {
            VariantInit(ret);
            V_VT(ret) = VT_BOOL;
            V_BOOL(ret) = VARIANT_TRUE;
        }
        return S_OK;
    }

    // method: start
    //  start program by arguments
    HRESULT start(DISPPARAMS* pDispParams, VARIANT* ret) {
        USES_CONVERSION;

        for(int n = 0; n < pDispParams->cArgs; n++) {
            HRESULT hr;
            VARIANT arg;
            VariantInit(&arg);
            hr = VariantChangeType(&arg, &pDispParams->rgvarg[n], 0, VT_BSTR);
            ShellExecute(NULL, _T("open"), OLE2T(arg.bstrVal), NULL, NULL, SW_SHOW);
        }
        if (ret) {
            VariantInit(ret);
            V_VT(ret) = VT_BOOL;
            V_BOOL(ret) = VARIANT_TRUE;
        }
        return S_OK;
    }

    // constructor
    MyObject() : m_cRef(0) {
        myObjectMethodTable.push_back(MY_OBJECT_METHOD_TABLE(1, "say", say));
        myObjectMethodTable.push_back(MY_OBJECT_METHOD_TABLE(2, "start", start));
    }

    STDMETHODIMP QueryInterface(REFIID rid, LPVOID *ppv) {
        *ppv = NULL;
        if (::IsEqualIID(rid, IID_IUnknown)) {
            *ppv = this;
            AddRef();
            return S_OK;
        }
        if (::IsEqualIID(rid, IID_IDispatch)) {
            *ppv = this;
            AddRef();
            return S_OK;
        }
        return E_NOINTERFACE;
    }
    ULONG STDMETHODCALLTYPE AddRef() {
        ULONG ref = InterlockedIncrement(&m_cRef);
        return ref;
    }
    ULONG STDMETHODCALLTYPE Release() {
        ULONG ref = InterlockedDecrement(&m_cRef);
        return ref;
    }
    STDMETHODIMP GetTypeInfoCount(UINT *ptiCount) {
        if (ptiCount) *ptiCount = 0;
        return S_OK;
    }
    STDMETHODIMP GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **pptInfo) {
        if (pptInfo) *pptInfo = NULL;
        return S_OK;
    }
    STDMETHODIMP GetIDsOfNames(REFIID riid, OLECHAR **rgszNames, UINT cNames, LCID lcid, DISPID *rgDispID) {
        USES_CONVERSION;

        for(UINT n = 0; n < cNames; n++) {
            std::vector<MY_OBJECT_METHOD_TABLE>::iterator it = myObjectMethodTable.begin();
            for(it = myObjectMethodTable.begin(); it != myObjectMethodTable.end(); it++) {
                if (!strcmp(it->name, OLE2A(rgszNames[n]))) {
                    rgDispID[n] = it->dispid;
                    break;
                }
            }
            if (it == myObjectMethodTable.end()) return E_UNEXPECTED;
        }
        return S_OK;
    }
    STDMETHODIMP Invoke(DISPID dispID, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr) {
        std::vector<MY_OBJECT_METHOD_TABLE>::iterator it = myObjectMethodTable.begin();
        for(it = myObjectMethodTable.begin(); it != myObjectMethodTable.end(); it++) {
            if (it->dispid == dispID) {
                return (this->*(it->fn))(pDispParams, pVarResult);
            }
        }
        if (it == myObjectMethodTable.end()) return E_UNEXPECTED;
        return S_OK;
    }
};
単純に引数の文字列をメッセージボックスで表示する「say」メソッドと、引数の文字列をプログラムとして起動する「start」メソッドを実装しています。
これを実行する「plugin.js」は以下の様になります。
MyObject.say('Hello');
MyObject.start('http://mattn.kaoriya.net');
これを実行すると「Hello」というメッセージボックスが表示された後、このサイトがブラウザで表示される結果となります。
20080319121041
全体のソースコードは以下の通り。
#include <atlbase.h>
#include <windows.h>
#include <string>
#include <vector>
#import <msscript.ocx> raw_interfaces_only, named_guids, no_namespace
#pragma comment(lib, "ole32.lib")
#pragma comment(lib, "shell32.lib")

class MyObject : public IDispatch
{
private:
    LONG m_cRef;

    // method type
    typedef HRESULT (MyObject::*Func)(DISPPARAMS*, VARIANT*);

    // method structure
    typedef struct _MY_OBJECT_METHOD_TABLE {
        _MY_OBJECT_METHOD_TABLE(DISPID dispid, const char* name, Func fn) {
            this->dispid = dispid;
            this->name = name;
            this->fn = fn;
        }
        DISPID dispid;
        const char* name;
        Func fn;
    } MY_OBJECT_METHOD_TABLE;

    // method table
    std::vector<MY_OBJECT_METHOD_TABLE> myObjectMethodTable;

public:
    // method: say
    //  show MessageBox
    HRESULT say(DISPPARAMS* pDispParams, VARIANT* ret) {
        USES_CONVERSION;

        for(int n = 0; n < pDispParams->cArgs; n++) {
            HRESULT hr;
            VARIANT arg;
            VariantInit(&arg);
            hr = VariantChangeType(&arg, &pDispParams->rgvarg[n], 0, VT_BSTR);
            MessageBox(0, OLE2T(arg.bstrVal), _T("MyObject"), MB_OK);
        }
        if (ret) {
            VariantInit(ret);
            V_VT(ret) = VT_BOOL;
            V_BOOL(ret) = VARIANT_TRUE;
        }
        return S_OK;
    }

    // method: start
    //  start program by arguments
    HRESULT start(DISPPARAMS* pDispParams, VARIANT* ret) {
        USES_CONVERSION;

        for(int n = 0; n < pDispParams->cArgs; n++) {
            HRESULT hr;
            VARIANT arg;
            VariantInit(&arg);
            hr = VariantChangeType(&arg, &pDispParams->rgvarg[n], 0, VT_BSTR);
            ShellExecute(NULL, _T("open"), OLE2T(arg.bstrVal), NULL, NULL, SW_SHOW);
        }
        if (ret) {
            VariantInit(ret);
            V_VT(ret) = VT_BOOL;
            V_BOOL(ret) = VARIANT_TRUE;
        }
        return S_OK;
    }

    // constructor
    MyObject() : m_cRef(0) {
        myObjectMethodTable.push_back(MY_OBJECT_METHOD_TABLE(1, "say", say));
        myObjectMethodTable.push_back(MY_OBJECT_METHOD_TABLE(2, "start", start));
    }

    STDMETHODIMP QueryInterface(REFIID rid, LPVOID *ppv) {
        *ppv = NULL;
        if (::IsEqualIID(rid, IID_IUnknown)) {
            *ppv = this;
            AddRef();
            return S_OK;
        }
        if (::IsEqualIID(rid, IID_IDispatch)) {
            *ppv = this;
            AddRef();
            return S_OK;
        }
        return E_NOINTERFACE;
    }
    ULONG STDMETHODCALLTYPE AddRef() {
        ULONG ref = InterlockedIncrement(&m_cRef);
        return ref;
    }
    ULONG STDMETHODCALLTYPE Release() {
        ULONG ref = InterlockedDecrement(&m_cRef);
        return ref;
    }
    STDMETHODIMP GetTypeInfoCount(UINT *ptiCount) {
        if (ptiCount) *ptiCount = 0;
        return S_OK;
    }
    STDMETHODIMP GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **pptInfo) {
        if (pptInfo) *pptInfo = NULL;
        return S_OK;
    }
    STDMETHODIMP GetIDsOfNames(REFIID riid, OLECHAR **rgszNames, UINT cNames, LCID lcid, DISPID *rgDispID) {
        USES_CONVERSION;

        for(UINT n = 0; n < cNames; n++) {
            std::vector<MY_OBJECT_METHOD_TABLE>::iterator it = myObjectMethodTable.begin();
            for(it = myObjectMethodTable.begin(); it != myObjectMethodTable.end(); it++) {
                if (!strcmp(it->name, OLE2A(rgszNames[n]))) {
                    rgDispID[n] = it->dispid;
                    break;
                }
            }
            if (it == myObjectMethodTable.end()) return E_UNEXPECTED;
        }
        return S_OK;
    }
    STDMETHODIMP Invoke(DISPID dispID, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr) {
        std::vector<MY_OBJECT_METHOD_TABLE>::iterator it = myObjectMethodTable.begin();
        for(it = myObjectMethodTable.begin(); it != myObjectMethodTable.end(); it++) {
            if (it->dispid == dispID) {
                return (this->*(it->fn))(pDispParams, pVarResult);
            }
        }
        if (it == myObjectMethodTable.end()) return E_UNEXPECTED;
        return S_OK;
    }
};

// load script and return the code
char* loadScriptAlloc(LPCTSTR pszFileName) {
    HANDLE hFile;
    DWORD dwFileSize, dwReadSize;
    char* pszData = NULL;

    hFile = CreateFile(
            pszFileName,
            GENERIC_READ,
            FILE_SHARE_READ,
            NULL,
            OPEN_EXISTING,
            FILE_ATTRIBUTE_NORMAL,
            NULL);
    if (hFile == INVALID_HANDLE_VALUE) goto leave;

    dwFileSize = GetFileSize(hFile , NULL);
    if (dwFileSize == (DWORD)-1) goto leave;
    pszData = (char*)calloc(dwFileSize + 1, 1);
    if (!pszData) goto leave;

    if (!ReadFile(
            hFile,
            pszData,
            dwFileSize,
            &dwReadSize, NULL)) goto leave;
leave:
    if (hFile != INVALID_HANDLE_VALUE) CloseHandle(hFile);
    return pszData;
}

int main(int argc, char *argv[]) {
    USES_CONVERSION;

    HRESULT hr = 0;
    IScriptControl* pScriptCtrl = NULL;
    char* pszCode = NULL;
    MyObject* pMyObject = NULL;

    CoInitialize(NULL);

    hr = CoCreateInstance(
            CLSID_ScriptControl,
            NULL,
            CLSCTX_ALL,
            IID_IScriptControl,
            (void**)&pScriptCtrl);
    if (FAILED(hr) || !pScriptCtrl) goto leave;

    hr = pScriptCtrl->put_Language(_bstr_t("JScript"));
    if (FAILED(hr)) goto leave;

    hr = pScriptCtrl->put_Timeout(-1);
    if (FAILED(hr)) goto leave;

    hr = pScriptCtrl->put_AllowUI(VARIANT_FALSE);
    if (FAILED(hr)) goto leave;

    pMyObject = new MyObject();
    hr = pScriptCtrl->AddObject(_bstr_t("MyObject"), pMyObject, VARIANT_TRUE);

    pszCode = loadScriptAlloc(_T("plugin.js"));
    if (!pszCode) goto leave;

    hr = pScriptCtrl->ExecuteStatement(A2BSTR(pszCode));
    free(pszCode);
    pszCode = NULL;

leave:
    if (pszCode) {
        free(pszCode);
        pszCode = NULL;
    }
    if (pScriptCtrl) {
        pScriptCtrl->Reset();
        pScriptCtrl->Release();
        pScriptCtrl = NULL;
    }
    if (pMyObject) {
        pMyObject->Release();
        pMyObject = NULL;
    }
    CoUninitialize();
    return 0;
}
あとはこの「say」メソッドなり「start」メソッドなりを既存アプリケーションとの連携用メソッドとして実装すれば、見事JavaScriptによるプラグイン機能が実現出来ます。
意外と少ないコードで実装出来るので皆さん昔に作ったアプリケーション等を拡張して見られてはどうでしょうか。

VisualC++プログラマのためのCOM入門: はじめるWindowsシステムプログラミング VisualC++プログラマのためのCOM入門: はじめるWindowsシステムプログラミング
豊田 孝
翔泳社 単行本 / ¥743 (1999年06月01日)
 
発送可能時間:

Posted at by