2011-12-16 16 views
1

Dllメソッドを呼び出すときに例外がスローされることがあります。Delphi DLLへのアクセス例外的な例外のスロー

私はこのようにそれを呼んでいる:

public class DllTest 
{ 

    [DllImport(@"MyDll.dll")] 
    public extern static string MyMethod(string someStringParam); 
} 


class Program 
{  

    static void Main(string[] args) 
    { 
     DllTest.MyMethod("SomeString"); 
    } 
} 

そして、私は時々を取得する例外はこれです:

AccessViolationException

Attempted to read or write protected memory. This is often an indication that other memory is corrupt.

私はこの例外を取得する理由は、誰もが任意のアイデアを持っています時々?時にはそれは時々スムーズに動くのですか?

+0

DLL内の関数の呼び出し規約は何ですか? – ain

+2

Delphiメソッドのコードを表示してください –

+0

Delphiでの関数の宣言は、C#の対応するものとは異なります –

答えて

14

p/invokeコードとDelphiコードの間に不一致があります。あなたはDelphiコードを表示していませんが、C#コードはDelphiコードの外観を知るには十分です。

DllImport属性は、呼び出し規約と文字セットのデフォルト値を使用します。つまり、呼び出し規約はstdcallで、文字セットはANSIです。マーシャリング属性を指定していないので、デフォルトのマーシャリングを使用する必要があります。

したがって、あなたのDelphiのコードは次のようになります。

function MyMethod(someStringParam: PChar): PChar; stdcall; 
begin 
    Result := ??; 
end; 

そして今、ここで問題です。 p/invoke marshallerはstringの戻り値を非常に特別な方法で扱います。戻り値のメモリを解放するのは、p/invoke marshallerの責任であると想定しています。また、ネイティブコードと同じアロケータを使用する必要があります。マーシャラーによって行われる前提は、共有COMアロケーターが使用されることです。

したがって、ネイティブコードは、CoTaskMemAllocを呼び出すことによってCOMアロケータでメモリを割り当てる必要があります。私の賭けはあなたのコードはそれをしないし、それは確かにエラーにつながるということです。

ここでは、コード内のC#シグネチャで動作するネイティブのDelphi関数を作成する方法の例を示します。

function MyMethod(someStringParam: PChar): PChar; stdcall; 
var 
    Size: Integer; 
begin 
    Size := SizeOf(Char)*(StrLen(someStringParam)+1);//+1 for zero-terminator 
    Result := CoTaskMemAlloc(Size); 
    Move(someStringParam^, Result^, Size); 
end; 

あなたがこのアプローチを採用する可能性がなくなり、私は選択肢をお勧めします。すべての文字列をC#側にBSTR、デルファイ側にWideStringとマーシャリングします。これらは、COMアロケータによって割り当てられる一致する型です。両方の当事者は、これらのタイプをどうするかを正確に知っており、あなたの人生をより楽にします。

デルファイは関数の戻り値に異なるABIを使用するため、残念ながら、interop境界を越えてDelphi関数からWideStringを返すことはできません。この問題の詳細は私の質問にありますWhy can a WideString not be used as a function return value for interop?

これを回避するには、Delphiコードの戻り型をTBStrと宣言できます。あなたのコードを次のようになります。

C#

[DllImport(@"MyDll.dll")] 
[return: MarshalAs(UnmanagedType.BStr)] 
private static extern string MyMethod(
    [MarshalAs(UnmanagedType.BStr)] 
    string someStringParam 
); 

デルファイ私にとって

function MyMethod(someStringParam: WideString): TBStr; stdcall; 
begin 
    Result := SysAllocString(POleStr(someStringParam)); 
end; 
+0

あなたの答えはすばらしく、多くのことが解決されました。ちょうど今、私は文字列としてexternメソッドを投稿したことに気付きましたが、それは無効です。しかし、それはあなたがそれを考慮に入れて、答えに加えただけなので、問題はありません。それでも、別の例を教えてもらえますか? ref(out)によって渡される文字列パラメータでDelphi関数をどのように消費するのですか?そして、長くてはっきりとした素晴らしい答えのおかげで、本当に助けてくれました。 – Smur

+1

'BSTR' /' WideString'で行います。これは、Delphi側では 'var'または' out'パラメータであり、C#側では 'ref'または' out'パラメータです。本当に、 'BSTR' /' WideString'はこれを簡単にする方法です。 –

+1

素晴らしい。もう一度ありがとう。 :) – Smur

2

、UnmanagedType.BStrを使用しての.NET文字列にデルファイWideStringにのマーシャリングは非常にうまく機能InおよびOutパラメータの場合しかし、文字列を返す関数の場合は失敗します。 WS()が例外を発生させながら、

[DllImport(@"my.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode)] 
[return: MarshalAs(UnmanagedType.BStr)] 
static extern string WS(
    [MarshalAs(UnmanagedType.BStr)] 
    string val 
); 

[DllImport("my.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode)] 
static extern void WS1(
    [MarshalAs(UnmanagedType.BStr)] 
    out string res); 

WS1への呼び出し()だけで正常に動作 -

function WS(val: WideString): WideString; stdcall; 
begin 
    result := val; 
end; 

procedure WS1(out result: widestring); stdcall; 
begin 
    result := 'ABCDE'; 
end; 

とコレスネット宣言 - 私は、Delphiの機能を持っています。また、例外はDelphiプロジェクトに含まれるユニットによって異なります。 "SysUtils"または "Classes"が含まれている場合 - .NETアプリケーションはSEHExceptionを発生させます。 "外部コンポーネントは例外をスローしました"両方のユニットが除外されている場合、アプリケーションは "009C43B4でランタイムエラー203"エラーダイアログを表示し、 。ところで、 "ShareMem"ユニットの使用は何も変わりません。

+0

これは答えではありません。この質問の再記述です:http://stackoverflow.com/questions/9349530/why-can-a-widestring-not-be-used-as-a-function-return-value-for-interop –