То, что для простых задач это работает легко (unmanaged в managed и наоборот) не обсуждается. Статьи есть на MSDN, да и сам делал - «Hello world» легко печатается из экспортируемой функции DLL с управляемым кодом. Но вот, когда я попросил NET использовать соглашение __stdcall в экпортируемых функциях, то компилятор выдал предупреждение, что моя просьба проигнорирована - __stdcall заменён модификатором __clrcall. Это может быть важным ( возможно не только для меня ) , если потребуется создать классы-обёртки для использования в неуправляемом коде управляемого кода DLL.
Например, есть декларация «интерфейсов» неких объектов, пусть, для работы с СУБД. Эти объекты расположены в DLL. Спецификация методов интерфейсов объектов, положим, в файле publicdbinterface.h:
Код: Выделить всё
// file publicdbinterface.h
struct IUnknownInterface
{
virtual HRESULT __stdcall QueryInterface (LPCTSTR int_type, void **ppv) = 0;
virtual void __stdcall CanErrorMessage(const int can_msg) = 0;
virtual void __stdcall ErrorMessage(LPTSTR* outval ) = 0;
…
…
};
// basic database interface definition
struct IDatabaseInterface : public IUnknownInterface
{
virtual HRESULT __stdcall Open (void) = 0;
virtual void __stdcall Delete(void) = 0;
virtual void __stdcall Close (void) = 0;
virtual void __stdcall Connection(LPCTSTR ConnectionStr) = 0;
virtual HRESULT __stdcall StartTransaction(void) = 0;
virtual HRESULT __stdcall InTransaction(void) = 0;
virtual void __stdcall Commit (void) = 0;
virtual void __stdcall Rollback(void) = 0;
virtual void __stdcall TransIsolation(LPCTSTR type) = 0;
…
…
};
typedef HRESULT (__stdcall *pCreateDatabaseInstance) (IDatabaseInterface ** pInterface, LPCTSTR inst_type);
// end of cut
Код: Выделить всё
#define DB_API extern "C" __declspec(dllexport)
DB_API HRESULT __stdcall CreateDatabaseInstance (IDatabaseInterface ** pInterface, LPCTSTR inst_type);
Код: Выделить всё
HRESULT __stdcall CreateDatabaseInstance(IDatabaseInterface ** pInterface, LPCTSTR inst_type)
{
try {
if( !_tcsicmp(inst_type, _TEXT("ODBC.MFC")) )
*pInterface = (IDatabaseInterface *) new CI_ODBCdb();
else if( !_tcsicmp(inst_type, _TEXT("ADO.MFC")) )
*pInterface = (IDatabaseInterface *) new CI_ADOdb();
else
*pInterface = NULL;
}
catch(...)
{
*pInterface = NULL;
return E_FAIL;
}
return ( *pInterface )? S_OK : E_FAIL;
}
Код: Выделить всё
HINSTANCE hlib;
pCreateDatabaseInstance CreateDatabaseInstance;
void _tmain( void )
{
hlib = LoadLibrary(_TEXT("mydb.dll"));
if (!hlib)
{
_tprintf(_TEXT("Can't load library"));
return;
}
CreateDatabaseInstance = (pCreateDatabaseInstance)
GetProcAddress(hlib,_TEXT "CreateDatabaseInstance"));
if (!CreateDatabaseInstance )
{
_tprintf(_TEXT("Function CreateDatabaseInstance not found\n"));
FreeLibrary(hlib);
return;
}
IDatabaseInterface* IDatabase;
if( FAILED( CreateDatabaseInstance(&IDatabase, _TEXT("BDE") )) )
{
_tprintf(_TEXT("...Can't create BDE database instance\n"));
FreeLibrary(hlib);
return;
}
IDatabase->Connection( _TEXT("ExampleDB") );
if( FAILED( IDatabase->StartTransaction() ) )
{
IQuery->Delete();
IDatabase->Delete();
FreeLibrary(hlib);
_tprintf(_TEXT("Can't open transaction\n"));
return;
}
…
…
…
}
Код: Выделить всё
// msadonet.h
#include "publicdbinterface.h"
#using <System.dll>
#using <System.Data.dll>
using namespace System;
using namespace System::Data;
using namespace System::Data::OleDb;
#define DB_API extern "C" __declspec(dllexport)
namespace msadonet
{
DB_API HRESULT __stdcall CreateDatabaseInstance (IDatabaseInterface ** pInterface, LPCTSTR inst_type);
class CI_ADONETdb: public IDatabaseInterface
{
private:
OleDbConnection* Database;
…
…
public:
CI_ADONETdb();
~CI_ADONETdb();
// IDatabaseInterface
HRESULT __stdcall Open( void );
void __stdcall Delete( void );
…
…
// IUnknownInterface
HRESULT __stdcall QueryInterface(LPCTSTR int_type, void **ppv);
void __stdcall CanErrorMessage(const int can_msg);
void __stdcall ErrorMessage(LPTSTR* outval);
…
…
};
}
// end file msadonet.h
Положение можно «подправить», сделав код класса CI_ADONETdb управляемым:
Код: Выделить всё
public __gc class CI_ADONETdb: public IDatabaseInterface
{
private:
OleDbConnection* Database;
…
…
};
Т.е. то, что было написано выше относительно «чисто абстрактных» классов IDatabaseInterface и IUnknownInterface теперь придётся изменить на следующее:
Код: Выделить всё
public __gc class IUnknownInterface
{
virtual HRESULT __stdcall QueryInterface(LPCTSTR int_type, void **ppv)
…
…
};
public __gc class IDatabaseInterface : public IUnknownInterface
{
virtual HRESULT __stdcall Open (void) = 0;
…
…
};
То, что компилируется – не большое утешение. Я уже не могу использовать эту DLL в своих проектах из–за ignored '__clrcall ' to '__stdcall '. Несмотря на то, что DLL компилируется, даже пробовать использовать её не стоит.
Как мне быть? Как «обернуть» объекты типа OleDbConnection так, чтобы сохранить соглашения __stdcall, указанные в начале моего поста?