🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

Q&A

解決済

2回答

4229閲覧

デリゲートと引数の型が違うメソッドをイベントに登録したい

tuyudaku

総合スコア75

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

0グッド

0クリップ

投稿2021/03/12 08:54

編集2021/03/12 11:14

タイトルだとちょっと意味が分かりづらいと思います。
自分もなんて表現したらいいかわからずこんな感じになってしまいました。
少し長くなってしまいますがご了承ください。

現在、DLLを動的にロードして使用するようにコーディングをしています。
細かい理由は言えませんが、ざっくりいうと普通に参照してDLLで定義されたものをインスタンス化させることができません。

DLLで定義されているイベントに対して、自分で定義したメソッドを登録しようとしています。

C#

1public delegate void DLLEvent(ushort[] data1, DLLInterface data2); 2public event DLLEvent DLLHandle;

このような感じで定義されているDLLHandleというイベントに対して

C#

1EventInfo info = Dlltype.GetEvent("DLLHandle"); 2Delegate delegate = Delegate.CreateDelegate(info.EventHandlerType, this, "MyHandle") 3info.AddEventHandler( instance, delegate );

と、このようにしてイベントへの登録をしようとしています。
instanceという変数はActivator.CreateInstance()を使用して取得した、イベントを持っているクラスのインスタンスです。

ここで問題になっているのが、デリゲートのDLLEventの引数に含まれるDLLInterfaceというDLL独自の型です。
このイベントに登録するメソッドを普通に作ろうとすれば
private void MyHandle( double[] data1, DLLInterface data2)
と作ると思うのですが。

最初に書いた、普通に参照してDLLで定義されたものをインスタンス化させることができないという問題のせいで、DLLInterfaceを引数にすることができません。
とりあえずDLLInterfaceの部分をobjectにしてやってみたのですが。

ターゲット メソッドとデリゲート型との間に、シグネチャまたはセキュリティ透過性の互換性がないため、ターゲット メソッドにバインドできません。

という例外がCreateDelegateで発生してしまいます。
色々検索してみたのですが解決方法がわからず困っています...
そもそも使い方が間違っているという可能性もありますが...

何か心当たり、知識がある方がいらっしゃいましたら
助言を頂けないでしょうか?お願いいたします。

###追記
問題が発生するアプリを作ってみました。
System.dllが私が実際に使いたい他社製DLLの代わりです。
本来であればElapsedイベントの引数(Delegate?)は
(object sender, ElapsedEventArgs e)なのでOnElapsedも同様の引数にしたいところなのですが、
ElapsedEventArgsSystem.dll内で定義されているものなのでそのまま使うことができません。
これを何とかして基底型?汎用型?のようなものからキャストしてデータを扱おうと思ったのですが、
そもそもイベントの登録ができない、というかデリゲートの作成ができなくて困っているという状況です。

分かりにくい質問で申し訳ありません、よろしくお願いいたします。

C#

1namespace ConsoleApp1 2{ 3 class Program 4 { 5 static object timer_instance = null; 6 static myClass c = null; 7 8 static void Main(string[] args) 9 { 10 c = new myClass(); 11 12 var a = Assembly.LoadFrom( "C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\System.dll" ); 13 var type = a.GetType("System.Timers.Timer"); 14 timer_instance = Activator.CreateInstance(type, new object[]{ 100.0 }); 15 // Timer timer = new Timer() という風に使うことが出来ない状態 16 17 var event_info = type.GetEvent("Elapsed"); 18 var _delegate = Delegate.CreateDelegate(event_info.EventHandlerType, c, "OnElapsed"); 19 event_info.AddEventHandler(timer_instance, _delegate); 20 21 var method = type.GetMethod("Start"); 22 method.Invoke(timer_instance, null); 23 24 Task.Delay(1000000).Wait(); 25 } 26 } 27 28 class myClass 29 { 30 // ElapsedEventArgs 31 private void OnElapsed( object sender, object e ) 32 { 33 Console.WriteLine("test"); 34 } 35 } 36} 37

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Zuishin

2021/03/12 09:01

> 普通に参照してDLLで定義されたものをインスタンス化させることができない がどういう意味かよくわかりませんが、私の想像している意味なら、もう一つ DLL を作り、そこで DLLInterface を宣言して、それを EXE と DLL から参照しましょう。
tuyudaku

2021/03/12 10:11 編集

>がどういう意味かよくわかりませんが そうですよね... なんといえばいいのかわからないのですが...制約だと思ってください なので、Wrapperのように間にDLLを挟んだとしてもダメで とにかくDLLのクラスなどを使いたい場合は、AssemblyやType、Activator.CreateInstance() などを駆使して動的に読み込んで使う必要があります。 その時に引数にDLLによって定義されている型を使うので困っているという状況です。 分かりにくくて申し訳ないです...
Zuishin

2021/03/12 10:21

いえ、「普通に」や「インスタンス化」などがわかりませんし、ラッパーの話もしていません。 断片的なコードではなく、コンパイル可能で問題の例外が発生する最小のコードで説明すれば伝わりやすくなるかもしれません。
tuyudaku

2021/03/12 11:03

>断片的なコードではなく、コンパイル可能で問題の例外が発生する最小のコードで説明すれば伝わりやすくなるかもしれません。 ご指摘ありがとうございます。 何とかサンプルのようなものを作ってみました。 見ていただけると幸いです...これでもわかりにくかったら申し訳ありません...
退会済みユーザー

退会済みユーザー

2021/03/13 04:59

前回の質問の続きみたいな感じですかね?DLLInterfaceの型が取得できないとどうにもならないと思います。
guest

回答2

0

自己解決

自己?解決しました
このようにすることで、object型でも登録ができました。

C#

1namespace ConsoleApp1 2{ 3 class Program 4 { 5 static object timer_instance = null; 6 static myClass c = null; 7 8 static void Main(string[] args) 9 { 10 c = new myClass(); 11 12 var a = Assembly.LoadFrom( "C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\System.dll" ); 13 var type = a.GetType("System.Timers.Timer"); 14 timer_instance = Activator.CreateInstance(type, new object[]{ 100.0 }); 15 // Timer timer = new Timer() という風に使うことが出来ない状態 16 17 var event_info = type.GetEvent("Elapsed"); 18 //var _delegate = Delegate.CreateDelegate(event_info.EventHandlerType, c, "OnElapsed"); 19 var methodInfo = c.GetType().GetMethod("OnElapsed", BindingFlags.NonPublic | BindingFlags.Instance); 20 var _delegate = Delegate.CreateDelegate(event_info.EventHandlerType, c, methodInfo); 21 event_info.AddEventHandler(timer_instance, _delegate); 22 23 var method = type.GetMethod("Start"); 24 method.Invoke(timer_instance, null); 25 26 Task.Delay(1000000).Wait(); 27 } 28 } 29 30 class myClass 31 { 32 // ElapsedEventArgs 33 private void OnElapsed( object sender, object e ) 34 { 35 var a = Assembly.LoadFrom( "C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\System.dll" ); 36 var type = a.GetType("System.Timers.ElapsedEventArgs"); 37 var info = type.GetProperty("SignalTime"); 38 var time = (DateTime)info.GetValue(e); 39 Console.WriteLine("test:" + time.ToString()); 40 } 41 } 42} 43

投稿2021/03/14 13:00

tuyudaku

総合スコア75

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2021/03/14 16:15

メソッド名ではなく、MethodInfoからだとCreateDelegate成功するんですね…
tuyudaku

2021/03/15 00:06

だったらメソッド名でもいいじゃないか...!! って気持ちになって釈然としませんが、なぜかこの方法ならできるみたいです!
guest

0

DynamicMethodを経由したら一応イベントの登録には成功しましたが、使用方法的に問題ないかは全く調査・検証してないです。

【参考にしたページ】
方法: リフレクションを使用してデリゲートをフックする

C#

1using System; 2using System.Threading.Tasks; 3using System.Reflection; 4using System.Reflection.Emit; 5 6namespace ConsoleCore 7{ 8 class Program 9 { 10 static dynamic _Timer = null; 11 static Delegate _ElapsedHandler; 12 13 static void Main(string[] args) 14 { 15 var a = Assembly.LoadFrom("C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\System.dll"); 16 var typeTimer = a.GetType("System.Timers.Timer"); 17 _Timer = Activator.CreateInstance(typeTimer, new object[] { 100.0 }); 18 19 var dynamicElapsed = new DynamicMethod( 20 "", 21 typeof(void), 22 new Type[] { typeof(object), typeof(object) }); 23 24 var il = dynamicElapsed.GetILGenerator(); 25 il.Emit(OpCodes.Ldarg_0); 26 il.Emit(OpCodes.Ldarg_1); 27 il.Emit(OpCodes.Call, typeof(MyClass).GetMethod("OnElapsed")); 28 il.Emit(OpCodes.Ret); 29 30 var evElapsed = typeTimer.GetEvent("Elapsed"); 31 _ElapsedHandler = dynamicElapsed.CreateDelegate(evElapsed.EventHandlerType); 32 evElapsed.AddEventHandler( 33 _Timer, 34 _ElapsedHandler); 35 _Timer.Start(); 36 37 Task.Delay(1000000).Wait(); 38 } 39 } 40 41 class MyClass 42 { 43 public static void OnElapsed(object sender, object e) 44 { 45 Console.WriteLine("test"); 46 } 47 } 48} 49

投稿2021/03/13 08:41

編集2021/03/13 10:25
退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問