I am trying to call a function from a DLL generated in LabVIEW. I thought this was going to be far more straightforward than it is turning out to be. The function is described below:
void __cdecl Device_Init(char DevName[]);
So in my C# code I am trying the following:
[DllImport(#"Device.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern void Device_Init(StringBuilder name);
I call this in my application by simply using the following:
StringBuilder devName = new StringBuilder(DeviceName);
Device_Init(devName);
Rather than getting any initialization on my device, I see a LabVIEW vi window pop up that has a title akin to a different method within the dll (i.e. AF1_GetPressure.vi). The application then hangs with this LabVIEW window popped up and I have to exit the debugging session.
I guess my question is how my function signature might be erroneous... I used StringBuilder as I found an example on the NI website that seemed to indicate that LabVIEW requires this variable type to better ascertain the number of characters in the array. http://www.ni.com/example/31050/en/
I have tried all kinds of different combinations of parameter types but I simply can't seem to get this to work. If I try calling the dll from C++ then I can get things to work. Although, oddly, I had to dynamically load the dll in C++ because I was getting a dll initialization failure when I tried to load it with the application.
Any help would be greatly appreciated!
I was able to build a DLL with LabView 2012, and import it into a .NET 4.0 console application, call the function, and receive a result. Here is a screenshot of the VI:
And here is the import statement in C#:
[DllImport(#"SharedLib.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern int StringLength(string str);
I would recommend trying something very simple like this and see if you can get it working.
I should note that I tried passing my parameter as a StringBuilder object and that worked as well - and I didn't expect it to!
Also, I recommend posting this question on the LabView forums. I was always able to get a very quick response there, and I think with LabView, you're likely to get a better response there than StackOverflow.
As requested, here are the contents of the .h file generated by LabView:
#include "extcode.h"
#pragma pack(push)
#pragma pack(1)
#ifdef __cplusplus
extern "C" {
#endif
/*!
* StringLength
*/
int32_t __cdecl StringLength(char String[]);
long __cdecl LVDLLStatus(char *errStr, int errStrLen, void *module);
#ifdef __cplusplus
} // extern "C"
#endif
#pragma pack(pop)
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 am having an issue passing a simple string from my .NET app, compiled as 64-bit, to my native DLL, also compiled as 64-bit.
C++ signature:
DllExport void SetFoo(LPWSTR foo);
C# signature:
[DllImport(Library, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
internal static extern void SetFoo(
[In][MarshalAs(UnmanagedType.LPWStr)] string foo);
Then I run the following code in C#:
X.SetFoo("some string");
When it reaches the debugger in the DLL, the value is swearing at me in Chinese: Ⴐ虘翺
When I change both the .NET and native code to 32-bit, the value I get in the debugger is the correct string. Where am I being stupid?
Minimal reproduction as a Visual Studio 2015 Solution: download here
To reproduce:
Create a new Visual Studio solution with a WinForms project.
Add a Win32 Project, of type DLL to the solution
Add the following files:
Foo.h:
extern "C" {
__declspec( dllexport ) void SetFoo(LPWSTR);
}
Foo.cpp:
#include "stdafx.h"
#include "Foo.h"
DllExport void SetFoo(LPWSTR foo)
{
}
Set a breakpoint on the opening brace in SetFoo
Add a button to the winforms form
Double click it, and call SetFoo("abc123").
Implement SetFoo:
Form1.cs:
[DllImport("Win32Project1.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
public static extern void SetFoo(string text);
Change the apps to build in 64-bit mode by opening Configuration Manager.
Set Win32Project1 to build as x64
Set WindowsFormApplication1 to build as x64, by picking platform new, pick x64, OK.
Change the output directory of WindowsFormsApplication1 to match the output directory of the other app.
Start without debugging.
Attach debugger (Ctrl+Alt+P) by setting Attach to to Managed (v4.5, v4.0) code, Native code and finding the process.
Observe value at breakpoint.
When you interpret ANSI encoded latin text as UTF-16 you see Chinese characters. That's clearly what is happening. So your C# code is sending ANSI encoded text somehow.
The p/invoke would be better written like this:
[DllImport(Library, CallingConvention = CallingConvention.Cdecl,
CharSet = CharSet.Unicode)]
internal static extern void SetFoo(string foo);
The [in] is not needed, it is the default. And the MarshalAs is pointless since you specified CharSet.Unicode. However, neither change affects the semantics.
The only sound explanations for what you describe in the question are:
The actual code is not as you have described it, or
There is a defect in the debugger.
I suggest that you change the unmanaged function to
DllExport void SetFoo(LPWSTR foo)
{
MessageBoxW(0, L"", foo, MB_OK);
}
If the message box displays the correct text then the conclusion would appear to be that the debugger is defective.
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 have a C++ DLL that is being called like below from a C# console app.
When run in Visual Studio to debug it, it throws an exception saying the stack is unstable and to check that the method arguments are correct. However, if I run the *.exe outside of VS from Windows Explorer it retuns data to the screen as expected.
How can I get this to run within Visual Studio?
Thanks
**From the C++ header file:**
#ifdef RFIDSVRCONNECT_EXPORTS
#define RFID_CONN_API __declspec(dllexport)
#else
#define RFID_CONN_API __declspec(dllimport)
#endif
RFID_CONN_API BSTR rscListDevices( long showall ) ;
[DllImport("MyDLL.dll")]
[return: MarshalAs(UnmanagedType.BStr)]
public static extern string rscListDevices(int showall);
static void Main(string[] args)
{
string data= rscListDevices(0);
Console.WriteLine(data);
Console.ReadKey();
}
Firstly, make sure you're using the same calling convention in both C++ and C#.
I suspect that the /Gd compiler option is set (since it is set by default), so __cdecl is used as default calling convention for unmarked functions.
You can fix crashes by either specifing the same calling convention in your C# code:
[DllImport("MyDLL.dll", CallingConvention=CallingConvention.Cdecl))]
[return: MarshalAs(UnmanagedType.BStr)]
public static extern string rscListDevices(int showall);
Or changing rscListDevices's calling convention to __stdcall (which is the default in C#):
RFID_CONN_API BSTR __stdcall rscListDevices( long showall ) ;
You can also set __stdcall as the default calling convention for the unmarked functions in your C++ DLL by changing compiler option from /Gd to /Gz in manually or using the Project Properties dialog:
But if you really want to disable MDA, you can go Debug->Exceptions and uncheck Managed Debugging Assistance.
You can read more about pInvokeStackImbalance and MDA here and here.
I can access a method from a C++ Dll using C# using this method in the C++:
extern "C"
{
__declspec(dllexport) void DisplayHelloFromDLL()
{
printf ("Hello from DLL !\n");
}
}
this works great...but the solution I am working with uses this as the entry point:
extern "C" int WINAPI _tWinMain(HINSTANCE hInstance,
HINSTANCE /*hPrevInstance*/,
LPTSTR lpCmdLine,
int /*nShowCmd*/)
Is there a way I can access this like I have done with the __declspec method?
Cheers
That is not a DLL entry point, that is a primary application entry point. You will need to create it as a new process via CreateProcess.
_tWinMain is actually a #define to either WinMain or wWinMain. You also need to make sure it's actually exported.
That being said, why would a DLL have a WinMain function at all? You should just export a normal function like DisplayHelloFromDLL.
[edit]
The project you are trying to reference -- the one with _tWinMain -- is an EXE (as #DeadMG says). You should not try to import its functions from C# like you do with DLLs; instead you should launch it with Process.Start.
The answer was to call a function created in the C++ using:
extern "C"
{
__declspec(dllexport) void StartAgent()
{
printf ("Starting Agent... \n");
StartServer(true);
RunMainLoop();
}
}
This is then called in the C# using:
[DllImport("myDll.dll")]
public static extern string StartAgent();
StartAgent();
Calling this from the C# and into the C++ gets the application running.