Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing Array of Bool to C++ Code from C#

Tags:

c++

arrays

c#

I'm currently working on a project with a few other people, who are sadly no longer available for reference material. This project uses C# for the GUI, and C++ for updating our archaic database. At the moment, I have this function in C#:

[DllImport("Synch.DLL", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
    //Synch bool Synch(void * voidPtr, const TCHAR * databaseDir, const TCHAR * truckDir)
    static extern Int32 Synch(IntPtr pSync, string databaseDir, string destDir, ref bool[] wholeFiles);

wholeFiles is an array of booleans used to determine if certain files should be synced or not, based on user input.

The C++ is supposed to take in this array, and be able to flip some of the bools to true or false as certain conditions are met.

if (numSyncRecords > 0){
        Log::WriteVerbose(_T("   %d recs, %d differences found"), (int) numRecsUsed, (int) numSynRecords);
        if(wholeFiles[sourceFileID]){
            CString tempPath = _T("");
            tempPath += destDir;
            tempPath += fileName;
            DeleteFile(tempPath);
        }
        else
            wholeFiles[sourceFileID] = false;
    }
    else
        wholeFiles[sourceFileID] = false;

In the case above, I pass in Trues, and if there are no changes to the file, set it to false to avoid processing a file that doesn't need it.

Now, my issue is, when the C++ code is finished, there are still True marks when they should be set to false. If I send it in by ref, then I get a stack overflow. Without ref, the array doesn't seem to change.

Edit: This was requested.

SYNC_API Int32 Synch(void * voidPtr, const TCHAR * databaseDir, const TCHAR * truckDir, bool wholeFiles[])

Edit2: I have, in the C++ code, a log statement that loops over the array and outputs true/false once per line per position in the wholeFiles array, and I end up with the appropriate log output, but when I look at the Array on the C# code after that is done, it's unchanged.

like image 752
user3169698 Avatar asked Dec 12 '25 23:12

user3169698


2 Answers

If the comment below your DllImport is the exact function declaration on the C++ side, then your wholeFiles array will never be seen on the C++ side because it's not expecting a 5th argument. Make sure to fix that issue first.

In C# there's only one size of bool, and that is 1 byte. In C++ there are two types, the C-style 1-byte bool and the Win32 4-byte BOOL. By default P/Invoke will marshal bools as BOOL.

So if that first bit isn't your entire issue and it's not working, then you are using the C-style bool and need to tell C# that with [MarshalAs] like this:

[DllImport("Synch.DLL", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
static extern Int32 Synch(
    IntPtr pSync,
    string databaseDir,
    string destDir,
    [MarshalAs(UnmanagedType.LPArray, ArraySubType=UnmanagedType.I1)]
    bool[] wholeFiles);

You also don't need the ref because an array is a reference type in itself. A ref array would only be useful if the function you are calling wants to swap the array with another one.

Also note that you don't have to make it multiline, the attribute will work if the function is all in one line, I just did that for readability here.

like image 200
Robert Rouhani Avatar answered Dec 15 '25 11:12

Robert Rouhani


It's hard to tell exactly what#s going on without the full C++ function, but one thing that you could try is getting the C++ function to return the modified array (rather than relying on the in-place modification).

[DllImport("Synch.DLL", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
// Now return an IntPtr
static extern IntPtr Synch(IntPtr pSync, string databaseDir, string destDir, ref bool[] wholeFiles);

wholeFiles = Synch(..., wholeFiles);

Option 2

Another option is to declare a buffer in C#, copy the wholeFiles array to that buffer, and then pass the address of the buffer through to C# (this is what I've done in the past). The C++ code then modifies the buffer and you can then copy this back into wholeFiles after the call to the dll completes.

Option 3

The best option (in my opinion) is to use SWIG. SWIG makes it very easy to wrap C++ functions for C# use (and actually many other languages). This will give you a C# function called Synch that calls the dll behind the scenes, dealing with any memory issues for you.

Example (simple) synch.i file assuming C++ header is called synch.h:

%module SynchSWIG
%{
#include "synch.h"
%}

%include "synch.h

Generate wrappers with command (not tested):

swig -c++ -csharp -dllimport Synch.DLL synch.i

This will give you C# files to compile into your C# project, and a C++ file to compile into the C++ dll project.

like image 41
Tim B Avatar answered Dec 15 '25 13:12

Tim B



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!