簡體   English   中英

PInvoke'class'Versus'ref struct'

[英]PInvoke 'class' Versus 'ref struct'

當我用Google搜索時,我看到帖子說傳遞C# class與使用PInvoke時將ref struct傳遞給C API相同(這里是一個帖子C#PInvoke struct vs class access violation )。

但是,在運行示例時,我看到的行為與預期不同。 其中ref struct充當真正的指針,而'class'則不是

C代碼:

//PInvokeProvider.h
#include "stdafx.h" 
typedef struct Animal_s
{
    char Name[10000];
} Animal;

extern "C" void __declspec(dllexport) ChangeName(Animal* pAnimal);


//PInvokeProvider.cpp    
#include "stdafx.h"
#include <stdio.h>
#include "PInvokeProvider.h"

extern "C" {
    void ChangeName(Animal* pAnimal)
    {
        printf("Entered C++\n");
        printf("Recieved animal : %s\n", pAnimal->Name);
        printf("This function will change the first letter of animal to 'A'\n");
        pAnimal->Name[0] = 'A';
        printf("Animal changed to : %s\n", pAnimal->Name);
        printf("Leaving C++\n");
    }
}

現在使用struct for“Animal”進入C#:

namespace PInvokeConsumer
{
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public struct Animal
    {
        /// char[10000]
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 10000)]
        public string Name;

        public Animal(string name)
        {
            Name = name;
        }
    }

    public partial class NativeMethods
    {
        [DllImportAttribute("PInvokeProvider.dll", 
                            EntryPoint = "ChangeName", 
                            CallingConvention = CallingConvention.Cdecl)]
        public static extern void ChangeName(ref Animal pAnimal);
    }

    internal class Program
    {
        public static void Main(string[] args)
        {
            Animal animal = new Animal("Lion");

            Console.WriteLine("Animal : {0}", animal.Name);

            Console.WriteLine("Leaving C#");
            NativeMethods.ChangeName(ref animal);
            Console.WriteLine("Back to C#");

            Console.WriteLine("Animal : {0}", animal.Name);
            Console.ReadKey();
        }
    }
}

使用ref struct的輸出正如預期的那樣在C#域中將第一個字母改為'A':

Animal : Lion
Leaving C#
Entered C++
Recieved animal : Lion
This function will change the first letter of animal to 'A'
Animal changed to : Aion
Leaving C++
Back to C#
Animal : Aion


但是當我嘗試使用class而不是ref struct

namespace PInvokeConsumer
{
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public class Animal
    {
        /// char[10000]
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 10000)]
        public string Name;

        public Animal(string name)
        {
            Name = name;
        }
    }


    public partial class NativeMethods
    {
        [DllImportAttribute("PInvokeProvider.dll", 
                            EntryPoint = "ChangeName", 
                            CallingConvention = CallingConvention.Cdecl)]
        public static extern void ChangeName(Animal pAnimal);
    }

    public static void Main(string[] args)
    {
        Animal animal = new Animal("Lion");

        Console.WriteLine("Animal : {0}", animal.Name);

        Console.WriteLine("Leaving C#");
        NativeMethods.ChangeName(animal);
        Console.WriteLine("Back to C#");

        Console.WriteLine("Animal : {0}", animal.Name);
        Console.ReadKey();
    }
}

輸出是:

Animal : Lion
Leaving C#
Entered C++
Recieved animal : Lion
This function will change the first letter of animal to 'A'
Animal changed to : Aion
Leaving C++
Back to C#
Animal : Lion

因此,當我使用類時,分配給Animal的內存不會被修改。 問題是為什么不呢?

  [StructLayout(LayoutKind.Sequential, ...)]

這才是最重要的。 必須將該屬性應用於類聲明。 您還將它應用於結構聲明但實際上並不是必需的,C#編譯器會自動為結構發出此聲明。 模數CharSet屬性。

類的默認[StructLayout]屬性不是 LayoutKind.Sequential,它是LayoutKind.Auto。 這是CLR利用的東西,它將重新組織類中的字段以提供最佳布局。 您可以在這篇文章中閱讀更多相關信息

最大的區別在於它使一個類不閃爍。 這是一百美元的單詞意味着pinvoke marshaller不能只是將普通指針傳遞給托管對象,它必須托管對象轉換為具有所請求布局的非托管對象。 它通過分配內存和復制字段來實現。 結果是,本機代碼對副本所做的任何更改都不會傳播回原始托管對象。

除非你明確告訴pinvoke marshaller這樣做。 固定:

    [DllImportAttribute(...)]
    public static extern void ChangeName([In, Out]Animal pAnimal);

[OutAttribute]告訴它傳回更改。

暫無
暫無

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

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