none
ObjPtrで取得したポインタをもう一度Object型に入れなおす方法 RRS feed

  • 質問

  • 実現したいことは、件名のとおりです。

    環境はVBA(VB6.0)です。
    以下のようなコードを書くと、[Set c2 = ptr]でコンパイルエラーになります。
    実現可能な方法はありませんでしょうか?

    Public Sub test()

        Dim c  As Class1
        Dim c2 As Class1
        
        Set c = New Class1
        
        Dim ptr As Long
        
        ptr = ObjPtr(c)
        
        Set c2 = ptr
        
    End Sub

    2008年10月10日 13:34

回答

  • 単純に"c2 = c"と記述すればOKだと思います。

    以下に変更したコードを記述します。

    Code Snippet
    Public Sub test()
        Dim c  As Class1
        Dim c2 As Class1
        
        Set c = New Class1
        Set c2 = c    
     
        Debug.Print ObjPtr(c)
        Debug.Print ObjPtr(c2)
        
    End Sub

    ObjPtr(c)が返すアドレスとObjPtr(c2)が返すアドレスが同じなのが確認できると思います。

     

    2008年10月11日 7:41
  • 回答ありがとうございます。

    自分が質問したいのは、ObjPtrで取得したポインタ(Long型のアドレス)から、Object型に入れなおすことができないのか?ということです。
    (オブジェクトが同一であるかという確認ではありません。)

    例えば、ObjPtrの逆のPtrObjが実現できないものかということを知りたいのです。

    Dim c As Class1
    Dim c2 As Class1

    Set c = New Class1
    Set c2 = ObjPtr(c) ' ←これをどうにかして、Object型に変換できれば・・・。


    ※非常に、トリッキーな事ですので
     「何でそんな事を聞きたいのか?」「そんな危険なことをすべきではない。」とお叱りを受けそうですが
     そこらへんはグッとこらえて頂きご回答いただければ有難く思います。

    2008年10月12日 10:40
  • こういうこと?

    Dim obj1 As Class1
    Dim obj2 As Class1

    Dim ptr As Long

    Set obj1 = New Class1

     

    ptr = ObjPtr(obj1)
    Call MoveMemory(VarPtr(obj2), VarPtr(ptr), 4)

     

    'Now, obj2 is obj1.

     

    ptr = 0
    Call MoveMemory(VarPtr(obj2), VarPtr(ptr), 4)

     

    Set obj1 = Nothing

    2008年10月12日 14:03
  •  

    Win32APIのMoveMemoryを使えばうまくいきそうなんだけど、テストプログラムを作ったんだけど

    VB自体が落ちてしまうため、コードを示すことが出来ません。

    (変数c2への代入まではうまくいくんだけど、サブルーチンから抜けるところで異常終了してしまう。)

     

    一度、Win32APIのMoveMemoryが使えないか調べてみてはどうでしょうか?

     

    余談:VB.NETだったら、CTypeが使えるから楽なんだけど。

     

    2008年10月12日 16:05
  •  

    ああそうか、サブルーチンから抜けるところでオブジェクトを解放していなかったから異常終了したんだ。

    ということで、Abstractさんのコードで動くようになります。

    ただ、MoveMemoryの定義が記述されていなかったので、以下のように定義してください。

     

    CopyMemory(MoveMemory)をAPIビューアで調べるとDestnationとSourceはByRefでAs Anyと定義されています。

    Code Snippet
    Public Declare Sub MoveMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)


    通常の変数であれば問題ないのですが、今回は、VBですでにポインタを取得して、それをAPIに渡しているので、

    DestnationとSourceはByValのAs Longで定義してください。

    Code Snippet
    Public Declare Sub MoveMemory Lib "kernel32" Alias "RtlMoveMemory" (ByVal Destination As Long, ByVal Source As Long, ByVal Length As Long)

     

    2008年10月12日 16:25

すべての返信

  • 単純に"c2 = c"と記述すればOKだと思います。

    以下に変更したコードを記述します。

    Code Snippet
    Public Sub test()
        Dim c  As Class1
        Dim c2 As Class1
        
        Set c = New Class1
        Set c2 = c    
     
        Debug.Print ObjPtr(c)
        Debug.Print ObjPtr(c2)
        
    End Sub

    ObjPtr(c)が返すアドレスとObjPtr(c2)が返すアドレスが同じなのが確認できると思います。

     

    2008年10月11日 7:41
  • 回答ありがとうございます。

    自分が質問したいのは、ObjPtrで取得したポインタ(Long型のアドレス)から、Object型に入れなおすことができないのか?ということです。
    (オブジェクトが同一であるかという確認ではありません。)

    例えば、ObjPtrの逆のPtrObjが実現できないものかということを知りたいのです。

    Dim c As Class1
    Dim c2 As Class1

    Set c = New Class1
    Set c2 = ObjPtr(c) ' ←これをどうにかして、Object型に変換できれば・・・。


    ※非常に、トリッキーな事ですので
     「何でそんな事を聞きたいのか?」「そんな危険なことをすべきではない。」とお叱りを受けそうですが
     そこらへんはグッとこらえて頂きご回答いただければ有難く思います。

    2008年10月12日 10:40
  • こういうこと?

    Dim obj1 As Class1
    Dim obj2 As Class1

    Dim ptr As Long

    Set obj1 = New Class1

     

    ptr = ObjPtr(obj1)
    Call MoveMemory(VarPtr(obj2), VarPtr(ptr), 4)

     

    'Now, obj2 is obj1.

     

    ptr = 0
    Call MoveMemory(VarPtr(obj2), VarPtr(ptr), 4)

     

    Set obj1 = Nothing

    2008年10月12日 14:03
  •  

    Win32APIのMoveMemoryを使えばうまくいきそうなんだけど、テストプログラムを作ったんだけど

    VB自体が落ちてしまうため、コードを示すことが出来ません。

    (変数c2への代入まではうまくいくんだけど、サブルーチンから抜けるところで異常終了してしまう。)

     

    一度、Win32APIのMoveMemoryが使えないか調べてみてはどうでしょうか?

     

    余談:VB.NETだったら、CTypeが使えるから楽なんだけど。

     

    2008年10月12日 16:05
  •  

    ああそうか、サブルーチンから抜けるところでオブジェクトを解放していなかったから異常終了したんだ。

    ということで、Abstractさんのコードで動くようになります。

    ただ、MoveMemoryの定義が記述されていなかったので、以下のように定義してください。

     

    CopyMemory(MoveMemory)をAPIビューアで調べるとDestnationとSourceはByRefでAs Anyと定義されています。

    Code Snippet
    Public Declare Sub MoveMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)


    通常の変数であれば問題ないのですが、今回は、VBですでにポインタを取得して、それをAPIに渡しているので、

    DestnationとSourceはByValのAs Longで定義してください。

    Code Snippet
    Public Declare Sub MoveMemory Lib "kernel32" Alias "RtlMoveMemory" (ByVal Destination As Long, ByVal Source As Long, ByVal Length As Long)

     

    2008年10月12日 16:25
  • 皆様方ご回答ありがとうございます。

    提示いただいたサンプルコードで、ポインタをObject型に入れなおすことができました。

    ただ、結果を確認する上でサンプルコードをいろいろと弄ってみたのですが、二重解放(?)のせいかExcelがぽんぽん落ちました。

    しかも、よくよく考えてみると、この方法だと参照カウントを無視することになるので
    ポインタだけを残して持ちまわるのならば(←元々これがやりたかったのです)
    NewとNothingを使わない参照カウントの管理外でObjectの生成と破棄を行わなう必要がありそうです。
    (という、考え方で間違ってないですよね?)

    上記に関しては、自分の今後の宿題(?)とさせていただき、本スレッドを閉じさせていただきます。

    ありがとうございました。
    2008年10月13日 10:35
  • VB のフォーラムなので VB の機能だけでやってみたのですが、
    現実的には C++ で AddRef を呼び出すのが一番いいんじゃないかな。

    2008年10月13日 11:31