Managed byte[] to unmanaged byte array using ATL/COM - c#

I want to pass some image data from C# code to unmanaged C++ using ATL/COM
From C# code side i do something like this:
void SendFrame([In, MarshalAs(UnmanagedType.SafeArray, SafeArraySubType=VarEnum.VT_UI1)] ref byte[] frameData);
But I'm not sure how should I handle this function in my C++ code.
For now I have something like this:
_ATL_FUNC_INFO OnSendFrameDef = { CC_STDCALL, VT_EMPTY, 1, { VT_SAFEARRAY | VT_UI1 } };
void __stdcall OnSendFrame(SAFEARRAY* ppData)
{
BYTE* pData;
SafeArrayAccessData(ppData, (void **) &pData);
// Doing some stuff with my pData
SafeArrayUnaccessData(ppData);
}
Can anyone give me some suggestions how I can make this thing work?
Thanks.

Since the SAFEARRAY is already marshaled to your unmanaged code, and you're using ATL, you can use the CComSafeArray class, as it handles all the cleanup when working with SAFEARRAY instances:
_ATL_FUNC_INFO OnSendFrameDef = { CC_STDCALL, VT_EMPTY, 1,
{ VT_SAFEARRAY | VT_UI1 } };
void __stdcall OnSendFrame(SAFEARRAY* ppData)
{
// Wrap in a CComSafeArray.
// On the stack means all calls to cleanup
// will be cleaned up when the stack
// is exited.
CComSafeArray<byte> array;
array.Attach(ppData);
// Work with the elements, get the first one.
byte b = array.GetAt(0);
// And so on. The destructor for CComSafeArray
// will be called here and cleaned up.
}

I've managed to achieve my goal! For those who are interested:
My event handler descriptor looks like this:
_ATL_FUNC_INFO Stream::OnStreamFrameCallbackDef = { CC_STDCALL, VT_EMPTY, 1, { VT_DISPATCH } };
My C++ function:
void __stdcall Stream::OnStreamFrameCallback(IDispatch* pFrame)
{
// NOTE that this "IStreamFramePtr" is COM's Ptr of my "IStreamFrame"
MyCOM::IStreamFramePtr pStreamFrame = pFrame;
// Thanks casperOne♦ for this:
CComSafeArray<byte> array;
array.Attach(pStreamFrame->GetBuffer());
// Now I can do stuff that I need...
byte* pBuffer = &array.GetAt(0);
}
My "IStreamFrame" in my .tlh file looks like this:
struct __declspec(uuid("1f6efffc-0ac7-3221-8175-5272a09cea82"))
IStreamFrame : IDispatch
{
__declspec(property(get=GetWidth))
long Width;
__declspec(property(get=GetHeight))
long Height;
__declspec(property(get=GetBuffer))
SAFEARRAY * Buffer;
long GetWidth ( );
long GetHeight ( );
SAFEARRAY * GetBuffer ( );
};
In my C# code I have something like this:
[ComVisible(true)]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch)]
public interface IStreamFrame
{
int Width { [return: MarshalAs(UnmanagedType.I4)] get; }
int Height { [return: MarshalAs(UnmanagedType.I4)] get; }
byte[] Buffer { [return: MarshalAs(UnmanagedType.SafeArray, SafeArraySubType=VarEnum.VT_UI1)] get; }
};
[ComVisible(false)]
public delegate void StreamFrameCallback(IStreamFrame frame);
[ComVisible(true)]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch)]
public interface IMyCOMStreamEvents
{
[DispId(1)]
void OnStreamFrameCallback([In, MarshalAs(UnmanagedType.IDispatch)] IStreamFrame frame);
}
Things seems to work just fine. But if anyone have any suggestions or noticed that I'm doing something wrong please let me know.
Thanks.

I strongly recommend you to create an IDL file to design your COM interfaces.
Given your example in your answer, a rather minimal IDL file could be like this:
import "oaidl.idl";
[object,
uuid(1f6efffc-0ac7-3221-8175-5272a09cea82),
dual,
oleautomation]
interface IStreamFrame : IDispatch {
[propget]
HRESULT Width([out, retval] long *pWidth);
[propget]
HRESULT Height([out, retval] long *pHeight);
[propget]
HRESULT Buffer([out, retval] SAFEARRAY(byte) *pBuffer);
};
[uuid(1f6efffc-0ac7-3221-8175-5272a09cea83)]
library ContosoStreamFrame {
importlib("stdole32.tlb");
interface IStreamFrame;
};
You then use midl.exe to generate a .h with C/C++ interfaces, a _i.c for the CLSID and IID constants for C/C++ linking, a dlldata.c for RPC registering, a _p.c with proxy and stub marshalling stuff and a .tlb which is in general terms the parsed representation of the .idl file. This is all better described in the documentation.
EDIT: There seems to be no way to avoid the C/C++ file generation.
EDIT2: I just found a workaround, use nul as the output file for what you don't want. For instance, the following command only generates file.tlb:
midl.exe /header nul /iid nul /proxy nul /dlldata nul file.idl
Note: if your IStreamFrame interface is not meant to be used across processes, add the local attribute to the interface.
In C/C++, you can use the specific files that are generated, or #import the TLB file. In .NET, you can run tlbimp.exe on the TLB file, which generates a .NET assembly.
You can also use tlbexp.exe if your project is .NET centric. However, that'll require you to know the .NET COM annotations and what they mean in terms of IDL, so I'm not sure if there's any gain on saving one extra source-file in another language at the expense of lots of decoration noise in your interface and class definitions. It's perhaps a good option if you want to have full control of the classes and interfaces at the source level and you want to make it as easy as possible (read, optimized for usability and maybe speed) on .NET code.
Finally, you can automate all of this in Visual Studio by creating a project. If you use the IDL approach, add a custom build step that invokes midl.exe and tlbimp.exeand make the dependant projects depend on this project for a correct build order. If you use the .NET approach, add a custom build step that invokes tlbexp.exe and make the dependant C/C++ projects depend on this project.
EDIT: If you don't need the generated C/C++ files from midl.exe, you may add del commands to your custom build step for the specific output files.
EDIT2: Or use the nul workaround described above.
A common approach used when the type library already exists is to use Visual Studio to import it into .NET. But this way, you'll have to remember to regenerate the TLB and import it again if you update your IDL file.

Have you considered C++/CLI? In your C++/CLI ref class you'd write a member function:
void SendFrame(cli::array<System::Byte> frameData)
{
pin_ptr<System::Byte> p1 = &frameData[0];
unsigned char *p2 = (unsigned char *)p1;
// So now p2 is a raw pointer to the pinned array contents
}

Related

Passing strings from C++ to C# (and vice versa)

I am following the example in How to call a managed DLL from native Visual C++ code in Visual Studio.NET or in Visual Studio 2005 to call a .NET DLL from native C++ code. The code for C# looks like this:
public class StringExample: IStringExample
{
public string returnString(string input)
{
return input;
}
}
I followed the example's steps to build, register the COM assembly, and export the type library (.tlb) from the C# code.
In C++ code, I am trying to use code similar to the following:
#import "..\StringExample\bin\Debug\StringExample.tlb" raw_interfaces_only
using namespace StringExample;
void abc()
{
// Initialize COM.
HRESULT hr = CoInitialize(NULL);
// Create the interface pointer.
IStringExample ptr(__uuidof(StringExample));
BSTR bstrInput = L"hello world";
BSTR bstrOutput =L"";
ptr->returnString(bstrInput, &bstrOutput);
TCHAR* szOutput = (TCHAR *)_bstr_t(bstrOutput);
// Uninitialize COM.
CoUninitialize();
}
However, bstrOutput is empty. Moreover, I need to convert the bstrOutput to TCHAR* to pass it to a different api.
Is there an error in the variable initialization? Is there a different way to pass string variables between .NET and C++?

Java JNI call to C# COM fails, when COM is registered without codebase option of regasm

Function calls from Java to C# through JNI-C++/CLI are failing when the C# COM is not registered using regasm with the codebase option. I've built a sample following the instructions in P2: Calling C# from Java with some changes.
Numero uno: C#
Change the C# dll into a COM by creating an interface, IRunner, and making the library assembly COM-visible.
namespace RunnerCOM
{
public interface IRunner
{
String ping();
}
public class Runner:IRunner
{
static void Main(string[] args)
{
}
public Runner() { }
public String ping()
{
return "Alive (C#)";
}
}
}
Numero due: Java
No changes made to the Java section.
Numero tre: C++
This part was changed to create a new instance of the RunnerCOM.Runner class and use that result. Here is a good tutorial on how to call managed code from unmanaged code: http://support.microsoft.com/kb/828736
#include "stdafx.h"
#include "Runner.h"
#pragma once
#using <mscorlib.dll>
#import "RunnerCOM.tlb"
JNIEXPORT jstring JNICALL Java_Runner_ping(JNIEnv *env, jobject obj){
RunnerCOM::IRunnerPtr t = RunnerCOM::IRunnerPtr("RunnerCOM.Runner");
BSTR ping = t->ping();
_bstr_t temp(ping, true);
char cap[128];
for(unsigned int i=0;i<temp.length();i++){
cap[i] = (char)ping[i];
}
return env->NewStringUTF(cap);
}
Now to my questions,
The code above fails with a _com_error exception, Class not registered (0x80040154) unless the codebase option is enabled during regsitration of RunnerCOM.dll, with regasm.exe. Why is this? If the code is not ran from JNI, I tested it as an exe, it works fine. The RunnerCOM.dll is simply found in the working directory.
Type casting _bstr_t temp to char* fails. For example, char *out = (char*) temp; Similar to the issue above, it works fine when it's built and executed as an exe but crashes the JVM when it's a JNI call.
By the way this is what I used to run it as an executable:
int main(){
RunnerCOM::IRunnerPtr t = RunnerCOM::IRunnerPtr("RunnerCOM.Runner");
BSTR ping = t->ping();
_bstr_t temp(ping, false);
printf(temp);
return 0;
}
Codebase creates a Codebase entry in the registry. The Codebase entry specifies the file path for an assembly that is not installed in the global assembly cache, so when you specify the codebase, the system will find the DLL based on the path. If not, it will try to locate the dll in the GAC and current working directory. In JNI, I think the current working directory is not the folder where the DLL is. You can use process explorer to find what is the current working directory, also, you can use process monitor to find out which directories the exe is looking into to find the dll.
The code converting _bstr_t to char*, the char* string cap is not ended with '\0', I think this might cause problem in JNI. Uses the _bstr_t operator (char *), you can obtain a null terminated string from the _bstr_t object. Please check the msdn example for details.
You mentioned C++/CLI, C++/Cli and COM warpper are two different ways to interop with C# code. If you're using C++/CLI as a bridge, you doesn't need to register C# DLL as COM, please see this: Calling .Net Dlls from Java code without using regasm.
If you're using COM, you should call CoInitialize() to init COM first in your code.

Implement a C# DLL COM File In Unmanaged C++ Program

Here is my other question that led into this one as reference also:
How to call managed C++ methods from Un-managed C++
I have successfully created a C# COM File. Now I need a simple explanation on how to implement it in unmanaged C++.
I am following this example but the c++ part is weak.
http://www.codeproject.com/Articles/7859/Building-COM-Objects-in-C
Here is my COM file
using System;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace cSharpRiJHarn
{
[Guid("ED1483A3-000A-41f5-B1BC-5235F5897872")]
public interface DBCOM_Interface
{
[DispId(1)]
String encrypt(string s);
[DispId(2)]
String decrpyt(string s);
}
[Guid("A6BCEC1D-B60C-4c97-B9AD-1FE72642A9F8"),
InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface DBCOM_Events
{
}
[Guid("7C13A8C6-4230-445f-8C77-0CA5EDECDCB5"),
ClassInterface(ClassInterfaceType.None),
ComSourceInterfaces(typeof(DBCOM_Events))]
public class RijndaelLink : DBCOM_Interface
{
public String encrypt(String s)
{
return Rijndael.EncryptString(s);
}
public String decrpyt(String s)
{
return Rijndael.DecryptString(s);
}
}
}
I just want a VERY basic example on using this with unmanaged code.
Please include in your answers:
Do I need to include the project or just the source files of the COM
Do I need to add a reference
A very basic example of passing a string and printing it out with cout.
Thanks for your help!
The first thing you need to do is properly define the COM object in .NET, to be used by the unmanaged world (C++ or other). Here is a decent definition:
namespace cSharpRiJHarn
{
[Guid("ED1483A3-000A-41f5-B1BC-5235F5897872")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[ComVisible(true)]
public interface IRijndaelLink
{
string encrypt(string s);
string decrypt(string s);
}
[Guid("7C13A8C6-4230-445f-8C77-0CA5EDECDCB5")]
[ComVisible(true)]
public class RijndaelLink : IRijndaelLink
{
public string encrypt(string s)
{
return Rijndael.EncryptString(s);
}
public string decrypt(string s)
{
return Rijndael.DecryptString(s);
}
}
}
Next, you need to register this .NET assembly for COM using the RegAsm tool. I suggest also you build a Type Library (.TLB) with it, something like this (I suppose you build the whole stuff for X86, not X64):
c:\Windows\Microsoft.NET\Framework\v4.0.30319\RegAsm.exe YourAssembly.dll /tlb:YourAssembly.tlb /codebase
Please adapt to your actual path. Also check the codebase arg as you may not need this in production.
This will build a .TLB file with both the interface and the class inside. It works because we added the ComVisible attribute. You will also note I have not defined a Dispatch or Dual interface because in this sample, I don't need COM Automation (VB, VBA) nor any scripting language (VBScript, JScript) support, only IUnknown interfaces which are much easier to use in plain C/C++ than IDispatch interfaces.
Now, there is an easy way to import that in the unmanaged c++ world using a Microsoft specific C++ extension: #import Directive, similar to Add References in .NET. Here is a sample Console Application that uses the COM Object:
#include "stdafx.h"
#import "c:\MyPathToTheTlb\YourAssembly.tlb" // import the COM TLB
using namespace YourAssembly;
int _tmain(int argc, _TCHAR* argv[])
{
CoInitialize(NULL); // needed to enter COM space
IRijndaelLinkPtr ptr(__uuidof(RijndaelLink)); // create the COM Object with the desired interface
_bstr_t s = ptr->encrypt("hello"); // call the function
printf("%S", (LPWSTR)s); // for example
CoUninitialize();
return 0;
}
You will notice the #import directive also create cool wrappers (_bstr_t, as .NET String will be exported as Automation BSTR here, even for IUnknown interfaces) for string handling, so it's no really big deal.
This is not the only way all this can work, but that's IMHO one of the most simple.

String for C# from C++ COM

I have a C++ DLL and some functions return Unicode null-terminated strings:
void SomeFunc(wchar_t* StrBuf)
The caller must allocate StrBuf - string can be up to 256 characters.
This DLL also exposes a COM object to use this DLL from C# via COM. Definition in IDL:
[id(1), helpstring("bla")]
HRESULT SomeFunc([in,out] BSTR* TextStr, [out,retval] LONG* RetValue);
Currently the C# code looks like this:
string val = new string('\0', 256); // Allocate memory
MyComObj.SomeFunc(ref val); // Get string
val = val.Substring(0, val.IndexOf('\0')); // Convert from null-terminated string
Is there a way to define such a COM function so it can be used from C# easier? Right now it looks ugly and takes three lines to call the function or five lines if a function has two string parameters.
If you have this exposed as a COM object, just use visual studio to add a reference to your object. It will automatically generate an assembly for you (I believe they call this a COM callable wrapper).
This will expose your COM objects methods to .NET and works unless you start trying to pass across some custom structures.
Here are some resources for you:
COM Callable Wrapper
COM Interop Part 1: C# Client Tutorial
If the memory for the string in the client allocates and fills it to the COM server, you must use a StringBuilder:
// IDL code
HRESULT GetStr(/*[out]*/ LPSTR pStr);
// Interop
void GetStr(/*[out]*/ [MarshalAs(UnmanagedType.LPStr)] StringBuilder pStr);
// C# code
var str = new StringBuilder(256);
MyComObj.GetStr(a);
Then the StringBuilder will be empty or filled with characters.
Solved. In C++ I use this code in the COM wrapper:
STDMETHODIMP MyClassClass::SomeFunc(BSTR* OptionValue, LONG* RetValue)
{
*RetValue = 0;
UNICODECHAR txt[MAXSTATSTRLEN];
//... Copy text to txt variable
*OptionValue = W2BSTR(txt);
return S_OK;
}
IDL:
[id(1), helpstring("")] HRESULT SomeFunc([out] BSTR* OptionValue, [out,retval] LONG* RetValue);
In C# it can now be called easily:
string val;
MyComObj.SomeFunc(out val);

Calling Excel/DLL/XLL functions from C#

I have a particular function in an Excel addin(xll).
The addin is proprietary and we do not have access to the source code. However we need to call some functions contained within the addin and we would like to call it from a C# program.
Currently, I was thinking of writing a C++ interface calling the Excel function with xlopers, then calling this C++ interface from C#.
Does anybody who has prior experience of this kind of issues know what would be the best solution for that ?
Anthony
You need to create a fake xlcall32.dll, put it in the same directory as your XLL (do not put excel's own xlcall32.dll in the PATH). Here is some code:
# include <windows.h>
typedef void* LPXLOPER;
extern "C" void __declspec(dllexport) XLCallVer ( ) {}
extern "C" int __declspec(dllexport) Excel4 (int xlfn, LPXLOPER operRes, int count,... ) { return 0; }
extern "C" int __declspec(dllexport) Excel4v(int xlfn, LPXLOPER operRes, int count, LPXLOPER far opers[]) {return 0;}
Now suppose I have an XLL called xll-dll.xll with a function called (use "depends.exe" to find out the names of the exported functions) xlAdd that well adds two doubles:
extern "C" __declspec(dllexport) XLOPER * __cdecl xlAdd(XLOPER* pA, XLOPER* pB);
The following code calls it:
# include <windows.h>
# include <iostream>
// your own header that defines XLOPERs
# include <parser/xll/xloper.hpp>
// pointer to function taking 2 XLOPERS
typedef XLOPER * (__cdecl *xl2args) (XLOPER* , XLOPER* ) ;
void test(){
/// get the XLL address
HINSTANCE h = LoadLibrary("xll-dll.xll");
if (h != NULL){
xl2args myfunc;
/// get my xll-dll.xll function address
myfunc = (xl2args) GetProcAddress(h, "xlAdd");
if (!myfunc) { // handle the error
FreeLibrary(h); }
else { /// build some XLOPERS, call the remote function
XLOPER a,b, *c;
a.xltype = 1; a.val.num = 1. ;
b.xltype = 1; b.val.num = 2. ;
c = (*myfunc)(&a,&b);
std::cout << " call of xll " << c->val.num << std::endl; }
FreeLibrary(h); }
}
int main()
{test();}
My exe actually works (to my own surprise), and output 3 as expected. You must have some knowledge of what your XLL actually expects for parameters. If it allocates some memory, you must check if the #define xlbitDLLFree 0x4000
is set on your XLOPER c->type, and call back "xlAutoFree".
You might like to try XLL Plus http://www.planatechsolutions.com/xllplus/default.htm. It's a bit pricey, but the
XLL Wrapper Libraries feature is exactly what you are looking for:
"Sometimes it is useful to be able to call your Excel add-in functions from other environments, such as command-line programs or interactive applications, written in C++, Java, C# or Visual Basic. The Xll Wrapper toolkit contains tools, a runtime library, samples and documentation to help with the development of COM modules and .NET assemblies that wrap Excel XLL add-ins"
An Excell add-in DLL might be written in C#. If so, then you can probably bypass Excel altogether. Might need a wrapper though.
check whether assembly is available in program files where you install the plguin,
reference that assembly directly to your c# project - check the object browser for your class, method or object
You should be able to use reflection to get access to your addin. try using The Reflector to see what's available.

Categories

Resources