喫茶XRAYトップページ


[7911] DLL内の関数のcdecl呼出規約返信 削除
2014/9/9 (火) 00:14:00 Mr.XRAY
とある場所で,cdecl というのを見かけましたので.ちょっと.
この cdecl は関数の呼出規約の 1 つです.
今回は DLL 内の関数の呼出しとなっています.

http://ja.wikipedia.org/wiki/%E5%91%BC%E5%87%BA%E8%A6%8F%E7%B4%84

C または C++ で作成した Linux 用の共有ライブラリを,
Kylix(カイリックス- 死語?), FreePascal で使用する際,
多くの場合,stdcall ではなく cdecl を使用します.
Linux の共有ライブラリの拡張子は so です.libXXXX.so.XXXX の形式です.

DLL 内の関数を cdecl 呼出規約で定義した場合,
DLL 内の関数を使用する側のプログラムも,当然 cdecl 呼出規約で
関数を定義します.

Windows の共有ライブラリ DLL では,上のリンクの記事のように,
stdcall を使用した方が無難です.
stdcall は Pascal の呼出規約と同じと考えて差し支えないと思います.
Windows では stdcall が事実上のデファクトスタンダードの呼出規約です.


呼出規約を cdecl にした場合のサンプルです.
基本的に,以下の記事のように,DLL 内の関数を使用するのに,
DLL を作成した言語は直接の関係はありません.
あるのは,作成した関数の引数の型や戻り値の扱い方等です.

現に,少し前に PDF の作成用の DLL を Visual Studio 2013 Express
で作成しています.

http://mrxray.on.coocan.jp/Delphi/Others/Usage_Win32API.htm


cdecl 呼出規約のDLL内の関数の使用例

implementation

{$R *.dfm}

type
  TQueryStatus = function(ID: Integer; var ResID: Integer): PWideChar; cdecl;

//-----------------------------------------------------------------------------
//  DLL内のcdecl呼出規約の関数の使用例
//
//  Windows 7 U64(SP1) + Delphi XE(UP1) Pro
//  DLLはWindows 2000 + Delphi 6で作成したもの
//
//  2014年09月08日 Mr.XRAY
//-----------------------------------------------------------------------------
procedure TForm1.Button1Click(Sender: TObject);
var
  DLLPath     : String;
  DLLHandle   : Cardinal;
  QueryStatus : TQueryStatus;
  ID          : Integer;
  ResID       : Integer;
  Description : WideString;
begin
  DLLPath := 'cdecl_Test.dll';
  DLLHandle := LoadLibrary(PChar(DLLPath));

  if DLLHandle <> 0 then begin
    try
      @QueryStatus := GetProcAddress(DLLHandle, 'QueryStatus');
      if @QueryStatus <> nil then begin
        ID := 20;
        Description := QueryStatus(ID, ResID);
        ShowMessage(Description + '    ' + IntToStr(ResID));
      end;
    finally
      FreeLibrary(DLLHandle);
    end;
  end;
end;


使用した DLL のコード
DLL は Windows 2000 + Delphi 6 で作成


library cdecl_Test;

uses
  SysUtils,
  Classes;

function QueryStatus(AID: Integer; var ResID: Integer): PWideChar; cdecl;
var
  FID     : Integer;
  StrText : WideString;
begin
  FID     := AID;
  StrText := 'テストDLL関数';

  Result := PWideChar(StrText);
  ResID  := FID * 50;
end;

exports
QueryStatus;

begin
end.

プロジェクトのダウンロード (EXE 付き)
http://mrxray.on.coocan.jp/Delphi/zip/cdecl_dll_functionCall.zip

下図は,添付の EXE の実行結果です.


[7912] Re:DLL内の関数のcdecl呼出規約返信 削除
2014/9/9 (火) 00:53:59 Mr.XRAY
今度は,DLL を Windows 7 U64(SP1) + Delphi XE(UP1) Pro で作成し,
DLL を使用する EXE を Windows 7 U64(SP1) + Delphi 7 Pro で作成した場合です.
コードは全く上の時と同じです.もちろん実行結果も同じです.
DLL のファイルサイズがかなり大きくなっていますが...

Delphi 6 で作成した DLL のサイズ 139 KB
Delphi XE で作した DLL のサイズ  302 KB


[7915] Re:DLL内の関数のcdecl呼出規約返信 削除
2014/9/9 (火) 15:44:01 Mr.XRAY
> あるのは,作成した関数の引数の型や戻り値の扱い方等です.

おっ,福士さんが.
以下に福士さんが詳しい説明をしています.
この喫茶XRAY に掲載したコードについては,福士さんのコードを参考にしてください.
以下のリンクでコードをみると,欠落部分があります.
実際のメールで確認してください.
(他力本願形式でスミマセン)

http://www.freeml.com/delphi-users/3918

SetLength を実行しないと,たとえ文字列が取得できても,
取得した文字列にゴミが入ることがあります.
サンプルプログラム集のどこかに書いていたのですが... 後で (^^;

# かなり前に,取得した文字列が変だという質問があり,直した記憶があります.

で,元の質問された方の現象の解決策は...  えっと... 分かりません (^^;
UP した,提示したサンプルは,あくまでも cdecl 呼出規約のテストです.
たまたま ML で参考となるのを見かけたのでテストしてみただけです.


[7925] Re2:DLL内の関数のcdecl呼出規約返信 削除
2014/9/10 (水) 13:17:03 Mr.XRAY
> で,元の質問された方の現象の解決策は...  えっと... 分かりません (^^;

敢えて言えば,新規にプロジェクトを作成して,その関係だけの動作を確認することですね.
しかし,そのようなレスをすると,中には,
「何万行ものプログラムの中での操作なので,そのようなことはできません」
返してくる方もいます.
そうなると,返す言葉はありません.
うかつにはレスできないです.

言われた方(質問された方)は,
「何言ってやがんだ.何万行もあるプログラムでそんなことできるわけないだろう.
あんたは,そんな大きなプログラム作ったことないから,そんなこと言えるんだ」
と,気分をがいするかも知れません.
一方,こちらは,「そんなことはなかんべ」と思いますしね.


[7926] Re3:DLL内の関数のcdecl呼出規約返信 削除
2014/9/10 (水) 14:36:16 福士
Delphiでの呼出規約についてはオンラインヘルプの
呼び出し規約 (手続きと関数(Delphi) - RAD Studio)
http://docwiki.embarcadero.com/RADStudio/XE7/ja/%E6%89%8B%E7%B6%9A%E3%81%8D%E3%81%A8%E9%96%A2%E6%95%B0#.E5.91.BC.E3.81.B3.E5.87.BA.E3.81.97.E8.A6.8F.E7.B4.84
が詳しいです。DLLではフツーstdcallだろ、というMr.XRAYさんのご指摘はごもっともでしたので、MLのほうは補足させていただきました。

Win32APIで文字列の取得などを行う関数としては例えば

GetComputerName 関数
http://msdn.microsoft.com/ja-jp/library/cc429732.aspx
GetComputerName function (Windows)
http://msdn.microsoft.com/ja-jp/library/windows/desktop/ms724295.aspx

のように、最大長が予め決まっており、バッファサイズを渡してNUL終端文字列が返されるパターンと、

ExpandEnvironmentStrings 関数
http://msdn.microsoft.com/ja-jp/library/cc429716.aspx
ExpandEnvironmentStrings function (Windows)
http://msdn.microsoft.com/ja-jp/library/windows/desktop/ms724265.aspx

のようにバッファサイズを取得できるので1回バッファをNULL(nil)で呼び出し、必要なサイズにバッファをSetLengthで確保してから
2回目を呼び出し、その後で実際に設定されたサイズでSetLengthし直す、というパターンの2種類があります(他にもShellAPI系のように
メモリ確保を呼び出し先で行い、破棄を呼び出し元で行うようなものもありますが)。

前者はNUL終端文字列を前提としており、Win16以来の比較的古いやり方、後者は比較的新しいやり方、ということになりますね。

Mr.XRAYさんには常識的なことだとは思いますが、まぁ補足(蛇足?)ということで。

[7927] Re4:DLL内の関数のcdecl呼出規約返信 削除
2014/9/10 (水) 15:11:01 Mr.XRAY
▼ 福士さん

うあ,スミマセン.こんなむさ苦しいお店まで足を運んでくださいまして.

> 呼び出し規約 (手続きと関数(Delphi) - RAD Studio)
> http://docwiki.embarcadero.com/RADStudio/XE7/ja/%E6%89%8B%E7%B6%9A%E3%81%8D%E3%81%A8%E9%96%A2%E6%95%B0#.E5.91.BC.E3.81.B3.E5.87.BA.E3.81.97.E8.A6.8F.E7.B4.84


ありゃ,オンラインヘルプにもありましたか.
調べませんでした.

> Mr.XRAYさんには常識的なことだとは思いますが、まぁ補足(蛇足?)ということで。

とんでもないでございます.結構迷いながらやっているもんです.
私にとっても,このお店のお客さんにも参考になると思います.

(1) あらかじめバッファサイズを指定して実行する関数
    取得可能な文字列の長さが決まっている場合
    GetComputerName 関数, GetWindowText 関数等
    FillChar 等でバッファを初期化する(文字列の型の定義による)
    Win16 時代からの,比較的古い関数に多い

(2) 1 回目の実行で,必要なバッファサイズを取得して,あるいは,取得する関数
   結果の Delphi の文字列型の長さは SetLength で設定(指定)する
   比較的新しい方法

という感じですね.
というか,改めて認識しました (^^;
恥ずかしながら,このように体系的にとらえていませんでしたね.
MSDN の関数の記事に,その都度したがっていただけです.
よし!!  サイトのどこかの記事に追加しておこうっと...

# 人様のネタで生きてるサイトの記事と喫茶XRAY でございます.

追記:
言い訳ではではありませんが,別にツッコミじゃぁございませんです(笑)
たまたま,過日,PDF の作成のコードで cdecl を使ったこともあり,
おそらく,多くの方はあまり見かけないのでは,という趣旨です.ハイ.
でも,Delphi のオンラインヘルプに書いてあるんですからね.ハハハッ.


[7928] Re5:DLL内の関数のcdecl呼出規約返信 削除
2014/9/10 (水) 15:50:53 福士
> うあ,スミマセン.こんなむさ苦しいお店まで足を運んでくださいまして.
イツモミテマスヨー

> 言い訳ではではありませんが,別にツッコミじゃぁございませんです(笑)
いや、MLのほうで呼出規約のことは見逃していたので、ここを見て、あー、忘れてたー、と
なったのです。ナイスツッコミでした。

[7929] Re6:DLL内の関数のcdecl呼出規約返信 削除
2014/9/10 (水) 15:55:51 Mr.XRAY
▼ 福士さん

> いや、MLのほうで呼出規約のことは見逃していたので、ここを見て、あー、忘れてたー、と
> なったのです。ナイスツッコミでした。


う〜む.福士さんにそう思わせるきっかけを書くなんて,私も成長したもんだ (^^;


INCM/CMT
Cyclamen v3.82