G
Guest
In my quest to create a derived edit control in ATL that will go into an
application that will hopefully make me some money one day, I have decided to
try to devise a custom licensing scheme for it. I will describe it and would
like to see what people think and how it can be improved if possible please.
My aim is not to prevent it from being used in design mode / OK in run mode,
but to have a *relatively simple* method of making sure that it is only used
by my client application and no other. The idea is that I don't have to
understand all the IClassFactory2 palava (which I think it would take me far
too long, if at all), I just have to implement a certain class in any client
application I write that I want to use it, and then just copy and paste a
GUID into that class's source code (from the component's source code) to pass
to the component to validate it - not great you may think, but it seems good
because it is simple, while still possibly keeping out all but hardened
cryptographers / machine-code crackers.
The validation algorithm goes as follows: The ActiveX control (which is an
VC7.1 unmanaged ATL control) has a COM-exposed Validate method, that takes
one parameter which is an interface of type IDispatch. The method then looks
up the dispid of a method called "NearlyThere" using
IDispatch::GetIDsOfNames. It then assembles a DISPPARAMS with no arguments
and calls the method it found using IDispatch::Invoke. The client
application, which is written in C#, creates an instance of a class which has
the InterfaceAttribute set to IDispatch (thus implements IDispatch) and uses
this to pass to the IDispatch parameter of the Validate method of the
control. The "NearlyThere" method in this class returns a GUID, which seems
to safely make it back into the C++ side, and is stored as a VT_BSTR VARIANT
class member variable of the control. I was then thinking of having a method
(let's say "Authorize") which checks the GUID in this BSTR against a
constant, and throws a _com_raise_error(?) if they don't match.
The code in the ATL project is as such:
VARIANT r; //(actually in the .h file)
STDMETHODIMP Clicensed::Initialize2(IDispatch* d)
{
// TODO: Add your implementation code here
HRESULT hr;
DISPID dispid = 0, dispid2 = 0;
ITypeInfo* typeinfo;
OLECHAR* text = OLESTR("NearlyThere");
hr = d->GetIDsOfNames(IID_NULL, &text, 1, LOCALE_SYSTEM_DEFAULT, &dispid);
d->GetTypeInfo(0, LOCALE_SYSTEM_DEFAULT, &typeinfo);
DISPPARAMS dp;
memset(&dp, 0, sizeof(DISPPARAMS));
dp.rgvarg = NULL;
dp.cNamedArgs = 0;
dp.cArgs = 0;
EXCEPINFO excinf;
UINT uArgErr;
memset(&r, 0, sizeof(VARIANT));
hr = d->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD,
&dp, &r, &excinf, &uArgErr);
ATLTRACE(r.bstrVal); //the GUID gets back here OK
return S_OK;
}
and the code in the C# project is very little, but this is it:
//theinitobj.cs
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface IMySecurity
{
string NearlyThere();
}
public class theinitobj : IMySecurity
{
public theinitobj(){}
#region IBonjSecurity Members
public string NearlyThere()
{
return "{1F3B54F8-8615-43e4-B74D-4E1699CC990A}";
}
#endregion
}
//frmMain.cs
private void Form1_Load(object sender, System.EventArgs e)
{
IMySecurity ibs = new theinitobj();
this.axlicensed2.Initialize2(ibs);
}
I'm thinking of calling a routine that checks the received GUID before an
important algorithm happens (maybe a preprocessor macro) - but how do you
think I should go about aborting if it's wrong - _com_raise_error or
_com_issue_error, what's the difference? And what parameter? Or something
else?
What does this sound like? I thought it would be good because it is quite
contrived - it's calling then calling back again - but how easy would it be
to "switch return values" without getting into the function, and how
successful will it be at preventing people from being able to use it, do you
reckon?
Could it be improved?
application that will hopefully make me some money one day, I have decided to
try to devise a custom licensing scheme for it. I will describe it and would
like to see what people think and how it can be improved if possible please.
My aim is not to prevent it from being used in design mode / OK in run mode,
but to have a *relatively simple* method of making sure that it is only used
by my client application and no other. The idea is that I don't have to
understand all the IClassFactory2 palava (which I think it would take me far
too long, if at all), I just have to implement a certain class in any client
application I write that I want to use it, and then just copy and paste a
GUID into that class's source code (from the component's source code) to pass
to the component to validate it - not great you may think, but it seems good
because it is simple, while still possibly keeping out all but hardened
cryptographers / machine-code crackers.
The validation algorithm goes as follows: The ActiveX control (which is an
VC7.1 unmanaged ATL control) has a COM-exposed Validate method, that takes
one parameter which is an interface of type IDispatch. The method then looks
up the dispid of a method called "NearlyThere" using
IDispatch::GetIDsOfNames. It then assembles a DISPPARAMS with no arguments
and calls the method it found using IDispatch::Invoke. The client
application, which is written in C#, creates an instance of a class which has
the InterfaceAttribute set to IDispatch (thus implements IDispatch) and uses
this to pass to the IDispatch parameter of the Validate method of the
control. The "NearlyThere" method in this class returns a GUID, which seems
to safely make it back into the C++ side, and is stored as a VT_BSTR VARIANT
class member variable of the control. I was then thinking of having a method
(let's say "Authorize") which checks the GUID in this BSTR against a
constant, and throws a _com_raise_error(?) if they don't match.
The code in the ATL project is as such:
VARIANT r; //(actually in the .h file)
STDMETHODIMP Clicensed::Initialize2(IDispatch* d)
{
// TODO: Add your implementation code here
HRESULT hr;
DISPID dispid = 0, dispid2 = 0;
ITypeInfo* typeinfo;
OLECHAR* text = OLESTR("NearlyThere");
hr = d->GetIDsOfNames(IID_NULL, &text, 1, LOCALE_SYSTEM_DEFAULT, &dispid);
d->GetTypeInfo(0, LOCALE_SYSTEM_DEFAULT, &typeinfo);
DISPPARAMS dp;
memset(&dp, 0, sizeof(DISPPARAMS));
dp.rgvarg = NULL;
dp.cNamedArgs = 0;
dp.cArgs = 0;
EXCEPINFO excinf;
UINT uArgErr;
memset(&r, 0, sizeof(VARIANT));
hr = d->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD,
&dp, &r, &excinf, &uArgErr);
ATLTRACE(r.bstrVal); //the GUID gets back here OK
return S_OK;
}
and the code in the C# project is very little, but this is it:
//theinitobj.cs
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface IMySecurity
{
string NearlyThere();
}
public class theinitobj : IMySecurity
{
public theinitobj(){}
#region IBonjSecurity Members
public string NearlyThere()
{
return "{1F3B54F8-8615-43e4-B74D-4E1699CC990A}";
}
#endregion
}
//frmMain.cs
private void Form1_Load(object sender, System.EventArgs e)
{
IMySecurity ibs = new theinitobj();
this.axlicensed2.Initialize2(ibs);
}
I'm thinking of calling a routine that checks the received GUID before an
important algorithm happens (maybe a preprocessor macro) - but how do you
think I should go about aborting if it's wrong - _com_raise_error or
_com_issue_error, what's the difference? And what parameter? Or something
else?
What does this sound like? I thought it would be good because it is quite
contrived - it's calling then calling back again - but how easy would it be
to "switch return values" without getting into the function, and how
successful will it be at preventing people from being able to use it, do you
reckon?
Could it be improved?