3

.NET から FoxPro (COM 準拠言語) にバイナリ データを取得しようとしています。ComVisible である .NET オブジェクトと、文字列パラメーターを持つイベントを持つイベント インターフェイスがあります。

以下の私の例では、0 から 255 までのすべての連続する文字を含む文字列を返すダミーの実装があります。疑問符に変換されます。154 を超えると、文字は再び変更されません。

この問題の原因は何ですか?残念ながら、FoxPro にはバイナリ データをネイティブに表現する方法がなく、文字列でカリー化する必要があります。

[Guid("974E3133-9925-4148-8A2B-F4B811072B17"), ComVisible(true), ComSourceInterfaces(typeof(IStreamEvents))]
public class DumbSerialPort {
    readonly string _buf;

    public event DataReceivedHandler DataReceived;
    public event EmptyDelegate Error;

    public DumbSerialPort() {
        var bbuf = new char[255];
        for (int c = 0; c < 255; c++)
            bbuf[c] = (char)c;

        _buf = new string(bbuf);
    }

    public void Fire() {
        if(DataReceived != null)
            DataReceived(_buf);
    }
}

[Guid("0F38F3C7-66B2-402B-8C33-A1904F545023"), ComVisible(true), InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface IStreamEvents {
    void DataReceived(string data);
    void Error();
}
4

3 に答える 3

4

問題

したがって、この問題の理由は少し複雑であり、FoxPro が Unicode 文字列をサポートしておらず、COM が文字列データにのみUnicode を使用するように明示的に定義されているという事実に根ざしています。このインピーダンスの不一致を除いて、不透明なバイナリ データを含む文字列を渡すと問題なく動作します。

FoxPro は、文字列引数を返す関数または文字列引数を持つ関数を呼び出すたびに、ユーザー コードに戻る前に内部的にコードページ変換を行います。この変換は明らかに、文字列に隠されているバイナリ データを移動するあらゆる種類の問題を引き起こします。

解決策 (問題あり)

うまくbyte[] いくはずで、部分的にはうまくいきます。この「部分的に」は、バイナリデータを文字列に隠そうとした原因です。

これが取り引きです(そして、私が使用しているのはVFP 9 SP2でのみこれを確認しました)。C# COM 側では、FoxPro次のように定義されたメソッドを処理できます。

public byte[] GetData() { ... }

そのメソッドを呼び出すと、FoxPro はデータをバイナリとして「マークされた」文字列として正しく返します ( 「マークされたバイナリ文字列」の説明については、 CreateBinary()を参照してください)。これらの文字列は、非バイナリ文字列と同様に、すべての標準文字列操作関数をサポートします。まさに私が必要としていたもの。byte[]これは、元の例のように、FoxPro によって実装され、パラメーターを持つ C# に渡される COM ソース イベント インターフェイスにも当てはまります。

CreateBinary()FoxPro が文字列を COM オブジェクトに送信するときにコードページ変換を行わないようにするには、文字列をバイナリとしてマークし、変換をバイパスする関数を使用して文字列を作成する必要があります。

ただし、FoxPro が処理しないのは、次のように定義されたメソッドに「バイナリ」文字列を渡すことです。

public void SendData(byte[] data) { ... }

これを呼び出そうとすると、無効なパラメータ タイプの COM 例外が発生します。

これが適切に機能しない理由はいくつかあります。これは基本的に、FoxPro がマーシャリングを自動的に処理しないことに起因します。

回避策

では、何ができるでしょうか?このような関数を定義します。

public void SendData(object data) { ... }

これで、バイナリ マーク付きの文字列を使用して関数を呼び出すことができます。FoxPro はコードページの変換を行わず、データは .NET に渡されます。しかし、dataパラメーターのデータ型は何ですか? ですSystem.Byte[*]。そのアスタリスクは何のためですか?私は手がかりがなかったので、優秀な人たちに SO で尋ねました

ゼロ以外の下限を持つ配列であることがわかります。

byte[]したがって、FoxPro からバイナリ データを取得する場合、FoxPro 配列が 1 ベースであるという事実を除いて、直接キャストすることができます。

この問題を解決するために、C# で次のことを行います。

public void SendData(object data) {
    byte[] buf = FPHelper.ToSZArray(data);
    // Use buf here
}

public class FPHelper {
    public static byte[] ToSZArray(object param) {
        var array = param as Array;

        if (array == null)
            throw new ArgumentException("Expected a binary array, (did you use CREATEBINARY()?)");
        if (array.Rank != 1)
            throw new ArgumentException("Expected array with rank 1.", "param");

        var dest = new byte[array.Length];
        Buffer.BlockCopy(array, 0, dest, 0, array.Length);

        return dest;
    }
}

FoxPro では、唯一の要件は、バイナリとして「マーク」された文字列で呼び出すことです。

cData = "Hello World!" + CHR(13) + CHR(12) + CHR(0)
oComObject.SendData(CREATEBINARY(cData))
于 2013-11-20T18:48:02.547 に答える
2

FoxPro での私の経験は非常にさびしいものですが、配列を COM オブジェクトに渡すことができることを覚えていますが、それらを受け取る際に問題がありました。したがって、逆の方法で行うことを検討し、Foxpro に、C#で作成されたを埋めるための配列を提供してもらいますCOMARRAY。C# から、DataReceivedイベントを発生させ、コールバック インターフェイスを提供しますIProvideData。FoxPro は、イベント ハンドラー内からそれを呼び出し、入力DataReceivedする配列を提供します。

public interface IStreamEvents {
    void DataReceived(int count, IProvideData obj);
    void Error();
}

public interface IProvideArray {
    void ProvideData([In, Out] 
        MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_UI1) byte[] buff);
}

FoxPro 側で配列を作成するときは、次の点に注意してください ( MSDNから)。

バイト配列 (VT_UI1) を使用して COM サーバーと通信する場合、Visual FoxPro はバイト配列を文字列に変換します。1000 の加算 nValue は、配列の元の適切な型を保持し、結果を文字列に変換しません。クライアントが Visual FoxPro COM サーバーへの参照によってバイト配列を渡す場合、Visual FoxPro COM サーバーは nValue 加算値を 1000 に設定する必要もあります。

C# 側では、配列を簡単に処理します。

public ProvideData(byte[] buff) {
    for (int c = 0; c < 255; c++)
        buff[c] = (byte)c;

}

public void Fire() {
    if(DataReceived != null)
        DataReceived(this); // this implements `IProvideArray`
}
于 2013-11-14T02:21:00.613 に答える
0
StringBuilder stringB = new StringBuilder();
foreach (char c in asciiStr)
{
    uint ii = (uint)c;
    stringB .AppendFormat("{0:X2}", (ii & 0xff));
}
return stringB.ToString();

お役に立てれば

于 2013-11-13T16:06:34.560 に答える