Re: C# DLL mittels COM-Interop aus C aufrufen
- From: Frank Dzaebel <tcnt.Dzaebel@xxxxxxxxxxxxxxxxxxx>
- Date: Mon, 22 Sep 2008 05:13:00 -0700 (PDT)
Hallo Peter,
Danke für deine schnelle Antwort, bin leider keine
Experts-Exchange Member,
ich auch nicht ... Du musst IMHO nur etwas warten
und dann herunterscrollen.
könntest du mir die Lösung direkt schreiben?
Wäre genial, danke schonmal!
ok, zur Sicherheit:
[Accepted Solution]
jkr:See e.g. http://msdn.microsoft.com/archive/en-us/directx9_c_summer_03/directx/intro/program/com/usingc.asp
("Using C to Access COM Objects "). Even though this is DirectX, it'll
give you the idea how this is done.
A more general explanation can be found in
http://support.microsoft.com/default.aspx?scid=kb;en-us;181473
("http://support.microsoft.com/default.aspx?scid=kb;en-us;181473").
The scoop is that when compiling in C, you will have to call the
methods via the VTBL that is embedded by conditional compilation, e.g.
#include <stdio.h>
#include <windows.h>
void main(void) {
IDispatch *pDisp; // Main IDispatch pointer.
unsigned short *ucPtr; // Temporary variable to hold names.
DISPID dispID; // Temporary variable to hold DISPIDs.
CLSID clsid; // Holds CLSID of server after CLSIDFromProgID.
HRESULT hr; // General error/result holder.
char buf[8192]; // Generic buffer for output.
// IDispatch::Invoke() parameters...
DISPPARAMS dispParams = { NULL, NULL, 0, 0 };
VARIANT parm1;
DISPID dispidNamed = DISPID_PROPERTYPUT;
// Initialize OLE Libraries.
OleInitialize(NULL);
{
// Get CLSID for Excel.Application from registry.
hr = CLSIDFromProgID(L"Excel.Application", &clsid);
if(FAILED(hr)) {
MessageBox(NULL, "Excel not registered.", "Error",
MB_SETFOREGROUND);
return;
}
// Start Excel97, Excel 2000, or Excel 2002 and get its
IDispatch pointer.
hr = CoCreateInstance(&clsid, NULL, CLSCTX_LOCAL_SERVER,
&IID_IDispatch, (void **)&pDisp);
if(FAILED(hr)) {
MessageBox(NULL, "Couldn't start Excel.", "Error",
MB_SETFOREGROUND);
return;
}
// Get the 'visible' property's DISPID.
ucPtr = L"Visible";
pDisp->lpVtbl->GetIDsOfNames(pDisp, &IID_NULL, &ucPtr,
1, // <--- Instead of 'pDisp->GetIDsOfNames()'
LOCALE_USER_DEFAULT,
&dispID);
sprintf(buf, "DISPID for 'Visible' property = 0x%08lx",
dispID);
MessageBox(NULL, buf, "Debug Notice", MB_SETFOREGROUND);
// Initiate parameters to set visible property to true.
VariantInit(&parm1);
parm1.vt = VT_I4;
parm1.lVal = 1; // true
// One argument.
dispParams.cArgs = 1;
dispParams.rgvarg = &parm1;
// Handle special-case for property-puts!
dispParams.cNamedArgs = 1;
dispParams.rgdispidNamedArgs = &dispidNamed;
// Set 'visible' property to true.
hr = pDisp->lpVtbl->Invoke(pDisp, // <--- Instead of
'pDisp->Invoke()'
dispID, &IID_NULL, LOCALE_SYSTEM_DEFAULT,
DISPATCH_PROPERTYPUT | DISPATCH_METHOD,
&dispParams, NULL, NULL, NULL
);
if(FAILED(hr)) {
sprintf(buf, "IDispatch::Invoke() failed with %08lx",
hr);
MessageBox(NULL, buf, "Debug Notice",
MB_SETFOREGROUND);
}
// All done.
MessageBox(NULL, "done.", "Notice", MB_SETFOREGROUND);
}
// Uninitialize OLE Libraries.
OleUninitialize();
}
See also http://msdn.microsoft.com/library/en-us/dncomg/html/msdn_com_co.asp
("The COM Programmer's Cookbook") on more details.
___________________
[ASSISTED SOLUTION]
Mukesh_81:Hi
I have found one article which explains u the why u r facing
problem. Coz C cannot create vtables which is required to simulate in
COM component. Please refer below article.
Have u tried the IDispatch interface, which is used for those language
which doesnot have capability of maintaining VTables.
Using COM Object in C:
Using COM components from C involves simulating the vtable and virtual
function calls that C++ does. This is fairly simple, but tedious, in
C. Thankfully, the MIDL-generated header file takes care of most of
the problem.
Converting the code back to C
Of course, we had to change all of the cout << "Output"; statements to
printf calls. And we had to go back to the old (void **) cast operator
since reinterpret_cast isn't available in C. That was tedious, but not
life-threatening.
The hardest part of converting the example above into C was the agony
of having to put up with C's restrictions. All of the variable
declarations had to be moved to the beginning of the block. And
there's no reference type in C, so we had to add ampersands (&) in
front of all of the GUIDs in function calls. And we had to say "struct
IFoo" instead of just "IFoo", as you can in C++. Still, you'll
recognize the code pretty easily.
Note that we use the exact same header file and link with the exact
same definitions file as in the C++ case. How can we use the same
header? A quick check of the MIDL-generated header file reveals that
it's full of conditional compilation blocks of the form:
#if defined(__cplusplus) && !defined(CINTERFACE)
// C++ code
#else
// C code
#endif
The __cplusplus symbol is defined automatically when you're compiling C
++ code?it's not defined otherwise. In other words, the fact that
we're including the file into a C module means that we're compiling
the proper code. Note that the second half of the condition gives you
a way to use C definitions even if you're compiling a C++ program?just
#define CINTERFACE before you include the header.
Data types to simulate C++ vtables and objects in C
This "proper code" can look pretty weird because we have to come up
with C data structures that correspond with a C++ class, including the
vtable. For example, here's the C code MIDL generated for our IFoo
vtable format:
typedef struct IFooVtbl
{
BEGIN_INTERFACE
HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
IFoo __RPC_FAR * This,
/* [in] */ REFIID riid,
/* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);
ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
IFoo __RPC_FAR * This);
ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
IFoo __RPC_FAR * This);
HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Func1 )(
IFoo __RPC_FAR * This);
HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Func2 )(
IFoo __RPC_FAR * This,
int inonly);
END_INTERFACE
} IFooVtbl;
BEGIN_INTERFACE and END_INTERFACE are defined as nothing on most
platforms, including Windows. So what we really need to look at are
the members of this structure.
Notice that there's one member for each method in the interface,
including the IUnknown methods. The type of each member is the
appropriate function pointer type. The name of each member is the name
of the method. Note that the values of the members are undefined?this
structure is merely a "template" (not a C++ template) that's placed
"over" the vtable that's provided by the object. (If you implement
your COM object in C++, you have to provide a filled-in structure as
your vtable.) This structure insures that your calls match the method
prototypes.
Note that each of the methods has an additional parameter called
"This". (An initial capital letter is used so the compiler won't get
confused with the C++ keyword this in the event that you compile C-
style COM usage with a C++ compiler.) You have to pass the This
pointer parameter explicitly in C?it's passed automatically in C++.
Any additional parameters come after This, as above.
The interface is defined as a structure that contains a pointer to the
vtable, as follows:
interface IFoo // interface is #defined as struct
{
CONST_VTBL struct IFooVtbl __RPC_FAR *lpVtbl;
};
CONST_VTBL is simply "const." This structure contains one member: a
pointer to the vtable. Note that this is exactly what a C++ object
with no data contains: a pointer to the class's vtable. Again, there
is no data initialization. The data is provided by the object and
we're simply interpreting the type of that data in C.
Initializing COM and creating our object in C
Initializing COM and creating our object in C looks almost like doing
it in C++. Note that the variables are declared at the top; note
further that we have to use the "struct" keyword for interface pointer
declarations since we're actually declaring a pointer to a structure.
After that, we call CoInitialize and CoCreateInstance almost as
normal. Note that we have to explicitly pass addresses of the GUID
structures to CoCreateInstance?C doesn't have a reference type.
HRESULT hr;
struct IFoo *pIFoo;
struct IGoo *pIGoo;
printf("Hello, world!\n\n");
hr = CoInitialize(NULL);
if (FAILED(hr)) {
printf("CoInitialize Failed: %x\n\n", hr);
exit(1);
}
else {
printf("CoInitialize succeeded\n");
}
hr = CoCreateInstance(&CLSID_MyObject, NULL, CLSCTX_ALL,
&IID_IFoo, (void **)&pIFoo);
if (FAILED(hr)) {
printf("CoCreateInstance Failed: %x\n\n", hr);
goto Uninit;
}
else {
printf("CoCreateInstance succeeded\n");
}
Again, we used gotos for errors. And, again, if you don't like it, you
can use nested "if" statements instead.
Calling methods on our object in C
Where you really pay the price for using C is in the awful code you
have to write to call methods. When you do a C++ virtual function
call, the compiler handles the double indirection and the this pointer
for you. When you use C to simulate a virtual function call, you have
to write it out yourself:
pIFoo->lpVtbl->Func1(pIFoo);
This code takes a bit of explanation. First, recall that piFoo is our
interface pointer?it points to a hunk of memory (in the object,
actually) that contains a vtable pointer. In the structure declaration
above, the name of the member that points to the vtable is lpVtbl.
So the first indirection gets us to the structure, and the second
indirection gets us to the vtable. Recall that we've defined the
vtable as a structure that contains a set of function pointers. One of
those pointers is called Func1; so we use that pointer now to call the
function in the vtable. (You don't have to explicitly dereference the
function pointer in C or C++, so the syntax is simpler than it could
be.) Because the type of the function pointer includes the types of
the parameters and return type, we're assured that the function will
be called with the right types.
If you declare a preprocessor symbol called COBJMACROS before you
include the header, you'll also get a macro for each method in the
interface. The macros are of the form:
#define IFoo_Func1(This) \
(This)->lpVtbl -> Func1(This)
We can then make the call below instead of the ugly call above:
IFoo_Func1(pIFoo);
It isn't as elegant as C++, but it's better than the other syntax.
Changing interfaces in C
Changing interfaces in C is almost as easy as in C++:
hr = pIFoo->lpVtbl->QueryInterface(pIFoo,
&IID_IGoo, (void **)&pIGoo);
if (FAILED(hr)) {
printf("QI Failed: %x\n\n", hr);
goto ReleaseIFoo;
}
else {
printf("QI succeeded\n");
}
Note that we have to do the funky function call, including explicitly
providing the this pointer. If we're using the COBJMACROS, we could
replace the QueryInterface call above with:
IFoo_QueryInterface(pIFoo, &IID_IGOO, (void **)pIGoo);
Cleaning up: Releasing the interfaces and uninitializing in C
Aside from the calling syntax, cleaning up when we're done is the same
as in C++:
IGoo_Release(pIGoo);
ReleaseIFoo:
pIFoo->lpVtbl->Release(pIFoo);
Uninit:
CoUninitialize();
Notice that I mixed the two syntaxes for calling Release. You can use
either with either interface pointer.
Assisted Solution
ciao Frank
--
Dipl.Inf. Frank Dzaebel [MCP/MVP C#]
http://Dzaebel.NET
.
- Follow-Ups:
- Re: C# DLL mittels COM-Interop aus C aufrufen
- From: Frank Dzaebel
- Re: C# DLL mittels COM-Interop aus C aufrufen
- References:
- C# DLL mittels COM-Interop aus C aufrufen
- From: Peter Wyss
- Re: C# DLL mittels COM-Interop aus C aufrufen
- From: Frank Dzaebel
- Re: C# DLL mittels COM-Interop aus C aufrufen
- From: Peter Wyss
- C# DLL mittels COM-Interop aus C aufrufen
- Prev by Date: Re: C# DLL mittels COM-Interop aus C aufrufen
- Next by Date: Re: GroupBox: Verhindern dass Inhalt den Rahmen überschreibt
- Previous by thread: Re: C# DLL mittels COM-Interop aus C aufrufen
- Next by thread: Re: C# DLL mittels COM-Interop aus C aufrufen
- Index(es):
Relevant Pages
|
Loading