简体   繁体   English

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

[英]Passing binary data in a string as a COM event parameter

I am trying to get binary data from .NET into FoxPro (a COM compliant language). 我试图从.NET获取二进制数据到FoxPro(COM兼容语言)。 I have a .NET object that is ComVisible and an event interface with an event with a string parameter. 我有一个ComVisible的.NET对象和一个带有字符串参数的事件的事件接口。

In my example below I have a dummy implementation that returns a string containing every consecutive character from 0 to 255. On the .NET side the string appropriately stores every character unmolested but when handling the event on the client side, the characters between 128 and 154 get converted to question marks. 在下面的示例中,我有一个虚拟实现,它返回一个包含0到255之间每个连续字符的字符串。在.NET端,字符串适当地存储每个未被调用的字符,但在客户端处理事件时,字符在128和154之间转换为问号。 Above 154 the characters are again unchanged. 154以上的字符再次不变。

Any idea what is causing this issue? 知道是什么导致了这个问题吗? FoxPro doesn't have a way to natively represent binary data unfortunately and requires currying it around in strings. 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();
}

The Problem 问题

So the reason for the problem problem is a little complex and is rooted in the fact that FoxPro does not support Unicode strings and COM is defined explicitly to ONLY use Unicode for string data. 因此,问题的原因有点复杂,并且根植于FoxPro不支持Unicode字符串并且COM明确定义为使用Unicode作为字符串数据的事实。 Passing strings with opaque binary data would work fine except for this impedance mismatch. 使用不透明的二进制数据传递字符串可以正常工作,除了这种阻抗不匹配。

Any time FoxPro calls a function that returns or has string arguments it internally does a codepage conversion before returning to user code. 每当FoxPro调用一个返回或具有字符串参数的函数时,它会在返回用户代码之前在内部执行代码页转换。 This conversion obviously causes all kinds of issues moving binary data hidden in strings. 这种转换显然会导致各种问题移动隐藏在字符串中的二进制数据。

The Solution (with a snag) 解决方案(有障碍)

Well a byte[] should work, and it partly does work, this "partly" is what was caused me to try to hide binary data in strings. 好一个byte[] 应该工作,这部分没有工作,这个“部分”是什么使我试图隐藏在字符串中的二进制数据。

Here's the deal (and I've only verified this with VFP 9 SP2 because that's what I'm using); 这是交易(我只用VFP 9 SP2验证了这一点,因为这就是我正在使用的); on the C# COM side, FoxPro CAN handle a method defined as follows: 在C#COM端,FoxPro CAN处理定义如下的方法:

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

When calling that method FoxPro will properly return the data as a string "marked" as binary (see CreateBinary() for an explanation of "marked binary strings"). 当调用该方法时,FoxPro将正确地将数据作为字符串“标记”为二进制返回(有关“标记二进制字符串”的解释,请参阅CreateBinary() )。 These strings support all the standard string manipulation functions just like a non-binary string; 这些字符串支持所有标准字符串操作函数,就像非二进制字符串一样; exactly what I needed. 正是我需要的。 This is also true for COM source event interfaces implemented by FoxPro and passed to C# that have a byte[] parameter, like in my original example. 对于由FoxPro实现并传递给具有byte[]参数的C#的COM源事件接口也是如此,就像在我的原始示例中一样。

To keep FoxPro from doing a codepage conversion on a string when sending it to a COM object, that string needs to be created with the CreateBinary() function which marks the string as binary and will bypass the conversion. 为了防止FoxPro在将字符串发送到COM对象时对字符串进行代码页转换,需要使用CreateBinary()函数创建该字符串,该函数将字符串标记为二进制并绕过转换。

However, what FoxPro does NOT handle is passing a "binary" string to a method defined like this: 然而,什么样的FoxPro 处理是传递一个“二进制”串像这样定义的方法:

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

If you try to call this you will get an invalid parameter type COM exception. 如果您尝试调用此方法,您将获得无效的参数类型COM异常。

There are a couple of reasons this doesn't work properly which basically boils down to FoxPro not handling the marshaling automatically. 有几个原因,这不能正常工作,这基本上归结为FoxPro不自动处理编组。

The Workaround Solution 变通方案解决方案

So, what can we do? 所以,我们能做些什么? Define a function like this. 定义这样的函数。

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

Ok, so now we can call the function, with a binary marked string and FoxPro will not do any codepage conversion and the data will come over to .NET. 好的,现在我们可以调用该函数,使用二进制标记字符串,FoxPro不会进行任何代码页转换,数据将转到.NET。 But what is the data type of the data parameter? 但是data参数的数据类型是什么? It is a System.Byte[*] . 它是一个System.Byte[*] What is that asterisk for? 那个星号是什么? I had no clue so I asked the brilliant people on SO . 我不知道所以我问了那些才华横溢的人

Turns out that it is an array with non-zero lower bound. 原来它是一个非零下限的数组。

So when we binary data from FoxPro we would be able to cast directly to byte[] except for the fact that FoxPro arrays are 1-based. 因此,当我们从FoxPro二进制数据时,我们可以直接转换为byte[]除了FoxPro数组是基于1的事实。

So to fix this issue, here is what I do in C#: 所以要解决这个问题,我在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;
    }
}

And in FoxPro the only requirement is to call it with a string "marked" as binary: 在FoxPro中,唯一的要求是使用“标记”为二进制的字符串来调用它:

cData = "Hello World!" + CHR(13) + CHR(12) + CHR(0)
oComObject.SendData(CREATEBINARY(cData))

While my experience with FoxPro is very rusty, I do remember it can pass arrays into COM objects, but has issues with receiving them back. 虽然我对FoxPro的体验非常生疏,但我确实记得它可以将数组传递给COM对象,但是接收它们有问题。 So, consider doing it the other way round and have Foxpro provide an array for C# to fill in , created with COMARRAY . 因此,请考虑以相反的方式进行,并让Foxpro为C#提供一个数组来填充 ,使用COMARRAY创建。 From C#, you'd fire DataReceived event and provide a callback interface IProvideData . 从C#开始,您将触发DataReceived事件并提供回调接口IProvideData FoxPro would call it from inside its DataReceived event handler and supply you with an array to fill in: 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);
}

When you create an array on the FoxPro side, keep the following in mind (from MSDN ): 在FoxPro端创建数组时,请记住以下内容(来自MSDN ):

When you use a byte array (VT_UI1) to communicate with a COM server, Visual FoxPro converts the byte array into a string. 当您使用字节数组(VT_UI1)与COM服务器通信时,Visual FoxPro将字节数组转换为字符串。 The additive nValue of 1000 retains the original proper type of the array and does not convert the result to a string. 添加剂nValue为1000保留了原始正确类型的数组,并且不会将结果转换为字符串。 If a client passes a byte array by reference to a Visual FoxPro COM Server, the Visual FoxPro COM Server must also set the nValue additive to 1000. 如果客户端通过引用传递一个字节数组Visual FoxPro COM服务器,Visual FoxPro COM服务器还必须将nValue添加剂设置为1000。

On the C# side, you simple deal with an array: 在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`
}
StringBuilder stringB = new StringBuilder();
foreach (char c in asciiStr)
{
    uint ii = (uint)c;
    stringB .AppendFormat("{0:X2}", (ii & 0xff));
}
return stringB.ToString();

hope this helps 希望这可以帮助

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM