I am trying to pass a byte array from c# to c++ . I am using Component Object Model.This is my first COM experience. How can i pass byte array from c# to c++? Any advice? There is no problem when i try to pass another types (strings, int etc.) except binary arrays
Thanks
Errors i get
1-)error C2440: '=' : cannot convert from 'SAFEARRAY' to 'byte'
2-)IntelliSense: no suitable conversion function from "SAFEARRAY" to "byte" exists
Here is the code i wrote
C# Side,
public byte[] GetImage()
{
try
{
SqlCommand command = new SqlCommand("this command returns Varbinary ", conn);
SqlDataAdapter dataAdapter = new SqlDataAdapter(command);
ImgBlobDT = new DataTable("ImgBlobDT");
dataAdapter.Fill(ImgBlobDT);
DataRow dr = ImgBlobDT.Rows[0];
imgBytes = (byte[])dr["ImgBinary"];
}
catch ()
{
//some codes
}
return imgBytes;
}
C++ Side
CoInitialize(NULL);
IDBCPtr obj;
obj.CreateInstance(__uuidof(DBC));
byte bytesArr[] = obj->GetImage();
CoUninitialize();
You either need to allocate unsafe memory as @KimKulling's answer suggests, or send the array as an appropriately marked SafeArray
. An example demonstrating three different methods is excerpted below, and available on github .
The C# interface looks would look like:
[ComVisible(true)]
[Guid("7FE927E1-79D3-42DB-BE7F-B830C7CD32AE")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IImageProvider
{
[return: MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_UI1)]
byte[] GetImage(int foo);
IntPtr GetImageAsUnmanaged(int foo, out int cbBuff);
void GetImageAsUnmanagedPreallocated(int foo, ref int cbBuff, IntPtr pBuff);
}
With a C# server implementation of:
[ClassInterface(ClassInterfaceType.None)]
[ComVisible(true)]
[Guid("D133B928-A98B-4006-8B00-4AA09BD042E7")]
[ProgId("CSByteArrayServer.ImageProvider")]
public class ImageProvider : IImageProvider
{
private const int E_INVALIDARG = unchecked((int)0x80070057);
RNGCryptoServiceProvider crng = new RNGCryptoServiceProvider();
[return: MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_UI1)]
public byte[] GetImage(int foo)
{
return GetBytes();
}
private byte[] GetBytes()
{
byte[] data = new byte[500];
crng.GetBytes(data);
return data;
}
public IntPtr GetImageAsUnmanaged(int foo, out int cbBuff)
{
var data = GetBytes();
var result = Marshal.AllocCoTaskMem(data.Length);
Marshal.Copy(data, 0, result, data.Length);
cbBuff = data.Length;
return result;
}
public void GetImageAsUnmanagedPreallocated(int foo, ref int cbBuff, IntPtr pBuff)
{
var data = GetBytes();
if (cbBuff < data.Length)
{
cbBuff = data.Length;
throw Marshal.GetExceptionForHR(E_INVALIDARG);
}
cbBuff = data.Length;
Marshal.Copy(data, 0, pBuff, data.Length);
}
}
And a C++ client of:
int main()
{
CoInitialize(NULL);
IImageProvider *pImgProvider;
CoCreateInstance(__uuidof(ImageProvider), nullptr, CLSCTX_INPROC_SERVER, __uuidof(IImageProvider), (LPVOID*)&pImgProvider);
int imageId = 0;
// with SafeArray
{
SAFEARRAY *pSafeArray;
auto hr = pImgProvider->GetImage(0, &pSafeArray);
assert(hr == S_OK);
CComSafeArray<BYTE> safeArray;
safeArray.Attach(pSafeArray);
ManipulateData(&safeArray);
// CComSafeArray will free the memory allocated by pSafeArray
}
// with CoTaskMemAlloc
{
char *pData;
int cbData;
auto hr = pImgProvider->GetImageAsUnmanaged(0, (long*)&cbData, (long*)&pData);
assert(hr == S_OK);
ManipulateData(pData, cbData);
CoTaskMemFree(pData);
}
// with caller allocate
{
int cbData;
auto hr = pImgProvider->GetImageAsUnmanagedPreallocated(imageId, (long*)&cbData, (long)nullptr);
assert(hr == E_INVALIDARG);
char *pData = new char[cbData];
hr = pImgProvider->GetImageAsUnmanagedPreallocated(imageId, (long*)&cbData, (long)pData);
assert(hr == S_OK);
ManipulateData(pData, cbData);
delete[] pData;
}
system("pause");
CoUninitialize();
return 0;
}
void ManipulateData(char* pBuff, int cbBuff)
{
char hash = 0;
for (int i = 0; i < cbBuff; i++)
{
hash ^= pBuff[i];
}
std::cout << "Hash is " << +hash << std::endl;
}
void ManipulateData(CComSafeArray<BYTE> *pSafeArray)
{
BYTE hash = 0;
for (ULONG i = 0; i < pSafeArray->GetCount(); i++)
{
hash ^= pSafeArray->GetAt(i);
}
std::cout << "Hash is " << +hash << std::endl;
}
You need to marshall your byte array into the unsave c++ world. You can use the Marshal-class to do this: https://msdn.microsoft.com/en-us/library/system.runtime.interopservices.marshal_methods(v=vs.110).aspx
So you can return an unsafe pointer tor c++ by using Marshaling:
public IntPtr GetImage() {
DataRow dr = ImgBlobDT.Rows[0];
byte[] imgBytes = (byte[])dr["ImgBinary"];
// Initialize unmanaged memory to hold the array.
int size = Marshal.SizeOf(imgBytes [0]) * imgBytes .Length;
IntPtr pnt = Marshal.AllocHGlobal(size);
// Copy the array to unmanaged memory.
Marshal.Copy(imgBytes , 0, pnt, imgBytes .Length);
// your method will return a unmanaged pointer instead of byte[]
return pnt;
}
In c++ you should be able to use this pointer like:
CoInitialize(NULL);
IDBCPtr obj;
obj.CreateInstance(__uuidof(DBC));
IntPtr intPtr = obj->GetImage();
CoUninitialize();
And don't forget to release the allocated unsafe memory after using it in C++:
// Free the unmanaged memory.
Marshal.FreeHGlobal(pnt);
Hopefully this could help you.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.