DOBON.NET DOBON.NETプログラミング掲示板過去ログ

フォルダダイアログについて

環境/言語:[XP Frameworkのバージョン 1.1]
分類:[.NET]

こんにちわ。
SHBrowseForFolder APIを使用してフォルダ選択ダイアログを作っているのですが、下記コードを実行すると、
「System.EntryPointNotFoundException:DLL user32のSendMessageというエントリポイントが見つかりません。」という
エラーが発生した後、画面が固まってしまいます。
コードは非.NETのVBのサンプルコードを手直ししたのですが(.NETのサンプルが見つからなかった)、コールバック関数部分
がうまく動作していないのでしょうか。
また、System.Windows.Forms.FolderBrowserDialogを使わないのはネットワークフォルダのみの指定ができないためです。
目的の仕様としては「ネットワークの共有フォルダのみ参照可能。初期フォルダは自PCの共有フォルダ(固定名)」
です。(下記コードはテストなのでDeskTopにしていますが)
これが実装できれば手法には拘っていません。下記コードの不正部分、もしくは上記仕様の実現についての情報があれば
よろしくお願いします。

Public Declare Function SHBrowseForFolder Lib "shell32.dll" (ByRef lpBROWSEINFO As BROWSEINFO) As Integer
Public Declare Function SHGetPathFromIDList Lib "shell32.dll" (ByVal pidl As Integer, ByVal pszPath As String) As Integer
Public Declare Sub CoTaskMemFree Lib "ole32.dll" (ByVal pv As Integer)

Public Const CSIDL_DESKTOP = &H0
Public Const BIF_RETURNONLYFSDIRS = &H1
Public Const MAX_PATH = 260

Public Structure BROWSEINFO
    Dim hwndOwner As Integer
    Dim pidlRoot As Integer
    Dim pszDisplayName As String
    Dim lpszTitle As String
    Dim ulFlags As Integer
    Dim lpfn As Delegate1
    Dim lParam As String
    Dim iImage As Integer
End Structure

Public Declare Function SendMessage Lib "user32" (ByVal hWnd As Integer, ByVal wMsg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As Integer

Public Const WM_USER = &H400
Public Const BFFM_SETSELECTIONA = (WM_USER + 102)
Public Const BFFM_INITIALIZED = 1

Public Delegate Function Delegate1(ByVal hWnd As Integer, ByVal uMsg As Integer, ByVal lParam As Integer, ByVal lpData As Integer) As Integer


   Private Function mOpenFolderDialog32(ByRef SelectedFoldername As String) As Boolean

        Dim Browse As BROWSEINFO
        Dim pID As Integer
        Dim PathName As String

        Try

            With Browse
                .hwndOwner = Me.Handle.ToInt32
                .pidlRoot = CSIDL_DESKTOP
                .lpszTitle = "フォルダを選択してください"
                .ulFlags = BIF_RETURNONLYFSDIRS
                .lpfn = AddressOf BrowseCallbackProc         'コールバック関数のアドレス
                .lParam = CurDir() & vbNullChar              '初期フォルダのパス名
            End With

            '「フォルダの参照」ダイアログの呼び出し
            pID = SHBrowseForFolder(Browse)

            If pID Then
                '予めNull文字をセット
                PathName = New String(CChar(" "), MAX_PATH)

                'SHBrowseForFolderで得られた値からフォルダのパス名を取得
                SHGetPathFromIDList(pID, PathName)

                '割り当てられたメモリを開放
                CoTaskMemFree(pID)

                Dim n As Integer = InStr(PathName, vbNullChar)
                If n Then
                    SelectedFoldername = Microsoft.VisualBasic.Strings.Left(PathName, n - 1)
                End If
            End If

            mOpenFolderDialog32 = True

        Catch ex As Exception
            'エラーメッセージを表示する
            MessageBox.Show(ex.ToString)
        End Try

    End Function

'コールバック関数
Public Function BrowseCallbackProc(ByVal hWnd As Integer, ByVal uMsg As Integer, ByVal lParam As Integer, ByVal lpData As Integer) As Integer
    If uMsg = BFFM_INITIALIZED Then
        SendMessage(hWnd, BFFM_SETSELECTIONA, 1, lpData)
    End If
End Function


'------------------呼び出し------------------
    Private Sub cmdFileDialog_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdFileDialog.Click
        Dim str As String
        mOpenFolderDialog32(str)
        MessageBox.Show(str)
    End Sub
> 「System.EntryPointNotFoundException:DLL user32のSendMessageというエントリポイントが見つかりません。」という

そのまんまでSendMessageという関数がないためですね。
実在するのはSendMessageA、SendMessageWだと思います。
参考にしたVBサンプルにAlias句がついていたはずだと思いますので、
サンプルを再確認してみてはいかがでしょう。

ちなみにSHBrowseForFolder APIを使用したフォルダ選択ダイアログの実装サンプルは
C#ですが、Microsoftで公開されていますよ。
http://support.microsoft.com/default.aspx?scid=kb;ja;JP306285
ちなみに、これが原因です。
http://msdn.microsoft.com/library/ja/default.asp?url=/library/ja/cpref/html/frlrfSystemRuntimeInteropServicesDllImportAttributeClassExactSpellingTopic.asp
Declare にせよ DllImport にせよデフォルトでは Ansi なので、VB.NET ではきっちり関数名を書く必要があります。
// C# などでは勝手に A を付けてくれるんですが。互換性の問題でしょうね。
Declare Auto にすれば実行時にプラットフォームに相応しい関数名のを呼び出してくれるようになります。String の文字セットにも影響しますけど。
よねKENさん、Hongliang さんありがとうございました。SendMessageの定義に AliasまたはDeclare Autoを記述したら無事動作致しました。
VB時代もあまりAPIを使用してこなかったのでほんとに初歩的なことですいません。
(少し言い訳ですが、参考にした.NETの書籍ではSHBrowseForFolder、SHGetPathFromIDListのサンプルでAlias句が外されていたので
SendMessageについても外して記述していました。)

ただその後、ネットワーク表示にしたのですが、初期フォルダの指定が効いてくれません。
Windows Networkまでは展開されるのですが、そこから先がダメなようです。もともとネットワークリソースを選択させる
のは無理があるのでしょうか・・・・

> ただその後、ネットワーク表示にしたのですが、初期フォルダの指定が効いてくれません。
> Windows Networkまでは展開されるのですが、そこから先がダメなようです。もともとネットワークリソースを選択させる
> のは無理があるのでしょうか・・・・

SHBrowseForFolder() を呼び出す時点で、そのネットワーク資源が利用可能=最低限その資源を公開しているPCにログオンしている必要があるでしょうね。
■No15144に返信(渋木宏明(ひどり)さんの記事)
> SHBrowseForFolder() を呼び出す時点で、そのネットワーク資源が利用可能=最低限その資源を公開しているPC
> にログオンしている必要があるでしょうね。

どうもです。まずは自分のPCの共有フォルダでテストしたので上記はクリアしているのですが、もともと想定されていな
いのかダメでした。
それで色々と足掻いた挙句、下記を参考にネットワークプレースを作成し、(元々仕様が固定名の共有フォルダが前提
でしたので)デフォルトでそれを選択させるという方法で逃げました。
(下記資料からネットワークプレースはDocuments and Settings\ユーザー名\NetHood にあるのが判ったため。)
ネットワークプレースを作成する際に共有フォルダが存在しない場合はメッセージを出して共有フォルダを作成してもらう
という仕様です。

http://www.microsoft.com/japan/technet/scriptcenter/resources/qanda/may05/hey0509.mspx

みなさま、色々とありがとうございました。
解決済みにするの忘れました・・・
解決済み!

DOBON.NET | プログラミング道 | プログラミング掲示板