はじめまして VB6からVB2005へ移行途中の者です。クラスの使い方で躓いております。
下記3点について御教授お願いします。
①変数のスコープ&クラスの寿命
下記のようなデジタル出力ボードを制御するクラスを作成しました。
Moduleに関数をまとめたときには問題なかったのですが、クラスのすると
初期化(Init_Dio)のときに取得したhDeviceHandleが実際に使うSet_Dioでは
ゼロになってしまいます。同様にポートの出力状態を保持しているOutBufも
ゼロになってしまいます。クラスではPublicをつけて宣言しても値が保持されない
のでしょうか?
Public Class FormMain
Private Sub FormMain_Load(省略) Handles MyBase.Load
Dim Dio3232 As Dio = New Dio
Dio3232.Init_DIO() ← Dioを初期化します
End Sub
End Class
Public Class Dio
Public Shared OutBuf(3) As Integer ←Sharedをつけると値は保持される
Public Shared hDeviceHandle As Long
'Public hDeviceHandle As Long ←Sharedがないと値が保持されない
'ここで DIOを初期化します
Public Sub Init_DIO()
hDeviceHandle = IFCDIO_ANY.DioOpen(lpszName, IFCDIO_ANY.FBIDIO_FLAGE)
End Sub
'実際に使うときにハンドルがゼロになってしまいます
Public Sub SetDio(ByVal Data As Integer)
OutBuf(PortNo) = OutBuf(PortNo) Or Bit
st = IFCDIO_ANY.DioOutputByte(hDeviceHandle, PortNo, mOutBuf(PortNo))
End Sub
End Class
②クラス内でのConst宣言
Public Const RANGE1 As Integer = 1 の様にクラス内でPublicをつけてConst宣言を しましたが、他のクラスやフォームで参照できませんでした。同じ内容をModuleに
書くとOKでした。プロジェクト内のすべてのクラス、フォームから参照できる
定数を宣言するにはModuleに書くしかないのでしょうか?
③クラスからフォームに貼り付けたコントロールを使う
下記は、シリアルポートクラスのポートオープンの部分なのですがフォームロード
するときにPortOpenメソドを実行しています。PortOpenメソドでは、各種設定をして
フォームに貼り付けたコントロールを実行してポートをオープンします。
クラスのメソドから直接フォーム上のコントロールを使うのは良くない・・・と
聞きましたがこのような場合は、クラスの考え方としてどの様にするのが正しい
のでしょうか?
測定器を動かす通信部分もTesterクラスに入れてしまった方が良い気もしますし、
汎用性を考えるとフォームに貼り付けたコントロールはクラスから操作しない方が
が良い気もします。現在試行錯誤の状態です
Public Class Tester
Public Sub PortOpen()
'COM1 RS232Cの設定
With FormMain.ComRS232
.PortName = "COM1"
内容省略
End With
FormMain.ComRS232.Open() ' 通信ポートを開く
End Sub
End Class
よろしくお願いします
hDeviceHandle は、Dio のインスタンスごとに存在します。
> Private Sub FormMain_Load(省略) Handles MyBase.Load
> Dim Dio3232 As Dio = New Dio
> Dio3232.Init_DIO() ← Dioを初期化します
> End Sub
で New した Dio のインスタンスじゃただのローカル変数で、このメソッドを抜ければ使われなくなります。
SetDio を使うときにまた別に Dio を New しているでしょう? そうすると別の Dio ですから別の hDeviceHandle を持つことになります。
メソッドを超えてインスタンスを維持する場合はフィールド(メンバ変数)に置いておかなければなりません。
あ、それから VB6 以前と VB7(VB2002)以降では Integer/Long のサイズが変わっています。あとハンドル/ポインタ用に IntPtr 型が存在しています。
> Public Const RANGE1 As Integer = 1 の様にクラス内でPublicをつけてConst宣言を
> しましたが、他のクラスやフォームで参照できませんでした。同じ内容をModuleに
> 書くとOKでした。プロジェクト内のすべてのクラス、フォームから参照できる
> 定数を宣言するにはModuleに書くしかないのでしょうか?
Module の場合はコンパイラが適当に変数名から探してきますが、クラス等に定義した静的メンバ(Const/Shared なもの)を参照するには クラス名.変数名 と記述する必要があります。
どのクラスに存在するものなのかぱっと見で分かる、そのため「どういう機能に属するどういう値なのか」を直感的に記述・読解できる、同名の静的メンバを(別クラスに配置することで)使い分けることができる、などのメリットがあります。
なお、Const 定数は基本的に絶対不変のものに使い(Math.PI とか)、そうでないものは Shared ReadOnly な変数にしておくのがいいでしょう。
> クラスのメソドから直接フォーム上のコントロールを使うのは良くない・・・と
> 聞きましたがこのような場合は、クラスの考え方としてどの様にするのが正しい
> のでしょうか?
メソッドの引数として渡せばいいんじゃないですか?
① 変数のスコープ&クラスの寿命
IFCDIO?ですか、インターフェース社のライブラリ使っておられる
ようにお見受けしますが・・・違いますか?
仮にインターフェース社のものとして、私も過去、モジュールで
はなく、クラスに・・・と何度かメーカーに申し入れしたのです
が残念ながら聞いてもらえなかったので自作しました。
で・・・
> Public Class FormMain
> Private Sub FormMain_Load(省略) Handles MyBase.Load
> Dim Dio3232 As Dio = New Dio
> Dio3232.Init_DIO() ← Dioを初期化します
> End Sub
> End Class
これだと、Hongliangさんが書かれたようにFormMain_Loadを
抜けたらDio3232インスタンスは破棄されますので・・・
Public Class FormMain
Private Dio3232 as Dio
Private Sub FormMain_Load(省略) Handles MyBase.Load
Dio3232 = New Dio
Dio3232.Init_DIO()
End Sub
Private Sub FormMain_FormClosing ...
Dio3232 = Nothing
End Sub
End Class
とするところなんですが、DioクラスをiDisposable属性を
つけていないと、アクセスしていない時にGCで破棄されて
しまう可能性がありますので、
Implaments iDisposable をDioクラスに付けたら・・・
よいと思います。これで私はうまくいってます。
※ スペル間違っていたらすいません。
② クラス内でのConst宣言
Hongliangが書かれている通りです。
固定値で変化しないなら・・・です。
③ クラスからフォームに貼り付けたコントロールを使う
フォームにコントロールを貼る場合も、自作シリアル通信
クラスを使うにせよ、どちらでもOKかと。
私は汎用性を求めて自作クラスにしてます。
画面の無いWindowsサービス中からでも通信が可能なように
と、いろいろな機器との通信に利用できるように、派生させ
て機能をオーバーライドし、機器専用通信機能として実現さ
せてます。パフォーマンス的にもまず問題はありません。
がんばって下さい!
※ 参考まで
インターフェースのDIOカードのドライバーは共有オープン
すると、複数のインスタンスからのアクセスも可能ですが
ビットをオンするのとオフするのが競合した場合、エラー
が出ません。必ず最後に実行された方が結果となります。
また、マルチスレッドや複数のタイマー内で同一インスタ
ンスのDIOをアクセスした場合も同様です・・・
よって、排他制御を入れてやる必要があります。
System.Threading.ReaderWriterLock を使って排他制御し
てます。効果はほとんど解らないと思いますが・・・
以上。
Hongliang様 オショウ様 ありがとうございます。
本を読んで クラスはクッキーの型 なんて説明でクラスを理解したつもりでも
実践ではなかなか上手くいかないですね。
① 変数のスコープ&クラスの寿命
お察しの通りインターフェース社のライブラリ(IFCDIO)を使用しています。
☆メンバ変数について 自分では
Public Class Dio
Private hDeviceHandle As IntPtr ←メンバ変数のつもり
Public Sub Init_DIO()
hDeviceHandle = IFCDIO_ANY.DioOpen(lpszName, IFCDIO_ANY.FBIDIO_FLAGE)
↑メンバ変数に代入しているつもり
End Sub
だったのですが・・・・。
☆Implements IDisposable
> Private Sub FormMain_FormClosing ...
> Dio3232 = Nothing
> End Sub
Implements IDisposableについて調べてみました。
これを追加するとガベージコレクタで回収されなくなるようですが・・・。
また、Public Sub Dispose() Implements IDisposable.Disposeが自動的に
追加されましたが Dio3232 = Nothingも 必要なのでしょうか?
☆OutBuf変数も値を保持したいのですが・・・
この変数はデジタル出力ボードの出力ビットの情報を保持するために
使っています。
Public Class Dio
Public OutBuf(3) As Integer ←メンバ変数のつもり
Public Sub SetDio(ByVal Data As Integer)
Dim PortNo As Integer
Dim Bit As Integer
PortNo = Data \ &H100
Bit = Data Mod &H100
OutBuf(PortNo) = OutBuf(PortNo) Or Bit
st = IFCDIO_ANY.DioOutputByte(hDeviceHandle, PortNo, moutbuf(PortNo))
If st <> 0 Then
MessageBox.Show("Output Data failed", "PCI2726Cエラー", MessageBoxButtons.OK, MessageBoxIcon.Error)
End If
End Sub
呼び出し元は、
Private Sub BackgroundWorker1_RunWorkerCompleted(省略) Handles BackgroundWorker1.RunWorkerCompleted
Dim Dio3232 As Dio = New Dio
'テスト結果出力
Dio3232.SetDio(TEST_RSLT1)
End Sub
このコードでは、OutBufの中身がゼロになってしまいます。
たぶんクラスの考え方が間違っているのだと思いますが・・・・。何が原因なのでしょうか?
② クラス内でのConst宣言
>そうでないものは Shared ReadOnly な変数にしておくのがいいでしょう。
Public Class Dio
Implements IDisposable
Public Shared ReadOnly Sw1 as Integer = &H1
Public Shared ReadOnly Sw2 as Integer = &H2
こんな感じでしょうか?
③クラスからフォームに貼り付けたコントロールを使う
>メソッドの引数として渡せばいいんじゃないですか?
「VB.Net メソドの引数」で検索したのですが、答えが見つかりませんでした。
コントロールをメソドの引数として渡す とはどのような事なのでしょうか?
お手数をおかけして申し訳ありません
よろしくお願いします。
ツイート | ![]() |