

.NET 4.5 (C# 6.0)にてWindowsフォームアプリケーションを開発しています。  
下記のライブラリを使用してTaskDialogを使用したところ、例外が発生します。

  * Microsoft.WindowsAPICodePack.dll
  * Microsoft.WindowsAPICodePack.Shell.dll

【例外の内容】

[code]

    System.ComponentModel.Win32Exception (0x80004005): 
    Dialog contents too complex.
    System.OutOfMemoryException: Insufficient memory to continue the execution of the program.
    at Microsoft.WindowsAPICodePack.Dialogs.NativeTaskDialog.NativeShow()
    at Microsoft.WindowsAPICodePack.Dialogs.TaskDialog.ShowCore()
    
[/code]

【追記】  
x64環境で64ビットモードで動作させた場合に例外が発生します。  
Visual Studioにて32ビット優先にチェックを入れてコンパイルしたところ、正常に動作します。


CC BY-SA 3.0
2014-12-29T03:12:12.803
2766
2014-12-31T09:06:25.433
2014-12-31T09:06:25.433
728
2207
question
windows c# .net
64bit環境でWindowsAPICodePackのTaskDialogにてTaskDialogCommandLinkを使用した場合例外が発生


Windows 8 では試せていませんが、Windows 8.1 + Visual Studio 2013の環境では動作しました。

C# 5.0と.NET 4.5です。

Windows Formアプリケーションを作成し、  
nuGetパッケージマネージャーから  
`Windows7APICodepack`を検索して無印とShellをインストールします。(これでCoreもインストールされ都合3つ入ります。)

プロジェクトにアプリケーションマニフェストファイルを追加し、追加されたファイルを開き、最後の方にコメントアウトされている、`Microsoft.Windows.Common-
Controls`(version="6.0.0.0")のdependencyのコメントアウトを解除して有効にします。

フォームにボタンを配置し、以下の様なイベントハンドラを設定して実行すればダイアログが表示されました。

[code]

    void button1_Click(object sender, EventArgs e) {
        var dialog = new TaskDialog();
        dialog.Caption = "cap";
        dialog.InstructionText = "inst";
        dialog.Text = "text";
        dialog.Icon = TaskDialogStandardIcon.Information;
        dialog.StandardButtons = TaskDialogStandardButtons.Ok;
        dialog.Show();
    }
    
[/code]

なお、そのままだとアイコンが表示されないようです(バグ？)、その時は以下の様にopenedで再代入してみてください。

[code]

    void button1_Click(object sender, EventArgs e) {
        var dialog = new TaskDialog();
        dialog.Caption = "cap";
        dialog.InstructionText = "inst";
        dialog.Text = "text";
        dialog.Icon = TaskDialogStandardIcon.Information;
        dialog.StandardButtons = TaskDialogStandardButtons.Ok;
    
        dialog.Opened += dialog_opened;
        dialog.Show();
    }
    
    void dialog_opened(object sender, EventArgs e) {
           TaskDialog taskDialog = sender as TaskDialog;
           taskDialog.Icon = taskDialog.Icon;
           taskDialog.InstructionText = taskDialog.InstructionText;
    }
    
[/code]

* * *

# 追記

新たにまっさらなWin8.1 Pro + VS2015プレビューと Win8.1 Pro を用意して確認してみました。

私が先に示したコードでは問題は起きませんでしたが、コメント欄で言っていた`TaskDialogCommandLink`
を使用した場合、確かに再現します。これはWindowsAPICodePackの問題です。  
VS2015上でデバッグ実行した場合でも同様に発生しました。

[**本家SO**](https://stackoverflow.com/questions/12059736/taskdialog-from-
winapicodepack-does-not-work-on-net-4-0)で同様の問題が投稿されていました。

こちらにあるようにポインタを32bit限定にしているのが問題で、Overflow Exceptionが発生します。  
これはMSの1.0.0でも1.1.0でも同様に発生します。

お使いの1.1.1でその例外(too
complex)が発生する原因はわかりませんが、githubのコードを修正して64bit環境でもTaskDialogCommandLinkが動作するバージョンの作成方法を示したいと思います。

元々 <http://code.msdn.microsoft.com/WindowsAPICodePack/>
でコードが公開されていたようですが既に閉じられています。（逆に言えば公式の修正はもうないのかも？）  
なのでソースを取得します。お使いの1.1.1と思われる[ **aybe/Windows-API-Code-
Pack-1.1**](https://github.com/aybe/Windows-API-Code-
Pack-1.1)のリポジトリのソースからダウンロードしました。

  * Microsoft公式のソースではありません。
  * .NET 4.5にアップグレードし、VS2015 Previewでコンパイルするための手順です。他の環境については説明しません。
  * TaskDialogCommandLinkが動作するためだけの最小限のソース修正のみを行っています。
  * ShellExtensionsプロジェクトは除外し、対応しません。
  * 特に私はプルリクエストを出すつもりもありませんのでここでの内容をプルリしても結構です。

以上を踏まえた上で確認してください。

## 修正手順

### プロジェクトのアップグレード

ソースをクローンまたはダウンロードし、`WindowsAPICodePack12.sln`のソリューションをVS2015
Preview開きます。オリジナルのソースも同梱されているようですので、そちらがお好みなら`source
(original)`ディレクトリのzipを解凍して`WindowsAPICodePack10.sln`ソリューションから修正してください。

元々3.5ベースなので、3.5を導入していない環境ではアップグレードウィザードが発生します。そのまま4.5にアップグレードします。  
3.5環境でも使いたいのであれば別の開発環境を用意するか、.NET 3.5をインストールしてください。

ShellExtensionsではインターフェースの参照エラーでいろいろとエラーが発生しますがそちらは今回参照しませんので対応しません。ソリューションから除外してください。

### 参照の追加

Shellプロジェクトにおいて`ContentProperty` 属性がみつからないためコンパイルエラーが発生します。  
ShellプロジェクトにSystem.Xaml名前空間を追加して解決します。

### is Nullableの修正

VS2015(というかRoslyn？)では is の第二オペランドにNullableを指定できません。このため

`WindowsAPICodePack\Shell\PropertySystem\ShellProperty.cs`の205行目でコンパイルエラーが発生します。

この行がそもそも何をしているのかすこし不明なのですが（コード的には、`value is
Nullable`がtrueなら、valueのHasValueプロパティを呼び出して、結果がfalseならnullだからnullと同じ処理をしようというコードに見えますが、Tがint?でも
value is Nullable はtrueにはならないし、Tがtypeでvalueが typeof(int?) でもtrueにはならない）、  
以下の様に修正しました。

[code]

    // if (value is Nullable)
    if (Nullable.GetUnderlyingType(typeof(T)) != null) 
    {   // when type of value T is Nullable-type ( Tがnullableなら ）
        Type t = typeof(T);
        PropertyInfo pi = t.GetProperty("HasValue");
        if (pi != null && value != null)
        {
    
[/code]

205行目だけでなく、210行目のif文でもvalue !=
nullとしている事に注意してください(確認したらNullable型のvalueがnullだとその後GetValueで落ちたので）。else
ifのブロックだけでもいいのではと思わないでもないですが。

### 問題の箇所を修正

問題のエラーの原因箇所を修正します。

`WindowsAPICodePack\Core\Interop\TaskDialogs\NativeTaskDialog.cs`  
の533行目 `AllocateAndMarshalButtons` メソッドのforeachの中でcurrentPtrを設定している部分

[code]

    Marshal.StructureToPtr(button, currentPtr, false);
    currentPtr = (IntPtr)((int)currentPtr + Marshal.SizeOf(button));
    
[/code]

を以下に書き換えます（本家では64bit限定に修正されますが、両対応する修正にします）。

[code]

    Marshal.StructureToPtr(button, currentPtr, false);
    // currentPtr = (IntPtr)((int)currentPtr + Marshal.SizeOf(button));
    if(IntPtr.Size == 4)
    {
        currentPtr = (IntPtr)((int)currentPtr + Marshal.SizeOf(button));
    }
    else
    {
        currentPtr = (IntPtr)(currentPtr.ToInt64() + Marshal.SizeOf(button));
    }
    
[/code]

4がマジックナンバーじゃんというツッコミがあるかもしれませんが、他箇所でも4とか8で判定している箇所があったので気にしません :-)

これでビルドすれば無事ビルドは完了すると思います。

### 自分のソースの参照を変更する

nuGetのパッケージをアンインストールし、先ほどビルドしたアセンブリを参照設定で追加します。  
Shellの方(Debugビルドなら`WindowsAPICodePack\Shell\bin\Debug`)を見ればCoreも参照されていてアセンブリがいると思いますので、ここにある2つを追加します。

その後、 プラットフォームターゲット `AnyCPU`で32bit優先オフでもプラットフォームターゲット
`x64`でも、AnyCPU+32bit優先、x86、どれでも動くと思います。

検証に使ったコード(ダイアログ表示部分)は以下の通りです。

[code]

        private void button2_Click(object sender, EventArgs e)
        {
            var commandLinkDialog = new TaskDialog();
    
            commandLinkDialog.Caption = "cap";
            commandLinkDialog.InstructionText = "inst";
            commandLinkDialog.Text = "How r u";
    
            var link = null as TaskDialogCommandLink;
    
            link = new TaskDialogCommandLink("fine", "I'm fine.");
            link.Click += (s, evt) => commandLinkDialog.Close();
            commandLinkDialog.Controls.Add(link);
    
            link = new TaskDialogCommandLink("sick", "I'm sick.");
            link.Click += (s, evt) => commandLinkDialog.Close();
            commandLinkDialog.Controls.Add(link);
    
            commandLinkDialog.Show();
        }
    
[/code]

![実行できている事とプロジェクトオプション](https://i.stack.imgur.com/8vFc0.jpg)

一応確認はしたつもりですが、修正対象ソースの取得元を2,3回変えたのでなにかおかしなことがあれば指摘してください。


CC BY-SA 3.0
2014-12-29T07:03:10.080
2776
2014-12-31T08:21:07.387
2017-05-23T12:38:56.467
-1
728
2766
answer