Marshaling structure to single line of string

可紊 提交于 2020-01-05 12:34:54

问题


[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public class Comarea
{
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 1)]
    public string status;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 5)]
    public string operationName;
}

public static void StringToObject(string buffer, out Comarea comarea)
{
    IntPtr pBuf = Marshal.StringToBSTR(buffer);
    comarea = (Comarea)Marshal.PtrToStructure(pBuf, typeof(Comarea));
}

I can create object from single line of string but I can not do opposite of that.

How can I do that operation?

public static void ObjectToString(out string buffer, Comarea comarea)
{
     ???
}

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

int size = Marshal.SizeOf(comarea);
IntPtr pBuf = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(comarea, pBuf, false);
buffer = Marshal.PtrToStringBSTR(pBuf); //Error

回答1:


[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public class Comarea
{
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 1)]
    public string status;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 5)]
    public string operationName;
}

That is laid out as 6 adjacent 16 bit wchar_t character elements. So, right off the bat,

public static void StringToObject(string buffer, out Comarea comarea)
{
    IntPtr pBuf = Marshal.StringToBSTR(buffer);
    comarea = (Comarea)Marshal.PtrToStructure(pBuf, typeof(Comarea));
}

is wrong. Beyond the fact that you leak a BSTR, your struct is not a BSTR.

You can implement it like this:

public static void StringToObject(string buffer, out Comarea comarea)
{
    comarea.status = buffer.Substring(0, 1);
    comarea.operationName = buffer.Substring(1, 5);
}

This is under the assumption that the six characters are the contained the the locations implied by the Substring calls.

In the opposite direction you write:

public static void ObjectToString(out string buffer, Comarea comarea)
{
     buffer = comarea.status + comarea.operationName;
}

Note that the struct definition must be wrong however

[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 1)]
public string status;

The marshaller always adds a null-terminator when using ByValTStr. So with SizeConst of 1, status will always be marshaled as an empty string. Without actually seeing the unmanaged struct definition, I would not care to tell you how to fix this problem.




回答2:


That is how I solved my problem: I used char array and Marshal.PtrToStringAuto(pBuf, size)

public static void ObjectToString(out string buffer, Comarea comarea)
{
    int size = 0;
    IntPtr pBuf = IntPtr.Zero;

    try
    {
        size = Marshal.SizeOf(comarea);
        pBuf = Marshal.AllocHGlobal(size);
        Marshal.StructureToPtr(comarea, pBuf, false);
        buffer = Marshal.PtrToStringAuto(pBuf, size).Substring(0, size/2); // Answer
    }
    catch
    {
        throw;
    }
    finally
    {
        Marshal.FreeHGlobal(pBuf);
    }
}





[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct Comarea
{
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
    private char[] status;

    public string Status
    {
        get
        {
            return new string(status);
        }

        set
        {
            status = value.ToFixedCharArray(1);
        }
    }

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)]
    private char[] operationName;

    public string OperationName
    {
        get
        {
            return new string(operationName);
        }

        set
        {
            operationName = value.ToFixedCharArray(5);
        }
    }
}



public static class FormatterExtensions
{
    [DebuggerStepThrough]
    public static char[] ToFixedCharArray(this string inputString, int arrayLength)
    {
        char[] outputArray = new char[arrayLength];
        char[] inputArray = inputString.ToSafeTrim().ToCharArray();

        if (inputArray.Length == arrayLength)
        {
            return inputArray;
        }
        else
        {
            int i = 0;

            while (i < arrayLength)
            {
                if (i < inputArray.Length)
                {
                    outputArray[i] = inputArray[i];
                }
                else
                {
                    break;
                }

                i++;
            }

            return outputArray;
        }
    }
}

It is so useful for IBM CICS communication(For Commarea)



来源:https://stackoverflow.com/questions/24697789/marshaling-structure-to-single-line-of-string

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!