[DX10]dll側でSetStyleするとメイン側でアドレスエラー発生する

解決


よっしー  2016-01-06 01:16:38  No: 47853  IP: [192.*.*.*]

XE5のVCLプログラムを10Seattleにコンバートしています。
メイン側のStyleをDll側TFormにも適用する処理で、XE5では問題ないのですが
10SeattleではDll側のTStyleManager.SetStyleコール後、メイン側のTMainMenuを
DropDownしてClickすると「access violation」エラーが発生してしまいます。
(メインとDll両方が10Seattleでビルドされている時のみの様です)

どなたか対処法が解る方いらっしゃいますか?
環境は Win10+Delphi10 Seattle(Update 1とUpdate 1 Hotfixまでは適用済)です。
よろしくお願いします。

[Unit1.pasの一部]
  object MainMenu1: TMainMenu
    Left = 40
    Top = 32
    object N0: TMenuItem
      Caption = #12501#12449#12452#12523
      object N1: TMenuItem
        Caption = #32066#20102
        OnClick = N1Click
      end
    end
  end

[Unit1.pas]
unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
  Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.Menus, Vcl.StdCtrls;

type
  TForm1 = class(TForm)
    MainMenu1: TMainMenu;
    N0: TMenuItem;
    N1: TMenuItem;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
    procedure N1Click(Sender: TObject);
  private
  public
  end;

var
  Form1: TForm1;

procedure DllSetStyle(AFileName: string); external 'DllForm.dll';

implementation

uses
  Vcl.Themes,  Vcl.Styles;

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var
  FileName: string;
begin
  FileName := ExtractFilePath(Application.ExeName) + 'Auric.vsf';
  TStyleManager.SetStyle(TStyleManager.LoadFromFile(FileName));
  DllSetStyle(FileName);
end;

procedure TForm1.N1Click(Sender: TObject);
begin
  Self.Close;
end;

end.

[DllForm.dpr]
library DllForm;

uses
  System.SysUtils, System.Classes, Vcl.Themes, Vcl.Styles;

{$R *.res}

procedure DllSetStyle(AFileName: string);
begin
  TStyleManager.SetStyle(TStyleManager.LoadFromFile(AFileName));
end;

exports
  DllSetStyle;

begin
end.

編集    削除
通りすがり  2016-01-06 03:21:35  No: 47854  IP: [192.*.*.*]

まずはDLLを新規作成するとdprojに自動的に書き込まれる
{ DLL のメモリ管理に関する重要なメモ: パラメータまたは関数結果として文字列を渡す
  手続きまたは関数を DLL がエクスポートする場合は、ShareMem をライブラリの
  uses 句およびプロジェクトの uses 句 ([プロジェクト|ソースの表示] を選択) の
  最初に記載する必要があります。これは、
  DLL との間で渡されるすべての文字列に当てはまります。レコードやクラスに
  ネストされているものも同様です。ShareMem は共有メモリ マネージャ BORLNDMM.DLL に対するインターフェイス
  ユニットです。この DLL は作成対象の DLL と一緒に配置する必要が
  あります。BORLNDMM.DLL を使用しないようにするには、PChar 型または ShortString 型の
  パラメータを使って文字列情報を渡します。}
というコメントの内容はクリアできていますか?

編集    削除
よっしー  2016-01-06 04:20:52  No: 47855  IP: [192.*.*.*]

通りすがりさん、ご回答ありがとうございます。

何やら面倒そうなので、テスト的にdllのDllSetStyle関数は引数なしに変更してみました。
しかし、結果は全く同じでした。

[Unit1.pas]
procedure DllSetStyle; external 'DllForm.dll';  //<--ここ

procedure TForm1.Button1Click(Sender: TObject);
var
  FileName: string;
begin
  FileName := ExtractFilePath(Application.ExeName) + 'Auric.vsf';
  TStyleManager.SetStyle(TStyleManager.LoadFromFile(FileName));
  DllSetStyle;  //<--ここ
end;

[DllForm.dpr]
procedure DllSetStyle;  //<--ここ
var
  FileName: string;
begin
  FileName := ExtractFilePath(Application.ExeName) + 'Auric.vsf';
  TStyleManager.SetStyle(TStyleManager.LoadFromFile(FileName));
end;

編集    削除
通りすがり  2016-01-06 19:28:42  No: 47856  IP: [192.*.*.*]

うーん、違いましたか。ところでAVが起きるのはDLL内ですか?それとも呼び出し元に戻ってきたときですか?
はたまた呼び出し元に戻った後の処理ですか?(疑っているのは呼び出し規約がEXEとDLLで一致していない状況です)

編集    削除
よっしー  2016-01-06 22:48:46  No: 47857  IP: [192.*.*.*]

通りすがりさん、ご回答ありがとうございます。

[メイン側の.dfm]
  object MainMenu1: TMainMenu
    object N0: TMenuItem
      Caption = 'ファイル'
      object N1: TMenuItem
        Caption = '終了'
        OnClick = N1Click
      end
    end
  end
その後もう少し検証してみたところ、Dll側のTStyleManager.SetStyleコール後、
メイン側のMainMenu1のN0(ファイル)をDropDown後フォーム内のどこかClickして、
MainMenu1のDropDownリストをクローズする時に「access violation」エラーが
発生いているようです。

MainMenu1のDropDownリストのクローズ以外は、メイン側フォームもdll側フォームも
正常にStyleが適用されていて動作も問題ありません。


(メインとDll両方が10Seattleでビルドされている時のみの様です)
  exe, dll
  XE5, XE5 => OK
  XE5, D10 => OK
  D10, XE5 => OK
  D10, D10 => NG  <--この組み合わせだけエラーになる!

よろしくお願いします。

編集    削除
通りすがり  2016-01-07 00:41:40  No: 47858  IP: [192.*.*.*]

呼び出し規約を明示的に指定したらどうなります?
[Unit1.pas]
procedure DllSetStyle; stdcall; external 'DllForm.dll';

[DllForm.dpr]
procedure DllSetStyle; stdcall;
var
  ...

編集    削除
よっしー  2016-01-07 04:22:42  No: 47859  IP: [192.*.*.*]

通りすがりさん、ご回答ありがとうございます。
ご指摘の通り stdcall; を追加してみましたが、
結果は全く同じでした。

編集    削除
通りすがり  2016-01-07 19:16:37  No: 47860  IP: [192.*.*.*]

うーん、呼び出し規約はとりあえず無罪ですか。DLL側のDllSetStyleの内容を無難なもの
(ラベルのCaptionを変更するだけとか)に変えたらどうでしょう?

# EXE側で呼び出し前後のスタックの状況とかCPUウィンドウ上で確認してみると何か判るかも…
# あとは公式フォーラムあたりで最低限の再現プロジェクトをつけて質問してみるとか。

編集    削除
Mr.XRAY  2016-01-09 21:13:31  No: 47861  IP: [192.*.*.*]

詳しい状況が分からないのですが,
一般的に,VCL スタイルの適用は,アプリケーションの初期化の時,あるいは,DLL の生成時が基本です. 
そうしないと,access violation (アクセス違反) が発生ことがあります.
Delphi XE5 で何故アクセス違反が発生しないかは,分かりません.

以下に,関係記事を追加してみました.参考になれば.
MDI のサンプル以外は,Delphi 10 Seattle (Subscription UP1) 試用版で動作確認しています.

[889_VCL スタイルをリソースとして埋め込んで使用]
http://mrxray.on.coocan.jp/Delphi/plSamples/889_VCLStyleResource.htm

その他,「delphi VCLスタイル」でググるといろいろ情報があるようです.
ただし,利用する時の注意等は,ひっそりと書かれている場合があります.

編集    削除
Mr.XRAY  2016-01-10 02:39:57  No: 47862  IP: [192.*.*.*]

>以下に,関係記事を追加してみました.参考になれば.
>MDI のサンプル以外は,Delphi 10 Seattle (Subscription UP1) 試用版で動作確認しています.

TMainMenu のアイテムで,Close した時にアクセス違反が発生する現象については,全く分かりません.

編集    削除
Mr.XRAY  2016-01-10 04:02:25  No: 47863  IP: [192.*.*.*]

>TMainMenu のアイテムで,Close した時にアクセス違反が発生する現象については,全>>く分かりません.

引き続きやってみました.
以下の条件で発生するようです.

(1) フォームを実装した DLL を作成
(2) その DLL を使用するプロジェクトを作成
(3) そのプロジェクトに VCL スタイルを設定
(3) そのプロジェクトに TMainMenu を配置
    その TMainMenu にアイテムを追加
    イベント等の設定は必要ない
    
プロジェクトをコンパイルして実行.
DLL の生成時または,EXE を実行して DLL に VCL スタイルを適用する.   
TMainMenu をクリックしてメニューを表示して,メニューをクリック

これで,アクセス違反,あるいは,「Project1.exe は動作を停止しました」のダイアログが現れます.
プロジェクトと DLL の両方に VCL スタイルを適用した場合に発生するようです.
どちらか一方だけに VCL スタイルを適用した場合は発生しません.
Delphi XE7, Delphi 10  Seattle で発生します.Delphi XE6, XE8 は未確認です (手元にないので).

バグっぽいですが,もう少し確認する必要がありそうです.
DLL でフォームを使用なければ問題ないと思うのですが.

編集    削除
よっしー  2016-01-10 06:16:50  No: 47864  IP: [192.*.*.*]

Mr.XRAYさん、色々検証ありがとうございます。

http://mrxray.on.coocan.jp/Delphi/plSamples/889_VCLStyleResource.htm
上記URLを参考に、procedure DLLEntry(Reason: Integer); でやってみましたが
ダメでした。

Windows10で超シンプルなプロジェクトを作成し検証しました。
  1.SetStyleするDllForm.DLLを作成
  2.SetStyleするDllStyle.exeアプリを作成(TFormにTEditを配置)

[DX10での現象]
  1.アプリのみSetStyleした時は問題ない
  2.DLLのみSetStyleした時は、アプリ側のTEditを右クリックしてメニューアイテムを開くと
    すでにDLLでセットしたStyleが適用済みになっている(アプリ側でSetStyleしてないのに..)
  3.アプリとDLLでSetStyleすると、TEditを右クリックしてメニューアイテムをColseすると
    access violation例外は発生する
  4.XE5では、2,3の現象は発生しない。

※DX10のTMenuItem系に問題がありそうです。

>DLL でフォームを使用なければ問題ないと思うのですが.
それは、重々承知のうえで....

編集    削除
よっしー  2016-01-10 06:18:18  No: 47865  IP: [192.*.*.*]

以下が検証の全ソースです。

[DllForm.dpr]
library DllForm;

uses
  Winapi.Windows, System.SysUtils, System.Classes, Vcl.Themes, Vcl.Styles;

{$R *.res}

procedure DllDummy; stdcall;
begin
end;

exports
  DllDummy;

begin
  TStyleManager.SetStyle(TStyleManager.LoadFromFile('Auric.vsf'));
end.

[DllStyle.dpr]
program DllStyle;

uses
  System.SysUtils, System.Classes, Vcl.Themes, Vcl.Styles, Vcl.Forms,
  Unit1 in 'Unit1.pas' {Form1};

{$R *.res}

begin
  TStyleManager.SetStyle(TStyleManager.LoadFromFile('Auric.vsf'));

  Application.Initialize;
  Application.MainFormOnTaskbar := True;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.

[Unit1.pas]
unit Unit1;

interface

uses
   System.Classes, System.SysUtils, Vcl.Forms, Vcl.StdCtrls, Vcl.Controls;

type
  TForm1 = class(TForm)
    Edit1: TEdit;
  end;

var
  Form1: TForm1;

procedure DllDummy; stdcall; external 'DllForm.dll';

implementation

uses
  Vcl.Themes,  Vcl.Styles;

{$R *.dfm}

initialization
  DllDummy;
end.

編集    削除
通りすがり  2016-01-10 06:39:59  No: 47866  IP: [192.*.*.*]

えーと、
> library DllForm;
> ...
> begin
>   TStyleManager.SetStyle(TStyleManager.LoadFromFile('Auric.vsf'));
> end.
これは多分駄目です(本筋には関係ないかもしれませんが)。
DLLMainに相当するこの部分では最低限の処理しか行うべきではありません。

編集    削除
よっしー  2016-01-10 07:01:00  No: 47867  IP: [192.*.*.*]

なぜ、XE5でTMenuItemのaccess violation例外が発生しないか判りました!

そもそも、XE5ではSetStyleしてもTMenuItemはStyleが適用されていない事に
今さら気が付きました。(DX10は適用されています)

※XE5以降のバージョンで、この問題を解決しようとした事によるエンバグ?...

通りすがり 、ご回答ありがとうございます。
>DLLMainに相当するこの部分では最低限の処理しか行うべきではありません。
無理矢理ですが、本筋とは関係なさそうなので。

編集    削除
Mr.XRAY  2016-01-10 07:49:20  No: 47868  IP: [192.*.*.*]

>そもそも、XE5ではSetStyleしてもTMenuItemはStyleが適用されていない事に
>今さら気が付きました。(DX10は適用されています)
>
>※XE5以降のバージョンで、この問題を解決しようとした事によるエンバグ?...

おおっ,よく見つけましたね.
私も,調べていたのですが「壁」です.

ということは...  
バグ,バグと言っていても仕方ないので,できないものはできないですね.

編集    削除
Mr.XRAY  2016-01-10 08:19:16  No: 47869  IP: [192.*.*.*]

私自身は,これ以上の追及はしないことにしました.
貴重な情報を頂きましたので,今後の参考にしたいと思います.

>DLLMainに相当するこの部分では最低限の処理しか行うべきではありません。

分かっていてやっていることとは思いますが,
そのためにメッセージ処理がありますんで (^^)

編集    削除
Mr.XRAY  2016-01-10 21:39:43  No: 47870  IP: [192.*.*.*]

>そもそも、XE5ではSetStyleしてもTMenuItemはStyleが適用されていない事に
>今さら気が付きました。(DX10は適用されています)
>
>※XE5以降のバージョンで、この問題を解決しようとした事によるエンバグ?...

ということですので.TMenuItem に VCL スタイルを適用しなければ使えることになります.

[delphi - VCL Style from DLL is affecting TMenuItem in Application - Stack Overflow]
http://stackoverflow.com/questions/25933252/vcl-style-from-dll-is-affecting-tmenuitem-in-application

↑には,私より厳しい意見があるようです (^^;

私のサイトにも記事にしておきました.
[06_呼出し側と DLL の両方に VCL スタイルを適用する場合]
http://mrxray.on.coocan.jp/Delphi/plSamples/889_VCLStyleResource.htm#06

編集    削除
Mr.XRAY  2016-01-10 21:44:33  No: 47871  IP: [192.*.*.*]

一応,DLL のプロセス生成時に,TMenuItem の VCL スタイルを無効にすることで,Delphi XE5 と同じように動作することを確認しました.
これで,DLL 側のフォームの TMenuItem は,VCL スタイルは無効になりました.

Windows 7 U64(SP1) + Delphi XE7, Delphi 10 Seattle で確認しました.

編集    削除
Mr.XRAY  2016-01-11 18:52:42  No: 47872  IP: [192.*.*.*]

ごめんなさい.間違えました.

> VCL スタイルを無効にすることで

「VCL スタイルフックを無効」です.

編集    削除
よっしー  2016-01-11 22:29:04  No: 47873  IP: [192.*.*.*]

Mr.XRAYさん、色々調べている頂きありがとうございます。

Mr.XRAYさんのサイトを参考に、Dll側だけに
TCustomStyleEngine.UnRegisterSysStyleHook('#32768', TSysPopupStyleHook);
を追加して、うまくいきました。

>[delphi - VCL Style from DLL is affecting TMenuItem in Application - Stack Overflow]
>http://stackoverflow.com/questions/25933252/vcl-style-from-dll-is-affecting-tmenuitem-in-application

↑私自身では、このサイトにはたどり着けなっただろうと思います。
丁寧な対応に大変感謝して居ります。

DLLでのTFormの使用は嫌な予感はしていたのですが、マルコ カントウの本にも使用例なんかも載ってるし...
プロジェクトが大きくなり過ぎて、今さら戻るのも大変でした。

せっかくの苦労を無にする結論ですが、
次のプロジェクトでは、使わないのが無難ですかね(^^)

編集    削除
Mr.XRAY  2016-01-12 02:24:07  No: 47874  IP: [192.*.*.*]

>↑私自身では、このサイトにはたどり着けなっただろうと思います。

検索は結構シビアですよね.ノウハウの 1 つかも知れません.

>丁寧な対応に大変感謝して居ります。

いえいえ,私のサイトにも関連記事があるので,その対応策の意味がありますで.
よっしーさんの質問のおかげで,十分な予防対策となったと思っています.

>プロジェクトが大きくなり過ぎて、今さら戻るのも大変でした。

最初の質問の文章と,コードの提示の仕方で,これは,テスト用のプログラムを新規に
作っているわけではないな,と思いました.
で,失礼ですが,他になんか変なことやっているんじゃないの,と思っていました.

スレッドが進むにつれ,あれっ ? 
まずい,私のサイトにも関連サンプルがあるし,というわけです.

>せっかくの苦労を無にする結論ですが、
>次のプロジェクトでは、使わないのが無難ですかね(^^)

いえ,そんなことはないと思います.自分の信念を貫いてください (笑)
私のサイトの記事は,警告的な意味があるんです.
コミュニティの場で,DLL にフォームを実装したり,それに VCL スタイルを適用する際に,
トラブッていて,苦労している方がいました.
そんなに悩むなら,そういうのはやめたら ? という意味も含んでいるんです.

ちなみに,自分が開発した業務アプリでは,DLL にフォームを実装したことはありません.・

編集    削除