tagCANDY CGI VBレスキュー(花ちゃん) の Visual Basic 2010 用 掲示板(VB.NET 掲示板) [ツリー表示へ]   [Home]
一括表示(VB.NET VB2005)
タイトルDeclareステートメントで定義されたアンマネージdll関数呼出について
記事No10385
投稿日: 2010/12/28(Tue) 18:09
投稿者テイラー
テイラーと申します。

Windows XP, VB2005でMDIアプリケーション開発を行っています。

ネット上では特にまとまった情報がなく、一方でVisualStudioのヘルプには基本的な例と言えるものがないので、こちらで質問したいと思うところです。技術的実体を把握せずに質問しているので、的外れな質問であったり、ある程度の規模の開発が必要になるようでしたら、関連情報の簡単な手がかりで良いので、ご教授頂ければと思います。

質問の内容ですが、開発中のMDIアプリケーションはVB2005で行い、子Formを文書1,2,3としてロードしていくとき、文書1,2,3Formについて、同じ名前のアンマネージdll関数をリンクさせ、内部のデータ領域に、文書1,2,3毎のデータを持たせながらロードしていく(予定の)プログラムとなっています。

問題になっているのは、アンマネージdll関数をDeclareステートメントで記述したクラスを、Lib_FunというVB2005のクラスで定義し、これを文書1,2,3Formでそれぞれインスタンスをロードして用いようとしていましたが、アンマネージdll関数は静的にリンクされた(?)のか、文書毎の個別のdll関数とはなりませんでした。

Assembly機能を用いて、Class Lib_Funをdllにしたものをインスタンスにして呼び出しても同じ結果となり、Lib_Fun内に記述したVB2005の記述によるパラメーターのみ個別のインスタンスに対応し、アンマネージdll関数は全てのインスタンスに共通1つのものとなっているようです。

実際にはThread機能を用いる物かもしれないと悩んでいるところです。

要は、アンマネージdll関数を、文書1,2,3それぞれに個別のものとしてリンク出来れば良いということです。

方向性としては合っているんでしょうか・・・

このような形のライブラリについて情報がありましたら、簡単な情報だけでもご教授頂ければと思います。

よろしくお願い申し上げます。

[ツリー表示へ]
タイトルRe: Declareステートメントで定義されたアンマネージdll関数呼出について
記事No10386
投稿日: 2010/12/28(Tue) 22:29
投稿者魔界の仮面弁士
> 文書毎の個別のdll関数とはなりませんでした。
複数の DLL があって、それぞれを個別に呼び出したいという事でしょうか。
だとしたら、Lib 句にフルパスを書いておいてはいかがでしょう。

ファイル名だけを記述していた場合は、LoadLibrary API 相当の
パス解決が行われるでしょうから、DLL の呼び分けは難しい気がします。

[ツリー表示へ]
タイトルRe^2: Declareステートメントで定義されたアンマネージdll関数呼出について
記事No10387
投稿日: 2010/12/28(Tue) 23:04
投稿者テイラー
早速お考えいただいてありがとうございます。^^

> > 文書毎の個別のdll関数とはなりませんでした。
> 複数の DLL があって、それぞれを個別に呼び出したいという事でしょうか。
> だとしたら、Lib 句にフルパスを書いておいてはいかがでしょう。

えっと、複数のdllというのが伝わりにくかったと思いますが、アンマネージdll関数の一群(これも私が書いたんですが)を基本単位としてクラス(Lib_Fun)を構成し、文書ごとにLib_Funクラスのインスタンスを持たせる方法で用いようとしております。

重要なのは、例えば、アプリケーションを多重起動すれば、これはアプリケーションごとに同じdllで動いていても、内部データは別々に持っているということです。
このとき、用いられているdllは同じですが、インスタンスは多重起動された個数だけ展開されていると考えてよいでしょうか・・・

このような動作を、1つのMDIアプリケーション内の文書1,2,3・・・について行わせたいということです。

技術情報がありそうでないこの話はかなりカルトな要望かもしれません・・・

誰にもできない可能性もありますが、知恵をお借りできればと思います。

[ツリー表示へ]
タイトルRe^3: Declareステートメントで定義されたアンマネージdll関数呼出について
記事No10388
投稿日: 2010/12/29(Wed) 00:01
投稿者YuO
やりたいことが見えていませんが……。


> > > 文書毎の個別のdll関数とはなりませんでした。
> > 複数の DLL があって、それぞれを個別に呼び出したいという事でしょうか。
> > だとしたら、Lib 句にフルパスを書いておいてはいかがでしょう。
> えっと、複数のdllというのが伝わりにくかったと思いますが、アンマネージdll関数の一群(これも私が書いたんですが)を基本単位としてクラス(Lib_Fun)を構成し、文書ごとにLib_Funクラスのインスタンスを持たせる方法で用いようとしております。
> 重要なのは、例えば、アプリケーションを多重起動すれば、これはアプリケーションごとに同じdllで動いていても、内部データは別々に持っているということです。
> このとき、用いられているdllは同じですが、インスタンスは多重起動された個数だけ展開されていると考えてよいでしょうか・・・

あるプロセス内でひとつのDLLを何度LoadLibraryしようが,アンロードされない限りDLLは一度だけロードされます。
これは理解されていますでしょうか。

どうも,LoadLibraryすればするだけ,DLLが読み込まれると勘違いされているのではないかと思われます。
アンマネージDLLでCOMを使わずにクラスのようなことがやりたいなら,ハンドルという形にする必要があります。
Create系の関数を用意して内部的には何らかのクラスなり構造体なりをnew等し,それを「ハンドル」という形で返すような実装にしておきます。
他のDLLの関数群は第一引数にハンドルを受け取って,それを元のクラスなり構造体なりにキャストして使う,という形になります。

Win32 APIのGDIなどのAPIを見て,複数のインスタンスを扱うにはどうすればよいのかの手本にすると良いでしょう。

[ツリー表示へ]
タイトルRe^4: Declareステートメントで定義されたアンマネージdll関数呼出について
記事No10390
投稿日: 2010/12/29(Wed) 02:13
投稿者テイラー
早速のアドバイスをありがとうございます。

> アンマネージDLLでCOMを使わずにクラスのようなことがやりたいなら,ハンドルという形にする必要があります。
> Create系の関数を用意して内部的には何らかのクラスなり構造体なりをnew等し,それを「ハンドル」という形で返すような実装にしておきます。
> 他のDLLの関数群は第一引数にハンドルを受け取って,それを元のクラスなり構造体なりにキャストして使う,という形になります。
>
> Win32 APIのGDIなどのAPIを見て,複数のインスタンスを扱うにはどうすればよいのかの手本にすると良いでしょう。


今までで試した内容は、アンマネージdllをVBクラスライブラリに組み込んで(つまりはラッパーdllを組み立てようとしたワケですが)、
アプリケーション側からSystem.Reflection.Assembly.LoadFrom(ラッパーdll名)でロードした後、.CreateInsance(クラス名)でインスタンスを生成してハンドルし、
GetType.GetMeThod(クラスメンバ名)で関数を保持して、Invoke(インスタンスハンドラ,引数)メソッドで呼び出す方法をとっていました。

しかし、これは、ライブラリインスタンスは新しいものの、アンマネージdll関数をラップした肝心のメンバ関数は、
同じオブジェクトをハンドルしているようです。(オブジェクト比較関数から解った事ですが)
つまりは、アンマネージdll関数は動的に呼び出しても静的にリンクされているらしい事が見えてきたワケです。

アンマネージdllをラップしているクラスライブラリのインスタンスを生成したら、
この中でDeclareステートメントで宣言したアンマネージdll関数も何らかの方法で、インスタンスを生成しなくてはならないかもしれない事がわかりました。
(この方向で合ってればという話ですが)

次はDeclareステートメントの問題かも知れませんが、この付近についてVBから解決する可能性について見当がつきましたら、アドバイス頂ければと思います。
一方では、アンマネージ関数の内部的な解決を模索して行きたいとも思います。
とてつもなく面倒な事をしているかも知れませんが、ここまでに試した事以外にも、良い方法がありましたらアドバイス頂ければと思います。

よろしくお願い申し上げます。

[ツリー表示へ]
タイトルRe^5: Declareステートメントで定義されたアンマネージdll関数呼出について
記事No10391
投稿日: 2010/12/29(Wed) 07:53
投稿者shu
ソースの概略を載せて説明されたほうがよいかと思います。
dllのクラス、各ヘッダ
呼び出し側の使い方など

[ツリー表示へ]
タイトルRe^6: Declareステートメントで定義されたアンマネージdll関数呼出について
記事No10393
投稿日: 2010/12/29(Wed) 10:50
投稿者テイラー
> ソースの概略を載せて説明されたほうがよいかと思います。
> dllのクラス、各ヘッダ
> 呼び出し側の使い方など

アドバイスありがとうございます。遅レスで申し訳ないですが、なにぶん悩んだ末記述が増えている状態なのでご容赦を・・・

VBクラスライブラリ(ファイル名はLib_Fun_vb.dll)の構成が、

'型宣言部
<StructLayoutAttribute(LayoutKind.Sequential)> Public Structure Structure1
    <MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst:=31)> Public Array1 As Integer()
    <MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst:=31)> Public Array2 As Integer()
    Public Sub New(ByVal num As Integer)
        '...何らかの処理

    End Sub
End Structure
<StructLayoutAttribute(LayoutKind.Sequential)> Public Structure Structure2
    Public Number As Long
    Public text() As String
    Public Sub New(ByVal num As Integer)
        '...何らかの処理

    End Sub
End Structure
<StructLayoutAttribute(LayoutKind.Sequential)> Public Structure Structure3
    Public num() As Integer
    Public X As Double
    Public Y As Double
    <MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst:=256)> Public Structure_Name As String
    Public Sub New(ByVal num As Integer)
        '...何らかの処理

    End Sub
End Structure

'アンマネージdll定義
Public Class Lib_Fun_Static
    Private Prop1 As String
    Private Prop2 As String
   '...

   'Property
    Public Property Property_1() As String
        Get
            Return Me.Prop1
        End Get
        Set(ByVal value As String)
            Me.Prop1 = value
        End Set
    End Property
    Public Property Property_2() As String
        Get
            Return Me.Prop2
        End Get
        Set(ByVal value As String)
            Me.Prop2 = value
        End Set
    End Property
   '...

    '下記の関数の一群には内部データを持たせています。
    Public Declare Function Function_1 Lib "アンマネージdllのファイル名" (ByVal Str1 As Structure1, ByRef Str2 As Structure2) As Boolean
    Public Declare Function Function_2 Lib "アンマネージdllのファイル名" (ByRef Str3 As Structure3) As Boolean
   '...
End Class
'
'
'アンマネージdllラッパー
Public Class Lib_Fun_w
    Private Fun_Static As New Lib_Fun_Static
    Public Property Property_1() As String
        Get
            Return Me.Fun_Static.Property_1
        End Get
        Set(ByVal value As String)
            Me.Fun_Static.Property_1 = value
        End Set
    End Property
    Public Property Property_2() As String
        Get
            Return Me.Fun_Static.Property_2
        End Get
        Set(ByVal value As String)
            Me.Fun_Static.Property_2 = value
        End Set
    End Property

    Public Function Function_1(ByVal Str1 As Structure1, ByRef Str2 As Structure2) As Boolean
        Function_1 = Me.Fun_Static.Function_1(Str1, Str2)
    End Function
    Public Function Function_2(ByRef Str3 As Structure3) As Boolean
        Function_2 = Me.Fun_Static.Function_2(Str3)
    End Function
End Class

という形になっています。一方で、呼び出し側のMDIアプリケーションでは呼び出しクラスを構築してあり、


Imports Lib_Fun_vb
Public Class Lib_Fun_dll
    Public dll_Path As String
    Public Lib_Fun_Assembly As Reflection.Assembly
    Public Lib_Handle As New Object

    'Methode List
    Public Property_1 As System.Reflection.PropertyInfo
    Public Property_2 As System.Reflection.PropertyInfo
    '...

    Public Function_1 As System.Reflection.MethodInfo
    Public Function_2 As System.Reflection.MethodInfo
    '...

    Public Sub New()
        MyBase.New()
        Call Me.Library_Loader()
    End Sub
    Public Sub Library_Loader()
        Me.dll_Path = "Lib_Fun_vb.dll"
        Me.Lib_Fun_Assembly = System.Reflection.Assembly.LoadFrom(Me.dll_Path)
        Me.Lib_Handle = Me.Lib_Fun_Assembly.CreateInstance(Me.dll_Path & ".Lib_Fun_w")
        'Property Get Procedure
        Me.Property_1 = Me.Lib_Handle.GetType.GetProperty("Property_1")
        Me.Property_2 = Me.Lib_Handle.GetType.GetProperty("Property_2")
        '...

        'Methode Get Procedure
        Me.Function_1 = Me.Lib_Handle.GetType.GetMethod("Function_1")
        Me.Function_2 = Me.Lib_Handle.GetType.GetMethod("Function_2")
        '...
    End Sub
End Class

Public Class Lib_Fun
    Private LibFun_dll As New Lib_Fun_dll()
    Private Property Lib_Handle() As Object
        Get
            Return Me.LibFun_dll.Lib_Handle
        End Get
        Set(ByVal value As Object)
            Me.LibFun_dll.Lib_Handle = value
        End Set
    End Property

   'Property Set
    Public Property Property_1() As String
        Get
            Return Me.LibFun_dll.Property_1.GetValue(Me.Lib_Handle, Nothing)
        End Get
        Set(ByVal value As String)
            Me.LibFun_dll.Property_1.SetValue(Me.Lib_Handle, value, Nothing)
        End Set
    End Property
    Public Property Property_2() As String
        Get
            Return Me.LibFun_dll.Property_2.GetValue(Me.Lib_Handle, Nothing)
        End Get
        Set(ByVal value As String)
            Me.LibFun_dll.Property_2.SetValue(Me.Lib_Handle, value, Nothing)
        End Set
    End Property
    '...

    'Methode Call
    Public Function Function_1(ByVal Str1 As Structure1, ByRef Str2 As Structure2) As Boolean
        Dim Methode_Parameter() As Object = {Str1, Str2}
        Function_1 = Me.LibFun_dll.Function_1.Invoke(Me.Lib_Handle, Methode_Parameter)
        Str2 = Methode_Parameter(1)
    End Function
    Public Function Function_2(ByRef Str3 As Structure3) As Boolean
        Dim Methode_Parameter() As Object = {Str3}
        Function_2 = Me.LibFun_dll.Function_2.Invoke(Me.Lib_Handle, Methode_Parameter)
        Cls2 = Methode_Parameter(0)
    End Function

End Class


などと構成してあり、MDIアプリケーションの文書子フォームではLib_FunクラスをNewで用いるようにしてあります。
ほとんど見た目にはLib_Funクラスのインスタンスを用いるのも、Lib_Fun_Staticクラスを用いるのも一緒のようにも見えます。
正直、どの様に振舞うのかはやってみて確認、という事だった訳ですが、VBのプロパティ変数はNewの度に新しいインスタンスを生成したようなので、
Declare宣言にしてあるdll関数を何とかすれば良いのではないかと言う状態です。

上記のコード例は読むのに手間がかかりますが(混乱を深めているとも)、もっと単純に扱える手段があればアドバイス頂ければと思います。

よろしくお願い申し上げます。

[ツリー表示へ]
タイトルRe^7: Declareステートメントで定義されたアンマネージdll関数呼出について
記事No10395
投稿日: 2010/12/29(Wed) 13:22
投稿者魔界の仮面弁士
> '下記の関数の一群には内部データを持たせています。
データは Function_1 側ではなく、Lib_Fun_Static 側で持たせるべきでは無いでしょうか。
もしくは内部データそのものを複数持てるようにしておき、それをハンドル等で区別させるとか。

今の実装のままだと、Lib_Fun_Static の各インスタンスを別のプロセス(≠スレッド)で
生成させでもしないと、同じデータを扱う事になってしまうように思えます。
http://msdn.microsoft.com/ja-jp/library/h90dkhs0.aspx


>> http://scripting.cocolog-nifty.com/blog/2007/09/jscriptnetwin32_560f.html
>> http://memo-space.blogspot.com/2010/02/powershellwin32api.html
> 上記の方法と似たことをVBでできるかどうか、どのようになるのか調べてやってみますので、
VB でもできますが、今回のケースでは解決策にならないと思いますよ。

[ツリー表示へ]
タイトルRe^8: Declareステートメントで定義されたアンマネージdll関数呼出について
記事No10397
投稿日: 2010/12/31(Fri) 10:37
投稿者テイラー
アドバイスありがとうございます。

> http://msdn.microsoft.com/ja-jp/library/h90dkhs0.aspx
>
>
> >> http://scripting.cocolog-nifty.com/blog/2007/09/jscriptnetwin32_560f.html
> >> http://memo-space.blogspot.com/2010/02/powershellwin32api.html
> > 上記の方法と似たことをVBでできるかどうか、どのようになるのか調べてやってみますので、
> VB でもできますが、今回のケースでは解決策にならないと思いますよ。

今、dllをいじり始めてます・・・

VBから解決はかなり難しい可能性があるので、暫らくはあちこちあたる格好です。
解決する方法が見えたらまたおじゃまします。

[ツリー表示へ]
タイトルRe^5: Declareステートメントで定義されたアンマネージdll関数呼出について
記事No10392
投稿日: 2010/12/29(Wed) 10:12
投稿者魔界の仮面弁士
わざわざ遅延バインドしているのは、アドインのように動作させたいという事なのでしょうか?

たとえば、何らかのアンマネージ リソースを通信ポートを排他オープンするような
アンマネージ関数の場合、DLL が個別にロードされているかどうかには関係なく、
2 番目以降の呼び出しが弾かれる事になるでしょうが、そういう話では無いのですよね?


> しかし、これは、ライブラリインスタンスは新しいものの、アンマネージdll関数をラップした肝心のメンバ関数は、
> 同じオブジェクトをハンドルしているようです。(オブジェクト比較関数から解った事ですが)
「関数がオブジェクトをハンドルしている」という点が良く分かりません。具体的にはどういう状態でしょうか。


> つまりは、アンマネージdll関数は動的に呼び出しても静的にリンクされているらしい事が見えてきたワケです。
その結論に至るまでの流れが見えてこないのですが、Declare 以外で P/Invoke したい
という意味ならば下記のような流れになります。解決策になるかどうかは分かりませんが。
http://scripting.cocolog-nifty.com/blog/2007/09/jscriptnetwin32_560f.html
http://memo-space.blogspot.com/2010/02/powershellwin32api.html

[ツリー表示へ]
タイトルRe^6: Declareステートメントで定義されたアンマネージdll関数呼出について
記事No10394
投稿日: 2010/12/29(Wed) 10:55
投稿者テイラー
> その結論に至るまでの流れが見えてこないのですが、Declare 以外で P/Invoke したい
> という意味ならば下記のような流れになります。解決策になるかどうかは分かりませんが。
> http://scripting.cocolog-nifty.com/blog/2007/09/jscriptnetwin32_560f.html
> http://memo-space.blogspot.com/2010/02/powershellwin32api.html

アドバイスありがとうございます。

上記の方法と似たことをVBでできるかどうか、どのようになるのか調べてやってみますので、動作状況が見えてきましたら、また質問するかもしれません。

その際はよろしくお願い申し上げます。

[ツリー表示へ]
タイトルRe^5: Declareステートメントで定義されたアンマネージdll関数呼出について
記事No10396
投稿日: 2010/12/29(Wed) 19:37
投稿者YuO
> 今までで試した内容は、アンマネージdllをVBクラスライブラリに組み込んで(つまりはラッパーdllを組み立てようとしたワケですが)、
> アプリケーション側からSystem.Reflection.Assembly.LoadFrom(ラッパーdll名)でロードした後、.CreateInsance(クラス名)でインスタンスを生成してハンドルし、
> GetType.GetMeThod(クラスメンバ名)で関数を保持して、Invoke(インスタンスハンドラ,引数)メソッドで呼び出す方法をとっていました。

根本的に,そのアンマネージDLLは複数の状態を持てるのですか?

例えば,
static int value = 0;

__declspec(dllexport) int WINAPI GetValue ()
{
  return ++value;
}
というアンマネージCコードをDLLに仕立て上げて,ひとつのプロセスから複数回DllImport/Declare/LoadLibrary(Ex) APIによる呼び出しを行ったところで,
毎回アンロード (LoadLibrary(Ex)使った場合のみFreeLibraryで可能) しない限り,GetValueの戻り値は増え続けます。

DLL自体が,
__declspec(dllexport) HANDLE WINAPI CreateValueHandle ()
{
  int * result = new int ();
  *result = 0;
  return (HANDLE)result;
}

__declspec(dllexport) int WINAPI GetValue (HANDLE handle)
{
  int * pointer_to_value = (int *)handle;
  return ++*pointer_to_value;
}

__declspec(dllexport) void WINAPI CloseValueHandle (HANDLE handle)
{
  int * pointer_to_value = (int *)handle;
  delete pointer_to_value;
}
のように複数の状態を持てる実装になっていない限り,いくらラッパー側で頑張っても単一状態しか持てないことにはかわりないですよ。
# アンマネージ側が状態をSave/Restoreできるなら,それをハンドルとして使えますが。

[ツリー表示へ]
タイトルRe^6: Declareステートメントで定義されたアンマネージdll関数呼出について
記事No10398
投稿日: 2010/12/31(Fri) 10:47
投稿者テイラー
アドバイスありがとうございます。

> 根本的に,そのアンマネージDLLは複数の状態を持てるのですか?
>
> 例えば,
> static int value = 0;
>
> __declspec(dllexport) int WINAPI GetValue ()
> {
>   return ++value;
> }
> というアンマネージCコードをDLLに仕立て上げて,ひとつのプロセスから複数回DllImport/Declare/LoadLibrary(Ex) APIによる呼び出しを行ったところで,
> 毎回アンロード (LoadLibrary(Ex)使った場合のみFreeLibraryで可能) しない限り,GetValueの戻り値は増え続けます。
>
> DLL自体が,
> __declspec(dllexport) HANDLE WINAPI CreateValueHandle ()
> {
>   int * result = new int ();
>   *result = 0;
>   return (HANDLE)result;
> }
>
> __declspec(dllexport) int WINAPI GetValue (HANDLE handle)
> {
>   int * pointer_to_value = (int *)handle;
>   return ++*pointer_to_value;
> }
>
> __declspec(dllexport) void WINAPI CloseValueHandle (HANDLE handle)
> {
>   int * pointer_to_value = (int *)handle;
>   delete pointer_to_value;
> }
> のように複数の状態を持てる実装になっていない限り,いくらラッパー側で頑張っても単一状態しか持てないことにはかわりないですよ。
> # アンマネージ側が状態をSave/Restoreできるなら,それをハンドルとして使えますが。

VBから動的にdllのインスタンスをいくつも発生させるのは難しい可能性があるので、当面はdllそのものに手を加えようとしています。

可能性がないわけはないと思いますので、解決の方法が見えたらまたおじゃまします。

[ツリー表示へ]