dll written in Delphi, according to the manual I have to first set the xml path by SET_XML(), then use any function you like.
Delphi functions according the manual:
function SET_XML(var path: PAnsiChar): LongInt;
function GET_CALCULATION_FAN_ALONE(var fanDescription: PAnsiChar): LongInt;
Use in VB according to the manual:
Public Declare Function SET_XML_PATH Lib "fan.dll" (ByRef path As String) As Long
Public Declare Function GET_CALCULATION_FAN_ALONE Lib "fan.dll" (ByRef path As String) As Long
Sub Main()
Dim a As Long, b As Long, Str_Result As String, Str_Input As String
Str_Input = "C:\Users\Sebastiaan\Documents\Visual Studio 2010\Projects\Lucam selectie\Lucam selectie\bin"
a = SET_XML_PATH(Str_Input)
Str_Result = "65464;;;1,2;;23;424,8;0,3766;;"
b = GET_CALCULATION_FAN_ALONE(Str_Result)
End Sub
I did rewrite it in C#
const string _dllLocation = "EbmPapstFan.dll";
[DllImport(_dllLocation)]
public static extern long SET_XML_PATH(ref String path);
[DllImport(_dllLocation)]
public static extern long GET_CALCULATION_FAN_ALONE(ref String fanDescription);
public Main()
{
String path = #"C:\Users\Sebastiaan\Documents\Visual Studio 2010\Projects\Lucam selectie\Lucam selectie\bin";
long a = SET_XML_PATH(ref path);
String fanDescription = "65464;;;1,2;;23;424,8;0,3766;;";
long c = GET_CALCULATION_FAN_ALONE(ref fanDescription);
}
When running de application I get an AccessViolationException
Attempt to read or write protected memory. This often indicates that other memory is corrupt.
When setting a breakpoint and debugging the code step by step nothing every thing rusn just fine. sometimes i got the error in the debug mode (on line "long c = GET_CALU...")
What am I doing wrong?
I'm not familiar with calling Delphi from C#, but from a quick search it appears that there are some issues with the calling conventions (how values are pushed and popped from the stack). See these threads:
Calling a Delphi DLL from C# produces unexpected results
Calling a delphi DLL method from C# Code
From these, it sounds like it may not be possible to call this directly due to the "fastcall" calling convention. You could try changing the calling convention, that could be worth a quick test. See this page:
http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.callingconvention.aspx
If that doesn't work, I would probably just write a C wrapper DLL that calls the Delphi DLL, and verify that works (make sure it's not just a bug in the DLL). If it does, then you could just call the C DLL from the C# program by using DllImport on your wrapper DLL's function.
Hope that helps a bit,
John
Related
Have a good day!
For portability reasons, i have created a C++ DLL with almost novice C++ knowledge just by searching thousands of pages, hundreds of compiling error corrections and couple of stackoverflow questions.
It is far beyond being stable but it is working most of the time :)
Just for curiosity and modularity reasons without importing the header and the cpp file to my application, i would like to ask your advice and help:
C++ Win32 Console Application
int main()
{
HMODULE lib = LoadLibrary(L"Serial.dll");
typedef void(*void_Connect)(UINT Port, UINT Baud);
void_Connect Connect = (void_Connect)GetProcAddress(lib, "Connect");
Connect(1, 9600);
}
I want to add an event into above C++ Win32 Console application, which will be triggered or hosted by the DLL
For example, a Received_Event(const char* data) or Connected_Event(BOOL status) with parameters.
Part from the DLL
typedef void(*fpCALLBACK) (const char* aParam);
static fpCALLBACK s_pCallback = NULL;
extern "C"
{
#define DLL __declspec(dllexport)
DLL void ReceivedEventRegister(fpCALLBACK p_pCallback)
{
s_pCallback = p_pCallback;
}
DLL void evntReceived(const char *receivedData);
}
DLL void evntReceived(const char *received)
{
s_pCallback(received);
}
I want to achive something similar to this C# version, with Standard C++
private static ManagedCallbackDelegate MessageCallbackDelegate;
public delegate void ManagedCallbackDelegate(string aParam);
[DllImport("Serial.dll", EntryPoint = "ReceivedEventRegister", CallingConvention = CallingConvention.Cdecl)]
private static extern void ReceivedEventRegister(ManagedCallbackDelegate callback);
static private void serialRecieved(string data)
{
Console.WriteLine(data);
}
static void Main(string[] args)
{
MessageCallbackDelegate = new ManagedCallbackDelegate(serialRecieved);
ReceivedEventRegister(MessageCallbackDelegate);
}
With C# code above, any data received by the DLL, the serialRecieved function is called in realtime. I want to achive this with Standard C++
I want to be informed or be aware of with the DLL process in realtime, in my Win32 C++ Console Application. ( Without blocking the Win32 Console application )
No MFC. if it is possible, no Component Object Model (It is possible with C# without need to convert the DLL into a COM). if it is possible, I want to do it with Standard C++.
I am compiling things with Visual Studio 2017 Community
Please go easy with me. It is a hobby for me and i just do it in my spare time.
Questions
Can you reference me some code examples written for this purpose, you are aware of?
Is there any specific name for this kind of communication that i can Google?
Is there any approach, you can suggest?
Thank you!
Once again, thanks to #dxiv's comment; helped me a lot, to sort things out! Even, when someone tells you that it should work, you resolve 90% of the problem.
At the DLL side
DLL void evntReceived(const char *received)
{
s_pCallback(received);
}
The DLL is calling the above function all the time as soon as it receives any data. To get that data realtime in your Win32 C++ Application, you need to import the DLL function which is calling the above callback function. Which is this
DLL void ReceivedEventRegister(fpCALLBACK p_pCallback)
part of the DLL.
As the DLL function's parameter type name fpCALLBACK suggests, our function parameter has to be as it is defind in the DLL:
typedef void(*fpCALLBACK) (const char* aParam);
At the Application side
//Callback Function: A function that is passed to another function as an argument
void Event_Received(const char *recvd)
{
std::cout << recvd << std::endl;
}
int main()
{
HMODULE lib = LoadLibrary(L"Serial.dll");
typedef void(*void_Connect)(UINT Port, UINT Baud);
void_Connect Connect = (void_Connect)GetProcAddress(lib, "Connect");
// Type definition of the function pointer.
// In our case: a Void type, which takes in a
// void type function pointer with a parameter (const char *aParam)
// dllCALLBACK is just a type name; you can name it whatever you want.
typedef void(*dllCALLBACK) (void(*fpCALLBACK) (const char* aParam));
// Now we will import the function from our DLL which takes
// the same parameters as we defined above, to call
// our callback function
dllCALLBACK ReceivedEventRegister = (dllCALLBACK)GetProcAddress(lib, "ReceivedEventRegister");
// Call the callback function with the imported function above.
// And that is all there is to it. As soon as any data received
// by our DLL - our Event_Received callback function
// fires in our C++ Win32 Console Application
ReceivedEventRegister(Event_Received);
Connect(1, 9600);
}
If you are a novice like me this video may help you to understand about Callback functions.
Thank you #dxiv again, taking time to read and answer my question!
I have problem with Boost.Interprocess (v1.66) library which I use in my C/C++ library which I use in C# through Marshalling (calling C native code from C#).
I found the problem if I was using Boost.Interprocess named_semaphore for sync between processes. (in open_or_create mode)
If I use my C/C++ lib with another native C/C++ code everything works fine (under newest Windows 10, Linux (4+ kernel) and even Mac OS X (>=10.11)).
The problem occurred under Windows - with C# I have C wrapper around C++ code. If I use Marshalling with simple own-build EXE --> Everything works! But If I use The same C# code (with the same C lib) in the third party application as a DLL plugin I got segfault from get_bootstamp in named_semaphore.
So I have third-party C# SW for which I create plugins (C# DLL). In that plugin I use my C library through marshalling. Marshalling work fine in test C# project (which just call C functions from C lib) but same code segfault in third-party SW.
C Library workflow:
Init all necessary C structures
Start desired TCP server (native C/C++ app) using Boost.Process
Wait for server (through named_semaphore) <-- segfault
Connect to the server...
C# code has same workflow.
Found the problem
The problem occured in boost::interprocess::ipcdetail::get_booststamp (which is called in named_semaphore). here:
struct windows_bootstamp
{
windows_bootstamp()
{
//Throw if bootstamp not available
if(!winapi::get_last_bootup_time(stamp)){
error_info err = system_error_code();
throw interprocess_exception(err);
}
}
//Use std::string. Even if this will be constructed in shared memory, all
//modules/dlls are from this process so internal raw pointers to heap are always valid
std::string stamp;
};
inline void get_bootstamp(std::string &s, bool add = false)
{
const windows_bootstamp &bootstamp = windows_intermodule_singleton<windows_bootstamp>::get();
if(add){
s += bootstamp.stamp;
}
else{
s = bootstamp.stamp;
}
}
If I debug to the line
const windows_bootstamp &bootstamp = windows_intermodule_singleton<windows_bootstamp>::get()
booststamp.stamp is not readable. The size is set to 31, capacity is set to some weird value (like 19452345) and the data is not readable. If i step over to
s += bootstamp.stamp;
the segfault occured!
Found the reason
I debug once more and set debug point to the windows_bootstamp constructor entry and I got no hit so the stamp is not initialized (I guess).
Confirmation
If I change get_bootstamp to
inline void get_bootstamp(std::string &s, bool add = false)
{
const windows_bootstamp &bootstamp = windows_intermodule_singleton<windows_bootstamp>::get();
std::string stamp;
winapi::get_last_bootup_time(stamp);
if(add){
s += stamp;
}
else{
s = stamp;
}
}
Recompile my lib and exe - everything works fine (without any problem).
My question is - what I am doing wrong? I read Boost.Interprocess doc really thoroughly but there are no advice/warnings about my problem (yeah there is "COM Initialization" in Interprocess doc but it not seems helpfull).
Or it's just a bug in Boost.interprocess and I may report it to Boost bug tracker?
Notice - if I start server manually (before I run C# code) It works without segfaults
I'm calling C++ COM interface from C# code using a dll.
At the C++ side, i have a WCHAR* global variable which is updated through a method with a BSTR parameter.
The problem is that, when i first call the C++ wrapper method from C# to change the variable, everything works fine, but at the moment that i call another C++ wrapper method from C#, unexplainably the WCHAR* global variable points to a different memory position and its value gets corrupted.
Some code:
//THE C# side:
capture.filename = PATH + "\\" + DIRECTORY_NAME + "\\";
capture.MaxMinutesPerFile = MAX_MINUTE_PER_FILE;
"capture" is an object of the C++ wrapper class (i think it is autogenerated when building the C++ code to a DLL. Not my code).
"filename" property calls a "put_FileName" C++ method and "MaxMinutesPerFile" a "put_MaxMinutesPerFile" method.
//C++ code
WCHAR *m_bstFileName = L"None";
(...)
STDMETHODIMP CCaptureMF::put_FileName(BSTR PathName)
{
EnterCriticalSection(&m_critsec);
HRESULT hr = S_OK;
m_bstFileName = PathName;
LeaveCriticalSection(&m_critsec);
return hr;
}
STDMETHODIMP CCaptureMF::put_MaxMinutesPerFile(LONG Minutes)
{
MaxMinutes= Minutes;
return S_OK;
}
So, after calling "put_FileName", "m_bstFileName" is updated correctly with the "PathName" value, but just after calling "MaxMinutesPerFile" (or any other interface wrapper method), "m_bstFileName" gets corrupted pointing to a different memory position and fulfilled with strange data.
Thank you.
EDIT:
To make a buffer of "m_bstFileName" and then copy the "PathName" data, i used the following code, taking in mind that "m_bstFileName" size can change at runtime:
m_bstFileName = (wchar_t*)malloc(sizeof(PathName));
wcscpy(m_bstFileName, PathName);
That code works fine, but the rest of the program behaves bad. I´m not sure why, i should investigate more, but for now, could you analyze that pice of code and tell me if it is correct or if i should implement it in other way?
SOLUTION:
Ok, following your recomendations i have finally implemented the following code, which works perfect for the whole application:
CComBSTR m_bstFileName = L"None";
(...)
STDMETHODIMP CCaptureMF::put_FileName(BSTR PathName)
{
EnterCriticalSection(&m_critsec);
HRESULT hr = S_OK;
m_bstFileName = PathName;
if (g_pCapture)
{
g_pCapture->SetPath(m_bstFileName);
}
LeaveCriticalSection(&m_critsec);
return hr;
}
If you think that this can be implemented better, just tell.
Thank you for your help!
In the most basic case you'll need to make a buffer to do a string copy into. Same operation could be accomplished via assignment with cstring, ccombstr, std::string, etc. depending on the framework you're using.
You need to copy the string to m_bstFileName, not just assign it. Use something like
strcpy(m_bstFileName, PathName);
have been working on this for hours, couldn't get it work :(
below code gives exception "Attempted to read or write protected memory. This is often an indication that other memory is corrupt." this is fixed, see below update
but when i change the return type to int and return a int value, it works. how to make it to return a float value? thanks.
vs2012
c++ code
extern "C" __declspec(dllexport)
float compute_similarity()
{
return 1.01;
}
c# code
[DllImport("demo.exe", EntryPoint = "compute_similarity", CallingConvention = CallingConvention.Cdecl)]
public static extern float compute_similarity();
public void Get()
{
float x = compute_similarity(); // here returns random value
}
=====================================
UPDATE
See comments from David below, the problem was the c# project is targeting x64 and c++ is targeting Win32. After changing the c# project to target to x86, the exception went away.
However the call from c# returns some random value instead of 1.01 expected
I think your problem is that your function is declared in an executable rather than a DLL. Convert your native code into a library project and compile a DLL.
Your code will result in a call to LoadLibrary passing the file name demo.exe. The documentation for LoadLibrary says:
LoadLibrary can also be used to load other executable modules. For example, the function can specify an .exe file to get a handle that can be used in FindResource or LoadResource. However, do not use LoadLibrary to run an .exe file. Instead, use the CreateProcess function.
And so your code is doing exactly what you are told not to do. The call to LoadLibrary will succeed. The subsequent calls to GetProcAddress will succeed. But the module that you loaded is not fit to execute code.
I am trying to call out to a legacy dll compiled from FORTRAN code. I am new to Interop, but I've read some articles on it and it seems like my case should be fairly straightforward.
The method I really want to call has a complex method signature, but I can't even call this simple GetVersion method without getting a protected memory violation.
Here's my DllImport code:
[DllImport("GeoConvert.dll",
EntryPoint="_get_version#4",
CallingConvention=CallingConvention.StdCall)]
public static extern void GetGeoConvertVersion([MarshalAs(UnmanagedType.LPStr, SizeConst=8)]
ref string version);
Here's the FORTRAN code:
SUBROUTINE GetVer( VRSION )
C
!MS$DEFINE MSDLL
!MS$IF DEFINED (MSDLL)
ENTRY Get_Version (VRSION)
!MS$ATTRIBUTES DLLEXPORT,STDCALL :: Get_Version
!MS$ATTRIBUTES REFERENCE :: VRSION
!MS$ENDIF
!MS$UNDEFINE MSDLL
C
CHARACTER*8 VRSION
C
VRSION = '1.0a_FhC'
C
RETURN
END
Here's my unit test that fails:
[Test]
public void TestGetVersion()
{
string version = "";
LatLonUtils.GetGeoConvertVersion(ref version);
StringAssert.IsNonEmpty(version);
}
Here's the error message I get:
System.AccessViolationException
Message: Attempted to read or write protected memory.
This is often an indication that other memory is corrupt.
Other things I've tried:
Using the default marshalling
Passing a char[] instead of a string (get method signature errors instead)
...snip...
OK, I got it to work, the problem was passing by ref. I'm not sure why, but this works:
...snip...
You need to pass by reference because that is the semantic being used by the FORTRAN code. The client code is passing in a buffer that the FORTRAN code is going to write to in lieu of using a return value.
...snip...
!MS$ATTRIBUTES REFERENCE :: VRSION
...snip...
This attribute in your FORTRAN code specifies that this parameter is passed by reference. That means the FORTRAN code is going to write to this address. If the DllImport doesn't declare it as a ref value also, you will get an access violation.
OK, I got it to work, the problem was passing by ref. I'm not sure why, but this works:
[DllImport("GeoConvert.dll",
EntryPoint="_get_version#4",
CallingConvention=CallingConvention.StdCall)]
public static extern void GetGeoConvertVersion([MarshalAs(UnmanagedType.LPArray)]
byte[] version);
With this test:
[Test]
public void TestGetVersion()
{
//string version = "";
byte[] version = new byte[8];
LatLonUtils.GetGeoConvertVersion(version);
char[] versionChars = System.Text.Encoding.ASCII.GetChars(version);
string versionString = new string(versionChars);
}
Have you tried using a StringBuilder?
Create your String as a StringBuilder and pass that into the dll function.
Im unsure as to what Marashlling statement to use, perhapse the default might work.
Have a look at: Marshal C++ “string” class in C# P/Invoke
Heres a good article the might help as well: Interop Marshalling
I cannot try this solution since I do not have a FORTRAN compiler, but I think this would work for you:
[DllImport("GeoConvert.dll",
EntryPoint="_get_version#4",
CallingConvention=CallingConvention.StdCall,
CharSet=CharSet.Ansi)]
public static extern void GetGeoConvertVersion(StringBuilder version);
Thank you all guys, I've been trying to pass a string from c# to a subroutine from fortran dll and this method was the only working one among lots of others