0

I am using a mixed assembly with the following code:

#include "stdafx.h"

#pragma managed

using namespace System::Security::Cryptography;

array<System::Byte, 1> ^ComputeHashS(array<System::Byte, 1> ^Data) {

    RIPEMD160Managed^ r = gcnew RIPEMD160Managed();

    return r->ComputeHash(Data);

}

#pragma unmanaged

BYTE *DoWork(BYTE *Data) {
    BYTE *n = ComputeHashS(Data);
    return DoSomething(n, 20);
}

where DoSomething(array, len) is an UNMANAGED C++ function. However, I get the following error:

argument of type "BYTE *" is incompatible with parameter of type "cli::array<unsigned char, 1> ^".

I am new to C++/CLI and especially mixed-mode assemblies, so how can I resolve this error?

2
  • 1
    Use pin_ptr: msdn.microsoft.com/en-us/library/1dz8byfh.aspx Commented Sep 1, 2014 at 7:53
  • You cannot compile that function as unmanaged code. You cannot use pin_ptr<>, the array will no longer be pinned when other code accesses the BYTE*. The most glaring problems with returning raw pointers to arrays is that the caller doesn't know how long the array might be and cannot reliably release the memory that was allocated for the array. You need to solve those problems first before you can consider calling the function from managed code. Commented Sep 1, 2014 at 10:48

1 Answer 1

1

In order for unmanaged code to reliably access the managed data you will need to "pin" the managed data first; before trying to pass it into an unmanaged API. Use pin_ptr for this purpose (and it must be pinned for the duration of the unmanaged call required).

The pin_ptr has the added advantage that it can be used for where a native pointer is needed. From MSDN;

A pin_ptr represents a superset of the functionality of a native pointer. Therefore, anything that can be assigned to a native pointer can also be assigned to a pin_ptr. An interior pointer is permitted to perform the same set of operations as native pointers, including comparison and pointer arithmetic.

An object or sub-object of a managed class can be pinned, in which case the common language runtime will not move it during garbage collection. The principal use of this is to pass a pointer to managed data as an actual parameter of an unmanaged function call. During a collection cycle, the runtime will inspect the metadata created for the pinning pointer and will not move the item it points to.

A basic code sample to illustrate;

int main()
{
    array<System::Byte>^ a = gcnew array<System::Byte>(10);
    cli::pin_ptr<System::Byte> p = &a[0];
    unsigned char* b = p;
}

Given the managed/unmanaged function and data requirements here, it may be more feasible to change the managed function ComputeHashS() to work with unmanaged data (mixed mode) and allow it to perform the appropriate conversions. The conversion/marshalling between System::Byte and BYTE here works as expected. Note; the pin_ptr here is not specifically required with these conversions since the unmanaged code never accesses the data in managed array (it is left in as commented for the more general case).

cli::array<Byte>^ marshal_array(std::vector<BYTE> const& src)
{
    cli::array<Byte>^ result = gcnew cli::array<Byte>((int)src.size());
    if (src.size()) {
        //cli::pin_ptr<Byte> pinned = &result[0];
        for (std::size_t i = 0; i < src.size(); ++i) {
            result[(int)i] = src[i];
        }
    }
    return result;
}

std::vector<BYTE> marshal_array(cli::array<Byte>^ const& src)
{
    std::vector<BYTE> result(src->Length);
    if (src->Length) {
        //cli::pin_ptr<Byte> pinned = &src[0];
        for (int i = 0; i < src->Length; ++i) {
            result[(std::size_t)i] = src[i];
        }
    }
    return result;
}

void ComputeHashS(std::vector<BYTE> in, std::vector<BYTE>& out)
{
    array<System::Byte, 1>^ Data = marshal_array(in);
    RIPEMD160Managed^ r = gcnew RIPEMD160Managed();
    array<System::Byte, 1>^ result = r->ComputeHash(Data);
    out = marshal_array(result);
}
Sign up to request clarification or add additional context in comments.

10 Comments

I really don't understand how to change my function. Even with the above info, which resolves the return type mismatch error, there is still the problem with parameter passing, specifically: error C2664: 'cli::array<unsigned char,1> ^ComputeHashS(cli::array<unsigned char,1> ^)' : cannot convert argument 1 from 'BYTE *' to 'cli::array<unsigned char,1> ^'
@Jason. When coping back into the managed cli::array, you loop through all the elements in the BYTE* and assign the values appropriately (looping and using the index operator).
@Jason. In this case, a signature more like void ComputeHashS(array<System::Byte, 1> ^Data, array<System::Byte, 1>^ hash) may be better. Or void ComputeHashS(array<System::Byte, 1> ^Data, array<System::Byte, 1>^% hash)
I cannot use managed types inside the DoWork() function, as I have the #pragma unmanaged and want this function to be unmanaged...
And cannot use unmanaged types inside the ComputeHashS() function (#pragma managed label)...
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.