I am trying to use a C++ dll to edit my StringBuilder object in C#. My C++ code looks like this:
extern "C" __declspec(dllexport) void __stdcall PrintHead(char* myString)
{
myString = "testIsOkey";
}
and my C# code is:
[DllImport("StringEdit.dll", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = false)]
public static extern void PrintHead([MarshalAs(UnmanagedType.LPStr)] StringBuilder stringBuilder);
private void button1_Click(object sender, EventArgs e)
{
StringBuilder stringBuilder = new StringBuilder("123456");
PrintHead(stringBuilder);
}
After PrintHead is called, i am expecting to see that the stringBuilder object's value is changed from "123456" to "testIsOkey" , but it does not change. I can't figure out where do i make a mistake.
Thanks for your help.
void __stdcall PrintHead(char* myString) {
myString = "testIsOkey";
}
That's not correct C++ code. It merely changes the pointer that was passed to the function. This has no side effects whatsoever. Fix:
void __stdcall PrintHead(char* myString) {
strcpy(myString, "testIsOkey");
}
But never write interop code like this, the C++ function can easily destroy the garbage collected heap this way. Which is exactly what happens in your code, the StringBuilder's Capacity isn't enough. You should add an extra argument that provides the size of the passed buffer. Fix:
void __stdcall PrintHead(char* myString, size_t bufferSize) {
strcpy_s(myString, bufferSize, "testIsOkey");
}
Pass the string builder's Capacity for that extra argument, like this:
var buffer = new StringBuilder(666);
PrintHead(buffer, buffer.Capacity);
var result = buffer.ToString();
Doesn't it have to be marked ref or with some other attribute, so that .NET knows that the marshalling should occur both ways?
Related
#ifdef SERVER_TCP_EXPORTS
class __declspec(dllexport) Sock_Server
#else
class __declspec(dllimport) Sock_Server
#endif
{
public:
int Server(const char* strErr,int bufSize);
...
}
cpp file
int Sock_Server::Server(const char* strErr,int bufSize)
{
// do something and assign the string to strErr
(say) strErr = "Hello World";
return -1;
}
in C#
[DllImport("Hello.dll", EntryPoint = "Server" CallingConvention = CallingConvention.StdCall)]
private extern static int Server(StringBuilder strErr, int bufSize);
public static int Connect(StringBuilder strErr, int bufSize)
{
int res = Server(strErr,bufSize); /// when the calls come here, strErr is empty
return res; // res has the value -1
}
private void Form1_Load(object sender, EventArgs e)
{
int res = 0;
int bufSize = 4096;
StringBuilder strErr = new StringBuilder(bufSize+1);
res = Connect(strErr, bufSize); //when the calls come here, strErr is empty
MessageBox.Show(strErr.ToString()); // it has the value -1
}
I am not a C# guy.I did some reading before posting this out,and tried all the possible combinations but some reason it didn't work.I am using Visual Studio 2013.I have couple of Q
[Q1] When I did MessageBox.Show(strErr.ToString()); in my C#, it just prints a blank string! Would really appreciate if anyone can help me as I am pretty new to all this.
[Q2] If I give EntryPoint ="Server" my code doesn't work.It complains,enrty point of Server is not found in Hello.dll. So,every time I have to find the exact entry in my dll using dumpbin.exe and then provide exactly how the compiler has created for me
[DllImport("Hello.dll", EntryPoint = "?Server#Sock_Server##QAEHPBDH#Z" CallingConvention = CallingConvention.StdCall)]
Is there a better way of doing this.This is making the code cupled
[Q3] Is there a way to call C++ constructor/destructor.I do need to call both of them.I have called C'tor through some other method,I know that's not a good idea.Any help would be appreciated.
Thanks.
I don't think it's all related to static. It's all about proper assignment of an object. The same piece of code work perfectly fine when called from other C++. I tweaked the code little bit. Now, I used strcpy_s and it's working because while marshing the data, I was loosing the data.
I am still waiting for Q2 to be answered.
I have the following code:
Generic.cs
class Generic
{
[DllImport("Generic.dll", EntryPoint = "Consult", CallingConvention = CallingConvention.Winapi)]
public static extern String Consulta(int NumeroSecao);
}
And
main.cs
private void Button_Click(object sender, RoutedEventArgs e)
{
Random rnd = new Random();
int random = rnd.Next(9999);
Stopwatch sw = new Stopwatch();
sw.Start();
String teste = Generic.Consult(random);
sw.Stop();
TextoLog = "Tempo de Execução:"+sw.Elapsed.Milliseconds+"ms | ConsultarSAT";
tempoEntreComandos();
}
The code of DLL is:
Generic.c
__declspec(dllexport) char* __stdcall Consult(int numeroSessao)
{
memset(pcReturn,0,sizeof(char) * BUFFER_SIZE);
HANDLE fileHandle;
Communicate(&fileHandle, pcReturn,10);
return pcReturn;
}
The problem occurs when I call function Consult the second time, the message received is an Access Violation Exception. What is happening here?
The solution is the type for the return in the declaration of function, instead of use String is necessary use of IntPtr.
Native strings have to be marshaled to be used in c#. Your native code is not actually returning a string object, but a char pointer. You have to tell the marshaler what the return type is, as so:
[DllImport("Generic.dll", EntryPoint = "Consult", CallingConvention = CallingConvention.Winapi)]
[return: MarshalAs(UnmanagedType.LPStr)]
public static extern String Consulta(int NumeroSecao);
Using LPStr for ASCII strings and LPWStr for unicode.
Actually it might be a better idea to marshal the string manually as the marshaler deallocates the native string right after conversion. The declaration would be then
[DllImport("Generic.dll", EntryPoint = "Consult", CallingConvention = CallingConvention.Winapi)]
public static extern IntPtr Consulta(int NumeroSecao);
And you manually marshal the IntPtr to string like so
String str = Marshal.PtrToStringAnsi(strptr); // for ansi
String str = Marshal.PtrToStringUni(strptr); // for unicode
I have this function in c++
extern "C" __declspec(dllexport) void SendPacketToServer(BYTE *packet, int Length)
{
_SendToServer(packet, Length);
}
How can I use it in c#?
I tried this so far:
[DllImport("DAFramework 1.0.dll", SetLastError = true)]
internal static extern void SendPacketToServer(IntPtr packet, int length);
unsafe
{
fixed (byte* pByte = new byte[] { 0x13, 0x00 })
{
IntPtr data = new IntPtr((void*)pByte);
SendPacketToServer(data, 2);
}
}
Am I doing something incorrect? If so, How can I make it work? I'm getting the error: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
can I do it a simpler way?
You must set appropriate calling convention:
[DllImport("DAFramework 1.0.dll", SetLastError = true, CallingConvention=CallingConvention.Cdecl)]
because your native function declares it (extern "C" __declspec(dllexport)).
I think its a similar problem, in my case was due to char* parameter. I think you have to allocate previously the parameter.
So your only solution is to pass the string parameters as IntPtr.
Allocate the memory with Marshal.StringToHGlobalAnsi
Attempted to read or write protected memory with dllimport in c#
I wave a method in C++ that receives a parameter of the LPCOLESTR type. I'm accessing this method through C#, but I can't make the correct conversion between String and this type.
Let's say the method signinature in C++ is:
void Something(LPCOLESTR str)
In C#, I'm trying to call it (all reference issues to access the method through a DLL have been solved already):
String test = "Hello world";
Something(test);
But with no luck, of course. If anyone can help me, I'd be very glad. Thank you!
Code snippet:
As an example, here's my C++ portion of code, defined in the file MixedCodeCpp.h (CLR Class Library)
#include "windows.h"
#pragma once
using namespace System;
namespace MixedCodeCpp
{
public ref class MyClass
{
public:
HRESULT Something(LPCOLESTR str)
{
return S_OK;
}
};
}
And here's my code in C# (I've added a reference to the C++ project in the C# project, through Visual Studio):
StringBuilder sb = new StringBuilder();
sb.Append("Hello world");
MixedCodeCpp.MyClass cls = new MixedCodeCpp.MyClass();
cls.Something(sb);
The argument will appear as Char* on the C# side. That requires unsafe code, like this:
unsafe static void CallSomething(MyClass obj, string arg) {
IntPtr mem = Marshal.StringToCoTaskMemUni(arg);
try {
obj.Something((Char*)mem);
}
finally {
Marshal.FreeCoTaskMem(mem);
}
}
It makes very little sense to expose the LPCOLESTR to other managed code. This method really should accept a String^ and convert to wchar_t* internally.
Try StringBuilder instead of String thusly:
System.Text.StringBuilder test = new System.Text.StringBuilder ();
test.Append("Hello world");
Something(test);
I've used it that way in pinvoke to Win32 functions that required various string pointers as parameters. Not sure it will work with your API but it's worth a shot. Here's some MSDN info about the process. And here is another.
Here's an arbitrary sample of what your import statement and declaration ought to look like. (To be taken with a grain of salt.)
[DllImport(SomeLib.SomeName, CharSet = CharSet.Unicode, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool Something(StringBuilder pMyString);
StringBuilder str = new StringBuilder(MAX_PATH);
DWORD uSize;
bool b = Something(str);
I want to send a string from C# to a function in a native C++ DLL.
Here is my code:
The C# side:
[DllImport(#"Native3DHandler.dll", EntryPoint = "#22",
CharSet = CharSet.Unicode)]
private static extern void func1(byte[] path);
public void func2(string path)
{
ASCIIEncoding encoding = new ASCIIEncoding();
byte[] arr = encoding.GetBytes(path);
func1(this.something, arr);
}
The C++ side:
void func1(char *path)
{
//...
}
What I get in the C++ side is an empty string, every time, no matter what I send.
Help?
Thanks.
It looks like you have 2 issues. The first is your native C++ uses an ANSI string but you are specifying unicode. Secondly, it's easiest to just marshal a string as a string.
Try changing the DllImport to the following
[DllImport(
#"Native3DHandler.dll",
EntryPoint = "#22",
CharSet = CharSet.Ansi)]
private static extern void func1(void* something, [In] string path);
Works fine for me with no extra marshalling instructions in VS2008:
C# side:
[DllImport("Test.dll")]
public static extern void getString(StringBuilder theString, int bufferSize);
func()
{
StringBuilder tstStr = new StringBuilder(BufSize);
getString(tstStr, BufSize);
}
C++ side:
extern "C" __declspec(dllexport) void getString(char* str, int bufferSize)
{
strcpy_s(str, bufferSize, "FOOBAR");
}
Your declaration is wrong. The parameter should be of type string, and you should set the character set encoding to Ansi, like so:
[DllImport(#"Native3DHandler.dll", EntryPoint = "#22",
CharSet = CharSet.Ansi)]
private static extern void func1(string path);
This assumes that you are not modifying the contents of the path variable in your C++ code. Then, you pass the string parameter directly (no need for the wrapper).
If you just want to send a string, just declare func1's parameter as a string. If you want to receive a string, declare it as a StringBuilder and allocate enough buffer space for what you want to receive.
Default Marshaling for Strings
http://msdn.microsoft.com/en-us/library/s9ts558h.aspx