簡體   English   中英

將帶有字符串數組的C#結構傳遞給C ++函數,該函數接受void *(對於c#結構)和char **(對於c#字符串數組)

[英]Passing C# structure with string array to c++ function which accepts void * for c# structure and char** for c# string array

我想將具有字符串數組C#結構發送到C ++函數 ,該函數接受void *作為c#結構,並接受char **作為c#結構字符串數組成員。

我能夠將結構發送到c ++函數,但是問題是, 無法從c ++函數訪問c#結構的字符串數組數據成員 當單獨發送字符串數組時,我能夠訪問數組元素。

示例代碼是-

C# Code:

[StructLayout(LayoutKind.Sequential)]
public struct TestInfo
{
    public int TestId;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
    public String[] Parameters;
}

[DllImport("TestAPI.dll", CallingConvention = CallingConvention.StdCall, EntryPoint    "TestAPI")]
private static extern void TestAPI(ref TestInfo data);

static unsafe void Main(string[] args)
{
TestInfo  testinfoObj = new TestInfo();
testinfoObj.TestId = 1;
List<string> names = new List<string>();
names.Add("first");
names.Add("second");
names.Add("third");
testinfoObj.Parameters=names.ToArray();
TestAPI(ref testinfoObj);
}



VC++ Code:

/*Structure with details for TestInfo*/
typedef struct TestInfo
{
int  TestId;
char **Parameters;
}TestInfo_t;

//c++ function
__declspec(dllexport) int TestAPI(void *data)
{
TestInfo *cmd_data_ptr= NULL;
cmd_data_ptr = (TestInfo) data;
printf("ID is %d \r\n",cmd_data_ptr->TestId);//Working fine

for(i = 0; i < 3; i++)
printf("value: %s \r\n",((char *)cmd_data_ptr->Parameters)[i]);/*Error-Additional     information: Attempted to read or write protected memory. This is often an indication that other memory is corrupt*/
}

分析內存堆棧時,觀察到,當我打印((char *)cmd_data_ptr-> Parameters)時,將打印第一個數組元素(“ first”),但使用((char *)cmd_data_ptr-> Parameters) [i]無法訪問元素,並且上述異常即將到來。

結構內存地址包含所有結構元素的地址,但是從c ++訪問數據時,它僅訪問字符串數組的第一個元素。

[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public String[] Parameters;

是一個內聯數組。 匹配的C ++聲明是:

char* Parameters[2];

但是您正在嘗試將其匹配到:

char** Parameters;

這是完全不同的。

您將需要手動封送。 在C#結構中,將Parameters聲明為IntPtr 然后使用Marshal.AllocHGlobal分配本機內存以保存指針數組。 然后使用指向您的字符串的指針填充這些指針。

[StructLayout(LayoutKind.Sequential)]
public struct TestInfo
{
    public int TestId;
    public IntPtr Parameters;
}

static void Main(string[] args) // no need for unsafe
{
    TestInfo testInfo;
    testInfo.TestId = 1;
    testInfo.Parameters = Marshal.AllocHGlobal(2*Marshal.SizeOf(typeof(IntPtr)));
    IntPtr ptr = testInfo.Parameters;
    Marshal.WriteIntPtr(ptr, Marshal.StringToHGlobalAnsi("foo"));
    ptr += Marshal.SizeOf(typeof(IntPtr));
    Marshal.WriteIntPtr(ptr, Marshal.StringToHGlobalAnsi("bar"));
    TestAPI(ref testinfoObj);
    // now you want to call FreeHGlobal, I'll leave that code to you
}

一種替代方法是使用固定的IntPtr []並將其放在testInfo.Parameters中。

這實際上是對David答案的擴展/擴展,但這是包裝自定義編組的一種方法:

public struct LocalTestInfo
{
    public int TestId;
    public IEnumerable<string> Parameters;

    public static explicit operator TestInfo(LocalTestInfo info)
    {
        var marshalled = new TestInfo
            {
                TestId = info.TestId, 
            };
        var paramsArray = info.Parameters
            .Select(Marshal.StringToHGlobalAnsi)
            .ToArray();
        marshalled.pinnedHandle = GCHandle.Alloc(
            paramsArray, 
            GCHandleType.Pinned);
        marshalled.Parameters = 
            marshalled.pinnedHandle.AddrOfPinnedObject();
        return marshalled;
    }
}

[StructLayout(LayoutKind.Sequential)]
public struct TestInfo : IDisposable
{
    public int TestId;
    public IntPtr Parameters;

    [NonSerialized]
    public GCHandle pinnedHandle;

    public void Dispose()
    {
        if (pinnedHandle.IsAllocated)
        {
            Console.WriteLine("Freeing pinned handle");
            var paramsArray = (IntPtr[])this.pinnedHandle.Target;
            foreach (IntPtr ptr in paramsArray)
            {
                Console.WriteLine("Freeing @ " + ptr);
                Marshal.FreeHGlobal(ptr);
            }
            pinnedHandle.Free();
        }
    }
}

請注意,我將測試交換到CDecl:

[DllImport(@"Test.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int TestAPI(ref TestInfo info);

我也認為您在C ++方面有錯別字:

extern "C" 
__declspec(dllexport) int TestAPI(void *data)
{
    TestInfo *cmd_data_ptr= NULL;
    cmd_data_ptr = (TestInfo*) data;
    printf("ID is %d \r\n",cmd_data_ptr->TestId);

    // char**, not char*
    char** paramsArray = ((char **)cmd_data_ptr->Parameters);
    for(int i = 0; i < 3; i++)
    {
        printf("value: %s \r\n",paramsArray[i]);
    }
    return 0;
}

和測試台:

static void Main(string[] args)
{
    var localInfo = new LocalTestInfo()
    {
        TestId = 1,
        Parameters = new[]
        {
            "Foo", 
            "Bar",
            "Baz"
        }
    };
    TestInfo forMarshalling;
    using (forMarshalling = (TestInfo)localInfo)
    {
        TestAPI(ref forMarshalling);                
    }
}

反向編組運算符作為練習留給讀者,但基本上應該看起來像顯式TestInfo運算符的逆。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM