C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

ログ内検索
  • キーワードを複数指定する場合は 半角スペース で区切ってください。
  • 検索条件は、(AND)=[A かつ B] (OR)=[A または B] となっています。
  • [返信]をクリックすると返信ページへ移動します。
キーワード/ 検索条件 /
検索範囲/ 強調表示/ ON (自動リンクOFF)
結果表示件数/ 記事No検索/ ON
大文字と小文字を区別する

全過去ログを検索

<< 0 | 1 >>
■75393  Re[10]: VB.netからVC++6.0のへの構造体配列参照渡し
□投稿者/ とら -(2015/03/19(Thu) 19:42:06)
    No75392 (とら さん) に追記

    すみません、こちらで見直したところ無事成功致しました。

    >> LPSAFEARRAY psa = *Type1;
    > 今回成功したパターンは、VC++で受け取った後にLPSAFEARRAY型に代入しているという事は
    > TEST_API int __stdcall Test1(LPSAFEARRAY *Type1)
    > という元々の状態ではなく
    > TEST_API int __stdcall Test1(T_Struct *type1)
    > の状態で受け取っているという事でしょうか?

    こちらは元々の状態、つまりVC++側には手を入れないで良いみたいですね。


    > >(1) AssemblyInfo.vb にて、
    > >   <Assembly: ComVisible(True)>
    > >   <Assembly: Guid("……")>
    > >  を指定しておきます。プロジェクトのプロパティの[アプリケーション]-[アセンブリ情報]でも指定可。
    >
    >> <ComVisible(True), Guid("8D60602D-452A-48A1-ACEF-AF148A6E41B8")> _
    >
    > GUIDの部分が少々理解が追い付きませんでしたが、ここに設定する値は
    >
    > >Guid.NewGuid().ToString("D")等で生成した値を指定
    >
    > とありましたが、この値はこの関数を実行するたびに変化するのでしょうか
    >
    >
    > また、
    >
    > >(2) 構造体側にも、また別の GUID を割り当てておきます。
    >
    > とありましたが、AssemblyInfo.vbに設定するGUIDと構造体に設定するGUIDは違うものにしておくのですか?

    これは別のGUIDを指定しておけば良いのですね。
    GUIDだからという先入観で同じものにするのかと思い込んでおりました、申し訳御座いません。


    このたびは大変お世話になりました、そして自分の力不足を痛感致しました。

    特に、魔界の仮面弁士さんには尽力を尽くして頂いたようで感謝の極みです。

    誠に有難う御座いました。
記事No.75306 のレス / END /過去ログ127より / 関連記事表示
削除チェック/

■75336  Re[4]: VB.netからVC++6.0のへの構造体配列参照渡し
□投稿者/ 魔界の仮面弁士 -(2015/03/16(Mon) 19:34:59)
    2015/03/17(Tue) 10:06:34 編集(投稿者)

    No75312 (魔界の仮面弁士) に追記
    > もし、引数が C スタイルの配列ではなく、COM の SAFEARRAY で宣言されていたなら
    > C++ 側で要素数を確認することもできるでしょうけれども、その場合、VB6 では
    > 「Type ステートメントで型定義されたユーザー定義型」の配列ではなく、
    > 「タイプライブラリにて型定義された構造体」の配列になっていたはず。

    駄目元で、型定義を公開してみました。
    P/Invoke の際に必要な手続きなのかどうかは分かりませんけど。
    (COM Interop だった場合は、型公開が必要なはず…?)


    (1) AssemblyInfo.vb にて、
       <Assembly: ComVisible(True)>
       <Assembly: Guid("……")>
      を指定しておきます。プロジェクトのプロパティの[アプリケーション]-[アセンブリ情報]でも指定可。

    (2) 構造体側にも、また別の GUID を割り当てておきます。

      <ComVisible(True), Guid("……"), StructLayout(LayoutKind.Sequential)> _
      Public Structure Structure1
        <MarshalAs(UnmanagedType.I4)> Public aaa As Integer
        <MarshalAs(UnmanagedType.I4)> Public bbb As Integer
        <MarshalAs(UnmanagedType.I4)> Public ccc As Integer
      End Structure


    実際の Guid 属性には、 Visual Studio 付属の GUIDGEN.EXE もしくは
    「TextBox.Text = Guid.NewGuid().ToString("D")」などで生成した値を指定します。


    (3) スタートメニューの "Visual Studio コマンドプロンプト" を右クリックして管理者起動し、
     「RegAsm C:\YourFolder\YourAppName.EXE」を実行します。(実際のパスにあわせてください)

     『型は正常に登録されました。』などのメッセージがされれば登録完了。

    この段階で、レジストリの
     HKEY_CLASSES_ROOT\Record\{構造体のGuid}\バージョン番号\
    に、構造体が記録されるはず。


    ---------

    上記を実施したうえで、SafeArraySubType を変更してみたり、
    Object 型でしてみたりと試行錯誤していたところ、

    ・配列が 1 件になることもなく呼び出せたものの、
     DLL 側での編集結果が反映されないケース(中身が呼び出し時のまま)。

    に至りました。好転したかと思いきや、同じコードのまま
     AccessViolationException「保護されているメモリに読み取りまたは書き込み操作を行おうとしました。他のメモリが壊れていることが考えられます。」
    が発生することもあったので、恐らくは誤った呼び出し方のようです。…難しいですね。

    他のパターンとしては、
     SafeArrayTypeMismatchException「指定された配列は期待されたタイプではありません。」
    で呼び出せなかったり、呼び出せても配列が 1 件になってしまう状況だったりしており、
    解決までの道のりは長そうです。


    ただ、以前と同じ Declare 宣言でも、構造体定義を RegAsm するかどうかで
    エラーの内容が変わる組み合わせがあったので、公開すること自体は
    まったくの無意味では無さそうに思います。(確証は持てませんが)


    No75332 (とら さん) に返信
    > 引き続き他の方々もご存知の方がおられましたらご回答頂ければ助かります。

    SAFEARRAY** や LPSAFEARRAY* での構造体受け渡しに関する質問は
    ネット上でも度々見かけますが、成功事例がなかなか見当たりません。

    セーフ配列を使わず、配列先頭ポインタと配列長を渡す実装に変更して回避するケース、
    質問を取り下げて諦めるケース、中継用のヘルパーライブラリを用意して回避したケース、
    VB/C# 側でSAFEARRAY 相当のメモリ構造を組み上げてそれを IntPtr で渡すケース、
    カスタムマーシャラを作るケースなどなど…。


    そんな中、こんな記事を見つけました。参考になりますか?

    "Marshaling a SAFEARRAY of Managed Structures by P/Invoke"
    https://limbioliong.wordpress.com/category/netcomactivex-interop/safearrays/

    私自身まだ読みきっていませんが、標題から推察するに、今回の案件に相当する話題かと。
記事No.75306 のレス /過去ログ127より / 関連記事表示
削除チェック/

■75340  Re[5]: VB.netからVC++6.0のへの構造体配列参照渡し
□投稿者/ とら -(2015/03/17(Tue) 17:13:07)
    No75336 (魔界の仮面弁士 さん) に返信

    > ただ、以前と同じ Declare 宣言でも、構造体定義を RegAsm するかどうかで
    > エラーの内容が変わる組み合わせがあったので、公開すること自体は
    > まったくの無意味では無さそうに思います。(確証は持てませんが)

    色々と詳細に調査して頂き感謝しきれません、有難う御座います。


    > セーフ配列を使わず、配列先頭ポインタと配列長を渡す実装に変更して回避するケース、
    > 質問を取り下げて諦めるケース、中継用のヘルパーライブラリを用意して回避したケース、
    > VB/C# 側でSAFEARRAY 相当のメモリ構造を組み上げてそれを IntPtr で渡すケース、
    > カスタムマーシャラを作るケースなどなど…。

    今回はあまり時間的な余裕もなく、これ以上VC++側に手を加えない方向で進むのは難しいと考え
    不本意ではありますが他の手法で行ってみます。

    取り急ぎ、
    > セーフ配列を使わず、配列先頭ポインタと配列長を渡す実装に変更して回避するケース、
    について調査してみます。

    一応質問は閉めず、結果の書き込みまたは別途上記ケースについて質問があった場合に追記致します。
    (恐らくVC側での質問を行う事になると思いますが……。)
記事No.75306 のレス /過去ログ127より / 関連記事表示
削除チェック/

■75354  Re[6]: VB.netからVC++6.0のへの構造体配列参照渡し
□投稿者/ とら -(2015/03/18(Wed) 14:04:58)
    今回の問題点を解決するにあたっての方針を以下のようにするつもりです。
    (ただし、当方VC++というかC++系はほとんど触れた事がありません、ポインタ等も概念程度を僅かに知っている程度です)
    
    @ VC++6.0をVC++2008へとアップグレードする。
    
    A VC++にて構造体配列の先頭データ1つと配列の要素数を受け取るように変更する。
    
    B VBで使用している構造体と同じように構造体を宣言し、VBから呼び出されたメソッドにて構造体配列を作成する。
    
    C VBから呼び出されたメソッドでは、引数で受け取った構造体配列を他のメソッドにそのまま渡していたので、
      引数で受け取った構造体配列を他メソッドに渡すのではなく、Bにて作成した構造体配列を渡すようにする。
    
    D VCで作成した構造体配列をVBから受け取った構造体配列に代入する事により値を返却する。
    
    
    改修イメージは以下のようになります。
    
     改修前
        TEST_API int __stdcall FuncTest(LPSAFEARRAY *Type1)
          {
            int nRet=0;
            nRet = FuncTestSub(Type1);
            return nRet;
          }
    
     改修後
        //A
        TEST_API int __stdcall FuncTest(LPSAFEARRAY *Type1, int AryUBound)
          {
            //B
            typedef struct {
              int lMemoryHandle;
              int lMemoryAddress;
              int lSemaphoreHandel;
            } T_Struct;
    
            //B
            T_Struct aryT_Struct[AryUBound];
    
            //C
            int nRet=0;
            nRet = FuncTestSub(aryT_Struct);
    
            //D
            Type1 = aryT_Struct
    
            return nRet;
          }
    
    
    現状では、とりあえずVCのデバッグ実行にて値が格納されるか確認しようとしたところ、下記のようなエラーが発生しました。
    
    「保護されているメモリに読み取りまたは書き込み操作を行おうとしました。他のメモリが壊れていることが考えられます。」
    
    構造体配列の宣言または渡し方が悪いのか分かりません。
    
    
    また、Dの代入する際の注意点等あればお教え頂ければと思います。
    
    
    以上、宜しくお願い致します。
    
記事No.75306 のレス /過去ログ127より / 関連記事表示
削除チェック/

■75355  Re[7]: VB.netからVC++6.0のへの構造体配列参照渡し
□投稿者/ 774RR -(2015/03/18(Wed) 14:35:51)
    えっと、提示 C コードは全面的にどうにもならない感じなんだけどどこまで解説がいるのかな?

    2.は文字で書いている仕様とは意味が違う
    3.は自動変数だからこの関数終了時に消失する
    5.はポインタのコピーなので配列のコピーぢゃない
    ということで、現在のコードはまるっきり意味が無い感じ。

    VB (というかマネージ側) でどういう marshal をしたいのかが(少なくとも俺には)わからないのでまずはそこから。
    仕様を先に決めて、実装はそれに合致するようにするもんだ。

    C/C++ 使ったこと無いんだよね・・・
    とりあえず http://www.pinvoke.net の何か適当な Win32API の関数でわかるものある?
    http://www.pinvoke.net/default.aspx/hid.HidD_GetAttributes
    とか読める?
記事No.75306 のレス /過去ログ127より / 関連記事表示
削除チェック/

■75356  Re[7]: VB.netからVC++6.0のへの構造体配列参照渡し
□投稿者/ 魔界の仮面弁士 -(2015/03/18(Wed) 15:16:21)
    No75354 (とら さん) に返信
    > 今回の問題点を解決するにあたっての方針を以下のようにするつもりです。
    C++ 側の実装に自信が無いのなら、元の DLL はそのままにしておいて、
    その DLL を呼ぶための中継役として、VB6 で ActiveX DLL を作っておき、
    それを VB2008 側で参照設定するという逃げ方とか。(泣)

    構造体のままだとマーシャリングに不安がありそうなので、
    中継段階でクラスモジュールに詰め替えるとか。

    まぁ、これは冗長的過ぎるので、元の DLL を変更して良いのなら、そっちの方が早そう。


    > (ただし、当方VC++というかC++系はほとんど触れた事がありません、ポインタ等も概念程度を僅かに知っている程度です)
    同じく。



    > TEST_API int __stdcall FuncTest(LPSAFEARRAY *Type1, int AryUBound)
    セーフ配列の受け渡しでトラブっているですから、
    LPSAFEARRAY 型のままにしておいたら意味が無いですよ。


    > VC++にて構造体配列の先頭データ1つと配列の要素数を受け取るように変更する。
    こんな実装かな。

     TEST_API int __stdcall Test1(T_Struct*type1, int length)
記事No.75306 のレス /過去ログ127より / 関連記事表示
削除チェック/

■75357  Re[8]: VB.netからVC++6.0のへの構造体配列参照渡し
□投稿者/ とら -(2015/03/18(Wed) 16:21:20)
    No75355 (774RR さん) に返信
    > えっと、提示 C コードは全面的にどうにもならない感じなんだけどどこまで解説がいるのかな?
    > ということで、現在のコードはまるっきり意味が無い感じ。

    紛らわしくて誠に申し訳御座いません、提示したソースはあくまで「改修【イメージ】」のつもりでした。
    前述した通り、当方にはVC++に関する知識が殆ど御座いませんので、VC++のお勉強をして下さいというご指摘でしたら
    大変申し上げ難いのですが、今回はあまり時間的な余裕が御座いませんのでまた別の機会にしたく考えております。


    > VB (というかマネージ側) でどういう marshal をしたいのかが(少なくとも俺には)わからないのでまずはそこから。
    > 仕様を先に決めて、実装はそれに合致するようにするもんだ。

    これまでのやり取りには目を通されてますか?過去のやりとりを見てそれでも解らない、または情報がばらばらで読み難い
    ということであれば再度まとめ直させて頂きます。
    それとも毎回きっちりまとめ直した方が宜しいのですか?こちらの掲示板はそういった方針なのでしょうか。
    如何せんこのような掲示板にて質問するのは初なもので、色々と不慣れでご迷惑をお掛けしてしまい誠に申し訳御座いません。


    > C/C++ 使ったこと無いんだよね・・・
    > とりあえず http://www.pinvoke.net の何か適当な Win32API の関数でわかるものある?
    > http://www.pinvoke.net/default.aspx/hid.HidD_GetAttributes
    > とか読める?

    何を以て読めるという判断になるか解りませんが、構造体の宣言を行っている例なのかなと
    当方では認識しましたが如何でしょうか。




    No75356 (魔界の仮面弁士 さん) に返信
    > C++ 側の実装に自信が無いのなら、元の DLL はそのままにしておいて、
    > その DLL を呼ぶための中継役として、VB6 で ActiveX DLL を作っておき、
    > それを VB2008 側で参照設定するという逃げ方とか。(泣)

    当方の知識的にはその方法が一番良いのですが、VB6.0を残すのはだめだそうです。


    >>TEST_API int __stdcall FuncTest(LPSAFEARRAY *Type1, int AryUBound)
    > セーフ配列の受け渡しでトラブっているですから、
    > LPSAFEARRAY 型のままにしておいたら意味が無いですよ。

    >>VC++にて構造体配列の先頭データ1つと配列の要素数を受け取るように変更する。
    >  TEST_API int __stdcall Test1(T_Struct*type1, int length)

    なるほど、配列の先頭だけというのは要素数1の配列を受け取らせるのではなく、
    1つの構造体として受け取らせるという事なのですね。

    VB側は配列の先頭である「構造体配列(0)」と「構造体配列の要素数」を渡し、
    VC++側は「構造体」と「構造体配列の要素数」を受け取ってから、構造体配列を作成して
    作成した構造体配列の先頭のポインタ?に対して受け取った構造体のポインタ?を設定する
    感じになるのですかね……。
    イメージは解るのですが手法がさっぱり……、まぁイメージが間違ってる可能性も多々ありますが。


    なお、VC++側が構造体配列を受け取った後に次の関数へ渡していたのですが、その受け先はvoid型となっていました。
    TestSub_API int __stdcall FuncTestSub (void *Type1);


    VC++側において、今のところも書き方が良く解っておらず難航しております。
    解決に向けたアドバイスが頂ければとても助かります。
記事No.75306 のレス /過去ログ127より / 関連記事表示
削除チェック/

■75359  Re[9]: VB.netからVC++6.0のへの構造体配列参照渡し
□投稿者/ 魔界の仮面弁士 -(2015/03/18(Wed) 17:38:11)
    2015/03/18(Wed) 17:50:38 編集(投稿者)

    No75357 (とら さん) に返信
    > 前述した通り、当方にはVC++に関する知識が殆ど御座いませんので、VC++のお勉強をして下さいというご指摘でしたら
    > 大変申し上げ難いのですが、今回はあまり時間的な余裕が御座いませんのでまた別の機会にしたく考えております。

    DLL の改修を行うのはとらさんでしょうか?
    それとも専任者がいるのでしょうか?

    セーフ配列を C 形式配列に置き換える作業を、
    VC++の知識無しで行うのは無理があるかと…。



    > 1つの構造体として受け取らせるという事なのですね。

    C 配列なので、配列先頭要素のポインタを渡すというだけで。
    1 つの構造体として渡す、という表現だと語弊があるかな…。


    そもそも 1 つだけならば、 配列である必要性も無いので、
    T_Struct* ではなく T_Struct でも渡せますしね。
    (DLL 側で構造体の値を書き換える必要がある場合は別ですが)

    VB6 の場合はユーザー定義型を値渡しできないため、Declare の段階で
    構造体メンバーのスタックを展開して引数化する必要がありましたが、
    VB2008 はそうした制限が無いので、そのまま ByVal x As T_Struct で渡せます。

    WindowFromPoint API などがこれにあたります。
     HWND WindowFromPoint( POINT Point );
    上記は、POINT 構造体(≠POINT 構造体のポインタ) を受け取るものですが、
    VB6 では構造体を ByVal で渡せないため、「ByVal x As Long, ByVal y As Long」
    と書き換える必要がありましたが、VB2008 では「ByVal x As POINT」と書けます。



    話を戻して:

    セーフ配列やマネージ配列の場合は、配列自体が要素数情報を保持していますが、
    C 配列はただのポインタなので、サイズ情報がありません。
    要素数が必要になるのはそのためです。
    配列と共に、その配列の要素数を表す引数を併用する場合は、
    MarshalAs 属性の SizeParamIndex で指定してください。

    ただし、常に要素数が固定という仕様なのであれば、
    配列要素数をわざわざ指定しなくても良いでしょう。


    > VC++側は「構造体」と「構造体配列の要素数」を受け取ってから、
    それだと、配列を受け取れませんよね。

    VC++側は構造体ではなく、構造体のポインタを受け取るようにします。
    VB2008 側は「<[In](), Out()> ByVal x() As 構造体, ByVal length As Integer」という感じですね。


    > VB側は配列の先頭である「構造体配列(0)」と「構造体配列の要素数」を渡し、
    「構造体配列(0)」ではなく、配列そのものを渡して下さい。

    「構造体配列(0) のアドレスを値渡し」したり
    「構造体配列(0) を参照渡し」する手法が通じるのは、
    あくまでも VB6 の場合です。

    VB2008 の場合、マネージ配列のメモリ位置が動的に変更される可能性があるため、
    先頭要素だけ渡す方法は安全ではありません。もし、そのような渡し方をする必要が
    あるのなら、GCHandle.AddrOfPinnedObject でピン止めする必要があります。


    > なお、VC++側が構造体配列を受け取った後に次の関数へ渡していたのですが、その受け先はvoid型となっていました。
    > TestSub_API int __stdcall FuncTestSub (void *Type1);
    おぉ、ブラックボックス…!

    DLL 側で、C 配列をセーフ配列に事前に詰め替えれば、上記を呼び出すことはできるでしょう。
    問題は、上記が Type1 引数の情報を読み取るだけでなく、変更していた場合の書き戻し処理です。

    構造体の値を書き換えるぐらいなら、さほど問題はありません。
    呼び出し時に OutAttribute をつけておく程度で良いと思います。


    問題は、配列サイズの変更がありえる場合です。

    セーフ配列と異なり、C 配列だと DLL 側で配列サイズを
    動的に増やすといったことはできないですから。


    要素数を表す引数については、int length ではなく int* length に
    するだけで良いと思いますが(この場合は、VB 側も ByRef Integer になる)、
    問題は配列本体の方。
    もしもサイズの動的変更がありえるのでれば、呼び出し元(VB2008 側)にて、
    あらかじめ十分な要素数を確保しておき、その中でやりくりするような
    DLL 実装に書き換えるなど、少々面倒な実装になりそうです。
記事No.75306 のレス /過去ログ127より / 関連記事表示
削除チェック/

■75358  Re[9]: VB.netからVC++6.0のへの構造体配列参照渡し
□投稿者/ 774RR -(2015/03/18(Wed) 17:10:07)
    俺は、今までの発言の中に SAFEARRAY の実体を取得しているのがどこかは書かれていないと読んでいる。
    現在既にある VB6 プログラムの動作上、通信バッファに相当する SAFEARRAY の実体を
    1. VB6 EXE 側が確保して VC DLL に渡し、用済み後 VB6 EXE が処分している
    2. VC DLL が取得・処分処理をカプセル化していて (malloc/free 相当処理を VC DLL 側で行っていて)
    VB6 EXE 側はメモリの取得・処分の詳細を知らなくてすむようになっている
    どちらだろう。

    1. なら通信バッファは VB.NET 側で取得した manage memory を使うことになり、
     単純に VC DLL に向けて marshal すればいいので簡単だろう。
    2. なら通信バッファは VC DLL 側で取得した unmanage memory を使うことになり
     VB.NET 側で unmanage memory を使う細工が必要で面倒と思われる。

    どう marshal したいかってのはこういう意味だし 1 2 で実装は異なってくる。
記事No.75306 のレス /過去ログ127より / 関連記事表示
削除チェック/

■75362  Re[9]: VB.netからVC++6.0のへの構造体配列
□投稿者/ Azulean -(2015/03/18(Wed) 22:14:29)
    2015/03/19(Thu) 07:19:40 編集(投稿者)

    No75357 (とら さん) に返信
    > 前述した通り、当方にはVC++に関する知識が殆ど御座いませんので、VC++のお勉強をして下さいというご指摘でしたら
    > 大変申し上げ難いのですが、今回はあまり時間的な余裕が御座いませんのでまた別の機会にしたく考えております。

    ビジネスですか?趣味ですか?学術的なものですか?
    趣味や研究、学問の範囲であればその判断に異議を唱えるつもりはありません。
    しかし、ビジネスであるのであれば、再考を提案します。


    C/C++ は VB6/VB.NET に比べるとかなり難しい領域です。
    いかようにも書けてしまうため、サンプルコード単独では正しくとも、周囲のソースコードと組み合わせたらまずいケースもあり得ます。
    ソースコードを理解し、適切な対応をとるための準備ができないのであれば、C/C++ を修正するという考えは捨てた方がいいと断言できるぐらいです。


    私が選択肢を考えるとすれば…

    (1)C/C++ 経験のある熟練した開発者を助っ人に頼む
    (2)リスクを説明し、「VB6 を捨てる」という方針を緩和してもらい、問題の部分だけ VB6 でラップする。
    (3)リスクを説明し、十分な学習時間・検証時間をとる
    (4)リスクを説明し、リーダーや上長に責任を持ってもらう(責任転嫁)

    でしょうか。

    「第三者がそんな強権的な発言するな」と思われるかもしれません。

    ソフトウェア開発業界で仕事している立場からすると、今のまま進めたら、後で悲惨な未来が待っているかもしれないことを危惧しております。
    納期ばかり気にするのではなく、修正したソースコードで不具合が出たら誰が責任を負うのか、提供先で不具合が出たときの損害額はいかほどか、そのとき自分はどんな立場に立たされるのか、責任追及されるのか、それらの発生確率・影響をご考慮いただければ幸いです。


    ------

    > TestSub_API int __stdcall FuncTestSub (void *Type1);

    SafeArrayAccessData で得た構造体配列の先頭ポインタを渡しているだけであれば、SafeArray の要素数が変わることはないと思っています。
    予想できる振る舞いとしては、内容の書き換えぐらいまでですかね。

    いずれにせよ、どのように修正するべきかは、FuncTestSub の実装とその呼び出し側のロジック次第です。
    全体を見通せない第三者では、正解を示せない可能性があることをご了承ください。

    (手元にソースコードがあっても、レガシーコードを触るのに十数時間から数十時間の調査・検討を重ねてやっと修正できるようなことも現実的にある世界です。。。)

    ----
    以下推測(まったく保障のない、私の勝手な予想に過ぎません)

    "TestSub_API int __stdcall FuncTestSub (void *Type1)" なら、渡した先で構造体のポインタにキャストしていると予想される。
    呼び出し元で事前にキャストしていないことから、先頭ポインタを渡すことを前提としているはず。
    であれば、要素数を渡さないこの関数のシグネチャからは要素数は一定以上、事実上固定のサイズと想定していそう。
    そうであれば、固定長の C 形式配列を渡すように関数を書き換え、不要となった SafeArray 系のコードを整理し、P/Invoke 側も書き換えればいけそうではある。
    予想もつかない突飛な実装をしていない限り…。
記事No.75306 のレス /過去ログ127より / 関連記事表示
削除チェック/

■75379  Re[10]: VB.netからVC++6.0のへの構造体配列
□投稿者/ とら -(2015/03/19(Thu) 12:46:31)
    私の説明不足により皆様にご心配及びご迷惑をお掛けした事をお詫び申し上げます。


    No75358 (774RR さん) に返信

    > 俺は、今までの発言の中に SAFEARRAY の実体を取得しているのがどこかは書かれていないと読んでいる。
    > 現在既にある VB6 プログラムの動作上、通信バッファに相当する SAFEARRAY の実体を
    > 1. VB6 EXE 側が確保して VC DLL に渡し、用済み後 VB6 EXE が処分している
    > 2. VC DLL が取得・処分処理をカプセル化していて (malloc/free 相当処理を VC DLL 側で行っていて)
    > VB6 EXE 側はメモリの取得・処分の詳細を知らなくてすむようになっている
    > どちらだろう。

    VB側のEXEにてPublicとして配列を宣言しており、VBのEXE終了時に開放しております。
    なのでこのケースですと1という事になります。

    > 1. なら通信バッファは VB.NET 側で取得した manage memory を使うことになり、
    >  単純に VC DLL に向けて marshal すればいいので簡単だろう。

    マーシャリングに関しては魔界の仮面弁士さんにご教授頂いた事も含めて試してみたのですが、
    私の力不足により良い結果は得られませんでした。



    No75359 (魔界の仮面弁士 さん) に返信
    > DLL の改修を行うのはとらさんでしょうか?
    > それとも専任者がいるのでしょうか?
    VC++の改修も私の方で行う予定です(本来はそもそもVC++側の改修予定はなかったのですが)

    > セーフ配列を C 形式配列に置き換える作業を、
    > VC++の知識無しで行うのは無理があるかと…。

    やはり難しいでしょうか。



    > C 配列なので、配列先頭要素のポインタを渡すというだけで。
    > 1 つの構造体として渡す、という表現だと語弊があるかな…。

    これに関しましてですが「セーフ配列を使わず、配列先頭ポインタと配列長を渡す実装に変更して回避するケース、」
    という事を行おうとして先のように実装しようとしたのですが、根本的に勘違いしていたようですか?

    色々と調べていたところ、配列の先頭だけを渡して云々といったのがあったので、あのような形になるのかと思いました。


    > セーフ配列やマネージ配列の場合は、配列自体が要素数情報を保持していますが、
    > C 配列はただのポインタなので、サイズ情報がありません。
    > ただし、常に要素数が固定という仕様なのであれば、
    > 配列要素数をわざわざ指定しなくても良いでしょう。



    >>VC++側は「構造体」と「構造体配列の要素数」を受け取ってから、
    > それだと、配列を受け取れませんよね。
    > VC++側は構造体ではなく、構造体のポインタを受け取るようにします。
    > VB2008 側は「<[In](), Out()> ByVal x() As 構造体, ByVal length As Integer」という感じですね。

    > 構造体の値を書き換えるぐらいなら、さほど問題はありません。
    > 呼び出し時に OutAttribute をつけておく程度で良いと思います。

    > 構造体の値を書き換えるぐらいなら、さほど問題はありません。
    > 呼び出し時に OutAttribute をつけておく程度で良いと思います。

    > 問題は、配列サイズの変更がありえる場合です。

    今回は配列の要素数が固定なので、要素数は渡さず、書き換えも発生するので以下の形にすれば良いのでしょうか。
    Declare Function Test1 Lib "TestFunc.DLL" (<InAttribute(), OutAttribute()> ByRef oStructure() As Structure1) As Integer

    配列の宣言は
    Public udtShareMemory(9) As Structure1


    呼ぶ際は
    iRet = Test1(aryStructure1)

    なお、構造体の宣言は以下のようにしています。
    Public Structure Structure1
    <MarshalAs(UnmanagedType.I4)> Public aaa As Integer
    <MarshalAs(UnmanagedType.I4)> Public bbb As Integer
    <MarshalAs(UnmanagedType.I4)> Public ccc As Integer
    End Structure


    VC++側は下記のようにしてみました。

    typedef struct {
    int aaa;
    int bbb;
    int ccc;
    } Struct1;


    TestFunc_API int __stdcall Test1(Struct1 *aryStruct[])

    nRet = FuncTestSub(aryStruct);


    TestSub_API int __stdcall FuncTestSub (void *Type1);


    しかしVC++側のソースが追えない部分にて落ちている様子

    Expression: (L"Buffer is too Smaall" && 0)


    うむむ……、VC++側の受け取り方を根本的に勘違いしているのでしょうか。



    No75362 (Azulean さん) に返信

    ご指摘はごもっともです。
    元々の予定ではVC++側に手を入れる予定はなく、VB側だけで何とかしようとしていたのですが、私の力不足により実装が難しく
    VC++側に手を入れる方向で再考致しましたが、その時点でVC++に精通した方に依頼すべきではありますね。

    しかし、まだ実際に可能かどうかも解っておらず、最低限の技術検証を行ってから実装したいという思いもあるので、
    予定の変更やVC++の技術者への依頼もまだ決めかねている状態です。


    >>TestSub_API int __stdcall FuncTestSub (void *Type1);
    > SafeArrayAccessData で得た構造体配列の先頭ポインタを渡しているだけであれば、SafeArray の要素数が変わることはないと思っています。
    > 予想できる振る舞いとしては、内容の書き換えぐらいまでですかね。
    > "TestSub_API int __stdcall FuncTestSub (void *Type1)" なら、渡した先で構造体のポインタにキャストしていると予想される。
    > 呼び出し元で事前にキャストしていないことから、先頭ポインタを渡すことを前提としているはず。
    > であれば、要素数を渡さないこの関数のシグネチャからは要素数は一定以上、事実上固定のサイズと想定していそう。

    要素数は固定で内容の書き換えは行っています。
記事No.75306 のレス /過去ログ127より / 関連記事表示
削除チェック/

■75637  VB2013からVC++6.0に構造体配列参照渡し【別件】
□投稿者/ とら -(2015/04/16(Thu) 19:56:33)

    分類:[.NET 全般] 

    再度お世話になります。

    @VB.netからVC++6.0のへの構造体配列参照渡し
    http://bbs.wankuma.com/index.cgi?mode=al2&namber=75306

    AVB.net⇒VC++6.0のVoid型に構造体配列参照渡
    http://bbs.wankuma.com/index.cgi?mode=al2&namber=75559

    にて質問した内容に近いのですが、またはまってしまったのでお知恵を拝借出来れば助かります。

    なお、AのVoid型に〜の時に同様の対応を行おうと致しましたが、あの時は結局VC側にてvoid型を
    受け取る構造体の型に変更し解決致しましたので、今回求めている解決法は未解決のままです。


    現状と致しましては、@の時のようにSafeArrayに対して構造体配列を受け渡ししたいのですが、
    前回と異なり数値だけではなく文字列変数が混ざっています。

    VC側はSafeArrayで受け取った後、同じような構造体にキャストして使用しているようですが、
    その際の文字列変数はBSTRで宣言してあり、数値変数はintでした。

    マーシャリングも色々と追加してみたのですが、どうにもうまくいかないので、お手数ですが
    お教え頂けないでしょうか。

    例によってVCへの変更、及びラッパー用DLL等の作成は最終手段とし、極力VB2013のみで
    完結したく考えております。


    以上、宜しくお願い致します。


    ## VC++6.0 ###########

    Test1_API int __stdcall Test1(LPSAFEARRAY *Data1, LPSAFEARRAY *Data2)


    ## VB6.0 #############

    -- 宣言部 ----

    Declare Function Test1 Lib "Test.DLL" (ByRef oType1() As Any, ByRef oType1() As Any) As Long

    Public Type Type1
    Str1 As String
    Str2 As String
    Lng1 As Long
    End Type

    Public Type Type2
    Str3 As String
    Lng2 As Long
    Lng3 As Long
    End Type

    Public oType1(0 To 9) As Type1
    Public oType2(0 To 9) As Type2


    -- 処理 ----

    Dim iRet As Long
    iRet = Test1(oType1(), oType2())


    ## VB2013 #############

    -- 宣言部 ----

    Declare Function Test1 Lib "Test.DLL" (<MarshalAs(UnmanagedType.SafeArray, SafeArraySubType:=VarEnum.VT_RECORD)>
    ByVal oStructure() As Structure1
    <MarshalAs(UnmanagedType.SafeArray, SafeArraySubType:=VarEnum.VT_RECORD)>
    ByVal oStructure() As Structure2) As Integer


    <ComVisible(True), Guid("91e3f2e5-341a-49bc-9f48-8637526c2833")>
    Public Structure Structure1
    <MarshalAs(UnmanagedType.BStr)> Dim Str1 As String
    <MarshalAs(UnmanagedType.BStr)> Dim Str2 As String
    Dim Int1 As Integer
    End Structure

    <ComVisible(True), Guid("e6b3817c-4342-4335-985f-5b8a0df41881")>
    Public Structure Structure2
    <MarshalAs(UnmanagedType.BStr)> Dim Str3 As String
    Dim Int2 As Integer
    Dim Int3 As Integer
    End Structure

    Public oStructure1(9) As Structure1
    Public oStructure2(9) As Structure2


    -- 処理 ----

    Dim iRet As Integer = -1
    iRet = Test1(oStructure1, oStructure2)

親記事 /過去ログ127より / 関連記事表示
削除チェック/

■82299  Re[1]:
□投稿者/ Azulean -(2016/12/31(Sat) 18:08:26)
    2016/12/31(Sat) 18:10:51 編集(投稿者)

    No82298 (あああ さん) に返信
    > sub test(ByVal frm As Object)
    > frm.DataGridView1 = (コードが書かれている)
    > end sub
    (略)
    > Form1では実行できるのですが
    > Form2で実行する時に
    > 公開メンバーDataGridBiew1は型Form2に見つかりませんでした
    > というエラーが出てしまいます。
    >
    > どうすれば良いでしょうか?

    逆に問いたいのですが、どのようにしたいのでしょうか?

    「Form1 には存在する DataGridView1 に対する操作」を「Form2 でも使い回したい」のであれば、「Form2 にも DataGridView1 を置く」しかありません。
    しかし、この質問に至ると言うことは、「Form2 には DataGridView1 を置いていない」ようですが、それはなぜですか?

    「Form2 には DataGridView1 を置かず」、「二つのフォームで一つのモジュール内のsubを共有して使う」ということは、どういったことを実現したいのか、伝わってきません。

    少なくとも、DataGridView1 と Form1、Form2 の役割をきちんと他人が理解できる形で説明できないと、Web ベースでの解決は難しいと思います。
    他者がイメージできるようにもっと説明を付け足してください。
記事No.82298 のレス /過去ログ140より / 関連記事表示
削除チェック/

<前の20件

<< 0 | 1 >>

パスワード/

- Child Tree -