none
APIが動作してくれません RRS feed

  • 質問

  • 初めてAPIを使用しているのですが、思うように動作してくれません。
    問題が発生しているのは”GetLogicalDriveStrings”という関数を使用した以下の部分です。

     

        Private Declare Function GetLogicalDriveStrings Lib "kernel32" _
        Alias "GetLogicalDriveStringsA" _
        (ByVal nBufferLength As Long, ByVal lpBuffer As String) As Long

        Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
            Dim sbuffer As String
            Dim result As Boolean
            sbuffer = Space(105)
            result = GetLogicalDriveStrings(Len(sbuffer), sbuffer)
        End Sub

     

    API関数を使用している行で「保護されているメモリに読み取りまたは書き込み操作を行おうとしました。

    他のメモリが壊れていることが考えられます。」というエラーになってしまいます。

    引数に設定しているバッファメモリーのポインターに問題があるのかと思い、いろいろ変更して試してみたのですが、

    解決できません。

    APIに詳しい方、アドバイス頂けたら助かります。

    2008年6月5日 12:23

回答

  • vb6用の宣言を持ってこられましたね。

    vb6のLongは、netでは、Integerです。

     

    2008年6月5日 13:03
  • As LongがAs Integerであることの他に、As Stringがまずいと思います。

    .NETではStringは固定のもので、中身をAPIに渡して書き換えるといったことはできません。

     

    pinvoke.netを参照すると、char配列にしてはどうかという記述もあるので参考にして下さい。

     

    http://www.pinvoke.net/default.aspx/kernel32/GetLogicalDriveStrings.html?diff=y

     

     

    C#でのコードサンプル

    Code Snippet

    static extern int GetLogicalDriveStrings(int nBufferLength, [Out] char[] lpBuffer);
    static void Main()
    {
      int nBufferLength = 255;
      char[] lpBuffer = new char[nBufferLength];
      GetLogicalDriveStrings(nBufferLength, lpBuffer);
    }

     

     

    http://labs.developerfusion.co.uk/convert/csharp-to-vb.aspx で変換して参考にしてみて下さい。

    ※動作が正しいかどうか、構文として適切かどうかについては評価していません。

    2008年6月5日 13:39
    モデレータ
  • 私がやるかぎり、Stringで書き換えてくれますよ。

     

    以下のコードでもできました。

    Code Snippet

    Private Declare Function GetLogicalDriveStrings Lib "kernel32" _
            Alias "GetLogicalDriveStringsA" _
            (ByVal nBufferLength As Integer, ByVal lpBuffer As IntPtr) As Long

        Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
            Dim sbuffer As String
            Dim result As Boolean
            sbuffer = Space(105)

            Dim p As IntPtr
            p = System.Runtime.InteropServices.Marshal.StringToHGlobalAnsi(sbuffer)
            result = GetLogicalDriveStrings(Len(sbuffer), p)
            sbuffer = System.Runtime.InteropServices.Marshal.PtrToStringAnsi(p)
            System.Runtime.InteropServices.Marshal.FreeHGlobal(p)


            MsgBox(sbuffer)
           
        End Sub

     

     

    まぁ、私は所詮VBプログラマーなので、つっこみがあったらどうぞ。(^^;
    2008年6月5日 14:07
  •  はなはなはな さんからの引用

    私がやるかぎり、Stringで書き換えてくれますよ。

    確かにStringでも書き換わるようです。確認不足ですみません。

     

     はなはなはな さんからの引用

    以下のコードでもできました。

    この例はまずいです。

    なぜなら、PtrToStringAnsiメソッドが最初のnull文字で区切ってしまって、2番目以降の要素を得られないからです。

     

    また、Space(105)から、StringToHGlobalAnsiにするくらいであれば、最初からAllocHGlobalを使った方が良いのではないかと思います。

    2008年6月5日 15:03
    モデレータ
  • う、だめだったですか。nullで区切ってしまうのですね。

    確かにC:\しかとりませんでした。

     

    そういう意味ではStringのばあいもC:\しかとってないけど、

    やはり、Azuleanさんの方法でないとだめですかね?

     

    ちょっと試してみます。

     

    2008年6月5日 22:54

すべての返信

  • vb6用の宣言を持ってこられましたね。

    vb6のLongは、netでは、Integerです。

     

    2008年6月5日 13:03
  • As LongがAs Integerであることの他に、As Stringがまずいと思います。

    .NETではStringは固定のもので、中身をAPIに渡して書き換えるといったことはできません。

     

    pinvoke.netを参照すると、char配列にしてはどうかという記述もあるので参考にして下さい。

     

    http://www.pinvoke.net/default.aspx/kernel32/GetLogicalDriveStrings.html?diff=y

     

     

    C#でのコードサンプル

    Code Snippet

    static extern int GetLogicalDriveStrings(int nBufferLength, [Out] char[] lpBuffer);
    static void Main()
    {
      int nBufferLength = 255;
      char[] lpBuffer = new char[nBufferLength];
      GetLogicalDriveStrings(nBufferLength, lpBuffer);
    }

     

     

    http://labs.developerfusion.co.uk/convert/csharp-to-vb.aspx で変換して参考にしてみて下さい。

    ※動作が正しいかどうか、構文として適切かどうかについては評価していません。

    2008年6月5日 13:39
    モデレータ
  • 私がやるかぎり、Stringで書き換えてくれますよ。

     

    以下のコードでもできました。

    Code Snippet

    Private Declare Function GetLogicalDriveStrings Lib "kernel32" _
            Alias "GetLogicalDriveStringsA" _
            (ByVal nBufferLength As Integer, ByVal lpBuffer As IntPtr) As Long

        Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
            Dim sbuffer As String
            Dim result As Boolean
            sbuffer = Space(105)

            Dim p As IntPtr
            p = System.Runtime.InteropServices.Marshal.StringToHGlobalAnsi(sbuffer)
            result = GetLogicalDriveStrings(Len(sbuffer), p)
            sbuffer = System.Runtime.InteropServices.Marshal.PtrToStringAnsi(p)
            System.Runtime.InteropServices.Marshal.FreeHGlobal(p)


            MsgBox(sbuffer)
           
        End Sub

     

     

    まぁ、私は所詮VBプログラマーなので、つっこみがあったらどうぞ。(^^;
    2008年6月5日 14:07
  •  はなはなはな さんからの引用

    私がやるかぎり、Stringで書き換えてくれますよ。

    確かにStringでも書き換わるようです。確認不足ですみません。

     

     はなはなはな さんからの引用

    以下のコードでもできました。

    この例はまずいです。

    なぜなら、PtrToStringAnsiメソッドが最初のnull文字で区切ってしまって、2番目以降の要素を得られないからです。

     

    また、Space(105)から、StringToHGlobalAnsiにするくらいであれば、最初からAllocHGlobalを使った方が良いのではないかと思います。

    2008年6月5日 15:03
    モデレータ
  • う、だめだったですか。nullで区切ってしまうのですね。

    確かにC:\しかとりませんでした。

     

    そういう意味ではStringのばあいもC:\しかとってないけど、

    やはり、Azuleanさんの方法でないとだめですかね?

     

    ちょっと試してみます。

     

    2008年6月5日 22:54
  • はなはなはなさん、Azuleanさん 返信頂きありがとうございました。

    コメントを順に確認して、何とか私のPCでも「C:\」まで表示するようになりました。

    やはり、sbufferのアドレスを渡す方法に問題があったようですね。

     

    2008年6月6日 0:45
  • 配列で受け取る場合 マーシャリングをANSIにすると Byte配列にしないとうまく受け取れないようです

     

    Code Snippet

        Private Declare Function GetLogicalDriveStrings2 Lib "kernel32" _
            Alias "GetLogicalDriveStringsA" _
            (ByVal nBufferLength As Integer, ByVal lpBuffer() As Byte) As Integer


        Private Declare Auto Function GetLogicalDriveStrings Lib "kernel32" _
            (ByVal nLength As Integer, ByVal lpBuuf() As Char) As Integer

     

     

    Autoにするか AliasでUnicode版を指定すればChar配列で受けと取れます

     

    Code Snippet

    dim nLen as Integer

    dim ch(256) as Char

    dim sDrives() as string, sb as new StringBuilder

    nLen = GetLogicalDriveStrings( ch.Length, ch )

    for n as integer = 0 to nLen

        if ch(n) = nothing then ch(n) = ";"

    next

    sb.Appned( ch, 0, nLen )

    sDrives = sb.TOString.Split( ";".ToCharArry, System.StringSplitOptions.RemoveEmptyEntries )

     

     

    といった具合で sDrivesに 『C:\』 『D:\』 などを取得出来ます

     

    Ansiでやるなら

    Code Snippet

    Dim bb(256) as Byte

    dim ch(256) as Char

    dim nLen as Integer

    dim sb as new System.Text.StringBulder, sDrive() as string

    Dim dd as System.Text.Decorder = System.Text.Defualt.GetDecorder

    nLen = GetLogicalDriveString2( bb.Length, bb )

    dd.GetChars( bb, 0, nLen, ch, 0 )

    for n as Integer = 0 to nLen

        if ch(n) = nothing then ch()n) = ";"

    next

    sb.Appned( ch, 0, nLen )

    sDrives = sb.TOString.Split( ";".ToCharArry, System.StringSplitOptions.RemoveEmptyEntries )

     

     

    といった具合です

    2008年6月6日 5:51
  • redfoxさんも久しぶりですねぇ。w

    charで受け取るのができなくて、もがいてました。

     

    結局nullを何かに置換してあげないといけないんですね。

    VBやってて、文字列のそういう意識はないからなぁ。
    2008年6月6日 7:35
  • redfox63さん、詳しいサンプルコードと解説ありがとうございました。

    おかげで全てのドライブ情報を取れるようになりました。助かりました。

     

    2008年6月6日 11:12