エクセルセル範囲を代入したVariant型変数をC/C++で作るDLLに渡すには?

解決


mitarashi  2009-04-26 10:27:01  No: 70062

初めて質問させていただきます。よろしくお願いします。
Excelの多量の文字列処理をC/C++で作ったDLLでやろうとしています。文字列一個ずつを渡して処理する事は出来たのですが、期待した程速くならなかったので、配列のまま渡して処理してみようとトライしています。
http://madia.world.coocan.jp/vc/vc_bbs/200609/200609_06090040.html
のBlueさんのご回答を参考に、一次元の文字列配列をDLL側で受けるのは確認出来たのですが、
http://support.microsoft.com/kb/410837/ja
の記事も参考に、二次元の配列を処理しようとしたとき、
VBA側で、variant型の変数に、セル範囲を代入したもの(例:myVariant = Range("A1:B2")といった具合に)をDLLに渡そうとすると、
if ( v->vt != ( VT_ARRAY | VT_BSTR | VT_BYREF ) ) return;
を通らないので、とりあえずコメントアウトして動かしてみると、
SafetyArrayに代入する
psa = *( v->pparray );
のところで、Excelが異常終了してしまいます。
「variant型の変数に、セル範囲を代入したもの」を渡すのは無理なのでしょうか。また、可能とすると、どのようにやれば良いのでしょうか。御教示下さい。
なお、VC++掲示板に投稿するのは申し訳ありませんが、BCCで作成しています。(PC絶不調でVS2005の再インストールは様子見をしています)


オショウ  2009-04-26 20:47:12  No: 70063

回答ではありません。

Excel外部との連携で処理を行う場合、劇的に処理速度が遅いと言うことは
ご存じでしょうか?

過去にエクセルブック(複数シート)に40万個のセルに文字列・数値セル
があり、それを読みだして処理する機能を作ることになりましたが、例え
C言語で作成してもエクセルから読みだす段階で時間がかかり断念しまし
た。

有償のコンポーネントを使って劇的な処理速度のアップを行え、以後は、
エクセル未搭載環境でもエクセルファイルの読み書きに重宝しています。

アドバンスソフトウェア社のExcelCreatorとか
エクセルソフト社販売のSpreadSheetGear2008とか

試用版がありますので、お試し下さい。

参考まで。

以上。


mitarashi  2009-04-27 01:30:33  No: 70064

オショウ様、ご回答ありがとうございます。
一介のアマチュアでして、興味本意でトライしています。折角のご提案ですが、有償コンポーネントには手が届きません。(使いこなすスキルも持ち合わせません)

本日、バックアップ用のHDが届いたので、VS2005のインストール〜HDイメージの保存を行ったところ朝から3時までかかってしまいました。その挙げ句に起動時にネットワーク接続のところでResetがかかる症状が再発して、ショックを受けています。旧イメージに戻す羽目にならなければ、VC++でもやってみる所存です。


mitarashi  2009-04-27 09:25:25  No: 70065

検索先を海外にまで広げ、それらしい物を見つけました。
http://www.codeproject.com/KB/office/A_C___dll_for_Excel.aspx
セル範囲そのもや、セル範囲をVariantがた変数に代入した物をを渡して動作するのは確認しましたが、ソースは...OLEなんて未知の世界です。
上記は足し算の例ですが、文字列が取り扱えるか考えてみます。


Blue  2009-04-27 18:19:44  No: 70066

Rangeオブジェクトの配列になるので、VARIANT型の変数には、
ディスパッチポインタが格納されるはずです。
vtメンバ変数で確認し、適した使用をしないと動くわけはないでしょう。

ディスパッチポインタから使えるようにするのは面倒です。
たぶん文字列で渡していたよりも遅くなると思われます。

codeprojectのはRangeオブジェクトで渡しているわけではないです。
数値であれば(VBA側で)double型にして渡しているので、文字列から変換するよりも若干早くなるのかも。

VARIANT型の文字列配列を得るのであれあば、
>myVariant = Range("A1:B2")
ではなく(これだとRangeオブジェクトの配列=ディスパッチポインタの配列)
Valueプロパティを使ってください。

    Dim v As Variant
    Dim i As Integer, j As Integer
    v = Range("A1:B2").Value
    
    ' 中身を確認してみる
    For i = LBound(v) To UBound(v)
        For j = LBound(v, 2) To UBound(v, 2)
            Debug.Print CStr(v(i, j)) & "(" & TypeName(v(i, j)) & ")"
        Next
    Next

数値が入っているとき、VARIANT型には数値で格納されてしまうので、
DLL側でvtをチェックして文字列に変換するか、VBA側で、Valueプロパティを使わず、
文字列で全てわたすようにすることが必要でしょう。


k  2009-04-27 20:47:54  No: 70067

速度については未確認ですが

Visual C++ Excel操作編
http://www.ujasiri.com/prglib/vc/excel/vc_excel.html

範囲指定したCellのデータを取得
http://www.ujasiri.com/prglib/vc/excel/import/xls_imp_getarray_exp.html
こゆことがしたいのでしょうか?


mitarashi  2009-04-28 06:48:54  No: 70068

Blueさん、kさん、昨晩の誤字脱字だらけの自己レスに対し、アドバイス・情報をありがとうございます。○○○○管理職なので、平日は進展が図れませんが、取り急ぎお礼を申し上げます。Reactionは遅れると思いますが、ご容赦下さい。


mitarashi  2009-04-29 09:29:28  No: 70069

Blue様

>codeprojectのはRangeオブジェクトで渡しているわけではないです。
何故あの例を載せてあるのか疑問ですが、サンプルのVBAコードはご指摘の通りです。
下記の様なVBAのコードで試験しましたが、いずれも動作しました。
Sub checkArrayTest()
    Dim rng As Range
    Dim rngArray As Variant
    
    Set rng = Range("b3:f7")
    Range("h3:l7") = SumOneToArray(rng) '9 VT_DISPATCH
    rngArray = rng
    Range("h3:l7") = SumOneToArray(rngArray) '8204 VT_ARRAY | VT_VARIANT
    rngArray = rng.Value
    Range("h3:l7") = SumOneToArray(rngArray) '8204 VT_ARRAY | VT_VARIANT
    rngArray = rng.Text
    Range("h3:l7") = SumOneToArray(rngArray) '1 VT_NULL
End Sub
コメントは、C++のコードに、
wsprintf(buf,"%d",sourceArray.vt);
を足して、何が受け渡されているのか確認したものです。VT_...の方はWEBでそれらしいものを探してみました。rngArray = rngと、rngArray = rng.Valueは同じ物が渡されている様でした。最後のが動くのは不思議です。

実は、上記の確認のために、wsprintf(buf,"%d",sourceArray.vt);と、MessageBoxAを、下記の様な関数を追加して記述したのですが、その1はメモリがreadになれないとして、異常終了し、その2は、VBAのエラー49が出てDLLが参照できませんでした。DEFファイルには、myType関数を加えてあります。コピーして中味を単純化しただけのつもりですが、何がまずいのでしょうか?VBAのDeclareのデータ型も合っていると思うのですが。
Public Declare Function myType Lib _
"ExcelArray.dll" (ByVal X As Variant) As Variant(またはlong)

<その1>
VARIANT _stdcall myType(VARIANT& myArray)
{
  char msg[256];
  wsprintf(msg,"%d",myArray.vt);
  MessageBoxA(0, msg, "Variant Type", MB_OK);
  return (_variant_t((long)0));
  VariantClear(&myArray);
}
<その2>
long _stdcall myType(VARIANT& myArray)
{
  中略
  return 0;
  VariantClear(&myArray);
}
中味を理解する前に、初めの一歩でつまずいています。ご指導いただけると幸いです。

k様
教えていただいたコードは、他アプリからExcelを制御するような事例の、C/C++版だと理解しました。C/C++には詳しくなく、ExcelからC/C++製のDLLを使う事で、比較的楽にC/C++の高速さを享受できないかなと考えておりますので、ご了解下さい。


mitarashi  2009-04-30 07:55:54  No: 70070

自己レスです。異常終了の原因は分かりました。codeprojectのコードで、
VARIANT _stdcall CheckExcelArray(VARIANT& ExcelArray)
と、
VARIANT _stdcall SumOneToArray(VARIANT sourceArray)
の関数があり、たまたま上の方をコピーしてきていじっていたのですが、上の方はVARIANT& だったのですね。思いこみというものは怖ろしいもので、はまってしまい、悩まされました。スレ汚しで済みません。これから中味を勉強いたします。


mitarashi  2009-05-04 01:43:34  No: 70071

こちらで質問したい誘惑にかられつつ、WEBで調べまくって数値の配列→文字列の配列への変更にトライしておりました。見よう見まねでSysAllocStringを使ってとりあえず下記で動きました。速度上のメリットは無いのかもしれませんが(未確認)、勉強にはなりました。しばらくしたら閉じます。
−中略−
  for(i=0; i<nrows; i++)
    {
    for(j=0; j<ncols; j++)
      {
      long indi[] = {i+1,j+1};
      //check for the type of VARIANT in the array
      switch(dArray[(i*ncols)+j].vt)
        {
        case VT_BSTR:
          wcscpy_s(retBuff,sizeof(retBuff)/sizeof(wchar_t), dArray[(i*ncols)+j].bstrVal);
          retVal = myShori(retBuff); //自作の処理関数
          SysFreeString(dArray[(i*ncols)+j].bstrVal);
          dArray[(i*ncols)+j].bstrVal = SysAllocString(retBuff); 
          break;

        default:
          break;
        }
      SafeArrayPutElement(destArray.parray, indi, &dArray[(i*ncols)+j]);
      SysFreeString (dArray[(i*ncols)+j].bstrVal );
      }
    }
  delete [] dArray;
  return destArray;


mitarashi  2009-05-11 05:10:20  No: 70072

ツッコミも入りませんので閉じさせていただきます。
Microsoftの、古い資料(VB3!〜6用)がいくつかhitしたので、現在引数引き渡しのバリエーションについて網羅してやろうとトライしています。また、不明の点が生じたら質問させてください。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加