C
C#2y ago
acookook

❔ Problem with including C DLL in a C# console app project.

So I have this C DLL imported into my console app (.NET 5). I simply cannot get to call it correctly, since everytime I get, something along the lines, that something with the entrypoint is broken, like: System.EntryPointNotFoundException: 'Unable to find an entry point named 'getCalculatedFitting' in DLL 'BPCalculationDLL.dll'.' The code with which I am defining this is [DllImport("BPCalculationDLL.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "getCalculatedFitting")] public static extern int getCalculatedFitting(float[] fittinglData, uint length, char ch); Honestly I tried a few things, but none of them seem to work in the current VS2022 editor. Does anyone have any idea how import this DLL and call it in another function?
60 Replies
Kouhai
Kouhai2y ago
use dll viewer or dependency walker and check the exported symbols
acookook
acookookOP2y ago
It looks like it is a C++ function after all. So what to include in the DLL import tag?
Accord
Accord2y ago
Looks like nothing has happened here. I will mark this as stale and this post will be archived until there is new activity.
acookook
acookookOP2y ago
Ok, I managed to get to work, but I am getting a System.AccessViolationException: 'Attempted to read or write protected memory. This is often an indication that other memory is corrupt.' The function i tried to import is something along the lines //private static extern int calculateBP(ushort[] data, uint len, char ch, uint samplingRate, IntPtr results); or //private static extern int calculateBP(out ushort[] data, uint len, char ch, uint samplingRate, IntPtr results); //private static extern int calculateBP(ref ushort[] data, uint len, char ch, uint samplingRate, ref Measurement[] results);defined as int CalculateBP(USHORT * data, UINT len, UCHAR ch, UINT samplingRate, measurement * results); I triend numerous things, including to allocate some memmory using marshal, but still no luck. Any idea how to debug and solve this. The measurement struct contains a number of floatsand integers which I have also defined, but apparently I can't call it correctly.
Jimmacle
Jimmacle2y ago
it would help to see both the C++ function signature and struct definition and C# struct definition and marshalling code first thing i see is your data types are probably wrong and i don't believe [DllImport] automatically marshals arrays for you
acookook
acookookOP2y ago
The C++ function as defined in the header file is the following: __declspec(dllexport) int CalculateBP(USHORT * data, UINT len, UCHAR ch, UINT samplingRate, measurement * results); while the DLL import is the following: [DllImport("BPCalculationDLL.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "?CalculateBP@@YAHPAGIEIPAUmeasurement@@@Z")] The C is defined by typedef struct{ float val1; float val2; int numBettween; int error; } measurement; I meant by marshaling is that is tried to use methods to deal with unmanaged code (from the System.Runtime.InteropServices.Marshal namespace), but to no success. I also trie some combinations mentioned on SO (https://stackoverflow.com/questions/28595903/copy-from-intptr-16-bit-array-to-managed-ushort), but no success.
Stack Overflow
Copy from IntPtr (16 bit) array to managed ushort
I have a IntPtr called rawbits, which points to a 10MB array of data, 16 bit values. I need to return a managed ushort array from that. The following code works but there is an extra BlockCopy I wo...
Jimmacle
Jimmacle2y ago
not just the dllimport attribute, the whole method signature
acookook
acookookOP2y ago
so as I defined it in C#?
Jimmacle
Jimmacle2y ago
right
acookook
acookookOP2y ago
Jimmacle
Jimmacle2y ago
first thing is C++ chars aren't the same as C# chars, the former is 1 byte and the second is 2 bytes
acookook
acookookOP2y ago
I tried multiple approaches with multiple combinations, but none of the seem to give the desired result
Jimmacle
Jimmacle2y ago
second you can't pass arrays to unmanaged code like that, the GC could move them while the unmanaged code is trying to work with them and that would be Bad they have to be pinned if you use the new-ish LibraryImport attribute instead of DllImport it can generate that code for you
acookook
acookookOP2y ago
oh, I understand How exactly do I do that?
Jimmacle
Jimmacle2y ago
it's more or less just replacing the attribute
acookook
acookookOP2y ago
oh, nevermind is this something in .net 7 only?
Jimmacle
Jimmacle2y ago
yeah
acookook
acookookOP2y ago
ok then I need first to upgrade to .net 7 from 5
Jimmacle
Jimmacle2y ago
if you can you might as well if not you can hand-write the same code that LibraryImport would generate
acookook
acookookOP2y ago
ok, so now i have this
acookook
acookookOP2y ago
acookook
acookookOP2y ago
an 3 errors
Kouhai
Kouhai2y ago
You need to specify which parameter is used for the array's size @šanji
acookook
acookookOP2y ago
like that?
acookook
acookookOP2y ago
[MarshalAs(unmanagedType: UnmanagedType.LPArray)] ushort[] data
Kouhai
Kouhai2y ago
No like, [MarshalAs(SizeParamIndex=1)] ushort[] data iirc default marshaling for arrays is already LPArray,
acookook
acookookOP2y ago
ok, so i am actuall more lost that I was before
Kouhai
Kouhai2y ago
😅 Okay, SizeParamIndex just specifies that the parameter index we'll use to indicate the length of the array data is at position 1 which is uint len
acookook
acookookOP2y ago
this just gives me an error
Kouhai
Kouhai2y ago
oops sorry [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)]
acookook
acookookOP2y ago
no problem
acookook
acookookOP2y ago
So i just need to figure out this
acookook
acookookOP2y ago
nevermin, it's a byte ok...I think I got it. private static partial int calculateBP([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] ushort[] data, uint len, [MarshalAs(UnmanagedType.U1)] byte ch, uint samplingRate, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] Measurement[] results); So now just need to pass in the neccessarry data and any empty measurement array of structs and I am golden? Do I need to worry if that the data is processed by other functions of the DLL and then that data can be corrupt? Ok, I definitely need to think deeper, since I get invalid measurement results out of the function, and also other functions return invalid data.
Kouhai
Kouhai2y ago
Can you show your C++ definition? I guess you've already sent it I think you're misunderstanding what passing an array by ref means passing an array by ref is basically passing a pointer to the array ref int[] is the equivalent of int** in C/C++
acookook
acookookOP2y ago
yeah I just would like to finally solve this issue. Although I fixed the DLL importing issue, I probably just too plug in different numbers
Accord
Accord2y ago
Was this issue resolved? If so, run /close - otherwise I will mark this as stale and this post will be archived until there is new activity.
acookook
acookookOP2y ago
Ok, so it seems that I magnaged VS no to crash on the call of the function, but something just doesn't seem right since i don't get the expected values in other functions
Kouhai
Kouhai2y ago
Hmmm, Are you still passing ref arrays?
acookook
acookookOP2y ago
getCalculatedFittingSize(UCHAR ch) is always returning zero, but anything above should be the result. The c# call is th following. private static partial int getCalculatedFittingSize([MarshalAs(UnmanagedType.U1)] byte cuffNumber); so now I am having the the function defined as private static partial int CalculateBP([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] ushort[] data, uint len, [MarshalAs(UnmanagedType.U1)] byte cuffNumber, uint samplingRate, ref Measurement result); and calling it as CalculateBP(dataToUShort, (uint)dataToUShort.Length, 0, samplingRate, ref result);
Kouhai
Kouhai2y ago
Okay, first are you 100% sure the C++ method produces the correct results?
acookook
acookookOP2y ago
yes, the same underlying code is used else where, so i am sure it is my C# code, not the C++ code.
Kouhai
Kouhai2y ago
Can you show your Measurement struct? Both C# and C++
acookook
acookookOP2y ago
the c++ struct is here https://discord.com/channels/143867839282020352/1090377331970949250/1091780654854840512 and C# stuct is [StructLayout(LayoutKind.Sequential)] public struct Measurement { [MarshalAs(UnmanagedType.R4)] float sys; [MarshalAs(UnmanagedType.R4)] float dia; [MarshalAs(UnmanagedType.R4)] float max; [MarshalAs(UnmanagedType.R4)] float amp; [MarshalAs(UnmanagedType.R4)] float hr; [MarshalAs(UnmanagedType.I4)] int maxIdx; [MarshalAs(UnmanagedType.I4)] int err; }
Kouhai
Kouhai2y ago
which fields have wrong values?
acookook
acookookOP2y ago
based on the c++ logic (which is not documented I can't say for sure, but since the int err is always zero, the values do not break the code or result in an error
Kouhai
Kouhai2y ago
Unfortunately that's not how it'll work Because your struct is different from this
typedef struct{
float val1;
float val2;
int numBettween;
int error;
} measurement;
typedef struct{
float val1;
float val2;
int numBettween;
int error;
} measurement;
This struct's error is at offset 0xC Meanwhile your 0xC offset is for amp The offsets are basically like this
typedef struct{
float val1; // 0x0
float val2; // 0x4
int numBettween; // 0x8
int error; // 0xC
} measurement
typedef struct{
float val1; // 0x0
float val2; // 0x4
int numBettween; // 0x8
int error; // 0xC
} measurement
public struct Measurement
{
[MarshalAs(UnmanagedType.R4)]
float sys; // 0x0

[MarshalAs(UnmanagedType.R4)]
float dia; // 0x4

[MarshalAs(UnmanagedType.R4)]
float max; // 0x8

[MarshalAs(UnmanagedType.R4)]
float amp; // 0xC

[MarshalAs(UnmanagedType.R4)]
float hr; // 0x10

[MarshalAs(UnmanagedType.I4)]
int maxIdx; // 0x14

[MarshalAs(UnmanagedType.I4)]
int err; // 0x18
}
public struct Measurement
{
[MarshalAs(UnmanagedType.R4)]
float sys; // 0x0

[MarshalAs(UnmanagedType.R4)]
float dia; // 0x4

[MarshalAs(UnmanagedType.R4)]
float max; // 0x8

[MarshalAs(UnmanagedType.R4)]
float amp; // 0xC

[MarshalAs(UnmanagedType.R4)]
float hr; // 0x10

[MarshalAs(UnmanagedType.I4)]
int maxIdx; // 0x14

[MarshalAs(UnmanagedType.I4)]
int err; // 0x18
}
acookook
acookookOP2y ago
yeah, my mistake...I was truncationg it,just to write minimum code the C# fields are present in C++ also
Kouhai
Kouhai2y ago
Huh, so CalculateBP should run okay then
acookook
acookookOP2y ago
so basically my values are messed up?
Kouhai
Kouhai2y ago
I honestly don't see what could fail, if you have the access to the source code you could step into it and see what happens after marshaling
acookook
acookookOP2y ago
is there a way to debug this DLL code? I mean like calling it from C#
Kouhai
Kouhai2y ago
Assuming you're using visual studio you just have to enable this on your launch profile
Kouhai
Kouhai2y ago
Accord
Accord2y ago
Was this issue resolved? If so, run /close - otherwise I will mark this as stale and this post will be archived until there is new activity.
acookook
acookookOP2y ago
Ok, so managed to get it to work, but after a few files have been successfully processed, I just a NullReferenceException and an Unable ro read memory in the "Watch" tab for the needed array. My guess is that i ran out of addressable memory. How to overcome that?
Kouhai
Kouhai2y ago
Why are you assuming you're running out of addressable memory? 😅
acookook
acookookOP2y ago
Because I can easily process a few files without any issues First I thought a file was corrupt, but then I tried out a new, independent set of files Apparently it is the last file that is being processed and the last data structure in this file.
Unknown User
Unknown User2y ago
Message Not Public
Sign In & Join Server To View
Accord
Accord2y ago
Was this issue resolved? If so, run /close - otherwise I will mark this as stale and this post will be archived until there is new activity.
Want results from more Discord servers?
Add your server