I have created a win32 dll which sends and recieves ssl data packets from our server, I am calling a dll function using P/Invoke mechanism from my C# app which does all necessary tasks.
When I call Connect(char* lpPostData)
function and I use static char postData [] array as a posting request it works fine , if I use char* lpPostData as sent parameter from my C# app for posting request it doesn't works. Is it something with conversion of C# string to char * ?? if that is the case how do i do it ?? How to debug the in Win32 dll ???
Calling the exported function from C# app:
[DllImport("testdllwm6.dll", EntryPoint = "Connect")] public
static extern int pConnect(string postdata);
string postdata="<SyncML><SyncHdr><VerDTD>1.2</VerDTD><VerProto>SyncML/1.2</VerProto><SessionID>33622878</SessionID><MsgID>1</MsgID><Target><LocURI>http://sync.com</LocURI></Target><Source><LocURI>IMEI::358997011403172</LocURI><LocName>syncfdg</LocName></Source><Meta><MaxMsgSize
xmlns=\"syncml:metinf\">10000</MaxMsgSize></Meta></SyncHdr><SyncBody><Alert><CmdID>1</CmdID><Data>201</Data><Item><Target><LocURI>contacts</LocURI></Target><Source><LocURI>./contacts</LocURI></Source><Meta><Anchor
xmlns=\"syncml:metinf\"><Last>000000T000000Z</Last><Next>20091125T122400Z</Next></Anchor></Meta></Item></Alert><Final></Final></SyncBody></SyncML>";
int j = pConnect(postdata);
Declaration is:
__declspec(dllexport) int Connect(char* lpPostData);
The function is defined as:
__declspec(dllexport) int Connect(char* lpPostData) {
LPCTSTR lpszAgent = _T("CeHttp");
DWORD dwError; DWORD sizeInResult,
sizeOutResult, sizeToWrite,
sizeWritten,dwRead; HINTERNET
hInternet=NULL; HINTERNET
hConnect=NULL; HINTERNET
hRequest=NULL; LPDWORD
pSizeInResult = &sizeInResult;
LPDWORD pSizeOutResult = &sizeOutResult;
LPDWORD pSizeToWrite = &sizeToWrite;
LPDWORD pSizeWritten = &sizeWritten; int read = 0;
char postData[637]
="<SyncML><SyncHdr><VerDTD>1.2</VerDTD><VerProto>SyncML/1.2</VerProto><SessionID>66622878</SessionID><MsgID>1</MsgID><Target><LocURI>http://sync.com</LocURI></Target><Source><LocURI>IMEI::358997011403172</LocURI><LocName>new123</LocName></Source><Meta><MaxMsgSize
xmlns=\"syncml:metinf\">10000</MaxMsgSize></Meta></SyncHdr><SyncBody><Alert><CmdID>1</CmdID><Data>201</Data><Item><Target><LocURI>contacts</LocURI></Target><Source><LocURI>./contacts</LocURI></Source><Meta><Anchor
xmlns=\"syncml:metinf\"><Last>000000T000000Z</Last><Next>20091125T122400Z</Next></Anchor></Meta></Item></Alert><Final></Final></SyncBody></SyncML>";
LPCWSTR lpszHeaders =_T("Content-Type: application/vnd.sync+xml");
BOOL bResult;
if(!HttpSendRequest(hRequest,lpszHeaders,wcslen(lpszHeaders),
lpPostData,strlen(lpPostData)))
{
dwError = GetLastError();
printf(" not HttpSendRequest");
return read;
}
return read;
The failure point is very obvious. Windows CE is Unicode. The string in C# is a wide-character array, the char[] in C is a multibyte. You're mixing the two, and that is bad, bad, bad.
I mean you're mixing them in the same call, sending wide headers and multibyte postData to HttpSendRequest? That certainly can't be right.
Change the Connect function to look like this:
int Connect(TCHAR* lpPostData)
try it again, and come back with the results.
Of course this also means you need to change the strlen call as well.
As a side note, I don't understand why you would call into C++ for this call anyway. You could do it right from your C# app.
seems the dll is a MFC extension dll, maybe only can be callec by MFC application. i am not sure.
Is it something with conversion of C# string to char * ??
The default CharSet used by the .Net Interop Marshaler is Ansi.
If you want use Unicode(LPCWSTR) parameters,
you can try:
[DllImport("testdllwm6.dll", EntryPoint = "Connect", CharSet=CharSet.Unicode)]
public static extern int pConnect(string postdata);
BTW, you can refer to .Net 2.0 Interoperability Recipes: A Problem-Solution Approach
for more information.
You need to add the MarshalAs attribute to the string parameter so the .NET runtime knows how to marshal it; by default strings are marshaled as Unicode, but you want ANSI:
[DllImport("testdllwm6.dll", EntryPoint="Connect")]
public static extern int Connect([MarshalAs(UnmanagedType.LPStr)] string lpPostData);
use
System.Text.StringBuilder
pass that to your function
Related
I have native C++ dll with function that finds the number of cameras connected to the computer and returns their serial number. I am trying to use native C++ dll in C# application but I keep getting the Access Violation error(Attempted to read or write protected memory).
The function in question is
uint32_t GetSerialNumList(char** theBufList, int theBufSize, int theListLength);
The way I am using PInvoke is as follows:
[DllImport(CameraDll, EntryPoint = "GetSerialNumList", CallingConvention = CallingConvention.Cdecl)]
private static extern uint GetSerialNumList(out byte[] pBuf, int BufSize, int ListLength);
If I create native C++ application to use the dll and use the function as follows:
char* theSerialNumb;
theSerialNumb = (char *) malloc(sizeof(char)* 8);
status = TRI_GetSerialNumList(&theSerialNumb, 8, 1);
It works fine however, if I use as follows in C# it give me above mentioned error:
byte[] BufList;
BufList = new byte[8];
rv = GetSerialNumList(out BufList, 8, 1);
The parameter you're passing in c# is a pointer to a byte array. What you're passing in c++ is a pointer to a pointer to a byte array. Also, in the C++ example, you're passing data to the function, but in the C# example, you're passing it as an out instead of a ref.
Although I'm not sure this would work, I would try to create a struct containing a byte array and pass the struct to the external function.
To answer some of the above comments, these functions typically modify memory passed to it rather than try to allocate additional memory due to the different ways programs create heaps.
The first thing I'd check is the C# import signature being used. There's the P/Invoke Interop Assistant tool available for free here.
Loading your function signature into the tool, translates it to:
public partial class NativeMethods {
/// Return Type: unsigned int
///theBufList: char**
///theBufSize: int
///theListLength: int
[System.Runtime.InteropServices.DllImportAttribute("<Unknown>", EntryPoint="GetSerialNumList")]
public static extern uint GetSerialNumList(ref System.IntPtr theBufList, int theBufSize, int theListLength) ;
}
The second thing, is that since you are allocating memory for the buffer in the C++/native version; perhaps you need to pass a pre-allocated buffer as well, when using C#.
Hope this helps.
Okay, I took pointers from Russell and kvr and did some digging around and following is the scheme that I came up with.
Original native function call:
uint32_t GetSerialNumList(char** theBufList, int theBufSize, int theListLength);
The way I am using PInvoke is as follows:
[DllImport(CameraDll, EntryPoint = "GetSerialNumList", CallingConvention = CallingConvention.Cdecl)]
private static extern int GetSerialNumList(ref IntPtr pBuf, int BufSize, int ListLength);
byte[] BufIn;
BufIn = new byte[8 * ListLength];
IntPtr pBuf = IntPtr.Zero;
pBuf = Marshal.AllocHGlobal(8 * ListLength);
Console.WriteLine("Calling GetSerialNumList");
rv = GetSerialNumList(ref pBuf, 8, ListLength);
Marshal.Copy(pBuf, BufIn, 0, 8*ListLength);
I feel this is somewhat long, but it gives me the desired result.
I've got a problem and I hope you guys can help me with this. I'm relatively new to programming in C# and have never worked with c++, so i can't figure out the following problem:
I'm using a SDK (which is writen in c++) to receive and request DDNS updates from a specific type of device. Setting the callback is working and the callback is triggered at the expected time.
The documentation says the callback contains a char** parameter, but I don't know what do do with this. I've tried a lot of options and the only way the application doesn't crash is by using an IntPtr for that parameter. But when I do this I'm not able to set a value for that IntPtr.
Original code I found using the callback:
int CALLBACK CBGetDeviceAddr(int *pResult, char** pIPAddr,unsigned short *pSdkPort, char *szDeviceID, char*szDeviceName)
{
int nCount=g_pDeviceList->GetItemCount();
memset(g_szIPAddr, 0, MAX_IP_LEN+1);
*pIPAddr=g_szIPAddr;
BOOL bRet=FALSE;
for(int i = 0; i< nCount; i++)
{
if (strcmp(szDeviceID,(LPCTSTR)g_pDeviceList->GetItemText(i, 2).GetBuffer(0)) == 0)//,g_pDeviceList->GetItemText(i,2).GetLength()
{
*pResult=1;
strcpy(*pIPAddr,g_pDeviceList->GetItemText(i,0).GetBuffer(0));
*pSdkPort = atoi(g_pDeviceList->GetItemText(i,4).GetBuffer(0));
TRACE("CALLBACK CBGetDeviceAddrInfo:in DeviceID=%s, DeviceName=%s,out DeviceIP=%s,DevicePort = %d\n",\
szDeviceID, szDeviceName, *pIPAddr, *pSdkPort);
bRet = TRUE;
break;
}
}
if (!bRet)
{
*pResult=0;
*pIPAddr=NULL;
TRACE("CALLBACK CBGetDeviceAddrInfo:in DeviceID=%s, DeviceName=%s,out no such device online!!!\n",\
szDeviceID, szDeviceName);
}
return 0;
}
What I'm currently using:
private int CBF_GetADeviceIP(ref int pResult, ref IntPtr pIPAddr, ref ushort pSdkPort, string szDeviceID, string szDeviceName)
This is working for pResult and pSdkPort (so I can send data back) but can't do anything with pIPAddr.
Any help?
Thanks
Normally when you see char**, it means "This parameter will be used to return a string".
When writing the P/Invoke for such an argument, you can usually use ref string (if the string is input, or input and output) or out string (if the string is output only).
The other thing you need to know is whether the native method is expecting ANSI or Unicode strings.
To specify which string encoding to use, use the CharSet attribute parameter:
[DllImport("MyDLL.dll", CharSet = CharSet.Unicode)]
or
[DllImport("MyDLL.dll", CharSet = CharSet.Ansi)]
and so on.
I'm not that good about C#, but isn't pIPAddr a pointer to a string?
So you should declare that parameter as ref string pIPAddr.
This is my first attempt at creating a C# wrapper to a C lib. The SDK is 3rd party and outside my control:
sdk_defines.h
#ifndef SDK_CHAR_T
#define SDK_CHAR_T
typedef char sdk_char_t;
#endif /* SDK_CHAR_T */
#ifndef SDK_CSTR_T
#define SDK_CSTR_T
typedef const sdk_char_t* sdk_cstr_t;
#endif /* SDK_CSTR_T */
sdk.h
sdk_cstr_t SDK_API
sdk_get_version(void);
My C# wrapper:
[DllImport("sdk.dll", CharSet = CharSet.Ansi)]
private static extern string sdk_get_version();
Any call to sdk_get_version causes a crash, no exception. I have a feeling it's the return type, but how do I properly marshal it in C#?
If you return as string, then the marshaller will call CoTaskMemFree on the pointer that is returned. I expect that's what's happening to you and is why your code fails. Looking at that API it seems most likely that the pointer that is returned points to a string literal in the DLL, certainly not something allocated with CoTaskMemAlloc.
So, you are going to need to return that as an IntPtr and then convert to string using Marshal.PtrToStringAnsi.
So you need it like this:
[DllImport("sdk.dll")]
private static extern IntPtr sdk_get_version();
....
IntPtr ptr = sdk_get_version();
string version = Marshal.PtrToStringAnsi(ptr);
Before you go much further you need to work out what SDK_API is. Is it stdcall or is it cdecl? It makes no difference for this no-parameter function, but before long you'll be passing parameters, and you'll need to know.
I have a C++ MFC regular DLL I am calling with the following:
public static class Access3rdPartyDLL
{
public static string FilePath;
[DllImport("3rdparty.dll")]
// I have also tried LPWStr
public static extern long Download([MarshalAs(UnmanagedType.LPStr)]string sDownloadFile,
int iDeviceNum
...);
public static long DownloadToDevice()
{
long result;
string FilePath = "C:\\myfile.txt"
result = Download(FilePath, 1, ...);
// check if success or error
if(result > 0)
...
}
}
I get an error back from the DLL saying "File: 'C:\myfile.txt' not found. But its there...
I have also tried using StringBuilder but this also fails.
Could this be a problem with the DLL or am I doing something wrong?
I found this current code here: SO: equivalent char* in C#
EDIT: I have done this in C++ before and this code works:
extern "C" __declspec(dllimport) HRESULT __stdcall Download(char* sDownloadFile, int ...
which I call with:
HRESULT result = Download(file_for_download, 1, .. // where file_for_download is a char*
The only thing wrong with the P/invoke is that you are using C# long which is 64 bits, but an HRESULT is only 32 bits.
You have matching calling conventions, default marshalling for managed string is char* on the unmanaged side.
Mismatching return value size would not explain why your C# code receives a string message File: 'C:\myfile.txt' not found so your main problem most likely lies in the code that you haven't shown us.
I don't see any reason why the following wouldn't work in this simple scenario:
[DllImport( "3rdparty.dll", CharSet = CharSet.Ansi )]
static extern long Download(string sDownloadFile, int iDeviceNum, ...)
long result = Download("C:\\myfile.txt", 1, ...);
I am new in microsoft world.
I have lot of problem trying to pass a simple string from c# to dll/c++
I have read a lot of post and documentation about but the problem is the same.
C++ code
extern "C" __declspec(dllexport) int Init( long l , char* url );
C# code
[DllImport("MCRenderer.dll", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = false)]
public static extern int Init(long a, StringBuilder url);
Init(hndl.ToInt64(), str );
what haeppen is that long value is passed correctly while string parameter is
0x00000000 <Bad Ptr>
can you help me ... Iam really confused
thanks!!
AG
You should pass a string, url should be of type string and not StringBuilder.
It's because you're marshalling incorrectly: long and int in C++ are both (usually) int in C#.
From MSDN, you need to:
The only caveat is that the StringBuilder must be allocated enough space for the return value, or the text will overflow, causing an exception to be thrown by P/Invoke
Also from Marshaling between Managed and Unmanaged Code
Don't pass StringBuilder by reference (using out or ref). Otherwise, the CLR will expect the signature of this argument to be wchar_t ** instead of wchar_t *, and it won't be able to pin StringBuilder's internal buffer. Performance will be significantly degraded.
Use StringBuilder when the unmanaged code is using Unicode. Otherwise, the CLR will have to make a copy of the string and convert it between Unicode and ANSI, thus degrading performance. Usually you should marshal StringBuilder as LPARRAY of Unicode characters or as LPWSTR.
Always specify the capacity of StringBuilder in advance and make sure the capacity is big enough to hold the buffer. The best practice on the unmanaged code side is to accept the size of the string buffer as an argument to avoid buffer overruns. In COM, you can also use size_is in IDL to specify the size.
So in your example, you need to specify the native size as the parameter of the StringBuilder like this StringBuilder str = new StringBuilder(SIZE_OF_NATIVE_STRING);
Try using LPCSTR in the dll function parameter
extern "C" __declspec(dllexport) int Init( long l , **LPCTSTR** url );
Here is a good example that I found on how to do this.
C++:
extern "C" __declspec(dllexport) void doSomething(LPCTSTR asString)
{
std::cout << "here" << (wchar_t)asString << endl;
system ("pause");
}
C#:
class Program
{
static void Main(string[] args)
{
String myString = "String in C#";
doSomething(myString);
}
private const String path = #"C:\testdll2.dll"; //Make sure that the DLL is in this location
[DllImport(path)]
private static extern void doSomething(string asString);
}