tagCANDY CGI VBレスキュー(花ちゃん)の Visual Basic 6.0用 掲示板 [ツリー表示へ]   [Home]
一括表示(VB6.0)
タイトルDLLへの文字列ポインタについて
記事No12289
投稿日: 2008/05/15(Thu) 22:17
投稿者cc
VB6.0 sp6からVC++6.0で作成したDLL内関数(以下のような仕様)へデータを渡したいです。
その中で、構造体内の文字列先頭ポインタの扱いがよく分からず困っております。

DLL内関数
Declare Function Func Lib "cmakelib" Alias "_DllFunc@4" (strct_dat As STRCT) As Long

STRCTは、独自の構造体でCでは

typedef struct {
    int         ldat;       // データ
    char        *lptr;      // ファイル名
} STRCT, *PSTRCT;

と定義されています。
Funcでは、lptrの参照のみするようです。(変更はしない)

Q1.VBでは
        Type STRCT
                ldat_A As Long       ' データA
                lptr As Long         ' ファイル名
                ldat_B As Long       ' データB
        End Type
    の様に定義するのでしょうか?
    それとも
        Type STRCT
                ldat_A As Long       ' データA
                lptr As String       ' ファイル名
                ldat_B As Long       ' データB
        End Type
        の様に定義するのでしょうか?

Q2.
    Longとして定義した場合、
        Dim strct_d As STRCT
        strct_d.ldat_A = 5           ' データA
        StrPtr(StrConv("test.txt", vbFromUnicode)) ' ファイル名
        strct_d.ldat_B = 6           ' データB
        ret = DllFunc(strct_d)
    の様に処理を書く事は、問題ないのでしょうか?

Q3−1.
    stringとして定義した場合、
    "String"と"char*"でサイズが異なり、
  データBが間違ってDLLへ伝わる事は無いですか?
    (API等でchar*を単独で渡す場合は、stringのByVal渡しで良いかと思いますが、
     ユーザ定義型の場合はどうなのでしょうか?)
Q3−2.
    DLL内でlptrへアクセスしたときに、正常に文字列を取得できるのでしょうか?

Q3−3.
    stringとして定義した場合は、
        Dim strct_d As STRCT
        strct_d.ldat_A = 5           ' データA
        strct_d.lptr = "test.txt"    ' ファイル名
        strct_d.ldat_B = 6           ' データB
        ret = DllFunc(strct_d)
    の様に処理を書く事は、問題ないのでしょうか?


以上、どなたかご存じの方いらっしゃいましたら、
アドバイス頂けますでしょうか。
よろしくお願いします。

[ツリー表示へ]
タイトルRe: DLLへの文字列ポインタについて
記事No12305
投稿日: 2008/05/17(Sat) 17:30
投稿者neptune
こんにちは

忘れているので思い出しがてら実験してみました。

' ///////////////VB側 標準モジュール////////////////
Public Type STRCT
    ldat_A As Long       ' データA
    lptr As String         ' ファイル名
End Type

Public Declare Function structTest Lib "DllSample.dll" _
        (ByVal handle As _
        Long, ByRef ptyp As STRCT) As Long

Public Sub StructTest1()
    Dim typBuf As STRCT
    Dim sFileName As String
    Dim ret As Long

    sFileName = "FileName"
    With typBuf
        .ldat_A = 10
        .lptr = sFileName
    End With
    ret = structTest(Form1.hWnd, typBuf)
    Debug.Print ret
End Sub


/*C側----------必要部分のみ抜粋--------- */
typedef struct {
    int         ldat;       // データ
    char        *lptr;      // ファイル名
} STRCT;

extern "C" __declspec(dllexport) int __stdcall structTest(HWND ,STRCT *);

EXPORT int __stdcall structTest(HWND hWnd,STRCT *lpstrct){
    int length;
    char *sbuf;
    char slen[10];

    length = strlen(lpstrct->lptr);  
    sbuf = lpstrct->lptr;
    itoa(length,slen,10);

    strcat(sbuf,"\n文字数は : ");
    strcat(sbuf,slen);
    MessageBox(hWnd,sbuf,"structText",MB_OK);
    return length;
}

以上の検証コードで文字列、文字数は正常に取得できました。

重要:とりあえず動きましたが、正直絶対これでOKと言うほどスキルがありません
  のでお断りしておきます。

たまに書かないと忘れますね。いい機会になりました。^ ^;;

> Q1.VBでは
>     それとも
>         Type STRCT
>                 ldat_A As Long       ' データA
>                 lptr As String       ' ファイル名
>                 ldat_B As Long       ' データB
>         End Type
>         の様に定義するのでしょうか?
↑ですね。

> Q2.
回答なし。

> Q3−1.
> Q3−2.
> Q3−3.
サンプルコード参照願います。

以下のMSDN(VS6用)参照願います。
[VB5] Visual Basic 5.0 から呼び出し可能な DLL の作成例
Microsoft Visual Basic Version 5.0 で使用する DLL の開発に関する注意

[ツリー表示へ]
タイトルRe^2: DLLへの文字列ポインタについて
記事No12309
投稿日: 2008/05/19(Mon) 15:13
投稿者neptune
こんにちは

もう見ないかもしれませんが、自分が間違った事を書いていたようなので記録として
訂正情報です。

http://hanatyan.sakura.ne.jp/logbbs1/wforum.cgi?mode=allread&no=5900&page=0

で、投稿時間:2006/02/25(Sat) 15:17 投稿者名:Blue

>まず、ユーザ定義型の中にString型メンバ変数を使うと、その方はCのほうでは
>BSTR型(wchar_t*型)に
>なります。
>通常のchar*ではないので、BSTR型文字列→char*型文字列の変換をしないといけません。
とあります。

従って、先の私のサンプルは偶然動いたのかもしれません。


ともあれ、C側の構造体の中で、文字列を使用する場合、BSTR型(wchar_t*型)として
おかなければならないと言う事です。

以前自分がBSTRに絡むスレに書き込んだ記憶があったので検索してみました。

[ツリー表示へ]
タイトルRe^3: DLLへの文字列ポインタについて
記事No12312
投稿日: 2008/05/19(Mon) 17:52
投稿者cc
neptuneさま

アドバイスありがとうございます。
netが使えない状況になってしまい、
お礼が遅くなり申し訳ございません。

早速確認してみます。
(linkも確認してみます。)

取り急ぎ、お礼のご返信をさせていただきます。
結果をまた投稿させて頂きます。

[ツリー表示へ]
タイトルRe^4: DLLへの文字列ポインタについて
記事No12317
投稿日: 2008/05/19(Mon) 21:40
投稿者cc
教えていただいた内容を
確認してみました。

事前実験で、
私もVBのStringを、DLLでchar *として扱う方法で
渡ってたので良いのかと思いました。
ただ、DLL側でデバッグし、
VB側から送った文字列の先頭アドレスを見ると、
Unicodeではなく、ANSI文字列が入っていました。
と言うことは、BSTRではなく、char *として扱ってもよいのかと考えていました。
Q1.自動的にUnicode→ANSIされるのでしょうか?


Q2.DLLの変更は避けたいのです。
となると、
    Type STRCT
            ldat_A As Long       ' データA
            lptr As Long         ' ファイル名
            ldat_B As Long       ' データB
    End Type
とし、
    Dim strct_d As STRCT
    strct_d.ldat_A = 5           ' データA
    StrPtr(StrConv("test.txt", vbFromUnicode)) ' ファイル名
    strct_d.ldat_B = 6           ' データB
    ret = DllFunc(strct_d)
の様に処理するのはNGなのでしょうか?


アドバイス頂けないでしょうか?
よろしくお願いします。

[ツリー表示へ]
タイトルRe^5: DLLへの文字列ポインタについて
記事No12318
投稿日: 2008/05/20(Tue) 11:18
投稿者neptune
こんにちは

ご覧になっていたんですね。^ ^;

> VB側から送った文字列の先頭アドレスを見ると、
> Unicodeではなく、ANSI文字列が入っていました。
これはそうなんですよね。

> Q1.自動的にUnicode→ANSIされるのでしょうか?
正直わかりません。

> Q2.DLLの変更は避けたいのです。
> となると、
>     Type STRCT
>             ldat_A As Long       ' データA
>             lptr As Long         ' ファイル名
>             ldat_B As Long       ' データB
>     End Type

>     Dim strct_d As STRCT
>     strct_d.ldat_A = 5           ' データA
>     StrPtr(StrConv("test.txt", vbFromUnicode)) ' ファイル名
>     strct_d.ldat_B = 6           ' データB
>     ret = DllFunc(strct_d)
> の様に処理するのはNGなのでしょうか?
DLL側の宣言をそのままにして↑ではできません。
これはUPしたサンプル検証の際確認しました。

で、本題ですが、先にも書きましたように、私は十分な説明が出来るほど
スキルがありません。(勉強してないんですが^ ^;)
以前に、VB用のDLLを作成した際、「BSTR およびC文字列の変換」
http://japan.internet.com/developer/20051004/27.html
を発見し、MSDNと↑を参考にしました。
ただ、そこには、
「複数言語のアプリケーション(Visual Basicアプリケーションからアクセスされる
C++ DLLなど)を書いている場合はBSTR/C文字列の変換が必要である。」
と書かれていますが、ccさんの書かれているように参照のみなら大丈夫なように気も
しますが、いかんせんVB用のDLL作成の際にはBSTRを使用するのが常道らしいです。
常道から外れるとろくな事ありませんからね。安全第一です。

申し訳ないですが、これ以上はお役に立てそうもありませんので、
識者の回答をお待ち下さい。

[ツリー表示へ]
タイトルRe^6: DLLへの文字列ポインタについて
記事No12326
投稿日: 2008/05/21(Wed) 09:20
投稿者cc
neptune様

度々のアドバイス
本当にありがとうございます。
そして、ご返事が遅く申し訳ございません。
大変参考になりました。
確かに、常道から外れるとろくな事はありませんね。

一点お聞きしたいのですが、
StrPtrを使った方法、私の実験ではDLLから参照可能でした。
neptune様の確認時、何か問題があったのでしょうか?
ちなみに、
> StrPtr(StrConv("test.txt", vbFromUnicode)) ' ファイル名
ではなく、
strct_d.lptr = StrPtr(StrConv("test.txt", vbFromUnicode)) ' ファイル名
ですね。
訂正します。

以上、アドバイス頂けないでしょうか。
よろしくお願いします。

[ツリー表示へ]
タイトルRe^7: DLLへの文字列ポインタについて
記事No12329
投稿日: 2008/05/21(Wed) 15:16
投稿者neptune
こんにちは
 
> 確かに、常道から外れるとろくな事はありませんね。
はい、自分で詳しく判断できないときはやはり常道というか安全側に
なってしまいます。^ ^;

> 一点お聞きしたいのですが、
> StrPtrを使った方法、私の実験ではDLLから参照可能でした。
> neptune様の確認時、何か問題があったのでしょうか?
> ちなみに、
> > StrPtr(StrConv("test.txt", vbFromUnicode)) ' ファイル名
> ではなく、
> strct_d.lptr = StrPtr(StrConv("test.txt", vbFromUnicode)) ' ファイル名
> ですね。
> 訂正します。
すみません。StrConvでvbFromUnicodeにするのを忘れていたような気がします。
書いたときはANSIに変換ってのをすっかり忘れていました。
コード自体はもう無いので、記憶ですが、
strct_d.lptr = StrPtr("test.txt")
のように書いていたと思います。

確認しましたが、ccさんの上記コードで動きました。
お騒がせしました。

以下独り言になるんですが、最初の自分の実験コードで再検証したんですが、
・・・なぜかしら、処理自体は正常に終わるんですが、VBを終了する際
異常終了しました。?DLLのつくりが悪いんだろうか、それとも文字列
の受け渡しが悪いんだろうか悩んだんですが、
もう一度ソースを見たら、
    sbuf = lpstrct->lptr;
    itoa(length,slen,10);
    strcat(sbuf,"\n文字数は : ");
    strcat(sbuf,slen);
と文字列操作をしています。多分このせいでメモリ操作がおかしくなったんでしょうね。
そこを以下のような直しました。

EXPORT int __stdcall structTest(HWND hWnd,STRCT *lpstrct){
    int length;
//    char *sbuf;
    char smsg[256];
    char slen[10];

    length = strlen(lpstrct->lptr);
//    sbuf = lpstrct->lptr;
    itoa(length,slen,10);
    strcpy(smsg,lpstrct->lptr);
    strcat(smsg,"\n文字数は : ");
    strcat(smsg,slen);
    MessageBox(hWnd,smsg,"structText",MB_OK);
    return length;
}
これで正常終了するようになりました。

やはり、参照は害の無いようにも思えますが、操作したら確実に駄目ですね。
やっぱり、BSTRが固い(面倒だけど)。

ちなみに修正前のDllでccさんのようにANSIに変換して、ポインタ渡しすると
正常終了しました。でもOKという確信は私のレベルではもてません。

ササッと作ったのは良いけど、元から初心者レベルなのにもう忘れている
自分が情けない。。。

長くなりました。

[ツリー表示へ]
タイトル【解決】
記事No12330
投稿日: 2008/05/21(Wed) 16:29
投稿者cc
neptune様

お答え頂きありがとうございます。

操作は確かに怖いですね。
Unicode→ANSIの部分も今ひとつ見えないので、
余計に怖いです。

参照だけなので、
BSTRは避けられるかと思っております。

長々とつきあって頂き、
非常に感謝しております。

とりあえず、解決とさせていただきます。

ありがとうございました。
今後も何かあった時は、
是非よろしくお願いします。

[ツリー表示へ]