ODBC、OLEDB、ADO

歴史

ODBC → OLEDB → ADO
という進化

新しい技術は過去の技術を内包している
内部的には古い技術を用いている
odbc_oledb_ado

ODBC

Open Database Connectivity
DB接続用コンポーネント
・ SQLServer
・ Oracle
等のDBMSだけでなく、
・ MicrosoftACCESS
・ CSV
等へも共通の記述で接続できる。
各リソースにおけるODBCドライバは各ベンダーが開発したものを公開している。
データベース等をインストールすると自動でODBCドライバーがインストールされる場合も多い
Java系:JDBC
MS系:ODBC

ODBCデータソースアドミニストレータ

Windowsの場合、ODBCドライバーをデータソースとして登録し、プログラムから参照できる
ODBCデータソースアドミニストレータは32bit/64bitがあり、
それぞれ32bit/64bitのODBCドライバーを登録する。
なおODBCドライバーを利用するアプリケーションの32bit/64bitに合わせたドライバー及びデータソース登録が必要

odbc1
「システムDSN」タブで登録を行う。
odbc2

接続文字列

データソース登録時(ODBCドライバーは必要)
Dsn=(登録名);
UID=myuser;
PWD=mypass;
データソース未登録時(ODBCドライバーは必要)
Driver={~};
UID=myuser;
PWD=mypass;

OLEDB

DB接続用コンポーネント
COM
ODBCの後続技術
ODBC同様、各リソースにおけるソフトウェアは各ベンダーが開発したものを公開している。

OLEDBではプロバイダと呼ばれるサービスとしてデータを仲介する
DBMS
↑↓
プロパイダ
↑↓
クライアント
OLEDB利用時の接続文字列
Provider=~;
Data Source=MyServer;
User ID=myuser;
Password=mypass

ADO

ActiveX Data Objects
データベースアクセスの為のソフトウェアコンポーネント
「OLE DB」をActiveXコントロールの形で使用できる
VB、VBA、VBScript、他言語から共通の方法でDBへアクセスできる

Excel VBA

アプリケーション

アプリケーション操作

最小化
ActiveWindow.WindowState = xlMinimized

警告メッセージ表示・非表示

シートの削除、ブックの終了時等
Application.DisplayAlerts = False
Application.DisplayAlerts = True

遂次実行

VBA上での画面操作(行削除やテキスト変更等)を実際の画面で反映させる・させないの設定
Application.ScreenUpdating = False
Application.ScreenUpdating = True

Book

ActiveBookパス
ActiveWorkbook.Path

Book操作

新規Book開く
Workbooks.Add

既存Bookを開く
Workbooks.Open ("C:\Time.csv")
Set ObjTime = ActiveWorkbook.Worksheets(1)

保存して閉じる
ActiveWorkbook.Close SaveChanges:=True
Set ObjTime = Nothing

CSV形式で保存
ActiveWorkbook.SaveAs SavePath & "\" & "TEST", Excel.XlFileFormat.xlCSV

TEXT形式で保存
ActiveWorkbook.SaveAs SavePath & "\" & "TEST", Excel.XlFileFormat.xlCurrentPlatformText
ActiveWorkbook.SaveAs SavePath & "\" & "TEST", Excel.XlFileFormat.xlNormal

保存せずに終了
ActiveWorkbook.Close SaveChanges:=False

シート

Sheet情報

シート数
1枚の時:1。0という事はない
ReturnValue = ActiveWorkbook.Sheets.Count
ReturnValue = 1

シート名
ReturnValue = ActiveWorkbook.Worksheets(1).Name
ReturnValue = "Sheet1"

シート操作

新シート追加
ActiveWorkbook.Sheets.Add

全シートを削除
ActiveWorkbook.Sheets.Delete

シートコピー
ActiveSheets.Copy After:=Worksheets(1)
ActiveSheets.Copy Before:=Worksheets(1)

Book間シートコピー
ActiveWorkBook.WorkSheets(1).Copy After:=Workbooks("~").Worksheets(1)
ActiveWorkBook.WorkSheets(1).Move Before:=Workbooks("~").Worksheets(1)

行・列操作

~.Rows(1:1).Copy '行のコピー
~.Rows(1:1).Cut '行の切り取り
~.Columns("A:D").Copy '列のコピー
~.Rows(2:2).Insert '↑内容の貼り付け
先頭行の固定
~.Rows(2:2).Select
~.ActiveWindow.FreezePanes = True

~.Columns("A:D").ColumnWidth = 20 '列幅
~.Rows("2:2").RowHeight = 20 '行高
~.Rows(5).EntireColumn.AutoFit '列幅自動調整
~.Columns(5).EntireRow.AutoFit '行高自動調整

ヘッダー・フッター

With ActiveSheet.PageSetup
 .LeftHeader = Range("A1").Value 'ヘッダー文字
 .CenterHeader = Range("A2").Value
 .RightHeader = Range("A3").Value
 .LeftFooter = Range("A4").Value 'フッター文字
 .CenterFooter = Range("A5").Value
 .RightFooter = Range("A6").Value
End With

抽出

Range("A1:B5000").AdvancedFilter
 Action:=xlFilterCopy, _
 CriteriaRange:=Range("F1:F2"), _ 'CriteriaRange … 検索条件
 CopyToRange:=Range("F4"), _ 'CopyToRange … 検索結果出力先
 Unique:=False

ソート

Cells(1, 1)、Cells(1, 2)順に並び変え
Range("A1:E5").Sort key1:=Cells(1, 1), _ 'キー
 order1:=xlAscending, _ '昇順
 key2:=Cells(1, 2), _
 order2:=xlAscending

フィルタリング

Range("A1:D500").AutoFilter
 Field:=2, _  'フィルタリング対象列番号
 Criteria1:=Array("1","2","3"), _  'Variant型の配列(↓参照)
 Operator:=xlFilterValues
※Dim Test(2) As Integer
Test(0) = 1 : Test(1) = 2 : Test(2) = 3
の時、Array("1","2","3") = Test

重複削除

ActiveSheet.Range("A1:D500").RemoveDuplicates Columns:=1, Header:=xlNo

オートシェイプ

絶対位置を取得。オートシェイプを配置する。
SX = Obj.Cells(2, 2).Left
SY = Obj.Cells(2, 2).Top + Obj.Cells(2, 2).Height / 2
EX = Obj.Cells(2, 5).Left
EY = Obj.Cells(2, 5).Top + Cells(2, 5).Height / 1
Set MyLine = Obj.Shapes.AddLine(SX, SY, EX, EY) '描画

点線の矢印
Obj.Shapes.Range(MyLine.Name).Line.EndArrowheadStyle = msoArrowheadTriangle '矢印に
Obj.Shapes.Range(MyLine.Name).Line.Weight = 0.75 '細さ
Obj.Shapes.Range(MyLine.Name).Line.DashStyle = msoLineDash '点線に

セル

線引き

With Cells(1,1)
 .Borders(xlEdgeTop).LineStyle = xlLineStyleNone '線を消す
 .Borders(xlEdgeTop).LineStyle = xlContinuous '上部に線を引く
 .Borders(xlEdgeBottom).LineStyle = xlContinuous '下部に線を引く
 .Borders(xlEdgeLeft).LineStyle = xlContinuous '左部に線を引く
 .Borders(xlEdgeRight).LineStyle = xlContinuous '右部に線を引く
 .Borders.LineStyle = xlContinuous '格子状に線を引く
 (例) Range(Cells(2,2),Cells(4,4)).Borders.LineStyle = xlContinuous

 .Borders.Weight = xlMedium '周りの線を太線に
 .Borders.Weight = xlThick '周りの線を極太線に

 線引きと色付けをまとめて
 Call .Range("A5").BorderAround(
 LineStyle:=xlContinuous, _
 Weight:=xlThick, _
 Color:=vbRed)
End With

結合

セルを結合する
Cells(1,1).Range(Cells(2,2),Cells(7,2)).Merge
結合しているか否か?
ReturnValue = Cells(1,1).Cells(2,2).MergeCells
ReturnValue = True
選択状態のセルの情報を取得
ReturnValue = Sellection.Row
ReturnValue = 2
(結合されている場合最上位行)
↑の法則を利用して結合セルの内容を取得
ReturnValue = Cells(Sellection.Row,2)
ReturnValue = "Header"

セル操作

選択状態に
Cells(3,2).Select

表の端のセルを参照
ReturnValue = Cells(2, 2).End(xlDown).Row
ReturnValue = 7
xlUp / xlDown / xlToLeft / xlToRight

指定のセルの相対位置のセルを参照
ReturnValue = Cells(2, 2).OffSet(5,0)
ReturnValue = Range("A5").OffSet(5,5)
ReturnValue = 7

Excelの関数を使用
Application.WorksheetFunction.~
Application.WorksheetFunction.SUM()
Application.WorksheetFunction.COUNT

通常方式のセル位置の文字列を取得

ReturnValue = .Cells(2, 2).Address
≠R1C1参照形式
ReturnValue = "$B$2"

書式

With Cells(1,1)
 .NumberFormatLocal = "[h]:mm" 時間設定(9:00)
 .NumberFormatLocal = "hh:mm" '09:00
 .NumberFormatLocal = "@" '文字列

 .VerticalAlignment = xlVAlignCenter 'センター表示
 .HorizontalAlignment = xlHAlignCenter 'センター表示
 .WrapText = True '折り返して全体を表示する
 .Orientation = 0 '方向の角度
 .AddIndent = True '前後にスペースを入れる
 .IndentLevel = 0 'インデントの数値
 .ShrinkToFit = True '縮小して全体を表示する
 .ReadingOrder = xlContext '文字の方向

 .Cells(1,1).Interior.Color = VbRed 背景色

 .Cells(1,1)Font.Size = 5 '文字サイズ
 .Cells(1,1)Font.Bold = True '太字
 .Cells(1,1)Font.Color = VbRed '文字色
End With

プログラム言語 エンコード・デコード処理

エンコードとは

符号化
文字に対してコンピュータで扱う文字コード=コード(数値)を割り振る事
テキストエディタで文字データを作成・保存した際、
OS毎の割り振られる文字コードは以下の通り。
・Windows:Shift_JIS
・UNIX系:EUC
・Mac:Unicode
テキストエディタの機能によって保存時の文字コードを変更する事は可能
OSに関わらずUTF-8が国際的に標準化されつつある。
特にHTML5では推奨される。
漢字をUTF-8で保存すると容量が増えるという問題点あり
XMLでは、XML宣言でエンコードの種類を明示的に表記する
<?xml version="1.0" encoding="Shift_JIS"?>
<a>~</a>

割り振られた文字コードを解析して読み込む事をデコードと言う。
割り振られた文字コードと違う文字コードで読み込んだ場合、文字化けする。

読込

string[] s;
s = File.ReadAllLines(@"D:\~txt", Encoding.Default);
s = File.ReadAllLines(@"D:\~.txt", Encoding.GetEncoding(name: "Shift_JIS"));
s = File.ReadAllLines(@"D:\~.txt", Encoding.Unicode);
s = File.ReadAllLines(@"D:\~.txt", Encoding.UTF8);

書込

File.WriteAllLines(@"D:\~txt", @"内容", Encoding.Default);
File.WriteAllLines(@"D:\~.txt", @"内容", Encoding.GetEncoding(name: "Shift_JIS"));
File.WriteAllLines(@"D:\~.txt", @"内容", Encoding.Unicode);
File.WriteAllLines(@"D:\~.txt", @"内容", Encoding.UTF8);

BASE64エンコード・デコード

BASE64とは?
バイナリデータ→テキストデータ
への変換方式
英数字のみで構成される
暗号ではない

import java.io.IOException;
import org.apache.commons.codec.binary.Base64;

public class base64test {
 public static void main(String[] args) {
  try{
   String after = encodeBase64("~");
  }
  catch ( ~ ){
   ~
  }

 }
 
 public static String encodeBase64(String before) throws IOException {
  byte[] out = Base64.encodeBase64(before.getBytes());
  return new String(out, "shift_jis");
 }
}

Sub main
 after = encode("~")
 after = decode("~")
End Sub

'Base64文字列にエンコードする
Public Function encode(val As String) As String

 Dim objBase64 As Object
 Dim b() As Byte
 b = stringToByte(val)
 
 'Msxml2.DOMDocumentオブジェクト = XML操作オブジェクト
 Set objBase64 = CreateObject("MSXML2.DOMDocument").createElement("b64")
 objBase64.DataType = "bin.base64"
 objBase64.nodeTypedValue = b
 encode = objBase64.text
 
 Set objBase64 = Nothing

End Function

'Base64文字列をデコードする
Public Function decode(val As String) As String
 Dim objBase64 As Object
 'Msxml2.DOMDocumentオブジェクト = XML操作オブジェクト
 Set objBase64 = CreateObject("MSXML2.DOMDocument").createElement("b64")
 objBase64.DataType = "bin.base64"
 objBase64.text = val
 
 Dim b() As Byte
 b = objBase64.nodeTypedValue
 decode = byteToString(b)
 
 Set objBase64 = Nothing

End Function

'文字列バイト列に変換する
Public Function stringToByte(ByVal strData As String) As Byte()

 Dim objStream As Object
 Set objStream = CreateObject("ADODB.Stream")
 
 objStream.Open
 objStream.Type = adTypeText
 objStream.Charset = "utf-8"
 objStream.WriteText strData
 
 objStream.Position = 0
 objStream.Type = adTypeBinary
 objStream.Position = 3
 stringToByte = objStream.Read
 
 objStream.Close
 Set objStream = Nothing
 
End Function

プログラム言語 XML操作

作成

文字列指定

using System.Xml;

const string url = "http://example.com/";
var doc = new XmlDocument();

親要素・属性
var root = doc.CreateElement("root", url);
var att = doc.CreateAttribute("att");
att.AppendChild(doc.CreateTextNode("sample"));
root.Attributes.Append(att);

子要素
var child1 = doc.CreateElement("child1", url);
child1.AppendChild(doc.CreateTextNode("sample1"));
root.AppendChild(child1);

var child2 = doc.CreateElement("child2", url);
child2.AppendChild(doc.CreateTextNode("sample2"));
root.AppendChild(child2);

doc.AppendChild(root);

Console.WriteLine(doc.OuterXml());

親要素・属性・子要素をまとめて構築
doc.LoadXml("<root att='sample' xmlns='http://example.com/'><child1>sample</child1><child2>sample</child2></root>");

Console.WriteLine(doc.OuterXml());

結果
<root att="sample" xmlns="http://example.com/">
 <child1>sample</child1>
 <child2>sample</child2>
</root>

using System.Xml.Linq;

var doc = new XDocument();
XNamespace name = "http://example.com/";
var root = new XElement(name + "root");
var attRoot = new XAttribute("attRoot", "value");
var elm1 = new XElement(name: "elm1", content: "value1");
var elm2 = new XElement(name: "elm2", content: "value2");
var attElm = new XAttribute("attElm", "value");

root.Add(elm1);
root.Add(elm2);
elm2.Add(attElm);
root.Add(attRoot);
doc.Add(root);

Console.WriteLine(doc.ToString());

XNamespace name = "http://example.com/";
var root = new XElement(
 name + "root", new XAttribute("att", "valueAtt"),
 new XElement("elm1", "value1"),
 new XElement("elm2", new XAttribute("attElm", "value"), "value2"));

Console.WriteLine(root.ToString());

結果
<root attRoot="value" xmlns="http://example.com/" >
 <elm1>value</elm1>
 <elm2 attElm="value">value</elm2>
</root>

文字列→XML変換

var doc = XDocument.Parse("<root xmlns='https://office-yone.com/'><a>システム開発</a><b>ホームページ作成</b></root>");

結果
Console.WriteLine(doc.ToString());
<root xmlns='https://office-yone.com/'>
 <a>システム開発</a>
 <b>ホームページ作成</b>
</root>

読取

値で検索

var doc = XDocument.Parse("<root xmlns='https://office-yone.com/'><a>システム開発</a><b type="web">ホームページ作成</b><c><d>インフラ構築</d></c></root>");

XNamespace name = "https://office-yone.com/";
int count = doc.Descendants(name + "a").Count();
count:1

int count = doc.Descendants(name + "b").Where( x => x.Value == "ホームページ作成").Count();
count:1

int count = doc.Descendants(name + "b").Where( x => x.Attribute("type").Value == "web").Count();


<root xmlns='https://office-yone.com/'>
 <a>システム開発</a>
 <b type="web">ホームページ作成</b>
 <c>
  <d>インフラ構築</d>
 </c>
</root>

要素名で検索

var doc = XDocument.Parse("<a><b><c>WordPress</c></b></a>");
string val = doc.Element("a").Element("b").Element("c").Value;
val:WordPress

<a>
 <b>
  <c>WordPress</c>
 </b>
</a>

var doc = XDocument.Parse("<a><b><c>C#</c></b><b><c>Java</c></b></a>");
foreach (var elm in doc.Element("a").Elements("b").Elements("c"))
{
 Console.WriteLine(elm.Value);
};
→C# Java

<a>
 <b>
  <c>C#</c>
 </b>
 <b>
  <c>Java</c>
 </b>
</a>

VB6とVSSを連動させる方法

VB6とVSSのインストールを行ったアカウントが異なる場合、
VB6からチェックアウト等の処理が行えないという現象が発生する。
以下、対処方法。

「VisualBasic」を終了
コマンドプロンプトにて「REGSVR32.exe」を実行。
↓ (1)~(3)のDLLをレジストリに登録する。
(1)VBSCC.dll
(場所: ~\Microsoft Visual Studio\VB98 )
(2)SSSCC.dll
(場所: ~\Microsoft Visual \Common\VSS\Win32 またはVSSインストールフォルダ等)
(3)SSAPI.dll
(場所: ~\Microsoft Visual Studio\Common\VSS\Win32 またはVSSインストールフォルダ等)
実行例 REGSVR32.EXE C:\Microsoft Visual Studio\VB98\VBSCC.DLL

C:\WINDOWS\VBADDIN.ini
の内容を書き換え(または追加)
VBSCC=0

VBSCC=3

VB6でマウスホイールによるスクロールを有効にする方法

Microsoft公式サイトからVB6MouseWheel.EXE パッケージをダウンロード

(1)VB6MouseWheel.EXE を適当な場所に解凍(例 C:\VB6MouseWheel\)

(2)コマンドプロンプトにてレジストリ登録
regsvr32 C:\VB6MouseWheel\VB6IDEMouseWheelAddin.dll

(3)VB6設定
・VB6.0を起動。
・[アドイン] →[アドイン マネージャ] →「MouseWheel Fix」を選択
・[ロード/アンロード]、[起動時にロード]にチェック
・[OK] で完了

縦横比を維持したままウィンドウサイズを変更する方法

using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;

// ウィンドウサイズ変更イベント
protected override void WndProc(ref System.Windows.Forms.Message m)
{
  this.AspectRatioSizeWndProc(ref m, 4 / 3, true);
  base.WndProc(ref m);
}

private struct WmRect
{
  public int Left;
  public int Top;
  public int Right;
  public int Bottom;
  
  public static implicit operator Rectangle(WmRect r)
  {
    return new Rectangle(r.Left, r.Top, r.Right – r.Left, r.Bottom – r.Top);
  }
  
  public static implicit operator WmRect(Rectangle r)
  {
    return new WmRect
    {
      Top = r.Top,
      Bottom = r.Bottom,
      Left = r.Left,
      Right = r.Right
    };
  }
  
  public override string ToString()
  {
    return string.Format(
      ”Left {0}, Top {1}, Right {2}, Bottom {3}”,
      this.Left.ToString(),
      this.Top.ToString(),
      this.Right.ToString(),
      this.Bottom.ToString());
  }
}

public enum WmSz
{
  Left = 1,
  Right = 2,
  Top = 3,
  Bottom = 6,
  
  TopLeft = Top + Left,
  TopRight = Top + Right,
  BottomLeft = Bottom + Left,
  BottomRight = Bottom + Right,
}

///

/// ウィンドウ・プロシージャ内で呼び出すことで、
/// マウスでウィンドウの縁をドラッグしてフォームのサイズを変更したときに、
/// ウィンドウサイズが指定したアスペクト比を維持するようになる。
///

/// サイズが変更されるウィンドウ /// ウィンドウ・プロシージャに渡されたWindowsメッセージ /// ウィンドウサイズのアスペクト比(幅/高さ) /// クライアント領域のアスペクト比を一定に保つようにする場合はtrue。
/// falseを指定すると、ウィンドウの境界線やタイトルバーも含めたウィンドウ全体のサイズのアスペクト比を保つようにする。>
public static void AspectRatioSizeWndProc(this Form form, ref Message m, float aspect, bool clientSize)
{
  const int WM_SIZING = 0x214;
  
  if (m.Msg == WM_SIZING)
  {
    if (aspect > 0f)
    {
      //画面上での、ウィンドウの上下左右の座標
      WmRect rc = (WmRect)Marshal.PtrToStructure(m.LParam, typeof(WmRect));
      WmSz res = (WmSz)m.WParam.ToInt32();
      
      switch (res)
      {
        case WmSz.Left:
        case WmSz.Right:
          {
            int w = rc.Right – rc.Left;
            int h;
            if (clientSize)
            {
              Size borders = Size.Subtract(form.Size, form.ClientSize);
              
              w -= borders.Width;
              h = (int)(w / aspect) + borders.Height;
            }
            else
            {
              h = (int)(w / aspect);
            }
            
            rc.Bottom = rc.Top + h;
            Marshal.StructureToPtr(rc, m.LParam, true);
            
            break;
          }
        case WmSz.Top:
        case WmSz.Bottom:
          {
            int h = rc.Bottom – rc.Top;
            int w;
            
            if (clientSize)
            {
              Size borders = Size.Subtract(form.Size, form.ClientSize);
              h -= borders.Height;
              w = (int)(h * aspect) + borders.Width;
            }
            else
            {
              w = (int)(h * aspect);
            }
            rc.Right = rc.Left + w;
            break;
          }
        case WmSz.TopLeft:
        case WmSz.TopRight:
          {
            int recW = rc.Right – rc.Left;
            int recH = rc.Bottom – rc.Top;
            int w, h;
            
            if (clientSize)
            {
              Size borders = Size.Subtract(form.Size, form.ClientSize);
              recW -= borders.Width;
              recH -= borders.Height;
              
              w = (int)(recH * aspect) + borders.Width;
              h = (int)(recW / aspect) + borders.Height;
            }
            else
            {
              w = (int)(recH * aspect);
              h = (int)(recW / aspect);
            }
            
            int dh = recW * recW + h * h;
            int dw = recH * recH + w * w;
            
            if (dh > dw)
            {
              rc.Top = rc.Bottom – h;
            }
            else
            {
              if (res == WmSz.TopLeft)
              {
                rc.Left = rc.Right – w;
              }
              else if (res == WmSz.TopRight)
              {
                rc.Right = rc.Left + w;
              }
            }
            
            break;
          }
        
        case WmSz.BottomLeft:
        case WmSz.BottomRight:
          {
            int recW = rc.Right – rc.Left;
            int recH = rc.Bottom – rc.Top;
            
            int w, h;
            
            if (clientSize)
            {
              Size borders = Size.Subtract(form.Size, form.ClientSize);
              recW -= borders.Width;
              recH -= borders.Height;
              
              w = (int)(recH * aspect) + borders.Width;
              h = (int)(recW / aspect) + borders.Height;
            }
            else
            {
              w = (int)(recH * aspect);
              h = (int)(recW / aspect);
            }
            
            int dh = recW * recW + h * h;
            int dw = recH * recH + w * w;
            
            if (dh > dw)
            {
              rc.Bottom = rc.Top + h;
            }
            else
            {
              if (res == WmSz.BottomLeft)
              {
                rc.Left = rc.Right – w;
              }
              else if (res == WmSz.BottomRight)
              {
                rc.Right = rc.Left + w;
              }
            }
            break;
          }
        default:
          break;
      }
      Marshal.StructureToPtr(rc, m.LParam, true);
    }
  }
}

プログラム言語 属性

概要

クラスやメンバーの付加情報
デバック中に機能するもの、コンパイル中に機能するもの、実行中に機能するものと様々。
クラス利用者へのメッセージ・指示、コンパイラへの指示等。

エントリポイント

///

/// アプリケーションのメイン エントリ ポイントです。
///

[STAThread]
static void Main()
{
Application.Run(new Form1());
}

スレッド種類

[STAThread]
メソッドがシングルスレッドアパートメントである事を明示
元々WindowsのCOMコンポーネントは、STA(SingleThreadApartment)でないとエラーになる為、
Mainメソッドにはこの属性が付与されている。

別スレッドにてActiveXコントロールを使用する際には以下の設定が必要。
Thread other = new Thread(start: new ThreadStart(非同期処理));
other.SetApartmentState(ApartmentState.STA);
other.Start();

コーディング時の警告

[Obsolete("警告:代わりに~を使って下さい。")]
public static void OldMethod(){}

[Obsolete("エラー:代わりに~を使って下さい。", true)]
public static void VeryOldMethod(){}

public static void Main()
{
  // コンパイル時:警告。「警告:代わりに~を使って下さい。」
  OldMethod();
  
  // コンパイル時:エラー。「エラー:代わりに~を使って下さい」
  VeryOldMethod();
}

プログラム言語 非同期処理

用語解説

非同期処理

複数の別の処理を同時に行う。
マルチコア環境(複数のCPUで処理する)の場合、高速化する可能性あり。

並列(並行)処理

単一の処理を複数に分割して行う。

プロセス

プログラムの実行単位
プロセスは1つ以上のスレッド、固有のヒープメモリ領域等で構成される。
起動や切り替えに時間が掛かる。
複数プロセスに対する処理時間の割り当てはOSが行う。
具体的にはプロセス内のスレッドに対して処理を割り当てる。

スレッド

CPUの利用単位
スレッド単位でスタック、CPUレジスタのコピーを持つ
ヒープメモリ領域は同一プロセス内のスレッドで共用する
起動や切り替えが速い。
複数スレッドに対する処理時間の割り当てはOSが行う。

UIスレッド
=メインスレッド
※エンド・ユーザーからの入力を受け付けたり、UIを描画したりするスレッド
ワーカースレッド
UIスレッドのバックグラウンドで動作する別スレッド

非同期処理(マルチスレッド)のメリット

プロセスA
 スレッド1
 スレッド2
プロセスB
 スレッド1
だとプロセスAの方が多くの処理時間が割り当てられる。
よってマルチスレッド処理は有用。

同期制御

複数のスレッドがタイミングを計りながらリソースに対してアクセスする事。

プリエンプティブ

マルチタスク、マルチスレッドの処理におけるスケジューリング方法の1つ。
タスク(スレッド)が動作中であろうが一定時間が経過すると強制的に処理を別タスクへ移す。

強調的(ノン・プリエンプティブ)

各タスクが自己申告で都合の良い場所まで処理を終えてから処理を別タスクへ移す。
実際にはシステムコールを呼び出すタイミングで処理が移される。
多段フィードバックキュー
・短いタスクを優先する
・FIFO(FirstInFirstOut)方式で最初にスケジューリングされたタスクを優先する
等の優先順位の重みに違いがあるプリエンプティブ方式

スレッドセーフ

1つのデータ(オブジェクト)に対して、複数のスレッドが同時にアクセスした場合、データの不整合が発生する。
排他制御を施し、この現象が発生しないクラスのこと。
スレッドセーフなクラスは継承禁止にすべき。
排他制御処理は時間が掛かる為、無用な使用は控えるべき。

スタック

FIFO方式で使用するメモリ領域。
スタックポインタ(SP)レジスタにスタックのアドレスを保持する事で、
スタックの読み込み先、書き込み先を判定する。
ローカル変数、関数の戻り先等に使用。

マルチスレッドにおけるスタック利用

スレッドを別に作成する毎にスタック領域が確保される(デフォルト:1MB)。
スレッドの切り替え時にはCPUレジスタの内容を退避/復帰する必要がある為、退避用メモリ領域も必要(デフォルト:1MB)。
容量及び、確保に時間を要するのでマルチスレッドはコストが高いと言われる。

スレッドプール

スレッド用のプログラムを保持しておくメモリ領域。
マルチスレッド用のスタック領域を破棄せずに使いまわす事で容量及び処理時間を短縮する目的で使用される。
.NET4.0以前
ThreadPool.QueueUserWorkItemメソッドを利用
(省略)
.NET4.0以降
=Taskクラスを利用したスレッド

実例

別スレッド呼び出し(引数無し)

以下の内容は現在ではメリット無し
非同期処理はスレッドプール(Taskクラス)を使う。

static void Main(string[] args)
{
  ThreadStartデリゲートにメソッドを指定
  Thread other1 = new Thread(start: new ThreadStart(OtherThread1));
  other1.Start();
  
  パラメータ用ThreadStartデリゲートにメソッドを指定
  Thread other2 = new Thread(start: OtherThread1);
  other2.Start();
  
  ラムダ式
  Thread other3 = new Thread(() => OtherThread1());
  other3.Start();
}

public static void OtherThread1()
{
  for (int i = 0; i < 10; i++)
  {
    Console.WriteLine(@”Other!”);
  }
}

var myTask = new Task(() => {
 非同期処理
});
myTask.Start();

new Task( () =>
{
 非同期処理
}).Start();

Task.Run(() =>
{
 非同期処理
});

var ret = Task.Run(() =>
{
 非同期処理
 return 5;
});

ret.Result:5

static async Task<string> myAsyncAwait()
{
 await Task.Run(() =>
 {
  非同期処理
 });
 return @"AAA";
}

var ret = myAsyncAwait().Result;
ret:AAA

Action func = async () =>
{
 await Task.Run(() =>
 {
  非同期処理
 });
};

func();

var html = Task.Run(async () => {
 return await new HttpClient().GetStringAsync(@"https://office-yone.com/");
});

html.Result:<html>~

Windowsコントロールへの非同期処理反映

SampleAsync();
Invoke等の処理は不要

public async void SampleAsync() {
 this.Windowsコントロール.~ = "実行中";
 await Task.Run( () => {
  非同期処理
 } );
 this.Windowsコントロール.~ = "完了";
}

public class ThreadA extends Thread{
 @Override
 public void run(){
  try{
   for (int i=0; i<10; i++){
    System.out.println( "A" );
    Thread.sleep(100);
   }
  }catch(InterruptedException e){
   System.out.println(e.getMessage());
  }

 }
}

public class ThreadB extends Thread{
 public void run(){
  try{
   for (int i=0; i<10; i++){
    System.out.println( "B" );
    Thread.sleep(100);
   }
  }catch(InterruptedException e){
   System.out.println(e.getMessage());
  }

 }
}

public class App{
 public static void main( String[] args )<{
  new ThreadA().start();
  new ThreadB().start();
 }
}

→A B B A A B B A A B A B B A B A B A A B

public class ThreadA implements Runnable{
 public void run(){
  try{
   for (int i=0; i<10; i++){
    System.out.println( "A" );
    Thread.sleep(100);
   }
  }catch(InterruptedException e){
   System.out.println(e.getMessage());
  }

 }
}

public class ThreadB implements Runnable{
 public void run(){
  try{
   for (int i=0; i<10; i++){
    System.out.println( "B" );
    Thread.sleep(100);
   }
  }catch(InterruptedException e){
   System.out.println(e.getMessage());
  }
 }
}

public class App{
 public static void main( String[] args ){
  new Thread(new ThreadA()).start();
  new Thread(new ThreadB()).start();
 }
}

→A B B A B A B A A B B A B A B A A B A B

別スレッド呼び出し(引数有り)

以下の内容は現在ではメリット無し
非同期処理はスレッドプール(Taskクラス)を使う。

  ラムダ式
  Thread other4 = new Thread(() => OtherThread2(@”4″));
  //other4.Start();

  匿名メソッド
  new Thread(start: delegate()
  {
    OtherThread2(“5”);
  }).Start();

  パラメータ用ThreadStartデリゲートに引数を渡す
  Thread other6 = new Thread(start: OtherThread3);
  other6.Start(parameter: @”6″);

  for (int i = 0; i < 10; i++)
  {
    Console.WriteLine(@”Main!”);
  }
}

static void OtherThread2(string msg)
{
  for (int i = 0; i < 10; i++)
  {
    Console.WriteLine(msg);
  }
  Console.ReadKey();
}

static void OtherThread3(object msg)
{
  ※ パラメータ用ThreadStartデリゲートに引数の引数は変換が必要
  string myMsg = (string)msg;
  for (int i = 0; i < 10; i++)
  {
    Console.WriteLine(myMsg);
  }
  Console.ReadKey();
}

public class MyThread extends Thread{
 private String _s;
 public MyThread(String s){
  this._s = s;
 }
 public void run(){
  System.out.println( this._s );
 }
}

public class App{
 public static void main( String[] args ){
  new MyThread("Java").run();
 }
}

public class MyThread implements Runnable{
 private String _s;
 public MyThread(String s){
  this._s = s;
 }
 public void run(){
  System.out.println( this._s );
 }
}

public class App{
 public static void main( String[] args ){
  new Thread(new MyThread("Java")).start();
 }
}

スレッドの終了を待つ

using System.Threading.Tasks;

var myTask = new Task( () =>
{
 Console.WriteLine("myTask : start");
 System.Threading.Thread.Sleep(1000);
 Console.WriteLine("myTask : end");

});

Console.WriteLine("program : start");
myTask.Start(); タスク開始
myTask.Wait(); タスクの終了を待つ
Console.WriteLine("program : end");


program : start
myTask : start
myTask : end
program : start

非同期処理が成功してから処理を開始する
var myTask = new Task(() =>
{
 Console.WriteLine("myTask1 : start");
 System.Threading.Thread.Sleep(5000);
 Console.WriteLine("myTask1 : end");

});

myTask.Start();
↑ スレッドの終了後に開始
myTask.ContinueWith(_ =>
{
 Console.WriteLine(@"myTask was finished.");
});

Console.WriteLine(@"program : end");
↑ スレッドの終了を待たずに実行される。
↑ スレッドは非同期

var myTask = new Task(() =>
{
 Console.WriteLine("myTask1 : start");
 System.Threading.Thread.Sleep(5000);
 Console.WriteLine("myTask1 : end");

});

myTask.Start();
myTask.ContinueWith(_ =>
{
 Console.WriteLine(@"myTask was finished.");
});
↑ スレッドの終了を待つ。
Task.WaitAll(myTask);
Console.WriteLine(@"program : end");

複数のスレッドの終了を待つ

var myThread1 = new Thread( () =>
{
 Console.WriteLine("myThread1 : start");
 System.Threading.Thread.Sleep(1000);
 Console.WriteLine("myThread1 : end");

});

var myThread2 = new Thread(() =>
{
 Console.WriteLine("myThread2 : start");
 System.Threading.Thread.Sleep(2000);
 Console.WriteLine("myThread2 : end");

});

Console.WriteLine("program : start");
myThread1.Start();
myThread2.Start();

myThread1.Join();
myThread2.Join();

Console.WriteLine("program : end");
スレッドが2つとも終了してから処理される

※タスククラスを使用した場合1
using System.Threading.Tasks;

var myTask1 = new Task(() =>
{
 Console.WriteLine("myTask1 : start");
 System.Threading.Thread.Sleep(1000);
 Console.WriteLine("myTask1 : end");

});

var myTask2 = new Task(() =>
{
 Console.WriteLine("myTask2 : start");
 System.Threading.Thread.Sleep(2000);
 Console.WriteLine("myTask2 : end");

});

Console.WriteLine("program : start");
myTask1.Start();
myTask2.Start();
Task.WaitAll(myTask1, myTask2);

Console.WriteLine("program : end");
スレッドが2つとも終了してから処理される

※タスククラスを使用した場合2
using System.Threading.Tasks;

Parallel.Invoke(() =>
{
 Console.WriteLine("myTask1 : start");
 System.Threading.Thread.Sleep(1000);
 Console.WriteLine("myTask1 : end");

},
() =>
{
 Console.WriteLine("myTask2 : start");
 System.Threading.Thread.Sleep(2000);
 Console.WriteLine("myTask2 : end");

});

Console.WriteLine("program : end");
スレッドが2つとも終了してから処理される

排他制御

複数のスレッドから共通のリソース(データ)にほぼ同時にアクセスする事によるデータの不整合を制御する事

using System.Threading.Tasks;

非同期処理をロックしない場合
static void myThread(int prm)
{
 Console.WriteLine(@"[");
 Thread.Sleep(prm * 1000);
 Console.WriteLine(@"]");
}

static void Main(string[] args)
{

 Parallel.Invoke(
  () => myThread(prm: 1),
  () => myThread(prm: 2)
 );
}
→ [ [ ] ]

非同期処理をロックする場合
private static object myLock = new object();
static void myThread(int prm)
{
 lock (myLock)
 {
  Console.WriteLine(@"[");
  Thread.Sleep(prm * 1000);
  Console.WriteLine(@"]");
 }
}

static void Main(string[] args)
{

 Parallel.Invoke(
  () => myThread(prm: 1),
  () => myThread(prm: 2)
 );
}
→ [ ] [ ]

public class FamilyTread extends Thread {
 private Bathroom room;
 private String name;

 public FamilyTread(Bathroom room, String name) {
  this.room = room;
  this.name = name;
 }

 public void run() {
  this.room.in(this.name);
 }
}

public class Bathroom {
 synchronized付与により、メソッドを同時呼び出し不可に
 public synchronized void in(String name) {
  System.out.println(name + ":enter");
  try {
   System.out.println(name + ":enterd");
   3秒待機
   Thread.sleep(3000);
  } catch (InterruptedException e) {
   
  }
  System.out.println(name + ":out");
 }

}

public class Main {
 public static void main(String[] args) {
  Bathroom room = new Bathroom();

  FamilyTread father = new FamilyTread(room, "父");
  FamilyTread mother = new FamilyTread(room, "母");
  FamilyTread sister = new FamilyTread(room, "姉");
  FamilyTread me = new FamilyTread(room, "自分");

  father.start();
  mother.start();
  sister.start();
  me.start();
 }
}

結果:synchronized無
母:enter
父:enter
姉:enter
自分:enter
姉:enterd
姉:out
父:enterd
父:out
母:enterd
母:out
自分:enterd
自分:out

結果:synchronized有
父:enter
父:enterd
父:out
自分:enter
自分:enterd
自分:out
姉:enter
姉:enterd
姉:out
母:enter
母:enterd
母:out

並列処理

using System.Threading;

Parallel.For(0, 10, (n) =>
{
 Console.WriteLine(n);
});
→0~9まで数字が出力されるが毎回結果が異なる
(出力に関して並列で処理が行われている)

ForEach利用
var ary = Enumerable.Range(start: 0, count: 10);
Parallel.ForEach(ary, n => {
 Console.WriteLine(n);
});

LINQの並列処理(PLINQ)
static void Main(string[] args)
{
 var oldAry = Enumerable.Range(start: 0, count: 10);
 
 var newAry1 = oldAry.Where(n => n > retNum()).Select(n => n).ToArray();
 時間は掛かるが0~9が順番通り格納される
 var newAry2 = oldAry.Where(n => n > retNum()).AsParallel().Select(n => n).ToArray();
 0~9がバラバラに格納される(並列で処理される)。
 var newAry3 = oldAry.Where(n => n > retNum()).AsParallel().AsOrdered().Select(n => n).ToArray();
 時間は掛かるが0~9が順番通り格納される(並列で処理されるがソートされる)。
}

時間を掛けて0を返すだけの関数
static int retNum()
{
 Thread.Sleep(millisecondsTimeout: 1000);
 return 0;
}

タイマー

フォームコントロールのタイマーとは別。
処理が軽い。

using System.Threading;

private delegate void _delegate();
※フォーム操作用
private System.Threading.Timer _timer();
※タイマーオブジェクトは外部に保持しておかないとガーベージコレクションの対象となる

private void MyForm_Load(object sender, EventArgs e)
{
  定期的に実行する処理を指定(callBackMethod)
  TimerCallback timerDelegate = new TimerCallback(callBackMethod);
  
  タイマー実行
  _timer = new System.Threading.Timer(
    callback: timerDelegate,  
    state: null,
    dueTime: 0,
    period: 1000);
}

定期的に実行される処理
private void callBackMethod(object o)
{
  処理
  処理
  
  フォームを操作する場合
  Invoke(method: new _delegate(method));
}

/複数行に渡る処理を行う場合
private void method()
{
  this.MyLabel.Text = “フォーム操作”;
}

IAsyncResultクラス利用による非同期処理

内部的にスレッドプールを使用している為処理が軽くなる。

基本

移譲オブジェクト
delegate string SampleDelegate(int prm1, string prm2);

static void Main(string[] args)
{
  移譲オブジェクト作成
  SampleDelegate sd = new SampleDelegate(DelegateMethod);

  非同期処理開始
  IAsyncResult ar = sd.BeginInvoke(prm1: 100, prm2: @”test”, callback: null, @object: null);

  非同期処理終了
  string result = sd.EndInvoke(result: ar);
}

非同期で実行させる(重たい)処理
static string DelegateMethod(int prm1, string prm2)
{
  return “”;
}

待ち合わせ・待機

移譲オブジェクト
delegate string SampleDelegate(int prm1, string prm2);

static void Main(string[] args)
{
  移譲オブジェクト作成
  SampleDelegate sd = new SampleDelegate(DelegateMethod);

  非同期処理開始
  IAsyncResult ar = sd.BeginInvoke(prm1: 100, prm2: @”test”, callback: null, @object: null);

  非同期処理を2秒中断
  ar.AsyncWaitHandle.WaitOne(millisecondsTimeout: 2000);

  非同期処理終了まで待機
  ar.AsyncWaitHandle.WaitOne();

  if (ar.IsCompleted)
  {
    非同期処理終了
    string result = sd.EndInvoke(result: ar);       
  }
}

非同期で実行させる(重たい)処理
static string DelegateMethod(int prm1, string prm2)
{
  return “”;
}

コールバック関数利用

移譲オブジェクト
delegate string SampleDelegate(int prm1, string prm2);

static void Main(string[] args)
{
  移譲オブジェクト作成
  SampleDelegate sd = new SampleDelegate(DelegateMethod);

  コールバック関数定義
  AsyncCallback cb = new AsyncCallback(CallBackMethod);

  非同期処理開始
  IAsyncResult ar = sd.BeginInvoke(prm1: 100, prm2: @”test”, callback: cb, @object: null);

  非同期処理終了
  string result = sd.EndInvoke(result: ar);
}

非同期で実行させる(重たい)処理
static string DelegateMethod(int prm1, string prm2)
{
  Console.WriteLine(“asynchronous”);
  Console.ReadKey();
  return “”;
}

static void CallBackMethod(IAsyncResult ar)
{
  Console.WriteLine(“callback”);
  Console.ReadKey();
}

スレッド関連メソッド/プロパティ

別スレッドを開始
Thread インスタンス = new Thread(start: new ThreadStart(メソッド名));
インスタンス.Start();

※別スレッドで起動できるのは戻り値無し、引数無しのメソッドのみ
 戻り値やパラメータを利用したいときは、後述のデリゲートを使用

バックグラウンド・スレッド
メインのスレッドが終了すると自身も終了するスレッド

フォアグラウンド・スレッド
メインのスレッドが終了しても終了しないスレッド

バックグラウンド・スレッドとする
インスタンス.IsBackground = true;

優先度変更
インスタンス.Priority = ThreadPriority.Highest;
インスタンス.Priority = ThreadPriority.Normal;
インスタンス.Priority = ThreadPriority.Lowest;

スレッドの一時停止
インスタンス.Suspend();

スレッドの再開
インスタンス.Resume();

スレッドの処理が終了するまで待つ
インスタンス.Join();

スレッドの強制終了
インスタンス.Abort();

1秒間待つ
Thread.Sleep(1000);

.NETで作成したDLLをVB6から呼び出す方法

用語解説

COMオブジェクト
COMコンポーネント=ActiveX
アセンブリ
.NetFrameworkで作成されたオブジェクト
GUID
GlobalUniqueIdentifier
世界中で一意な識別子。
MACアドレス等。
単にGUIDと呼ぶ場合は、WindowsにおけるオブジェクトのクラスIDを指す。

コーディング(.Net側)

DLLとしてコンパイル(ビルド)する。⇒test.dll
using System.Runtime.InteropServices;
namespace nsFromVB6
{
  [ComVisible(true)]
  public interface INmcTagger
  {
    string ReturnMessage();
  }
  [ClassInterface(ClassInterfaceType.None)]
  public class clsFromVB6 : INmcTagger
  {
    public string ReturnMessage()
    {
      return @"VB6からの呼びだし成功";
    }
  }
}

.Net側設定

(1) [プロジェクト]-[XXX のプロパティ]-[アプリケーション]タブの[アセンブリ情報]
アセンブリを COM 参照可能にする」にチェックを入れておく
(2) ビルドにてプラットフォームのターゲットを「x86」とする。
(3) ビルドにて「COM相互運用機能の登録」にチェックをつける。
(4) ビルドにて署名を作成する

COM登録

RegAsm.exeを実行
※COMとして利用できる様にレジストリに登録する
コマンドプロンプトにて.NetFrameworkのインストールフォルダのRegAsm.exeを実行
(例) cd C:\Windows\Microsoft.NET\Framework\v4.0.30319
regasm test.dll /tlb /codebase

※/codebaseを行う為には署名を作成しておくことが必要

VB6からの参照設定

VB6から利用する為にはタイプライブラリ(クラスの型情報ファイル)が必要
※署名を作成している場合は自動で作成されるので不要
コマンドプロンプトにて
tlbexp test.dll
例:C:\Program Files (x86)\Microsoft SDKs\Windows\v8.1A\bin\NETFX 4.5.1 Tools\tlbexp.exe
※test.tlbが作成される
設定後、VB6の「参照設定」からtest.tlb を読み込む。

コーディング(VB6)

Private Sub Form_Load()
  Call CallDotNet
End Sub
Private Sub CallDotNet()
  On Error GoTo Err_Trap
  
  Dim obj As clsFromVB6
  Set obj = New clsFromVB6
  Call MsgBox( obj.ReturnMessage )
  
  Exit Sub
Err_Trap:
  MsgBox (Err.Description)
End Sub