Access violation when invoking a C++ DLL from Delphi - c++

I write a Unicode DLL in Visual C++ 6.0. Then try to invoke the DLL function from Delphi XE3.
When I debug in Delphi, when step over the line to invoke the DLL function, I will always get an Access violation exception.
However, when I debug in Visual C++, I can see all parameters passed from Delphi are correct and I can step over all codelines without any exceptions.
If running outside the debugger, then I will not see any "access violation exceptions.
I try many methods but still cannot figure out how to eliminate the exception when debuggin in Delphi.
Below is the code in Visual C++ 6.0 part:
TestDLL.cpp:
extern "C" VOID WINAPI Test(CONST MESSAGEPROC lpMessageProc, LPVOID lParam)
{
if (lpMessageProc != NULL)
(*lpMessageProc)(1500, (const LPVOID)(LPCTSTR)CString((LPCSTR)IDS_MYTEST), lParam);
/*
if (lpMessageProc != NULL)
(*lpMessageProc)(1500, (const LPVOID)(LPCTSTR)CString(_T("Test")), lParam);*/
}
TestDLL.h:
// TestDLL.h : main header file for the TESTDLL DLL
//
#if !defined(AFX_TESTDLL_H__38054A53_5CEE_4ABF_9BA8_BCE427FCB8E1__INCLUDED_)
#define AFX_TESTDLL_H__38054A53_5CEE_4ABF_9BA8_BCE427FCB8E1__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#ifndef __AFXWIN_H__
#error include 'stdafx.h' before including this file for PCH
#endif
#include "resource.h" // main symbols
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
typedef BOOL (CALLBACK* MESSAGEPROC)(CONST DWORD dwMessageId, CONST LPVOID lp, LPVOID lParam);
VOID WINAPI Test(CONST MESSAGEPROC lpMessageProc, LPVOID lParam);
#ifdef __cplusplus
}
#endif
/////////////////////////////////////////////////////////////////////////////
//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
#endif // !defined(AFX_TESTDLL_H__38054A53_5CEE_4ABF_9BA8_BCE427FCB8E1__INCLUDED_)
Below is the codes in Delphi XE3 part:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
public
{ Public declarations }
end;
PForm1 = ^TForm1;
TMessageProc = function (const dwMessageId: DWORD; const lp: Pointer; lParam: Pointer): BOOL; stdcall;
{$EXTERNALSYM TMessageProc}
var
Form1: TForm1;
procedure Test(const lpMessageProc: TMessageProc; lParam: Pointer); stdcall;
implementation
{$R *.dfm}
procedure Test; external 'TestDLL.dll' index 2;
function MessageProc(const dwMessageId: DWORD; const lp: Pointer; lParam: Pointer): BOOL; stdcall;
begin
Result := True;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
Test(MessageProc, #Self); // <---- This code line will cause "access violation
end;
I belive the problem occurs in DLL test function, when it try to load string from resource using CString((LPCSTR)IDS_MYTEST). If I change the code to CString(_T("Test")), then problem disappears.
Thanks

As you surmised, this statement will not work:
CString((LPCSTR)IDS_MYTEST)
Although this constructor of CString does allow you to pass it a resource ID, it will try to find the resource in the calling process's (ie, the Delphi EXE's) resources, not in the DLL's resources. You need to use the HINSTANCE of the DLL, as provided by the DLL's DllMain(), when loading strings from the DLL's resources. You can use the CString::LoadString() method for that, eg:
HINSTANCE hInst;
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
hInst = hinstDLL;
return TRUE;
}
extern "C" VOID WINAPI Test(CONST MESSAGEPROC lpMessageProc, LPVOID lParam)
{
if (lpMessageProc != NULL)
{
CString str;
str.LoadString(hInst, IDS_MYTEST);
(*lpMessageProc)(1500, (LPCTSTR)str, lParam);
}
}

I finally figure out this is a bug of MFC codes(VC6.0 version).
I don't know if I can post MFC source codes so I will just paste the function headers and related parts only.
In Microsoft Visual Studio\VC98\MFC\SRC\STRCORE.CPP, we can see the following 3 functions:
//////////////////////////////////////////////////////////////////////////////
// More sophisticated construction
CString::CString(LPCTSTR lpsz) // Function 1
{
Init();
if (lpsz != NULL && HIWORD(lpsz) == NULL)
{
UINT nID = LOWORD((DWORD)lpsz);
if (!LoadString(nID))
TRACE1("Warning: implicit LoadString(%u) failed\n", nID);
}
else
{
// Construct string normally
}
}
/////////////////////////////////////////////////////////////////////////////
// Special conversion constructors
#ifdef _UNICODE
CString::CString(LPCSTR lpsz) // Function 2
{
// Construct string normally
}
#else //_UNICODE
CString::CString(LPCWSTR lpsz) // Function 3
{
// Construct string normally
}
#endif //!_UNICODE
As we can see in the above code snippet, only function 1 contains codes that will take special process on lpsz and check if it is a string resource ID, if yes, then load the string from the resource. Both function 2 & 3 have no such special processes.
When we create a project in VS6, the default settings for a project is _MBCS, in such a case, function 1 will become
CString::CString(LPCSTR lpsz)
so CString((LPCSTR)nResID) will actually invoke function 1 and load string resource properly.
Function 2 will be disabled since _UNICODE is not defined. And function 3 works with wide char strings.
Therefore, for _MBCS project, everything works perfectly and consistently with the MSDN document.
However, when I change _MBCS to _UNICODE, function 1 will become
CString::CString(LPCWSTR lpsz)
Fucntion 2 will be enabled and function 3 will be disabled.
So CString((LPCSTR)nResID) will actually invoke function 2, which does NOT have special process to load string resource, which makes the problem.
There are two solutions for this problem:
Always use CString((LPCTSTR)nResID) instead of CString((LPCSTR)nResID) to load a string from resource. However, this usage is inconsistent with MSDN document so we have to call it as an undocumented usage.
Always use LoadString to load a string resource.
Though solution 1 is a little simpler, it is an undocumented usage so I finally opt solution 2 to solve my problem.
Many thanks to all your helps in solving this issue.

Related

How does MFC interpret SetWindowTextW(LPCTSTR)?

In MFC there are no methods defined as CWnd::SetWindowTextA/CWnd::SetWindowTextW, yet the following code will compile and run correctly depending on Unicode settings:
//UNICODE is defined
BOOL CMyDialog::OnInitDialog()
{
CDialogEx::OnInitDialog();
//this line won't compile as expected
//SetWindowTextA(L"ANSI");
//this line compiles, but CWnd::SetWindowTextW doesn't exits
//SetWindowTextW ends up calling CWnd::SetWindowText
SetWindowTextW(L"Unicode");
return TRUE;
}
//UNICODE is not defined
BOOL CMyDialog::OnInitDialog()
{
CDialogEx::OnInitDialog();
//this line compiles, but CWnd::SetWindowTextA doesn't exits!
//SetWindowTextA ends up calling CWnd::SetWindowText
SetWindowTextA("ANSI");
//this line won't compile as expected
//SetWindowTextW(L"Unicode");
return TRUE;
}
It makes sense that SetWindowText is mapped in to SetWindowTextA/SetWindowTextW depending on the macro. But I don't understand how wnd->SetWindowTextA/wnd->SetWindowTextW get mapped back in to CWnd::SetWindowText.
It's a side effect of the macro declaration in WinUser.h. It applies not only to the global function declaration for the Windows API, but also any other identifier named SetWindowText that appears in code: global, local, or class scope.
#ifdef UNICODE
#define SetWindowText SetWindowTextW
#else
#define SetWindowText SetWindowTextA
#endif // !UNICODE
So any C++ class that declares a method called SetWindowText gets all that method implicitly converted by the preprocessor.
I don't have MFC installed, but I do know this method exists for the CWindow class on ATL and is defined as follows.
class CWindow
{
public:
...
BOOL SetWindowText(_In_z_ LPCTSTR lpszString) throw()
{
ATLASSERT(::IsWindow(m_hWnd));
return ::SetWindowText(m_hWnd, lpszString);
}
...
};
But at compile time, the above code (for a debug build) is going to get converted by the preprocessor into something like the following:
BOOL SetWindowTextW( LPCTSTR lpszString) throw()
{
(void)( (!!((::IsWindow(m_hWnd)))) || (1 != _CrtDbgReportW(2, L"c:\\program files...
return ::SetWindowTextW(m_hWnd, lpszString);
}
Ironically, the LPCTSTR method parameter is typedef'd instead of being a macro replacement, but you get the idea.
If you have a large enough Windows application, chances are very high that one of your existing C++ classes that you defined yourself has a method or member variable that matches a Windows API. And it's getting the same treatment.

x64 DLL export function names

I am trying to port a 32-bit dll (and application) to 64-bit and I have managed to build it without errors. When trying to load it with my 64-bit application I noticed that the exported function names differ. This is how I export the functions:
#ifdef __cplusplus
extern "C" {
#endif
__declspec(dllexport) long __stdcall Connect(char * name, long size);
#ifdef __cplusplus
}
#endif
In Dependency Walker the exported functions have the following format:
32-bit: _Connect#8
64-bit: Connect
In the application using the dll I explicitly load the dll (LoadLibrary is successful) but GetProcAddress fails for 64-bit because it cannot find a function with the provided name.
In our application I keep the function names as follows:
#define ConnectName "_Connect#8"
...
GetProcAddress(Dll, ConnectName);
So I was wondering if it is possible to export the same function names for both 32-bit and 64-bit dlls or is this a bad idea? Or do I need to do the following in my applications:
#if _WIN64
#define ConnectName "Connect"
#else
#define ConnectName "_Connect#8"
#endif
I appreciate any help.
An option you have to export function names without any decoration (independently from the particular calling convention you used in x86, __stdcall, __cdecl, or other) and with the same undecorated name in both x86 and x64 builds, is to export your DLL functions using DEF files.
E.g. you could add a .DEF file like this to your project:
LIBRARY YOURDLL
EXPORTS
Connect #1
AnotherFunction #2
... etc. ...
Repro Follows
Create an empty solution in Visual Studio (I used VS2013), and inside that create an empty Win32 console project (the test client) and an empty Win32 DLL project (the test DLL).
Add this NativeDll.def .DEF file in the DLL project:
LIBRARY NATIVEDLL
EXPORTS
SayHello #1
Add this NativeDll.cpp C++ source code in the DLL project:
///////////////////////////////////////////////////////////////////////////////
//
// NativeDll.cpp -- DLL Implementation Code
//
///////////////////////////////////////////////////////////////////////////////
#include <Windows.h>
#include <atldef.h>
#include <atlstr.h>
//
// Test function exported from the DLL
//
extern "C" HRESULT WINAPI SayHello(PCWSTR name)
{
//
// Check for null input string pointer
//
if (name == nullptr)
{
return E_POINTER;
}
try
{
//
// Build a greeting message and show it in a message box
//
CString message;
message.Format(L"Hello %s from the native DLL!", name);
MessageBox(nullptr, message, L"Native DLL Test", MB_OK);
// All right
return S_OK;
}
//
// Catch exceptions and convert them to HRESULT codes
//
catch (const CAtlException& ex)
{
return static_cast<HRESULT>(ex);
}
catch (...)
{
return E_FAIL;
}
}
Add this NativeClient.cpp C++ source code in the client test project:
///////////////////////////////////////////////////////////////////////////////
//
// NativeClient.cpp -- EXE Test Client Code
//
///////////////////////////////////////////////////////////////////////////////
#include <Windows.h>
//
// Prototype of the function to be loaded from the DLL
//
typedef HRESULT (WINAPI *SayHelloFuncPtr)(PCWSTR /* name */);
//
// Simple RAII wrapper on LoadLibrary()/FreeLibrary().
//
class ScopedDll
{
public:
//
// Load the DLL
//
ScopedDll(PCWSTR dllFilename) throw()
: m_hDll(LoadLibrary(dllFilename))
{
}
//
// Unload the DLL
//
~ScopedDll() throw()
{
if (m_hDll)
{
FreeLibrary(m_hDll);
}
}
//
// Was the DLL loaded successfully?
//
explicit operator bool() const throw()
{
return (m_hDll != nullptr);
}
//
// Get the DLL handle
//
HINSTANCE Get() const throw()
{
return m_hDll;
}
//
// *** IMPLEMENTATION ***
//
private:
//
// The wrapped raw DLL handle
//
HINSTANCE m_hDll;
//
// Ban copy
//
private:
ScopedDll(const ScopedDll&) = delete;
ScopedDll& operator=(const ScopedDll&) = delete;
};
//
// Display an error message box
//
inline void ErrorMessage(PCWSTR errorMessage) throw()
{
MessageBox(nullptr, errorMessage, L"*** ERROR ***", MB_OK | MB_ICONERROR);
}
//
// Test code calling the DLL function via LoadLibrary()/GetProcAddress()
//
int main()
{
//
// Return codes
//
static const int kExitOk = 0;
static const int kExitError = 1;
//
// Load the DLL with LoadLibrary().
//
// NOTE: FreeLibrary() automatically called thanks to RAII!
//
ScopedDll dll(L"NativeDll.dll");
if (!dll)
{
ErrorMessage(L"Can't load the DLL.");
return kExitError;
}
//
// Use GetProcAddress() to access the DLL test function.
// Note the *undecorated* "SayHello" function name!!
//
SayHelloFuncPtr pSayHello
= reinterpret_cast<SayHelloFuncPtr>(GetProcAddress(dll.Get(),
"SayHello"));
if (pSayHello == nullptr)
{
ErrorMessage(L"GetProcAddress() failed.");
return kExitError;
}
//
// Call the DLL test function
//
HRESULT hr = pSayHello(L"Connie");
if (FAILED(hr))
{
ErrorMessage(L"DLL function call returned failure HRESULT.");
return kExitError;
}
//
// All right
//
return kExitOk;
}
Build the whole solution (both the .EXE and the .DLL) and run the native .EXE client.
This is what I get on my computer:
It works without modifications and with the undecorated function name (just SayHello) on both x86 and x64 builds.
__stdcall is not supported (and is ignored) on x64. Quoting MSDN:
On ARM and x64 processors, __stdcall is accepted and ignored by the compiler; on ARM and x64 architectures, by convention, arguments are passed in registers when possible, and subsequent arguments are passed on the stack.
The calling convention on x64 is pretty much __fastcall.
Since the calling conventions and name decoration rules on x86 and x64 differ, you have to abstract this somehow. So your idea with #if _WIN64 goes in the right direction.
You can examine x86 calling conventions and your needs and perhaps devise a macro which could automate the name selection process.
As you can tell, in 64-bit Windows names are not decorated.
In 32-bit __cdecl and __stdcall symbols, the symbol name is prepended by an underscore. The trailing '#8' in the exported name for the 32-bit version of your example function is the number of bytes in the parameter list. It is there because you specified __stdcall. If you use the __cdecl calling convention (the default for C/C++ code), you won't get that. If you use __cdecl, it makes it much easier to wrap GetProcAddress() with something like:
#if _WIN64
#define DecorateSymbolName(s) s
#else
#define DecorateSymbolName(s) "_" ## s
#endif
then just call with
pfnConnect = GetProcAddress(hDLL, DecorateSymbolName("Connect"));
pfnOtherFunc = GetProcAddress(hDLL, DecorateSymbolName("OtherFunc"));
or something similar (error checking omitted in example).
To do this, remember to declare your exported functions as:
__declspec(dllexport) long __cdecl Connect(char * name, long size);
__declspec(dllexport) long __cdecl OtherFunc(int someValue);
In addition to being easier to maintain, if during development the signature of an exported function changes, you don't have to screw around with your #define wrappers.
Downside: if during development the number of bytes in a given function's parameter list changes, it will not be caught by the application importing the function because the changing the signature will not change the name. Personally, I don't think this is an issue because the 64-bit build would blow up under the same circumstances anyway as the names are not decorated. You just have to make sure your application is using the right version of the DLL.
If the user of the DLL is using C++, you can wrap things in a better way using C++ capabilities (wrap the entire explicitly-loaded library in a wrapper class, e.g.):
class MyDLLWrapper {
public:
MyDLLWrapper(const std::string& moduleName); // load library here
~MyDLLWrapper(); // free library here
FARPROC WINAPI getProcAddress(const std::string& symbolName) const {
return ::GetProcAddress(m_hModule, decorateSymbolName(symbolName));
}
// etc., etc.
private:
HMODULE m_hModule;
// etc.
// ...
};
There's actually a lot more you can do with a wrapper class like this, it's just an example.
On edit: since OP mentioned using PInvoke in the comments - if anyone decides to do this, do not forget to add CallingConvention = CallingConvention.Cdecl in the [DllImport] declaration when using PInvoke. __cdecl might be the default for unmanaged C/C++, but is not the default for managed code.
For Win32 build:
If you use __stdcall, you will get something like this (dumped with dumpbin /exports):
__declspec(dllexport) int __stdcall
->
ordinal hint RVA name
1 0 00001240 _F1#0 = _F1#0
2 1 0000124D _F2#0 = _F2#0
And you have to use GetProcAddress("_F1#0") to locate the function pointer.
If you use __cdecl, you will get something like this:
__declspec(dllexport) int __cdecl
->
ordinal hint RVA name
1 0 00001240 F1 = _F1
2 1 0000124D F2 = _F2
And you can use GetProcAddress("F1") to locate the function pointer.
BTW, if you add a XXX.def file to your Visual Studio project. One more link option will be silently added to your linker command line /DEF:"XXX.def" in the All Options window. And if you change your .def file name later for whatever reason, this link option doesn't change accordingly. You need to manually change the def file name in the project properties window.

How to get file information?

I have an existing file on the computer and I was wondering if it is possible to know when it was made, Size of the file, and more properties on the file..
I tried to use in ifstream But there's the information I have on file
(I'm using Visual C++ 6.0,Cannot using Boost)
Look at function GetFileAttributesEx.
#include <windows.h>
WIN32_FILE_ATTRIBUTE_DATA fInfo;
GetFileAttributesEx("test.dat", GetFileExInfoStandard, &fInfo);
The WIN32_FILE_ATTRIBUTE_DATA contains a lot of the "common" file informations (size, creation/edit time, attributes).
Update: I just saw, that you're using Visual C++ 6. Since GetFileAttributesEx is supported since Windows XP it might not be available in your WIN API headers... You can use the function by dynamic linking. The following code does the same thing as the snippet from above:
/* clone definition of WIN32_FILE_ATTRIBUTE_DATA from WINAPI header */
typedef struct file_info_struct
{
DWORD dwFileAttributes;
FILETIME ftCreationTime;
FILETIME ftLastAccessTime;
FILETIME ftLastWriteTime;
DWORD nFileSizeHigh;
DWORD nFileSizeLow;
} FILE_INFO;
/* function pointer to GetFileAttributesEx */
typedef BOOL (WINAPI *GET_FILE_ATTRIBUTES_EX)(LPCWSTR lpFileName, int fInfoLevelId, LPVOID lpFileInformation);
HMODULE hLib;
GET_FILE_ATTRIBUTES_EX func;
FILE_INFO fInfo;
hLib = LoadLibrary("Kernel32.dll");
if (hLib != NULL)
{
func = (GET_FILE_ATTRIBUTES_EX)GetProcAddress(hLib, "GetFileAttributesExW");
if (func != NULL)
{
func("test.dat", 0, &fInfo);
}
FreeLibrary(hLib);
/*
** Don't call func after FreeLibrary !!!
** It should be ok since kernel32.dll is loaded by your application anyway but if
** you get a function pointer from a dll only loaded by LoadLibrary the function
** pointer is invalid once the library if freed.
*/
}
The size and creation data (and more) are available via FindFirstFile.

Delphi: Calling a function from a vc++ dll that exports a interface / class

i have some trouble accessing a dll written in vc++ that exports an interface. First i tried to use classes, but after some google-search i came to the solution, that this i not possible. I just want to make sure, that the plugin interface can accessed, by using other languages like c++.
Delphi Interface
IPlugIn = interface
function GetName: WideString; stdcall;
end;
Delphi Plugin call
procedure TForm1.Button5Click(Sender: TObject);
var
hLib: Cardinal;
MLoadPlugIn: TLoadPlugIn;
PlugIn: IPlugIn;
begin
hLib := LoadLibrary('PluginB.dll');
try
if not(hLib = 0) then
begin
#MLoadPlugIn := GetProcAddress(hLib, 'LoadPlugIn');
if not(#MLoadPlugIn = nil) then
begin
if MLoadPlugIn(PlugIn) then
try
ShowMessage(PlugIn.GetName); // here i get the access-violation using the vc++ plugin
finally // i get the return value but the instance is not created
PlugIn := nil;
end;
end
else
raise Exception.Create('');
end;
finally
FreeLibrary(hLib);
end;
end;
Delphi plugin dll
TMyPlugin = class(TInterfacedObject, IPlugIn)
public
function GetName: WideString; stdcall;
end;
function TMyPlugin.GetName;
begin
result := 'TMyPlugin';
end;
function LoadPlugIn(var PlugIn: IPlugIn): Boolean; stdcall;
begin
try
PlugIn := TMyPlugin.Create;
result := True;
except
result := False;
end;
end;
exports
LoadPlugIn;
vc++ plugin dll
// IPlugIn
__interface //__declspec(uuid("E44BB34F-D13F-42D7-9479-4C79AF5C0D1B"))
IPlugIn : public IUnknown
{
void _stdcall GetName(BSTR* result);
};
// TMyPlugIn header
class TMyPlugIn : public IPlugIn
{
public:
// Constructor
TMyPlugIn() : m_cRef(1) {}
// Destructor
~TMyPlugIn() {}
// Needed to implement IUnknown used by COM to acces your component
HRESULT _stdcall QueryInterface(const IID& iid, void** ppv);
ULONG _stdcall AddRef();
ULONG _stdcall Release();
void _stdcall GetName(BSTR* result);
private:
long m_cRef ;
};
// TMyPlugIn cpp
HRESULT _stdcall TMyPlugIn::QueryInterface(const IID& iid, void** ppv)
{
if (iid == IID_IUnknown)
{
*ppv = static_cast<IPlugIn*>(this) ;
}
else if (iid == IID_IPlugIn)
{
*ppv = static_cast<IPlugIn*>(this) ;
}
else
{
*ppv = NULL ;
return E_NOINTERFACE ;
}
reinterpret_cast<IUnknown*>(*ppv)->AddRef() ;
return S_OK ;
}
ULONG _stdcall TMyPlugIn::AddRef()
{
return InterlockedIncrement(&m_cRef) ;
}
ULONG _stdcall TMyPlugIn::Release()
{
if (InterlockedDecrement(&m_cRef) == 0)
{
delete this ;
return 0 ;
}
return m_cRef ;
}
void _stdcall TMyPlugIn::GetName(BSTR* result)
{
string s1 = "PluginName";
*result = A2WBSTR(s1.c_str());
}
// the export function from the cpp plugin
extern "C" bool __declspec(dllexport) __stdcall LoadPlugIn(IPlugIn* PlugIn);
bool __declspec(dllexport) __stdcall LoadPlugIn(IPlugIn* PlugIn)
{
PlugIn = new TMyPlugIn;
return TRUE;
}
You get the access violation because this code
extern "C" bool __declspec(dllexport) __stdcall LoadPlugIn(IPlugIn* PlugIn);
bool __declspec(dllexport) __stdcall LoadPlugIn(IPlugIn* PlugIn)
{
PlugIn = new TMyPlugIn;
return TRUE;
}
creates an instance of your plugin class and writes the address to the stack, where it quickly will be forgotten. Back in the Delphi program the original plugin interface variable is still nil, so calling a method on it crashes. You need to mimic what QueryInterface() does, like so:
extern "C" bool __declspec(dllexport) __stdcall LoadPlugIn(IPlugIn** PlugIn);
bool __declspec(dllexport) __stdcall LoadPlugIn(IPlugIn** PlugIn)
{
*PlugIn = new TMyPlugIn;
return TRUE;
}
This passes the address of the interface variable, and the address of the plugin instance will be written to the variable.
In addition to what mghie has said, you also have a problem with mismatched definitions between Delphi and C++
Your C++ signature for GetName is:
void _stdcall GetName(BSTR* result);
Your Delphi signature is:
function GetName: WideString; stdcall;
There are (at least) 2 possible ways to fix this.
1) If you want the Delphi code to work as a function, then make it safecall and adjust the C++ to match:
Delphi:
function GetName: WideString; safecall;
C++:
HRESULT _stdcall GetName(BSTR* result);
or
2) fix the Delphi to match the existing C++ defn:
procedure GetName( var name: WideString );
I (personally) would probably go the safecall route, as I think it is much cleaner on the Delphi side...
In general, you should not export interfaces (and for that matter: objects should really not be exported) across DLL boundaries because you do not know which memory manager, run-time library and object model will be on either side.
See also this thread about exceptions in DLL's (exceptions are objects).
Since the Delphi interface model is binary compatible with the COM interface model, and Visual C++ can export COM objects, you should go the COM way (was Adelf also suggested).
--jeroen
All Delphi classes parent - TObject class from VCL.
If you use Borland C++ Builder(VCL library) - you can write plugin to Delphi with this way.
For another cases.. you should read about COM.

MFC Fail to Load Dlg from DLL

I have installed in my PC VS2008 and Windows Mobile 6 SDK.
I have made a SmartDevice MFC application and a Regular DLL MFC, both uses shared MFC DLL.
But when I called DoModal() of the DLL the application hangs, show a "Debug Assertion Failed" message and freeze my device.
Can you help me?
Codes:
The EXE code:
typedef BOOL (CALLBACK* LPFNDLLLOAD)();
typedef BOOL (CALLBACK* LPFNDLLRUN)(HINSTANCE, HWND, LPBYTE *, LONG *);
BOOL CTesteExeDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
//CModule mod;
//mod.Create(L"\\Program Files\\PMA\\Teste.dll");
//mod.Run(AfxGetInstanceHandle(), GetSafeHwnd(), 0, 0);
HMODULE m_hModule = AfxLoadLibrary(L"\\Program Files\\PMA\\TesteDll.dll");
LPFNDLLLOAD m_lpfnLoad= (LPFNDLLLOAD)GetProcAddress(m_hModule, _T("_Load"));
LPFNDLLRUN m_lpfnRun = (LPFNDLLRUN)GetProcAddress(m_hModule, _T("_Run"));
m_lpfnLoad();
m_lpfnRun(AfxGetInstanceHandle(), GetSafeHwnd(), 0, 0);
return TRUE; // return TRUE unless you set the focus to a control
}
The DLL Code:
I remove default CTesteDllApp class and put this:
#include "stdafx.h"
#include "TesteDll.h"
#include "TesteDllDlg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
extern "C" BOOL PASCAL EXPORT _Load()
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
return TRUE;
}
extern "C" BOOL PASCAL EXPORT _Unload()
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
return TRUE;
}
extern "C" BOOL WINAPI EXPORT _Run(HINSTANCE hInst,
HWND hwndParent,
LPBYTE *buffer,
LONG *size)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
CTesteDllDlg d;
d.DoModal(); ////-------------> Error Here
return FALSE;
}
The DLL Dlg code:
BOOL CTesteDllDlg::OnInitDialog()
{
CDialog::OnInitDialog();
AfxMessageBox(L"Oi");
return TRUE; // return TRUE unless you set the focus to a control
// EXCEPTION: OCX Property Pages should return FALSE
}
The def File in DLL
; TesteDll.def : Declares the module parameters for the DLL.
LIBRARY "TesteDll"
EXPORTS
; Explicit exports can go here
_Load #1
_Unload #2
_Run #3
In a similar problem, I had to use AFX_MANAGE_STATE macro in the OnInitDialog, OnKillActive and OnSize methods of the DLL dialog. I had to add OnKillActive and OnSize methods just to call the mentioned macro, they do nothing but to call the macro, then base implementation, and return. Maybe it would work for your case.