how to use byte array as parameter when calling a WindowsPhoneRuntimeComponent? - c#

First of all hey guys! I would like to access 2 C++ functions from my windows phone app.
so i followed every step of this tutorial and i managed to call the function as the poster of the tutorial. Now i wanted to access my own functions so i created the class in the header and in the .cpp file and the project is building ok as long as my functions are not public. Means i cant access them.
public ref class Base64Encoding sealed
{
public:
char *EncodeData(char *data, int length, int *resultLength); //this doesnt compile
char *DecodeString(char *data, int *resultLength); //this compiles buts inaccessible
};
i get a return exception saying error C3992: signature of public member contains invalid type char.
I did some googling and as far as i understand i cannot send parameters of type char since its unmanaged code or something like that.
So what is the issue here? why i cannot pass parameters of type char?
Update
i followed robwirving suggestion and now the header looks like this.
public ref class Base64Encoding sealed
{
public : Platform::String^ EncodeData(String^ StringData);
public : Platform::String^ DecodeString(String^ StringData);
};
in order now to get char* data from the String^ StringData parameter i did in my .cpp
#include <string>
#include <iostream>
#include <msclr\marshal_cppstd.h>
using namespace Platform;
using namespace std;
String^ EncodeData(String^ StringData)
{
// base64 lookup table. this is the encoding table for all 64 possible values
static char *figures = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
msclr::interop::marshal_context context;
std::string s = context.marshal_as<std::string>(StringData);
char *data = new char[s.size() + 1];
int length = s.length;
int *resultLength = s.length;
data[s.size()] = 0;
/* bla bla some functions irrelevant*/
.
.
.
return StringFromAscIIChars(result);
}
static String^ StringFromAscIIChars(char* chars)
{
size_t newsize = strlen(chars) + 1;
wchar_t * wcstring = new wchar_t[newsize];
size_t convertedChars = 0;
mbstowcs_s(&convertedChars, wcstring, newsize, chars, _TRUNCATE);
String^ str = ref new Platform::String(wcstring);
delete[] wcstring;
return str;
}
but now i get 2 errors on building.
1: error C1114: WinRT does not support #using of a managed assembly
2: IntelliSense: an ordinary pointer to a C++/CX mapping ref class or interface class is not allowed

You said the answer right in your question. You can't use a char in a public function of a WinRT component. You can however use strings, I'd suggest changing your functions to the following:
public ref class Base64Encoding sealed
{
public:
Platform::String^ EncodeData(Platform::String^ data);
Platform::String^ DecodeString(Platform::String^ data);
};
In the definition of your encode/decode functions you can then convert your inputs from a Platform::String^ to a char array, call your original C++ function, then convert the return value back to a Platform::String^
I know this may seem like a lot of extra work, but it makes the interop a lot easier for the C# that will consume your WinRT component.
Update:
I think your additional errors probably come from the including msclr\marshal_cppstd.h and the way you are converting your Platform::String^ to a std::string.
Reference this post for how to convert from Platform::String^ to a char*: How to convert Platform::String to char*?

Related

Pass an array to a C# DLL from C++ DLL

I am trying to handle a C++ DLL which in turn connects to a C# DLL.
My problem is when I want to send or receive arrays between C++ and C# DLLs.
I have created the C# DLL and C++ DLL, so I can modified all the files. Within the C# DLL, I must necessarily have an array of string an another of double because that is where I make use of other functions unrelated to this question. (The declaration of the function that I have to use in C# DLL is Error SetMultipleChannelValues(string[] names,double[] values).
I am using CLI in Visual Studio 2015 to compile and generate all DLLs and projects.
Here it is the C# code that I have:
public static Error SetMultipleSignals(string[] signals, double[] values)
{
Error error = null;
error = workspace.SetMultipleChannelValues(signals, values);
if (error.IsError)
Console.WriteLine("[DLL] Error in SetMultipleChannelValues(). Code: " + error.Code + ". Message: " + error.Message);
return error;
}
Here it is the C++ code that I have:
bool setMultipleSignals(double* setSignal_values)
{
array<double, DEFAULT_SETSIGNAL_SIZE> values;
for (int index = 0 ; index < DEFAULT_SETSIGNAL_SIZE ;index++)
{
values[index] = *(setSignal_values + index);
}
if (!veristand_wrapper_cs::VeriStand_dll::SetMultipleSignals(setSignals_path, values)) // This is the call to C# function
return true;
else
return false;
}
Here it is the C++ header that I have:
#pragma once
#define VERISTAND_WRAPPER_CPP __declspec(dllexport)
#include <string>
#include <iostream>
#include <windows.h>
#include <array>
using namespace std;
using namespace System;
using System::Runtime::InteropServices::Marshal;
#using "veristand_wrapper_cs.dll" // This is the C# DLL
#define DEFAULT_SETSIGNAL_SIZE 100
extern "C" VERISTAND_WRAPPER_CPP bool setMultipleSignals(double* setSignal_values); // This function is called from API C++
I pass the double array with a pointer to double as a parameter from my C++ application, but I have to pass to C# DLL an array, so I have tried to build a whole array of double before pass it.
The array setSignals_path is created as a global array in this C++ DLL as array<String^, DEFAULT_SETSIGNAL_SIZE> setSignals_path;.
The problem is that the call of C# function provides me an error that says that I can not call the function with these arguments. It expects array<System::String^> ^signals, array<double> ^values and I am passing std::array<System::String^, 100Ui64>, std::array<double, 100Ui64>
The concept of my idea is simple. From the C++ API, pass a pointer to the array of doubles, so that the function of my DLL in C++ passes the array of doubles and strings to the DLL of C#, and it returns the array of modified doubles to be able to indicate to the API from C++ that the values ​​have been modified from that memory address.
Would anyone know how to approach this problem or how to do it in some other way to make it work?
It looks like you are using c++/cli. So you can actually use .Net types in native code.
In C++/cli you need to explicitly declare if a variable is a reference, that is what the ^ is for. Your arrays is declared without this, but your method needs a reference. You will also need to allocate your array on the managed heap. So your array should probably be declared as
array<double>^ values = gcnew array< double>(DEFAULT_SETSIGNAL_SIZE);
But it was a while ago I coded c++/cli, so I may have missed something. See How to use arrays in c++/cli. It also looks like you may be able to use ref new instead of gcnew
Use following. The size of array must be 100, or add another parameter before the string array indicating length. The string in code is a byte array terminating with '\0'. c++ must use a method that will read the null terminated string. A double is 8 bytes so the 100 double can be consecutive memory locations (800 bytes).
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct StringArray
{
public IntPtr[] unmanagedArray;
}
public struct DoubleArray
{
public double[] values;
}
public static Error SetMultipleSignals(IntPtr signals, IntPtr values)
{
const int DEFAULT_SETSIGNAL_SIZE = 100;
StringArray stringStruct = new StringArray();
stringStruct.unmanagedArray = new IntPtr[DEFAULT_SETSIGNAL_SIZE];
Marshal.PtrToStructure(signals, stringStruct);
string[] strArray = new string[DEFAULT_SETSIGNAL_SIZE];
for(int i = 0; i < DEFAULT_SETSIGNAL_SIZE; i++)
{
strArray[i] = Marshal.PtrToStringAnsi(stringStruct.unmanagedArray[i]);
}
DoubleArray dArray = new DoubleArray();
dArray.values = new double[DEFAULT_SETSIGNAL_SIZE];
dArray = Marshal.PtrToStructure<DoubleArray>(values);
return null;
Here is going the other way.
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct StringArray
{
public IntPtr[] unmanagedArray;
}
static void Main(string[] args)
{
string[] strArray = { "Message 1", "Message 2", "Message 3"};
StringArray stringStruct = new StringArray();
stringStruct.unmanagedArray = new IntPtr[strArray.Length];
for (int i = 0; i < strArray.Length; i++)
{
stringStruct.unmanagedArray[i] = Marshal.StringToBSTR(strArray[i]);
}
IntPtr unmanagedArray = Marshal.AllocHGlobal(Marshal.SizeOf(stringStruct));
Marshal.StructureToPtr(stringStruct, unmanagedArray, false);
}

Pass string like parameter to external method as char *

I'm creating Windows Runtime Component (c++) to use it later in windows phone 8.1 application (c#). My problem is preety simple but I cannot find any answer to it:
I need to create a method which take string/char */anything which is filepath as parameter and pass it to external method which takes char * as parameter.
I've tried with std::string, String^, char *. But I still get errors like such types are not supported (not in String^ case) or some other one.
Is there some simple answer which tells me how should I do this?
Code samples with errors
beginning
#include "pch.h"
#include "Class1.h"
using namespace Platform;
namespace WindowsRuntimeComponent2
{
public ref class Class1 sealed
{
various types:
int Foo(std::string param) {
cause: Foo': signature of public member contains native type 'std::basic_string<char,std::char_traits<char>,std::allocator<char>>' WindowsRuntimeComponent2 and few more containing same information about string's dependencies
int Foo(char * param) {
cause: 'Foo': signature of public member contains native type 'std::basic_string<char,std::char_traits<char>,std::allocator<char>>' WindowsRuntimeComponent2
int Foo(String^ photoPath) {
this one do not cause any errors but I don't know how to parse it to my char *.
Essentially here you just need to know how to convert from System.String^ to std::string, this can be done in C++/CLI mode as follows:
#include <msclr\marshal_cppstd.h>
//...
int Foo(String^ photoPath) {
std::string unmanaged_photoPath = msclr::interop::marshal_as<std::string>(photoPath);
// then convert to c-style string as normal using unmanaged_photoPath.c_str();
return 0;
}

C++/CLI wrapper memleak when calling managed lib from unmanaged

I have this error when calling managed code library from unmanaged code:
Run-Time Check Failure #2 - Stack around the variable 'BridgeObj' was corrupted.
The code I created is:
Managed lib:
using System;
namespace My.Name.Space
{
public class Sample
{
public int Request(string xml_input, out string xml_output)
{
xml_output = "Retun string.";
return 0;
}
}
}
Wrapper C++/CLI:
#include "stdafx.h"
#include <msclr\auto_gcroot.h>
#using "..\link\ManagedLib.dll"
using namespace System::Runtime::InteropServices; // Marshal
struct ManagedModul
{
public:
msclr::auto_gcroot<My::Name::Space::Sample^> SampleModul;
};
class __declspec(dllexport) Bridge
{
private:
ManagedModul _private;
public:
Bridge()
{
_private.SampleModul = gcnew My::Name::Space::Sample();
};
~Bridge()
{
}
int Request ( const char * xmlin, char ** xmlout )
{
System::String ^ps;
_private.SampleModul->Request(gcnew System::String(xmlin), ps);
* xmlout = (char*) (Marshal::StringToHGlobalAnsi(ps)).ToPointer();
return 0;
}
};
Sample usage:
#include "stdafx.h"
#include <Windows.h>
#pragma comment ( lib, "..\\link\\Wrapper.lib" )
class Bridge
{
public:
Bridge();
~Bridge();
int Request ( const char * xmlin, char ** xmlout );
};
int _tmain(int argc, _TCHAR* argv[])
{
Bridge BridgeObj;
char * buffer = NULL;
BridgeObj.Request("aaaaa", & buffer );
LocalFree ( buffer );
return 0;
}
class Bridge
{
public:
Bridge();
~Bridge();
int Request ( const char * xmlin, char ** xmlout );
};
This is a very, very bad practice. Instead of using a .h file that is used in both projects, you redeclared the Bridge class. And got it wrong, you missed the _private member. This always goes wrong. If not immediately then in a year from now when you modify the real Bridge class.
What happens next is pretty inevitable. The real class object is larger than what the compiler thinks when it compiles your redeclared class. So it doesn't reserve enough space on the stack to store the object. Inevitable, that will cause another variable on the stack to get whacked, overwritten whenever the real class assigns the _private member.
Buy Microsoft a cigar for building in this diagnostic, stack corruption like this is normally extraordinarily hard to diagnose. And use a .h file to declare Bridge.

How to send a struct by ref containing a string from C# to unmanaged C++ library

I need to send a struct from C# managed code to a C library. The C library will populate the values in the struct. I have been trying to pass the struct as a reference so that the C# code will get the updated data values.
This is an example C function in libshlib.so:
void sharedStruct(struct data* d)
{
d->number = calcSomething();
d->message = doSomething();
}
I can send individual parameters (int and StringBuilder) and the library code runs and returns new values to the C# code that called it.
But how can I create a struct in C# that contains both int and string and send it to the unmanaged code (C library) which will populate the values to be used back in the C# code?
The C struct might be like this:
struct data
{
int number;
char* message;
};
Right now I'm trying to establish the best way to manipulate data in the C library for use in C#. I am writing both pieces of code so I am flexible but right now I haven't been able to get it working.
If you want the struct to be populated by the C code, then you are probably looking for an out variable. Note that the mono runtime by default will use free() to deallocate any strings you pass in and you might have to take extra care with padding in structures (see the StructLayout stuff in msdn). For strings, further problems can be had with character sets.
Sample code:
Managed.cs:
using System;
using System.Runtime.InteropServices;
struct Data
{
public int number;
public string message;
}
class Managed
{
[DllImport("unmanaged")]
extern static void Foo(out Data data);
static void Main()
{
Data data;
Foo(out data);
Console.WriteLine("number = {0}, message = {1}", data.number, data.message);
}
}
unmanaged.c:
#include <string.h>
struct data
{
int number;
char* message;
};
void Foo(struct data* data)
{
data->number = 42;
data->message = strdup("Hello from unmanaged code!");
}
Test run:
$ mcs Managed.cs
$ gcc -shared -fPIC -o libunmanaged.so unmanaged.c
$ LD_LIBRARY_PATH=$PWD mono Managed.exe
number = 42, message = Hello from unmanaged code!

C++ managed wrapper called from C#

I have some native C++ code and I have then created a wrapper to go around it.
creator.h
#pragma once
#include "file.h"
#define EXPORT __declspec(dllexport)
class EXPORT creator{
public:
creator();
~creator();
bool function1(int);
bool startFunction1(char *in, char *cache, char *out, long, double);
};
wrapper.h
#pragma once
using namespace System;
namespace nativeWrapper
{
public ref class Wrapper{
public:
creator *c;
Wrapper();
~Wrapper();
bool wStartCreating(System::String ^_in, System:: ^_cache, System:: ^_out);
};
}
wrapper.cpp
Lets just go straight to the wStartCreating function
bool Wrapper::wStartCreating(System::String ^_in, System:: ^_cache, System:: ^_out)
{
char *in = marshaling(_in);
char *out = marshaling(_out);
char *cache = marshaling(_cache);
return c->startFunction(in, cache, out, 0.0, 0.0);
}
in the c# code:
[DllImport("wrapper.dll")]
private static extern bool wStartCreating(String _in, String cache, String _out);
Then, I want to call wStartCreating(String, String, String) but I get a dllnotfoundexception whenever I try to make the call. I tried to add the reference (by the way this is in Visual studio 2005) and in the build event I copied the dll over to the referenced folder. Need some help as to why this runtime error may be coming up.
Thanks
-Tommy

Categories

Resources