Updated:
I created a project that can 100% reproduce the problem.
1. DLL Project
I named this DLL project TestDLL
The most important codes:
AddC.h:
#pragma once
class CTestDLL;
#include <string>
using namespace std;
//use AddC somewhere inside the DLL internally
class AddC{
private:
string _fake_string_ = "_fake_string_";
void _fake_method_();
public:
void add2Result(CTestDLL* test);
};
AddC.cpp:
#include "pch.h"
#include "AddC.h"
#include "TestDLL.h"
void AddC::_fake_method_()
{
_fake_string_ = "_fake_string_in_fake_method_";
}
void AddC::add2Result(CTestDLL * test)
{
test->result += 1;
}
TestDLL.h:
#ifdef TESTDLL_EXPORTS
#define TESTDLL_API __declspec(dllexport)
#else
#define TESTDLL_API __declspec(dllimport)
#endif
#include "AddC.h"
class TESTDLL_API CTestDLL {
public:
CTestDLL(void);
void test();
int getResult();
private:
friend class AddC;
private:
AddC addc;
int result=0;
};
TestDLL.cpp:
#include "pch.h"
#include "framework.h"
#include "TestDLL.h"
CTestDLL::CTestDLL(){
result = 0;
return;
}
void CTestDLL::test(){
auto _this = this;
addc.add2Result(_this);
}
int CTestDLL::getResult(){
return this->result;
}
2. SimpleTestApplication Project
The TestApplication has only two code files.
dll.hpp:
#pragma once
class AddC{};
class CTestDLL {
public:
CTestDLL(void);
void test();
int getResult();
private:
int result = 0;
AddC addc;
};
app.cpp:
#include "dll.hpp"
#include <iostream>
using namespace std;
#pragma comment(lib, "TESTDLL.lib")
int main(){
CTestDLL test;
test.test();
test.test();
test.test();
cout << test.getResult() << endl;
return 0;
}
The testApplication compile and link successfully.
But,But.When the main function exits, the program throws an exception that the stack was corrupted.
The key point is 'fake string_' variable in AddC.
But I don't how to do.
Previous:
Like this on a dll project:
class B;
class C;
class __declspec(dllexport) A{
private:
friend class B;
friend class C;
}
And I compiled the dll project;
Use .dll,.lib and the following header file on a simple console project:
dll.hpp:
class __declspec(dllimport) A{
private:
friend class B;
friend class C;
}
The main func:
#include "dll.hpp"
#pragma comment(lib, "dll.lib")
int main(){
A a;
return 0;
}
It compiled successfully.But the program threw an exception when running:
Run-Time Check Failure #2 - Stack around the variable 'a' was corrupted.
Who can help me?
#selbie's suggestion regarding the factory function / method creating is the cleanest way to go (would prevent you from ending in situations like this), and design-wise it makes the most sense, but it's not mandatory (after all there are lots of .dlls out there exporting classes).
What jumped into my eye, was the fact that (in both scenarios) a header file was present in the application that was (somewhat) duplicating the (main) one in the .dll. Every .dll comes with its own header file(s), and those are the ones that should be used by its clients (apps or other dlls). They are both maintained by the dll creators, and when updates come, they are both in sync. Doing otherwise is not scalable (each app creating its own header file version), makes no sense, and I don't know why would anyone do something like that.
With this in mind, I recreated the environment (both scenarios), doing things the right way, and everything worked fine, then I worked my way to your (wrong) way, trying to identify the culprit.
Example (numbered the 2 scenarios: 00 and 01):
DllExport.h:
#pragma once
#if defined(_WIN32)
# if defined(DLL_STATIC)
# define DLL_EXPORT_API
# else
# if defined(DLL_EXPORTS)
# define DLL_EXPORT_API __declspec(dllexport)
# else
# define DLL_EXPORT_API __declspec(dllimport)
# endif
# endif
#else
# define DLL_EXPORT_API
#endif
Scenario 00:
AddC.hpp:
#pragma once
#include <string>
using namespace std;
class CTestDLL;
//use AddC somewhere inside the DLL internally
class AddC {
private:
string _fake_string_ = "_fake_string_";
void _fake_method_();
public:
void add2Result(CTestDLL *pTest);
};
TestDLL.hpp:
#pragma once
#include <DllExport.h>
#include <AddC.hpp>
class DLL_EXPORT_API CTestDLL {
public:
CTestDLL();
void test();
int getResult();
private:
friend class AddC;
private:
AddC addc;
int result = 0;
};
dll:
AddC.cpp:
#include "AddC.hpp"
#include "TestDLL.hpp"
void AddC::_fake_method_()
{
_fake_string_ = "_fake_string_in_fake_method_";
}
void AddC::add2Result(CTestDLL *pTest)
{
pTest->result += 1;
}
TestDLL.cpp:
#define DLL_EXPORTS
#include <TestDLL.hpp>
CTestDLL::CTestDLL()
{
result = 0;
return;
}
void CTestDLL::test()
{
auto _this = this;
addc.add2Result(_this);
}
int CTestDLL::getResult()
{
return this->result;
}
app:
dll.hpp:
#pragma once
#define _WRONG_INNER_CLASS
#if defined(_WRONG_INNER_CLASS)
class AddC {};
#else
# include <AddC.hpp>
#endif
#define _WRONG_IMPORT_DIRECTIVE
#if defined(_WRONG_IMPORT_DIRECTIVE)
# define CLASS_IMPORT_API
#else
# define CLASS_IMPORT_API __declspec(dllimport)
#endif
class CLASS_IMPORT_API CTestDLL {
public:
CTestDLL();
void test();
int getResult();
private:
int result = 0;
AddC addc;
};
main.cpp:
#include <iostream>
#include <cstdio>
#define _DUPLICATE_DLL_HEADER_FILE
#if defined (_DUPLICATE_DLL_HEADER_FILE)
# include <dll.hpp>
#else
# include <TestDll.hpp>
#endif
#pragma comment(lib, "dll.lib")
using std::cout;
using std::endl;
int main()
{
CTestDLL test;
test.test();
test.test();
test.test();
cout << test.getResult() << endl;
printf("Done.\n");
return 0;
}
Scenario 01:
dll.hpp:
#pragma once
#include <DllExport.h>
class DLL_EXPORT_API A {
private:
friend class B;
friend class C;
};
dll:
dll.cpp:
#define DLL_EXPORTS
class B;
class C;
#include <dll.hpp>
app:
AppDll.hpp:
#pragma once
#define CLASS_IMPORT_API __declspec(dllimport)
class CLASS_IMPORT_API A {
private:
friend class B;
friend class C;
};
main.cpp:
#include <cstdio>
#define _DUPLICATE_DLL_HEADER_FILE
#if defined (_DUPLICATE_DLL_HEADER_FILE)
# include "AppDll.hpp"
#else
# include <dll.hpp>
#endif
#pragma comment(lib, "dll.lib")
int main()
{
A a;
printf("Done.\n");
return 0;
}
Project structure:
Results:
Scenario 00:
There were 2 mistakes:
Class AddC redefinition
Missing import directive for CTestDll
In dll.hpp there are 2 _WRONG* macros. If both are commented, it will work
Scenario 01:
I couldn't reproduce the crash (with / without friends)
Note: Tested with VStudio 2022 and MSSDK 10.0.22000.0
I can use code like this:
// RendererDLL.h
#pragma once
class gfx::BaseObject;
class gfx::MeshObject;
namespace gfx {
extern "C" {
void __declspec(dllexport) InitGraphics(UINT width, UINT height, HWND hwnd);
void __declspec(dllexport) ReleaseGraphics();
}
}
I don't use a class for the return type and parameters. And this code works.
But I can't use code like this:
namespace gfx {
extern "C" {
void __declspec(dllexport) EnqueueObject(gfx::BaseObject* pBaseObj);
}
}
or
namespace gfx {
extern "C" {
__declspec(dllexport) gfx::MeshObject* CreateMeshObject();
}
}
This code uses classes for the return type or parameter type.
I get errors. First, at EnqueueObject():
linkage specification is incompatible with previous "gfx::EnqueueObject"
Second, at CreateMeshObject():
cannot overload functions distinguished by return type alone
// RendererDLL.cpp
#include "pch.h"
#include "RendererDLL.h"
#include "Graphics.h"
#include "BaseObject.h"
#include "MeshObject.h"
#include "RenderQueue.h"
void gfx::EnqueueObject(gfx::BaseObject* pBaseObj) {
gfx::RenderQueue::GetInstance().Enqueue(pBaseObj);
}
gfx::MeshObject* gfx::CreateMeshObject() {
return new gfx::MeshObject();
}
This code is in a .cpp file.
I think the first error occurs because of using a class for the return type or parameter type. But I don't know why.
Hi I'm sorry I've seen that there is a lot of "unresolved external symbol error" questions, and I've seen them but none of the answers that I found fixed my error.
I've tested 2 ways to compile the DLL and use the HelloWorld method from SerialPort class.
btw I'm using VS2019 community edition
Both ways are throwing the same error :
Error LNK2019 unresolved external symbol "public: void __thiscall SerialPort::HelloWorld(void)" (?HelloWorld#SerialPort##QAEXXZ) referenced in function _main Test DriverCore C:\Users\$USER\source\repos\Test DriverCore\Test DriverCore\Main.obj 1
To what I've understood it's a linker error and the name of the method that I'm using is unresolved (not found) but I have no idea how to fix that (I thought that extern "C" prevented this to happen)
I've also tried to add #pragma comment(lib, "DriverCore.lib")(with DriverCore.lib in the same Dir as DriverCore.h) but still nothing :/
Way 1
using a function to return a pointer to the class
DriverCore.h
#pragma once
#ifdef DRIVERCORE_EXPORTS
#define DLLCALL __declspec(dllexport)
#else
#define DLLCALL __declspec(dllimport)
#endif
class SerialPort
{
private:
bool connected = 0;
public:
SerialPort() {};
void HelloWorld();
bool isConnected() { return 0; };
int readSerialPort(char* buffer, unsigned int buf_size) { return 0; };
bool writeSerialPort(char* buffer, unsigned int buf_size) { return 0; };
};
extern "C" {
DLLCALL SerialPort* __stdcall CreateSerialPort();
};
DriverCore.cpp
#include "pch.h"
#include "DriverCore.h"
#include <iostream>
#define DRIVERCORE_EXPORTS
BOOL APIENTRY DllMain( HMODULE hModule,DWORD ul_reason_for_call,LPVOID lpReserved)
{
return TRUE;
}
SerialPort* __stdcall CreateSerialPort()
{
return new SerialPort();
}
void SerialPort::HelloWorld()
{
std::cout << "Hello World !";
}
Main.cpp
#include "pch.h"
#include <Windows.h>
#include <iostream>
#include "DriverCore.h"
typedef SerialPort* (__stdcall *SerialPortImported) ();
int main()
{
// instantiate the dll location
HINSTANCE hDLL = LoadLibraryW(L"DriverCore.dll");
if (!hDLL) {
std::cout << "could not load the dynamic library" << std::endl;
return EXIT_FAILURE;
}
//Resolve Objects Addr
SerialPortImported pCSerialPort = (SerialPortImported) GetProcAddress(hDLL, "CreateSerialPort") ;
SerialPort* CSerialPort = pCSerialPort();
CSerialPort->HelloWorld();
return 0;
}
Way 2
without using extern "c" {...} but using __declspec directly onto the class declaration
DriverCore.h
#pragma once
#ifdef DRIVERCORE_EXPORTS
#define DLLCALL __declspec(dllexport)
#else
#define DLLCALL __declspec(dllimport)
#endif
class DLLCALL SerialPort
{
private:
bool connected = 0;
public:
SerialPort() {};
void HelloWorld();
bool isConnected() { return 0; };
int readSerialPort(char* buffer, unsigned int buf_size) { return 0; };
bool writeSerialPort(char* buffer, unsigned int buf_size) { return 0; };
};
DriverCore.cpp
#include "pch.h"
#include "DriverCore.h"
#include <iostream>
#define DRIVERCORE_EXPORTS
BOOL APIENTRY DllMain( HMODULE hModule,DWORD ul_reason_for_call,LPVOID lpReserved)
{
return TRUE;
}
void SerialPort::HelloWorld()
{
std::cout << "Hello World !";
}
Main.cpp
#include "pch.h"
#include <Windows.h>
#include <iostream>
#include "DriverCore.h"
int main()
{
// instantiate the dll location
HINSTANCE hDLL = LoadLibraryW(L"DriverCore.dll");
if (!hDLL) {
std::cout << "could not load the dynamic library" << std::endl;
return EXIT_FAILURE;
}
//Resolve Objects Addr
SerialPort* pSerialPort = (SerialPort*) GetProcAddress(hDLL, "SerialPort") ;
pSerialPort->HelloWorld();
return 0;
}
Thanks a lot in advance for your help !
You are calling HelloWorld which is missing its implementation in your application.
There is some fundamental misunderstanding about how C++ executables are compiled and linked against DLLs.
No libraries:
All symbols that the Application needs must be defined in the Application.
All needed symbol definitions must be available to the linker.
Static libraries:
All symbols that the Application needs must be defined in the Application or a static library.
All needed symbol definitions must be available to the linker.
The symbols are added to the generated Application's executable.
Dynamic libraries:
All symbols that the Application needs must be defined in the Application or a dynamiclibrary.
All needed symbol definitions must be available to the linker.
The symbols remain at their original places and they are loaded only at load time. This allows swap the dynamic libraries with any other ABI-compatible one at load time.
Since you are not linking with the dll and only load it at runtime, the linker correctly complains about the missing HelloWorld method.
Extern "C" is irrelevant here.
First of all I want to apologize for my english. I'm afraid that is not my born language. I'm going crazy with this code.
I'm making a little engine to make games. I implemented a class called "process" that is the class that will inherit all the elements that make up the game. What happens is that by implementing two of these elements so that inherit from "process" the compiler throws me the following error:
||=== Build: Debug in MotorSDL (compiler: GNU GCC Compiler) ===|
include/mainProcess.h |6 | error: invalid use of incomplete type ‘class process’
include/declarativas.h |65| error: forward declaration of ‘class process’
include/exeControl.h |6 | error: invalid use of incomplete type ‘class process’
include/declarativas.h |65| error: forward declaration of ‘class process’
/home/yawin/Dokumentuak/c/MotorSDL/src/engine.cpp | | In destructor ‘virtual Engine::~Engine()’:
/home/yawin/Dokumentuak/c/MotorSDL/src/engine.cpp |11| warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
/home/yawin/Dokumentuak/c/MotorSDL/src/engine.cpp | | In member function ‘void Engine::update()’:
/home/yawin/Dokumentuak/c/MotorSDL/src/engine.cpp |76| warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
||=== Build failed: 4 error(s), 2 warning(s) (0 minute(s), 0 second(s)) ===|
I have read many of the issues in this respect have been published on this forum before and I understand that the mistake is that I am not correctly using a class (the class "process") because not instantiate properly or because it lacks something to the class (a required method not implemented).
But as I read and check my code I can't find where I'm making the mistake. Is anyone able to see what I'm wrong?
Here is the source code:
https://github.com/yawin123/SDLEngine
"main.cpp"
#include <SDL2/SDL.h>
#include "declarativas.h"
int main()
{
Engine *e = new Engine(true);
e->newWindow("Hola mundo", SDL_WINDOW_OPENGL, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, SDL_RENDERER_ACCELERATED);
e->newTask(new exeControl());
do
{
e->run();
SDL_Delay(1);
}while(e->isRunning());
e->destroyWindow();
return 0;
}
"include/declarativas.h"
#ifndef DECLARATIVAS_H
#define DECLARATIVAS_H
enum keyCode
{
_esc,
_1, _2, _3, _4, _5, _6, _7, _8, _9, _0,
_minus, _plus,
_backspace,
_tab,
_q, _w, _e, _r, _t, _y, _u, _i, _o, _p,
_a, _s, _d, _f, _g, _h, _j, _k, _l,
_z, _x, _c, _v, _b, _n, _m,
_f1, _f2, _f3, _f4, _f5, _f6, _f7, _f8, _f9, _f10, _f11, _f12,
_enter,
_c_enter,
_control,
_l_shift,
_comma,
_point,
_r_shift,
_c_asterisk,
_prn_scr,
_alt,
_caps_lock,
_num_lock,
_scroll_lock,
_home,
_c_home,
_up,
_c_up,
_pgup,
_c_minus,
_left,
_c_left,
_c_center,
_right,
_c_right,
_c_plus,
_end,
_c_end,
_down,
_c_down,
_pgdn,
_c_pgdn,
_ins,
_c_ins,
_del,
_c_del,
_less,
_equals,
_greater,
_asterisk,
_r_alt,
_r_control,
_l_alt,
_menu,
_l_windows,
_r_windows,
_close_window,
_FOO_KEY
};
class Engine;
class process;
class Ventana;
//Procesos
class mainProcess;
class exeControl;
#include "process.h"
#include "engine.h"
#include "Ventana.h"
#include "mainProcess.h"
#include "exeControl.h"
#endif // DECLARATIVAS_H
"include/process.h"
#ifndef PROCESS_H
#define PROCESS_H
#include "engine.h"
#include "declarativas.h"
class process
{
public:
process();
process(Engine *e);
virtual ~process();
virtual void Update();
int id;
int father;
void setEngine(Engine *e);
protected:
Engine *engine;
};
#endif // PROCESS_H
"src/process.cpp"
#include "process.h"
process::process(){return;}
process::process(Engine *e)
{
setEngine(e);
}
process::~process()
{
return;
}
void process::setEngine(Engine *e)
{
engine=e;
}
void engine::Update()
{
return;
}
"include/engine.h"
#ifndef ENGINE_H
#define ENGINE_H
#include "Ventana.h"
#include "process.h"
#include "declarativas.h"
#include <SDL2/SDL.h>
#include <vector>
#include <iostream>
using namespace std;
class Engine
{
public:
Engine(bool debug=false);
virtual ~Engine();
void run();
void newWindow(string i_title, int i_windowMode, int i_posX, int i_posY, int i_width, int i_height, int i_rendererMode);
void destroyWindow();
Ventana* ventana;
/**Ciclo de ejecución**/
void input();
void update();
void render();
/*********************/
/**Control de ejecución*******/
bool isRunning();
void isRunning(bool m_r);
bool m_run=true;
/*****************************/
vector<process*> taskManager; //Gestor de procesos
int newTask(process *task); //Dar de alta procesos
SDL_Event event; //Evento para controlar teclado
bool key[_FOO_KEY]; //Array de teclas pulsadas
/**Control de debug*******/
bool isDebug;
void Debug(string t);
void Debug(int t);
void Debug(float t);
/************************/
};
#endif // ENGINE_H
"src/engine.cpp"
#include "engine.h"
Engine::Engine(bool debug)
{
isDebug=debug;
SDL_Init(SDL_INIT_VIDEO);
}
Engine::~Engine()
{
for(int i=0;i<taskManager.size();i++)
{
delete(taskManager[i]);
}
taskManager.clear();
delete(ventana);
SDL_Quit();
}
void Engine::newWindow(string title, int windowMode, int posX, int posY, int width, int height, int rendererMode)
{
if(ventana)
{
destroyWindow();
}
ventana = new Ventana(title,windowMode,posX,posY,width,height,rendererMode);
}
void Engine::destroyWindow()
{
delete(ventana);
}
void Engine::run()
{
input();
update();
render();
}
void Engine::input()
{
for(int i=0;i<_FOO_KEY;i++)
{
key[i]=false;
}
while (SDL_PollEvent(&event))
{
switch (event.type)
{
case SDL_QUIT: //Si pulsamos el botón de cerrar ventana
key[_close_window]=true;
break;
case SDL_KEYDOWN:
switch (event.key.keysym.sym)
{
case SDLK_ESCAPE:
key[_esc]=true;
break;
default:
break;
}
break;
default:
break;
}
}
}
void Engine::update()
{
for(int i=0;i<taskManager.size();i++)
{
taskManager[i]->Update();
}
}
void Engine::render()
{
ventana->render();
}
bool Engine::isRunning()
{
return m_run;
}
void Engine::isRunning(bool m_r)
{
m_run=m_r;
}
int Engine::newTask(process *task)
{
taskManager.push_back(task);
taskManager[taskManager.size()-1]->setEngine(this);
return taskManager.size()-1;
}
void Engine::Debug(string t)
{
if(isDebug)
cout<<t<<endl;
}
void Engine::Debug(int t)
{
if(isDebug)
cout<<t<<endl;
}
void Engine::Debug(float t)
{
if(isDebug)
cout<<t<<endl;
}
"include/mainProcess.h"
#ifndef MAINPROCESS_H
#define MAINPROCESS_H
#include "process.h"
class mainProcess : public process
{
public:
mainProcess();
virtual ~mainProcess();
virtual void Update();
};
#endif // MAINPROCESS_H
"src/mainProcess.cpp"
#include "mainProcess.h"
mainProcess():process(){}
mainProcess::~mainProcess(){}
void mainProcess::Update()
{
return;
}
"include/exeControl.h"
#ifndef EXECONTROL_H
#define EXECONTROL_H
#include "process.h"
class exeControl : public process
{
public:
exeControl();
virtual ~exeControl();
virtual void Update();
};
#endif // EXECONTROL_H
"src/exeControl.cpp"
#include "exeControl.h"
exeControl::exeControl():process(){}
exeControl::~exeControl(){}
void exeControl::Update()
{
if(engine->key[_esc] || engine->key[_close_window])
{
engine->isRunning(false);
}
}
I think you don't need the rest of the classes. But if you need, you can find in the link to github above.
You have a circular inclusion issue.
process.h includes engine.h and engine.h includes process.h. You need to break this cycle. This is usually done with using forward declarations and pushing includes in the the source files.
At a quick glance it looks like you can do a forward declaration of process in engine.h and remove the include.
Note: Often you include a header in the header file of a class and in the source file of the same class but it's not necessary in the header already includes it.
Example Issue
Foo.h
#ifndef FOO_H
#define FOO_H
#include "Bar.h"
class Foo
{
private:
Bar* mBar; // Raw pointer should actually be an appropriate smart pointer type
};
#endif
Foo.cpp
// Nothing required here for this example
Bar.h
#ifndef BAR_H
#define BAR_H
#include "Foo.h"
class Bar
{
private:
Foo mFoo;
};
#endif
Bar.cpp
// Nothing required here for this example
In the above there is a circular inclusion issue; the include guards have nothing to do with fixing this issue. Furthermore, since Foo only uses a pointer to a Bar instance a forward declaration can be used to break the cycle.
Example Solution
Foo.h
#ifndef FOO_H
#define FOO_H
// Note: #include "Bar.h" is removed
class Bar; // This forward declares the `Bar` class
class Foo
{
private:
Bar* mBar; // Raw pointer should actually be an appropriate smart pointer type
};
#endif
Foo.cpp
#include "Bar.h"
Bar.h
#ifndef BAR_H
#define BAR_H
#include "Foo.h"
class Bar
{
private:
Foo mFoo;
};
#endif
Bar.cpp
// Nothing required here for this example
You have a case of cyclic dependency here: Engine class uses process and process uses Engine. This is often an indicator of a flawed architecture.
If all you care for is resolving the compilation issue, forward declaration should do the trick: remove either "process.h" from "engine.h" (or vice versa) and declare
class process;
in the "engine.h" header.
I'm trying to create a C++ dynamic library that won't have a class. I'd like it to work similar to how you can include <string.h> and call strlen directly.
I can create a class that will compile, but won't link correctly with my library.
Here's the test library I'm working on now:
Header
#ifndef _DLL_H_
#define _DLL_H_
BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved);
extern "C" __declspec(dllexport) int testMethod(int a);
#endif
Cpp
#include "dll.h"
int testMethod(int num)
{
std::cout << "test message" << std::endl;
return 1;
}
BOOL APIENTRY DllMain (HINSTANCE hInst, // Library instance handle. ,
DWORD reason, // Reason this function is being called. ,
LPVOID reserved) // Not used. )
{
switch (reason)
{
case DLL_PROCESS_ATTACH:
break;
case DLL_PROCESS_DETACH:
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
}
// Returns TRUE on success, FALSE on failure
return TRUE;
}
Finally, here's the class I'm using to test the Dll, which is told to link against the lib mingw outputs
#include <iostream>
#include "../Dll/dll.h"
using namespace std;
int main(int argc, char *argv[])
{
testMethod(5);
}
I haven't used C++ in about a year so I'm pretty rusty
extern "C" __declspec(dllexport) int testMethod(int a);
This needs to be dllimport in the application which is linking against the DLL. Most people compile their DLL with a #define which controls if it is an export or an import.
#ifdef INSIDE_MYDLL
#define MYDLLAPI __declspec(dllexport)
#else
#define MYDLLAPI __declspec(dllimport)
#endif
extern "C" MYDLLAPI int testMethod(int a);