ASP.NET MVC Windowsフォームからのリクエスト

概要

クライアント側がブラウザではなく、Windowsフォームを使用する例

GET

※MVCプロジェクト
namespace MyMVC.Controllers
{
 クライアントからのリクエストを制御
 public class TestController : Controller
 {
  public string Do(string prm)
  {
   ActionResult型でなく、文字列型を返す
   return prm;
  }
 }
}

クライアントからのパラメータの振り分け先設定
namespace MyMVC
{
 public class RouteConfig
 {
  public static void RegisterRoutes(RouteCollection routes)
  {
   routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
   
   routes.MapRoute(
    name: "Default",
    url: "{controller}/{action}/{prm}",
    defaults: new { controller = "Test", action = "Do", prm = UrlParameter.Optional }
   );
  }
 }
}

※Windowsフォームプロジェクト
WebRequest作成
var req = WebRequest.Create(@"http://localhost:12345/Test/Do/AAA");
本来はブラウザで指定するURLをWebRequestに設定
(AAAはパラメータ)
WebResponse作成
var res = req.GetResponse();
応答データ受信の為のStreamを取得
Stream st = res.GetResponseStream();
文字コード指定、StreamReader作成
StreamReader sr = new StreamReader(st, Encoding.UTF8);
データ受信
string htmlSource = sr.ReadToEnd();
MessageBox.Show(htmlSource);
→AAA
終了処理
sr.Close();
st.Close();
res.Close();

POST

WebRequestの作成
WebRequest req = WebRequest.Create("http://localhost:12345/Test/Do");
本来はブラウザで指定するURLをWebRequestに設定
POST送信する文字列をバイト型配列に変換
byte[] postDataBytes = Encoding.ASCII.GetBytes("AAA");

メソッドにPOSTを指定
req.Method = "POST";
ContentTypeを"application/x-www-form-urlencoded"に
req.ContentType = "application/x-www-form-urlencoded";
POST送信するデータの長さを指定
req.ContentLength = postDataBytes.Length;
POST送信の為のStreamを取得
Stream reqStream = req.GetRequestStream();
送信するデータ書き込み
reqStream.Write(postDataBytes, 0, postDataBytes.Length);
reqStream.Close();
リクエストメッセージ取得
WebResponse res = req.GetResponse();
↑ 受信データからStreamを取得
Stream resStream = res.GetResponseStream();
読み込み
StreamReader sr = new StreamReader(resStream, Encoding.GetEncoding("shift_jis");
MessageBox.Show(sr.ReadToEnd());
→AAA
終了処理
sr.Close();

※MVCプロジェクト
namespace MyMVC.Controllers
{ 
 クライアントからのリクエストを制御
 public class TestController : Controller
 {
  [HttpPost]
  public string Do()
  {
   Request.InputStream.Position = 0;
   using (StreamReader reader = new StreamReader(Request.InputStream, Encoding.UTF8))
   {
    string output = reader.ReadToEnd();
    ActionResult型でなく、文字列型を返す
    return output;
   }
  }
 }
}

プログラム言語 HTTPクライアント

GET

using System.Net;
using System.IO;

リクエスト作成
var req = WebRequest.Create(@"http://office-yone.com");
リクエスト送信&レスポンス取得
var res = req.GetResponse();
↑ 受信データからStreamを取得
Stream resStream = res.GetResponseStream();
読み込み
var reader = new StreamReader(st, Encoding.GetEncoding("Shift_JIS"));
string html = reader.ReadToEnd();
reader.Close();
st.Close();

$ curl http://~.com
$conn = curl_init();

取得するURLを指定
$url = "http://office-yone.com/";
curl_setopt ( $conn, CURLOPT_URL , $url );

$response = curl_exec ( $conn );

エラー内容
echo curl_errno($conn);
echo curl_error($conn);

curl_close($conn);

echo('<pre>');
print_r($response);
echo('</pre>');

use Cake\Http\Client;

$http = new Client();
$response = $http->get('https://office-yone.com/');

クエリパラメータ
$response = $http->get('https://office-yone.com/',
 ['prm1' => 'aaa', 'prm2' => 'bbb']
);

echo '<pre>';
print_r($response);
echo '</pre>';
exit;

POST

using System.Net;
using System.IO;

WebRequestの作成
var req = WebRequest.Create("http://~;");

POST送信する文字列をバイト型配列に変換
byte[] postDataBytes = Encoding.ASCII.GetBytes("AAA");

メソッドにPOSTを指定
req.Method = "POST";

ContentTypeを"application/x-www-form-urlencoded"に
req.ContentType = "application/x-www-form-urlencoded";

POST送信するデータの長さを指定
req.ContentLength = postDataBytes.Length;
POST送信の為のStreamを取得
Stream reqStream = req.GetRequestStream();

送信するデータ書き込み
reqStream.Write(postDataBytes, 0, postDataBytes.Length);
reqStream.Close();

リクエストメッセージ取得
var res = req.GetResponse();
↑ 受信データからStreamを取得
Stream resStream = res.GetResponseStream();

読み込み
StreamReader sr = new StreamReader(resStream, Encoding.GetEncoding("shift_jis"));
string html = reader.ReadToEnd();
reader.Close();
st.Close();

$ curl
-w '\n' 'http://~.com'
-d 'prm1=aaaa&prm2=bbb&prm3=ccc'
-XPOST
$conn = curl_init();

// 取得するURLを指定
$url = "http://office-yone.com/";
curl_setopt ( $conn, CURLOPT_URL , $url );

curl_setopt($ch, CURLOPT_POST, TRUE);
パラメータ
curl_setopt($ch, CURLOPT_POSTFIELDS, "prm1=aaa&prm2=bbb");

$response = curl_exec ( $conn );
curl_close($conn);

use Cake\Http\Client;

$http = new Client();

ポストパラメータ
$response = $http->post('https://office-yone.com/',
 ['prm1' => 'aaa', 'prm2' => 'bbb']
);

echo '<pre>';
print_r($response);
echo '</pre>';
exit;

HTML取得

using System.Net;
using System.IO;
指定のURLデータを取得
Stream st = new WebClient().OpenRead("http://office-yone.com");
var sr = new StreamReader(st, Encoding.GetEncoding("Shift_JIS"));
string html = sr.ReadToEnd();
sr.Close();
st.Close();
MessageBox.Show(html);

$conn = curl_init();

取得するURLを指定
$url = "http://office-yone.com/";
curl_setopt ( $conn, CURLOPT_URL , $url );

実行結果を文字列で返す
curl_setopt($conn, CURLOPT_RETURNTRANSFER, 1);

$response = curl_exec ( $conn );
curl_close($conn);

クッキー送受信

DownloadFile

using System.Net;
指定のURLデータを取得&保存
new WebClient().DownloadFile(address: @"http://office-yone.com", fileName: @"office-yone.html");

$ curl http://~.com -o

エラーメッセージ表示
$ curl http://~.com -o -s

DownloadData

using System.Net;
指定のURLデータを取得
byte[] data = new WebClient().DownloadData("http://office-yone.com");
string html = Encoding.GetEncoding("Shift_JIS").GetString(data);
MessageBox.Show(html);

プロキシ利用

using System.Net;
var client = new WebClient();
プロキシ利用
client.Proxy = WebRequest.DefaultWebProxy;
プロキシ指定
client.Proxy = new WebProxy("http:~:8080");
プロキシ未使用
client.Proxy = null;
client.~

$conn = curl_init();

// 取得するURLを指定
$url = "http://office-yone.com/";
curl_setopt ( $conn, CURLOPT_URL , $url );

curl_setopt ($conn , CURLOPT_PROXY, '999.999.999.999.9999);

$response = curl_exec ( $conn );
curl_close($conn);

セキュリティ

$conn = curl_init();

// 取得するURLを指定
$url = "http://office-yone.com/";
curl_setopt ( $conn, CURLOPT_URL , $url );

詳細な情報を出力。STDERRか、またはCURLOPT_STDERRで指定したファイルに出力される
curl_setopt($ch, CURLOPT_VERBOSE, TRUE);

サーバー証明書の検証を行わない
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);

1:SSLピア証明書に一般名が存在するかどうかを調べる
2:1に加え、その名前がホスト名と一致することを検証する※規定値
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);

取得結果を加工しない
curl_setopt($conn, CURLOPT_RETURNTRANSFER, TRUE);

$response = curl_exec ( $conn );
curl_close($conn);

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

エンコードとは

符号化
文字に対してコンピュータで扱う文字コード=コード(数値)を割り振る事
テキストエディタで文字データを作成・保存した際、
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>

ASP.NET Webフォーム Ajax利用

Ajaxとは?

Asynchronous JavaScript XML
WEBページの一部を変更したい時に、
ページ全体を更新せずに一部分だけを更新する技術
XMLHttpRequest(ブラウザに組み込まれたJavaScript組み込みクラス)クラスにおける非同期通信によってなされる。
非同期通信を行うとFormLoadイベント等は発生するがページ全体の更新は行われないので、ブラウザの「戻る」ボタン等も有効にならない。

Ajax通信時のロード処理

Ajax通信時もPostBackは発生する(=Page_Loadイベントが発生する)。
Page.IsPostBack = True
ただしクライアント側において全画面の再描画は行われない。

通常
Buttonクリック→サーバー処理→全画面再描画
Ajax通信時
Buttonクリック→サーバー処理→指定部分再描画

※~.aspx
<script>
 function myFunc() {
  window.alert("LoadOK");
 }
</script>
<body onload="myFunc();">
 <asp:ScriptManager ID="manager" runat="server"></asp:ScriptManager>
 <asp:UpdatePanel ID="myUpdate" runat="server">
  <ContentTemplate>
   <asp:TextBox ID="myText" runat="server"></asp:TextBox><br />
  </ContentTemplate>   
  <Triggers>
   <asp:AsyncPostBackTrigger ControlID="myButton" EventName="Click" />
  </Triggers>
 </asp:UpdatePanel>
 <asp:Button ID="myButton" runat="server" Text="Button" onclick="myButton_Click" />
</body>

※~.aspx.cs
protected void Page_Load(object sender, EventArgs e)
{
 if (!Page.IsPostBack)
 {
  this.myText.Text = "NoPostBack";
 }
 else
 {
  this.myText.Text = "PostBack";
 }
}
protected void myButton_Click(object sender, EventArgs e)
{
 this.myText.Text = "Ajax";
}

【初回ロード時】
サーバー:
Page_Loadイベント

Page.IsPostBack = False なので、this.myText.Text = "NoPostBack";

クライアント:
画面描画

onloadイベント

fncInitSpread()実行

【ボタンクリック時】
サーバー:
Page_Loadイベント

Page.IsPostBack = True なので、this.myText.Text = "PostBack";

クライアント:
UpdatePanel内のみ描画

※onloadイベントは発生しない

AJAX Extensionsコントロール

ASP.NETでは「ASP.NET AJAX Extensionsコントロール」を配置する事でAjaxを利用できる。
ASP.NET AJAX Extensionsコントロールには、
・UpdatePanel
等があり、
UpdatePanel上のデータを非同期でページ全体のポストバックを発生させずに更新できる。
非同期ポストバックと呼ばれる画面一部のポストバックは発生する

ScriptManagerコントロール

UpdatePanel等のAjax Extensionsコントロールを使用する際、
先頭に配置する事で配下のコントロールの非同期通信を可能にする。

UpdatePanelコントロール

※~.aspx
<form id="myForm" runat="server">
 ※スクリプトマネージャー:AJAXを有効にする
 <asp:ScriptManager ID="myScriptManager" runat="server"></asp:ScriptManager>
 
 ※Updateパネル:パネル内のコントロールについて非同期通信を行う
 <asp:UpdatePanel ID="myUpdatePanel" runat="server" >
  <ContentTemplate>
   <asp:Label ID="myLabel" runat="server" Text=""></asp:Label>
  </ContentTemplate>
  
  ※↓のイベントをトリガーとして↑パネル内のみ非同期通信を行う
  <Triggers>
   <asp:AsyncPostBackTrigger ControlID="myButton" EventName="Click" />
  </Triggers>
  
 </asp:UpdatePanel>
 ※↓ボタン押下イベントハンドラをaspx.csファイルに記述
 <asp:Button ID="myButton" runat="server"
   Text="非同期更新" OnClick="myButton_Click" />
</form>

※~.aspx.cs
public partial class MyForm : System.Web.UI.Page
{

 protected void myButton_Click(object sender, EventArgs e)
 {
  // 非同期通信処理
  this.myLabel.Text = this.myTextBox.Text;
  
  ※複数のUpdateパネルを使用している場合に、
  規定のトリガー以外で非同期通信を実行する場合

  hisUpdatePanel.Update();
 }
}

UpdateProgressコントロール

※~.aspx
<form id="myForm" runat="server">
 <asp:ScriptManager ID="myScriptManager" runat="server"></asp:ScriptManager>
 
 <asp:UpdatePanel ID="myUpdatePanel" runat="server">
  <ContentTemplate>
   <asp:Label ID="myLabel" runat="server"
    Text="処理前"></asp:Label><br />
   <asp:Button ID="myButton" runat="server"
    Text="非同期更新" OnClick="myButton_Click" /><br />
  </ContentTemplate>
 </asp:UpdatePanel>
 
 // ↑の非同期処理中のみ表示される
 <asp:UpdateProgress ID="myUpdateProgress" runat="server"
   AssociatedUpdatePanelID="myUpdatePanel">
  <ProgressTemplate>
   <asp:Image ID="myLoading" runat="server"
    // myUpdatePanelが通信中のみ表示される。
    ImageUrl="(gif画像パス)" />
    しばらくお待ちください。
  </ProgressTemplate>
 </asp:UpdateProgress>
</form>

※~.aspx.cs
public partial class MyForm : System.Web.UI.Page
{

 protected void myButton_Click(object sender, EventArgs e)
 {
  // 非同期通信処理
  ~
  this.myLabel.Text = "完了";
 }
}

非同期通信後のスクリプト埋め込み

Updateパネル内の非同期通信処理発生時にサーバーサイドからクライアントサイドへスクリプトを埋め込み/実行させる場合
(非同期通信中は通常のポストバックが発生しない為、Page.~の埋め込みは使用できない)
※UpdatePanel内に配置したコントロール(ボタン等)のイベント内に書く事
ScriptManager.RegisterClientScriptBlock(
 ページ,
 スクリプトを埋め込むコントロールの型,
 キー,
 スクリプト文,
 <script>タグを追加するか否か?);
ScriptManager.RegisterClientScriptBlock(
 this,
 this.GetType(),
 "myKey",
 "fnMyfunc();",
 true);
→<script>fnMyFunc();</script>
ScriptManager.RegisterClientScriptBlock(
 this,
 this.GetType(),
 "myKey",
 "<script>fnMyFunc();</script>",
 false);
→<script>fnMyFunc();</script>

参考:通常ポストバック時のスクリプト埋め込み

画面の表示前にスクリプトを実行する
Page.RegisterClientScriptBlock(型, キー, スクリプト文)
Page.RegisterClientScriptBlock(this.GetType(), "client", "<script> alert('前') <" + "/script>");
画面の表示後にスクリプトを実行する
Page.RegisterStartupScript(型, キー, スクリプト文)
Page.RegisterStartupScript(this.GetType(), "startup", "<script> alert('後') <" + "/script>");

AJAX Control Toolkit

ToolkitScriptManagerコントロール

ScriptManagerコントロールとほぼ同等の機能を持つ
ToolkitScriptManager.CombineScriptsプロパティ=True により、
Ajaxに関わる重複するJavaScriptファイルをダウンロードせずに済ませられる。

UpdatePanelAnimationExtenderコントロール

<ajaxToolkit:UpdatePanelAnimationExtender
 ID="~"
 runat="server"
 
 Ajax通信が実行されるコントロールを指定。
 TargetControlID="~">
 
 対象コントロールの通信終了後に、指定のアニメーションを実行する。
 <Animations>
  更新完了時のアニメーションを定義
  <OnUpdated>
   <Sequence>
    ラベルの背景色を#008800(緑)から#FFFFFF(白)に変更
    <Color AnimationTarget="lblAsync"
     PropertyKey="backgroundColor"
     StartValue="#008800" EndValue="#FFFFFF" />
    
    JavaScript関数を実行
    <ScriptAction Script="myJsFunc();" />
   </Sequence>
  </OnUpdated>
 </Animations>
</ajaxToolkit:UpdatePanelAnimationExtender>

Microsoft AJAX Library

クライアントサイド技術
・Sys.WebForms.PageRequestManagerクラス
UpdatePanelコントロールによって発生した非同期通信(部分更新)を管理
サーバーサイドのUpdatePanelコントロールの機能を拡張する技術
JavaScriptから利用する。
・Sys.Applicationクラス
ページ全体に関わる部分を管理

<script type="text/javascript">
 function pageLoad()
 {
  // PageRequestManagerインスタンスの作成(newではない)
  var mng = Sys.WebForms.PageRequestManager.getInstance();
  
  // 非同期通信の開始と同時に実行される。
  mng.add_initializeRequest(
   function (sender, args) {
    $get('lblStatus').innerHTML =
      args.get_postBackElement().id
        + 'から非同期ポストバックが実行されました。';
   }
  );
  
  // 非同期通信の終了と同時に実行される。
  mng.add_endRequest(
   function (sender, args) {
    $get('lblStatus').innerHTML = '';
   }
  );
 }
</script>

ASP.NET Webフォーム 変数の取り扱い方法

※~.aspx.cs
public partial class MyForm : System.Web.UI.Page
{

 // モジュール変数
 protected const string CONST_myValue = @"サーバー変数1";
 protected string mMyStr = @"サーバー変数2";

 protected void Page_Load(object sender, EventArgs e)
 {
  // セッション変数
  Page.Session["value"] = "セッション変数";
  ※「ASP.NET Webフォーム 組み込みオブジェクト」参照
 }
}

※~.aspx
<%@ Page
 Language="C#"
 AutoEventWireup="true"
 CodeBehind="~.aspx.cs"
 Inherits="~.~" %>
 ※「ASP.NET Webフォーム Pageディレクティブ」参照

※ASPXファイル上でC#/VBの資源を扱う場合は<%~%>で囲む
<%
 // ローカル変数
 string myValue = "ローカル変数"; 
%>
<%=myValue %>
→ ローカル変数
<%=CONST_myValue %>
→ サーバー変数1
<%=Session["value"] %>
→ セッション変数

埋め込み
<asp:Button ID="MyButton"
 runat="server" Text="<%=myValue %>">
<script type="text/javascript">
 var str = "<%=this.mMyStr%>";
 → サーバー変数2
</script>

プログラム言語 属性

概要

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

エントリポイント

///

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

[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);

WEBカメラで録画する方法 (AForge.NET利用)

概要

AForge.NET Frameworkというフリーのライブラリを使用して画像を動画に変換できる。
1本の動画は2GByteまで。
音声無し
AForge.NETライブラリからライブラリを直接ダウンロード。
多くはNuGetでもインストール可能。

静止画を動画に変換

using AForge.Video.VFW;
public partial class MyForm : Form
{
  private void MyForm_Load(object sender, EventArgs e)
  {

    AVIWriter writer = new AVIWriter();
    
    writer.Open(fileName: @"C:\data\test.avi", width: 500, height: 300);
    
    // フレームレート設定
    aviWriter.FrameRate = 30;
    
    // 1フレームをBitMapで定義
    Bitmap img = new Bitmap(500, 300);
    
    //ImageオブジェクトのGraphicsオブジェクトを作成する
    Graphics g = Graphics.FromImage(img);
    
    //全体を黒で塗りつぶす
    g.FillRectangle(Brushes.Black, g.VisibleClipBounds);
    
    for (int i = 0; i < 100; i++)
    {
      //黄色い扇形を描画する
      g.DrawPie(pen:Pens.Yellow,
        x:60 + i,
        y:10, width:80,
        height:80,
        startAngle:30,
        sweepAngle:300);
      
      //録画(1フレームを追加)
      writer.AddFrame(img);
    }
    
    writer.Close();
  }
}

WEBカメラの静止画を動画に変換(方法1)

using AForge.Video.DirectShow;
public partial class MyForm : Form
{
  private void MyForm_Load(object sender, EventArgs e)
  {

    // ビデオキャプチャデバイスを選択するダイアログの生成
    var form = new VideoCaptureDeviceForm();
    // 選択ダイアログを開く
    if (form.ShowDialog() != System.Windows.Forms.DialogResult.OK) return;
    
    // 選択されたデバイスをVideoSourcePlayerのソースに設定
    videoSourcePlayer.VideoSource = form.VideoDevice;
    
    // ビデオキャプチャのスタート
    videoSourcePlayer.Start();
    
    
    AVIWriter writer = new AVIWriter();
    writer.Open(fileName: @"C:\data\test.avi", width: 500, height: 300);
    
    Bitmap bmp = videoSourcePlayer.GetCurrentVideoFrame();
    writer.AddFrame(img);
    
    writer.Close();
    
    
    // 閉じるときの処理
    if (videoSourcePlayer.VideoSource != null &&
     videoSourcePlayer.VideoSource.IsRunning)
    {
      videoSourcePlayer.VideoSource.SignalToStop();
      videoSourcePlayer.VideoSource = null;
    }
    
  }
}

WEBカメラの静止画を動画に変換(方法2)

上の方法でカメラから静止画を取得できない場合(カメラの問題?)
//AForge.NETライブラリ
using AForge.Video;
using AForge.Video.DirectShow;
using AForge.Video.VFW;
public partial class MyForm : Form
{

  AVIWriter writer = new AVIWriter();
  Bitmap img = new Bitmap(width: 640, height: 480);
  
  private void MyForm_Load(object sender, EventArgs e)
  {
    //カメラの取得
    FilterInfoCollection videoDevices
       = new FilterInfoCollection(FilterCategory.VideoInputDevice);
    

    //カメラが複数台ある場合は配列内の数字を選択できる様にする工夫が必要。
    VideoCaptureDevice videoSource
       = new VideoCaptureDevice(videoDevices[0].MonikerString);
    
    //カメラの画像が更新される度にイベントを発生させる。
    videoSource.NewFrame += new NewFrameEventHandler(this.video_NewFrame);
    
    videoSource.DesiredFrameSize = new Size(width: 640, height: 480);
    videoSource.Start();
    
    writer.Open(fileName: @”C:\test.avi”, width: 640, height: 480);
  }
  
  private void video_NewFrame(object sender, NewFrameEventArgs eventArgs)
  {
    //静止画をBitMap形式で取得
    img = eventArgs.Frame;
    writer.AddFrame(frameImage: img);
  }
  
  private void MyForm_FormClosed(object sender, FormClosedEventArgs e)
  {
    writer.Close();
  }
}

プログラム言語 外部プログラム連携

概要

必要な機能が大きく、開発に要する時間が短い昨今、
プログラムの全てを自分で開発するのは効率が悪い。
他人が作ったプログラムをソースでなくDLLのまま利用する為の方法。
他言語で開発されたプログラムであってもそれは可能。

参考

Windowsはなぜ動くのか
Windowsの仕組み(.NET Frameworkとは?)
Windowsプログラミング

マーシャリング

異なるシステムへデータを渡す場合に渡し先で有効な型へ変換すること。
マネージ環境のプログラムから、アンマネージ環境で動作するプログラムを呼び出す際にマーシャリングが必要になる。

マーシャラー

マーシャリングを行うプログラム。

マネージ環境

CLR(CommonLanguageRuntime)によって管理された環境。
ガーベッジコレクション、セキュリティ機能等の.NetFrameworkの機能を提供する実行エンジン。
ガーベッジコレクションの動作を含むメモリ管理、配列・文字列の長さ管理が自動で行われる。

アンマネージ環境

上記以外の環境。
Win32API、COMオブジェクト、C/C++等、多言語で作られたプログラム等。
バッファがオーバーフローしない様、ガーベッジコレクションの動作に該当する使い終わったメモリの破棄等の処理は自動では行われない。

COM相互運用

マーシャリングを行った上で.NetFramework←→WindowsAPIでデータをやり取りを行う事。

ウィンドウハンドル

WindowsAPIにおいて操作対象毎に割り振られる管理番号
操作対象へのポインタ

実例

DLL(API)

APIとは?
Windowsプログラミング/API」参照

using System.Runtime.InteropServices;

class MyWin32
{
  [DllImport("USER32.DLL", EntryPoint = "MessageBoxA")]
  public static extern int myMessageBox(
    int hWnd,
    String lpText,
    String lpCaption,
    uint uType);
  
  ※USER32.DLLにおけるMessageBoxAをmyMessageBoxとして宣言
  
  public const int MB_OK = 0x00000000;
  public const int MB_OKCANCEL = 0x00000001;
  public const int MB_ABORTRETRYIGNORE = 0x00000002;
  public const int MB_YESNOCANCEL = 0x00000003;
  public const int MB_YESNO = 0x00000004;
  public const int MB_RETRYCANCEL = 0x00000005;

}

public partial class MyForm : Form
{
  private void MyForm_Load(object sender, EventArgs e)
  {

    MyWin32.myMessageBox(
      hWnd: 0,
      lpText: "メッセージ",
      lpCaption: "キャプション",
      uType: MyWin32.MB_YESNO);
  }
}

VB外部の関数(Windows API/DLL)を呼び出す場合に定義
Public Declare Auto Function GetUserNameA Lib "advapi32" (ByVal buf as string, size as long) As Long

Public Declare Auto Function(又はSub) [VBで使う関数名]
Lib "[DLLファイル名]"
Alias "[DLL内の実際のファイル名]" ([ファイルの引数]) As [戻り値の型]

Lib:DLLファイルを指定。拡張子を抜きもOK

Alias:DLL 内の関数名と別名で呼び出すことができる。指定無しも可
Public Declare Function GetUserName Lib "advapi32" Alias "GetUserNameA" (ByVal buf as string, size as long) As Long
Call GetUserName(引数)

Declare 定義した後は、通常の関数と同じに呼び出せる。
尚、Windows API や C で作られた DLL のパラメタは一般に値渡しなので、ByVal キーワードが必要。

VB外部の関数(Windows API/DLL)を呼び出す場合に定義
Public Declare Function GetUserNameA Lib "advapi32" (ByVal buf as string, size as long) As Long

Public Declare Function(又はSub) [VB.NETで使う関数名]
Lib "[DLLファイル名]"
Alias "[DLL内の実際のファイル名]" ([ファイルの引数]) As [戻り値の型]

Lib:DLLファイルを指定。拡張子を抜きもOK

Alias:DLL 内の関数名と別名で呼び出すことができる。指定無しも可
Public Declare Function GetUserName Lib "advapi32" Alias "GetUserNameA" (ByVal buf as string, size as long) As Long
Call GetUserName(引数)

Declare 定義した後は、通常の関数と同じに呼び出せる。
尚、Windows API や C で作られた DLL のパラメタは一般に値渡しなので、ByVal キーワードが必要。

Private Declare Function 公開名 Lib "DLL名" Alias "別名" (ByVal 引数1 As Long, ・・・) As Long
Private Declare Function 公開名 Lib "DLL名" Alias "別名" () As Long
Private Declare Function 公開名 Lib "DLL名" () As Long

Private Declare Sub 公開名 Lib "DLL名" Alias "別名" (ByVal 引数1 As Long, ・・・)
Private Declare Sub 公開名 Lib "DLL名" (ByVal 引数1 As Long, ・・・)
Private Declare Sub 公開名 Lib "DLL名" ()
※DLL名:DLLは実行ファイルと同階層に配置するか、パスを記載する。

Dim myApi As Long
myApi = 公開名(5,5)
Call 別名(5,5)

DLL(ActiveX)

ActiveXとは?
Windowsプログラミング/API」参照

Dim myObj As Object
myObje = CreateObject("~")
Call myObje.メソッド(~)
myObje.プロパティ = ~
DLL(COM)

COMとは?
Windowsプログラミング/COM」参照

「参照設定」によるDLLのパス設定が必要
COMコンポーネントでないDLLは参照設定できない。
DLLのCOM登録は「.NETで作成したDLLをVB6から呼び出す方法」参照

Dim myObj As New ~
Call myObje.メソッド(~)
myObje.プロパティ = ~

EXE

非同期で起動。※呼び出し元PGは実行されたまま。

' メモ帳を起動する
Call Shell("Notepad", vbNormalFocus)

' タスクIDを取得(≠Windowsハンドル)
Dim RetVal As Variant
RetVal = Shell("notepad", vbNormalFocus)
' Windowをアクティブに(≠最小化解除)
Call AppActivate(RetVal)

' ファイルを指定してメモ帳を起動する
Call Shell("Notepad C:\test.txt", vbNormalFocus)

' 引数がスペースを含む場合
Call Shell("""~.exe"" ""2015/05/08 10:00:00""", vbNormalFocus)

vbHide(0)
フォーカスを持ち、非表示
vbNormalFocus(1)
フォーカスを持ち、元のサイズと位置に復元
vbMinimizedFocus(2)
フォーカスを持ち、最小化表示
vbMaximizedFocus(3)
フォーカスを持ち、最大化表示
vbNormalNoFocus(4)
フォーカスを持たず、最後にウィンドウを閉じたときのサイズと位置に復元
※現在アクティブなウィンドウは、アクティブのまま
vbMinimizedNoFocus(6)
フォーカスを持たず、最小化表示
※現在アクティブなウィンドウは、アクティブのまま

System.Diagnostics.Process p =
  System.Diagnostics.Process.Start("notepad.exe");

//コマンドライン引数に「"C:\test\1.txt"」を指定してメモ帳を起動する
System.Diagnostics.Process.Start("notepad.exe", @"""C:\test\1.txt""");

プロセスの終了を検知する

using System.Diagnostics;

static void Main()
{
  // プログラムを起動しプロセスのインスタンスを取得
  Process hProcess = Process.Start(
    fileName: @"C:\Test.exe",
    arguments: @"prm1 prm2 prm3");
  
  // ↑プロセスが終了した時にExitedイベントを発生させる
  hProcess.EnableRaisingEvents = true;
  
  // Exitedイベントのハンドラを追加する
  hProcess.Exited += new System.EventHandler(Test_Exited);
  
  // 監視の為にアプリケーションを起動させておく必要あり。
  // タスクバーには表示されないがプロセスは起動する
  Application.Run();
}

private static void Test_Exited(object sender, System.EventArgs e)
{
  プロセス終了時、ここが呼ばれる
}