GhostscriptのDLLをC#から呼び出す

今の仕事で、PostscriptファイルをPDFへ変換するルーチンを書いてるわけです。
んで、最初はProcessを作って、「gswin32c.exe」を呼び出そうとしていたんだけど、どうも連続実行させると調子が悪い。(逝きっぱなしになる)
メモリ関連かなとも思うんだけど、調査しきれず。


んで、せっかくオープンソース物だし、上記EXEも一緒についてくるDLLのフロントエンドらしいし、DLL直で呼び出せばインプロセスで動くし、メモリ系の問題があっても調査しやすいだろうと言うことで、こちらを試してみました。


結論から言うとうまくいったんだけどね。


マネージ(アプリケーション) → アンマネージ(DLL)
のパラメータマーシャリングというか、ポインタのポインタをどうやって渡すかとか。


まぁ、いい勉強になったわ。


つー事で、サンプル公開。

/// Ghostscriptからの標準出力用デリゲート宣言

/// int(*stdin_fn)(void *caller_handle, char *buf, int len)
public delegate int GSStdin(System.IntPtr caller, StringBuilder str, System.Int32 len);

/// int(*stdout_fn)(void *caller_handle, const char *str, int len)
public delegate int GSStdout(System.IntPtr caller, String str, System.Int32 len);

/// int(*stderr_fn)(void *caller_handle, const char *str, int len)
public delegate int GSStderr(System.IntPtr caller, String str, System.Int32 len);

public class PDFMaker
{
	/// 
	/// PDFを生成します。
	/// 
	/// 入力されるPostscriptファイル名
	/// 出力するPDFファイル名
	public bool makePdf(String infile, String outfile)
	{
		String cmds = new String[14];

		cmds[0] = this.GSCOMMAND;
		cmds[1] = this.MakeIncludePath();
		cmds[2] = "-dSAFER";
		cmds[3] = "-dNOPAUSE";
		cmds[4] = "-dBATCH";
		cmds[5] = "-sDEVICE#pdfwrite";
		cmds[6] = "-dAutoRotatePages#/PageByPage";
		cmds[7] = "-dPDFFitPage";
		cmds[8] = "-sOutputFile=" + outfile;
		cmds[9] = "-dCompatibilityLevel=" + this.GetPDFVersionString();
		cmds[10] = "-c";
		cmds[11] = ".setpdfwrite";
		cmds[12] = "-f";
		cmds[13] = infile;

		int ret = this.CallGS(cmds); 
			
		if( 0 != ret)
		{
			return false;
		}

		return true;
	}

	private String MakeIncludePath()
	{
		Ps2PdfSetting p2p = new Ps2PdfSetting(); // 設定を持ってるクラスがあると思ってくれ。

		String gs851base = Directory.GetParent(p2p.GSPath).Parent.FullName;
		String gsbase = Directory.GetParent(gs851base).FullName;
		String fontPath = p2p.WinFontPath;

		String incpath = gs851base + @"\Resource";
		incpath += ";";
		incpath += gs851base + @"\lib";
		incpath += ";";
		incpath += gs851base + @"\kanji";
		incpath += ";";
		incpath += gsbase + @"\fonts";
		incpath += ";";
		incpath += fontPath;

		incpath = "-I\"" + incpath + "\"";

		return incpath;
		
	}

	///int gsapi_new_instance (void **pinstance, void *caller_handle);
	[DllImport("gsdll32.dll" , EntryPoint="gsapi_new_instance")]
	unsafe public static extern int gsapi_new_instance(
		[In,Out]System.IntPtr* inst, 
		[In]System.IntPtr p
		);

		
	/// int gsapi_init_with_args (void *instance, int argc, char **argv);
	[DllImport("gsdll32.dll" , EntryPoint="gsapi_init_with_args")]
	unsafe public static extern int gsapi_init_with_args(
		[In]System.IntPtr inst, 
		[In]System.Int32 argc, 
		[In]String cmd
		);

	///  int gsapi_exit (void *instance);
	[DllImport("gsdll32.dll" , EntryPoint="gsapi_exit")]
	unsafe public static extern int gsapi_exit(
		[In]System.IntPtr inst 
		);

	/// void gsapi_delete_instance (void *instance);
	[DllImport("gsdll32.dll" , EntryPoint="gsapi_delete_instance")]
	unsafe public static extern int gsapi_delete_instance(
		[In]System.IntPtr inst 
		);

	/// int gsapi_set_stdio (void *instance, int(*stdin_fn)(void *caller_handle, char *buf, int len), 
	///			int(*stdout_fn)(void *caller_handle, const char *str, int len), 
	///			int(*stderr_fn)(void *caller_handle, const char *str, int len));
	[DllImport("gsdll32.dll" , EntryPoint="gsapi_delete_instance")]
	unsafe public static extern int gsapi_set_stdio(
		System.IntPtr inst,
		GSStdin stdin, 
		GSStdout stdout, 
		GSStderr stderr 
		);


	private int GsStdOutCallback(System.IntPtr caller, String str, System.Int32 len)
	{
		Console.WriteLine(str);

		return 0;
	}

	unsafe private int CallGS(String[] cmdline)
	{
		int ret = 0;

		System.IntPtr inst = new IntPtr();
		ret = gsapi_new_instance(&inst, new IntPtr(null));
		if(ret != 0)
			return ret;

		GSStdout so = new GSStdout(this.GsStdOutCallback);
		ret = gsapi_set_stdio(inst, null, so, null);
		if(ret != 0)
			return ret;

		ret = gsapi_init_with_args(inst, cmdline.Length, cmdline);

		gsapi_exit(inst);

		gsapi_delete_instance(inst);

		return ret;
	}
}


結局、標準出力にはき出されるGhostscriptからの出力はハンドリングしきれてないんだけどね。
デリゲートをアンマネージに渡すってのが、今のくさってる頭じゃ無理。
いずれやろうかと思います。




コード汚いとか言うな。