-
最近の仕事で、三菱のシーケンサソフトである「GT SoftGOT1000」との通信を行うものがあり、 そのアクセス用のDLLとしての「GDevlib_GT16.dll」を使用しました。
「GT SoftGOT1000」と通信を行うために、「GT SoftGOT1000」がインストールされているパソコンに さらに今回開発のプログラムと同一ディレクトリに「GDevlib_GT16.dll」を設置しました。
「GDevlib_GT16.dll」を使うにあたって、三菱さんからはサンプルプログラムが提供されていますが VC++のソースしか無かったので、今回VB.NET用に書き換えました。
VB.NET用のソースは以下にありますので、よろしければお使い下さい。 但し、間違い等があるかもしれませんので、責任は負いかねますのでご了承下さい。
(「GDevlib_GT16.dll」利用するためのクラスとして「CDev.vb」の1個のファイルにしてあります。)
尚、「GDevlib_GT16.dll」を使っての開発は、三菱さんに伺ったところ「VisualStudios2012上での開発については、保証外となります」 とのことですので、VisualStudio2008で開発を行いました。
■「CDev.vb」クラスの使い方
大体以下の様な感じで使えますが、ErrLogはこの関数外で宣言したエラー出力用の関数です。
'[GT SoftGOT1000]内部デバイス操作クラス Private Device As GDev Private Sub TestSub() ' [GT SoftGOT1000]内部デバイス操作クラス作成 Device = New GDev() '----- '開始処理 '----- If Device.IsOpen() = False Then 'SoftGOT1000内部デバイス・オープン If Device.Open(1) = False Then 'オープンエラー ErrLog("GT SoftGOT1000 が開始されていません。 ") Exit Sub End If End If '===== 'SGT1000.exeの実行中確認 '===== Dim ArrProcess As Process() ArrProcess = Process.GetProcessesByName("SGT1000") If ArrProcess.Length <= 0 Then ErrLog("SoftGOT1000が動作していません。") Exit Sub End If '----- '読出 '----- 'デバイスNO Dim DevNum As UInt32 = 82 'デバイスNO:82から8個のWORD領域 '内部デバイス入力データ領域 Dim ArrDev(8 * 2 + 1) As UShort '8ワードの領域 Dim blnRet As Boolean blnRet = Device.Read(GDev.DeviceName.GD, DevNum, ArrDev) If blnRet = False Then ErrLog("読出エラー(" & DevNum.ToString & ")") Exit Sub End If '----- '書込 '----- 'デバイスNO Dim DevNumO As UInt32 = 400 'デバイスNO:400から8個のWORD領域 '内部デバイス出力データ領域 Dim ArrDevO(8 * 2 + 1) As UShort '8ワードの領域 blnRet = Device.Write(GDev.DeviceName.GD, DevNumO, ArrDevO) If blnRet = False Then ErrLog("書込エラー") Exit Sub End If 'SoftGOT1000内部デバイス・クローズ If Device.IsOpen() Then Device.Close() End If End Sub
■「CDev.vb」クラスのソース
Imports System Imports System.Text Imports System.Runtime.InteropServices Class GDev Private m_hMapFile As IntPtr Private m_ulMapPointer As UInt32 Private m_sGotNo As Int16 <DllImport("GDevlib_GT16.dll")> _ Private Shared Function GDev_OpenMapping(ByRef phMapFile As IntPtr, ByVal sGotNo As Int16) As UInt32 End Function <DllImport("GDevlib_GT16.dll")> _ Private Shared Sub GDev_CloseUnMapping(ByVal hMapFile As IntPtr, ByVal ulMapPointer As UInt32) End Sub <DllImport("GDevlib_GT16.dll")> _ Private Shared Function GDev_Read(ByVal ulMapPointer As UInt32, ByVal sDevNameID As Int16, ByVal lDevNum As Int32, ByRef pusDataTable As UInt16, ByVal lDataSize As Int32) As Int32 End Function <DllImport("GDevlib_GT16.dll")> _ Private Shared Function GDev_Write(ByVal ulMapPointer As UInt32, ByVal sDevNameID As Int16, ByVal lDevNum As Int32, ByRef pusDataTable As UInt16, ByVal lDataSize As Int32) As Int32 End Function Public Enum DeviceName GB GD GS End Enum Protected Overrides Sub Finalize() Try Close() Finally MyBase.Finalize() End Try End Sub ''' ----------------------------------------------------------------------- '''
=====''' オープン処理 ''' '''GOT-NO '''処理結果(True:OK, False:NG) '''''' ----------------------------------------------------------------------- Public Function Open(ByVal sGotNo As Int16) As Boolean If m_hMapFile = IntPtr.Zero Then Dim hMapFile As IntPtr m_ulMapPointer = GDev_OpenMapping(hMapFile, sGotNo) If m_ulMapPointer <> 0 Then m_hMapFile = hMapFile m_sGotNo = sGotNo Return True End If End If Return False End Function ''' ----------------------------------------------------------------------- ''' ''' オープン済チェック ''' '''チェック結果(True:OK, False:NG) '''''' ----------------------------------------------------------------------- Public Function IsOpen() As Boolean Return (m_hMapFile <> IntPtr.Zero) End Function ''' ----------------------------------------------------------------------- ''' ''' クローズ処理 ''' '''''' ----------------------------------------------------------------------- Public Sub Close() If m_hMapFile <> IntPtr.Zero Then GDev_CloseUnMapping(m_hMapFile, m_ulMapPointer) m_hMapFile = IntPtr.Zero m_sGotNo = 0 End If End Sub ''' ----------------------------------------------------------------------- ''' ''' デバイス名からID変換 ''' '''デバイス名 '''ID '''''' ----------------------------------------------------------------------- Private Function GetDevNameID(ByVal eDevName As DeviceName) As Int16 Select Case eDevName Case DeviceName.GB Return 0 Case DeviceName.GD Return 1 Case DeviceName.GS Return 2 End Select Return -1 End Function Private Function GetFlag(ByVal sVal As UInt16, ByVal iBit As Integer) As Boolean Dim iMask As Integer = 1 << iBit Return ((sVal And iMask) <> 0) End Function Private Function SetFlag(ByVal sVal As UInt16, ByVal iBit As Integer, ByVal bFlag As Boolean) As UInt16 Dim iMask As Integer = (1 << iBit) If bFlag Then Return CType(sVal Or iMask, UInt16) Else Return CType(sVal And Not iMask, UInt16) End If End Function ''' ----------------------------------------------------------------------- ''' ''' 単独データ読出し ''' '''デバイス名 '''デバイスNO '''返すデータ '''読出結果(True:OK, False:NG) '''''' ----------------------------------------------------------------------- Public Function Read(ByVal eDevName As DeviceName, ByVal lDevNum As Int32, ByRef DataVal As UInt16) As Boolean If m_hMapFile <> IntPtr.Zero Then Dim usValue As UInt16 = 0 Dim sDevNameID As Int16 = GetDevNameID(eDevName) If GDev_Read(m_ulMapPointer, sDevNameID, lDevNum, usValue, 1) = 0 Then If eDevName = DeviceName.GB Then If GetFlag(usValue, lDevNum Mod 16) Then DataVal = 1 Else DataVal = 0 End If Else DataVal = usValue End If Return True End If End If Return False End Function ''' ----------------------------------------------------------------------- ''' ''' 連続データ読出し ''' '''デバイス名 '''デバイスNO '''データ配列 '''読出結果(True:OK, False:NG) '''ビットデータは想定していない ''' ----------------------------------------------------------------------- Public Function Read(ByVal eDevName As DeviceName, ByVal lDevNum As Int32, ByRef DataTable() As UInt16) As Boolean If m_hMapFile <> IntPtr.Zero Then Dim sDevNameID As Int16 = GetDevNameID(eDevName) If GDev_Read(m_ulMapPointer, sDevNameID, lDevNum, DataTable(0), DataTable.Length) = 0 Then Return True End If End If Return False End Function ''' ----------------------------------------------------------------------- '''''' 単独データ書込み ''' '''デバイス名 '''デバイスNO '''データ値 '''''' 書込結果(True:OK, False:NG) ''' ----------------------------------------------------------------------- Public Function Write(ByVal eDevName As DeviceName, ByVal lDevNum As Int32, ByVal usValue As UInt16) As Boolean If m_hMapFile <> IntPtr.Zero Then Dim sDevNameID As Int16 = GetDevNameID(eDevName) If eDevName = DeviceName.GB Then Dim usTmpValue As UInt16 = 0 If GDev_Read(m_ulMapPointer, sDevNameID, lDevNum, usTmpValue, 1) = 0 Then usValue = SetFlag(usTmpValue, lDevNum Mod 16, (usValue <> 0)) End If End If If GDev_Write(m_ulMapPointer, sDevNameID, lDevNum, usValue, 1) = 0 Then Return True End If End If Return False End Function ''' ----------------------------------------------------------------------- '''''' 連続データ書込み ''' '''デバイス名 '''デバイスNO '''データ配列 '''書込結果(True:OK, False:NG) '''ビットデータは想定していない ''' ----------------------------------------------------------------------- Public Function Write(ByVal eDevName As DeviceName, ByVal lDevNum As Int32, ByRef DataTable() As UInt16) As Boolean If m_hMapFile <> IntPtr.Zero Then Dim sDevNameID As Int16 = GetDevNameID(eDevName) If GDev_Write(m_ulMapPointer, sDevNameID, lDevNum, DataTable(0), DataTable.Length) = 0 Then Return True End If End If Return False End Function Public Overrides Function ToString() As String Return String.Format("GT SoftGOT1000") End Function End Class
2014/09/03:の時の情報
PR -
システムに必要なテーブルで、自動的に番号を振っていくものが必要なときがあります。 たとえば、各種の伝票データの伝票番号の様なものです。
プログラム処理上、データを登録した直後に、自動採番された値を取得し何かに使いたいことがよくあります。
SQL-Serverでは現在の自動採番の値を取得する方法が用意されています。
取敢えず自動採番を行える簡単なテーブルの例を示します。(前回ストアド・ファンクションで使用したテーブルに細工します。)
CREATE TABLE [dbo].[TABLE_B]( [ID] [int] IDENTITY(1,1) NOT NULL, [DATA1] [nvarchar](50) COLLATE Japanese_CI_AS NULL, [DATA2] [nvarchar](50) COLLATE Japanese_CI_AS NULL, CONSTRAINT [PKEY_TW_MAG_ADDR] PRIMARY KEY CLUSTERED ( [ID] ASC )WITH (PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY] ) ON [PRIMARY]
まず、テストテーブルBに1件データを挿入します。 「ID」は自動採番の設定なので登録する値は設定しません。
INSERT INTO TABLE_B (DATA1, DATA2) VALUES('A0001', 'B0001')
■自動採番された値を取得
この直後に、設定された「ID」値を取得するには以下のSELECT文を実行します。
SELECT IDENT_CURRENT('TABLE_B') AS LASTID
結果として「1」という値が返されます。
■自動採番された値をリセット
デバッグ途中でテーブルのデータを全て削除して、さらに自動採番が「1」からにしたい場合はよくあります。 そのときに以下の命令をクエリアナライザ等で実行します。
DBCC CHECKIDENT('TABLE_B', RESEED, 0)
自動採番の値を「0」にする命令ですが、実際INSERT実行時には+1された値が「ID」に設定されます。
=====
2013/02/14:の時の情報
-
当然といえばそうなのかもしれませんが、SQL-Serverのストアド・ファンクションではUPDATEなどが実行できない。
TRY・・・CATCHも記述できないようです。関数は戻り値のみを返すもので、テーブル等に変更を与えてはいけないのでしょう。
さて、取敢えずストアド・ファンクションの簡単な例を示します。
前回使用したテストテーブルを利用して、ストアド・ファンクションを作成します。構造は以下の感じです。
CREATE TABLE [dbo].[TABLE_A]( [ID] [int] NOT NULL, [DATA1] [nvarchar](50) COLLATE Japanese_CI_AS NULL, [DATA2] [nvarchar](50) COLLATE Japanese_CI_AS NULL, CONSTRAINT [PK_TABLE_A] PRIMARY KEY CLUSTERED ( [ID] ASC )WITH (PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY] ) ON [PRIMARY]
■スカラ値関数
テストテーブルAを利用した簡単な値を返すストアド・関数です。
与えられた検索用IDでテーブルAの「DATA1」カラムの内容を返す簡単な関数です。
IF OBJECT_ID('dbo.FuncTest2') IS NOT NULL DROP FUNCTION [dbo].[FuncTest2] GO CREATE FUNCTION [dbo].[FuncTest2]( @SrcID NVARCHAR ) RETURNS NVARCHAR(50) BEGIN DECLARE @Des NVARCHAR(50) SELECT @Des = DATA1 FROM TABLE_A WHERE ID = @SrcID RETURN @Des END
SELECT文で実行してやれば、以下のようにします。
======================================
SELECT [dbo].[FuncTest2](1)
======================================
■テーブル値関数その1(インラインテーブル値)
テストテーブルAを利用した簡単な値を返すストアド・関数です。
IF OBJECT_ID('dbo.FuncTest3') IS NOT NULL DROP FUNCTION [dbo].[FuncTest3] GO CREATE FUNCTION [dbo].[FuncTest3]( @SrcID NVARCHAR ) RETURNS TABLE AS RETURN ( SELECT ID, DATA1 FROM TABLE_A WHERE ID = @SrcID )
SELECT文のFROM句で実行してやれば、以下のようにします。
======================================
SELECT * FROM [dbo].[FuncTest3](1)
======================================
■テーブル値関数その2(複数の行を返すテーブル値)
テストテーブルAを利用した複数の行を返すストアド・関数です。
指定されたID以降のデータを全て返す様な動作を行います。
RETURNS句で指定されたTABLEにINSERTしていくことで、結果をテーブルの値として返します。
IF OBJECT_ID('dbo.FuncTest4') IS NOT NULL DROP FUNCTION [dbo].[FuncTest4] GO CREATE FUNCTION [dbo].[FuncTest4]( @SrcID NVARCHAR ) RETURNS @DesTbl TABLE ( ID int primary key NOT NULL, DATA1 nvarchar(255) NOT NULL ) AS BEGIN INSERT @DesTbl SELECT ID, LTRIM( DATA1 ) FROM TABLE_A WHERE ID >= @SrcID /* INSERT @DesTbl VALUES(0, '0000') */ RETURN END
INSERTで追加してやればいろんなことができると思います。上の例ではコメント行になっていますが、 強制的にID「0」を作成することも可能です。
この関数の実行も「テーブル値関数その1」で行った方法と同様です。
■スカラ値関数にUPDATE文を記述
IF OBJECT_ID('dbo.FuncTest2') IS NOT NULL DROP FUNCTION [dbo].[FuncTest2] GO CREATE FUNCTION [dbo].[FuncTest2]( @SrcID NVARCHAR ) RETURNS NVARCHAR(50) BEGIN DECLARE @Des NVARCHAR(50) SELECT @Des = DATA1 FROM TABLE_A WHERE ID = @SrcID UPDATE TABLE_A SET DATA2 = NULL WHERE ID = @SrcID RETURN @Des END
SQL実行結果として以下のエラーメッセージが表示されます。
============================================================
メッセージ 443、レベル 16、状態 15、プロシージャ FuncTest2、行 8
副作用のある演算子または時間に依存する演算子を関数内の 'UPDATE' で使用することはできません。
============================================================
TRY、CATCHを入れてもエラーが表示され使用出来ないようです。
IF OBJECT_ID('dbo.FuncTest2') IS NOT NULL DROP FUNCTION [dbo].[FuncTest2] GO CREATE FUNCTION [dbo].[FuncTest2]( @SrcID NVARCHAR ) RETURNS NVARCHAR(50) BEGIN BEGIN TRY DECLARE @Des NVARCHAR(50) SELECT @Des = DATA1 FROM TABLE_A WHERE ID = @SrcID END TRY BEGIN CATCH END CATCH RETURN @Des END
SQL実行結果として以下のエラーメッセージが表示されます。
============================================================
メッセージ 443、レベル 16、状態 14、プロシージャ FuncTest2、行 6
副作用のある演算子または時間に依存する演算子を関数内の 'BEGIN TRY' で使用することはできません。
メッセージ 443、レベル 16、状態 14、プロシージャ FuncTest2、行 9
副作用のある演算子または時間に依存する演算子を関数内の 'END TRY' で使用することはできません。
メッセージ 443、レベル 16、状態 14、プロシージャ FuncTest2、行 10
副作用のある演算子または時間に依存する演算子を関数内の 'BEGIN CATCH' で使用することはできません。
メッセージ 443、レベル 16、状態 14、プロシージャ FuncTest2、行 11
副作用のある演算子または時間に依存する演算子を関数内の 'END CATCH' で使用することはできません。
============================================================
副作用のある処理をファンクションのなかでは記述できないので、 どうしても処理したいのであれば、ストアド・プロシージャとして作成するしかないようです。
そこで、結果として何かを返したいのであれば、引数に出力属性のものを持つしかありません。
また、プロシージャの中からファンクションを実行することはできますが、ファンクションの中から プロシージャを実行することはできないようです。
ファンクションのコールが入れ子になっていて、深いところでUPDATEなどを行いたい場合、 全てのファンクションをプロシージャにしないといけなくなってきます。
このあたりは、ファンクション、プロシージャの切り分けをしっかりしておく必要がありそうです。
=====
2013/02/14:の時の情報
-
SQL-Serverのテーブルの内容を簡単にインポート・エキスポートできる、ユーティリティ(BCP)の忘備録です。
BCPはコマンドラインで使用するEXEファイルで、MSSQLをインストールした時点でユーティリティとして "C:\Program Files\Microsoft SQL Server\90\Tools\Binn"の様なディレクトリに存在するはずです。
パスも通っているはずですから、DOS窓から「BCP」を実行すれば動作するはずです。
(引数無しで実行するとヘルプが表示されます。以下を参照)C:\>bcp 使用法: bcp {dbtable | query} {in | out | queryout | format} datafile [-m 最大エラー数] [-f フォーマット ファイル] [-e エラー ファイル] [-F 先頭行] [-L 最終行] [-b バッチ サイズ] [-n ネイティブ型] [-c 文字型] [-w UNICODE 文字型] [-N text 以外のネイティブ型を保持] [-V ファイル フォーマットのバージョン] [-q 引用符で囲まれた識別子] [-C コード ページ指定子] [-t フィールド ターミネータ] [-r 行ターミネータ] [-i 入力ファイル] [-o 出力ファイル] [-a パケット サイズ] [-S サーバー名] [-U ユーザー名] [-P パスワード] [-T 信頼関係接続] [-v バージョン] [-R 地域別設定有効] [-k NULL 値を保持] [-E ID 値を保持] [-h "読み込みヒント"] [-x XML フォーマット ファイルを生成]
各種の引数があって、いろいろな使い方ができますが、私が基本的に使っている方法を記します。
尚、実行はMSSQLがインストールされているサーバ上で実行しています。
■テーブルをテキストファイルに出力する
テスト用のデータベースである「TEST」と、そのDB内のテスト用テーブル「TABLE_A」の内容全てを テキストファイル「table_a.txt」に出力する方法です。
C:\>bcp TEST.dbo.TABLE_A out "table_a.txt" -c -S localhost\SQLEXPRESS -T コピーを開始しています... 2 行コピーされました。 ネットワーク パケット サイズ (バイト): 4096 クロック タイム (ミリ秒) 合計 : 1 平均 : (2000.00 行/秒)
出力結果はテーブルのカラムの区切り記号としてタブ文字が使われます。また、文字コードはShift-JISです。
(尚、実行はCドライブのルートで実行しています。) 最後の引数の「-T」は、Windows認証の指定ですが、SQL Server認証の場合は「-T」の代わりに「-U」「-P」を用います。
================================================================================
-U [ユーザ名] -P [パスワード]
================================================================================
■SQLの結果をテキストファイルに出力する
上記の説明で使用したテーブル「TABLE_A」を対象としたSQL文の結果をテキストファイル「table_b.txt」に出力する方法です。
C:\>bcp "SELECT * FROM TEST.dbo.TABLE_A ORDER BY ID" queryout "table_b.txt" -c -Slocalhost\SQLEXPRESS -T コピーを開始しています... 2 行コピーされました。 ネットワーク パケット サイズ (バイト): 4096 クロック タイム (ミリ秒) 合計 : 1 平均 : (2000.00 行/秒)
最初のコマンドの引数と異なるところは、SQL文を指定するところと、処理区分を「queryout」とすることです。
出力結果は、「table_a.txt」とほぼ同じものが出力されます。
■テーブルにテキストファイルをインポートする
上記の説明で使用したテーブル「TABLE_A」にテキストファイル「table_c.txt」の内容をインポートする方法です。
C:\>bcp TEST.dbo.TABLE_A in "table_c.txt" -c -Slocalhost\SQLEXPRESS -T コピーを開始しています... 2 行コピーされました。 ネットワーク パケット サイズ (バイト): 4096 クロック タイム (ミリ秒) 合計 : 63 平均 : (31.75 行/秒)
テキストファイルの内容は、インポートされるテーブルの構造と同じである必要があります。
また、キーの重複があればエラーが発生し、全てのインポート処理は破棄されます。
C:\>bcp TEST.dbo.TABLE_A in "table_c.txt" -c -Slocalhost\SQLEXPRESS -T コピーを開始しています... SQLState = 23000, NativeError = 2627 Error = [Microsoft][SQL Native Client][SQL Server]制約 'PK_TABLE_A' の PRIMARY K EY 違反。オブジェクト 'dbo.TABLE_A' には重複したキーを挿入できません。 SQLState = 01000, NativeError = 3621 Warning = [Microsoft][SQL Native Client][SQL Server]ステートメントは終了されまし た。 BCP コピー in が失敗しました
=====
2013/02/12:の時の情報
-
前回のSQL-Serverの関数およびプロシージャの生成のバッチファイルでの実行方法の忘備録です。
ホストがローカルホストでインスタンス名がSQLEXPRESSでwindows認証の場合、 以下のコマンドを記述したバッチをサーバー上で実行します。
「test.bat」sqlcmd -S .\SQLEXPRESS -i ProcTest.sql sqlcmd -S .\SQLEXPRESS -i FuncTest.sql
「-S」:サーバーの接続先
「-i」:入力ファイルの指定
(この例ではバッチファイルとSQLファイルは同じフォルダに在ります。)
バッチの実行結果です。C:\test>sqlcmd -S .\SQLEXPRESS -i ProcTest.sql データベース コンテキストが 'TEST' に変更されました。 C:\test>sqlcmd -S .\SQLEXPRESS -i FuncTest.sql データベース コンテキストが 'TEST' に変更されました。
「ProcTest.sql」USE [TEST] GO IF OBJECT_ID('dbo.ProcTest') IS NOT NULL DROP PROCEDURE [dbo].[ProcTest] GO ----------------------------------------- -- 階乗を計算する(プロシージャ) ----------------------------------------- CREATE PROCEDURE [dbo].[ProcTest] @SrcNumber DECIMAL --引数 ,@DesNumber DECIMAL OUTPUT --戻り値 AS BEGIN SET @DesNumber = 1 WHILE @SrcNumber > 0 BEGIN SET @DesNumber = @DesNumber * @SrcNumber SET @SrcNumber = @SrcNumber - 1 END END
「FuncTest.sql」USE [TEST] GO IF OBJECT_ID('dbo.FuncTest') IS NOT NULL DROP FUNCTION [dbo].[FuncTest] GO ----------------------------------------- -- 階乗を計算する(関数) ----------------------------------------- CREATE FUNCTION [dbo].[FuncTest]( @SrcNumber DECIMAL --引数 ) RETURNS DECIMAL BEGIN DECLARE @DesNumber DECIMAL SET @DesNumber = 1 WHILE @SrcNumber > 0 BEGIN SET @DesNumber = @DesNumber * @SrcNumber SET @SrcNumber = @SrcNumber - 1 END RETURN @DesNumber END
データベース名を「TEST」として実行しています。
尚、関数・プロシージャとも内容の変更が有った場合を想定して、DROP命令で最初に削除しています。
=====
2012/06/13:の時の情報