从 C# 调用 C++ 线程

Posted

技术标签:

【中文标题】从 C# 调用 C++ 线程【英文标题】:Calling C++ thread from C# 【发布时间】:2014-11-05 13:37:25 【问题描述】:

我对 C++ 比较陌生。出于某种原因,我需要做一个流动模型的工作。

第 1 步:我在 C++ 中有一个 Method1,它将更改从 C# 传递的变量的值。我称该变量为str

第 2 步:创建一个线程并在几毫秒后将 str 更改为另一个值。

在 C++ 中:

char* temp; // Temporary pointer

void Thread1(void* arr)
 
    Sleep(1000); // Wait for one second

    strcpy_s(temp, 100, "Thread 1"); // Change value of str to ‘Thread 1’ -> A exception was threw because ‘temp’ is undefined


__declspec(dllexport) void Method1(char* str)

    temp = str; // Keep pointer of ‘str’ to temporary pointer to change ‘str’ value in Thread

    strcpy_s(temp, 100, "Method 1"); // Change ‘str’ to ‘Method 1’. -> It work OK

    _beginthread(Thread1, 0, NULL); // Start Thread1

在 C# 中:

    public static StringBuilder str = new StringBuilder(100);

    [DllImport("C++.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern void Method1(StringBuilder test);

    static void Main(string[] args)
    
        Method1(str); // Call Method1 in C++ dll.

        Console.WriteLine(str.ToString());  // Result: “Method 1” -> OK

        while (true)
        
            Console.WriteLine(str.ToString()); // Print str value every 0.1 second. It exception after 1 second
            Thread.Sleep(100);
        
    

调用Method1时的结果,str变为Method1,但Thread1运行时:指针temp为空,所以抛出异常。 请提供一些关于如何在Thread1 中更改str 的见解。

非常感谢。

【问题讨论】:

【参考方案1】:

您不能为此使用StringBuilder。这是因为封送处理假定对象仅在函数执行期间使用(即假定函数返回后,本机代码将不再使用它)。 C++ 不知道 StringBuilder 是什么,因此运行时仅在 P/Invoke 调用期间通过缓冲区提供对它的访问。

您应该分配一些内存并将其传递给您的函数。这是一种应该有效的方法:

[DllImport("C++.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void Method1(byte* test);

然后:

unsafe

    var buffer = new byte[100]; // a C++ char is 1 byte
    fixed (byte* ptr = buffer)
    
        Method1(ptr);

        while (true)
        
            // WARNING: There's no locking here (see comment below)
            //          It may cause undefined behavior.
            var str = Encoding.ASCII.GetString(buffer);

            Console.WriteLine(str);
            Thread.Sleep(100);
        
    

缓冲区必须在 C++ 代码可以访问它的整个过程中保持固定 (fixed)。

请注意,这仍然不安全:

没有锁,因此 C# 代码读取字符串的同时可能与 C++ 代码写入字符串的时间相同。这可能会导致未定义的行为。 您的缓冲区必须至少为 100 字节(因为提供给 strcpy_s 的值)。这是隐含的。最好将缓冲区大小传递给您的函数。

【讨论】:

【参考方案2】:

您不应将字符串复制到未分配的指针。实际上,当您使用指针时,这只是一个地址(4 个字节),因此您没有足够的空间来复制您的字符串。

将 char *temp 更改为 char temp[100]。

这样你就要求内存给你 100 字节来复制数据。

这应该可以工作

【讨论】:

【参考方案3】:

变量 temp 只是一个指针,你的赋值 temp = str;仅将指针分配给字符串指针。要点是您永远不会为 temp 分配内存。 考虑到这一点,在调用线程后 str 参数超出范围并被取消分配,因此您的临时指针现在无效。

【讨论】:

【参考方案4】:

感谢所有回答。 最后,我基于 Lucas Trzesniewski 的 解决方案和我的代码工作。 我将 C# 代码更改为:

    [DllImport("C++.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern void Method1(byte[] str);

    static void Main(string[] args)
    
        var buffer = new byte[100]; // a C++ char is 1 byte
        Method1(buffer);

        while (true)
        
            var str = Encoding.ASCII.GetString(buffer);

            Console.WriteLine(str);
            Thread.Sleep(100);
        
    

【讨论】:

以上是关于从 C# 调用 C++ 线程的主要内容,如果未能解决你的问题,请参考以下文章

从 C# 线程调用非托管代码

C# 到 C++ 多线程,有啥问题吗?

如何使用 p/invoke 终止从 C# 调用的 C++ 函数

当我从 C# 代码调用 C++ 代码时,它是线程安全的吗?

在 C# 应用程序退出时清理非托管 C++ 线程

从 C# 调用 C++ dll 函数时出现问题