I have the following C# code(sample.dll).
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Sample
{
public class Worker
{
/// <summary>
/// Callback method signature to send array of values.
/// </summary>
/// <param name="values">The values.</param>
public delegate void FloatValuesReady(float[] values);
/// <summary>
/// Occurs when [float values are ready].
/// </summary>
public event FloatValuesReady ReadFloatValues;
/// <summary>
/// Sums the specified i.
/// </summary>
/// <param name="i">The i.</param>
/// <param name="j">The j.</param>
/// <returns></returns>
public int Sum(int i, int j)
{
return i + j;
}
/// <summary>
/// Gets the Student object.
/// </summary>
/// <returns></returns>
public ManagedStudent GetStudent()
{
Console.WriteLine("Enter a Name:");
string text = Console.ReadLine();
if (text != string.Empty)
{
return new ManagedStudent { Name = text };
}
return new ManagedStudent { Name = "NoName" };
}
/// <summary>
/// Read some float values from Console and give it back to the caller using ReadFloatValues callback.
/// So Caller should add event handler to ReadFloatValues event.
/// </summary>
public void GetSomeFloatValues()
{
List<float> values = new List<float>();
Console.WriteLine("Enter 4 valid float values for the Native App");
while (values.Count < 4)
{
string valueText = Console.ReadLine();
float value;
if(float.TryParse(valueText, out value))
{
values.Add(value);
}
}
if (this.ReadFloatValues != null)
{
this.ReadFloatValues(values.ToArray());
}
}
}
/// <summary>
/// A Managed Class
/// </summary>
public class ManagedStudent
{
public string Name { get; set; }
}
}
I have created C++ wrapper for the above sample.dll to acess the managed code in unmanaged code(wrapper.dll). Below is the code for SampleWrapper.h,SampleWrapper.cpp,NativeInterface.h.
/*Sample Wrapper.h*/
using namespace System;
using namespace Sample;
namespace Wrapper
{
public ref class SampleWrapper
{
/* Private Constructor to achieve signle ton*/
SampleWrapper(void)
{
workerObj = gcnew Worker();
workerObj->ReadFloatValues += gcnew Worker::FloatValuesReady(this, &Wrapper::SampleWrapper::FloatArrayReadyMethod);
}
public:
Worker ^ workerObj;
/* Static reference to single ton instace.
In order to use applications built in C.*/
static SampleWrapper ^ Instance = gcnew SampleWrapper();
void FloatArrayReadyMethod(array<float> ^ values);
};
}
/*NativeInterface.cpp/*
#ifdef __cplusplus
extern "C"
{
#endif
/* Un managed type definition of student equalent to the Managed*/
typedef struct
{
char* name;
}UnManagedStudent;
/* A simple interface using the premitive types. Accepts 2 paramters and retun*/
__declspec(dllexport) int SumFromCSharp(int i, int j);
/* An interface to get the Student Information in a Structure.
This function calls the C# class method and gets the managed Student Object
and converts to unmanged student*/
__declspec(dllexport) UnManagedStudent GetStudent();
/* Function pointer to a native function to achieve call back*/
typedef void (*GetFloatArrayCallback) (float values[], int length);
/* An interface that makes call to C# to read some float values.
The C# worker respond back in event call back.
In order to pass the information back to the native app
it should give the callback pointer*/
__declspec(dllexport) void GetFloatArrayFromCSharp(GetFloatArrayCallback cb);
#ifdef __cplusplus
}
#endif
/*sampleWrapper.cpp*/
#include "SampleWrapper.h"
#include "NativeInterface.h"
using namespace Sample;
namespace Wrapper
{
#ifdef __cplusplus
extern "C"
{
#endif
void copyManagedStringToCharPointer(char target[], System::String ^ inputString)
{
int maxSize = inputString->Length;
if ( maxSize > 0)
{
for (int index = 0; index < maxSize; ++index )
{
target[index] = (char)inputString->default[index];
}
target[maxSize] = '\0';
}
}
void copyManagedFloatToUnfloatArray(float target[], array<float> ^ values)
{
int maxSize = values->Length;
if ( maxSize > 0)
{
for (int index = 0; index < maxSize; index++ )
{
target[index] = (float)values[index];
}
}
}
__declspec(dllexport) int SumFromCSharp(int i, int j)
{
Worker ^ worker = SampleWrapper::Instance->workerObj;
return worker->Sum(i,j);
}
__declspec(dllexport) UnManagedStudent GetStudent()
{
Worker ^ worker = SampleWrapper::Instance->workerObj;
ManagedStudent ^ studentObj = worker->GetStudent();
String ^ mName = studentObj->Name;
UnManagedStudent studentStruct;
studentStruct.name = new char[mName->Length];
copyManagedStringToCharPointer(studentStruct.name,mName);
return studentStruct;
}
GetFloatArrayCallback floatArrayCallback;
__declspec(dllexport) void GetFloatArrayFromCSharp(GetFloatArrayCallback cb)
{
floatArrayCallback = cb;
Worker ^ worker = SampleWrapper::Instance->workerObj;
worker->GetSomeFloatValues();
}
void SampleWrapper::FloatArrayReadyMethod(array<float> ^ values)
{
float *nativeValues = new float[values->Length];
copyManagedFloatToUnfloatArray(nativeValues, values);
floatArrayCallback(nativeValues, values->Length);
}
#ifdef __cplusplus
}
#endif
}
then created small application which will access managed code through unamanaged code like below(native.dll).
#include "DemoNativeDll.h"
#include "NativeInterface.h"
using namespace nsNativeDll;
CDemoNativeDll::CDemoNativeDll(void)
{
}
CDemoNativeDll::~CDemoNativeDll(void)
{
}
int CDemoNativeDll::GetSum(int value1, int value2)
{
return SumFromCSharp(value1,value2);
}
Then i have created unit case for Native.dll as below
#include "stdafx.h"
#include "CppUnitTest.h"
#include "..\NativeDll\DemoNativeDll.h"
#include "..\Wrapper\NativeInterface.h"
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
using namespace nsNativeDll;
namespace UnitTest1
{
TEST_CLASS(UnitTest1)
{
public:
TEST_METHOD(TestMethod1)
{
CDemoNativeDll* obj = new CDemoNativeDll();
int sum = obj->GetSum(10,15);
Assert::AreEqual(sum,15);
// TODO: Your test code here
}
};
}
I am getting the EEFileLoadException exception. How to reslove this exception. I am using Visual Studio 2012 cppunit.
__declspec(dllexport) void GetFloatArrayFromCSharp(GetFloatArrayCallback cb);
Using __declspec(dllexport) is a simple way to expose managed code to native code. But it is too simple, there is no reasonable way to detect any mishap in the managed code. Which likes throwing exceptions when anything goes wrong. All you can see is an "it didn't work" exception without any way to find out anything about the managed exception. No exception message, no access to the Holy Stack Trace.
You are getting a preview of how hard it is going to be to support your customer. Who will call you and tell you "it did not work". Nothing you can do to help unless you can get a debugger running on his machine. That tends to be hard to come by.
Well, you have one on yours, you can at least use it to diagnose the unit test failure. Project + Properties, Debugging, change the Debugger Type setting from "Auto" to "Mixed". That enables the managed debugging engine, you can now look at the exception details. Usually a bog-standard one, "File not found" is a very common mishap. Especially since the CLR has no good reason to look for the managed assembly in the directory where it is stored right now, configuring the primary appdomain is something else you can't do. You probably have to put it in the GAC or move the cppunit test runner so it is in the same directory as your DLLs. Fuslogvw.exe is a good utility to diagnose assembly resolution problems, it shows you where the CLR looked for the assembly and what config it uses.
But feel free to put this unit test result into the "major fail" category. Consider getting ahead with COM interop or CLR hosting, they both give you an HRESULT error code and support IErrorInfo to obtain the exception message.
Related
I'm developing a C++ library for export as a DLL in Visual Studio 2013 (C++ 11), and I'm running into a challenge where the library, once imported as an external dependency by another program, contains the classes that I wanted, but none of the functions of the classes are included.
View of classes within external dependency
I have included one particular class that is supposed to be part of this DLL export.
Here is my RegressionTrainer class header file:
#ifndef REGRESSION_TRAINER_H
#define REGRESSION_TRAINER_H
#include "MachineLearning.h"
#pragma once
#ifndef DLL_EXPORT
#define DLL_EXPORT __declspec(dllexport)
#endif
using namespace MachineLearning;
namespace MachineLearningTraining{
public class RegressionTrainer{
public:
DLL_EXPORT RegressionTrainer();
virtual DLL_EXPORT ~RegressionTrainer();
std::vector<sample_type> DLL_EXPORT getInputData();
std::vector<double> DLL_EXPORT getAugmentedOutputs();
std::vector<double> DLL_EXPORT getNonAugmentedOutputs();
protected:
pugi::xml_parse_result DLL_EXPORT setDataFile(pugi::xml_document &doc, char* file_name);
void DLL_EXPORT setDataFolder(char* folder_name);
std::vector<char*> DLL_EXPORT findDataFiles();
char* data_folder;
std::vector<char*> file_names;
std::vector<sample_type> input_data;
/*
sample_type m;
m(0, 0) = 14.86;
m(1, 0) = 0.24;
*/
std::vector<double> augmented_outputs;
std::vector<double> non_augmented_outputs;
pugi::xml_parse_result result;
void DLL_EXPORT setInputData();
void DLL_EXPORT setAugmentedOutputs();
void DLL_EXPORT setNonAugmentedOutputs();
virtual int DLL_EXPORT trainAugmentedModel();
virtual int DLL_EXPORT trainNonAugmentedModel();
};
}
#endif
Here are the contents of MachineLearning.h:
#include <vector>
#include <iostream>
#include <exception>
#include <fstream>
#include <string>
#include <dlib/svm.h>
#include "pugixml.hpp"
namespace MachineLearning{
// Here we declare that our samples will be 2 dimensional column vectors.
typedef dlib::matrix<double, 3, 1> sample_type;
// Now we are making a typedef for the kind of kernel we want to use. I picked the
// radial basis kernel because it only has one parameter and generally gives good
// results without much fiddling.
typedef dlib::radial_basis_kernel<sample_type> kernel_type;
}
And here is my RegressionTrainer.cpp file:
#include "Stdafx.h"
#include "RegressionTrainer.h"
#include "dirent.h"
using namespace std;
using namespace dlib;
using namespace MachineLearning;
namespace MachineLearningTraining{
RegressionTrainer::RegressionTrainer(){
file_names = findDataFiles();
}
RegressionTrainer::~RegressionTrainer(){
}
pugi::xml_parse_result RegressionTrainer::setDataFile(pugi::xml_document &doc, char *file_name){
return doc.load_file(file_name);
}
void RegressionTrainer::setDataFolder(char *folder_name){
data_folder = folder_name;
}
std::vector<char*> RegressionTrainer::findDataFiles(){
DIR *dir;
struct dirent *ent;
std::vector<char*> file_names;
if ((dir = opendir(data_folder)) != NULL) {
/* print all the files and directories within directory */
while ((ent = readdir(dir)) != NULL) {
file_names.push_back(ent->d_name);
}
closedir(dir);
}
else {
/* could not open directory */
perror("Could not open directory");
}
return file_names;
}
std::vector<sample_type> RegressionTrainer::getInputData(){
return input_data;
}
std::vector<double> RegressionTrainer::getAugmentedOutputs(){
return augmented_outputs;
}
std::vector<double> RegressionTrainer::getNonAugmentedOutputs(){
return non_augmented_outputs;
}
void RegressionTrainer::setInputData(){
pugi::xml_document doc;
for (unsigned i = 0; i < file_names.size(); i++){
setDataFile(doc, file_names[i]);
std::cout << "Load result: " << result.description() << endl;
pugi::xml_node measures = doc.child("case").child("measures");
sample_type m;
int count = 0;
for (pugi::xml_node measure = measures.first_child(); measure; measure = measure.next_sibling()){
m(count, 0) = measure.text().as_double();
count++;
}
input_data.push_back(m);
}
}
void RegressionTrainer::setAugmentedOutputs(){
pugi::xml_document doc;
for (unsigned i = 0; i < file_names.size(); i++){
setDataFile(doc, file_names[i]);
std::cout << "Load result: " << result.description() << endl;
pugi::xml_node output = doc.child("case").child("studyresults").child("averageangledeviation");
augmented_outputs.push_back(output.text().as_double());
}
}
void RegressionTrainer::setNonAugmentedOutputs(){
pugi::xml_document doc;
for (unsigned i = 0; i < file_names.size(); i++){
setDataFile(doc, file_names[i]);
std::cout << "Load result: " << result.description() << endl;
pugi::xml_node output = doc.child("case").child("studyresults").child("averageangledeviationAR");
augmented_outputs.push_back(output.text().as_double());
}
}
int RegressionTrainer::trainAugmentedModel(){
return 0;
}
int RegressionTrainer::trainNonAugmentedModel(){
return 0;
}
}
Would welcome any thoughts!
Your code is confusing:
public class RegressionTrainer
Is this C++ or C#? Some other part of your code clearly shows it is C++. Therefore you must always put exact (or syntactically same) code.
Back to your problem, you cannot export members of a class. You must export entire class from a DLL.
Then issues start to begin. For one, you must expose (export or not, doesn't matter) all types the class uses (pugi::xml_parse_result for example). Then, you need to take care of different compilers versions (Even VC2015, various versions, debug/release, compiler settings etc). For example a vector on VC2015 debug build would be different from a release version. Service pack would complicate the problem.
In short: Don't export entire class having data-members. Even if entire data is private, you will need to export entire class, so that client (consumer of DLL) can compile and link (to code) properly.
So, what's the solution?
Well, just export a helper class:
class DLL_EXPORT RegressionTrainerHelper
{
RegressionTrainer* pCoreClass;
};
Expose (export) all required methods (for the client) from this class. You'd just need to forward the calls from helper to real class.
Now, you might wonder, you will need to export the underlying type RegressionTrainer, and you are back to same problem.
Well, yes and no. If this helper class is being compiled within DLL, RegressionTrainer would be real thing. If not, just:
typedef int RegressionTrainer;
Pointer of anytype is of same size (32-bit or 64-bit). Hence the size of entire exported class would always match the size in DLL and in EXE.
EDIT
For example, there is a XML parser class, ParseXML, but it uses comlpex data members, you have a method Parse.
class ParseXML
{
// complex data members
// some private OR public datamembers and functions, you dont want/need to export
public:
void Parse(const char*); // or something different
};
You would want to export only Parse, through helper class.
class DLL_EXPORT Exported_ParseXML
{
private:
ParseXML* pCoreXMLInstance;
public:
Exported_ParseXML()
{
// implementation in CPP
pCoreXMLInstance = new ParseXML();
}
// The function!
void Parse(const char* pData)
{
// FORWARD
pCoreXMLInstance->Parse(pData);
}
The client will simply use the exported class:
Exported_ParseXML parser;
parser.Parse("XML-data");
The server compiler (DLL) would see ParseXML as real class type. But the client (EXE, or other DLL), would need see ParseXML as int. You got to figure out this!
Q: Can I make a native client that consumes a .NET COM component, using reg free COM?
I.e. Can I make a C++ application with COM info in its manifest, instead of registering the component (COM 'stuff') in the registry?
Note: I started looking at reg free COM with this question in mind, and didn't find a quick answer via web search. When I found the answer, thought'd I'd post on SO in case helps anyone else searching...
Yes.
And here's an MSDN has an article (from 2005) that walks through a working example of:
...the registration-free activation of a .NET Framework-based component by native clients via COM interop. (11 printed pages)
https://msdn.microsoft.com/en-us/library/ms973915.aspx (accessed Jan 2015)
Daryn,
This blog entry explain you how to do it:
http://blogs.msdn.com/b/rodneyviana/archive/2015/08/24/pure-native-c-consuming-net-classes-without-com-registration.aspx
Basically you use a entry point to return the pointer to the interface (using DllExport Nuget for "export"):
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using RGiesecke.DllExport;
namespace ContosoCom
{
public static class Exports
{
[DllExport(CallingConvention = CallingConvention.Cdecl)]
public static void GetClass([Out] [MarshalAs((UnmanagedType.Interface))] out IMyClass pInterface)
{
pInterface = new MyClass();
}
}
[ComVisible(true)]
[System.Runtime.InteropServices.InterfaceTypeAttribute(System.Runtime.InteropServices.ComInterfaceType.InterfaceIsIUnknown)]
public interface IMyClass
{
void DisplayMessageBox([MarshalAs(UnmanagedType.BStr)] string Text);
void GetTicksAndDate([Out] out MyStruct Structure);
}
[ComVisible(true)]
[StructLayout(LayoutKind.Sequential, Pack = 8)]
public struct MyStruct
{
public long TicksOfNow;
public int Day;
public int Month;
public int Year;
}
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
[ComDefaultInterface(typeof(IMyClass))]
public class MyClass : IMyClass
{
public void DisplayMessageBox(string Text)
{
MessageBox.Show(Text);
}
public void GetTicksAndDate(out MyStruct Structure)
{
Structure.TicksOfNow = DateTime.Now.Ticks;
Structure.Day = DateTime.Now.Day;
Structure.Month = DateTime.Now.Month;
Structure.Year = DateTime.Now.Year;
}
}
}
And this is how the C++ code looks like:
#include "stdafx.h"
#import "..\..\bin\Debug\ContosoCom.tlb" auto_rename
using namespace ContosoCom;
typedef void(*pGetClass)(IMyClass **iMyClass);
int _tmain(int argc, _TCHAR* argv[])
{
pGetClass getClass = NULL;
IMyClass *mc = NULL;
HINSTANCE hDLL = 0;
// load the DLL
hDLL = ::LoadLibrary(L"ContosoCom.dll");
::CoInitialize(NULL);
if(!hDLL)
{
printf("ERROR: Unable to load library ContosoCom.dll\n");
return -1;
}
//
// TO DO: Add code here to get an instance of MyClass
//
getClass = (pGetClass)GetProcAddress(hDLL, "GetClass");
if(!getClass)
{
printf("ERROR: Unable to find entry for GetClass()\n");
return -1;
}
getClass(&mc);
// At this point we do not have how to get a pointer even with the libray loaded
// End of TO DO
if(!mc)
{
printf("ERROR: Unable to get a pointer for MyClass\n");
return -1;
}
mc->DisplayMessageBox("Hello World from native to .NET without registration");
MyStruct st;
ZeroMemory(&st, sizeof(MyStruct));
mc->GetTicksAndDate(&st);
printf("Ticks %I64i\n",st.TicksOfNow);
printf("Today is %i/%i/%i\n",st.Month,st.Day,st.Year);
printf("SUCCESS: Leaving gracefully\n");
return 0;
}
Ok so I am trying to create a runtime DLL for another application that can be called using GetProcAddress.
So I have built the DLL in VS2012 C++ Express (Header and source below) and it returns NULL even when dll is in same folder as exe. So this lead me to believe there was an issue with the dllmain function. So I began looking through MSDN and found the following link
http://msdn.microsoft.com/en-us/library/vstudio/988ye33t.aspx
It states that it is already taken care for me when I create dll template which I did following this MSDN link.
http://msdn.microsoft.com/en-us/library/ms235636%28v=vs.80%29.aspx
So I decided to try build the DLL in release mode (thinking something about this might be the issue) but I get a linker error saying entry point must be define. It builds fine in debug mode but not in release mode. I assume I am missing something simple here.
Error 1 error LNK1561: entry point must be defined C:\Users\ProRip\Documents\Visual Studio 2012\Projects\PhantomAdapter\AdapterDLL\LINK AdapterDLL
Header
#ifdef PHANTOMADAPTER_EXPORTS
#define PHANTOMADAPTER_API __declspec(dllexport)
#else
#define PHANTOMADAPTER_API __declspec(dllexport)
#endif
#using <mscorlib.dll>
#using <system.dll>
using namespace System;
using namespace System::IO::Ports;
using namespace System::Threading;
namespace PhantomAdapter
{
class PhantomAdapter
{
public:
static PHANTOMADAPTER_API int open();
static PHANTOMADAPTER_API int close();
//static PHANTOMADAPTER_API double init(bool );
//static PHANTOMADAPTER_API int noDevices();
static PHANTOMADAPTER_API int angle(double& angle);
static PHANTOMADAPTER_API int torque(double torque);
static PHANTOMADAPTER_API int ready();
};
public ref class SerialPort
{
private:
static System::IO::Ports::SerialPort^ p1;
public:
static int openPort();
static void closePort();
static int read();
static void send(Byte data);
static int check();
};
}
Source
#include "stdafx.h"
#include "stdafx.h"
#include "PhantomAdapter.h"
#include <stdexcept>
using namespace std;
namespace PhantomAdapter
{
int PhantomAdapter::open()
{
int flag=0;
flag=SerialPort::openPort();
return flag;
}
int PhantomAdapter::close()
{
SerialPort::closePort();
return 1;
}
int PhantomAdapter::angle(double& angle)
{
SerialPort::send(0x82);
angle = (SerialPort::read() * 255) + (SerialPort::read());
angle = angle*(6.2832/512);
return 1;
}
int PhantomAdapter::torque(double torque)
{
return 1;
}
int PhantomAdapter::ready()
{
return SerialPort::check();
}
int SerialPort::openPort()
{
bool check=0;
p1 = gcnew System::IO::Ports::SerialPort();
p1->BaudRate = 57600;
p1->PortName = "COM3";
if(p1->IsOpen)
return 0;
else {
p1->Open();
return 1;
}
}
int SerialPort::check()
{
array<String^>^ serialPorts = nullptr;
int flag=0;
serialPorts = p1->GetPortNames();
for each(String^ port in serialPorts)
{
if(port=="COM3")
flag=1;
}
return flag;
}
void SerialPort::closePort()
{
p1->Close();
}
void SerialPort::send(Byte data)
{
array<unsigned char>^ buffer = gcnew array<Byte>(1);
buffer[0] = (char)data;
p1->Write(buffer,0,1);
}
int SerialPort::read()
{
return p1->ReadByte();
}
}
In your VS project, can you check the settings:
1. Configuration Properties > General > Configuration Type? Is this set to .dll in case of Release mode?
2. Configuration Properties > Linker > System > SubSystem? Is this the same between the 2 modes?
You cannot call a .NET method (and C++/CLI methods are .NET methods) from native code via LoadLibrary/GetProcAddress. .NET methods and classes are not exported that way. You will always get a NULL pointer.
What you can do:
Write a COM component in .NET to reference in plain C++
Export normal, plain C++ methods that call .NET code only internally
Import a managed dll into a managed (the same C++/CLI or C# or any other .NET language) executable by using the Assembly class and it's methods.
I've got problems passing a member function of a C++ CLI class to a native C callback from a library.
To be precise its the Teamspeak 3 SDK.
You can pass a non member function using the following code without problem:
struct ClientUIFunctions funcs;
/* Initialize all callbacks with NULL */
memset(&funcs, 0, sizeof(struct ClientUIFunctions));
funcs.onConnectStatusChangeEvent = onConnectStatusChangeEvent;
But I need to pass a pointer to a member function, for example:
funcs.onConnectStatusChangeEvent = &MyClass::onConnectStatusChangeEvent;
Any other idea how to use the event within a non static member function is welcome to.
Thanks in advance!
This can only be done via a static class function because C doesn't know anything about the vtable or what object the function is part of. See below for a C++ and Managed C++ example
This could however be a work around, build a wrapper class which handles all the callbacks you need.
#include <string.h>
struct ClientUIFunctions
{
void (*onConnectStatusChangeEvent)(void);
};
class CCallback
{
public:
CCallback()
{
struct ClientUIFunctions funcs;
// register callbacks
my_instance = this;
/* Initialize all callbacks with NULL */
memset(&funcs, 0, sizeof(struct ClientUIFunctions));
funcs.onConnectStatusChangeEvent = sOnConnectStatusChangeEvent;
}
~CCallback()
{
// unregister callbacks
my_instance = NULL;
}
static void sOnConnectStatusChangeEvent(void)
{
if (my_instance)
my_instance->OnConnectStatusChangeEvent();
}
private:
static CCallback *my_instance;
void OnConnectStatusChangeEvent(void)
{
// real callback handler in the object
}
};
CCallback *CCallback::my_instance = NULL;
int main(int argc, char **argv)
{
CCallback *obj = new CCallback();
while (1)
{
// do other stuff
}
return 0;
}
Another possibility would be if the callback supports and void *args like void (*onConnectStatusChangeEvent)(void *args); which you can set from the plugin. You could set the object in this args space so in de sOnConnectStatusChangeEvent you would have something like this:
static void sOnConnectStatusChangeEvent(void *args)
{
if (args)
args->OnConnectStatusChangeEvent();
}
For managed C++ it should be something like this, however I can't get it to compile because it doesn't like the template brackets..
wrapper.h:
using namespace std;
using namespace System;
using namespace System::Runtime::InteropServices;
using namespace System::Text;
namespace Test
{
struct ClientUIFunctions
{
void (*onConnectStatusChangeEvent)(void);
};
public delegate void ConnectStatusChangeEvent(void);
public ref class ManagedObject
{
public:
// constructors
ManagedObject();
// destructor
~ManagedObject();
//finalizer
!ManagedObject();
event ConnectStatusChangeEvent^ OnConnectStatusChangeEvent {
void add(ConnectStatusChangeEvent^ callback) {
m_connectStatusChanged = static_cast<ConnectStatusChangeEvent^> (Delegate::Combine(m_connectStatusChanged, callback));
}
void remove(ConnectStatusChangeEvent^ callback) {
m_connectStatusChanged = static_cast<ConnectStatusChangeEvent^> (Delegate::Remove(m_connectStatusChanged, callback));
}
void raise(void) {
if (m_connectStatusChanged != nullptr) {
m_connectStatusChanged->Invoke();
}
}
}
private:
ConnectStatusChangeEvent^ m_connectStatusChanged;
};
class CCallback
{
public:
static void Initialize(ManagedObject^ obj);
static void DeInitialize(void);
private:
static void sOnConnectStatusChangeEvent(void);
static gcroot<ManagedObject^> m_objManagedObject;
};
}
wrapper.cpp:
#include <string.h>
#include "wrapper.h"
using namespace System;
using namespace Test;
void CCallback::Initialize(ManagedObject^ obj)
{
struct ClientUIFunctions funcs;
// register callbacks
m_objManagedObject = obj;
/* Initialize all callbacks with NULL */
memset(&funcs, 0, sizeof(struct ClientUIFunctions));
funcs.onConnectStatusChangeEvent = sOnConnectStatusChangeEvent;
}
void CCallback::DeInitialize(void)
{
// unregister callbacks
m_objManagedObject = nullptr;
}
void CCallback::sOnConnectStatusChangeEvent(void)
{
if (m_objManagedObject != nullptr)
m_objManagedObject->OnConnectStatusChangeEvent();
}
// constructors
ManagedObject::ManagedObject()
{
// you can't place the constructor in the header but just for the idea..
// create wrapper
CCallback::Initialize(this);
}
// destructor
ManagedObject::~ManagedObject()
{
this->!ManagedObject();
}
//finalizer
ManagedObject::!ManagedObject()
{
CCallback::DeInitialize();
}
gcroot<ManagedObject^> CCallback::m_objManagedObject = nullptr;
int main(array<System::String ^> ^args)
{
ManagedObject^ bla = gcnew ManagedObject();
while (1)
{
// do stuff
}
return 0;
}
I was trying to compile a simple Helloworld program for BADA but through command prompt.After compiling i ma getting the errors as
c:/Helloworld/src/Helloworld.cpp:12: error: prototype for 'Osp::App::Application HelloWorld::CreateInstance()' does not match any in class 'HelloWorld'
C:/Helloworld/inc/HelloWorld.h:21: error: candidate is: static Osp::App::Application* HelloWorld::CreateInstance()
could any body help that what needs to be done with it.
Thanks
Code for Helloworld.h
#ifndef __HELLOWORLD_H__
#define __HELLOWORLD_H__
#include <FBase.h>
#include <FGraphics.h>
#include <FLocales.h>
#include <FSystem.h>
#include <FApp.h>
using namespace Osp::Base;
using namespace Osp::Graphics;
using namespace Osp::Locales;
using namespace Osp::System;
using namespace Osp::App;
class HelloWorld :
public Application // must inherit from Application class
{
public:
// The application must have a factory method that creates an instance of the application.
static Application* CreateInstance(void);
public:
HelloWorld();
~HelloWorld();
public:
// The application must provide its name.
String GetAppName(void) const;
protected:
// The application must provide its ID.
AppId GetAppId(void) const;
AppSecret GetAppSecret(void) const;
public:
// This method is called when the application is initializing.
bool OnAppInitializing(AppRegistry& appRegistry);
// This method is called when the application is terminating.
bool OnAppTerminating(AppRegistry& appRegistry);
// Thie method is called when the application is brought to the foreground
void OnForeground(void);
// This method is called when the application is sent to the background.
void OnBackground(void);
// This method is called when the application has little available memory.
void OnLowMemory(void);
// This method is called when the device's battery level changes.
void OnBatteryLevelChanged(BatteryLevel batteryLevel);
};
#endif
Code for Helloworld.cpp
#include "HelloWorld.h"
HelloWorld::HelloWorld()
{
}
HelloWorld::~HelloWorld()
{
}
Application*
HelloWorld::CreateInstance(void)
{
// You can create the instance through another constructor.
return new HelloWorld();
}
String
HelloWorld::GetAppName(void) const
{
static String appName(L"HelloWorld");
return appName;
}
AppId
HelloWorld::GetAppId(void) const
{
static AppId appId(L"93bt1p123e");
return appId;
}
AppSecret
HelloWorld::GetAppSecret(void) const
{
static AppSecret appSecret(L"9C645DDBA19C71BAD1204DA4DAA7A0B9");
return appSecret;
}
bool
HelloWorld::OnAppInitializing(AppRegistry& appRegistry)
{
// TODO:
// Initialization including UI construction can be done here.
// Load the application's latest data, if necessary.
// If this method is successful, return true; otherwise, return false.
return true;
}
bool
HelloWorld::OnAppTerminating(AppRegistry& appRegistry)
{
// TODO:
// Deallocate or close any resources still alive.
// Save the application's current states, if applicable.
// If this method is successful, return true; otherwise, return false.
return true;
}
void
HelloWorld::OnForeground(void)
{
result r = E_SUCCESS;
Canvas* pCanvas = GetAppFrame()->GetCanvasN();
if(pCanvas == null)
return;
Font* pFont = new Font();
pFont->Construct(FONT_STYLE_PLAIN | FONT_STYLE_BOLD, 50);
pCanvas->SetFont(*pFont);
r = pCanvas->DrawText(Point(30, 30), GetAppName());
if (IsFailed(r))
{
AppLog("pCanvas->DrawText() failed.\n");
delete pCanvas;
return;
}
r = pCanvas->Show();
if (IsFailed(r))
{ AppLog("pCanvas->Show() failed.\n");
delete pCanvas;
return;
}
delete pCanvas;
}
void
HelloWorld::OnBackground(void)
{
}
void
HelloWorld::OnLowMemory(void)
{
// TODO:
// Deallocate as many resources as possible.
}
void
HelloWorld::OnBatteryLevelChanged(BatteryLevel batteryLevel)
{
// TODO:
// It is recommended that the application save its data,
// and terminate itself if the application consumes much battery
}
Code for Helloworldentry.cpp
/**
* OSP Application entry point(OspMain) introduced.
*/
#include <fapp.h>
#include "HelloWorld.h"
using namespace Osp::Base::Collection;
extern "C"
{
__declspec(dllexport) void OspMain(int hInstance, int argc, char *argv[]);
}
/**
* Entry function of OSP Application which is called by the operating system.
*/
extern "C" {
void OspMain(int hInstance, int argc, char *argv[])
{
AppLog("OspMain() Started. \n");
result r = E_SUCCESS;
ArrayList* pArgs = new ArrayList();
pArgs->Construct();
for (int i = 0; i < argc; i++)
{
String* pEachArg = new String(argv[i]);
pArgs->Add(*pEachArg);
}
r = Osp::App::Application::Execute(HelloWorld::CreateInstance, pArgs);
if (IsFailed(r))
{
AppLog("Application execution has failed.\n");
}
if (pArgs)
{
pArgs->RemoveAll(true);
delete pArgs;
}
AppLog("OspMain() Ended. \n");
}
}
The compiler is complaining that you have defined a method with this signature:
Osp::App::Application HelloWorld::CreateInstance()
whereas the HelloWorld class declares that its CreateInstance method has this signature:
Osp::App::Application* HelloWorld::CreateInstance()
Note the difference in the return type. The class definition says that the method with this name returns an Application pointer but you have implemented a method that returns an Application object.
In the future, please post code along with compiler errors. It's rarely possible to adequately explain compiler errors in isolation from the code that produced them. For example, I can't tell you which return type is correct in this case; I can only tell you that the return types don't match (which is exactly what the compiler already said).