parameter passing to unmanaged code dll

A

antarikshv

I am loading a native dll, i.e. a dll created using native code. My
development environment is VC++ .Net 2.0, managed code.

The code is as follows,
The function signature in the dll is:
int CalculateKey(BYTE *seed, int seedLength, BYTE *key, int
keyLength);

the delegate declaration in my code is:

delegate int CalculateKey(int * seed, int seedLength,int * key, int
keyLength);

the code for loading of the dll and using its function:

OpenFileDialog ^ of = gcnew OpenFileDialog();
of->ShowDialog();

IntPtr hExe = LoadLibrary(of->FileName);
if (hExe.ToInt32() == 0)
{
MessageBox::Show("Cannot open " + of->FileName);
}

GCHandle ^ gch = GCHandle::Alloc(of->FileName,GCHandleType::pinned); //
convert object to handle
IntPtr hProc = GetProcAddress(hExe,"CalculateKey");
if(hProc.ToInt32() == 0)
{
MessageBox::Show("Not Found");
}

CalculateKey ^ ObjCalcFunc = (CalculateKey ^)
Marshal::GetDelegateForFunctionPointer(hExe,CalculateKey::typeid);

array<char> ^ uSeed = gcnew array<char>{1,2,3,4};
array<char> ^ uKey = gcnew array<char>(4);
pin_ptr<char> pinptrp = &uSeed[0];
pin_ptr<char> pinptrp1 = &uKey[0];
int * ppp = reinterpret_cast<int *>(pinptrp);
int * ppp1 = reinterpret_cast<int *>(pinptrp1);

ObjCalcFunc(ppp,uSeed->Length,ppp1,uKey->Length);

With the above code i am facing the exception when the ObjCalcFunc is
called as

"An unhandled exception of type 'System.AccessViolationException'
occurred in tesbed.exe

Additional information: Attempted to read or write protected memory.
This is often an indication that other memory is corrupt."

Now i have searched various sites for finding a solution to this, but
haven't succeeded yet. Please let me know the solution.
Thanks in advance.
 
G

Giovanni Dicanio

The code is as follows,
The function signature in the dll is:
int CalculateKey(BYTE *seed, int seedLength, BYTE *key, int
keyLength);

the delegate declaration in my code is:

delegate int CalculateKey(int * seed, int seedLength,int * key, int
keyLength);

Two things come in my mind:

1. if the native CalculateKey function expects a BYTE *, why do you use int*
?

2. Pay attention to calling conventions. Is your CalculateKey in the native
DLL defined as __stdcall?
(Or does it use __cdecl calling convention?)

Giovanni
 
G

Giovanni Dicanio

antarikshv said:
I am loading a native dll, i.e. a dll created using native code. My
development environment is VC++ .Net 2.0, managed code.

You may want to consider downloading this simple test project, that shows
how to call into native DLL which exports a pure-C interface function, from
C++/CLI code:

http://www.geocities.com/giovanni.dicanio/vc/TestCallNative.zip

I used this kind of code to call in the DLL:

<code>
// Caller.cpp : Call into a native DLL

#include "stdafx.h"

using namespace System;

// Function exported from the DLL
typedef int (__stdcall *PFNCALCULATEKEY)(
BYTE *, //seed
int, // seedLength
BYTE *, // key,
int // keyLength
);

int main(array<System::String ^> ^args)
{
Console::WriteLine(L"Test calling native DLL with a C interface.");

// Load DLL
HINSTANCE hDll = LoadLibrary( TEXT("TestDLL.dll") );
if (hDll == NULL)
{
Console::WriteLine(L"Error in loading DLL.");
return 1;
}

// Load function from DLL
PFNCALCULATEKEY pfnCalculateKey = reinterpret_cast<PFNCALCULATEKEY>(
GetProcAddress(hDll, "CalculateKey"));
if (pfnCalculateKey == NULL)
{
Console::WriteLine(L"Error in loading function.");
FreeLibrary(hDll);
hDll = NULL;
return 1;
}

// Allocate arrays
array<byte> ^ seed = gcnew array<byte>{1,2,3,4};
array<byte> ^ key = gcnew array<byte>(4);

// Pin pointers before calling into native code
pin_ptr<byte> ptrSeed = &seed[0];
pin_ptr<byte> ptrKey = &key[0];

// Call DLL function
pfnCalculateKey(ptrSeed, seed->Length, ptrKey, key->Length);

// Print result
Console::WriteLine(L"Key:");
for (int i = 0; i < key->Length; i++)
{
Console::WriteLine(key);
}

// Release DLL
FreeLibrary(hDll);
hDll = NULL;

// All right
return 0;
}

</code>

HTH,
Giovanni
 
A

antarikshv

Thanks Giovanni,
the complete solution code you provided was really helpful.
i checked with the header files you are using,
i used only Windows.h instead of using both Windows.h and atlbase.h,
and my code is working.

But i need to ask one question, as to why was the Exception
encountered on the first place?
 
G

Giovanni Dicanio

antarikshv said:
Thanks Giovanni,
the complete solution code you provided was really helpful.
i checked with the header files you are using,
i used only Windows.h instead of using both Windows.h and atlbase.h,
and my code is working.

Probably I #included <atlbase.h> to get some utility stuff from ATL (like
ATLASSERT()).

But i need to ask one question, as to why was the Exception
encountered on the first place?

I don't know... I would need a compilable project to test inside Visual
Studio.

Giovanni
 
A

antarikshv

Thanks Giovanni,

I have tested with your code and also did a dllimport for the
functions LoadLibrary,GetProcAddress,FreeLibrary.

1. When i am using your solution with your style of coding, its
working. But in the solution which you have provided, it is assumed
that the dll file name is known at the compile time itself. So i made
a small change and brought in a OpenFile dialog to be able to browse
the dll file. The problem here was i wasn't able to convert the String
return type from the OpenFile dialog to the LPCTSTR required by the
LoadLibrary function.

So to overcome this,
2. I started using the dllimport. But then here the original exception
is occuring.

"An unhandled exception of type 'System.AccessViolationException'
occurred in tesbed.exe
Additional information: Attempted to read or write protected memory.
This is often an indication that other memory is corrupt."


So
1. Any suggestion as to how to convert the (String ^) to LPCTSTR.
2. Any leads to why actually that kind of exception might occur?
[Note: The steps followed in both the styles are same, only the
declarations change]
 
D

David Wilkinson

antarikshv said:
So
1. Any suggestion as to how to convert the (String ^) to LPCTSTR.

Assuming you are in Unicode build:

String ^str = L"Hello";
pin_ptr<const wchar_t> wch = PtrToStringChars(str);
HMODULE hMod = LoadLibrary(wch);
 
B

Ben Voigt [C++ MVP]

David Wilkinson said:
Assuming you are in Unicode build:

String ^str = L"Hello";
pin_ptr<const wchar_t> wch = PtrToStringChars(str);
HMODULE hMod = LoadLibrary(wch);

Just use LoadLibraryW, then project character set settings don't matter.
 
G

Giovanni Dicanio

1. Any suggestion as to how to convert the (String ^) to LPCTSTR.

To add to what David already suggested, you may try the marshal_as library
that comes with VS2008:

<code>

// Allocate a new marshaling context for string conversion
marshal_context^ mc = gcnew marshal_context();

// Convert from .NET string (i.e.: String^) to old C-style string (i.e.
LPCTSTR)
LPCTSTR psz = mc->marshal_as< LPCTSTR >( managedString );

.... use psz ...

// Delete the marshaling context, releasing resources
delete mc;

</code>

Giovanni
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top