C++/CLI DLL linker issue - c++

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.

Related

C++ console app call C++/CLI static library

I'm playing around with the idea of creating a C++/CLI static library which exposes a number of static functions for the managed EventLogger functionality. My current Solution exists out of a /clr compiled C++ project and a C++ console app.
Visual Studio tells me that the ConsoleApp needs to be compiled with the /clr parameter but that's something I don't want to do. Is there some way of achieving what I'm trying to do here?
#### ConsoleApp.cpp
#include <iostream>
#include "../ClrEtwLogger/ClrEtwLogger.h"
int main()
{
clr_etw_logger::etw_logger::etw_log();
}
#### ClrEtwLogger.h (/clr), static library
#pragma once
#using <system.dll>
using namespace System;
using namespace Diagnostics;
namespace clr_etw_logger
{
public ref class etw_logger
{
public:
static void etw_log()
{
const auto source = gcnew String("dotNet ClrEtwLogger");
const auto log = gcnew String("EtwLogger");
const auto event = gcnew String("Sample Event 1");
if (!EventLog::SourceExists(source))
{
EventLog::CreateEventSource(source, log);
}
EventLog::WriteEntry(source, event);
EventLog::WriteEntry(source, event, EventLogEntryType::Warning, 234);
}
};
}

C++ DLL not exporting functions

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!

Exposing application API and data to DLL

I have a dll plugin ,myDLL.cpp, which has the following code:
#include "myDLL.h"
#include "MainApp.h"
class A{
public:
// NOTE: SomeType is defined in main application code.
A(SomeType* data) : m_data{data}
void MemberFunc(){
// NOTE: do_something should access data in main application.
m_data->do_something();
}
private:
SomeType* m_data;
};
// exported function
A* createInstance(SomeType* data){
return new A(data);
}
In the main application I have:
stc::vector<int> IntArray;
class SomeType{
SomeType(){
IntArray.resize(1000);
}
void do_something(){
// manipulate IntArray's contents.
IntArray[rand() % IntArray.size()] = rand();
}
};
typedef A*(_createInstance)(SomeType*);
void main(){
// Load the Dll and get createInstance()
_createInstance createInstance = LoadMyDLL();
SomeType someType;
A* a = createInstance(&someType);
a->MemberFunc();
// Free the instance of A and unload the DLL.
UnloadMyDLL(a);
}
The dll code now can use the API of the main application, but it can't access the right data. When I put a break point at m_data->do_something(); and enter the method call, then I see that IntArray is empty. What am I doing wrong and how do I solve the problem?
I could succesfully run your example without experiencing your problem:
I assumed that in your headers there are only the class definitions and not definition of its member functions
So I buid a DLL project. But it failed producing the dll because of the missing do_something() function. Normal, because with your architecture it should be defined in the application not in the DLL ! I could solve the problem by making do_something() virtual.
Then I build the application. I first chose for simplicity and linked the application with the DLL (no loading issues). Unfortunately it didn't find either MemberFunc() nor createInstance(). I could solve this by exporting the DLL entry.
FInally I updated the application, to have a dynamic load of the library. For avoiding unnecessary hassle of having to find back MemberFunc(), I made it virtual as well.
In all the tests above, I had absolutely no problem. IntArray was always correct. In debug mode I could see it with the expected content, as soon as it entered the scope.
My conclusion, from these tests and looking at your snippet (expecially with doSomething not being virtual): your problem is probably that have defined SomeType class with functions and eventually IntArray in Main.h.
If this is the case, your DLL refers to its own copy of these elements and not as you think to those in main ! THis explains why you don't see the expected values !
Solution:
File MainApp.h:
class SomeType{
public:
SomeType();
virtual void do_something();
};
File MyDLL.h:
#ifdef MYDLL_EXPORTS
#define MYDLL_API __declspec(dllexport)
#else
#define MYDLL_API __declspec(dllimport)
#endif
class A {
public:
A(SomeType* data);
virtual void MemberFunc(); // access through vtable. No need to link
private:
SomeType* m_data;
};
extern "C" { // use unmangled name for easo of use of dynamic loaded DLL
MYDLL_API A* createInstance(SomeType* data);
};
File MyDLL.cpp:
#define MYDLL_EXPORTS
#include "MainApp.h"
#include "MyDLL.h"
A::A(SomeType* data) : m_data{ data } {}
void A::MemberFunc(){ m_data->do_something(); }
extern "C" {
MYDLL_API A* cdecl createInstance(SomeType* data){ return new A(data); }
}
File main.cpp:
#include <Windows.h>
#include <iostream>
#include <vector>
#include "MainApp.h"
#include "MyDLL.h"
using namespace std;
vector<int> IntArray;
SomeType::SomeType(){
IntArray.resize(1000);
IntArray[0] = 1; IntArray[1] = 101; IntArray[2] = 10101;
}
void SomeType::do_something(){
for (int i = 0; i < 4; i++) // read
cout << IntArray[i] << endl;
IntArray[3] = 2702; // write
}
int main(int ac, char**av)
{
HINSTANCE LoadMe = LoadLibrary(L"MyDLL.dll");
if(LoadMe != 0)
cout << "DLL Library successfully loaded!\n";
else throw exception("DLL library failed to load!\n");
typedef A*(*_createInstance)(SomeType*);
_createInstance fcreateInstance = (_createInstance) GetProcAddress(LoadMe, "createInstance");
if (fcreateInstance)
cout << "DLL function found !\n";
else throw exception("Function not found in DLL!\n");
SomeType someType;
A* a = fcreateInstance(&someType);
a->MemberFunc();
cin.get();
}

failed to run unmanaged cpp unit test for managed code

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.

Compiling BADA simple helloworld program from command prompt and getting errors

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).