1. kob58im

    No comment

    kob58im
Changes in body
Source | HTML | Preview
@@ -1,271 +1,413 @@
[COMオブジェクトのメンバー一覧を取得する (PowerShell)](https://qiita.com/kob58im/items/ea1ca69b9f898845a71f)
を応用してブラウザつくってみた。
※oleview.exeを持ってればいらない説…(まだ使ってないのでわからない)
ちなみにCOMオブジェクトでない普通の.Netのクラス(System.IO.Fileとか)であれば、
[C#リフレクションTIPS 55連発](https://qiita.com/gushwell/items/91436bd1871586f6e663)
が参考になります。
# つくってみた(キャプチャ)
![comobjbrowser.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/438183/74fe8480-6a6e-700c-51dc-5f7497a1dc04.png)
# ソースコード
```csharp
using System;
using System.Collections;
using System.Collections.ObjectModel;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Windows.Forms;
using System.Management.Automation;
-class MainForm:Form
+public class ProgIdInfo
{
- Button btn;
- Button btnGetMembers;
- SplitContainer spl;
- TextBox txtClassName;
- ListView lsvObj;
- ListView lsvMemb;
-
+ private PSObject _psObj; // ProgID レジストリキー情報のPSObject
+ private string _ProgID;
+ private string _CLSID;
+ private string _ValueOfCLSID;
+
+ public string ProgID { get{return _ProgID;} }
+ public string CLSID { get{return _CLSID;} }
+ public string ValueOfCLSID { get{return _ValueOfCLSID;} }
+
+ // レジストリのキーを返すので取り扱い注意
+ // (戻ってきた PSObject に対してSetやDelete等の書き込み系のメソッドを使わないように。)
+ private static Collection<PSObject> GetProgIdCollection()
+ {
+ using ( var invoker = new RunspaceInvoke() ) {
+ return invoker.Invoke(@"dir REGISTRY::HKEY_CLASSES_ROOT\CLSID -Include PROGID -Recurse");
+ }
+ }
+
- static Collection<PSObject> GetObjNames()
+ public static List<ProgIdInfo> GetProgIDs()
{
- string[] source = new string[] {
- @"$a = dir REGISTRY::HKEY_CLASSES_ROOT\CLSID -Include PROGID -Recurse",
- "$a | foreach {$_.GetValue(\"\")} | sort"
- };
+ var ret = new List<ProgIdInfo>();
- using ( var invoker = new RunspaceInvoke() ) {
- Collection<PSObject> tmp = invoker.Invoke(source[0]);
- Collection<PSObject> result = invoker.Invoke(source[1]);
- return result;
+ var psObjs = GetProgIdCollection();
+
+ foreach ( var psObj in psObjs ) {
+ ProgIdInfo t = NewProgIdInfo(psObj);
+ ret.Add(t);
}
+
+ return ret;
}
+ private static ProgIdInfo NewProgIdInfo(PSObject psObj)
+ {
+ var ret = new ProgIdInfo();
+ ret._psObj = psObj;
+ ret._ProgID = "";
+ ret._CLSID = "";
+ ret._ValueOfCLSID = "";
+
+ ret._ProgID = PSObject_Invoke_GetValue(psObj); // ProgID の取得
+
+ string[] regKeyPathOfProgID = (psObj.Members["Name"].Value??"").ToString().Split('\\');
+ // ["Name"] は HKEY_CLASSES_ROOT\CLSID\{xxxx..-xx..}\ProgID が返る
+
+ if ( regKeyPathOfProgID.Length >= 2 ) {
+ ret._CLSID = regKeyPathOfProgID[regKeyPathOfProgID.Length-2]; // parent of last node
+ }
+ string parentKey = MyPartialJoin("\\", regKeyPathOfProgID, 1, 1); // 先頭の @"HKEY_CLASSES_ROOT\" と末尾の @"\ProgID" を捨てる
+ ret._ValueOfCLSID = GetClassRootRegisterValue(parentKey, ""); // 既定の値を読み出す
+
+ return ret;
+ }
- static Collection<PSObject> GetMembers(string comObjectName)
+
+ // レジストリアクセス
+ private static string GetClassRootRegisterValue(string regKeyPath, string regValueName)
{
- Regex r = new Regex(@"^[._0-9A-Za-z]+$");
-
+ if ( regKeyPath == null || regKeyPath == "" ) {
+ return "";
+ }
+
+ Microsoft.Win32.RegistryKey regkey = Microsoft.Win32.Registry.ClassesRoot.OpenSubKey(regKeyPath);
+ if (regkey == null) {
+ return "";
+ }
+
+ try{
+ return (regkey.GetValue(regValueName)??"").ToString();
+ }
+ finally{
+ regkey.Close();
+ }
+ }
+
+ private static string MyPartialJoin(string separator, string[] ss, int startIndex, int numOfOmitLast)
+ {
+ // CopyメソッドなりLINQなり使った方が綺麗な気がするが、とりあえずこれで。
+ int n = ss.Length - startIndex - numOfOmitLast;
+ if ( n <= 0 ) { return ""; }
+ else {
+ string[] ss2 = new String[n];
+ for ( int i = startIndex; i < ss.Length - numOfOmitLast; i++) {
+ ss2[i-startIndex] = ss[i];
+ }
+ return String.Join(separator, ss2);
+ }
+ }
+
+ private static string PSObject_Invoke_GetValue(PSObject psObj)
+ {
+ object t = psObj.Methods["GetValue"].Invoke(new object[]{""}); // 規定値を取得する。 // レジスタアクセスでやってもよい
+ return (t??"").ToString(); // Microsoft PenInputPanel ControlがなぜかProgIdの値が空欄なので、処置しないと例外発生する
+ }
+}
+
+
+class MainForm:Form
+{
+ Button btnGetProgID;
+ Button btnGetMembers;
+// CheckBox chkFilter;
+ TextBox txtFilter;
+ SplitContainer spl;
+ TextBox txtProgID;
+ ListView lsvProgID;
+ ListView lsvMemb;
+
+ List<ProgIdInfo> allProgIDs; // filter処理を入れたいので、ListViewに直接持たせるのはやめた。
+
+
+ static Collection<PSObject> GetMembers(string comObjectName)
+ {
+ Regex r = new Regex(@"^[ ._0-9A-Za-z]+$"); // MS Remote など、スペースが入っている輩がいるので注意
+
if ( r.IsMatch(comObjectName) ) { // 危険なパラメータが混入しないように簡易チェック
string[] source = new string[] {
- "$t = New-Object -ComObject " + comObjectName,
+ "$t = New-Object -ComObject \"" + comObjectName + "\"",
"Get-Member -InputObject $t"
};
-
using ( var invoker = new RunspaceInvoke() ) {
try {
Collection<PSObject> tmp = invoker.Invoke(source[0]);
Collection<PSObject> result = invoker.Invoke(source[1]);
return result;
}
catch (System.Management.Automation.CmdletInvocationException e) {
// memo:
// VideoRenderCtl.VideoRenderCtl.1 選んだらCreateInstanceに失敗したっぽい例外を吐いたので
// とりあえずcatchしておく
Console.WriteLine(e);
return new Collection<PSObject>();
}
}
}
else {
return new Collection<PSObject>();// 要素数 0 の Collection を返しておく
}
}
-
-
MainForm()
{
Text = "COM Object Browser";
- btn = new Button();
- btn.Size = new System.Drawing.Size(130,25);
- btn.Text = "Get Object List";
- btn.Click += (sender,e)=>{ReloadLsvObj();};
- Controls.Add(btn);
-
- txtClassName = new TextBox();
- txtClassName.Location = new Point(160, 0);
- txtClassName.Width = 250;
- txtClassName.Text = "InternetExplorer.Application";
- Controls.Add(txtClassName);
-
- btnGetMembers = new Button();
- btnGetMembers.Location = new Point(430, 0);
- btnGetMembers.Size = new System.Drawing.Size(130,25);
- btnGetMembers.Text = "Get Members";
- btnGetMembers.Click += (sender,e)=>{ReloadLsvMemb(txtClassName.Text);};
- Controls.Add(btnGetMembers);
-
+ allProgIDs = new List<ProgIdInfo>();
+
spl = new SplitContainer();
- spl.Location = new Point(0, btn.Bottom);
+ spl.Location = new Point(0, 0);
+ spl.Dock = DockStyle.Fill;
spl.Orientation = Orientation.Vertical;
Controls.Add(spl);
////// 左 Panel ここから
- lsvObj = new ListView();
- lsvObj.View = View.Details;
- lsvObj.FullRowSelect =true;
- lsvObj.GridLines = true;
- lsvObj.Columns.Add("Name", 210, HorizontalAlignment.Left);
- lsvObj.Dock = DockStyle.Fill;
- lsvObj.SelectedIndexChanged += LsvObj_SelectedIndexChanged;
- spl.Panel1.Controls.Add(lsvObj);
+ btnGetProgID = new Button();
+ btnGetProgID.Location = new Point(0, 0);
+ btnGetProgID.Size = new System.Drawing.Size(80,25);
+ btnGetProgID.Text = "Get ProgIDs";
+ btnGetProgID.Click += (sender,e)=>{ReloadProgIDs();};
+ spl.Panel1.Controls.Add(btnGetProgID);
+
+ txtFilter = new TextBox();
+ txtFilter.Location = new Point(btnGetProgID.Right+10, 0);
+ txtFilter.Size = new System.Drawing.Size(90,25);
+ txtFilter.Text = "";
+ txtFilter.TextChanged += (sender,e)=>{UpdateLsvProgID();}; // 入力中も変化するので鬱陶しいが妥協する。
+ spl.Panel1.Controls.Add(txtFilter);
+ new ToolTip().SetToolTip(txtFilter, "Filter text for <ProgID>, <Value of CLSID>\r\nTo show all, clear the TextBox content.");
+
+ lsvProgID = new ListView();
+ lsvProgID.Location = new Point(0, 30);
+ lsvProgID.View = View.Details;
+ lsvProgID.FullRowSelect =true;
+ lsvProgID.GridLines = true;
+ lsvProgID.Columns.Add("ProgID", 210, HorizontalAlignment.Left);
+ lsvProgID.Columns.Add("CLSID", 210, HorizontalAlignment.Left);
+ lsvProgID.Columns.Add("Value of CLSID", 210, HorizontalAlignment.Left);
+ lsvProgID.SelectedIndexChanged += LsvProgID_SelectedIndexChanged;
+ lsvProgID.ColumnClick += LsvProgID_ColumnClick;
+ lsvProgID.ListViewItemSorter = new ListViewItemComparer(0); // index 0 のcolumnでソート設定しておく
+ spl.Panel1.Controls.Add(lsvProgID);
////// 左 Panel ここまで
-
+
+
////// 右 Panel ここから
+ txtProgID = new TextBox();
+ txtProgID.Location = new Point(0, 0);
+ txtProgID.Width = 250;
+ txtProgID.Text = "InternetExplorer.Application";
+ spl.Panel2.Controls.Add(txtProgID);
+ new ToolTip().SetToolTip(txtProgID, "ProgID");
+
+ btnGetMembers = new Button();
+ btnGetMembers.Location = new Point(txtProgID.Right+10, 0);
+ btnGetMembers.Size = new System.Drawing.Size(100,25);
+ btnGetMembers.Text = "Get Members";
+ btnGetMembers.Click += (sender,e)=>{ReloadLsvMemb(txtProgID.Text);};
+ spl.Panel2.Controls.Add(btnGetMembers);
+ new ToolTip().SetToolTip(btnGetMembers, "Get Members from ProgID in the TextBox");
+
lsvMemb = new ListView();
+ lsvMemb.Location = new Point(0, 30);
lsvMemb.View = View.Details;
lsvMemb.FullRowSelect =true;
lsvMemb.GridLines = true;
lsvMemb.Columns.Add("MemberType", 70, HorizontalAlignment.Left);
lsvMemb.Columns.Add("ReturnType", 100, HorizontalAlignment.Left);
lsvMemb.Columns.Add("Name", 130, HorizontalAlignment.Left);
lsvMemb.Columns.Add("Definition", 350, HorizontalAlignment.Left);
- lsvMemb.Dock = DockStyle.Fill;
spl.Panel2.Controls.Add(lsvMemb);
////// 右 Panel ここまで
-
ClientSize = new System.Drawing.Size(800,530);
Load += Form_Resize;
Resize += Form_Resize;
ResizeEnd += Form_Resize;
+ spl.SplitterMoving += Form_Resize;
+ spl.SplitterMoved += Form_Resize;
}
-
void Form_Resize(object sender, EventArgs e)
{
- spl.Size = new System.Drawing.Size(ClientSize.Width, ClientSize.Height - spl.Top);
+ lsvProgID.Size = new System.Drawing.Size(spl.Panel1.ClientSize.Width, spl.Panel1.ClientSize.Height - lsvProgID.Top);
+ lsvMemb.Size = new System.Drawing.Size(spl.Panel2.ClientSize.Width, spl.Panel2.ClientSize.Height - lsvMemb.Top);
}
-
- void ReloadLsvObj()
+
+ // 再取得処理
+ void ReloadProgIDs()
{
- Collection<PSObject> psObjs;
-
- btn.Enabled = false; // 処理に時間がかかるので操作を禁止しておく。※結局押したら意味ないかも
+ btnGetProgID.Enabled = false; // 処理に時間がかかるので操作を禁止しておく。※結局押したら意味ないかも
try {
- psObjs = GetObjNames();
+ allProgIDs = ProgIdInfo.GetProgIDs();
+ UpdateLsvProgID();
}
finally {
- btn.Enabled = true;
+ btnGetProgID.Enabled = true;
}
-
- lsvObj.Items.Clear();
- lsvObj.BeginUpdate();
+ }
+
+ // 再取得 or フィルタ変更時の lsvProgID 更新
+ void UpdateLsvProgID()
+ {
+ lsvProgID.Items.Clear();
+ lsvProgID.BeginUpdate();
try {
- foreach ( var t in psObjs ) {
- lsvObj.Items.Add(MyNewListItem_LsvObj(t));
+ foreach ( var t in allProgIDs ) {
+ string filterText = txtFilter.Text.Trim().ToLowerInvariant();
+
+ if ( filterText == "" ||
+ t.ProgID.ToLowerInvariant().Contains(filterText) ||
+ t.ValueOfCLSID.ToLowerInvariant().Contains(filterText) ) {
+ lsvProgID.Items.Add(MyNewListItem_LsvProgID(t));
+ }
+
}
}
finally {
- lsvObj.EndUpdate();
+ lsvProgID.EndUpdate();
}
}
-
void ReloadLsvMemb(string comName)
{
Collection<PSObject> psObjs;
-
btnGetMembers.Enabled = false;
try {
psObjs = GetMembers(comName);
- }
- finally {
- btnGetMembers.Enabled = true;
- }
-
- lsvMemb.Items.Clear();
- lsvMemb.BeginUpdate();
- try {
- foreach ( var t in psObjs ) {
- lsvMemb.Items.Add(MyNewListItem_LsvMemb(t));
+
+ lsvMemb.Items.Clear();
+ lsvMemb.BeginUpdate();
+ try {
+ foreach ( var t in psObjs ) {
+ lsvMemb.Items.Add(MyNewListItem_LsvMemb(t));
+ }
+ }
+ finally {
+ lsvMemb.EndUpdate();
+
}
}
finally {
- lsvMemb.EndUpdate();
+ btnGetMembers.Enabled = true;
}
}
- ListViewItem MyNewListItem_LsvObj(PSObject psObj)
+
+
+ ListViewItem MyNewListItem_LsvProgID(ProgIdInfo progId)
{
- var itm = new ListViewItem(new string[]{psObj.ToString()});
- itm.Tag = psObj;
+ var itm = new ListViewItem(new string[]{progId.ProgID, progId.CLSID, progId.ValueOfCLSID});
+ itm.Tag = progId;
return itm;
}
-
ListViewItem MyNewListItem_LsvMemb(PSObject psObj)
{
ListViewItem itm;
- Regex r = new Regex(@"^([^ ]+)\b");
- // フォーマットが複雑すぎて分解することが困難なため、戻り値の型(っぽい)部分だけを取り出す。追記:単語区切り\bだとドットで切れてしまうはずなので要見直し…
+ Regex r = new Regex(@"^([^ ]+)");
+ // フォーマットが複雑すぎて分解することが困難なため、戻り値の型(っぽい)部分だけを取り出す。
// - overloadが1行に入ってたりする (例: System.Random)
// - 仮引数名があったりなかったり
// - ()がない場合もある
// - ()がネストしている場合もある
// - 後ろに{get} {set} がつく場合もある
-
Match m = r.Match(psObj.Members["Definition"].Value.ToString());
//Match m = r.Match(psObj.ToString());
-
if ( m.Success ) {
string memberType = (psObj.Members["MemberType"].Value).ToString(); // PSMemberTypes (Method / Property など)
string returnType = m.Groups[1].Value;
string methodName = psObj.Members["Name"].Value.ToString();
string definition = psObj.Members["Definition"].Value.ToString();
itm = new ListViewItem(new string[]{memberType, returnType , methodName, definition});
}
else {
// 想定外の形式の場合は、とりあえずToString()して表示しておく
itm = new ListViewItem(new string[]{"", "", "", psObj.ToString()});
}
return itm;
}
+
+ void LsvProgID_SelectedIndexChanged(object sender, EventArgs e)
+ {
+ if (lsvProgID.SelectedIndices.Count != 1) {return;}
+ int index = lsvProgID.SelectedIndices[0];
+ ProgIdInfo progIdInfo = (ProgIdInfo)(lsvProgID.Items[index].Tag);
+ txtProgID.Text = progIdInfo.ProgID;
+ }
- void LsvObj_SelectedIndexChanged(object sender, EventArgs e)
+ void LsvProgID_ColumnClick(object sender, ColumnClickEventArgs e)
{
- if (lsvObj.SelectedIndices.Count != 1) {return;}
-
- int index = lsvObj.SelectedIndices[0];
- PSObject psObj = (PSObject)(lsvObj.Items[index].Tag);
-
- txtClassName.Text = psObj.ToString();
+ if ( e.Column >= 0 && e.Column < lsvProgID.Columns.Count ) {
+ lsvProgID.ListViewItemSorter = new ListViewItemComparer(e.Column);
+ }
+ }
+
+ public class ListViewItemComparer : IComparer
+ {
+ private int _column;
+ public ListViewItemComparer(int col) { _column = col; }
+
+ public int Compare(object obj1, object obj2)
+ {
+ string s1 = ((ListViewItem)obj1).SubItems[_column].Text;
+ string s2 = ((ListViewItem)obj2).SubItems[_column].Text;
+ return string.Compare(s1, s2, true); // 第3引数の true は、大文字小文字の差異を無視する指定(ignore case)
+ }
}
[STAThread]
static void Main(){
Application.Run(new MainForm());
}
}
+
+
```
判ったこと:
PSObjectのMembersプロパティやMethodsプロパティは、COMオブジェクトの情報は拾えないようである。。
機能追加したいこと:
・テキストコピーできるようにする
-・ListView上の検索(filtering)
+・ListView上の検索(filtering) → 済み
# コンパイル方法
```
csc ^
/r:C:\Windows\assembly\GAC_MSIL\System.Management.Automation\1.0.0.0__31bf3856ad364e35\System.Management.Automation.dll ^
xxxx.cs
```
# 参考サイト
- [C# 上で PowerShell スクリプトを実行 (++C++; //未確認飛行 C)](https://ufcpp.net/study/powershell/interop.html)
- [PSObjectクラス (Microsoft) ](https://docs.microsoft.com/en-us/dotnet/api/system.management.automation.psobject?view=pscore-6.2.0)