C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[13]: MissingMethodExceptionで死亡


(過去ログ 20 を表示中)

[トピック内 14 記事 (1 - 14 表示)]  << 0 >>

■8699 / inTopicNo.1)  MissingMethodExceptionで死亡
  
□投稿者/ yagiey (25回)-(2007/10/07(Sun) 13:53:09)

分類:[C#] 

C#にてとあるアプリケーションを作っていまして、7〜8割できたところです。
環境は、XP Pro、SP2、Visual Studio 2005 Standardです。
最近気づいたのですが(遅すぎ?)、このアプリケーションのexeを取り出して他のプロジェクトに突っ込むと、なんとアプリケーションで使ってたクラスなどpublicで定義した要素が丸見えではないですか。

これはマズいとばかりに、とりあえず、公開したくないものをpublicからinternalへちまちま変更していくことにしました。
...でも、これはすぐに挫折しました。
1つのpublicをinternalへ変更するたびに、芋づる式に「アクセシビリティ云々」というコンパイルエラーが発生してしまいます。
終りが見えません。

そこでソリューション内部のpublicを一括でinternalへ置換しました。
もちろん、コンパイルエラーがたくさん出ました。
でも、頑張れば何とかなる数(400個程度)でしたし、エラーの種類もほとんど同じものだったので、これでやってみました。
(ちなみにそのエラーとは、internalに置換したことにより、実装されたインターフェースメンバがinternalになってしまったから、などです)

頑張ってコンパイルエラーを全部摘み取り、ビルドできる状態になりました。
...しかし、今度は実行時エラーが待っていました。
しかも、今までに見たことのない、「MissingMethodException」なる例外です。
ちょっと調べてみると、リフレクション関係ではないですか...。
MSDNによると、「存在しないメソッドに動的にアクセスしようとした場合にスローされる例外。」だそうです。
# いや、動的になんてやってないし。少なくとも自分は。


以上のような状況です。
さて、どこらへんでMissingMethodException例外が投げられるかというと...。
すみません。まんまソースコードは出せるはずがない(長すぎるし、一応商用アプリだし)ので、すごく簡単に説明します。
以下のようなDataGridViewが登場します。


・internal abstract class ProperySettingsGridView : DataGridView { ... }
 コレクションの全要素のプロパティを編集するためのDataGridView。
 内部にobject型の参照をもっており、こいつが編集対象のコレクションを参照している。
 コレクションの各要素を行、要素の各プロパティを列にしてセルを編集することで特定の要素の特定のプロパティを編集する。
 コレクションを特定したクラスを派生クラスとして利用する。
 派生クラスに共通に持たせたい機能(アンドゥなど)も実装。
 VirtualModeはtrue。

・internal class RecordsSettingsGridView : ProperySettingsGridView { ... }
 PropertySettingsGridViewの、コレクションをRecordsクラスに特定したバージョン。


このRecordsSettingsGridViewをフォームRecordsSettingsFormに貼り付けて利用します。
ある情報のまとまり(Records)を編集するためのフォームです。
以下のように、RecordsSettingsFormのコンストラクタでRecordsのインスタンスをRecordsSettingsGridViewに渡しています。
(実際は、コンストラクタから呼ばれるメンバ初期化用の関数Initでやってます。下のスタックトレースからも分かるかと思います)

internal RecordsSettingsForm(Records records)
{
 InitializeComponent();
 ...
 gridView.Records = records; // gridViewはRecordsSettingsGridViewです
 ...
}

ってやってて、さらにRecordsSettingsGridViewのRecordsプロパティの定義は以下です。

[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public Records Records
{
 get { return (Records)base.Collection; }
 set
 {
  ...
  if (value == null) base.Collection = null;
  else base.Collection = value.Clone() as Records;
  
  RowCount = Records.Count; // ここでMissingMethodException
  ...
  ...
 }
}


コメントとおり、ここで例外が投げられます。
例外のMessageは「このプロジェクトで、引数なしコンストラクタは定義されていません。」です。
StackTraceも書いておきます。
あ、アプリケーションが特定されそうな箇所は伏せています :-)

  場所 System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean noCheck, Boolean& canBeCached, RuntimeMethodHandle& ctor, Boolean& bNeedSecurityCheck)
  場所 System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean fillCache)
  場所 System.RuntimeType.CreateInstanceImpl(Boolean publicOnly, Boolean skipVisibilityChecks, Boolean fillCache)
  場所 System.Activator.CreateInstance(Type type, Boolean nonPublic)
  場所 System.Windows.Forms.DataGridViewTextBoxCell.Clone()
  場所 System.Windows.Forms.DataGridView.CompleteCellsCollection(DataGridViewRow dataGridViewRow)
  場所 System.Windows.Forms.DataGridView.get_RowTemplateClone()
  場所 System.Windows.Forms.DataGridViewRowCollection.Add(Int32 count)
  場所 System.Windows.Forms.DataGridView.set_RowCount(Int32 value)
  場所 --------.RecordsSettingsGridView.set_Records(Records value) 場所 ********
  場所 --------.RecordsSettingsForm.Init(Records records, ******** ********, ++++++++ ++++++++, Boolean @@@@@@@@) 場所 ********
  場所 --------.RecordsSettingsForm..ctor(Records records, ******** ********, ++++++++ ++++++++, Boolean @@@@@@@@) 場所 ********
  場所 --------.RecordsSettingsForm..ctor(Records records, ******** ********, ++++++++ ++++++++) 場所 ********
  場所 --------.RecordsSettingsForm..ctor(Records records, ******** ********) 場所 ********
  場所 --------.========.EditRecords() 場所 ********
  場所 --------.========.btnSettingRecords_Click(Object sender, EventArgs e) 場所 ********
  場所 System.Windows.Forms.ToolStripItem.RaiseEvent(Object key, EventArgs e)
  場所 System.Windows.Forms.ToolStripButton.OnClick(EventArgs e)
  場所 System.Windows.Forms.ToolStripItem.HandleClick(EventArgs e)
  場所 System.Windows.Forms.ToolStripItem.HandleMouseUp(MouseEventArgs e)
  場所 System.Windows.Forms.ToolStripItem.FireEventInteractive(EventArgs e, ToolStripItemEventType met)
  場所 System.Windows.Forms.ToolStripItem.FireEvent(EventArgs e, ToolStripItemEventType met)
  場所 System.Windows.Forms.ToolStrip.OnMouseUp(MouseEventArgs mea)
  場所 System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons button, Int32 clicks)
  場所 System.Windows.Forms.Control.WndProc(Message& m)
  場所 System.Windows.Forms.ScrollableControl.WndProc(Message& m)
  場所 System.Windows.Forms.ToolStrip.WndProc(Message& m)
  場所 System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
  場所 System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
  場所 System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
  場所 System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)
  場所 System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(Int32 dwComponentID, Int32 reason, Int32 pvLoopData)
  場所 System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)
  場所 System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)
  場所 System.Windows.Forms.Application.Run(Form mainForm)
  場所 --------.Program.Main(String[] args) 場所 ********
  場所 System.AppDomain.nExecuteAssembly(Assembly assembly, String[] args)
  場所 System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
  場所 Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
  場所 System.Threading.ThreadHelper.ThreadStart_Context(Object state)
  場所 System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
  場所 System.Threading.ThreadHelper.ThreadStart()


「引数なしコンストラクタ」ってのはTraceによるとDataGridViewTextBoxCellのコンストラクタのことでしょうか。
でも、DataGridViewTextBoxCellにはちゃんと引数なしのpublicコンストラクタがあるしなぁ。
もうわけがわかりません。
いったい何が起きているのでしょうか。
internalに置換する前は起きなかったバグなので、置換したことが原因だと思いますが。


以上のことから何か分かる方、なんでも良いですので助言をお待ちしています。
よろしくお願いします。

長くなってすみません。全部読んでくださった方、ありがとうございます:-)
あと、詳しいことを書くことができず、申し訳ありません。

引用返信 編集キー/
■8704 / inTopicNo.2)  Re[1]: MissingMethodExceptionで死亡
□投稿者/ 渋木宏明(ひどり) (453回)-(2007/10/07(Sun) 21:36:53)
渋木宏明(ひどり) さんの Web サイト
> これはマズいとばかりに、とりあえず、公開したくないものをpublicからinternalへちまちま変更していくことにしました。

本当にマズいんですか?

> MSDNによると、「存在しないメソッドに動的にアクセスしようとした場合にスローされる例外。」だそうです。
> # いや、動的になんてやってないし。少なくとも自分は。

たとえば、データバインドなんか使ってたりすると、内部的にリフレクションが使われる可能性は大いにあります。

ちまちま変更する前に、「本当に public ではマズいのか?」について、もう一度検討してみた方がよろしいんじゃないかと思います。

引用返信 編集キー/
■8709 / inTopicNo.3)  Re[2]: MissingMethodExceptionで死亡
□投稿者/ yagiey (26回)-(2007/10/08(Mon) 19:52:40)
> ちまちま変更する前に、「本当に public ではマズいのか?」について、もう一度検討してみた方がよろしいんじゃないかと思います。
たいていののクラスをpublicで定義していたもので。
あと、上からの (ry。
すみません。internalにすることは決定事項のようで、僕にはどうすることもできません。


とりあえずMissingMethodExceptionの件は解決しましたので、報告します。

例外が投げられたところ(StackTraceのトップ)は、.NET Frameworkの奥深くで、MSDNで探しても見つからない場所でした。
そこで、MSDNのキーワード検索で情報が得られる所までStackTraceをさかのぼってみました。
具体的に言うと、System.Activator.CreateInstanceでようやくMSDNに情報が出てきました。

何を"System.Activator.CreateInstance"しているのか分からなかったのですが、StackTraceからおそらくDataGridViewTextBoxCellを"System.Activator.CreateInstance"しているのだろうと推測しました。
DataGridViewTextBoxCellをCloneするとき、リフレクションを使ってインスタンス化しているようですね。

ここでピンときました。
DataGridViewTextBoxCellを継承したクラスを定義していたんです。
もしかしたら、それがinternalに変わったことが原因かもと思い、それらのコンストラクタをpublicにしました。
ビルドして実行すると、例外がなげられず、うまくいきました。

...また同じようなことが違う所で起きる可能性はあるわけで。
テストのやり直し。orz
あぁ、なんと危なっかしい開発なのでしょう。


アクセス修飾子とリフレクションの関係をもっと勉強したいと思います。
あと、メンバのアクセス権同様クラスのそれについてもしっかり設計しなくちゃなぁ、と思いました。
解決済み
引用返信 編集キー/
■8712 / inTopicNo.4)  Re[3]: MissingMethodExceptionで死亡
□投稿者/ 渋木宏明(ひどり) (454回)-(2007/10/08(Mon) 22:00:58)
渋木宏明(ひどり) さんの Web サイト
2007/10/08(Mon) 22:15:00 編集(投稿者)

> すみません。internalにすることは決定事項のようで、僕にはどうすることもできません。

「public にすることがマズい」のならば、どうして

> もしかしたら、それがinternalに変わったことが原因かもと思い、それらのコンストラクタをpublicにしました。
> ビルドして実行すると、例外がなげられず、うまくいきました。

という対応が許されるのでしょう?

隠すなら全部隠さないと意味がないように思いますが。

引用返信 編集キー/
■8715 / inTopicNo.5)  Re[4]: MissingMethodExceptionで死亡
□投稿者/ yagiey (27回)-(2007/10/09(Tue) 07:16:29)
> 「public にすることがマズい」のならば、どうして
>
>>もしかしたら、それがinternalに変わったことが原因かもと思い、それらのコンストラクタをpublicにしました。
>>ビルドして実行すると、例外がなげられず、うまくいきました。
>
> という対応が許されるのでしょう?
>
> 隠すなら全部隠さないと意味がないように思いますが。


当該クラスのアクセス修飾子はinternalのままで、そのクラスのコンストラクタをpublicにしたんです。
するとリフレクションに関する例外が投げられなくなったし、違うアセンブリからはインスタンス化もできない状態になりましたよ。
自分としては期待した動きだったので、これで良しとしましたが、ダメでしょうか...?
引用返信 編集キー/
■8718 / inTopicNo.6)  Re[5]: MissingMethodExceptionで死亡
□投稿者/ 囚人 (193回)-(2007/10/09(Tue) 09:05:40)
無駄な労力である事を本気で訴えた方が良いと思いますよ。

百歩譲って、exe に定義されているものは internal に出来たとしても、dll に定義されているものは public にするしかないものが必ずありますよね。

マズイ理由が分からないんですが、何故なんでしょうか?

他人に中身が見られるからマズイのだとしたら、public だろうが internal だろうが private だろうが差はないですよ。
引用返信 編集キー/
■8727 / inTopicNo.7)  Re[6]: MissingMethodExceptionで死亡
□投稿者/ yagiey (28回)-(2007/10/09(Tue) 10:21:42)
> 無駄な労力である事を本気で訴えた方が良いと思いますよ。

出来上がったexeを別プロジェクトに加えて実験したとき、exe内の諸々のクラスが利用できるところを確認して、なんだか嫌な感じだなーって思いました。
確かにソースコードを公開しているわけではないのですが、それでも他人から利用されるということに関して、なんかいやな感じだったんです。

恥ずかしながら、僕はそんな主観的な判断でしかないのです。
他の人がどういう理由でpublicを拒むのか知りませんが。

正直言うと、publicではマズい理由もpublicでも構わない理由もよく理解できていません。
なので、無駄な労力である理由を理路整然と説明し、相手を納得させる自信がないのです...。 orz
引用返信 編集キー/
■8728 / inTopicNo.8)  Re[7]: MissingMethodExceptionで死亡
□投稿者/ 囚人 (196回)-(2007/10/09(Tue) 10:39:38)
>他の人がどういう理由でpublicを拒むのか知りませんが。

これが分からないと、何を説明して良いのかもわかりませんね。

ありがちな理由が
1. 赤の他人が public なクラスなどを使える。
2. 赤の他人にコードが見られる。

2 はさっきも言ったように、pubic でも private でも差はないです。
1 は使おうと思えば、public でも private でも大差なく使えます。private はほんのちょっとだけ苦労するかな。

しかし、public や private などのアクセス修飾の目的はそういう事ではなく、プログラミング上のスコープを制限し、間違いを抑える事や、考える範囲を狭くして生産性をあげる事が目的です。

従って「他人に見られるから public では困る」という考えはベクトルが違うので、他の手段(例えば難読化など)を模索すべきでしょう。

引用返信 編集キー/
■8755 / inTopicNo.9)  Re[8]: MissingMethodExceptionで死亡
□投稿者/ yagiey (29回)-(2007/10/09(Tue) 15:35:36)
2 はソースコードを公開するわけではないので、他人にコードが見られることはないかと思います。
1 に関して実験してみました。
リフレクションを用いると、アセンブリ外部に非公開なクラスをインスタンス化、さらにはそのインスタンスのprivateメソッドですら実行できました...。

ともかく、上の人に「internalでもpublicでもprivateでも、他のアセンブリから利用可能ですよ」と提言してみます。
どうなるか分かりませんが。

解決済み
引用返信 編集キー/
■8757 / inTopicNo.10)  Re[9]: MissingMethodExceptionで死亡
□投稿者/ 囚人 (199回)-(2007/10/09(Tue) 15:42:30)
>2 はソースコードを公開するわけではないので、他人にコードが見られることはないかと思います。

いや、逆アセンブルが非常に容易で、更に C# のソースコードに戻す事も容易です。勿論、完全に戻るわけではありませんが(コメントやローカル変数の名前は残らない)。
引用返信 編集キー/
■8761 / inTopicNo.11)  Re[10]: MissingMethodExceptionで死亡
□投稿者/ yagiey (30回)-(2007/10/09(Tue) 16:23:11)
逆アセンブルのことを言ってらっしゃったのか...。
そこまで考えが及びませんでした。

> 勿論、完全に戻るわけではありませんが(コメントやローカル変数の名前は残らない)。

ということは、クラス名やメソッド名やフィールド名は残るのかな。
...って、残るんでしょうね。リフレクションで参照できるってことは、メタデータとして残っているわけですから。
ということは、どんなクラスなのか大体は察しがつくわけで、なおさらinternalの件は意味がないような気がしてきた。
ほぼオープンソース?(言いすぎ?)

だめだこりゃー。 orz

.NETで開発するということはそういうことなんですね。
勉強になりました。
解決済み
引用返信 編集キー/
■8766 / inTopicNo.12)  Re[11]: MissingMethodExceptionで死亡
□投稿者/ 魔界の仮面弁士 (461回)-(2007/10/09(Tue) 17:50:05)
> ということは、クラス名やメソッド名やフィールド名は残るのかな。
> ...って、残るんでしょうね。リフレクションで参照できるってことは、メタデータとして残っているわけですから。
> ということは、どんなクラスなのか大体は察しがつくわけで、なおさらinternalの件は意味がないような気がしてきた。

Reflector for .NET をダウンロードして、作成した exe を読み込ませてみるべし。
http://www.aisto.com/roeder/dotnet/
http://www.codeplex.com/reflectoraddins
解決済み
引用返信 編集キー/
■8805 / inTopicNo.13)  Re[12]: MissingMethodExceptionで死亡
□投稿者/ yagiey (31回)-(2007/10/10(Wed) 21:25:49)
> Reflector for .NET をダウンロードして、作成した exe を読み込ませてみるべし。

ぬぁんじゃこりゃー!!
丸見くぁwせdrftgyふじこlp;
解決済み
引用返信 編集キー/
■8828 / inTopicNo.14)  Re[13]: MissingMethodExceptionで死亡
□投稿者/ やまだ (1回)-(2007/10/11(Thu) 02:55:07)
蛇足かもしれませんが。

> .NETで開発するということはそういうことなんですね。
ちなみにJavaでもその辺は同じです。というかリバースの容易さにかけては Java が先輩ですね。

囚人さんもおっしゃってますが、難読化ツールというものがあります。多少解読しにくくなります。
Standard Edition をお使いだとのことですので、Dotfuscator Community Edition ってのがツールメニューにあるはずだと思います。それを使ってみると多少変わると思いますよ。
#私はJavaでしか難読化ツールを使ったことがないので、どこが違うとはいえませんが。

より高度な難読化がしたければ上位製品を買え、ということなのでしょうけど、まずは付属のツールで難読化を試してみては?

解決済み
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -