I use this code:
EDIT:
interface __declspec(uuid("{325ADA34-FF89-4AAB-BB14-A81A07F8C791}")) IRegister : public IDispatch
{
public:
virtual bool IsRegister(char* address) = 0;
virtual void Register(char* address) = 0;
};
IRegister* pBox;
void Test1()
{
pBox->Register("test");
auto a = pBox->IsRegister("test");
}
extern "C" int _tmain(int argc, _TCHAR * argv[])
{
CoInitialize(NULL);
CLSID clsid;
HRESULT hr1 = CLSIDFromProgID(OLESTR("GameCheater.COM.RegisterManager"), &clsid);
HRESULT hr = CoCreateInstance(
clsid,
NULL,
CLSCTX_LOCAL_SERVER,
IID_PPV_ARGS(&pBox)
);
if (FAILED(hr))
{
printf("Couldn't create the instance!...");
}
else
{
Test1();
}
return 0;
}
The handler is created, but when I try to use the class, I received this error:
This working perfect in C#:
I use this code to create the com class:
[ComVisible(true)]
[Guid("325ADA34-FF89-4AAB-BB14-A81A07F8C791")]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface IRegister
{
[DispId(0)]
bool IsRegister(string address);
[DispId(1)]
void Register(string address);
}
[ComVisible(true)]
[Guid("DA27DEC3-6B10-452E-B9B0-0AAC8C86D892")]
[ProgId("GameCheater.COM.RegisterManager")]
[ClassInterface(ClassInterfaceType.None)]
public class RegisterManager : IRegister
{
public bool IsRegister(string address)
{
return RegisterPull.dictionary.TryGetValue(address, out _);
}
public void Register(string address)
{
var clientDetails = new ClientDetails()
{
Address = address,
LastRegister = DateTime.Now
};
if (RegisterPull.dictionary.ContainsKey(address))
RegisterPull.dictionary[address] = clientDetails;
else
RegisterPull.dictionary.TryAdd(address, clientDetails);
}
public RegisterManager()
{
}
~RegisterManager()
{
}
}
How I can solve the error? How can I cast the class with C++ class?
I searched for many tutorials about this, but I don't found anything.
All COM interfaces derive from IUnknown
, and sometimes from IDispatch
. Your interface
needs to do the same, according to the requirements of the COM object you are trying to use, eg:
interface RegisterManager : public IUnknown { // or IDispatch
...
};
Also, interface
methods need to be declared as pure virtual (ie abstract), eg:
public:
virtual bool IsRegister(char* address) = 0;
virtual void Register(char* address) = 0;
Also, you don't need the InterfaceDispatcher
class just to use __declspec(uuid)
. You can (and should) apply the uuid
directly to the interface
, eg:
interface __declspec(uuid("{325ADA34-FF89-4AAB-BB14-A81A07F8CE71}")) RegisterManager : public IUnknown {
...
};
In which case, your call to CoCreateInstance()
can then make use of the IID_PPV_ARGS
macro, eg:
HRESULT hr = CoCreateInstance(
clsid,
NULL,
CLSCTX_LOCAL_SERVER,
IID_PPV_ARGS(&pBox)
);
Also, you are calling Test1()
even if CoCreateInstance()
fails, which will cause your code to crash. Don't do that, eg:
HRESULT hr = CoCreateInstance(...);
if (SUCCEEDED(hr))
Test1();
else
printf("Couldn't create the instance, error: 0x%08x", hr);
UPDATE:
Now that you have posted your C# code, I see that your IRegister
interface is marked as InterfaceIsIDispatch
, which means it derives from IDispatch
rather than IUnknown
(also, its methods are marked with DispId
, so they are callable via the IDispatch.Invoke()
method).
Unfortunately, being marked as InterfaceIsIDispatch
also means the methods are accessible ONLY by IDispatch.Invoke()
(ie, late binding), so they are much more complicated to call then using the IRegister
interface directly (ie, early binding).
Per the InterfaceTypeAttribute
documentation:
By default, the Tlbexp.exe (Type Library Exporter) exposes a managed interface to COM as a dual interface, giving you the flexibility of late binding or the performance of early binding. The
ComInterfaceType
enumeration enables you to override the default behavior and specify late binding only or early binding only. For example, you can applyInterfaceType (ComInterfaceType.InterfaceIsIDispatch)
to an interface to produce metadata to restrict callers to late binding only. Although interfaces that derive from theIDispatch
interface are often dual, theInterfaceIsIDispatch
enumeration member allows only late-bound calls to the interface methods. This attribute has no effect on the managed view of the interface. For additional information on how interfaces are exposed to COM, see Exported Type Conversion.
So, for example (error handling omitted for brevity):
IDispatch* pBox;
void Test1()
{
VARIANTARG arg;
VariantInit(&arg);
V_VT(&arg) = VT_BSTR;
V_BSTR(&arg) = SysAllocString(OLESTR("test"));
DISPPARAMS param = {};
param.rgvarg = &arg;
param.cArgs = 1;
DISPID dispid;
//pBox->Register("test");
LPCWSTR name = OLESTR("Register");
pBox->GetIDsOfNames(IID_NULL, (LPOLESTR*)&name, 1, 0, &dispid);
pBox->Invoke(dispid, IID_NULL, 0, DISPATCH_METHOD, ¶m, NULL, NULL, NULL);
//auto a = pBox->IsRegister("test");
name = OLESTR("IsRegister");
pBox->GetIDsOfNames(IID_NULL, (LPOLESTR*)&name, 1, 0, &dispid);
VARIANT a;
VariantInit(&a);
pBox->Invoke(dispid, IID_NULL, 0, DISPATCH_METHOD, ¶m, &a, NULL, NULL);
VariantClear(&a);
VariantClear(&arg);
}
extern "C" int _tmain(int argc, _TCHAR * argv[])
{
CoInitialize(NULL);
CLSID clsid;
HRESULT hr1 = CLSIDFromProgID(OLESTR("GameCheater.COM.RegisterManager"), &clsid);
HRESULT hr = CoCreateInstance(
clsid,
NULL,
CLSCTX_LOCAL_SERVER,
IID_PPV_ARGS(&pBox)
);
if (FAILED(hr))
{
printf("Couldn't create the instance!...");
}
else
{
Test1();
pBox->Release();
}
CoUninitialize();
return 0;
}
Refer to Accessing Members Through IDispatch for more details.