1- using System ;
1+ using System ;
22using System . Collections . Generic ;
33using System . Linq ;
4- using System . Linq . Expressions ;
54using System . Reflection ;
5+ using System . Text . Json ;
66using System . Windows . Forms ;
77
88namespace Crystallography . Controls ;
99
10- [ Serializable ( ) ]
11- public class MacroTriger ( string target , bool debug , object [ ] obj , string macroName = "" )
10+ // 注: クラス名 "MacroTriger" は typo ("Trigger") だが、IPAnalyzer 等から
11+ // `new MacroTriger("PDI", ...)` で参照されているため改名不可。
12+ //
13+ // 260414Cl 全面改修:
14+ // 旧版は [Serializable] + Clipboard.SetDataObject(triger) で送信していたが、
15+ // .NET 9 以降 WinForms Clipboard の BinaryFormatter ベース経路が廃止されサイレント
16+ // 失敗していた (csproj の EnableUnsafeBinaryFormatterSerialization は .NET 9 以降 no-op)。
17+ //
18+ // 対策: DataObject を継承し、コンストラクタ内で自身を JSON シリアライズして byte[] を
19+ // format=typeof(MacroTriger).FullName で SetData する。これにより呼び出し側
20+ // (IPAnalyzer など) は従来通り `Clipboard.SetDataObject(new MacroTriger(...))` だけで
21+ // 済み、IPAnalyzer 本体のコードを 1 行も触らずに済む。
22+ //
23+ // 受信側 (PDIndexer) は GetData(typeof(MacroTriger)) で byte[] を受け取って
24+ // MacroTriger.Deserialize(byte[]) で復元する (旧版は MacroTriger インスタンスを
25+ // そのまま受け取っていたが、現在は byte[] 経由になる)。
26+ public class MacroTriger : DataObject
1227{
13- public bool Debug { set ; get ; } = debug ;
14- public string Target { set ; get ; } = target ;
15- public string MacroName = macroName ;
16- public object [ ] Obj = obj ;
28+ public string Target { get ; set ; }
29+ public bool Debug { get ; set ; }
30+ public string MacroName { get ; set ; }
31+ public object [ ] Obj { get ; set ; }
32+
33+ // 260414Cl JSON デシリアライズ用 parameterless ctor
34+ public MacroTriger ( ) { }
35+
36+ public MacroTriger ( string target , bool debug , object [ ] obj , string macroName = "" )
37+ {
38+ Target = target ;
39+ Debug = debug ;
40+ Obj = obj ;
41+ MacroName = macroName ;
42+
43+ // 自身を JSON 化して byte[] として登録。
44+ // クリップボード経由の cross-process 転送で受信側はこの byte[] を受け取る。
45+ SetData ( typeof ( MacroTriger ) , Serialize ( ) ) ;
46+ }
47+
48+ private static readonly JsonSerializerOptions JsonOpts = new ( ) { WriteIndented = false } ;
49+
50+ /// <summary>JSON シリアライズ。クリップボード SetData の中身として使う。</summary>
51+ public byte [ ] Serialize ( ) => JsonSerializer . SerializeToUtf8Bytes ( this , JsonOpts ) ;
52+
53+ /// <summary>
54+ /// JSON byte[] から <see cref="MacroTriger"/> を復元する。
55+ /// JSON で復元すると <see cref="Obj"/> 配列要素は <see cref="JsonElement"/> になるため
56+ /// プリミティブ型へ戻す。
57+ /// </summary>
58+ public static MacroTriger Deserialize ( byte [ ] json )
59+ {
60+ var t = JsonSerializer . Deserialize < MacroTriger > ( json , JsonOpts ) ;
61+ if ( t ? . Obj != null )
62+ {
63+ for ( int i = 0 ; i < t . Obj . Length ; i ++ )
64+ {
65+ if ( t . Obj [ i ] is JsonElement e )
66+ {
67+ t . Obj [ i ] = e . ValueKind switch
68+ {
69+ JsonValueKind . Number => e . TryGetInt64 ( out var l ) ? l : e . GetDouble ( ) ,
70+ JsonValueKind . String => e . GetString ( ) ,
71+ JsonValueKind . True => true ,
72+ JsonValueKind . False => false ,
73+ JsonValueKind . Null => null ,
74+ _ => e . ToString ( )
75+ } ;
76+ }
77+ }
78+ }
79+ return t ;
80+ }
1781}
1882
19- [ Serializable ( ) ]
83+ [ Serializable ]
2084public class MacroBase
2185{
86+ // mainObject は派生クラス側 (PDIndexer / ReciPro / IPAnalyzer の Macro) から
87+ // FormMain インスタンスを保持するために使われる。dynamic は scope object 経由で
88+ // SetMacroToMenu を呼ぶための簡易ディスパッチ用。完全 typed 化はスコープ外。
2289 public dynamic mainObject ;
23- public string [ ] Help => [ .. help ] ;
2490 public string ScopeName = "" ;
2591 public List < string > help = [ ] ;
92+ public string [ ] Help => [ .. help ] ;
2693
2794 public MacroBase ( dynamic _main , string scopeName )
2895 {
2996 mainObject = _main ;
3097 ScopeName = scopeName ;
3198 }
3299
33- public void SetMacroToMenu ( string [ ] name )
34- {
35- mainObject . SetMacroToMenu ( name ) ;
36- }
37-
38-
100+ // 260414Cl virtual 化 (旧: 非 virtual)。FormMacro.obj が MacroBase 型に
101+ // なったあとも、IPAnalyzer 側 Macro が override で固有処理を維持できるように。
102+ public virtual void SetMacroToMenu ( string [ ] name ) => mainObject . SetMacroToMenu ( name ) ;
39103}
40104
41- [ Serializable ( ) ]
42- public class MacroSub
105+ [ Serializable ]
106+ public class MacroSub ( Control _context )
43107{
44- private readonly Control context ;
45- public MacroSub ( Control _context )
46- {
47- context = _context ;
48- }
49-
50- //スレッド間で安全にコントロールを操作する、関数群
51- public Type Execute < Type > ( Expression < Func < Type > > expression ) => Execute < Type > ( context , expression . Compile ( ) , null ) ;
52-
53- public void Execute ( Expression < Action > expression ) => Execute ( context , expression . Compile ( ) , null ) ;
54-
55- //public bool Execute(Func<bool> func) => Execute<bool>(func);
56-
57- //public string Execute(Func<string> func) => Execute<string>(func);
58-
59- //public string[] Execute(Func<string[]> func) => Execute<string[]>(func);
60-
61- // public double[] Execute(Func<double[]> func) => Execute<double[]>(func);
62-
63- //public int[] Execute(Func<int[]> func) => Execute<int[]>(func);
64-
65- //public int Execute(Func<int> func) => Execute<int>(func);
66-
67- //public double Execute(Func<double> func) => Execute<double>(func);
68-
69- public static Type Execute < Type > ( Control _context , Delegate process ) => Execute < Type > ( _context , process , null ) ;
70-
71- public Type Execute < Type > ( Delegate process ) => Execute < Type > ( context , process , null ) ;
72-
73- public static void Execute ( Control _context , Delegate process ) => Execute ( _context , process , null ) ;
74-
75- public void Execute ( Delegate process ) => Execute ( context , process , null ) ;
76-
77- public static Type Execute < Type > ( Control _context , Delegate process , params object [ ] args )
78- {
79- #region
80- /*if (context == null)
81- {
82- throw new ArgumentNullException("context");
83- }
84- if (process == null)
85- {
86- throw new ArgumentNullException("process");
87- }
108+ private readonly Control context = _context ;
88109
89- if (!(context.IsHandleCreated))
90- {
91- return null;
92- }
93- */
94- #endregion
95- return _context . InvokeRequired ? ( Type ) _context . Invoke ( process , args ) : ( Type ) process . DynamicInvoke ( args ) ;
96- }
97-
98- public static void Execute ( Control _context , Delegate process , params object [ ] args )
99- {
100- #region
101- /*if (context == null)
102- {
103- throw new ArgumentNullException("context");
104- }
105- if (process == null)
106- {
107- throw new ArgumentNullException("process");
108- }
110+ // 260414Cl 全面整理:
111+ // 旧: Execute<Type>(Expression<Func<Type>>), Execute(Expression<Action>),
112+ // Execute<Type>(Delegate), Execute(Delegate), 静的バリアント, params 付き等
113+ // 計 8 オーバーロードを定義していたが、実際に呼ばれていたのは Expression 版のみ。
114+ // Func<T> / Action 版を併存させると lambda 引数で CS0121 曖昧性エラーになるため、
115+ // Expression 版を撤去し以下 2 つに集約する。
109116
110- if (!(context.IsHandleCreated))
111- {
112- return null;
113- }
114- */
115- #endregion
116- if ( _context . InvokeRequired )
117- _context . Invoke ( process , args ) ;
118- else
119- process . DynamicInvoke ( args ) ;
120- }
117+ public T Execute < T > ( Func < T > func )
118+ => context . InvokeRequired ? ( T ) context . Invoke ( func ) : func ( ) ;
121119
122- public Type Execute < Type > ( Delegate process , params object [ ] args )
120+ public void Execute ( Action action )
123121 {
124- #region
125- /*if (context == null)
126- {
127- throw new ArgumentNullException("context");
128- }
129- if (process == null)
130- {
131- throw new ArgumentNullException("process");
132- }
133-
134- if (!(context.IsHandleCreated))
135- {
136- return null;
137- }
138- */
139- #endregion
140- return context . InvokeRequired ? ( Type ) context . Invoke ( process , args ) : ( Type ) process . DynamicInvoke ( args ) ;
141- }
142-
143- public void Execute ( Delegate process , params object [ ] args )
144- {
145- #region
146- /*if (context == null)
147- {
148- throw new ArgumentNullException("context");
149- }
150- if (process == null)
151- {
152- throw new ArgumentNullException("process");
153- }
154-
155- if (!(context.IsHandleCreated))
156- {
157- return null;
158- }
159- */
160- #endregion
161122 if ( context . InvokeRequired )
162- context . Invoke ( process , args ) ;
123+ context . Invoke ( action ) ;
163124 else
164- process . DynamicInvoke ( args ) ;
125+ action ( ) ;
165126 }
166127}
167128
168129#region HelpAttribute
169130
170131[ AttributeUsage ( AttributeTargets . All ) ]
171- public class HelpAttribute : System . Attribute
132+ public class HelpAttribute ( string text , string arg = "" ) : Attribute
172133{
173- public string Text ;
174- public string Argument ;
175- public HelpAttribute ( string text , string arg = "" ) { Text = text ; Argument = arg ; }
134+ public string Text = text ;
135+ public string Argument = arg ;
136+
176137 public static List < string > GenerateHelpText ( Type type , string name )
177138 {
178139 var strList = new List < string > ( ) ;
179- var header = type . Namespace + "." + name + "." ;
180140
181- if ( header . Contains ( "PDIndexer" ) ) header = header . Replace ( "PDIndexer" , "PDI" ) ;
182- if ( header . Contains ( "IPAnalyzer" ) ) header = header . Replace ( "IPAnalyzer" , "IPA" ) ;
141+ // 260414Cl name が null/空 の場合 "PDI..MethodName" のような空セクションが
142+ // 出ないよう連結を調整。root クラス自身に対しても呼べるようになった。
143+ var ns = type . Namespace ?? "" ;
144+ if ( ns . Contains ( "PDIndexer" ) ) ns = ns . Replace ( "PDIndexer" , "PDI" ) ;
145+ if ( ns . Contains ( "IPAnalyzer" ) ) ns = ns . Replace ( "IPAnalyzer" , "IPA" ) ;
146+ var header = string . IsNullOrEmpty ( name ) ? ns + "." : ns + "." + name + "." ;
183147
184148 foreach ( var p in type . GetProperties ( ) . Where ( e => e . GetCustomAttribute < HelpAttribute > ( ) != null ) )
185149 strList . Add ( header + p . Name + "#" + p . GetCustomAttribute < HelpAttribute > ( ) . Text ) ;
@@ -188,7 +152,6 @@ public static List<string> GenerateHelpText(Type type, string name)
188152
189153 return strList ;
190154 }
191-
192155}
193156
194- #endregion
157+ #endregion
0 commit comments