3

我正在尝试将二进制数据从 .NET 获取到 FoxPro(一种兼容 COM 的语言)。我有一个 ComVisible 的 .NET 对象和一个带有字符串参数的事件的事件接口。

在下面的示例中,我有一个虚拟实现,它返回一个包含从 0 到 255 的每个连续字符的字符串。在 .NET 端,该字符串适当地存储了每个未被干扰的字符,但是在客户端处理事件时,128 到 154 之间的字符转换为问号。超过 154 个字符再次保持不变。

知道是什么导致了这个问题吗?不幸的是,FoxPro 没有办法在本地表示二进制数据,并且需要在字符串中对其进行curry。

[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 调用返回或具有字符串参数的函数时,它都会在返回到用户代码之前在内部进行代码页转换。这种转换显然会导致移动隐藏在字符串中的二进制数据的各种问题。

解决方案(有障碍)

那么 abyte[] 应该可以工作,并且它部分工作,这个“部分”是导致我试图在字符串中隐藏二进制数据的原因。

这是交易(我只用 VFP 9 SP2 验证了这一点,因为这就是我正在使用的);在 C# COM 端,FoxPro CAN处理定义如下的方法:

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

调用该方法时,FoxPro 将正确地将数据作为“标记”为二进制的字符串返回(有关“标记的二进制字符串”的解释,请参见CreateBinary() )。这些字符串支持所有标准字符串操作函数,就像非二进制字符串一样;正是我需要的。这也适用于由 FoxPro 实现并传递给具有byte[]参数的 C# 的 COM 源事件接口,就像在我的原始示例中一样。

为了防止 FoxPro 在将字符串发送到 COM 对象时对其进行代码页转换,需要使用将CreateBinary()字符串标记为二进制并绕过转换的函数来创建该字符串。

但是,FoxPro 不处理的是将“二进制”字符串传递给这样定义的方法:

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

如果您尝试调用它,您将得到一个无效的参数类型 COM 异常。

这有几个原因不能正常工作,基本上归结为 FoxPro 没有自动处理编组。

变通方案

所以,我们能做些什么?像这样定义一个函数。

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

好的,现在我们可以调用该函数,使用二进制标记的字符串,FoxPro 不会进行任何代码页转换,数据将转到 .NET。但是data参数的数据类型是什么?它是一个System.Byte[*]. 那个星号是干什么用的?我一无所知,所以我问了 SO 上的聪明人

原来它是一个具有非零下限的数组。

因此,当我们从 FoxPro 二进制数据时,我们可以直接转换byte[]为,但 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 回答