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 // 作成クラス using CSRender; using CSRender.UtHash; using static CSRender.RenderPDF; // *重要* デスクトップアプリで 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 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の通信データを使う。引数はPipeName:PortAddressNameとする [DataMember] public bool bVerbose = false;// 詳細デバッグ public ParamData Clone() { return (ParamData)MemberwiseClone(); } } public class Program { static void DispHelp() { var pgName = Path.GetFileName(Path.GetFileName(Assembly.GetExecutingAssembly().Location));// プログラム名 var pgNameFullPath = Assembly.GetExecutingAssembly().Location;// プログラム名 Console.WriteLine("pageNameF="+pgNameFullPath); 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/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" + $"\t【未】/BM,/BT,/BA,/BA,/BC: Select one box.(default=/BC:CrobBox): Boxies:MediaBox/BleedBox/TrimBox/ArtBox/CropBox\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" + $"\n" + $"\t/RESULT : 比較結果を格納するファイルパス\n" + $"\t\t/FCコマンドを指定すると一致したら0,不一致なら1を返却するようになる\n" + $"\t\tは、,<[OK] or [@Difference]>の行で構成される\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 renderer main /// /// /// 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); //} 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("/Verbose")) { var arg = (qu.Count > 0) ? qu.Dequeue() : "True";// next word. string[] sel = {"True","1","ON"}; pm.bVerbose = sel.Contains(arg, eIgnoreCase); } else if (isOpt("/SubExe")) { pm.subExe = (qu.Count > 0) ? qu.Dequeue() : null;// next word. var sp = pm.subExe.Split(':'); if (sp.Length < 2 ) { Console.WriteLine($"SubExe指定は\":\"区切りが必要: {pm.subExe}"); return -1; } } 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; } } } // ↑引数解析終わり setEcho(pm.bVerbose); if (pm.bVerbose) echo("Varbose Mode!"); // bool bSubExe = (pm.subExe != null);// SubExeで起動されている場合。 xChangeWCFPipe.IXData cltSrv = null; if (!bSubExe) { /// サーバー側 echo("++MainProcess start"); } else { // SubExe側の動作に差し替える var sp = pm.subExe.Split(':'); var pipeName = sp[0]; var pipeAddr = sp[1]; cltSrv = xChangeWCFPipeGEN.CLT_T.makeCLT(pipeName:pipeName,pipeAddress:pipeAddr); Ut.Ut.SetAutoSelfKillByMainProcEnd(); // 起動元が終了した場合に自身を終了させる pm = cltSrv.GetParam(); // パラメータを書き換える setEcho(pm.bVerbose); echo("++SubProcess start:" + pm.subExe); } 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()}"); } var resultData = new SortedDictionary(); 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($@"Use bPDFium={pm.bPDFium}"); if (!bDirMode) { if (pm.bFC) { //比較モード // ハッシュデータの読み込み echo($@"pre pdfPath={pm.pdfPath}"); echo($@"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(""); if (pm.resultPath != "") {// 初期化 File.WriteAllText(Path.GetFullPath(pm.resultPath), ""); } ret = RenderPDF.RenderPdfDocCompare( pdfPath: Path.GetFullPath(pm.pdfPath), refPdfPath: Path.GetFullPath(pm.pdfPath2), resultDataPath: pm.resultPath, 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 &&(!pm.bFC)) { // FCモードは除外します。 var tokenSource = new CancellationTokenSource(); ParallelOptions options = new ParallelOptions { MaxDegreeOfParallelism = 4 }; ret = 0;//Success 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]); var tgtID = $@"{index}/{pdfPathLstBoth.Count}"; Console.WriteLine($@"**Remain({count})****tgt({tgtID})={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); string guidStr = Guid.NewGuid().ToString("N"); string pipeName = $"PN_{guidStr}"; string pipeAddress = $"P_{index}_{guidStr}"; var svrHost = xChangeWCFPipeGEN.SVR_T.makeSVR( baseIf : typeof(xChangeWCFPipe.IXData), obj : svrData, pipeName : pipeName, pipeAddress : pipeAddress ); if ( svrHost == null) { Console.WriteLine("WCF cannot make"); //tokenSource.Cancel();// 処理のキャンセル( throwされる) lpState.Stop(); return; } var myExePath = Assembly.GetExecutingAssembly().Location; var proc = Ut.Ut.DoCmd( cmdAndArgs: new string[] { myExePath, "/SubExe",$"{pipeName}:{pipeAddress}" } , bEcho: false , bSameConsole: true ); proc.WaitForExit(); var retProc = proc.ExitCode; echo($@"retProc={retProc}"); if ( retProc < 1 ) { //PDFページ数以外が返った時 ret = -1; Console.WriteLine( $@"Warning ::Cannot get PDFPage:{retProc}"); } else { ;// 初期値 ret=0; } //while (!proc.HasExited) { // System.Threading.Thread.Sleep(2 * 1000); //} var tmpH = svrData.GetHashData(); if (tmpH == null) { echo("tmpH is null"); } if ( (hashDataTgt!=null)&&(tmpH!=null)) { lock(hashDataTgt) { //hashDataTgt.Files += tmpH.Files; if ( tmpH.Files.Count() < 1 ) {// 中身が空なら警告 Console.WriteLine($@"Warning 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; Console.WriteLine($@"End tgt({tgtID})"); } //GC.Collect();// これでメモリリークが解決した Paraの中ではやめておく count--; }); if ( !loopResult.IsCompleted ) { Console.WriteLine("中断"); pm.bMkHash = false;// Hash値保存抑制 } ////tokenSource.Cancel();// 処理のキャンセル //if ( tokenSource.IsCancellationRequested ) { // pm.bMkHash = false;// Hash値保存抑制 //} //}; // Non Para Block } else { // NoExeSepa if (pm.resultPath != "") {// 初期化 File.WriteAllText(Path.GetFullPath(pm.resultPath), ""); } for (var index = 0; index < pdfPathLstBoth.Count; index++) { var tgt = Path.Combine(pm.pdfPath, pdfPathLstBoth[index]); Console.WriteLine($@"tgt={index+1}/{pdfPathLstBoth.Count}:{tgt}"); if (pm.bFC) { //比較モード var reff = Path.Combine(pm.pdfPath2, pdfPathLstBoth[index]); //Console.WriteLine(""); ret = RenderPDF.RenderPdfDocCompare( pdfPath: Path.GetFullPath(tgt), refPdfPath: Path.GetFullPath(reff), resultDataPath: pm.resultPath, 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($"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] 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 CSRenderMain.ParamData pm; [DataMember] public HashData hdata; } } 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.pm = new CSRenderMain.ParamData(); } 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 void SetHashData(HashData h) { lock (LockObj) { _dc.hdata = h; } } public HashData GetHashData() { lock (LockObj) { return _dc.hdata; } } public CSRenderMain.ParamData GetParam() { lock (LockObj) { return _dc.pm; } } // クライアントに公開しなくてもよいI/F public void SetParam(CSRenderMain.ParamData pm) { lock (LockObj) { _dc.pm = pm; } } // private data private string _msg = "init msg"; private int _progress = 0;// 0-100 private DataContainer _dc = new DataContainer(); } } 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() { echo($"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; } } } }