Reputation: 233
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
Reputation: 233
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
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:
strcpy_s
). This is implicit. It would be better to pass the buffer size to your function.Upvotes: 2
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
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