I've been assigned the task of upgrading an old interop dll into visualstudio2015 with the v140 toolset.
I'm getting an odd access violation now.
Exception thrown at 0x000007fef7d3d95c (mscoreei.dll) in HostApp.exe:
0xC0000005: Access violation reading location 0xffffffffffffffff
Did anything change in cpp/cli from v110 to v140?
I can tell the error occurs when the methods are defined in a separate cpp instead of inline in the Foo.h. This work-around isn't possible since this is where I need to call managed objects.
Here is my attempt at reconstructing the outline of the code:
shared header IFoo.h
#ifdef DLL_EXPORTS
#define __API__ __declspec(dllexport)
#else
#define __API__ __declspec(dllimport)
#endif
class IFoo
{
public:
__API__ static IFoo* Create();
virtual void DoSomething()=0;
};
Foo.h
#include IFoo.h
class Foo : public IFoo
{
public:
Foo() { } // if inline no crash
void DoSomething();
};
Foo.cpp
#include ManagedStuff.h
#include Foo.h
// Foo::Foo() { } // AV if not inline.
void Foo::DoSomething()
{
gcroot<ManagedClass^> stuff = gcnew ManagedClass();
.....
}
FooImpl.cpp
#include Foo.h
IFoo* IFoo::Create()
{
IFoo* f = new Foo(); // CRASH if ctor is NOT inline.
f->DoSomething(); //CRASH
return f;
}
Related
I have a c++ code that needs to be called in c# by a cli wrapper. I am stuck at the operator overloading part.
//this is my code
//Cppclass.h
#ifdef CPP_EXP_EXPORTS
#define CPP_EXP __declspec(dllexport)
#else
#define CPP_EXP __declspec(dllimport)
#endif
class CPP_EXP Cppclass;
CPP_EXP Cppclass operator-(Cppclass const &, Cppclass const &);
class CPP_EXP Cppclass
{
public:
friend CPP_EXP Cppclass operator-(Cppclass const &, Cppclass const &);
};
//this is my managed c++ code.
#include "Cppclass.h"
namespace Wrapper
{
public ref class cppwrapclass
{
public:
static cppwrapclass ^ operator-(cppwrapclass%A,cppwrapclass%B)
{
operator-(A.obj,B.obj);
return gcnew cppwrapclass();
}
private:
Cppclass *obj;
};
}
Its showing an intellisense error and not getting compiled.
You write a wrapper like this:
public ref class cppwrapclass
{
public:
cppwrapclass(Cppclass *obj)
: obj(obj)
{
}
~cppwrapclass()
{
this->!cppwrapclass();
}
!cppwrapclass()
{
if (obj)
{
delete obj;
obj = nullptr;
}
}
static cppwrapclass^ operator-(cppwrapclass% A, cppwrapclass% B)
{
return gcnew cppwrapclass(new Cppclass(*A.obj - *B.obj));
}
private:
Cppclass* obj;
};
A.obj is of type Cppclass*, therefore *A.obj is of type Cppclass, and *A.obj - *B.obj is a temporary Cppclass which needs to be moved to the heap in order to be referenced by the wrapper, hence the copy constructor call: new Cppclass(*A.obj - *B.obj).
The rest is the Dispose pattern and finalizer plumbing code.
We are currently migrating our sources who were developed under C++Builder 5 to the newer Embarcadero's XE5. Thinking ahead, we would like to write our unit tests under C++Builder5 which, ideally, would be fully functional after the migration with few maintenance to none.
My question is simple, though. Is it possible to use the same DUnit framework Embarcadero has on C++Builder 5? If so, can you please provide us with any hints?
Thank you.
DUnit can indeed be used on CppBuilder5.
To do so:
Get the source code for DUnit from here : http://sourceforge.net/projects/dunit/files/latest/download
Build DUNITRTL.lib using the following command lines, or you can make a .bat file and execute it from /dunit/src folder :
SET NDC6=C:\PROGRA~2\Borland\CBUILD~1
%NDC6%\bin\dcc32.exe Dunit.dpr /O..\objs /DBCB /M /H /W /JPHN -$d-l-n+p+r-s-t-w-y- %2 %3 %4
%NDC6%\bin\tlib.exe DUNITRTL.lib /P32 -+dunit.obj -+DunitAbout.obj -+DUnitMainForm.obj -+GUITestRunner.obj -+TestExtensions.obj -+TestFramework.obj -+TestModules.obj -+TextTestRunner.obj
Once done, making a test project becomes easy:
Create a VCL Form application.
Remove Unit1.cpp along its form from the project.
Add DUNITRTL.lib file we built into the project (Project > Add to Project).
Add /dunit/src paths to both library and include paths. (Project > Options > Folder/Conditions).
Go to Project1.cpp file and make sure it looks like this:
#include <vcl.h>
#pragma hdrstop
#include <GUITestRunner.hpp>
USERES("Project1.res");
USELIB("DUNITRTL.lib");
//---------------------------------------------------------------------------
WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
try
{
Application->Initialize();
Guitestrunner::RunRegisteredTests();
}
catch (Exception &exception)
{
Application->ShowException(&exception);
}
return 0;
}
Add a new unit to the project which will be used as a TestSuite.
MyTestCase.h
//---------------------------------------------------------------------------
#ifndef __TMYTESTCASE_H__
#define __TMYTESTCASE_H__
//---------------------------------------------------------------------------
#include <TestFramework.hpp>
class TMyTestCase : public TTestCase
{
public:
__fastcall virtual TMyTestCase(AnsiString name) : TTestCase(name) {}
virtual void __fastcall SetUp();
virtual void __fastcall TearDown();
__published:
void __fastcall MySuccessfulTest();
void __fastcall MyFailedTest();
};
#endif
MyTestCase.cpp
#include <vcl.h>
#pragma hdrstop
//---------------------------------------------------------------------------
#include "TMyTestCase.h"
//---------------------------------------------------------------------------
void __fastcall TMyTestCase::SetUp()
{}
void __fastcall TMyTestCase::TearDown()
{}
void __fastcall TMyTestCase::MySuccessfulTest()
{
int a = 1;
a = a + 1;
CheckEquals(2,a,"test adding");
}
void __fastcall TMyTestCase::MyFailedTest()
{
int a = 1;
a = a + 2;
CheckEquals(2,a,"test adding");
}
static void registerTests()
{
_di_ITestSuite iSuite;
TTestSuite* testSuite = new TTestSuite("Testing TMyTestCase.h");
if (testSuite->GetInterface(__uuidof(ITestSuite), &iSuite))
{
testSuite->AddTests(__classid(TMyTestCase));
Testframework::RegisterTest(iSuite);
}
else
{
delete testSuite;
}
}
#pragma startup registerTests 33
#pragma package(smart_init)
The project can be compiled and ran. The tests should be executed without a hitch.
I'm attempting to make some of our IJW wrappers between our C++ and our .NET code easier to use.
The following is a test situation with our wrapper platform:
// NativeWrapper.h
#pragma once
template <class T>
public ref class NativeWrapper
{
public:
// ...
operator T* ();
operator T& ();
// ...
};
// Test.h
#pragma once
class Test { /* ... */ };
#pragma make_public( Test )
// TestN.h
#pragma once
#include "NativeWrapper.h"
#include "Test.h"
public ref class TestN : public NativeWrapper<Test>
{ /* ... */ };
// main.cpp
#include "TestN.h"
void f(const Test&) { /* ... */ }
int main()
{
TestN^ t = gcnew TestN();
f(t);
return 0;
}
The implicit cast from TestN^ to const Test& via the user-defined operator Test&() works just fine, as I had expected.
However, when I try this in my production code, something is different. It cannot find the proper conversion. I must do the following:
f((Test&)t);
I haven't been able to identify any fundamental differences between what I'm doing in my test and what I'm doing in the main codebase. The NativeWrapper template is identical.
What types of situations might cause the compiler to fail to use a non-const conversion operator when the only acceptable type is the const reference type?
Thanks.
I try to create a DLL in Embarcadero C++ Builder XE3, and use it in a test-project in the same environment.
I take example on a tutorial which code does not give a good result for me (!) : http://docwiki.embarcadero.com/RADStudio/XE3/en/Tutorial:_Using_Dynamic_Linked_Libraries_in_C%2B%2BBuilder_Applications
Here is the content of my DLL :
BaseAuth.h file :
#ifndef BaseAuthH
#define BaseAuthH
#include <System.hpp>
class TBaseAuth
{
public:
virtual void TestMessage() = 0;
};
#endif // BaseAuthH
Auth.h file :
//---------------------------------------------------------------------------
#ifndef AuthH
#define AuthH
//---------------------------------------------------------------------------
#include "BaseAuth.h"
class TAuth : public TBaseAuth
{
public:
TAuth();
~TAuth();
void TestMessage();
};
#endif
Auth.cpp file :
//---------------------------------------------------------------------------
#pragma hdrstop
#include "Auth.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
TAuth::TAuth()
{
}
TAuth::~TAuth()
{
}
void TAuth::TestMessage()
{
MessageBox(0, "Test message", "Test", MB_OK);
}
and File1.cpp :
#pragma hdrstop
#pragma argsused
#include "Auth.h"
extern "C" __declspec(dllexport) void* __stdcall GetClassInstance()
{
return static_cast<void*>(new TAuth());
}
extern "C" int _libmain(unsigned long reason)
{
return 1;
}
Now in the test application I have :
the same BaseAuth.h file
a form with a Button :
Test_DLLAuthOrga.h :
#ifndef Test_DLLAuthOrgaH
#define Test_DLLAuthOrgaH
//---------------------------------------------------------------------------
#include <System.Classes.hpp>
#include <Vcl.Controls.hpp>
#include <Vcl.StdCtrls.hpp>
#include <Vcl.Forms.hpp>
//---------------------------------------------------------------------------
#include "BaseAuth.h"
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published: // Composants gérés par l'EDI
TButton *Button2;
void __fastcall Button2Click(TObject *Sender);
private: // Déclarations utilisateur
TBaseAuth *mpAuth;
public: // Déclarations utilisateur
__fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif
Test_DLLAuthOrga.cpp :
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "Test_DLLAuthOrga.h"
#include "BaseAuth.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
const wchar_t* library = L"DLLAuthOrga.dll";
extern "C" __declspec(dllimport) void* __stdcall GetClassInstance();
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner)
{
HINSTANCE load;
try
{ load = LoadLibrary(library); }
catch(Exception &e)
{ ShowMessage(e.Message); }
if (load)
{
ShowMessage("Library Loaded!");
void *myFunc;
myFunc = (void *)GetProcAddress(load, "GetClassInstance");
mpAuth = (AuthParent*)myFunc;
}
else { ShowMessage("Library not loaded!"); }
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
if (mpAuth == NULL) return;
try { pRes = mpAuth->TestMessage(); }
catch(Exception &e) { ShowMessage(e.Message); return; }
}
The result is :
the pointer mpAuth has an adress.
But its methods have no adress, and when I call a simple method such as "void TestMessage()", it raises an access violation.
=> It first seemed to be a question of string compatibility (but between "C++ Builder XE3" and "C++ Builder XE3" I would expect the same string format to be used ?!) : Error calling DLL with Unicode Delphi
=> I found a similar issue but with C++ DLL into Delphi, not C++ DLL into C++ ... : Call C++ DLL in Delphi app
=> I tried using "HMODULE" instead of "HINSTANCE load;" : same result.
=> I tried without success using
mpAuth = static_cast<AuthParent*>(myFunc);
instead of :
mpAuth = (AuthParent*)myFunc;
=> I also tried replacing "__stdcall" by "__cdecl" or "" (removing) : the libray loads but GetProcAdress returns NULL.
=> What am I doing wrong in attempting to call the DLL's method "TestMessage()" ?
To correctly bind function from dll you should give it full definition, including calling convention, arguments, return type and __dllexport/__dllimport modifier. The easiest way to do it - using typedef so instead of typing (in Test_DLLAuthOrga.cpp)
void *myFunc;
myFunc = (void *)GetProcAddress(load, "GetClassInstance");
use
typedef __declspec(dllimport) void (__stdcall *MyFuncPointerType)(void);
MyFuncPointerType myFunc;
myFunc = (MyFuncPointerType)GetProcAddress(load, "GetClassInstance");
If you are using __cdecl convention you should also add underscore to the target function name
typedef __declspec(dllimport) void (__cdecl *MyFuncPointerType)(void);
MyFuncPointerType myFunc;
myFunc = (MyFuncPointerType)GetProcAddress(load, "_GetClassInstance");
you can also explicitly define AuthParent* as a return type for your factory function to get rid of uneccessary casts to void* and back to AuthParent* (in File1.cpp and Test_DLLAuthOrga.cpp) so, the final code snippet would look like this:
typedef __declspec(dllimport) AuthParent* (__cdecl *MyFuncPointerType)(void);
MyFuncPointerType myFunc;
myFunc = (MyFuncPointerType)GetProcAddress(load, "_GetClassInstance");
mpAuth = myFunc();
I have two classes: first generate position data (latitude and longitude), how I can access this data (variables latitude and longitute) in second class? becouse in second class I get crazy number(
Here are headers and classes:
first header:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QGeoPositionInfoSource>
#include <QGeoPositionInfo>
#include <QtCore/QPointer>
#include <QGeoSatelliteInfo>
#include <QGeoSatelliteInfoSource>
#include "gpsform.h"
QTM_USE_NAMESPACE
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
double latitude;
double longitude;
double altitude;
double speed;
public slots:
void positionUpdated(QGeoPositionInfo geoPositionInfo);
private:
Ui::MainWindow *ui;
QPointer<QGeoPositionInfoSource> locationDataSource;
private slots:
void on_pushButton_2_clicked();
void on_pushButton_4_clicked();
void startGPS();
void on_pushButton_clicked();
signals:
void updated();
};
#endif // MAINWINDOW_H
first class
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "quitdiallog.h"
#include <QGeoCoordinate>
#include <QDebug>
#include <QtGui/QMessageBox>
#include <QList>
#include "gpsform.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
setWindowTitle("Мой кОмпаС");
ui->setupUi(this);
startGPS();
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::startGPS()
{
// Obtain the location data source if it is not obtained already
if (!locationDataSource)
{
locationDataSource =
QGeoPositionInfoSource::createDefaultSource(this);
if (locationDataSource)
{
// Whenever the location data source signals that the current
// position is updated, the positionUpdated function is called.
QObject::connect(locationDataSource,
SIGNAL(positionUpdated(QGeoPositionInfo)),
this,
SLOT(positionUpdated(QGeoPositionInfo)));
// Start listening for position updates
locationDataSource->setUpdateInterval(100);
locationDataSource->setPreferredPositioningMethods(QGeoPositionInfoSource::SatellitePositioningMethods);
locationDataSource->startUpdates();
} else {
// Not able to obtain the location data source
// TODO: Error handling
}
} else {
// Start listening for position updates
locationDataSource->setUpdateInterval(5000);
locationDataSource->startUpdates();
}
}
void MainWindow::positionUpdated(QGeoPositionInfo geoPositionInfo)
{
//gpsform *gpf=new gpsform;
if (geoPositionInfo.isValid())
{
//locationDataSource->stopUpdates();
QGeoCoordinate geoCoordinate = geoPositionInfo.coordinate();
latitude = geoCoordinate.latitude();
longitude = geoCoordinate.longitude();
altitude=geoCoordinate.altitude();
ui->label->setNum(latitude);
ui->label_2->setNum(longitude);
/*if(QGeoPositionInfo::GroundSpeed)
{
speed=QGeoPositionInfo::GroundSpeed;
ui->label_4->setNum(speed);
}*/
emit updated();
//gpf->latitude=this->latitude;
//gpsform *gpf=new gpsform;
//gpf->show();
//gpf->latitude=latitude;
}
}
void MainWindow::on_pushButton_clicked()
{
/*ui->label_3->setNum(latitude);
qDebug()<<latitude<<" "<<longitude<<" "<<altitude;*/
gpsform *gps=new gpsform;
this->hide();
gps->show();
}
void MainWindow::on_pushButton_4_clicked()
{
QuitDiallog *qi=new QuitDiallog;
this->hide();
qi->show();
}
void MainWindow::on_pushButton_2_clicked()
{
ui->label_3->setNum(latitude);
}
second header
#ifndef GPSFORM_H
#define GPSFORM_H
#include <QWidget>
#include "mainwindow.h"
QTM_USE_NAMESPACE
namespace Ui {
class gpsform;
}
class gpsform : public QWidget
{
Q_OBJECT
public:
explicit gpsform(QWidget *parent = 0);
~gpsform();
double latitude;
private:
Ui::gpsform *ui;
private slots:
void on_pushButton_clicked();
void updatedata();
};
#endif // GPSFORM_H
second class:
#include "gpsform.h"
#include "ui_gpsform.h"
#include "mainwindow.h"
#include <QTimer>
#include <QDebug>
gpsform::gpsform(QWidget *parent) :
QWidget(parent),
ui(new Ui::gpsform)
{
ui->setupUi(this);
/*ui->label->setNum(mw->latitude);*/
/* QTimer * timer = new QTimer(this);
connect(timer,SIGNAL(timeout()),this,SLOT(update()));
timer->start(50);*/
/* MainWindow *mw = new MainWindow;
QObject::connect(mw,SIGNAL(updated()),this,SLOT(updatedata()));*/
}
gpsform::~gpsform()
{
delete ui;
}
void gpsform::updatedata()
{
/* MainWindow *mw = new MainWindow;
this->latitude=mw->latitude;
ui->label->setNum(mw->latitude);*/
}
void gpsform::on_pushButton_clicked()
{
MainWindow *mw = new MainWindow;
//latitude=mw->latitude;
qDebug()<<mw->latitude;
ui->label->setNum(latitude);
}
For example I want to see latitude in second class, by pressing button. In future I'll do this by Signal/slot to generate label text every time, the position is updated. But now I'll get crazy number. Help me please
MainWindow *mw = new MainWindow;
//latitude=mw->latitude;
qDebug()<<mw->latitude;
You create a new instance of MainWindow and directly access its latitude member. But was it initialized? I see the only place writing into this member in MainWindow is positionUpdated. Are you sure this method gets invoked before you access mw->latitude? You could easily verify that with another qDebug printout from positionUpdated, or by using the debugger.
To comment on the code style in general - it's not good practice to directly access members of other classes like this. One of the problems (as you've just encountered!) with this approach is that the class has no way to actually control the validity of its member. Even the most basic solution - having an accessor method instead of raw member access could do wonders for you here, because the accessor method could potentially check if the value has been computed (or even compute it lazily).