2016-05-10 8 views
5

私は、次のC#の方法があります:それはしかしnewがインターフェイスで使用することはできないと言って、let shell = new Shell32.Shell()ラインのエラーを報告しShell COMを使用するC#コードをF#に変換する方法は?

let private isLink filename = 
    let pathOnly = Path.GetDirectoryName(filename) 
    let filenameOnly = Path.GetFileName(filename) 
    let shell = new Shell32.Shell() 
    let folder = shell.NameSpace(pathOnly) 
    let folderItem = folder.ParseName(filenameOnly) 
    folderItem <> null && folderItem.IsLink 

:として私はF#1にこれを変換しようとしている

private static bool IsLink(string shortcutFilename) 
{ 
    var pathOnly = Path.GetDirectoryName(shortcutFilename); 
    var filenameOnly = Path.GetFileName(shortcutFilename); 

    var shell = new Shell32.Shell(); 
    var folder = shell.NameSpace(pathOnly); 
    var folderItem = folder.ParseName(filenameOnly); 
    return folderItem != null && folderItem.IsLink; 
} 

をタイプ。

私はちょっと愚かな構文ミスを犯しましたか、F#からCOMにアクセスするために余分な作業が必要ですか?

+1

私はちょうど私が答えのための人気のある検索エンジンをチェックすると思った。 "f#com shell32"を検索し、この質問がトップの結果として出てきました。これは一般的なF#ユースケースではないと推測します:) –

+4

F#コンパイラにはおそらく[CoClass]属性ルックアップ機能がありません。 interop型の埋め込みもサポートしません。回避策は '新しいShell32を使うことです。Shell32Class() 'または、C#クラスライブラリに書き込んで参照を追加すると、誰もがそうしていると思います。 –

+0

@HansPassant、ありがとう、それは正しい方向に私を指摘した。 'let shell = new Shell32.ShellClass()'に行を変更すると問題は解決し、すべて正常に動作します。今私は "ああ、それを固定して、今すぐ移動する"アプローチを取るのが好きではないので、*働いている理由*を働かせてください。私は知る必要がある! :) –

答えて

9

私はF#コンパイラについては十分に分かっていませんが、あなたのコメントは十分に分かります。 C#およびVB.NETコンパイラは、COM組み込み関数を明示的にサポートしています。あなたの文は、インターフェイスタイプのnew演算子を使用して、相互運用ライブラリでShell32.Shellはこのようになっていることに注意してください:

[ComImport] 
[Guid("286E6F1B-7113-4355-9562-96B7E9D64C54")] 
[CoClass(typeof(ShellClass))] 
public interface Shell : IShellDispatch6 {} 

IShellDispatch6は、実際のインターフェイスタイプで、あなたもIShellDispatch5インターフェイスを介してIShellDispatchを見ることができます。これは過去20年にわたる職場でのバージョン管理であり、COMインターフェイスの定義は不変です。変更すると、実行時にほとんど常にハードなクラッシュが発生するためです。

[CoClass]属性はこのストーリーにとって重要なものです。これは、[ComImport]インターフェイスタイプでnewを使用するC#コンパイラが探しているものです。 Shell32.ShellClassインスタンスのインスタンスを作成してオブジェクトを作成し、シェルインターフェイスを取得するように指示します。 F#コンパイラは何をしません。

ShellClassは、偽のクラスであり、タイプライブラリインポータによって自動生成されます。 COMは具体的なクラスを公開することはなく、ハイパーピュアなインターフェイスベースのプログラミングパラダイムを使用します。オブジェクトは常にオブジェクトファクトリによって作成されますが、CoCreateInstance()がそのための大きな挑戦者です。それ自身の便利な機能は、実際の作業はユニバーサルIClassFactory interface、ハイパーピュアなスタイルで行われます。すべてのCOMコクラスは、そのCreateInstance()メソッドを実装しています。

タイプライブラリインポータはShellClassはこのように見える:火災や動きの

[ComImport] 
[TypeLibType(TypeLibTypeFlags.FCanCreate)] 
[ClassInterface(ClassInterfaceType.None)] 
[Guid("13709620-C279-11CE-A49E-444553540000")] 
public class ShellClass : IShellDispatch6, Shell { 
    // Methods 
    [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime), DispId(0x60040000)] 
    public virtual extern void AddToRecent([In, MarshalAs(UnmanagedType.Struct)] object varFile, [In, Optional, MarshalAs(UnmanagedType.BStr)] string bstrCategory); 
    // Etc, many more methods... 
} 

ロットを、それのどれもが今まで使用してはいけません。 が実際にの問題はCoCreateInstance()が必要とするCLSIDを提供する[Guid]属性だけです。また、インタフェース宣言によって提供されるIID、インタフェースの[Guid]も必要です。

したがって、F#の回避策は、C#コンパイラが暗黙的に行うように、Shell32.ShellClassオブジェクトを作成することです。技術的には、参照をShellClass変数に保存することができますが、代わりにインターフェイスタイプを強く推奨します。 COMの方法、純粋な方法、それはthis kind of problemを避ける。結局のところ、ジョブを完了させるCLRは、new演算子実装のShellClassクラス宣言の[ClassInterface]属性を認識します。 .NETでのより明示的な方法は、Type.GetTypeFromCLSID()とActivator.CreateInstance()を使用することです。これは、コクラスのGuidしか持っていないときに便利です。

+3

Jon Skeetのブログ記事「C#コンパイラを騙すためのCOM Faking」(https://codeblog.jonskeet.uk/2009/07/07/faking-com-to-fool-the) -c-compiler /)は、このトピックを別の観点から説明します。 – Brian

関連する問題