While using the exact same arguments as in the C++ example that uses the same .dll, the function Open() successfully opens the COM port. While calling the same function using the same arguments in C#, Open() fails to open the COM port, returning -1. I do not know why Open() successfully opens the COM port when calling from C++, but is unsuccessful when calling from C#.
The header file looks like:
extern "C" __declspec (dllexport) int Open(char* serialNo, int nBaud, int timeout);
The C# import looks like
[DllImport(dllLocation)]
private static extern int Open(byte[] serialNo, int nBuad, int timeout);
and I am calling Open() using
char[] inp = new char[4];
inp[0] = 'C';
inp[1] = 'O';
inp[2] = 'M';
inp[3] = '6';
res = Open(inp, 115200, 10);
The C++ example looks like
char com[20] = { 0 };
cin >> com;
int hdl = Open(com, 115200, 10);
UPDATE
I have also tried using a string:
[DllImport(dllLocation)]
private static extern int Open([MarshalAs(UnmanagedType.LPStr)] string serialNo, int nBuad, int timeout);
string inp = "COM6";
int res = Open(inp, 115200, 10);
I have tried proceeding 0s:
char[] inp = new char[5];
inp[0] = 'C';
inp[1] = 'O';
inp[2] = 'M';
inp[3] = '6';
inp[4] = '\0';
I have tried:
DllImport(dllLocation)]
private static extern int Open(byte[] serialNo, int nBuad, int timeout);
byte[] inp = new byte[4];
inp[0] = 67;
inp[1] = 79;
inp[2] = 77;
inp[3] = 54;
int res = Open(inp, 115200, 10);
Related
I have created a dll, with a function like this
CFunc.h
extern __declspec(dllexport) void fillStr(char* retStr, int strlen);
CFunc.cpp
void fillStr(char* retStr, int strlen)
{
//Option 1
const char* retTemp = "abc";
//Option 2
string retString = "abc";
const char* retTemp = retString.c_str();
cout << "ret in dlb:" << retTemp << endl;
strcpy_s(retStr, strlen, retTemp);
}
Then in C# I have
[DllImport("myDll.dll")]
static extern string fillStr(StringBuilder str, int strLen);
StringBuilder sb = new StringBuilder(2^31);
fillStr(sb, sb.Capacity);
string myString = sb.ToString();
When I use option 1, which is to directly create the const char*, the program works fine.
However, when I use a string, it crashes unexpectedly, with the exception "A heap has been corrupted". I have tried using memcpy and creating the string on the heap, to then delete later, but same error.
Found my mistake.
The C++ function is void while the C# code looks for a returned string.
Changing it to this
[DllImport("myDll.dll")]
static extern void fillStr(StringBuilder str, int strLen);
StringBuilder sb = new StringBuilder(2^31);
fillStr(sb, sb.Capacity);
string myString = sb.ToString();
worked.
I'm trying to convert an array to DLL and modify the values in it.It goes well and the values in array changed.But the problem is,it cause a heap corruption that sometimes it thows out an AccessViolationException,other times the program crash at random
function in DLL code in C++:
MCUPROTOCOL_API int funMcuReadEEprom(const char bank, unsigned char page, char* EEprom,int DataSize)
{
st_PROTOCOL stResponsePackage;
RS232_AT_COMMAND Command;
memset(Command.szCommand, 0x00, sizeof(Command.szCommand));
unsigned char Package[8] = { 0x00 };
unsigned char ResponsePackage[32] = { 0x00 };
unsigned char Data[2] = { 0x00 };
int State;
int ResponsePackageLen;
Data[0] = bank;
Data[1] = page;
fun_ProducePackage(TEST_READ_SOFTWARE_VERSION, Data, Package);
memcpy(Command.szCommand, Package, sizeof(Package));
Command.dwLength = sizeof(Package);
Uart.WriteByte(Command);
Sleep(200);
State = Uart.ReadByte((char *)ResponsePackage, &ResponsePackageLen);
if (ERROR_SUCCESS != State)
{
return FAIL_UART;
}
State = fun_AnalyzeResponsePackage(ResponsePackageLen, ResponsePackage, &stResponsePackage);
if (ERROR_SUCCESS != State)
{
return State;
}
//memcpy(EEprom, stResponsePackage.data, DataSize);
return SUCCESS;
}
C#:
[DllImport("McuProtocol.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int funMcuReadEEprom(byte bank, byte page, IntPtr EEprom, int DataSize);
and I call it like this:
byte page = 129;
byte bank = 2;
string TransferStr = "";
IntPtr SoftwareVersion;
byte[] transfer = new byte[20];
SoftwareVersion = Marshal.StringToCoTaskMemAuto(TransferStr);
McuProtocolApi.funMcuRegister(int.Parse(Info.UartCom.Substring(3)), int.Parse(Info.UartBaud));
McuProtocolApi.funMcuReadEEprom(bank, page, SoftwareVersion, SoftwareVersionSize);
McuProtocolApi.funMcuRelease();
transfer = System.Text.Encoding.Unicode.GetBytes(Marshal.PtrToStringAuto(SoftwareVersion, SoftwareVersionSize / 2));
TransferStr = System.Text.Encoding.UTF8.GetString(transfer);
StrSW = TransferStr;
If I annotate funMcuReadEEprom ,the program went well,otherwise,when it reach this line,the Program would crash.
EDIT 1:
I optimized the code by the suggestions in the comment and the problem still exist.
byte page = 128;
byte bank = 2;
string TransferStr;
IntPtr SoftwareVersion = Marshal.AllocHGlobal(100);
byte[] transfer = new byte[20];
McuProtocolApi.funMcuRegister(int.Parse(Info.UartCom.Substring(3)), int.Parse(Info.UartBaud));
McuProtocolApi.funMcuReadEEprom(bank, page, SoftwareVersion, SoftwareVersionSize);
McuProtocolApi.funMcuRelease();
transfer = System.Text.Encoding.Unicode.GetBytes(Marshal.PtrToStringAuto(SoftwareVersion, SoftwareVersionSize / 2));
TransferStr = System.Text.Encoding.UTF8.GetString(transfer);
StrSW = TransferStr;
Marshal.FreeHGlobal(SoftwareVersion);
I've read most of the hints but I can't get it to work.
I have a native C dll with this prototype:
int utl_Conv_HexString(U8 u8_Mode, void* DataIn, void* DataOut, int *piInOutLen, int maxOutLen);
This dll converts several string formats in byte arrays:
The dll is used in a system with unmanaged code (written in C)
Now I would like to use this dll in a C# / WPF Enviroment.
I still use other dll's in C#, but all have prototypes with no void*.
Examples from C:
//ByteArr to Telegramm
u8_Dst[0] = 0xAA;
u8_Dst[1] = 0xBB;
u8_Dst[2] = 0xCC;
u32_InOutLen = 3;
s32_res = utl_Conv_HexString(UTL_CONV_BYTEARR_TO_TELEGRAM, u8_Dst, ac8_Src, &u32_InOutLen, sizeof(ac8_Src));
or
strcpy(ac8_Src, "0xAA,0xBB,0xCC");
memset(u8_Dst, 0, sizeof(u8_Dst));
s32_res = utl_Conv_HexString(UTL_CONV_TELEGRAM_TO_BYTEARR, ac8_Src, u8_Dst, &u32_InOutLen, sizeof(u8_Dst));
My problem is that I can not figure out how this could be used in C#
You should use it like:
public enum U8
{
UTL_CONV_BYTEARR_TO_TELEGRAM = 1, // TODO
UTL_CONV_TELEGRAM_TO_BYTEARR = 2,
}
(you will have to put here your constants...)
[DllImport("SomeDll.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int utl_Conv_HexString(U8 u8Mode, byte[] dataIn, byte[] dataOut, ref int piInOutLen, int maxOutLen);
(note that the CallingConvention could be StdCall... you'll have to check your code)
and then:
byte[] src = Encoding.UTF8.GetBytes("0xAA, 0xBB, 0xCC");
byte[] dest = new byte[64];
int lenSrc = src.Length;
int res = utl_Conv_HexString(U8.UTL_CONV_TELEGRAM_TO_BYTEARR, src, dest, ref lenSrc, dest.Length);
The void* is normally translated to a byte[].
I need to return array of struct to c# from c++ DLL in Wince 7.
C++ code
struct AvailableNetworkList
{
char strProfileName[7];
ULONG WLanSignalQuality;
};
BOOL GetAvailableNetworkLists(OUT AvailableNetworkList** data,OUT int *length)
{
AvailableNetworkList availableNetwork;
AvailableNetworkList *availableNetworkList = new AvailableNetworkList[pList->dwNumberOfItems];
for(DWORD i=0;i<pList->dwNumberOfItems;i++)
{
availableNetwork.WLanSignalQuality=pList->Network[i].wlanSignalQuality;
availableNetwork.strProfileName[0] = 'a';
availableNetwork.strProfileName[1] = 'b';
availableNetwork.strProfileName[2] = 'c';
availableNetwork.strProfileName[3] = 'd';
availableNetwork.strProfileName[4] = 'e';
availableNetwork.strProfileName[5] = 'f';
availableNetwork.strProfileName[6] = 'g';
availableNetworkList[i] = availableNetwork;
}
*length=pList->dwNumberOfItems;
*data = availableNetworkList;
return true;
};
c# code
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode), Serializable]
unsafe struct AvailableNetworkList
{
public uint WLanSignalQuality;
[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 7)]
public string strProfileName;
}
[DllImport("WLANWRAPPER.dll")]
public static extern bool GetAvailableNetworkLists(out IntPtr arrayPtr, out int size);
Code to call api and sync data with c# structure
IntPtr resultPtr = IntPtr.Zero;
int size = 0;
bool result2 = GetAvailableNetworkLists(out resultPtr, out size);
var dataEntrySize = Marshal.SizeOf(typeof(AvailableNetworkList));
var availableNetworkList = new AvailableNetworkList[size];
for (var i = 0; i < size; i++)
{
var cur = (AvailableNetworkList)Marshal.PtrToStructure(resultPtr, typeof(AvailableNetworkList));
availableNetworkList[i] = cur;
resultPtr = new IntPtr(resultPtr.ToInt32() + dataEntrySize);
}
Here everything is working fine .only problem is string value(strProfileName) in struct is set as "abcdefg" in c++ and when it came to c# ,the value is not in readable format as shown in below image.Other integer value is correct.
Any help would be appreciated.Thanks in Advance.
I have a C# function, a callback, called from a Win32 DLL written in C++. The caller gives me a UTF8 string, but I can't receive it properly, all the hungarian special characters go wrong.
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int func_writeLog(string s);
When I changed the parameter type to IntPtr, and wrote the code, it writes properly. But I find this is a very slow solution:
byte[] bb = new byte[1000];
int i = 0;
while (true)
{
byte b = Marshal.ReadByte(pstr, i);
bb[i] = b;
if (b == 0) break;
i++;
}
System.Text.UTF8Encoding encodin = new System.Text.UTF8Encoding();
var sd = encodin.GetString(bb, 0, i);
I tried to write some attribute to string parameter, like:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int func_writeLog([In, MarshalAs(UnmanagedType.LPTStr)] string s);
no one was working. Any advice please? Thanks in advance!
There's no decent way to do this fast in pure managed code, it always requires copying the string and that's very awkward because you don't know the required buffer size. You'll want to pinvoke a Windows function to do this for you, MultiByteToWideChar() is the work-horse converter function. Use it like this:
using System.Text;
using System.Runtime.InteropServices;
...
public static string Utf8PtrToString(IntPtr utf8) {
int len = MultiByteToWideChar(65001, 0, utf8, -1, null, 0);
if (len == 0) throw new System.ComponentModel.Win32Exception();
var buf = new StringBuilder(len);
len = MultiByteToWideChar(65001, 0, utf8, -1, buf, len);
return buf.ToString();
}
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern int MultiByteToWideChar(int codepage, int flags, IntPtr utf8, int utf8len, StringBuilder buffer, int buflen);