using System; using System.IO; using System.Collections.Generic; using System.Linq; //using System.Text; //using System.Threading; //using System.Threading.Tasks; //using System.Windows; //using System.Windows.Media.Imaging; using System.Reflection; // Assembly. using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.ServiceModel; // WCF using System.Diagnostics; // for Process // UWP-APIの使用 // using Windows.Data.Pdf; // 作成クラス using CSRender; using CSRender.UtHash; // *重要* デスクトップアプリで UWP Api を呼び出す プロジェクトの設定方法が記載されている // https://docs.microsoft.com/ja-jp/windows/apps/desktop/modernize/desktop-to-uwp-enhance // 参照はwindows. winmd :C:\Program Files (x86) \Windows Kits\10\UnionMetadata\/ファサード namespace xChangeWCFPipe { } namespace CSRenderMain { [DataContract] //[Serializable()] // これでもいけそう public class ParamData { [DataMember] public string pdfPath = ""; //対象ファイル [DataMember] public string pdfPath2 = ""; //比較ファイル [DataMember] public string outuptImageDir = ""; // /O [DataMember] public string dpi = "72.0"; // /D [DataMember] public string boxSelect = "Crop"; // /B [DataMember] public string pageRange = "1-*"; // /P [DataMember] public string mode = "page"; // /MODE [DataMember] public string imageType = "JPG"; // /JPG or /PNG [DataMember] public string jpegQ = "91"; // JPEGQ [DataMember] public bool bHash = false; // ハッシュ値を生成する [DataMember] public bool bMkHash = false; // ハッシュファイルを作成する [DataMember] public bool bFC = false; [DataMember] public string resultPath = ""; [DataMember] public bool bPDFium = true; // [DataMember] public bool bExeSepa = true;// 実行分離 [DataMember] public string subExe = null;// "Sub"が指定されたら、処理をExe分離する。内部コマンド // データはWCFの通信データを使う。引数はPortAddressNameとする public ParamData Clone() { return (ParamData)MemberwiseClone(); } } public class Program { static void DispHelp() { var pgName = Path.GetFileName(Path.GetFileName(Assembly.GetExecutingAssembly().Location));// プログラム名 var pgNameFull = Assembly.GetExecutingAssembly().Location;// プログラム名 string msg = $"{pgName} [/] \n" + $"* Render of PDF file. available 3 command mode:[Basic Rendering] [Make Hash command] [Compare command], and [Render Options]\n" + $"\tPDFの画像化は[Basic Rendering]。\n" + $"\t比較は/MkHash([Make Hash command])後に、/FC([Compare command])で高速実行できます。\n" + $"\n" + $"[Basic Rendering] 基本的なレンダリング\n" + $"\t{pgName} [/] \n" + $"\t/F : pdfPath(pdfファイル名|ディレクトリ) /Fは省略可能\n" + $"\t/O : 出力ディレクトリ。省略時は\"IMG\"フォルダ\n" + $"\n" + $"[Render Options] レンダリングオプション\n" + $"\t/D <解像度> : 解像度指定 9 - 300dpi(default=72dpi)\n" + $"\t/BM,/BT,/BA,/BA,/BC: Select one box.(default=/BC:CrobBox): Boxies:MediaBox/BleedBox/TrimBox/ArtBox/CropBox\n" + $"\t/JPG,/JPEG,/PNG,/TIF,/TIFF,/GIF,/BMP: Select one output format.(default=/JPG)\n" + $"\t/JPEGQ : Jpegの品質指定1-100(default=91)\n" + $"\n" + $"\t/P : ページの範囲を指定する(省略時は全ページ)\n" + $"\t\t連続した範囲を指定する場合は、ハイフン('-')を用いる。終了側を省略すると最終pageまで。\n" + $"\t\t複数のページを指定する場合は、カンマ(',')を用いる\n" + $"\t\tEx. /P \"1,2,30-100\" //1,2pages and 30-100pages.\n" + $"\t[unsupport] 未対応\n" + $"\t/L : 入力PDFファイルリスト(*unsupport)\n" + $"\t/T : テンポラリフォルダを指定(省略時は出力先フォルダと同じ(*unsupport no need)\n" + $"\t/OP <0|1> : オーバープリントのOn/Off (省略時は1)(*unsupport allways on[1])\n" + $"\t/U <0|1> : 同名上書き設定 0:上書きしない 1:上書き(*unsupport allways overwrite[1])\n" + $"\t/OFFSET : ミリ単位でオフセットを指定する(省略時は共に0mm)(*unsupport)\n" + $"\t/PDFium <0or1>: GoogoleのPDFiumViewerエンジンを使用する(default=1>\n" + $"\n" + $"[Make Hash command] 比較用ハッシュ値作成コマンド\n" + $"\t{pgName} /MkHash ...... \n" + $"\t/MKHash : ハッシュ値を出力する。前記の[Render Options]を指定すること\n" + $"\t[HASHファイル作成]\n" + $"\n" + $"[Compare command] 比較コマンド\n" + $"\t{pgName} /FC ...... \n" + $"\t/FC : 2つのPDFを比較する。前記の[Render Options]を指定すること。無名引数が2つ必要です\n" + $"\t 事前に/MkHashを実行しておくことで高速に処理できる\n" + $"\t/RESULT : 比較結果を格納するファイルパス\n" + $"\t\t/FCコマンドを指定すると一致したら0,不一致なら1を返却するようになる\n" + $"\t\tは、,の行で構成される\n" + $"\n" + $"[ELSE ] その他のオプション\n" + $"\t/PDFium <0|1>:PDFiumライブラリを使う,デフォルト=1\n" + $"\t/NoExeSepa :実行分離しない(遅い)\n" + $"\t内部コマンド:/SubExe :実行分離,PDF単位で別Processで処理\n" + $"\n" + $"/H or /? : This help\n" /* PDFと同じフォルダに data.<出力条件>.jsonファイルを作成する 出力条件:+++ data.72_BT_JPG_Q34.json 既に存在したら、それを読んでから、追記(もしくは書き換える>。 */ // Remove TEST //+$"\t[for TEST]\n" //+$"\t/M Sync(default) or ASync\n" //+$"\t/M : pageBitMap(default), pageBitMapImage, page(same as pageBitMap), org(orginal source)\n" + "\n"; Console.Write(msg); } #if COMENT // 設計の見直し * ハッシュ作成のみのコマンド → /MkHashをつけると ハッシュファイルのみ作成。ただし、ディレクトリ指定に限る → CSRender /MkHash <各種Renderパラメータ> * 単純なRenderを維持。それに同時にハッシュファイルのOn/Off → /MkHashをつけなければよい。 → CSRender <各種Renderパラメータ> * 比較モードは、Autoハッシュで、存在しなければ最初に作成。Target/Refともに。比較しながら作成してよし Manualでハッシュなしで、遅いけどできるようにする。ハッシュ動作の検証用に → /FCをつけると、デフォルトでハッシュ優先で検査(Auto相当)、ハッシュ検査を無効にしたければ → /NoHash追加でハッシュを無視してTarget/Refとも再計算。ハッシュを使いたければ、事前に/MkHashで 作成しておけばよい。 * ヘルプは <単純なRender> <ハッシュ作成> <比較>にわける * <単純なレンダラ>: 1ファイルおよびディレクトリの比較。 Renderingパラメータの一覧(項目分ける)まで. * <ハッシュ作成>: /MkHashで高速比較のための前準備。ディレクトリモードでハッシュファイルのみを作成する。Renderingパラメータは合わせること * <比較>:/FCコマンドで比較の説明 * ここにAsiccDocを埋められないの? → 情報なし* // 遅くなるので * 比較モード時の差異があったとき、Diffを出す上限値をLimitDiffNumがほしい(デフォルトは各PDFで1pageとしたい) * 比較NGになって、フルでDIFF画像を出したいときはLimitDiffを解除すべき。単独ファイル指定のとき。 // 設計の見直し(END) #endif /// /// PDFレンダラーメイン /// /// /// static int Main(string[] args) { // 引数の保持 var pm = new ParamData(); bool bDirMode = false; // ディレクト指定の場合 //var pdfPathLst = new List(); string[] pdfPathLst = null; string[] pdfPathLst2 = null; var pdfPathLstBoth = new List(); var pdfPathLstNoBoth = new List(); //ハッシュ関係 string hashBaseName = "RenderHash"; // CSRender.Check.GetDPI(); {// ベースハッシュ値の計算テスト var streamPDF = ResData.GetBaseHashPDF(); RenderPDF.RenderPdfInitCheck(streamPDF); //return 0; } var qu = new Queue(args); // 引数をQueに登録 (qu.Enqueue(a)でも可能) while (qu.Count > 0) { // 引数のパース var wd = qu.Dequeue(); // 取り出しqu.Dequeue()で次の要素を取得する if (wd.First() == '-') { // 先頭-(ハイフンもオプション扱いにする)→"/"に置換 wd = Regex.Replace(wd, @"^\-", "/"); } //大文字小文字無視でオプションチェック var eIgnoreCase = StringComparer.OrdinalIgnoreCase; // オプションチェックローカル関数 bool isOpt(params string[] opts) => opts.Contains(wd, eIgnoreCase); // ボックスオプション辞書 var BoxSelOptDic = new Dictionary(eIgnoreCase) { ["/BM"] = "Media", ["/BT"] = "Trim", ["/BB"] = "Bleed", ["/BC"] = "Crop", ["/BA"] = "Art" }; if (isOpt("/?", "/H")) { DispHelp(); return -1; } else if (isOpt("/F")) { pm.pdfPath = (qu.Count > 0) ? qu.Dequeue() : "";// next word. } else if (isOpt("/O")) { pm.outuptImageDir = (qu.Count > 0) ? qu.Dequeue() : "";// next word. } else if (isOpt("/D")) { pm.dpi = (qu.Count > 0) ? qu.Dequeue() : "";// next word. if (!double.TryParse(pm.dpi, out double dmy)) { Console.WriteLine($"解像度が不正です:/D {pm.dpi}"); DispHelp(); return -1; } } else if (isOpt("/P")) { pm.pageRange = (qu.Count > 0) ? qu.Dequeue() : "";// next word. } else if (isOpt("/JPG", "/JPEG")) { pm.imageType = "JPG"; } else if (isOpt("/PNG")) { pm.imageType = "PNG"; } else if (isOpt("/TIF", "/TIFF")) { pm.imageType = "TIFF"; } else if (isOpt("/GIF", "/GIFF")) { pm.imageType = "GIF"; } else if (isOpt("/BMP")) { pm.imageType = "BMP"; } else if (isOpt("/JPEGQ")) { pm.jpegQ = (qu.Count > 0) ? qu.Dequeue() : "";// next word. if (!int.TryParse(pm.jpegQ, out int dmy)) { Console.WriteLine($"JPEG Qualityが不正です:/JPEGQ {pm.jpegQ}"); DispHelp(); return -1; } if (!(0 < dmy && dmy <= 100)) { Console.WriteLine($"JPEG Qualityが不正です(not 1-100):/JPEGQ {dmy}"); return -1; } } else if (isOpt("/M")) { pm.mode = (qu.Count > 0) ? qu.Dequeue() : "";// next word. } else if (BoxSelOptDic.ContainsKey(wd)) { pm.boxSelect = BoxSelOptDic[wd];// "/BT" -> "Trim",... } else if (isOpt("/HASH")) { pm.bHash = true; } else if (isOpt("/MKHASH")) { pm.bMkHash = true; pm.bHash = true; } else if (isOpt("/FC")) { pm.bFC = true; } else if (isOpt("/PDFium")) { pm.bPDFium = true; var flgStr = (qu.Count > 0) ? qu.Dequeue() : "";// next word. if (!int.TryParse(flgStr, out int dmy)) { Console.WriteLine($"PDFiumフラグが不正です:/PDFium {flgStr}"); return -1; } if (dmy == 0) { pm.bPDFium = false; } else if (dmy == 1) { pm.bPDFium = true; } else { Console.WriteLine($"PDFiumフラグが不正です:/PDFium {flgStr}"); return -1; } } else if (isOpt("/SubExe")) { pm.subExe = (qu.Count > 0) ? qu.Dequeue() : null;// next word. } else if (isOpt("/NoExeSepa")) { pm.bExeSepa = false; } else if (isOpt("/RESULT")) { pm.resultPath = (qu.Count > 0) ? qu.Dequeue() : "";// next word. } else if (wd.First() == '/') { // 処理の無いオプションを明示的に無視する Console.WriteLine($"Warning::Ignore opt:{wd}"); } else { // 引数をファイル名として取得する if (pm.pdfPath == "") { pm.pdfPath = wd; continue; } if (pm.bFC && (pm.pdfPath2 == "")) { pm.pdfPath2 = wd;// 比較時のみ取得 continue; } } } // bool bSubExe = (pm.subExe != null);// SubExeで起動されている場合。 xChangeWCFPipe.IXData cltSrv = null; if (!bSubExe) { /// サーバー側のセットアップ Console.WriteLine("++MainProcess start"); } else { // SubExe側の動作に差し替える Console.WriteLine("++SubProcess start:"+pm.subExe); //cltSrv = xChangeWCFPipe.CLT.makeCLT(pipeName:"pName",pipeAddress:pm.subExe); cltSrv = xChangeWCFPipeGEN.CLT_T.makeCLT( pipeName:"pName",pipeAddress:pm.subExe); Console.WriteLine("[S]:End makeCLT"); var pm2 = cltSrv.GetParam(); Console.WriteLine($@"[S]:pm2.pdfPath={pm2.pdfPath}"); var ppp = cltSrv.GetData(); cltSrv.SetMessage("Message"); Console.WriteLine("[S]:End GetData"); pm = ppp.pm; // パラメータを呼び出し元のものに置き換え Console.WriteLine("[S]:End GetData pm"); Console.WriteLine($@"[S]:pm.pdfPath={ppp.pm.pdfPath}"); Console.WriteLine($@"[S]:ppp.dataB.b={ppp.dataB.b}"); } if (pm.pdfPath == "") { Console.WriteLine("pdfファイルが指定されてません"); DispHelp(); return -1; } if (!(File.Exists(pm.pdfPath) || Directory.Exists(pm.pdfPath))) { // ファイルもしくはディレクトも見つからない場合 Console.WriteLine($"ファイルが存在しません:{pm.pdfPath}"); return -1; } var rdCond = new RenderPDF.RenderConditionParams { Dpi = double.Parse(pm.dpi), ImageType = pm.imageType, BoxType = pm.boxSelect, JpegQ = int.Parse(pm.jpegQ) }; bool bDir = File.GetAttributes(pm.pdfPath).HasFlag(FileAttributes.Directory); if (bDir) { // Directoryが指定されたのでファイルリストアップ bDirMode = true; pdfPathLst = System.IO.Directory.GetFiles(pm.pdfPath, "*.pdf"/*, System.IO.SearchOption.AllDirectories*/); pdfPathLst = Array.ConvertAll(pdfPathLst, f => Path.GetFileName(f)); // 配列書き換え(ファイル名のみにする // var enumLst = pdfPathLst.Select(f => Path.GetFileName(f)); LINQ式に置き換えることも可能(返り値は配列ではない) //foreach ( var f in pdfPathLst) { // Console.WriteLine($@"path1={f}"); //} Console.WriteLine($@"path1.Len={pdfPathLst.Count()}"); } if ((!pm.bFC) && bDir) { // 単純レンダリング時 かつ ディレクト時に 対象リストに追加 foreach (var f in pdfPathLst) { pdfPathLstBoth.Add(f); } } if (pm.bFC) {// 比較モード時 if (pm.pdfPath2 == "") { Console.WriteLine("比較モードで2つ目のpdfファイルが指定されてません"); return -1; } if (!(File.Exists(pm.pdfPath2) || Directory.Exists(pm.pdfPath2))) { Console.WriteLine($"比較ファイルが存在しません:{pm.pdfPath2}"); return -1; } bool bDir2 = File.GetAttributes(pm.pdfPath2).HasFlag(FileAttributes.Directory); if (bDir2) { if (!bDir) { Console.WriteLine($"比較対象はファイルパスでないといけません:{pm.pdfPath2}"); return -1; } // 2つ目のファイルリストアップ pdfPathLst2 = System.IO.Directory.GetFiles(pm.pdfPath2, "*.pdf"/*, System.IO.SearchOption.AllDirectories*/); pdfPathLst2 = Array.ConvertAll(pdfPathLst2, f => Path.GetFileName(f));// 配列の書き換え // 共通のファイルを見つける。Lstの要素がLst2に含まれているかどうか foreach (var f in pdfPathLst) { if (pdfPathLst2.Contains(f)) { pdfPathLstBoth.Add(f); } else { pdfPathLstNoBoth.Add(f);// LstがLst2に含まれていない } } // Lst2のみファイルをNoBothに登録 foreach (var f in pdfPathLst2) { if (!pdfPathLstBoth.Contains(f)) { pdfPathLstNoBoth.Add(f); } } // pdfPathBoth,pdfPathNoBothが作成済み if (pdfPathLstNoBoth.Count() != 0) { Console.WriteLine($@"不一致のファイル={pdfPathLstNoBoth.Count()}"); foreach (var f in pdfPathLstNoBoth) { Console.WriteLine($@"warning [no match]={f}"); } } } } var watch = System.Diagnostics.Stopwatch.StartNew(); // 時間の生成と計測開始を同時に行う var otDir = pm.outuptImageDir; if (otDir == "") { // 出力ディレクトリの作成 // 元PDFの同一フォルダにIMGフォルダを作成する otDir = Path.Combine(Directory.GetParent(pm.pdfPath).FullName, "IMG"); } if (!Directory.Exists(otDir) /* && (!bMkHash)*/ ) { // MkHashのとき不要→必要 Directory.CreateDirectory(otDir); } var otHashPath = ""; var otHashPath2 = ""; int ret = -1; Console.WriteLine($@"bPDFium={pm.bPDFium}"); if (!bDirMode) { if (pm.bFC) { //比較モード // ハッシュデータの読み込み Console.WriteLine($@"pre pdfPath={pm.pdfPath}"); Console.WriteLine($@"pre pdfPath2={pm.pdfPath2}"); var hashDataTgt = HashData.load(pm.pdfPath, hashBaseName, pm.dpi, pm.boxSelect, pm.imageType, pm.jpegQ, ref otHashPath); var hashDataRef = HashData.load(pm.pdfPath2, hashBaseName, pm.dpi, pm.boxSelect, pm.imageType, pm.jpegQ, ref otHashPath2); //Console.WriteLine(""); ret = RenderPDF.RenderPdfDocCompare( pdfPath : Path.GetFullPath(pm.pdfPath), refPdfPath : Path.GetFullPath(pm.pdfPath2), inDir : otDir, pm : rdCond, inPageRange : pm.pageRange, inHashDataTgt: hashDataTgt, inHashDataRef: hashDataRef, bPDFium : pm.bPDFium ); } else { // Sync version. // Console.WriteLine(""); // シングル指定では既存のハッシュファイルを読みださない → 単独名のハッシュファイル");[ var count = pdfPathLstBoth.Count; //ParallelOptions options = new ParallelOptions { MaxDegreeOfParallelism = 6 }; //Parallel.For(0,pdfPathLstBoth.Count, options, index => { //逆に遅くなる UWPコール(render)は対応していない? if (pm.bExeSepa) { var tokenSource = new CancellationTokenSource(); ParallelOptions options = new ParallelOptions { MaxDegreeOfParallelism = 4, CancellationToken=tokenSource.Token }; var loopResult = Parallel.For(0,pdfPathLstBoth.Count, options, (index,lpState) => { //for (var index = 0; index < pdfPathLstBoth.Count; index++) { var tgt = Path.Combine(pm.pdfPath, pdfPathLstBoth[index]); Console.WriteLine($@"**remain({count})****tgt({index}/{pdfPathLstBoth.Count})={tgt}"); if (pm.bFC) { //比較モード var reff = Path.Combine(pm.pdfPath2, pdfPathLstBoth[index]); } else { // Sync version(Paralles). var svrData = new xChangeWCFPipe.XData(); var pmClone = pm.Clone(); pmClone.pdfPath = tgt;// 引数のターゲットのみを書き換える svrData.SetParam(pmClone); var svrHost = xChangeWCFPipeGEN.SVR_T.makeSVR( baseIf : typeof(xChangeWCFPipe.IXData), obj : svrData, pipeName : "pName", pipeAddress : $"PortName{index}" ); //var svrHost = xChangeWCFPipe.SVR.makeSVR(x: svrData, pipeName: "pName", pipeAddress:$"PortName{index}"); if ( svrHost == null) { Console.WriteLine("WCF cannot make"); //tokenSource.Cancel();// 処理のキャンセル( throwされる) lpState.Stop(); return; } else { var pgFullName = Assembly.GetExecutingAssembly().Location; var proc = Ut.Ut.DoCmd( cmdAndArgs: new string[] { $@"{pgFullName}", "/SubExe", $"PortName{index}" } , bEcho: false , bSameConsole: true ); while (!proc.HasExited) { // DoCmd()が終了するまで監視 //var dc = svrData.GetData(); System.Threading.Thread.Sleep(2 * 1000); } var tmpH = svrData.GetHashData(); if (tmpH == null) { Console.WriteLine("tmph is null"); } if ((hashDataTgt != null) && (tmpH != null)) { //hashDataTgt.Files += tmpH.Files; Console.WriteLine($@"tempF.Count={tmpH.Files.Count()}"); var merged = hashDataTgt.Files .Concat(tmpH.Files.Where(pair => !hashDataTgt.Files.ContainsKey(pair.Key)) ).ToDictionary( pair => pair.Key, pair => pair.Value ); hashDataTgt.Files = new SortedDictionary(merged);// hashを合成した。 } proc.Dispose(); svrHost.Close(); svrData = null; } } //GC.Collect();// これでメモリリークが解決した count--; }); if ( !loopResult.IsCompleted ) { Console.WriteLine("中断"); pm.bMkHash = false;// Hash値保存抑制 } ////tokenSource.Cancel();// 処理のキャンセル //if ( tokenSource.IsCancellationRequested ) { // pm.bMkHash = false;// Hash値保存抑制 //} //}; } else { // NoExeSepa for (var index = 0; index < pdfPathLstBoth.Count; index++) { var tgt = Path.Combine(pm.pdfPath, pdfPathLstBoth[index]); Console.WriteLine($@"tgt={tgt}:{index}/{pdfPathLstBoth.Count}"); if (pm.bFC) { //比較モード var reff = Path.Combine(pm.pdfPath2, pdfPathLstBoth[index]); //Console.WriteLine(""); ret = RenderPDF.RenderPdfDocCompare( pdfPath: Path.GetFullPath(tgt), refPdfPath: Path.GetFullPath(reff), inDir: otDir, pm: rdCond, inPageRange: pm.pageRange, inHashDataTgt: hashDataTgt, inHashDataRef: hashDataRef, bPDFium: pm.bPDFium ); } else { // Sync version(Paralles). ret = RenderPDF.RenderPdfDoc( pdfPath: Path.GetFullPath(tgt), inDir: otDir, pm: rdCond, inPageRange: pm.pageRange, bSaveImage: pm.bMkHash ? false : true,// ハッシュ値生成ではイメージ保存しない。 bHash: pm.bHash,// bHash,dumy inHashData: pm.bMkHash ? hashDataTgt : null, //ハッシュコマンドモードのみDataを渡す bPDFium: pm.bPDFium ); } count--; GC.Collect();// これでメモリリークが解決した }; } if (pm.bMkHash) { hashDataTgt.save(otHashPath);//ハッシュモードで保存する(Dutyチェックもいるでしょう } } watch.Stop(); Console.WriteLine($"bPDFium={pm.bPDFium},result={ret},time={ watch.ElapsedMilliseconds / 1000.0 }[sec]"); #if COMMNET Assembly assm = Assembly.GetExecutingAssembly(); Console.WriteLine(assm.FullName); // 参照しているすべてのアセンブリを取得し、表示する foreach (AssemblyName refassm in assm.GetReferencedAssemblies()) { Console.WriteLine($@" {refassm.FullName},Ver({refassm.Version}),VerComp({refassm.VersionCompatibility})" ); } string clrVersion = System.Environment.Version.ToString(); Console.WriteLine($@"clrVer={clrVersion}"); #endif return ret;//success. } // リソースからの取得 public static class ResData { public static Stream GetBaseHashPDF(string resName = "CSRender.RES.BaseHash.pdf") { System.Reflection.Assembly asm = System.Reflection.Assembly.GetExecutingAssembly(); var sel = from x in asm.GetManifestResourceNames() select resName; if (sel.Count() == 1) { return asm.GetManifestResourceStream(sel.First()); } return null; } } } } /// /// WCF通信モジュールのラッパクラス。 /// 2つのExe間をWCFパイプモードで通信を行う /// namespace xChangeWCFPipe { /// シリアライズ可能にすること /// [ServiceContract] public interface IXData { [OperationContract(IsOneWay = true)] void Execute(); [OperationContract] bool SetMessage(string msg); [OperationContract] bool SetProgress(int per);//0-100. [OperationContract] DataContainer GetData(); [OperationContract] void SetData(DataContainer d); [OperationContract] HashData GetHashData(); [OperationContract] void SetHashData(HashData d); [OperationContract] CSRenderMain.ParamData GetParam(); //CSRenderMain.Program.ParamData GetParam(); } // ServiceContract https://tnakamura.hatenablog.com/entry/20080606/1220023868 // https://devlights.hatenablog.com/entry/20111023/p2 /// /// 通信用複雑データ。スカラー型(int,double,,,.)以外は[DataCOntract]属性を /// つけて、通知用Interfaceの引数や、返値で利用する /// // 複雑データ [DataContract] public class DataContainer { [DataMember] public DataA dataA; [DataMember] public DataB dataB; [DataMember] public CSRenderMain.ParamData pm; [DataMember] public HashData hdata; } public class DataA { public int a; } public class DataB : Inheritance { public string b; } public abstract class Inheritance { public byte[] c; } } namespace xChangeWCFPipe { /// /// Main(サーバー)側 データ定義 /// [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple, UseSynchronizationContext = false)] public class XData : IXData { private readonly Object LockObj = new object();// 排他制御用 public delegate void callFunc(); public callFunc callDelegate; public XData() { _dc.dataA = new DataA(); _dc.dataA.a = 1; _dc.dataB = new DataB(); _dc.dataB.b = "OK!!!!x"; _dc.dataB.c = new byte[] { 0, 1, 2, 3 }; _dc.pm = new CSRenderMain.ParamData(); _dc.pm.pdfPath = "hoge init path"; } public void Execute() { lock (LockObj) { callDelegate(); // 実装はデリゲートします } } public bool SetMessage(string msg) { lock (LockObj) { Console.WriteLine($"SVR:called setMessage({msg}) from CLT"); _msg = msg; } return true; } public bool SetProgress(int per) {//0-100. lock (LockObj) { Console.WriteLine($"SVR:called setProgress({per}) from CLT"); _progress = per; } return true; } public DataContainer GetData() { lock (LockObj) { return _dc; } } public void SetData(DataContainer dc) { lock (LockObj) { _dc = dc; } } public void SetHashData(HashData h) { lock (LockObj) { _dc.hdata = h; } } public HashData GetHashData() { lock (LockObj) { return _dc.hdata; } } //public void SetParam(ParamData pm) { public void SetParam(CSRenderMain.ParamData pm) { lock (LockObj) { Console.WriteLine("w: ParamData"); Console.WriteLine($@"w1:path:{pm.pdfPath}"); _dc.pm = pm; Console.WriteLine($@"w2:path:{_dc.pm.pdfPath}"); } } public CSRenderMain.ParamData GetParam() { //public CSRenderMain.Program.ParamData GetParam() { lock (LockObj) { Console.WriteLine("getParam call"); Console.WriteLine($@"getParam pdfPath={_dc.pm.pdfPath}"); return _dc.pm; } } // private data private string _msg = "init msg"; private int _progress = 0;// 0-100 public DataContainer _dc = new DataContainer(); } /// /// Main(サーバー)側クラス /// public class SVR { // - static - /// /// 通信用のインスタンスを作成する /// /// /// /// /// static public SVR makeSVR(XData x, string pipeName = "PDFormstudioESorGS", string pipeAddress = "SubModuleAddress") { return (new SVR(x, pipeName, pipeAddress)); } // - public I/F - public void Close() { Console.WriteLine($"Close Service:{_service!=null},{_serviceHost != null}"); _service = null; Console.WriteLine("1"); _serviceHost.Close(); Console.WriteLine("2"); } // - private - private XData _service = null;// 要らないかも private ServiceHost _serviceHost = null; private SVR() { } private SVR(XData x, string pipeName = "PDFormstudioESorGS", string pipeAddress = "SubModuleAddress") { const string pipeBase = "net.pipe://localhost"; var uri = pipeBase + "/" + pipeName; _service = x; // デリゲート登録 //callDelegate = () => { Console.WriteLine("svr:Delegate func called"); } #if false { // インターフェースを求める // AddServiceEndpoint()にわたすIXDataはXData.Interfaces()から求められるが // インタフェースは一つとは限らないのでやめておく。 Type interfType = null; Type type = _service.GetType(); Type[] interfaces = type.GetInterfaces(); Console.WriteLine($@"GetInterfaces***********************************"); foreach (Type t in interfaces) { Console.WriteLine(t.ToString()); interfType = t; }; Console.WriteLine($@"End of GetInterfaces***********************************"); } #endif _serviceHost = new ServiceHost(_service, new Uri(uri)); try { _serviceHost.AddServiceEndpoint(typeof(IXData)/*interfType*/, new NetNamedPipeBinding(), pipeAddress); _serviceHost.Open(); } catch (AddressAlreadyInUseException) { Console.WriteLine("既にサービスは起動しています。"); } catch (Exception e) { Console.WriteLine(e.ToString()); } } } } namespace xChangeWCFPipe { /// /// Sub(クライアント)側クラス /// public class CLT { /// /// Sub側の初期化処理。Main側とpipeName,pipeAddressを同じにすること /// /// /// /// static public IXData makeCLT(string pipeName = "PDFormstudioESorGS", string pipeAddress = "SubModuleAddress") { const string pipeBase = "net.pipe://localhost"; var address = pipeBase + "/" + pipeName + "/" + pipeAddress; //try { var factory = (new ChannelFactory(new NetNamedPipeBinding(), new EndpointAddress(address))); IXData toMain = factory.CreateChannel(); return toMain; //} catch (Exception e) { // Console.WriteLine("makeCLT failed:" + e.ToString()); // return null; //} } } } /// /// ジェネリック版 /// /// #if COMMENT // インターフェースクラスはサーバーとクライアント両方で参照可能なところに定義する // 実装データクラスはサーバー内で定義する [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple, UseSynchronizationContext = false)] public class XData : IXData { ... } #endif namespace xChangeWCFPipeGEN { /// /// Main(サーバー)側 データ定義 /// //[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple, UseSynchronizationContext = false)] //public class XData : IXData //{ // ... //} /// /// Main(サーバー)側クラス /// /// public class SVR_T { // - static - /// /// /// /// 公開するインターフェースクラス /// 実装インスタンス(baseIfを含むこと) /// /// /// static public SVR_T makeSVR(Type baseIf, object obj/*XData*/ , string pipeName = "PDFormstudioESorGS", string pipeAddress = "SubModuleAddress") { Type oType = obj.GetType(); bool hasBaseIf = oType.GetInterfaces().Any(t => (t == baseIf) ); if ( !hasBaseIf) { Console.WriteLine($@"Error:'{oType.FullName}' have not the interface '{baseIf.FullName}'");//A have not the interface B. return null; } return (new SVR_T(baseIf, obj, pipeName, pipeAddress)); } // - public I/F - public void Close() { Console.WriteLine($"Close Service:{_service != null},{_serviceHost != null}"); _service = null; _serviceHost.Close(); } // - private - private object _service = null;// 要らないかも private Type _baseIf = null; private ServiceHost _serviceHost = null; private SVR_T() { } private SVR_T(Type baseIf, object oInstance, string pipeName, string pipeAddress) { const string pipeBase = "net.pipe://localhost"; var uri = pipeBase + "/" + pipeName; _baseIf = baseIf; _service = oInstance; // デリゲート登録 //callDelegate = () => { Console.WriteLine("svr:Delegate func called"); } #if false { // インターフェースを求める // AddServiceEndpoint()にわたすIXDataはXData.Interfaces()から求められるが // インタフェースは一つとは限らないのでやめておく。 Type interfType = null; Type type = _service.GetType(); Type[] interfaces = type.GetInterfaces(); Console.WriteLine($@"GetInterfaces***********************************"); foreach (Type t in interfaces) { Console.WriteLine(t.ToString()); interfType = t; }; Console.WriteLine($@"End of GetInterfaces***********************************"); } #endif _serviceHost = new ServiceHost(_service, new Uri(uri)); try { _serviceHost.AddServiceEndpoint(_baseIf/*typeof(Ti)*/, new NetNamedPipeBinding(), pipeAddress); _serviceHost.Open(); } catch (AddressAlreadyInUseException) { Console.WriteLine("既にサービスは起動しています。"); } catch (Exception e) { Console.WriteLine(e.ToString()); } } } /// /// Sub(クライアント)側クラス /// public class CLT_T { /// /// Sub側の初期化処理。Main側とpipeName,pipeAddressを同じにすること /// /// /// /// static public Ti makeCLT(string pipeName = "PDFormstudioESorGS", string pipeAddress = "SubModuleAddress") { const string pipeBase = "net.pipe://localhost"; var address = pipeBase + "/" + pipeName + "/" + pipeAddress; //try { var factory = (new ChannelFactory(new NetNamedPipeBinding(), new EndpointAddress(address))); Ti toMain = factory.CreateChannel(); return toMain; //} catch (Exception e) { // Console.WriteLine("makeCLT failed:" + e.ToString()); // return null; //} } } } namespace Ut { public class Ut { // Util // -------------------------------------------------------------- /// /// 親プロセスが終了したら、自身を終了させる /// /// public static void SetAutoSelfKillByMainProcEnd(bool bEnable = true) { if (_SingletonProc != null) return;//一度しか呼び出せない if (bEnable == false) return; const string fname = "SetAutoSelfKillByMainProcEnd"; // 自動的に親のプロセスがいなくなったら自動的にキルモードを設定する // メインプロセスの終了チェック // 親プロセスIDでProcessハンドルを取得、そのExitedイベントに自身の終了関数を設定(Environment.Exit()) int paProcID = GetParentProcessId(); var paProc = Process.GetProcessById(paProcID); paProc.EnableRaisingEvents = true; Console.WriteLine($@"{fname}:ParrentProcessName ={paProc.ProcessName}({paProcID})"); paProc.Exited += new EventHandler( (object s, EventArgs a) => { Console.WriteLine($@"{fname}:Exited Event!!!!"); var ss = s as Process; Console.WriteLine($@"Sure ProcName is {ss.ProcessName}({ss.Id}) {ss.StartInfo.Arguments}"); ; System.Threading.Thread.Sleep(10 * 1000); ss.Close(); ss.Dispose(); Console.WriteLine($@"{fname}:Exited Event!!!!(afer10sec)"); //Environment.Exit(-1); }); // _SingletonProc = paProc; } private static System.Diagnostics.Process _SingletonProc = null; /// /// コマンドライン引数複数個をエンコードして、スペースで結合 /// /// string[] コマンドライン引数 /// コマンドライン文字列(Escaped) public static string makeCmdLine(IEnumerable args) { if (args == null) throw new ArgumentNullException("args"); string EscapeCmdLineArg(string v) { if (string.IsNullOrEmpty(v)) return ""; var containsSpace = v.IndexOfAny(new[] { ' ', '\t' }) != -1; v = ReCommandLineEscapePattern.Replace(v, @"$1\$&");//「\…\"」をエスケープ.「"」直前の「\」の数を 2倍+1 if (containsSpace) { v = "\"" + ReLastBackSlashPattern.Replace(v, "$1$1") + "\""; } return v; } return string.Join(" ", args.Select(v => EscapeCmdLineArg(v))); } private static Regex ReCommandLineEscapePattern = new Regex("(\\\\*)\""); private static Regex ReLastBackSlashPattern = new Regex(@"(\\+)$"); /// /// 親のプロセスIDを取得する /// /// 親ProcessID static int GetParentProcessId() { var myProcId = GetCurrentProcessId(); var query = string.Format($@"SELECT ParentProcessId FROM Win32_Process WHERE ProcessId = {myProcId}"); //クエリから結果を取得 using (var search = new System.Management.ManagementObjectSearcher(@"root\CIMV2", query)) using (var results = search.Get().GetEnumerator()) { if (!results.MoveNext()) throw new ApplicationException("Couldn't Get ParrentProcessId."); var queryResult = results.Current; //親プロセスのPIDを取得 uint pa = (uint)(queryResult["ParentProcessId"]); return (int)(pa); } } /// /// 自身のプロセスIDを取得する /// /// ProcessID static int GetCurrentProcessId() { return Process.GetCurrentProcess().Id; } /// /// 外部プログラムを起動する /// /// 引数文字列の配列 /// true:コンソールを呼び出しと共有 false:別Console Windows /// 標準出力(未実装) /// コマンドが終了するまで待つ(false:未実装,常に待つ) /// タイムアウト時間ms(未実装) /// public static Process DoCmd(IEnumerable cmdAndArgs, bool bSameConsole = true, bool bEcho = true, bool bWait = false, int nWaitLimitMSec = -1) { var cmdName = cmdAndArgs.First();//[0]; var argStr = makeCmdLine(cmdAndArgs.Skip(1));// 先頭を除外してコピー new ArraySegment(cmdAndArgs.ToArray(), 1, cmdAndArgs.Count()-1) Console.WriteLine($@"DoCmd:{cmdName}:args[{argStr}]"); //using (var p = new Process()) { var p = new Process(); { var info = p.StartInfo; p.StartInfo.FileName = cmdName; p.StartInfo.Arguments = argStr; if (bSameConsole) { Console.WriteLine("same windows mode"); p.StartInfo.UseShellExecute = false; // Shell経由で実行しない(必須) true: 別ウインドウ。false:コンソールを共有 p.StartInfo.CreateNoWindow = false; // ウィンドウを作成しない } else { Console.WriteLine("new windows mode"); p.StartInfo.UseShellExecute = true; p.StartInfo.CreateNoWindow = true; } p.Start();// 実行 if (bWait == false) { return p; } if (nWaitLimitMSec == -1) { p.WaitForExit();// タイムアウト無し } else { bool bExit = p.WaitForExit(nWaitLimitMSec); if (!bExit) {// 処理の終了を待つ Console.WriteLine("処理が終了しないので強制終了"); p.Kill(); // 強制終了する return p; } } return p; } } } }