将字符串中的二进制数据作为COM事件参数传递

时间:2013-11-13 15:52:36

标签: c# com visual-foxpro comvisible

我试图将.NET中的二进制数据转换为FoxPro(COM兼容语言)。我有一个ComVisible的.NET对象和一个带有字符串参数的事件的事件接口。

在下面的示例中,我有一个虚拟实现,它返回一个包含0到255之间每个连续字符的字符串。在.NET端,字符串适当地存储每个未被调用的字符,但在客户端处理事件时,字符之间128和154转换为问号。 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();
}

3 个答案:

答案 0 :(得分:4)

问题

因此问题的原因有点复杂,其根源在于FoxPro不支持Unicode字符串,而COM明确定义为 ONLY 使用Unicode作为字符串数据。使用不透明的二进制数据传递字符串可以正常工作,除了这种阻抗不匹配。

只要FoxPro调用一个返回或具有字符串参数的函数,它就会在返回用户代码之前在内部执行代码页转换。这种转换显然会导致各种问题移动隐藏在字符串中的二进制数据。

解决方案(有障碍)

一个byte[] 应该工作,部分 工作,这个“部分”是因为我试图隐藏字符串中的二进制数据

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

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

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

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

然而,FoxPro执行 NOT 处理的是将“二进制”字符串传递给定义如下的方法:

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

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

有几个原因导致无法正常工作,这基本上归结为FoxPro无法自动处理编组。

变通方法解决方案

那么,我们能做什么?定义这样的函数。

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

好的,现在我们可以使用二进制标记字符串调用该函数,FoxPro不会进行任何代码页转换,数据将转到.NET。但是data参数的数据类型是什么?这是System.Byte[*]。那个星号是什么?我不知道所以我asked the brilliant people on 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))

答案 1 :(得分: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将字节数组转换为字符串。添加剂   nValue为1000保留了原始正确类型的数组并且确实存在   不将结果转换为字符串。如果客户端传递一个字节数组   引用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`
}

答案 2 :(得分:0)

StringBuilder stringB = new StringBuilder();
foreach (char c in asciiStr)
{
    uint ii = (uint)c;
    stringB .AppendFormat("{0:X2}", (ii & 0xff));
}
return stringB.ToString();

希望这会有所帮助