前言:为了介绍C#写界面,C++写算法的快捷开发方式,C#与C++的交互,首先介绍c++,C#内部的DLL,COM调用。
一, 静态的Lib:静态的lib经过编译后只有.h和.lib文件,没有dll,因为实现部分也包含在lib中,这就是与动态dll的区别。还有在写静态lib的时候不需要在使用导出关键字_declspec(dllexport)。一般有2中方法调用静态lib,如下实例:
静态lib:CPPLib->test.h
#pragma once
class CTest
{
public:
CTest(void);
public:
~CTest(void);
public:
int Add(int x, int y);
int Square(int x);
};
//函数的实现必须写在.cpp文件中,否则编译有错,说重复定义
int Max(int x, int y);
静态lib的实现文件: CPPLib->test.cpp
#include "StdAfx.h"
#include "Test.h"
CTest::CTest(void)
{
}
CTest::~CTest(void)
{
}
int CTest::Add(int x, int y)
{
return x+y;
}
int CTest::Square(int x)
{
return x*x;
}
int Max(int x, int y)
{
return x;
}
client调用CPPLibClient->CPPibClient.cpp
#include "stdafx.h"
//#include "test.h"
//#include <windows.h>
//#include <string>
//#include <assert.h>
//#include "../CppLib/test.h"
//#pragma comment(lib,"../debug/CppLib.lib")
//#pragma 使用法
//#include "test.h"
//修改编译选项调用静态库
//需要修改:include的path,lib的path,和加入lib的名字,如下:
//C++->General->additional include directories
//Linker->General->additional library directories
//linker->input->additional dependencies
//不能动态加载静态的lib库
//HMODULE m_handle = LoadLibrary(L"../debug/CppLib.Lib");
//GetProcAddress(m_handle, "Max");
//FreeLibrary(m_handle);
int _tmain(int argc, _TCHAR* argv[])
{
CTest test;
int a = test.Add(10,20);
printf("the result is :%d\n",a);
a = Max(10,20);
printf("the result is :%d\n",a);
return 0;
}
调用方法:可以看出对于静态的只可以使用修改编译选项和pragma comment()来调用,不能使用loadlibrary()来调用。
二 ,动态DLL:在动态dll中,可以导出变量,函数和整个类。编译后有.h,.lib和dll文件,真正的实现包含在dll中,所以在client调用动态dll的时候,必须要使用dll,最后和client的放在同意目录下。要导出必须使用导出关键字_declspec(dllexport)。有时还使用extern “C”,为了使导出能够与C兼容,一般我们都加extern “c”。
一般调用有3中方法,实例如下:
实例1:演示了导出变量和函数,和前2中调用方法,修改编译选项和pragma comment().
动态dll:CPPdll->test.h
#pragma once
extern "C" _declspec(dllexport) int nCppDll;
extern "C" _declspec(dllexport) int fnCppDll(void);
extern "C" _declspec(dllexport) int Max(int a, int b);
extern "C" _declspec(dllexport) int Min(int a, int b);
动态dll的实现:CPPDLL->test.cpp
#include "StdAfx.h"
#include "Test.h"
// This is an example of an exported variable
int nCppDll=100;
// This is an example of an exported function.
int fnCppDll(void)
{
return 42;
}
int Max(int a, int b)
{
if(a>=b)return a;
else
return b;
}
int Min(int a, int b)
{
if(a>=b)return b;
else
return a;
}
client的调用:cppclient->cppclient.cpp
#include "stdafx.h"
#pragma comment(lib, "../debug/CppDll.lib")
extern "C" int Max(int a,int b);//_declspec(dllimport)
extern "C" int Min(int a,int b); //_declspec(dllimport)
extern "C" _declspec(dllimport) int nCppDll;
extern "C" int fnCppDll(void);
//#include "test.h"
//修改编译选项调用静态库
//需要修改:include的path,lib的path,和加入lib的名字,如下:
//C++->General->additional include directories
//Linker->General->additional library directories
//linker->input->additional dependencies
int _tmain(int argc, _TCHAR* argv[])
{
int a;
a =Min(8,10);
printf("比较的结果为 %d\n",a);
a= Max(8,10);
printf("比较的结果为%d\n",a);
printf("导出的变量:%d\n",nCppDll);
a = fnCppDll();
printf("fnCppDll的结果:%d\n",a);
return 0;
}
上面演示了对一般变量和函数的导出的调用方法中的其中的2中,修改编译选项和pragma comment(),当使用pragma comment()的使用,应当注意:
使用#pragma隐式加载动态库
对于变量,必须申明且不能include头文件。extern "C" _declspec(dllimport) int nCppDll;
对于函数,或include头文件,或是申明。extern "C" int fnCppDll(void);
对于类,最好使用函数封装导出指针供使用。
参考:http://www.cppblog.com/mzty/archive/2006/07/24/10419.html
实例2:演示类的导出和使用动态加载来调用。
动态dll的类导出:CPPDll2->test.h
#pragma once
//#include "boost/shared_ptr.hpp"
class Test
{
public:
virtual ~Test() {}
virtual void DoIt() =0;
};
//extern "C" _declspec(dllexport) std::auto_ptr<Test> CreateTest();
//extern "C" _declspec(dllexport) boost::shared_ptr<Test> CreateTest();
extern "C" _declspec(dllexport) Test* CreateTestPtr();
extern "C" _declspec(dllexport) void DeleteTestPtr(Test*);
动态dll的类导出的实现:CPPDll2->test.cpp
//test.cpp
#include "stdafx.h"
#include "Test.h"
#include <stdio.h>
//#include <memory>
//#include "boost/shared_ptr.hpp"
class CTest : public Test
{
public:
virtual void DoIt()
{ printf("Should do something\n"); }
};
//std::auto_ptr<Test> CreateTest()
//{
// return std::auto_ptr<Test>(new CTest);
//}
//boost::shared_ptr<Test> CreateTest()
//{
// return boost::shared_ptr<Test>(new CTest);
//}
Test* CreateTestPtr()
{
return new CTest();
}
void DeleteTestPtr(Test* t)
{
if(t != NULL)
{
delete t;
t = NULL;
}
}
对loadlibrary的分装,可以作为tools:
//library.h
#pragma once
#include <windows.h>
#include <string>
#include <assert.h>
class Library
{
public:
explicit Library(const wchar_t* name)
{
m_handle = LoadLibrary(name);
assert(m_handle);
if (!m_handle)
throw std::runtime_error(std::string("Could not find library file:"));
}
~Library()
{
FreeLibrary(m_handle);
}
void* GetProc(const char* name)
{
void* proc = ::GetProcAddress(m_handle, name);
assert(proc);
return proc;
}
private:
HMODULE m_handle;
};
client的调用:
#include "stdafx.h"
#include "library.h"
#include "../CppDll2/test.h"
int _tmain(int argc, _TCHAR* argv[])
{
typedef Test* (*CREATE)();
typedef void (*DEL)(Test*);
Library lib(L"CppDll2.dll");
//std::auto_ptr<Test> test = ((std::auto_ptr<Test> ) lib.GetProc("CreateTest"));
Test* test = (((CREATE)(lib.GetProc("CreateTestPtr")))());
test->DoIt();
((DEL)(lib.GetProc("DeleteTestPtr")))(test);
return 0;
}
上面的是对类的动态调用,注意需要include头文件哦!
//通过API动态加载动态库
//对于类的导出,最好使用函数封装,导出类的指针。
//动态加载dll, 如果导出的函数只使用 extern,而没有使用extern "C" 则GetProcAdress会找不到函数的指针,要想找到可以使用真正要找的函数原型哦,可能是函数名后加@@。。。,也可以使用编号来找到需要的函数地址。
//但是如果导出函数使用extern "C"的话,导出函数的返回值不能是auto_ptr<>或shared_ptr<>哦,但是我们仍然可以使用智能指针哦,采用的方法是不使用return返回,使用函数的参数返回哦。//使用智能指针导出的更好的实现,请参考 http://www.cppblog.com/eXile
//参考: http://www.cppblog.com/eXile/archive/2007/04/19/22262.html
//更多dll类型:http://www.vckbase.com/document/viewdoc/?id=1116
//调用约定:http://blog.chinaunix.net/u/21790/showart_265932.html
三 资源DLL
在C++中,我们可以建立纯资源的动态dll,比如说我们建立了一个动态的资源dll,里面增加一个string: id为IDS_APPLICATION,值为:aaa,
则我们可以在client动态调用如下:
#include "stdafx.h"
#include <windows.h>
#include "../ResDll/resource.h"
int _tmain(int argc, _TCHAR* argv[])
{
HMODULE hModule=LoadLibrary(L"../debug/ResDll.dll");
if( NULL != hModule)
{
TCHAR szName[200];
::LoadString(hModule,IDS_APPLICATION,szName,200);
FreeLibrary(hModule);
}
return 0;
}
资源还可以是其他的比如是icon,bitmap。。。。等,有对应的load。()函数去调用。
四,总结
熟悉dll调用的3中方法,其中对静态的调用只有2中方法,一般对类的导出调用要使用函数封装哦!:~
代码下载:http://www.cppblog.com/Files/mzty/DLLTest.rar