大量のPDFをJpegなどの画像を変換するプログラムを書いた時に、VB.Net(VB2008)からC言語で書かれたgsdll32.dllに引数を渡す際には、文字コードの変換やら、.Netの文字列を文字型のバイト列に変える必要があるようです。
まだまだ、GChandle構造体とか「アンマネージドメモリ」とか、理解できているわけではないのですが、一応思い通りの動作をしてくれるところまで至ったのでメモを兼ねてコードを貼り付けさせて頂きます。
まずは、
“DLL Import” や「GCHandle構造体」を使うために
Imports System.Runtime.InteropServices
が冒頭に書かれているとして、次にDLL中の関数を使うために、
<DllImport("gsdll32.dll", EntryPoint:="gsapi_new_instance")> _ Private Shared Function CreateAPIInstance(ByRef pinstance As IntPtr, ByVal caller_handle As IntPtr) As Integer End Function <DllImport("gsdll32.dll", EntryPoint:="gsapi_init_with_args")> _ Private Shared Function InitAPI(ByVal instance As IntPtr, ByVal argc As Integer, ByVal argv As IntPtr) As Integer End Function <DllImport("gsdll32.dll", EntryPoint:="gsapi_exit")> _ Private Shared Function ExitAPI(ByVal instance As IntPtr) As Integer End Function <DllImport("gsdll32.dll", EntryPoint:="gsapi_delete_instance")> _ Private Shared Sub DeleteAPIInstance(ByVal instance As IntPtr) End Sub
を書いておきます。
さて、gsdll32.dllは
1.gsapi_new_instance (CreateAPIInstance)
2.gsapi_init_with_args (InitAPI)
3.gsapi_exit (ExitAPI)
4.gsapi_delete_instance (DeleteAPIInstance)
の順番で呼び出す必要があるのですが、gsapi_init_with_args の引数で出力するファイル名などを文字列で渡してやらないといけません。
この文字列というのがくせ者で、C言語には文字列は文字の配列として扱っています。
さらに、VB.netでは文字コードがUnicode、CではANSIとなっているので文字コードを変換してあげないと行けません。
と、途中をすっとばして、こんな感じで変換してみたら動いてくれました。
Private Sub Hoge() Dim sArgv(10) As String Dim ipGs As IntPtr Dim oAnsiArgs() As Object Dim ipArgs() As IntPtr Dim ipArg As IntPtr Dim gch() As GCHandle Dim gchArgs As GCHandle sArgv(0) = "pdf2img" sArgv(1) = "-dNOPAUSE" sArgv(2) = "-dBATCH" sArgv(3) = "-dSAFER" sArgv(4) = "-r200" sArgv(5) = "-sDEVICE=pngmono" sArgv(6) = "-sOutputFile= 出力する画像ファイル " sArgv(7) = "入力するファイル" ReDim oAnsiArgs(sArgv.Length - 1) ReDim ipArgs(sArgv.Length - 1) ReDim gch(sArgv.Length - 1) For i As Integer = 0 To sArgv.Length - 1 '①文字コード変換 oAnsiArgs(i) = System.Text.Encoding.GetEncoding(932).GetBytes(sArgv(i)) '②変換した文字列をひとかたまりに集める gch(i) = GCHandle.Alloc(oAnsiArgs(i), GCHandleType.Pinned) '③gchの先頭のアドレス(ポインタ) ipArgs(i) = gch(i).AddrOfPinnedObject() Next i gchArgs = GCHandle.Alloc(ipArgs, GCHandleType.Pinned) ipArg = gchArgs.AddrOfPinnedObject() CreateAPIInstance(ipGs, IntPtr.Zero) InitAPI(ipGs, sArgv.Length, ipArg) ExitAPI(ipGs) DeleteAPIInstance(ipGs) End Sub
参考:
・C言語で作成されたDLLをVB.NETにて呼び出す方法
・GhostScript 公式の解説(英語)
・GCHandle 構造体
・http://www.experts-exchange.com/Programming/Languages/C_Sharp/Q_20762995.html
2013/4/22 追記
突然、上記のコードが動かなくなってしまいました。
GhostScript Ver9.0.7 のgsdll32.dllだと動かないようです。
Ver9.0.5のgsdll32.dllだったら問題なく動くので、引数の書式 or gsdll32.dllの依存関係が変わったのでしょうか?
時間が無くて調べられていないのですが、ご存じの方がいらっしゃいましたらお知らせください。
2014/1/16 追記
もしかしたら、この関係かも!?
http://bugs.ghostscript.com/show_bug.cgi?id=694720