C++Builder : implibでVisual C++のDLLを使う
ガイド
知っていれば便利だと思います
目次
1 | 概要 | |
2 | 解説ビデオ | |
3 | PDFlibのDLLを使ってみる | |
4 | リソース |
履歴
editor | 唯野 |
2020.6.1 | 公開 |
2020.6.3 | 修正 |
2020.6.7 | 修正 |
2020.6.27 | 修正 |
概要
C++には優れたライブラリが多いものの昨今ではWindows用に提供されるMakefileは、ほぼVisual C++一択である。とはいえGUIをRADで作るならC++Builder(以下BCB)の方が早くて楽なのも事実である。そこでDLLはVC++のものを使い、implibでBCB用のlibを生成しリンクするという、両者のいいとこ取りをやってみた。
ちょうどEmbarcaderoからもYoutubeビデオが出ていたので合わせて紹介しておく。実はPDFライブラリについて調べていたところだったので、実例としてVC++で作られたPDFlibのDLLをサンプルとして使ってみる。
環境
Windows10 Pro x64
C++Builder 10.3.2 Enterprise
PDFlib 9.2.0 MSWin32 C/C++ (デモ)
解説ビデオ
全10回でVC++のDLLを使う際の手順や注意点などが紹介されている。やりとりするデータ型も数値、文字列、構造体と一通り扱っている。また、メモリをアロケートした場合、ファイルハンドルの扱い、DLLの動的リンクにも触れており、翻訳も含め内容はよくまとまっている。時間がなければ最後のビデオのまとめだけ見ればよい。その前はVC++とBCBで同等のGUIアプリ(ストップウォッチ)を作りBCBの優位性を宣伝しているだけなのでDLLとは関係ない。
さすがにVC++からいまどきMFCで新規Windowsプログラムを作るのか疑問だが、MSがWPFを含めて迷走しているのも確かなので.net Core次第なのも無理からぬところではある。個人的にはMSが迷走してくれているおかげでレガシーアプリならBCB(Delphi)もまだアリだろう。レガシーアプリがWindows10でも動く点が良くも悪くもMicrosoft = Windowsではなかろうか。
それはともかく非常に乱暴にポイントをまとめると以下の通り。
- VCのDLLからBCB用のlibはimplibで生成する
- .objの変換にはcoff2omfを使う
- DLLの関数はCリンケージで呼び出す
- 呼び出し規約はDLLでは基本的に__stdcallでにする
- シンボルが一致しているかどうかはtdumpで調べる
- 数値・文字列はサイズを含めた型を一致させる
- 構造体はアライメント・パディングを一致させる
- メモリ確保/開放はAP/DLL側のいずれかで両方とも行うようにする
- 但しWin32 API(HeapAllloc/HeapFree)を使う場合この限りではない
- これはファイルオープン/クローズでも同じ
- 動的リンクの場合は関数ポインタのシグネチャを一致させる
リソース管理を一方でまとめるのは同じ標準関数でもVCとBCBではランタイムライブラリが異なるため、VCでmalloc()したポインタをBCBでfree()しても無効だからである。そのため現実的にはfree()呼び出しを行なうラッパーを提供する。
PDFlibのDLLを使ってみる
PDFライブラリとしてlibHaruはフリーだしBCBでもmakeできてお手軽であるが、久しくメンテナンスされていない点が少し心配だったのでPDFlibのDLLも使ってみた。実際にBCBからサンプルプログラムでPDFを生成させてみた手順は以下の通り。
PDFlibのデモをダウンロード
PDFlib Product FamilyからWindows Server x86 and Windows 7/8/10 x86にあるPDFlib-9.2.0-MSWin32-C-C++.zipをダウンロード。下の方には日本語向けのTutorialとAPI Referenceがあるので合わせてダウンロードする。
implibでBCB用のlibを作る
PDFlib-9.2.0-MSWin32-C-C++.zipを解凍するとpdflibディレクトリにDLLがあるのでコマンドラインからimplibを使う。
なお、doc\system-requirements.txtに記載のある通りPDFlibのC/C++バインディングは標準でVC++ 2010以降となっており、VS Community 2017でbind\cpp\examples.slnを開いてビルドすればサンプルプログラムを生成・実行できる。(私の環境ではいくつかエラーになったがhello.exeはできていたので、とりあえず先に進む。)
> implib -a pdflib_bcb.lib pdflib.dll > tdump pdflib_bcb.lib > pdflib_bcb.txt
tdumpした結果を見ると分かるがシンボル名にアンダーバーが必要なのでimplibは-aオプションを付ける。
BCBでVC++のDLLを使う
BCBで新規VCLアプリケーション(Win32)のプロジェクトを作り、プロジェクトにbind\cにあるpdflib.hとhello.c、先ほど生成したpdflib_bcb.libを追加する。
hello.cのmain()をhello()などと名前を変えて以下のように修正する。ついでなので文字列を日本語で出力してみる。
int hello(void) // 関数名変更 { PDF *p; /* This is where the data files are. Adjust as necessary. */ // パスを適時修正、またはdataの方を移動させる(中身まで見ていないが...) const char* searchpath = "../data"; /* create a new PDFlib object */ if ((p = PDF_new()) == (PDF *) 0) { printf("Couldn't create PDFlib object (out of memory)!\n"); return(2); } PDF_TRY(p) { char optlist[256]; const char* fontopt = // 日本語フォントに変更しエンコードをcp932にする(sjisだとエラー) "fontname=MS明朝:0 encoding=cp932 fontsize=24"; /* This means we must check return values of load_font() etc. */ PDF_set_option(p, "errorpolicy=return"); // 日本語フォントを使う場合に必要っぽい PDF_set_option(p, "FontOutline={MS明朝=msmincho.ttc}"); sprintf(optlist, "SearchPath={{%s}}", searchpath); /* Set the search path for font files */ PDF_set_option(p, optlist); if (PDF_begin_document(p, "hello.pdf", 0, "") == -1) { printf("Error: %s\n", PDF_get_errmsg(p)); return(2); } /* This line is required to avoid problems on Japanese systems */ PDF_set_option(p, "hypertextencoding=host"); PDF_set_info(p, "Creator", "hello.c"); PDF_set_info(p, "Author", "Thomas Merz"); PDF_set_info(p, "Title", "Hello World (C)!"); PDF_begin_page_ext(p, 0, 0, "width=a4.width height=a4.height"); PDF_fit_textline(p, "Hello world!", 0, 50, 700, fontopt); // 出力文字列を日本語に変更 PDF_fit_textline(p, "(says C by C++builder 日本語)", 0, 50, 676, fontopt); PDF_end_page_ext(p, ""); PDF_end_document(p, ""); } // 以下略
フォームにボタンを配置してダブルクリックしUnit1.cppにhello()のexternとhello()の呼び出しを追加する。
// includeの下あたり extern "C" { extern int hello(void); } // hello()呼び出し void __fastcall TForm1::Button1Click(TObject *Sender) { hello(); }
フォントパスの指定が不明なので、C:\Windows\Fontsにあるmsmincho.ttcをexeのできるWin32\Debugディレクトリにコピーする。また、pdflib\pdflib.dllも同様にコピーする。
実行
アプリケーションを実行しボタンを押すとexeのあるディレクトリにhello.pdfができる。指定した日本語文字列が出力されていることを確認する。
なお、下記のリソースやAPIリファレンスを見る限りでは、CバインディングでもUTF8を使えるようだ。
リソース
- PDFlib での文字列の扱い Java/PHPでMS明朝を使った場合のサンプルプログラムも掲載されている