GhostScriptのgsdll32.dllの使い方について

解決


HMMA  2016-12-18 22:50:33  No: 48444

こんにちは
GhostScriptのgsdll32.dllを使って、pdfから画像を取り出したいと思って、次のリンクなどを参考にしています。
http://stackoverflow.com/questions/14764652/calling-external-dll-with-array-of-pchar-as-parameter-in-xe

uses gsapi;

procedure PDF2PNG(input : AnsiString; output: AnsiString);
var
  ExitCode:integer;
  instance: Pointer;
  Arg: array of PAnsiChar;
begin
  ExitCode := gsapi_new_instance(instance, nil);
  if ExitCode < 0 then
    raise Exception.Create('Impossible to open an instance of ghostscript. Error ExitCode: '+IntToStr(ExitCode));
  try
    SetLength(Arg, 11);
    Arg[0] := PAnsiChar('ps2pdf');
    Arg[1] := PAnsiChar('-dNOPAUSE');
    Arg[2] := PAnsiChar('-dBATCH');
    Arg[3] := PAnsiChar('-dSAFER');
    Arg[4] := PAnsiChar('-sDEVICE=pngalpha');
    Arg[5] := PAnsiChar('-r300');
    Arg[6] := PAnsiChar('-dTextAlphaBits=4');
    Arg[7] := PAnsiChar('-sOutputFile='+output+' Page-%02d.png');
    Arg[8] := PAnsiChar('-c');
    Arg[9] := PAnsiChar('.setpdfwrite');
    Arg[10]:= PAnsiChar('-f'+ input);

    ExitCode := gsapi_init_with_args(instance, Length(Arg), @Arg[0]);
    if ExitCode < 0 then raise Exception.Create('ERROR: init_args: '+IntToStr(ExitCode));
    gsapi_exit(instance);
  finally
    gsapi_delete_instance(instance);
  end;
end;

procedure TForm1.Button4Click(Sender: TObject);
var
 PdfPath:string;
begin
 PdfPath:= 'C:\tmp\';
 pdf2tif(PdfPath+'test.pdf',PdfPath+'test.png');
end;

「Impossible to open an instance of ghostscript. Error code: -100.」が出て出力できません。
どこが悪いのかどなたか、教えてください。
よろしくお願いします。

Ghostscript 9.20
https://ghostscript.com/download/
Win10とDelphi 10 Seattleを使っています。


通りすがり  2016-12-19 00:41:19  No: 48445

gsapi_new_instanceはどのような定義ですか?
gsapi_new_instance(@instance,nil);
のような気がしますが。


HMMA  2016-12-19 02:12:00  No: 48446

通りすがりさん、早速ありがとうございます。

>gsapi_new_instanceはどのような定義ですか? 
uses している gsapi.pas の
function gsapi_init_with_args(pinstance:Pgs_main_instance;argc:integer;argv:PPAnsiChar):integer; stdcall; 
を呼んで、
implementation  下は、 
function gsapi_init_with_args; stdcall; external gsdll32 name 'gsapi_init_with_args'; 
となっています。

gsapi.pasは、
最初に貼った、http://stackoverflow.com/questions/14764652/calling-external-dll-with-array-of-pchar-as-parameter-in-xe 
の中ほどの
Update: here is a corrected version of the gsapi.pas unit that should work in both Delphi versions:
以下のものです。

「delphi gsapi gsdll32.dll」等で検索すると似たような、サンプルがいくつもあり、コピペして見ていますが、
同じようなエラーになります。コピペばかりで根本が分かっていないものですから・・・
お世話になります。


HMMA  2016-12-19 04:39:33  No: 48447

>gsapi_new_instance(@instance,nil); 
>のような気がしますが。 

"@"を入れてみましたら、
「Impossible to open an instance of ghostscript. Error code: -100.」が
「ERROR: init_args: -100.」に変わりました。


通りすがり  2016-12-19 10:15:21  No: 48448

うまくいったようですね。つまりコピペではなくきちんとコードを理解して記述すれば動くようになるということです。

あなたが読む気のないコードをこちらも読む気はないので当てずっぽうですが、コンパイルエラーが出ないのに
正しく動かない理由は、DLLで『ポインタのポインタ』を要求しているのに、あなたのコードは単なるポインタを
渡しているから、のような気がします。コンパイラは単なるポインタの受け渡しをすればよいと理解してそのような
バイナリを出力しますが、実際に動作させるとDLLは渡されたアドレスから逆参照を2回行って、不正な内容が
存在しているのでエラーを返している、ということではないかと。


HMMA  2016-12-19 18:22:24  No: 48449

>コンパイルエラーが出ないのに正しく動かない理由は、
>DLLで『ポインタのポインタ』を要求しているのに、あなたのコードは単なるポインタを渡しているから、
>不正な内容が存在しているのでエラーを返している、ということではないかと。 

>うまくいったようですね。
せっかく、遅い時間にもかかわらず助言をいただいたのに、理解できずにすいません。
DLLに送った内容が不正という事でしょうか?
もう少しもがいてみます。


au  2016-12-19 19:22:20  No: 48450

リンク先の中段辺りのgsapi.pasを使ってるという事ですが、そのgsapi.pasは、「Update: here is a corrected version of the gsapi.pas unit that should work in both Delphi versions: 」のgsapi.pasのリンク先の物を使ってませんか?
そのリンク先のじゃなく、SOの回答の中に書いてあるgsapi.pasをコピーしたら、そこにあるコードでそのまま動くんじゃないですかね?

回答の中に書いてあるgsapi.pasと回答からリンク張ってある先のgsapi.pasは同じじゃないですよ


HMMA  2016-12-19 22:56:20  No: 48451

auさん、ご指摘ありがとうございます。

>回答の中に書いてあるgsapi.pasと回答からリンク張ってある先のgsapi.pasは同じじゃないですよ 
おっしゃる通りでした。
他の形式はこれから試してみますが、以下、bmpで試したところ問題なくPDFがbmpに変換されました。

//procedure pdf2bmp(input : String; output: String);
procedure pdf2bmp(input : AnsiString; output: AnsiString);
var
  code:integer;
  instance: Pointer;
  argv: array of PAnsiChar;
 // argv:PPAnsiChar;
begin
  code := gsapi_new_instance(instance, nil);
  if code < 0 then
    raise Exception.Create('Impossible to open an instance of ghostscript. Error code: '+IntToStr(code));
  try
    SetLength(argv, 8);
    argv[0] := PAnsiChar('gsdll32TEST');
    argv[1] := PAnsiChar('-dSAFER');
    argv[2] := PAnsiChar('-dBATCH');
    argv[3] := PAnsiChar('-dNOPAUSE');
    argv[4] := PAnsiChar('-sDEVICE=bmp16m');
    argv[5] := PAnsiChar('-r400');
   // argv[6] := PAnsiChar('-sOutputFile=test.bmp');  //-->Stringでok
    argv[6] := PAnsiChar('-sOutputFile='+output); //-->AnsiStringならok

   // argv[7] := PAnsiChar('test.pdf'); //-->Stringでok
    argv[7] := PAnsiChar(input);  //-->AnsiStringならok

     code := gsapi_init_with_args(instance, Length(argv), @argv[0]);
    if code < 0 then
      raise Exception.Create('ERROR: init_args: '+IntToStr(code));
    try
    //  ...
    finally
      gsapi_exit(instance);
    end;
  finally
    gsapi_delete_instance(instance);
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
//PdfPath:String;
 PdfPath:AnsiString;

begin
 PdfPath:= 'C:\tmp\';
 pdf2bmp(PdfPath+'test.pdf',PdfPath+'test.bmp');
end;

意味が分かってないのに、あちこちからコピペして試しているうちに、なおさら訳が分からなくなっていました。
ポインタの概念はわかりませんが、DLLを初めて利用しとても有難いものだと思いました。
おかげさまで利用のためのヒントをいただいた気がします。
通りすがりさん、au さん、本当にありがとうございました。


HMMA  2016-12-20 16:39:51  No: 48452

Tiffやpng等、Ghostscriptをバッチファイルで使うのと同じように変換できました。
ありがとうございました。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加