C#
で作る AIR Native Extension
Adobe AIR3 から ANE という ネイティブ拡張機能が実装されたのでWindowsで試してみました。
全部で4ページにまとめます。
今回はその1ページ目です。
開発環境
Windows XP SP3
Visual Studio 2010 Express Edition( C# / VC++ )
FlashBuilder4.6( FlexSDK4.6 )
まぁ、アレです、各プラットフォーム固有の機能が使えたりするというヤツです。
Windowsならね、わざわざAIRでやらなくてもね、C#で全部出来るからね…、あんまり意味無いかもしれませんけどね。
orz
フロントエンドをAIRでまとめて、各プラットフォームごとにANEで固有機能を実装して…という具合にアプリを作るなら、ANE
はアリです。
が、結構かなりシビアにキツイです。キツイっス…。orz
レジストリにアクセスしたり、WMI(
Windows
Management
Instrumentation)を使ってハードウェア・ソフト
ウェアの情報を取得したり、C#のAPIに丸投げしたり出来ま
す。はい。
ane-lab(AIR
Native Extension is Lab codes) - Google Project Hosting
にC#で作ったサンプルをコミットさせて頂きましたので、参考にして下さい。
間違ってるかもしれないので、決して鵜呑みにしないように。
で、作り方。
作るのは4つ。
C#クラスライブラリ(C#のDLL)
VC++のCLRのクラスライブラリ(VC++ CLR/DLL)
Flexライブラリプロジェクト(SWC)
Flexプロジェクト(デスクトップ AIRアプリ)
です。
1.C#でDLL作るよ!
最初にキモを抑えて起きましょう。
C#で作ったDLLをVC++から参照して、VC++で作るDLLの中でFlashRuntimeExtensionsを組み込むことにより、AIRアプ
リに通知することが出来るようになります。
えー???ですよね。C#からVC++のDLLをImportする…のではなく、その逆です。VC++からC#のDLLを参照しま
す。
イヤーな予感がします。はい。
キモはコレ!
VC++から参照出来るように、「アセンブリをCOM参照可能にする」のチェックと、「COM相互運用機能の登録」のチェックをONにしておきます。
これでVC++からCOM経由でC#のクラスやそのメンバを参照出来るようになります。
ここで1つ注意。
COM
参照可能にする、とかCOM相互運用機能の登録とありますが、これはWindowsのCOMという機能を利用/経由(.NET/C#がCOMにラップされ
る)して、VC++から.NET
Objectを操作出来るようにする(マネージドからアンマネージにアクセスする)…というものです。
詳しくは「COM ・ RCW ・ 参照カウント
」などのキーワードで調べてください。
C#(.NET)にはGC(ガベージコレクション)という機能があります。が、COMはMSのドキュメントによると、「COM
自身は、オブジェクトが不要になったと判断しても、メモリから自動的にオブジェクトを削除するわけではありません。不要なオブジェクトの削除は、オブジェ
クトのプログラマが行います。プログラマは、参照カウントに基づいて、オブジェクトを削除できるかどうかを判断します。」とあります。(
http://msdn.microsoft.com/ja-jp/library/4947zb56%28v=vs.100%29.aspx)
完全にGCと相反していますね…。C#でどうやんのよ、それ…。
えー、通常C#で開発をする場合、変数にスコープがあり、このスコープから抜けるとGCの対象になる(実際にGCが何時行われるかはプログラマからは分か
りませんが)…のですが、VC++から参照させるために、C#のライブラリをCOM参照させる必要があるため、C#側のインスタン
スを明示的に解放してやる必要があります。
MSのドキュメントにも「
すべてのア
ウトポ
インタパ
ラメータを明
示的にNULLに設定する必要があります。」
と記載されています。(
http://msdn.microsoft.com/ja-jp/library/ms686638.aspx)&
rdquo;すばらしいGoogle翻訳”。
えー、ですので、C#側でも不必要な変数にはNULLを入れてやって下さい。
そうしないと、AIRアプリのプロセスが正常に終了しない場合があります。
あと、string[]
とかは使わない方が良いです。使うなら、List<string>とかCollection.Genericを使いましょ
う。
では、サンプルです。
OS内にインストールされているアプリケーション一覧を表示します。
これで、えろげとかもバッチリだね!(ぉ
プロジェクト名は ” ApplicationList_CS ” です。
[ App.cs ]
----------------------------------------
using System;
using System.Collections;
using System.Collections.Generic;
using System.Management;
using Microsoft.Win32;
namespace ApplicationList_CS {
class App {
//HotFix情報
private const string root = "root\\CIMV2";
private const string query = "SELECT * FROM Win32_QuickFixEngineering
WHERE ServicePackInEffect <> '' AND InstalledBy
<> ''";
public ArrayList getApplicationNames() {
List<string> hotfix = new List<string>();
List<string> app = new List<string>();
ManagementObjectSearcher searcher = new ManagementObjectSearcher(root,
query);
try {
foreach (ManagementObject queryObj in searcher.Get()) {
hotfix.Add((string)queryObj["HotFixID"]);
}
} catch (Exception e) {
Console.WriteLine(e.StackTrace);
} finally {
searcher = null;
}
RegistryKey regKey = null;
RegistryKey _subRegKey = null;
try {
//アプリケーション情報
string reg = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\";
string regValue = "DisplayName";
regKey = Registry.LocalMachine.OpenSubKey(reg);
string[] subkey = regKey.GetSubKeyNames();
foreach (string keyName in subkey) {
_subRegKey = Registry.LocalMachine.OpenSubKey(reg + keyName);
string value = (string)_subRegKey.GetValue(regValue);
if (value != null) {
for (int j = 0; j < hotfix.Count; j++) {
if (value.Contains(hotfix[j].ToString())) {
;
} else if (value.Contains("KB")) {
;
} else {
app.Add(value);
break;
}
}
}
//value = null;
}
app.TrimExcess();
reg = null;
regValue = null;
subkey = null;
} catch (NullReferenceException e) {
Console.WriteLine(e.StackTrace);
} finally {
regKey.Close();
_subRegKey.Close();
}
app.Sort();
for (int i = 0; i < (app.Count - 1); i++) {
try {
if (app[i].Equals(app[i + 1])) {
app.RemoveAt(i);
}
} catch (Exception ex) {
Console.WriteLine(ex.Message);
}
}
app.TrimExcess();
hotfix = null;
return new ArrayList(app);
}
}
}
[ EOF ]
----------------------------------------
[ AppList_CS.cs ]
----------------------------------------
using System;
using System.Collections;
namespace ApplicationList_CS {
public class AppList_CS {
private App app = new App();
public ArrayList getApplicationNames() {
return app.getApplicationNames();
}
}
}
[ EOF ]
----------------------------------------
やっちゃダメなダメ・コードも記載しておきます。絶対やっちゃダメだよw
配列多すぎ。orz
[ アプリケーション名・バージョン・HotFix情報を取得する変なC#コード ]
----------------------------------------
private void setApplicationReg() {
string[] val1 = null;
string[] val2 = null;
string[] val3 = null;
ArrayList application;
ArrayList applicationVersion;
ArrayList hotfix;
application = new ArrayList(); // application
applicationVersion = new ArrayList(); // applicationVersion
hotfix = new ArrayList(); // HotFix
try {
//HotFix情報
string root = "root\\CIMV2";
string query = "SELECT * FROM Win32_QuickFixEngineering WHERE
ServicePackInEffect <> '' AND InstalledBy
<> ''";
ManagementObjectSearcher searcher = new ManagementObjectSearcher(root,
query);
int cnt = 0;
foreach (ManagementObject queryObj in searcher.Get()) { cnt++; }
val1 = new string[cnt];
int i=0;
foreach (ManagementObject queryObj in searcher.Get()) {
val1[i] = (string)queryObj["HotFixID"];
i++;
}
}catch(Exception e){
string error = e.StackTrace;
//Console.WriteLine(e.StackTrace);
}
try {
//アプリケーション情報
string reg = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\";
string regValue = "DisplayName";
RegistryKey regKey = Registry.LocalMachine.OpenSubKey(reg);
string[] subkey = regKey.GetSubKeyNames();
int max = subkey.Length;
regKey.Close();
//アプリケーションの総数
val2 = new string[max];
val3 = new string[max];
RegistryKey _subRegKey = null;
int i = 0;
foreach (string keyName in subkey) {
_subRegKey = Registry.LocalMachine.OpenSubKey(reg + keyName);
if ((string)_subRegKey.GetValue(regValue) != null) {
val2[i] = (string)_subRegKey.GetValue("DisplayName");
val3[i] = (string)_subRegKey.GetValue("DisplayVersion");
i++;
}
_subRegKey.Close();
}
} catch (NullReferenceException e) {
string error = e.StackTrace;
//Console.WriteLine(e.StackTrace);
}
try {
for (int i = 0; i < val2.Length; i++) {
if(val2[i] != null){
for(int j = 0; j < val1.Length; j++){
if (val2[i].Contains(val1[j])) {
hotfix.Add(val2[i]);
val2[i] = null;
val3[i] = null;
break;
}else if(val2[i].Contains("KB")){
hotfix.Add(val2[i]);
val2[i] = null;
val3[i] = null;
break;
}
}
}
}
foreach(string _val in val2){
if( _val == null){
;
} else {
application.Add(_val);
}
}
foreach (string _val in val3) {
if (_val == null) {
;
} else {
applicationVersion.Add(_val);
}
}
application.TrimToSize();
applicationVersion.TrimToSize();
hotfix.TrimToSize();
application.Sort();
for (int i = 0; i < application.Count; i++ ) {
try {
if (application[i].Equals(application[i + 1])) {
application.RemoveAt(i);
}
}catch(Exception){
}
}
application.TrimToSize();
this.application = new string[application.Count];
this.applicationVersion = new string[applicationVersion.Count];
this.hotfix = new string[hotfix.Count];
for (int i = 0; i < application.Count; i++) {
this.application[i] = (string)application[i]; }
for (int i = 0; i < applicationVersion.Count; i++) {
this.applicationVersion[i] = (string)applicationVersion[i]; }
for (int i = 0; i < hotfix.Count; i++) { this.hotfix[i] =
(string)hotfix[i]; }
} catch (NullReferenceException ne) {
Console.WriteLine(ne.StackTrace);
}
}
----------------------------------------
もうちょっとだけ続くんじゃ。
ダメじゃない方のコードをコンパイルすると、DLLとtlbファイルが出来ますので、その2つをVC++のプロジェクトで使用します。
で、結局このDLLってどうなるのっ?ってことなのですが、「Visual Studioコマンドプロンプト」というのがあります。
このDOS窓で、oleviewと入力すると、COMを使用しているアプリケーションやコンポーネントが見れたりします。
えー、2ページ目に続きますが、今度はVC++を使って、AIRアプリとを繋ぐDLLを作成していきます。