2011年10月29日

釣銭機のOPOSドライバメモ

※今回の記事に関しては、私の手元に動かせる環境がないため質問などを頂いてもお答えできません。その他の呼び出し手続きに関しても同様です。
※またDelphi7で当方で動かした結果からの推測ですので、いつも以上に記事の正確性は保証しかねますので、参考にされる際には充分にテストを行った上で、飽くまで自己責任でご利用下さい

※※個人の趣味で釣銭機を動かすって人は、かなり少ないと思われるので...(苦笑)

職場で釣銭機の担当者からReadCashCountsを動かすと例外が出るって相談を受けて、一緒に調査。

○問題その1

概要:どうやらドライバ側はvar pDiscrepancy: WordBoolに4バイト書き込んでくるようだ。

"ReadCashCounts"でぐぐった所、↓の文書を発見。

OLE for Retail POS技術解説マニュアル

--- 引用開始 ---
【Q3.2.4】 自動釣り銭機のSO処理で、ReadCashCountsメソッド処理で、pDiscrepancyのBOOL値を書き込むとVBの変数エリアが破壊されますが、何故でしょうか。
【A3.2.4】 VC++のクラスウィザードを使用してReadCashCountsメソッドを追加する際、pDiscrepancyのタイプをBOOL*で指定します。この際、C++のBOOL*型が割り当てられますが、BOOL型はlong型で宣言されており、4バイトの記憶領域サイズが割り当てられます。ところが、VBのBoolean変数は2バイトの記憶領域サイズとなっており、SOで、
*pDiscrepancy = TRUE;
等とコーディングすると、2バイト余計にVBの記憶領域に書き込みます。

--- 引用終了 ---

Delphiのbooleanだって2バイトだし!

生成されたTLBを見てみると...

function ReadCashCounts(var pCashCounts: WideString; var pDiscrepancy: WordBool): Integer;

当初、pDiscrepancy: WordBoolをLongBoolに変えてみたが、COMにはンな型はないと怒られコンパイルが通らない。

#ホント何で中身LongBoolにしたんだ一体?

そこでpDiscrepancy: PWordBoolに変えて、呼び元のpDiscrepancyはLongBoolにしておく作戦で確認した所、確かに4バイト分書き込まれていた!

...そりゃアドレス違反になるわ(--;

○問題その2

概要:var pCashCounts: WideStringにはSysAllocString()で割り当てたポインタ以外だと動きがおかしくなるようだ。
※これはCOMのBSTRに関しては一般的な話っぽい。

これでアドレス違反の問題は解決したけど、デバッガで動かしていると、時々CPU窓が開いたりと挙動不審。再びぐぐった結果から、どうやら元の定義は↓のようだ。

LONG ReadCashCounts (BSTR* pCashCounts, BOOL* pDiscrepancy);

今度はBSTRをぐぐった所、以下の情報が。

http://www2.wbs.ne.jp/~kanegon/doc/bstr.txt

--- 引用開始 ---
しかし、実際には先頭に文字列長さを保持しており、この2つを混同しないこと。
BSTR のポインタはメモリ上の先頭を指していないため、BSTR を要求する関数に
LPOLESTR を渡してはならない。また、その関係で BSTR のメモリ管理は完全に別物
である。通常のメモリアロケート関数で処理しないこと。
ただし、LPOLESTR を要求する関数に BSTR を渡すことは問題ない。
※図を省略)
BSTR の領域の取得/解放は SysAllocString()/SysFreeString() または
その関連 API を使用して行なう。
--- 引用終了 ---

DelphiのWideStringは...UNICODEのNULL終止互換(※うろ覚え(^^;) 担当者はフツーにGetMemしていたので、SysAllocString()/SysFreeString()に変えてみたところ、これまた型が合わないと怒られる(^^; まぁそうだね〜と思いvar pCashCounts: WideStringをPointerに修正(だんだん面倒になったw)
これでCPU窓も開かずに安定した模様...

#これは私のCOMに対する理解不足が原因...(^^;


-----------------------
TLB側
-----------------------
・_DCashChanger = dispinterfaceのReadCashCountsはそのまま。

・TCashChanger = class(TOleControl)のReadCashCountsを変更

function ReadCashCounts(var pCashCounts: WideString; var pDiscrepancy: WordBool): Integer;
 ↓
function ReadCashCounts(pCashCounts: Pointer; pDiscrepancy: PWordBool): Integer;

・TCashChangerの実装を修正

function ReadCashCounts(pCashCounts: Pointer; pDiscrepancy: PWordBool): Integer;
Result := DefaultInterface.ReadCashCounts(WideString(pCashCounts^), pDiscrepancy^);
end;

-----------------------
呼び元側サンプル
※コンポは適当に張ってね。
-----------------------
procedure TForm1.Button1Click(Sender: TObject);
var
pStr: PWideChar;
bRes: LongBool; // 問題1対応。絶対にLongBoolで
p: pointer;
s: string;
begin
with CashChanger1 do begin
s := DupeString(#$20#$0, 100); // stringでUNICODEの空白を100文字分(※手抜きw)
pStr := SysAllocString(PWideChar(s));

if ReadCashCounts(@pstr, @bRes) <> OPOS_SUCCESS then begin
ShowMessage('ReadCashCounts:' + IntToStr(ResultCode));
end;

if bRes then begin
ShowMessage('在高異常');
end;

pnl_Aridaka.Caption := pstr;
pnl_ResultCode.Caption := IntToStr(ResultCode);
pnl_ResultCodeEx.Caption := IntToStr(ResultCodeExtended);
end;

SysFreestring(pStr);
end;

ラベル:Delphi7 OPOS
posted by ありい at 16:44| Comment(0) | TrackBack(0) | Delphi | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。
※ブログオーナーが承認したコメントのみ表示されます。

この記事へのトラックバック
×

この広告は1年以上新しい記事の投稿がないブログに表示されております。