C# Equivlant of C void? - c#

Hi I have the following sub:
static ustr *construct_packet(int ptype, int stype, int argc, ...)
static ustr *construct_subpacket(int sptype, const char *data, int datalen, int lenbyte)
I tried the "..." in C# and it just gives me an error of "Type Expected."
I also need to send multiple construct_subpacket's into the construct_packet sub.
What would be the equivalent to these in C#?
Question 2:
I want to pass weird ASCII characters like "char(13)" the box through a string without it being replaced by "?." How do I go about doing such a thing? Or is there no way?
Thanks

static string construct_packet(int ptype, int stype, int argc, ...)
static string construct_subpacket(int sptype, string data, int datalen, int lenbyte)
Note:
You don't really have to pass datalen in C# - unlike with pointers, with strings you always know length of the string by checking the Length property on the string
If you want to pass variable number of arguments, you can use "params" keyword. All those arguments have to be the same type

Related

Converting from string to char* only copies the first character

I have looked at most of the string to char* conversion SO answers, but it is not working for me. Here is my code:
public static void Main() {
string name = "ELEM";
unsafe{
fixed(char* name_ptr = name) {
Console.WriteLine(name_ptr->ToString());
}
}
}
// Output: E
I need to do this since I have to pass a char* to my C++ custom DLL. Why would it only copy the first character only, and how can I properly convert string to a char*?
You get the first character only, because name_ptr is nothing but a reference to a single character and when you call name_ptr->ToString() you actually call char.ToString().
You should use a StringBuilder instead to pass a string to a C/C++ DLL. See this question.

How to correctly pass array of structs from MQL4 to C# DLL library

I am writing a script in MQL4 language for MetaTrader Terminal 4 which is using C# DLL library for further data processing.
What I'm trying to do is to pass an array of following MQL4-structures:
struct marketUpdate
{
double openPrice;
double closePrice;
double highPrice;
double lowPrice;
int volume;
double pipValuePerLot;
int intervalLength;
string symbol;
string time;
};
to this function in C# DLL:
[DllExport("HistoricMarketData", CallingConvention.StdCall)]
public static int HistoricMarketData(
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] MarketUpdate[] unmanagedArray,
int length)
{
// further processing
}
This is the function definition on MQL4 side:
int HistoricMarketData(
marketUpdate &marketUpdates[],
int length);
and this is the structure on C# side:
[StructLayout(LayoutKind.Explicit)]
public struct MarketUpdate
{
[MarshalAs(UnmanagedType.R8)]
[FieldOffset(0)]
public double openPrice;
[MarshalAs(UnmanagedType.R8)]
[FieldOffset(8)]
public double closePrice;
[MarshalAs(UnmanagedType.R8)]
[FieldOffset(16)]
public double highPrice;
[MarshalAs(UnmanagedType.R8)]
[FieldOffset(24)]
public double lowPrice;
[MarshalAs(UnmanagedType.I4)]
[FieldOffset(32)]
public int volume;
[MarshalAs(UnmanagedType.R8)]
[FieldOffset(36)]
public double pipValuePerLot;
[MarshalAs(UnmanagedType.I4)]
[FieldOffset(44)]
public int intervalLength;
[MarshalAs(UnmanagedType.LPWStr)]
[FieldOffset(48)]
public string symbol;
[MarshalAs(UnmanagedType.LPWStr)]
[FieldOffset(60)]
public string time;
}
My problem is that it is not working when calling HistoricMarketData from MQL4. The script either crashes whole MetaTrader program or throws an exception and fails.
As you can see I'm using Robert Giesecke's Unmanaged Exports library to make it works and I correctly imported DLL to MQL4 as other functions inside are working without any issue.
After lot of trial and error I was able to find out that when I remove those 2 string attributes (symbol and time) from structure all works as expected and structure is correctly passed to C# function.
So my question is how to correctly pass string in structure from MQL4 to C#?
I already tried to change UnmanagedType parameter at MarshalAs annotation in structure to other string variants but it didn't help. It also didn't help to change string to char[] on both sides.
I'll appreciate any advices or help.
Thanks!
Welcome to the Wild Worlds of MQL4 where string is not string,but a struct
So,
this,
and not only this,
was causing a lot of headache for re-integrationg MQL4 with DLL-s
My own strategy is:
1) avoid MQL4-strings, if not of a core importance,
2) if needed, pass them via a DLL-proxy utility function, which handles needed conversions:
#import <dll> // DLL-native CallSignatures:
...
int mql4s_send ( int socket, uchar &text[] );
...
#import
// DLL-proxy with StringToCharArray() conversion:
int s_send ( int socket, string text ) { uchar textChar[]; StringToCharArray( text, textChar );
return ( mql4s_send ( socket, textChar ) );
}
Anyway,rather do read MQL4 Help, actually better re-read it again after each MetaTrader Build update ( yes, I am saying, that new help files contain ( at least ) some remarks on new MQL4-features, that have no other easy way to find and learn about -- it saves a lot of time. With code-base under maintenance, spanning more than a few hundreds man*years, I know the pain very well ).
The following can't be used for parameters in imported functions:
·pointers;
·links to objects that contain dynamic arrays and/or pointers.
Classes, string arrays or complex objects that contain strings and/or dynamic arrays of any types cannot be passed as a parameter to functions imported from DLL.
But Devil is hidden in details like this ( indirectly forbidden feature ):
...
Simple Structures
Structures that do not contain strings or objects of dynamic arrays are called simple structures; variables of such structures can be freely copied to each other, even if they are different structures. Variables of simple structures, as well as their array can be passed as parameters to functions imported from DLL.

c# dllimport parameters shift

I am currently developing an Excel add-in in C#, and I am using interop with a C++ library. Both are developed by me. The C++ library is not a COM component, but just a DLL exporting some functions.
I have one C# function, that I want to pass a number and two strings to the C++ function, and the C++ function returns a string. First I tried to return a string directly, so the prototypes look something like
//C#
[return: MarshalAs(UnmanagedType.LPStr)]
private static extern string Function([MarshalAs(UnmanagedType.I4)] int number,
[MarshalAs(UnmanagedType.LPStr)]string str1,
[MarshalAs(UnmanagedType.LPStr)] string str2);
//C++
extern "C" __declspec(dllexport) string Function(int, char*, char*);
(Btw, I set already the calling convention in C# for this function as Cdecl)
Then when I call the C# version, and stepped into the C++ (when debugging), I found that the parameters are shifted, so the integer is the value of the pointer to the first string, and the first string is the second string, and the second string points to somewhere else. I also checked the memory, and found that the parameters were not passed according to the _cdecl convention, the integer was the last one pushed into the stack (which is ok), but the positions of the two strings were switched. There are many other information that the compiler added, so I don't want to go further deep into this.
However, later I changed the return string as a parameter, and the prototypes look like
//C#
private static extern void Function([MarshalAs(UnmanagedType.I4)]int number,
[MarshalAs(UnmanagedType.LPStr)] string str1,
[MarshalAs(UnmanagedType.LPStr)] string str2,
[MarshalAs(UnmanagedType.LPStr), Out] StringBuilder returnStr);
//C++
extern "C" __declspec(dllexport) void Function(int, char*, char*, string&);
And now the parameters are passed correctly.
So my first question is, how does this happen?
My second question is, even using the second method, I could not get the program run correctly, the program crashed when returning from the C++ function (actually in the C++ function, the only thing being done is calling another member function of a class, so the function is just like a wrapper of the member function, and the program crashed exactly when returning in the member function. Since the warpper function just calls the member function, so I cannot tell where the problem is).
Does anyone have an idea how to do this? So the main point of the function is to take a number and two strings and return another string.
Thanks in advanced.
look at this article about the usage of stringbuilder in unmanaged code
try something like this:
//C#
public string Function(int number, string str1, string str2)
{
StringBuilder sbStr1 = new StringBuilder(str1);
StringBuilder sbStr2 = new StringBuilder(str2);
StringBuilder sbReturnStr = new StringBuilder(1024);
Function(number, sbStr1 , sbStr2 , sbReturnStr , sbReturnStr.Capacity);
return sbReturnStr.ToString();
}
//C# p/invoke
private static extern void Function(int number, StringBuilder str1, StringBuilder str2, StringBuilder returnStrBuffer, int size);
//C++
extern "C" __declspec (dllexport) void __stdcall Function(int number, const char* str1, const char* str2, char* returnStrBuffer, int size)
{
//fill the returnStrBuffer
strncpy(returnStrBuffer, "blablalb", size); //example!!
}

Using Delphi's stuct arrays and strings in C#

I've been trying to invoke a method that have been created in Delphi in the following way:
function _Func1(arrParams: array of TParams): Integer;stdcall;
type
TParams = record
Type: int;
Name: string;
Amount : Real;
end;
My code is:
[DllImport("some.dll", EntryPoint = "_Func1", CallingConvention = CallingConvention.StdCall)]
public static extern int Func(
[MarshalAs(UnmanagedType.LPArray, ArraySubType=UnmanagedType.Struct)] TParams[] arrParams)
And the struct is:
[StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct TParams
{
public int Type;
[MarshalAs(UnmanagedType.AnsiBStr)]
public string Name;
public double Amount;
}
When I am calling this method I'm getting the error:
Cannot marshal field 'Name' of type 'TParams': Invalid managed/unmanaged type combination (String fields must be paired with LPStr, LPWStr, BStr or ByValTStr).
However none of those combinations works, as Delphi's strings are prefixed with its length and it is Ansi for sure (I've tried it with other string parameters). Does anyone have a clue how to solve this?
There are two main problems with this, use of open arrays and use of Delphi string.
Open arrays
Delphi open arrays are implemented by passing a pointer to the first element of the array and an extra parameter specifying the index of the last item, high in Delphi terminology. For more information see this answer.
Delphi strings
The C# marshaller cannot interop with a Delphi string. Delphi strings are private types, only to be used internally to a Delphi module. Instead you should use a null-terminated string, PAnsiChar.
Putting it all together you can write it like this:
Delphi
type
TParams = record
_Type: Integer;//Type is a reserved word in Delphi
Name: PAnsiChar;
Amount: Double;
end;
function Func(const arrParams: array of TParams): Integer; stdcall;
C#
[StructLayoutAttribute(LayoutKind.Sequential)]
public struct TParams
{
public int Type;
public string Name;
public double Amount;
}
[DllImport("some.dll")]
public static extern int Func(TParams[] arrParams, int high);
TParams[] params = new TParams[len];
...populate params
int retval = Func(params, params.Length-1);
To compliment David's answer, you can marshal to a Delphi string, but it's ugly. In C#, you have to replace all of your strings in the struct with IntPtr.
private static IntPtr AllocDelphiString(string str)
{
byte[] unicodeData = Encoding.Unicode.GetBytes(str);
int bufferSize = unicodeData.Length + 6;
IntPtr hMem = Marshal.AllocHGlobal(bufferSize);
Marshal.WriteInt32(hMem, 0, unicodeData.Length); // prepended length value
for (int i = 0; i < unicodeData.Length; i++)
Marshal.WriteByte(hMem, i + 4, unicodeData[i]);
Marshal.WriteInt16(hMem, bufferSize - 2, 0); // null-terminate
return new IntPtr(hMem.ToInt64() + 4);
}
This can directly be sent to Delphi, where it'll properly be read as a string.
Remember that you must free this string when you're done with it. However, GlobalFree() can't be called directly on the pointer to the string, because it doesn't point to the start of the allocation. You'll have to cast that pointer to a long, then subtract 4, then cast it back to a pointer. This compensates for the length prefix.

C# Dll Invoke String Encoding Problem

StringBuilder codeline = new StringBuilder(100);
protected virtual int OnCodeLine(int code, int docId, ref StringBuilder codeline)
{
////
}
This is what i get with *ref StringBuilder*
ĞĞÑĞÒĞÓĞÔĞÕĞÖĞ×ĞØĞÙĞÚĞÛĞÜĞİĞŞĞßĞàĞáĞâĞãĞäĞåĞæĞçĞèĞéĞêĞëĞìĞíĞîĞïĞğĞñĞòĞóĞôĞõĞöĞ÷ĞøĞùĞúĞûĞüĞıĞşĞÿĞÑÑÑÑÑÑÑ Ñ
ÑÑÑ
ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
and only with StringBuilder i only get 3
This is what its suppose to return
300 078 9059431
By the way this the MICR Code from Cheques
[DllImport("mflib.dll")]
public static extern int mfScanFeeder(int mode, int font, int timeout);
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate int MFS100_CodeLineDelegate(int code, int docId, ref StringBuilder codeline);
public event MFS100_CodeLineDelegate MFS100_CodeLine;
private static MFS100_CodeLineDelegate cache_CodeLine;
Update : Here is the Original Code that works in vb6
Public Function MFS100_OnCodeline(ByVal code As Long, ByVal docId As Long, ByVal codeline As String) As Long
Dim i As Integer
WriteEvent "OnCodeline:"
WriteEvent " code = " & code
WriteEvent " docId = " & docId
WriteEvent " codeline = " & codeline
MFS100_OnCodeline = -1 ' -1 means: sorting will be done by mfSort()
g_codeline = codeline
CScannerForm.TmrSort.enabled = True
End Function
Update 2
mfSetEvent((int)EventEnum.E_EVENT_CODELINE, cache_CodeLine);
[DllImport("mflib.dll")]
private static extern int mfSetEvent(int eventID, Delegate callsback);
When i use StringBuilder with ref i get a string that have
32361 length. Without ref i get
only the first value of the
string.
OnCodeLine is for the callback
from the scanner device. What is the
problem ?
You should not pass the StringBuilder by ref. When you do that you say you only get one character back. That's the hallmark of expecting ANSI encoding but actually receiving Unicode (UTF-16). Fix it by specifying the character set:
[UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet=CharSet.Unicode)]
public delegate int MFS100_CodeLineDelegate(int code, int docId, StringBuilder codeline);
I don't understand what are you trying to achieve, but if you want to collect a string generated inside OnCodeLine, you don't need to pass the StringBuilder by reference, as it is a reference type.
Just pass the StringBuilder without ref, populate it, and when you return you'll have the desired string in it.
Regarding what you get after calling the OnCodeLine, can you provide some info regarding the implementation?
Sorry, I didn't notice the PInvoke was involved!! :(

Categories

Resources