I was showing a coworker how easily a COM object could be created and used in C++ using the Microsoft ATL library. In particular, I wanted to use demonstrate IDispatch to show how it can be used to dynamically call a method. However, I don't use C++/ATL frequently enough to not get lost in the templates and convenience methods and classes of ATL.
We created a small COM class, MyCOMLibrary.SimpleClass that has a single method named AppendMessage that takes 2 BSTRs and has no return value.
From a simple Win32 console application, creating and using the COM object was easy:
CComPtr<IDispatch> simpleClass;
debugPrint.CoCreateInstance(L"MyCOMLibrary.SimpleClass");
if (simpleClass) {
CComVariant vModule(L"Demo");
CComVariant vMessage(L"Welcome to COM");
simpleClass.Invoke2(L"AppendMessage", &vModule, &vMessage);
}
While this makes creating and managing the COM object nearly painless (especially when compared to using DISPPARAMS), I was looking for an even more concise way of calling the AppendMessage method that reflects modern C++/ATL/COM usage (in VS 2012).
The best case would be like C#'s dynamic keyword (dynamic documentation) (or any other "late-bound" language like JavaScript/VB6/etc):
simpleClass.AppendMessage(L"Demo", L"Welcome to COM");
Or, without that, this would be more readable:
simpleClass.Invoke2(L"AppendMessage", L"Demo", L"Welcome to COM");
However, I know the first won't compile as the method doesn't exist on CComPtr<IDispatch> and the second won't compile as Invoke2 requires VARIANTs.
I did try using the interface directly as a comparison:
// workaround VS2012 intellisense issue with #import
#ifndef __INTELLISENSE__
#import "progid:MyCOMLibrary.ISimpleClass" version("1.0")
#else
#include "Debug\MyCOMLibrary.tlh"
#endif
CComPtr<MyCOMLibrary::ISimpleClass> simpleClass;
simpleClass.CoCreateInstance(L"MyCOMLibrary.ISimpleClass");
if (simpleClass) {
CComBSTR module(L"Interfacing");
CComBSTR message(L"And, then, there was COM.");
simpleClass->AppendMessage((BSTR)module, (BSTR)message);
}
But, it's nearly identical to the original (and further, I want to do late-bound).
Question
Is there a different/shorter/better/awesome way of calling AppendMethod using IDispatch and ATL that would further impress upon my coworkers that C++ isn't always difficult? (Or, a better way without ATL would also be fine).
Take a look at VOLE by matthew wilson.
It is at http://vole.sourceforge.net/
It think it does what you want. I have used it before, and it makes IDispatch client programming a breeze.
Take a look at http://www.codeproject.com/Articles/19962/Driving-Microsoft-Word-using-VOLE for an example using Microsoft World
Related
I have the following setup:
CSharp.dll, a C# dll
CppCli.dll, a Mixed-Mode C++/Cli dll.
Native.dll, a native C++ dll.
The native project declares
class Native
{
public:
virtual void Add(int a);
};
which is implemented in the mixed mode project:
class Mixed : public Native
{
public:
virtual void Add(int a);
ICSharpInterface^ MakeManaged();
};
Since the base class is defined in Native, I can pass my implementation to other code in native. But now I want to test this class.
If I use #pragma make_public(Mixed); it will only make a struct public, without any functions visible to the outside.
If I try to link to the mixed-mode dll from another mixed-mode dll, I get linker errors because it's a native class and the mixed-mode dll generates no .lib to link against.
And if I try to __declspec(dllexport) the class, the Visual Studio complains because the interface exposes managed stuff.
So my question is:
How do I instantiate (link to) this class in my tests? I would be happy with any solution that shows how to create an instance where I can call its public interface on, doesn't matter if from C++, C++/Cli or from C#.
You are missing an important step, you haven't yet considered how a native program is going to create an instance of the Mixed object. Which is non-trivial, it does require getting the CLR loaded and initialized so it can execute managed code. Keep in mind that the client code does not do this automatically, it doesn't know beans about the CLR, it only knows about Native.dll. There are three basic ways to get this done:
You can expose managed classes as COM objects, very simple to do with the [ComVisible] attribute. Possible to do directly from C#, you don't need the C++/CLI wrapper. The usual disadvantage is that the client code has to use COM to instantiate the object and make calls on it, not the kind of code that programmers like to write.
You can host the CLR yourself, the most efficient and flexible solution. This magazine article gives an introduction, beware that it is dated.
The C++/CLI compiler gives you a way to give you an unmanaged exported function that can execute managed code. It auto-generates a stub that gets the CLR loaded and initialized, if necessary.
Focusing a bit on the last bullet, since that's what you probably like, what you need here is a factory function, one that creates an instance of the Mixed class and returns a Native* back to the caller. So it can call ptr->Add() and invoke Mixed::Add(). That can look like this:
extern "C" __declspec(dllexport)
Native* CreateObject() {
return new Mixed;
}
Also gets you the import .lib that you can link in your native project. Do beware the disadvantages, they are significant. It is not exactly fast since the stub must check if the CLR is initialized yet and make the native-to-managed transition. The error reporting is extremely lousy since you have no decent way to diagnose exceptions that are thrown in the C# code. And memory management is an issue, the caller has to be able to successfully destroy the returned object which requires all modules to use the exact same CRT. The kind of problems that COM solves.
The exact same technique is available in C# as well by using an IL rewriter. Gieseke's unmanaged exports template is popular.
I have the following structure in an MFC DLL:
struct retornoSAP
{
enum tipoRetorno
{
Ok,
Falha,
AbrirArquivo, // mStrData válido
InserirArquivo, // mStrData válido
retornoMap, // mLstData válido
retornoTable, // mLstTable válido
retornoString, // mStrData válido
};
tipoRetorno mTipo;
CString mStrData;
CMapStringToString mLstData;
retTable* mLstTable;
CMapStringToString mLstVars;
retornoSAP( tipoRetorno pTipo )
{
mTipo = pTipo;
mLstTable = NULL;
}
~retornoSAP()
{
if ( mLstTable ) CISap::release( mLstTable );
}
};
retTable definition is:
typedef vector<CMapStringToString*> retTable;
This structure is used to store data read from an SAP API, and I have a lot of functions that return a "retornoSAP" value.
It happens that I have to call those functions from VB.NET, using P/Invoke (DllImport). I have read some material about marshaling unmanaged types to .NET (like this http://blogs.msdn.com/b/dsvc/archive/2009/02/18/marshalling-complicated-structures-using-pinvoke.aspx), and it probably would be easy to marshal a structure with some basic types in it, but I wonder if it is even possible to marshal a CMapStringToString or, even worse, a vector of CMapStringToString.
My question is if it's worth spending some time trying to translate this structure to a .NET type (in this case, where I could find some good documentation)?
If not, I wonder if it sounds like a good idea to use a XML parser in C++, write all my data to a XML structure, and then return that XML structure as a BSTR string, so I could read the BSTR return value easily it in my .NET application and parse it back to a XML structure. In that case, I would pass some big strings between the MFC DLL and the .NET application...
You can't really deal with MFC classes with P/INVOKE. I think you can choose one of two option:
Not touching the MFC dll at all: create a dll with CLI C++. Some effort to learn the language, but is a thunk dll, you don't need to study it all. This way you can expose some ref classes to .NET world and call from its methods the MFC dll. From inside the ref class you can read your original MFC structures and populate some other structures more .NET friendly.
Modify the MFC dll, expose some entry point with extern "C" decoration, in order to avoid name mangling, and convert internal structure in something easyer to manage, string as you guess, would be a not so elegant, but cutting edge solution;)
Both solution have some performance overhead, but I guess the first one would pay better, and it is sometimes the only one preventing modification to the original MFC dll, and sometimes this is desiderable. Second one is probably simpler, but passing magic string would involve some parsing leading to performance leak, possible errors so you need more test, more costs and so on.
Another drawback of solution 1 is you need an extra deploy for the C++/CLI redistributables.
I did not mention since you probably already know, but doing this kind of interop needs the .NET code compiled in x86 mode if your target C++ dll are 32 bit compiled.
Is it possible to pass a Delphi stream (TStream descendant) to a DLL written in c/c++? DLL will be written in Microsoft c/c++. If that is not possible, how about if we use C++ Builder to create the DLL? Alternatively, are there any Stream (FIFO) classes which can be shared between Microsoft C/C++ and Delphi?
Thanks!
You can do this using IStream and TStreamAdapter. Here's a quick example (tested in D2007 and XE2):
uses
ActiveX;
procedure TForm1.DoSomething;
var
MemStream: TMemoryStream;
ExchangeStream: IStream;
begin
MemStream := TMemoryFile.Create;
try
MemStream.LoadFromFile('C:\Test\SomeFile.txt');
MemStream.Position := 0;
ExchangeStream := TStreamAdapter.Create(MemStream) as IStream;
// Pass ExchangeStream to C++ DLL here, and do whatever else
finally
MemStream.Free;
end;
end;
Just in case, if you need to go the other way (receiving an IStream from C/C++), you can use TOleStream to get from that IStream to a Delphi TStream.
Code compiled by Microsoft C/C++ cannot call methods directly on a Delphi object. You would have to wrap the methods up and present, to the C++ code, an interface, for example.
Code compiled by C++ Builder can call methods directly on a Delphi object.
In general, wrapping up a Delphi class and presenting it as an interface is not completely trivial. One reason why you can't just expose the raw methods via an interface is that the Delphi methods using the register calling convention which is proprietary to Embarcadero compilers. You'd need to use a calling convention that is understood by the Microsoft compiler, e.g. stdcall.
Another complication comes with exceptions. You would need to make sure that your interface methods did not throw exceptions since your C++ code can't be expected to catch them. One option would be to use Delphi's safecall calling convention. The safecall calling convention is stdcall but with an added twist that converts exceptions into HRESULT values.
All rather straight forward in concept, but probably requiring a certain amount of tedious boilerplate code.
Thankfully, in the case of TStream, you can use TStreamAdapter to expose the Delphi stream as a COM IStream. In fact, the source code for this small class shows how to handle the issues I describe above.
i need to instantiate com object which is .dll and on local machine in visual c++,i know that it can be done by using CoCreateInstance("clsid") ,but i am confused about declaration.so can anyone explain all steps involved?
for late binding as well as early binding
is any import/inclusion required
how to declare com object ?
any other steps required before createinstance (e.g CoInitialize?)
or provide any specific reference involving step by step code
First you have to call CoInitialize and don't forget to callCoUnitialize if initialization was successful.
So your code will have the following structure:
HRESULT hr = CoInitialize(NULL);
if (SUCCEEDED(hr))
{
try
{
CoCreateInstance(...)
// ...
}
catch (_com_error &e)
{
//...
}
CoUninitialize();
}
For more information visit MSDN. I recommend you to start with The COM Library and then you should read something about CoInitialize and CoCreateInstance functions before you use them.
This tutorial could help you too: Introduction to COM - What It Is and How to Use It.
#import is very much recommended. if you import the typelib with #import, you'll be using the Native COM framework, which isolates some gritty details and makes life generally easier.
In Native COM, something like this:
LibName::IMyInterfacePtr pInterface;
In raw C++:
IMyInterface *pInterface;
But see above.
Call CoInitialize() in the beginning of the program, CoUninitialize() in the end. if running inside a DLL, then it's much more complicated.
I am trying to build an app that uses a COM component in VisualStudio ´05
in native C++.
The mix of native and managed desciptions of things in the MSDN totally wrecked my
brain. (I think the MSDN is a total mess in that respect)
I need a short and simple native C++ sample of code to load my Component
and make it usable.
I am ok with the compiler creating wrappers and the like.
Please don't advise me to use the dialog based MFC example, because
it does not work with this component and is in itself a huge
pile of c... code.
Can this be an issue native com vs managed com?
I am totally lost, please give me some bearings...
EDIT: Thanks for all the help.
My problem is that all I have is a registered dll (actually the OCX, see below)
. I (personally) know
what the Interface should look like, but how do I tell my program?
There are no headers that define
IDs for Interfaces that I could use. But I read that the c++ compiler
can extract and wrap it up for me. Anyone know how this is done?
CLARIFICATION: I have only the OCX and a clue from the documentation
of the component, what methods it should expose.
Fully working example (exactly what you need) from my blog article: How to Call COM Object from Visual Studio C++?
// https://helloacm.com/how-to-call-com-object-from-visual-studio-c/
#include <iostream>
#include <objbase.h>
#include <unknwn.h>
#include <Propvarutil.h>
#import "wshom.ocx" no_namespace, raw_interfaces_only
using namespace std;
int main() {
HRESULT hr;
CLSID clsid;
CoInitializeEx(nullptr, COINIT_MULTITHREADED);
CLSIDFromProgID(OLESTR("WScript.Shell"), &clsid);
IWshShell *pApp = nullptr;
hr = CoCreateInstance(clsid, nullptr, CLSCTX_INPROC_SERVER, __uuidof(IWshShell), reinterpret_cast<LPVOID *>(&pApp));
if (FAILED(hr) || pApp == nullptr) {
throw "Cannot Create COM Object";
}
int out;
VARIANT s;
InitVariantFromInt32(0, &s);
VARIANT title;
InitVariantFromString(PCWSTR(L"title"), &title);
VARIANT type;
InitVariantFromInt32(4096, &type);
BSTR msg = ::SysAllocString(L"Hello from https://helloacm.com");
pApp->Popup(msg, &s, &title, &type, &out);
CoUninitialize();
cout << "Out = " << out;
return 0;
}
The bare minimums for instantiating a COM object are as follows:
1) Must have a COM apartment available.
This is accomplished by most applications by calling CoInitialize / CoInitializeEx to setup the COM library and the initial COM apartment if it is the first time for a process.
2) Call CoCreateInstance / CoCreateInstanceEx to create an object, and specify flags to denote how it will be instantiated.
3) Properly balance calls of AddRef and Release on the interfaces of any COM components that you create, calling the last Release() when you are done using a COM component.
-
In a managed application, #1 is almost always handled for you. #2 is abstracted away if you import a reference to a COM library, and you can just use the imported names as if they were .NET class definitions and such. #3 is automatically handled for you, but your needs may vary. Unfortunately, there are sometimes quirks in how references are handled in managed applications, which can cause COM objects to stick around longer than intended. The Marshal helper class in System.Runtime has methods that can help you out there if you encounter issues.
-
In an unmanaged application, you will have to do some legwork if you are creating an application from scratch.
Call CoInitialize/CoInitializeEx early in your application's main thread to setup the apartment.
When your application's main thread is about to exit, call CoUninitialize() to close out the apartment.
For additional threads that you create, you should also call CoInitialize/CoInitializeEx when they start if you need to use COM objects from those threads. Also, depending on your application you may want to set the apartment parameters.
For those threads, also call CoUninitialize() when they are exiting to cleanup properly.
I applaud your efforts to go with native C++ to deal with COM - you need to go through the pain to truly appreciate today's luxurious (managed) development environment :)
Back when the world (and I) were younger, Kraig Brockshmidt's book "Inside OLE" was the tome for making sense of COM (before COM even was COM). This book predates managed code, so no chance of managed confusion here. There's a second edition, too.
Don Box's books "Essential COM" and "Effective COM" were later, but welcome additions to the store of (unmanaged) COM knowledge.
However, if your wallet doesn't extend to acquiring these dusty old books, the Microsoft COM tutorial material here could help set you on the right track.
Happy hacking.
I use a combination of ATL smart COM pointers and the ATL::CAxWindow class for COM objects and components. I find the smart pointers particularly easy to use.
http://www.murrayc.com/learning/windows/usecomfromatl.shtml
http://76.105.92.243/notes/atlcom.html#import
http://msdn.microsoft.com/en-us/library/yx242b61%28VS.80%29.aspx
It would help if you gave a little more information about what you're exactly doing. Do you know what interfaces the object implements, etc?
In general though, the API that you can Google for more specific help is CoCreateInstance. You'll need to pass it the GUID of the object you want to play with. All COM objects implement the IUnknown interface, and you can query for any others it might have. So some sample pseudocode to get you started might look something like:
CoInitializeEx( NULL, COINIT_APARTMENTTHREADED );
CoCreateInstance( CLSID,
ptrIUnknown,
ClassCxt, // generally CLSCTX_INPROC_SERVER,
riid , // reference id
(void **)&pRequest); // the interface that corresponds to the riid
Here you can query additional interfaces using the IUnknown interface you got from the ptrIUnknown.
Then clean up with
CoUninitialize()
Don Box's Essential COM is a great book on this topic. Also, just for testing how your COM object works, using something like VBScript makes this super easy. Also, it's probably worthy of note that the GUID of the class ID is stored in a somewhat unusual way, so if you're just ripping a GUID from the registry, you might have some trouble figuring out the ordering. That's probably for a different question though.
Try using #import from Visual C++. This will create smart pointer wrappers for the interfaces.