Passing reference to COM interface from COM library - com

How do I pass reference to a COM interface as an argument from within a COM library?
Here's the sample:
1) Client code successfully creates coclass and receives the interface pointer in pFunctionDiscovery as below:
hr = CoCreateInstance(
__uuidof(FunctionDiscovery),
NULL,
CLSCTX_INPROC_SERVER,
__uuidof(IFunctionDiscovery),
(LPVOID*)&pFunctionDiscovery );
if (FAILED(hr))
{
TRACE_MESSAGE(Error,"Failed to get IFunctionDiscovery COM %08x\n",hr);
goto Exit;
}
2) Now calling member function of pFunctionDiscovery as below gives error message: 800706f4, which corresponds to A null reference pointer was passed to the stub.
hr = pFunctionDiscovery->GetInstanceCollection(
FCTN_CATEGORY_DEVICEDISPLAYOBJECTS,
NULL,
FALSE,
&pFICollection );
3) COM library is written with aid of ATL library and the code is as below:
// The module attribute is specified in order to implement DllMain,
// DllRegisterServer and DllUnregisterServer
[ module(dll, name = "MyServer", helpstring = "MyServer 1.0 Type Library") ];
[ emitidl ];
/////////////////////////////////////////////////////////////////////////////
// IFunctionInstanceCollection interface
[
object,
uuid("F0A3D895-855C-42A2-948D-2F97D450ECB1"),
oleautomation,
helpstring("IFunctionInstanceCollection Interface"),
pointer_default(unique)
]
__interface IFunctionInstanceCollection : IUnknown
{
public:
virtual HRESULT STDMETHODCALLTYPE GetCount(__RPC__out DWORD *pdwCount) = 0;
};
// IFunctionDiscovery interface
[
object,
uuid("4df99b70-e148-4432-b004-4c9eeb535a5e"),\
oleautomation,
helpstring("IFunctionDiscovery Interface"),
pointer_default(unique)
]
__interface IFunctionDiscovery : IUnknown
{
virtual HRESULT GetInstanceCollection(
__RPC__in_string const WCHAR* functionCategory,
__RPC__in_opt_string const WCHAR* subcategory,
BOOL fIncludeAllSubCategories,
__RPC__deref_out_opt IFunctionInstanceCollection **ppIFunctionInstanceCollection
) = 0;
};
/////////////////////////////////////////////////////////////////////////////
// FunctionDiscovery class
[
coclass,
threading(apartment),
vi_progid("FunctionDiscovery.Discovery"),
progid("FunctionDiscovery.Discovery.1"),
version(1.0),
uuid("C72BE2EC-8E90-452c-B29A-AB8FF1C071FC"),
helpstring("FunctionDiscovery Class")
]
class ATL_NO_VTABLE FunctionDiscovery :
public IFunctionDiscovery
{
public:
FunctionDiscovery() {};
virtual ~FunctionDiscovery(){};
virtual HRESULT GetInstanceCollection(
__RPC__in_string const WCHAR* functionCategory,
__RPC__in_opt_string const WCHAR* subcategory,
BOOL fIncludeAllSubCategories,
__RPC__deref_out_opt IFunctionInstanceCollection **ppIFunctionInstanceCollection
)
{
printf("GetInstanceCollection called");
return 0;
}
DECLARE_PROTECT_FINAL_CONSTRUCT()
HRESULT FinalConstruct()
{
return S_OK;
}
void FinalRelease()
{
}
static BOOL DllMainAttach();
static void DllMainDetach();
};
Please let me know where the problem is?
Thanks
Nick
Ok, passing a non null string in the call from client, worked fine. The way I am assigning object to *ppIFunctionInstanceCollection is as below:
On the Server side, I declare a new class like this and create an object from within GetInstanceCollection. When client calls GetInstanceCOllection, this created object is returned. I do get a valid instance on the server side, but on the client side, it shows up as NULL.
1)
class CFunctionInstanceCollection : public IFunctionInstanceCollection
{
public:
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID IID, void **pv) throw()
{ return 0; };
ULONG STDMETHODCALLTYPE AddRef(void) throw()
{ return 0; };
ULONG STDMETHODCALLTYPE Release(void) throw()
{ return 0; };
virtual HRESULT STDMETHODCALLTYPE GetCount(__RPC__out DWORD *pdwCount)
{ return 10; };
};
2) From within GetInstanceCollection, I am doing:
*ppIFunctionInstanceCollection = new CFunctionInstanceCollection();
I was hoping that the above assignment valid instance of CFunctionInstanceCollection into last parameter of GetInstanceCollection() method, which is *ppIFunctionInstanceCollection. I have verified this on the server side and it prints valid pointer, with size of class as 4 (presence of virtual functions yields class size as 4).
But on the client side, value is NULL. I think there is something more in passing of parameters between client/server. If you see anything else, please let me know that too. Thanks!!!

Apologies if I'm telling you things you already know.
In RPC (and COM), a proxy is the piece of code the client actually invokes when it calls a remote procedure. The proxy generally marshals the input parameters and then sends the request to the server where a piece of code called the stub unmarshals the parameters and then uses them to invoke the actual procedure being called.
When the called procedure returns a result, the stub marshals the out parameters and the result and sends the response back to the proxy which in turn unmarshals the out parameters etc. and hands them back to the client.
That's the general model anyway, things are sometimes optimised away (e.g., in the case of in process COM objects) in which case there may not be an actual stub and an actual proxy. Still, that's the background we can use to understand what a "proxy" and a "stub" are.
The "A null reference pointer was passed to the stub" error suggests that the problem is happening at the stub (i.e., server) side. The two pieces of code that might be passing things to the stub are the proxy and the implementation of GetInstanceCollection with GetInstanceCollection being the more likely culprit.
I suspect your problem is that the GetInstanceCollection implementation does not assign a value to *ppIFunctionInstanceCollection.
Try adding code to assign *ppIFunctionInstanceCollection before GetInstanceCollection returns.
updated 3/15
Your updated implementation of GetCount returns the value 10. But, that will be interpreted as the HRESULT 10 not the count value 10. The implementation of GetCount should really look something like this...
virtual HRESULT STDMETHODCALLTYPE GetCount(__RPC__out DWORD *pdwCount)
{
*pdwCount = 10;
return S_OK;
};
That said, it really isn't a good idea to dummy up the IUnknown methods (QueryInterface, AddRef and Release) because you can break all sorts of things unexpectedly. For example, every time you call GetInstanceCollection your program is going to leak a CFunctionInstanceCollection instance because one is created and never destroyed.
The code you have is okay for an experiment but it would be better to use ATL to do a full implementation of IUnknown for CFunctionInstanceCollection just as you did for your FunctionDiscovery class.
Updated 3/16
For completeness, I should probably also have mentioned that assigning *ppIFunctionInstanceCollection the way you do is valid but potentially risky in general.
You've written the CFunctionInstanceCollection class, so you know it implements the IFunctionInstanceCollection interface directly, so you know your assignment is safe. But in the more general case where you didn't write the class, the CFunctionInstanceCollection class might do something less straight forward - for example it might aggregate some other class that implements the interface. To be really really safe you should use QueryInterface to retrieve the IFunctionInstanceCollection interface pointer.

This blog post explains why you have this problem, pointer_default(unique) does not do what you think it does. Attribute the subcategory argument with [unique].

Related

Use native function pointer for listening to managed event / marshaling issue

I'm writing a mixed mode C++/CLI assembly bridge in order to be able to call into my .NET class library from old C++ application.
In one of my classes in the .NET library one can attach to an event whenever some message needs to be displayed (to console or whatever depending on calling application).
class NetApi
{
public event EventHandler<MessageEventArgs> MessageReported;
}
To call this from native C++ application, I defined the following pointer/delegate bridge:
typedef void(*MessageHandler)(const char* msg);
delegate void ManagedMessageHandler([MarshalAs(UnmanagedType::LPStr)] String^ msg);
Omitting from glue for connecting everything (attaching to MessageReported, removing sender from EventHandler, etc...), here is how I create managed delegate from native function pointer:
class NetApiBridge
{
public:
void SetMessageHandler(MessageHandler handler)
{
wrappedListener = (ManagedMessageHandler^)Marshal::GetDelegateForFunctionPointer((IntPtr)handler, ManagedMessageHandler::typeid);
}
private:
msclr::auto_gcroot<NetApi^ > wrappedApi;
msclr::auto_gcroot<ManagedMessageHandler^ > wrappedListener;
// In another helper ref class in fact, but here pseudo code to simplify
void onMessageReported(Object^ sender, MessageEventArgs^ e)
{
if (!wrappedListener) { return; }
wrappedListenter(e->Message); // Send message to native function pointer
}
}
And I'm almost there when creating dummy C++ test code:
void messageHandler(const char* s)
{
cout << s;
}
void main()
{
NetApiBridge api = new NetApiBridge();
api->SetMessageHandler(&messageHandler);
api->Measure();
delete api;
}
Everything goes fine, events are reported correctly except .... except I receive a PInvokeStackImbalance from Managed Debugging Assistant when leaving the native handler and I clearly don't know why ?
What's wrong with marshaling const char* as UnmanagedType::LPStr here with GetDelegateForFunctionPointer ?
NB: C++ bridge is compiled in x86 if it is important to know here.
typedef void(*MessageHandler)(const char* msg);
delegate void ManagedMessageHandler([MarshalAs(UnmanagedType::LPStr)] String^ msg);
Your delegate declaration is not compatible with the function pointer declaration in 32-bit code. The default calling convention in native code is almost always __cdecl. The default for delegates is __stdcall. A somewhat quirky choice but inspired because interop was assumed to be useful to make OS calls, Windows and COM use __stdcall.
The mismatch right now causes the delegate stub to pop the arguments off the stack. So does the native code so the stack gets imbalanced by 4 bytes. The MDA is there to help you diagnose this common mishap.
You'll have to help and get them to agree. Either with the delegate declaration:
using namespace System::Runtime::InteropServices;
...
[UnmanagedFunctionPointer(CallingConvention::Cdecl)]
delegate void ManagedMessageHandler(String^ msg);
Or the function pointer declaration:
typedef void (__stdcall * MessageHandler)(const char* msg);

VB.Net Merge property class from more than one web references

I have project that need to reference to some web service, just say my reference is
service1Facade and service2Facade
both of them contain class name objectA
i must load objectA from service1Facade and use it as parameter in service2Facade.
but i got error
"value of type service1Facade.objectA cannot be converted to service2Facade.objectA"
how can i convert these object ?
what i have try but still not work:
group all reference into same folder, but .NET change its name into
objectA and objectA1
I copy every property of the property inside objectA, but still not working.
The functionality that is responsible for generating proxy classes based on your WSDL specification doesn't know (and it shouldn't know) that both your services use the same underlying type for objectA, and as I mentioned, no assumptions can be made regarding this since web services are meant to be decoupled from each other (from the consumer point of view).
I'd say your best option is to have your own proxy class (let's say ServiceProxyDTO) that can be used in both service #1 and #2. Something along the lines of:
public class ServiceProxyDTO
{
// Define properties from "objectA"
public ServiceProxyDTO() { }
public ServiceProxyDTO(service1Facade.ObjectA copyFrom)
{
// Copy state from "copyFrom"
}
public ServiceProxyDTO(service2Facade.ObjectA copyFrom)
{
// Copy state from "copyFrom"
}
public static implicit operator service1Facade.ObjectA(ServiceProxyDTO dto)
{
return new service1Facade.ObjectA() { /* Copy state back */ };
}
public static implicit operator service2Facade.ObjectA(ServiceProxyDTO dto)
{
return new service2Facade.ObjectA() { /* Copy state back */ };
}
public static implicit operator ServiceProxyDTO(service1Facade.ObjectA obj)
{
return new ServiceProxyDTO(obj);
}
public static implicit operator ServiceProxyDTO(service2Facade.ObjectA obj)
{
return new ServiceProxyDTO(obj);
}
}
With this code you can instantiate ServiceProxyDTO and pass it as parameter to both service #1 and #2 (as well as get the return values from both of these services).
Hope this helps.

What are the RemoteRead and RemoteWrite members of ISequentialStream?

I am developing an COM library that uses the IStream interface to read and write data. My MIDL code looks like this:
interface IParser : IUnknown
{
HRESULT Load([in] IStream* stream, [out, retval] IParsable** pVal);
};
Since IStream and it's base interface ISequentialStream are not defined inside a type library, they get defined in mine. So far so good. However, when I view my type library with OLEView, ISequentialStream only defines the members RemoteRead and RemoteWrite, while I expected Read and Write, since they are what I am actually calling. Even more strange is, that the the MSDN lists those two members (additionally to the original ones), but states they are not supported.
The question
So what are those members and how do I use them from a client (e.g. a managed application to create a managed Stream wrapper for IStream)?
The long story
I want to implement a wrapper on the client side, that forwards IStream calls to .NET streams, like System.IO.FileStream. This wrapper could inherit from IStream like so:
public class Stream : Lib.IStream
{
public System.IO.Stream BaseStream { get; private set; }
public Stream(System.IO.Stream stream)
{
this.BaseStream = stream;
}
// All IStream members in here...
public void Read(byte[] buffer, int bufferSize, IntPtr bytesReadPtr)
{
// further implementation...
this.BaseStream.Read();
}
}
And then, I want to call my server with this wrapper:
var wrapper = new Stream(baseStream);
var parsable = parser.Load(wrapper);
The problem is, that Lib.Stream in the previous example only provides RemoteRead and RemoteWrite, so that server calls to stream->Read() would end up in no mans land. As far as I understood, there is System.Runtime.InteropServices.ComTypes.IStream for managed COM servers, but in my example I have a unmanaged COM server and a managed client that should provide IStream instances.
Actually, there is no RemoteRead and RemoteWrite in ISequentialStream v-table layout. They exist only in ObjIdl.Idl, as an aid for RPC proxy/stub code generator. Have a look at ObjIdl.h from SDK:
MIDL_INTERFACE("0c733a30-2a1c-11ce-ade5-00aa0044773d")
ISequentialStream : public IUnknown
{
public:
virtual /* [local] */ HRESULT STDMETHODCALLTYPE Read(
/* [annotation] */
__out_bcount_part(cb, *pcbRead) void *pv,
/* [in] */ ULONG cb,
/* [annotation] */
__out_opt ULONG *pcbRead) = 0;
virtual /* [local] */ HRESULT STDMETHODCALLTYPE Write(
/* [annotation] */
__in_bcount(cb) const void *pv,
/* [in] */ ULONG cb,
/* [annotation] */
__out_opt ULONG *pcbWritten) = 0;
};
It's hard to guess why your type library ends up with RemoteRead/RemoteWrite names, instead of Read/Write. You may want to upload your IDL somewhere and post a link to it, if you need help with that.
However, as long as the v-table layout, the method signatures and the GUID of the interfaces from your typelib match those of ISequentialStream and IStream from ObjIdl.h, the method names do not matter.
Anyway, I would do as Igor suggested in his comment. Do not expose IStream at all in the type library. Use IUnknown in the IDL, and just cast it to System.Runtime.InteropServices.ComTypes.IStream inside the C# client's method implementation, when you actually do read/write, i.e.:
IDL:
interface IParser : IUnknown
{
HRESULT Load([in] IUnknown* stream, [out, retval] IParsable** pVal);
};
C#:
IParsable Load(object stream)
{
// ...
var comStream = (System.Runtime.InteropServices.ComTypes.IStream)stream;
comStream.Read(...);
// ...
}
[UPDATE] I guess I see what's going on with the method names. Your situation is exactly like this:
https://groups.google.com/forum/#!topic/microsoft.public.vc.atl/e-qj0xwoVzg/discussion
Once more, I suggest to not drag non-automation compatible interfaces into the type library, and I'm not alone here with this advice. You actually drag a lot more unneeded stuff into your typlib, which projects to the C# side too. Stick with IUnknown and make your typelib neat. Or, at last, define your own binary/GUID compatible versions of them from scratch.

Passing custom data through predefined COM interface

I'm using 3-rd party COM service. It's exposed from .NET assembly. There are several interfaces this service provides that actually I can use in my C++ application (using early binding). Actually I would like to know if it's possible to pass custom data through using these interfaces, i.e. for me it's not enough what these interfaces provide and I want to add some additional data/methods there (though interface is not mine thus I can't change it). Please advice if it's possible, if not might be there're some workaround (example would be very helpfull)?
I'm trying to understand if it's possible to pass custom data from my producer to my consumer through 3-rd party COM service. Might be I need to create my own interface that includes my methods and that inherites 3-rd party ISomething and use it?
Below is the code that illustrates the problem. Many thanks for your help...
1) Class that I'm using to pass data from producer to consumer (through 3-rd party COM service):
//ISomething is 3-rd party interface with some limited # of data and methods
//Something is my class that will be used to pass data where ISomething is asked
//and it contains some methods that I need and they are not defined in ISomething
class Something: public CComObjectRootEx<CComSingleThreadModel>, public IDispatch
{
private:
bstr_t Name;
bstr_t MyData;
public:
//COM map omitted
//Method defined in ISomething
STDMETHOD(get_Name)(BSTR * pRetVal)
{
*pRetVal = ::SysAllocString(Name);
return S_OK;
}
//Method defined in ISomething
STDMETHOD(put_Name)(BSTR pRetVal)
{
Name = pRetVal;
return S_OK;
}
**//Method that is NOT defined in ISomething**
STDMETHOD(get_MyData)(BSTR * pRetVal)
{
MyData= pRetVal;
return S_OK;
}
**//Method that is NOT defined in ISomething**
STDMETHOD(put_MyData)(BSTR pRetVal)
{
MyData = pRetVal;
return S_OK;
}
}
2) My data producer fills the data and passes it to 3-rd party COM service
CComObject<Something> *Obj = NULL;
CComObject<Something>::CreateInstance(&Obj);
//Calling method defined in ISomething
Obj->put_Name(_bstr_t("Some data"));
**//Calling method that is NOT defined in ISomething**
Obj->put_MyData(_bstr_t("My data"));
//Passing data to COM service
CComPtr<ISomething> iObj;
Obj->QueryInterface(__uuidof(ISomething),(void **) &iObj);
CComPtr<ICommand> command = //init omitted, it's another 3-rd party object;
//Setting data
command->do(iObj);
3) My data consumer tries to get both defined and non-defined data but succeeds only in getting defined one, non-defined contains garbage
class SomethingEventSink : public CComObjectRootEx<CComSingleThreadModel>,
public IDispatch
{
//COM map omitted
STDMETHOD(SomethingEventHandler)(VARIANT sender, struct _SomethingEventArgs *args)
{
ISomething* obj;
Something* extObj;
args->get_Something(&obj);
BSTR Name, Name1, MyData;
//Works fine
obj->get_Name(&Name);
//Casting to my object pointer
extObj = reinterpret_cast<Something*>(obj)
//Works fine
extObj->get_Name(&Name1);
**//Works, but NO DATA I've set at producer step**
**//HOW TO MAKE IT WORK?**
extObj->get_MyData(&MyData);
return S_OK;
}
}
What your trying to do isn't possible the way you are doing it. However you may be able to get it to work if you can declare your own new interface?
In this case your object can implement ISomething & IMyInterface and you can define IMyInterface to have any new methods you want.

COM - [in] parameter as SAFEARRAY(STRUCT)

I am able to call a COM interface method using SAFEARRAY(BSTR) as input. If I define instead a simple (containing only some BSTR-s fields) STRUCT to pass into the COM I get
"[System.ArgumentException] = {"The parameter is incorrect. (Exception from HRESULT: 0x80070057 (E_INVALIDARG))"
The call to my COM server is not made because as it seems the arguments from client does not match the expected arguments on server: E_INVALIDARG
Here is my declaration in IDL:
typedef enum UserEntityType
{
User,
Group,
IPAddress
} UserEntityType;
[
uuid(4786F77E-BA5F-4806-B224-12AA8601A5B1)
]
typedef struct UserEntity
{
UserEntityType EntityType;
BSTR Value;
} UserEntity;
[id(9)] HRESULT SetUsers([in] SAFEARRAY(UserEntity) input);
The exception is thrown at run-time when a C# 4 client calls SetUsers().
The problem was in the C# client that was using the exe COM server where
SetUsers([in] SAFEARRAY(UserEntity) input)
was defined.
In order to fix this (at least for .NET 4.0) one has to change the following property of the imported COM server: Embed Interop Types = False