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.
Related
I probably missing something but as I understand string and String are aliases as explained in difference-between-string-and-string so string is just an object!
Now what I don't understand is how the following code initialize new object of String?
string s="Hello World"
Can I do this trick for regular objects?
If you are compiling them in your code.. they are compile time constants.. i.e. code explicitly references them from your compiled binary, which is of course loaded in memory at runtime..
If you construct them at runtime.. like from char array, I would guess that CLR has the necessary implementation for doing so. For example - look at following code from http://referencesource.microsoft.com/#mscorlib/system/string.cs,97ccd50b20126543
[System.Security.SecuritySafeCritical] // auto-generated
private static String ConcatArray(String[] values, int totalLength) {
String result = FastAllocateString(totalLength);
int currPos=0;
for (int i=0; i<values.Length; i++) {
Contract.Assert((currPos <= totalLength - values[i].Length),
"[String.ConcatArray](currPos <= totalLength - values[i].Length)");
FillStringChecked(result, currPos, values[i]);
currPos+=values[i].Length;
}
return result;
}
[System.Security.SecurityCritical] // auto-generated
[ResourceExposure(ResourceScope.None)]
[MethodImplAttribute(MethodImplOptions.InternalCall)]
internal extern static String FastAllocateString(int length);
Essentially strings get special treatment in the language, and although they are objects (immutable), you are right in your understanding that they are not instantiated with new operator in traditional sense. i.e. you don't do what you said in your comment String s=new String("Hello World"); because if you think about it, the new is redundant, because you have already defined your string in double quotes as a string literal.
And hence while you could use implicit operator, to convert a string to a given type.. It is not the same trick. The trick in case of strings is the native support from CLR.
EDIT:
Here is another proof.. There is a special IL OpCode to load strings.
OpCodes.Ldstr
"Pushes a new object reference to a string literal stored in the metadata."
If you'll look at your compiled code, you'd see ldstr opcode being used to load strings, instead of newobj opcode.
As mentioned by #KirkWoll, from Using Conversion Operators you could use an implicit operator
Something like
public class FOO
{
private string _foo;
public static implicit operator FOO(string foo)
{
FOO f = new FOO {_foo = foo};
return f;
}
}
and then calling it
FOO bar = "TADA";
google didnt help me such as i wanted so im writing post here.
i have Unicode string in C# and C function (in dll) which want the char(ANSI)*.
i try to do
string tmp = "ala ma kota";
unsafe
{
fixed (byte* data = &tmp.ToCharArray()[0])
{
some_function(data);
}
}
but i cannot convert directly without encoding. I try to use Encode class but without any effects. I know that, the some_function needs to be called with char pointer. Where it points to array of byte.
You don't need to do this explicitly yourself.
Instead, declare the C method using P/Invoke to accept a parameter of type string.
Then add to the P/Invoke declaration:
[DllImport("YourDllName.dll", CharSet=CharSet.Ansi)]
The marshaller will then convert the string to ANSI for you.
Note that I'm assuming that the string is being passed TO the called function, so the parameter is NOT a pointer being used to return a new string FROM the called function.
See here for more details: http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.dllimportattribute.charset.aspx
In fact, the default is CharSet.Ansi so you might only need to declare the parameter as string instead of byte* (or whatever you are using just now).
You should use the System.Text namespace:
string tmp = "ala ma kota";
unsafe
{
fixed (byte* data = &System.Text.Encoding.ASCII.GetBytes(tmp)[0])
{
some_function(data);
}
}
I have a C++ dll that has a function that returns a c string and I have a C# program that calls this function and returns the data to a C# string. Here's what I mean
__declspec(dllexport) const char* function (const char* str) {
std::string stdString( str );
std::cout << stdString.c_str() << std::endl; // this prints fine, no data loss
return stdString.c_str();
}
And here's the C# code
[DllImport("MyDLL.dll")]
public static extern string function(string data);
string blah = function("blah");
Console.WriteLine(blah); // doesn't print anything...
When I look into the locals it says variable 'blah' is equal to "".
What happened to the data?
Your C++ code is broken. You are returning a pointer to a local variable. It no longer exists after the function returns. This tends to work by accident in a C++ program but is strong Undefined Behavior. It cannot possibly work in an interop scenario, the pinvoke marshaler's use of the stack will overwrite the string.
A declaration that could work:
void function (const char* str, char* output, size_t outputLength)
Use a StringBuilder in the [DllImport] declaration for the output argument and pass an initialized one with sufficient Capacity.
I have the following FORTRAN:
SUBROUTINE MYSUB(MYPARAM)
!DEC$ ATTRIBUTES DLLEXPORT::SetPaths
CHARACTER*50 MYPARAM
WRITE(6, *) MYPARAM
END SUBROUTINE
Then I have the following in C#
class Program
{
static void Main(string[] args)
{
StringBuilder sb = new StringBuilder(50);
sb.Append(#"something");
MYSUB(sb);
Console.ReadLine();
}
[DllImport(#"myCode.dll", EntryPoint = "MYSUB")]
public static extern void MYSUB(StringBuilder input);
}
However, the WRITE in my FORTRAN shows a bunch of junk after "something." Looks like the string terminator is not being honored. Help!
Strings are the trickiest datatype to interchange between different languages.
The basic Fortran string is fixed length, padded on the end with blanks. (Fortran now has variable length strings, but those would be harder to interchange.) The intrinsic "trim" is provided to suppress trailing blanks; "len_trim" to provide the length less trailing blanks.
C flags the end of a string with a null character.
I don't know how C# handles strings -- an internal variable for the length?? a terminator??
But Fortran isn't going to understand C#'s representation, it will just see the full length of the string, as declared, including, in this case, uninitialized memory. The best solution is probably to initialize the remainder of the string to blanks in C#.
How to return List from c# method and Use the List return values in c++ can u give guidance how to do it??
I going like following way my complete scenario:
in c# DemoLib.cs
usng System;using System.Collections.Generic;
public interface IDiscover
{
void GetList1();
String GetList2();
List<string> GetList3();
};
namespace DemoLibrary
{
class DemoLib
{
public void GetList1()
{
Console.WriteLine(" I am from Void GetList()");
}
public string GetList2()
{
Console.WriteLine(" I am from string GetList()");
return "Stack OverFlow";
}
public List<string> GetList3()
{
List<string> li=new List<string>();
li.Add("India");
li.Add("America");
li.Add("London");
Console.WriteLine(" I am from List<string> GetList()");
return li;
}
Build successfully and created DemoLib.dll
I copy the DemoLib.dll to c:\DemoLib.dll and using regasm created Demolib.tlb in c:\
Now in Vc++ i Hav main()
DemoLibMain.vc++
#include<list>
#include<stdio.h>
void main()
{
HRESULT Hr=CoInitialize(NULL);
IDiscoverPtr Iptr(__uuidof(DemoLib));
Iptr->GetList1();
std::string str=Iptr->GetList2();
printf("%s",str); //dispalys "null" insted of "stack overflow" but when i debugging it
shows the value "stack overflow" at str.
Iptr->(Afraid since it doesnot give GetList3() )
when i forced to write Iptr->GetList3() it Display the error as
GetList3 is not a member of IDiscover;
I did it using the object type of c#
C# exported as COM
public void GetList(ref object list)
{
String[] dummy = { "1" };
Array.Resize(ref dummy, 3);
list = dummy;
}
Native C++
_variant_t list;
GetList(list);
const VARTYPE type (static_cast<VARTYPE>(list.vt & VT_TYPEMASK));
const bool isArray (0 != (list.vt & VT_ARRAY));
const bool isVector(0 != (list.vt & VT_VECTOR));
if(isArray) {
SAFEARRAY* buffer = src.parray;
...
}
Problem here is that a SAFEARRAY isn't accessible by vbscript. But its no problem for most other languages.
Same question yesterday:
How to return a collection of strings from C# to C++ via COM interop
(Short answer: List won't be included in the COM interface because it uses a generic type).
When you import a tlb in VC++ and compile it, an intermediate file is generated with extension .tlh in your intermediate directory (normally debug/release directory depending upon your current configuration) . This is sort of a proxy file and contains all the methods that you can call. Have a look at this file and this will give you the exact syntax of what should be passed.
As far as your method GetList2 is concerned, it is returning a simple string which translates to a BSTR in the generated tlb so you need to get the return value in a BSTR. For GetList3 since you are returning an array, therefore, most probably the return type in .tlh file will be a variant containing an array of BSTRs. The vt of the variant will most probably be VT_ARRAY|VT_BSTR. You can loop-through this array in the variant and get all the values one-by-one.
EDIT: As somebody else suggested that it is List<>, therefore, it won't get translated to a COM Type. IMO, your best option is to use ArrayList instead of using List<>
Another Edit: I just checked and ArrayList does not get translated to any of the COM Automation types. In this case your best bet is to use a simple string array, cast it to object and return that object from your method. This object will be converted to a variant containing a SafeArray of BSTRs. You will need to loop-through that safearray to get your data.