Calling C++ thread from C#

I’m relatively new to C++. For some reason, I need to do a job as a flowing model.

Step 1: I have a Method1 in C++ which will change value of a variable that is passed from C#. I call that variable str.

Step 2: Create a thread and change str to another value after several milliseconds have passed.

In 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
}

In 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);
        }
    }

The result when Method1 is called, the str is changed to Method1 but when Thread1 runs: the pointer temp was null, so an exception was thrown. Please provide some insight as to how I can change str in the Thread1.

Thanks a lot.

Upvotes: 1

Views: 1626

Answers (4)

Thanks for all answers. Finally, i based Lucas Trzesniewski's solution and my code work. I changed C# code to:

    [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);
        }
    }

Upvotes: 0

Lucas Trzesniewski
Lucas Trzesniewski

Reputation: 51330

You can't use a StringBuilder for this. This is because marshaling assumes the object will be used only during the function execution (ie it assumes that after the function returns the native code will no longer use it). C++ doesn't know what a StringBuilder is, so the runtime does only provide access to it through a buffer during the P/Invoke call.

You should allocate some memory and pass it to your function. Here's a way which should work:

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

And then:

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);
        }
    }
}

The buffer must stay pinned (fixed) during the whole time the C++ code may access it.

Note that this is still unsafe:

  • There are no locks, so the C# code may be reading the string at the same time than the C++ code is writing to it. This can cause undefined behavior.
  • Your buffer must be at least 100 bytes (because of the value provided to strcpy_s). This is implicit. It would be better to pass the buffer size to your function.

Upvotes: 2

clausc
clausc

Reputation: 166

The variable temp is only a pointer and your assignment temp = str; only assigns the pointer to the string pointer. Point being that you never allocate memory for temp. With that in mind, after invoking the thread the str parameter goes out of scope and gets de-allocated whereby your temp pointer is now invalid.

Upvotes: 0

Tristan Djahel
Tristan Djahel

Reputation: 1212

You should not copy a string to a pointer that is not allocated. In fact, when you use a pointer this is just an address (4 bytes) so you don't have enough space to copy your string.

Change char *temp to char temp[100].

With this you ask the memory to give you 100 byte to copy the data.

This should work

Upvotes: 0

Related Questions