|
|
|
|
There are 2 ways to create an ActiveX control in an MFC application :
1. Via the Window/Dialog Box.
1.1 If you have inserted an ActiveX control in a dialog box via the dialog editor, the wrapper classes for your ActiveX control would be generated automatically by the MFC Wizard.
1.2 You would also need to add a variable in your Window/Dialog Box class of type control.
1.3 Do not call CWnd::Create() of your ActiveX wrapper class.
1.4 This is because the ActiveX control objeect would already be created when your Window/Dialog Box is created.
2. Dynamically in Code.
2.1 Use the "Project"|"Add Class" menu item to insert an MFC class from an ActiveX control.
2.2 The wrapper class for your ActiveX will be generated (.h and .cpp files).
2.3 You would also need to add a variable in your Window/Dialog Box class of type control.
2.4 This time, you -do- need to call the CWnd::Create() method of your ActiveX wrapper class.
|
|
|
|
|
Note that code like the following :
BSTR bstr = L"My BSTR";
does not allocate a BSTR.
To allocate a BSTR, you must use ::SysAllocString() :
BSTR bstr = ::SysAllocString(L"My BSTR");
Verify this with a call to ::SysStringByteLen() :
UINT uiLen = ::SysStringByteLen(bstr);
In the first case (using merely L), we get uiLen == 0.
In the second case, (using ::SysAllocString()), we get uiLen == 14 (the size of the string "My BSTR" in unicode.
This is because creating a BSTR via ::SysAllocString() generates a proper BSTR with a preceding 4 byte length indicator placed in the memory location just preceding the actual BSTR data.
Code like L"My BSTR" only creates in memory a Unicode string without any preceding 4 byte length indicator.
- Bio.
|
|
|
|
|
The following is a definition of an X.509 Certificate as given by CP member Jeffery Walton in his article (Cryptographic Interoperability: Keys) :
A public key certificate is a digitally signed statement from one entity, stating that the public key of another entity is authentic. A signed certificate binds an entity to a public key. The certificate allows us (the users) to confirm the identity of the owner of a public key. In addition, it allows us (the users) to confirm the authenticity of the public key. If the public key were tampered, the signature on the certificate would no longer be valid. The same applies if the entity's information was tampered or changed.
|
|
|
|
|
Observe the following code :
class CMyClass
{
public :
// Default constructor.
CMyClass()
{
m_int = 0;
}
// Customized constructor.
CMyClass(int i)
{
m_int = i;
}
// Copy constructor.
CMyClass(const CMyClass& rhs)
{
operator=(rhs);
}
// Destructor.
~CMyClass()
{
long l = 0;
l++;
}
// Assignment operator.
CMyClass& operator = (const CMyClass& rhs)
{
m_int = rhs.m_int;
return *this;
}
// Property accessor.
int get_Int()
{
return m_int;
}
protected :
int m_int;
};
void DemonstrateArrayConstructorIterator()
{
CMyClass myclass_array[5];
return;
}
The following are pertinent points :
1. The declaration for "myclass_array" requires the presence of the default constructor for CMyClass. Without the default constructor for CMyClass, the above array declaration will not compile.
2. Declaration code like that would cause the VC++ compiler to generate internal functions known as the "vector constructor iterator" and the "vector destructor iterator" :
Via debugging and viewing assembly code, we will see the following code emited for the DemonstrateArrayConstructorIterator() function :
52: CMyClass myclass_array[5];
0040101A push offset CMyClass::~CMyClass (004012a0)
0040101F push offset CMyClass::CMyClass (00401280)
00401024 push 5
00401026 push 4
00401028 lea eax,[myclass_array]
0040102B push eax
0040102C call `eh vector constructor iterator' (00401d20)
53:
54: return;
00401031 push offset CMyClass::~CMyClass (004012a0)
00401036 push 5
00401038 push 4
0040103A lea ecx,[myclass_array]
0040103D push ecx
0040103E call `eh vector destructor iterator' (00401bc0)
55: }
The "vector constructor iterator" function will perform a loop to call the default constructor of CMyClass according to the number of elements declared for the "myclass_array" array.
The "vector destructor iterator" function will perform a loop to call the destructor of CMyClass according to the number of elements declared for the "myclass_array" array.
- Bio.
|
|
|
|
|
A copy constructor is exactly what the name implies : a constructor for an object that is initialized as a copy of another object. In other words it is a constructor for making clones.
If A is a clone of another object B, it means that A should be an exact copy of B and so A should contain member data that are copies of the member data of B. If, in the destructor, any member data of A is destroyed, this must not affect the member data of B.
Programs that invoke copy constructors usually involve STL containers (e.g. vectors, maps, queues, etc). Containers contain clones of objects. Hence the need for copy constructors. This is known as copy semantics. Programs that involve copy semantics usually do not emphasize size efficiency.
|
|
|
|
|
1. A default (no-parameter) constructor is for creating an object with default state.
1.1 That is, with member data set to default values.
1.2 The principle behind this is that (by design) such objects can be used immediately after creation.
1.3 Note that it must make sense to construct an object with default state.
1.4 There are 2 points to note about code like the following :
some_class instance;
1.4.1 This indicates that we want to create an object "instance" of type "some_class" and that "instance" should have default state.
1.4.2 If it is deemed acceptable that a "some_class" object, in default state, has member data that can be set to default values, then it does make sense to have "instance" in default state.
1.4.3 The compiler requires the definition of a default constructor in order to successfully compile code like the above.
2. If there are no constructor functions at all for "some_class", it means that we want the compiler to create the default constructor for us. This is not always a good idea because an unsatisfactory constructor may be created for our code.
3. If we have at least one non-default constructor for "some_class", but no default constructor for the class, e.g. :
some_class(int i);
This indicates the following :
3.1 That by design a "some_class" object has no default state.
3.2 That in order to be constructed properly, parameters must be given in order to sensibly initialize the state of the object.
3.3 In the case of "some_class", it means that parameter "i" is used to somehow initialize an instance to a meaningful state.
3.4 In this case, the compiler will not generate any default constructor for "some_class" because by design the class must not have one.
|
|
|
|
|
Note two of my points in my last post :
3.2 That in order to be constructed properly, parameters must be given in order to sensibly initialize the state of the object.
3.3 In the case of "some_class", it means that parameter "i" is used to somehow initialize an instance to a meaningful state.
Now, it can happen that the parameter(s) of a constructor be meaningless and would not sensibly initialize the object.
In this case, we can also throw an exception inside the constructor in order to indicate to the client code that something has gone wrong. The client code must of course contain appropriate try/catch blocks.
|
|
|
|
|
The following HTML code is a modified version of a generic test HTML code provided by Brian Muth MVP taken from the MSDN Forum for Visual C++ :
<html>
<head>
<title></title>
<script type="text/javascript">
<!--
function init()
{
var myobj = new ActiveXObject('<ProgID>');
var result = myobj.TestMethod();
document.getElementById ('Results').innerText = result;
}
-->
</script>
</head>
<body onload="init();">
<p id="Results"></p>
</body>
</html>
|
|
|
|
|
The following is advise from Bruan Muth MVP, taken from the MSDN Forum for Visual C++ Language :
To ensuring a safe ActiveX Object to be used inside a Web Page implement the IObjectSafety interface.
|
|
|
|
|
The following is advise from Brian Muth MVP, taken from the MSDN Forum for Visual C++ Language :
To use the <OBJECT> tag (to create a COM object), the ATL COM object needs to implement the IObjectWithSite interface. This can be done quite trivially, since ATL gives you the IObjectWithSiteImpl<> template. Just add the following line to your list of inherited classes:
public IObjectWithSiteImpl<CMyIeObj>,
and to the BEGIN_COM_MAP table, add:
COM_INTERFACE_ENTRY(IObjectWithSite)
|
|
|
|
|
The following advise is for Internet Explorers that features tabs :
In order to debug a web page for the purpose of stepping into a COM object or an ActiveX created within HTML code, it is not sufficient to simply start iexplore.exe.
This is because the main IE process may spawn additional iexplore.exe for every tab.
Hence one way to debug into a HTML page is as follows :
1. Start IE.
2. Load the web page.
3. Determine the Process ID (PID) of the iexplore.exe that is hosting the web page.
4. Debug into the running process of that iexplore.exe by using the "Attach To Process" facility.
5. Refresh the web page.
- Bio.
modified on Saturday, December 19, 2009 7:54 PM
|
|
|
|
|
According to the Web Design Group's web page for the "OBJECT" tag (<a href="http://htmlhelp.com/reference/html40/special/object.html"></a>) :
The CLASSID attribute may be used to specify an implementation for the object. Java applets, Python applets, and ActiveX controls all provide implementations for the embedded object, and so are specified with the CLASSID attribute...
Some examples of the use of the CLASSID attribute are also listed :
CLASSID="yahtzee.py"
CLASSID="java:Yahtzee.class"
Hence, the string value contained inside a CLASSID attribute need not always specify an ActiveX. It may be anything that can be interpreted by the browser. For example, the "CLSID:" prepending string is interpreted by the IE browser to expect the GUID of an ActiveX control to follow.
|
|
|
|
|
A potentially useful #import attribute is the inject_statement() attribute.
This attribute can be used to insert various statements inside a generated .TLH file.
For example, I was trying to import the EXCEL type library embededd inside EXCEL.EXE. However, this type library required several declarations from MSO.DLL.
What I did was to import both EXCEL.EXE and MSO.DLL and inserted an "#include" statement in the generated EXCEL.TLH file. This was what I did :
#include <windows.h>
#import "C:\Program Files\Common Files\Microsoft Shared\Office10\MSO.DLL"
#import "C:\Program Files\Microsoft Office\Office10\EXCEL.EXE" inject_statement("#include \"MSO.tlh\"") exclude("IRange", "IDummy") raw_interfaces_only
The "inject_statement" attribute led to the following being generated inside EXCEL.TLH :
namespace Excel {
//
// User-injected statements
//
#include "MSO.tlh"
}
Note the comment "User-injected statements" and the #include "MSO.tlh".
Cool.
- Bio.
|
|
|
|
|
The following case is extracted from the MSDN Visual C++ Forum :
Posted by Anand K Reddy :
I have created an MFC ActiveX dll and I have exposed the methods in Dispatch map. I am able to call the method and get the result using C#.NET client but unable to call from Javascript. When the method is called from Javascript and exception is thrown without any message. I am not able to debug where exactly the problem is.
<code>
The Javascript code is pasted below and the sample activeX control is attached.
<HTML>
<HEAD>
<OBJECT classid="2D0B18FB-673F-4B90-865F-3EC067C9DEA9" id="a"></OBJECT>
<TITLE></TITLE>
<script type='text/javascript' language='javascript'>
function callTest()
{
try{
var myobject;
myobject = new ActiveXObject("SAMPLEACTIVEX.sampleActiveXCtrl.1");
if(myobject != null) alert("Object Found");
if((myobject.MyAdd(2,3))!=null)
alert("got result");
else
alert("Result is Null");
}
catch(e)
{
alert("exception:"+e.message);
}
}
</SCRIPT>
</HEAD>
<BODY>
<INPUT TYPE="button" value="TEST" onClick="callTest()">
</BODY>
</HTML>
</code>
|
|
|
|
|
The following is Anand's IDL file snippet :
#include <olectl.h>
#include <idispids.h>
#define DISPID_ADD (1025314)
#define DISPID_MYADD (1025315)
[ uuid(4B16D152-7D25-48A4-8DFE-EABF2AFB7FA3), version(1.0),
helpfile("sampleActiveX.hlp"),
helpstring("sampleActiveX ActiveX Control module"),
control ]
library sampleActiveXLib
{
importlib(STDOLE_TLB);
// Primary dispatch interface for CsampleActiveXCtrl
[ uuid(D99114F8-641E-471E-AA70-5644C0A91E9A),
helpstring("Dispatch interface for sampleActiveX Control")]
dispinterface _DsampleActiveX
{
properties:
methods:
[id(DISPID_ABOUTBOX)] void AboutBox();
[id(DISPID_ADD)] long Add(short x, short y);
[id(DISPID_MYADD)] long MyAdd(short param1, short param2);
};
// Event dispatch interface for CsampleActiveXCtrl
[ uuid(AD8B5691-3F3D-413E-8468-77F7440ADD33),
helpstring("Event interface for sampleActiveX Control") ]
dispinterface _DsampleActiveXEvents
{
properties:
// Event interface has no properties
methods:
};
// Class information for CsampleActiveXCtrl
[ uuid(2D0B18FB-673F-4B90-865F-3EC067C9DEA9),
helpstring("sampleActiveX Control"), control ]
coclass sampleActiveX
{
[default] dispinterface _DsampleActiveX;
[default, source] dispinterface _DsampleActiveXEvents;
};
}
|
|
|
|
|
The following was my reply to Anand :
Hello Anand,
Quick answer :
1. In your OBJECT tag :
<OBJECT classid="2D0B18FB-673F-4B90-865F-3EC067C9DEA9" id="a"></OBJECT>
add a preceding "CLSID:" before the GUID of your ActiveX control :
<OBJECT classid="CLSID:2D0B18FB-673F-4B90-865F-3EC067C9DEA9" id="a"></OBJECT>
2. Do not create your ActiveX control as an ActiveX object :
// var myobject; // remove
// myobject = new ActiveXObject("SAMPLEACTIVEX.sampleActiveXCtrl.1"); // remove
if(a != null) alert("Object Found");
if((a.MyAdd(2,3))!=null)
alert("got result");
else
alert("Result is Null");
Rather, use the ActiveX control (i.e. "a") referred to in your OBJECT tag.
Pls try and let me know if you succeed. I'll return with a longer answer on another post.
- Bio.
|
|
|
|
|
The following is a continuation of Bio's reply :
Hello Anand,
As promised, the following is my long answer :
Long Answer
1. Unless the value of the CLSID attribute of the OBJECT tag in your HTML code contains the symbol "CLSID:" as in :
<OBJECT classid="CLSID:2D0B18FB-673F-4B90-865F-3EC067C9DEA9" id="a"></OBJECT>
The CLASSID attribute remains invalid and so your ActiveX will not get created.
2. Next, comes the problem with "myobject" :
myobject = new ActiveXObject("SAMPLEACTIVEX.sampleActiveXCtrl.1");
The issue has to do with the calling of the IPersistPropertyBag::InitNew() method of your MFC ActiveX Control. This interface method is called by the MSHTML.DLL code within IExplorer.exe.
3. The IPersistPropertyBag::InitNew() method is implemented for you by MFC via the COleControl::XPersistPropertyBag::InitNew() method. Your MFC class would have been derived from COleControl and so this function is already embedded within your MFC class.
3. Inside COleControl::XPersistPropertyBag::InitNew(), you will notice a call to set COleControl::m_bInitialized to TRUE. This is important as we will see later.
4. Now, when any of your control's methods is called, the JScript engine will first get MSHTML to obtain the ID of your method. This is the essence of late binding. Hence, if MyAdd() is to be invoked, JScript will need to know the ID of the MyAdd() function which in your ActiveX control would be DISPID_MYADD.
5. MSHTML will inquire of your ActiveX the ID of MyAdd() by calling your ActiveX control's IDispatch::GetIDsOfNames() method. This is implemented for you by MFC with the function : COleDispatchImpl::GetIDsOfNames().
6. After obtaining this ID, the IDispatch::Invoke() method will be called. The IDispatch::Invoke() method is implemented for your ActiveX by the COleDispatchImpl::Invoke() method. It is here that your MyAdd() function should get called. However, there is a line within COleDispatchImpl::Invoke() that goes like this :
// allow subclass to disable Invoke
if (!pThis->IsInvokeAllowed(dispid))
return E_UNEXPECTED;
This is the function COleControl::IsInvokeAllowed() and it is the "archilles tendon" of your problem.
7. Examining COleControl::IsInvokeAllowed() :
BOOL COleControl::IsInvokeAllowed(DISPID)
{
return m_bInitialized;
}
We will note that the state of the COleControl::m_bInitialized, if FALSE, will result in the premature conclusion of the COleDispatchImpl::Invoke() method, resulting in the return of the E_UNEXPECTED HRESULT. This is the cause of the exception in your Javascript.
8. In summary, adding "CLSID:" to the CLASSID attribute of the OBJECT tag is not sufficient. You must use the ActiveX control as declared by the OBJECT tag whose id is "a". The use of the ActiveXObject() function is meant to create and return a COM interface. There are 2 problems with the use of the ActiveXObject() function in relation to your Javascript :
8.1 By declaring "myobject" and then setting it to a new ActiveXObject(), a brand new instance of your ActiveX control will be created. The "a" object will remain but it will not be used. The calling of IPersists* interface methods of your ActiveX control will not be called.
8.2 "myobject" will end up without its COleControl::XPersistPropertyBag::InitNew() ever being called which will result in its COleControl::m_bInitialized remaining FALSE. This results in the COleDispatchImpl::Invoke() function returning E_UNEXPECTED.
Hence my recommendation that you use the "a" object and remove the declaration and use of "myobject".
I hope the above rather long explanation helps.
- Bio.
|
|
|
|
|
The following is take from a post by Nibu Thomas (MVP) in the Microsoft Visual C++ Language forum :
There is only one way to delete properly and it depends on the way you allocated.
So if you allocate with
int* ptr = new int[100]
delete [] ptr; // then delete like this
then you got to delete with delete [] ptr. Don't get fooled by what you see in the output window of visual studio. Because visual studio places a marker around at the beginning of the memory block to check whether delete has been called or not (it's provided just to help). It does not differentiate between 'delete []' and 'delete'.
So if you allocate with
int * ptr = new int;
delete ptr; // then delete like this
To get rid of these allocation and de-allocation headaches use
std::vector<int> Ints;
|
|
|
|
|
[This thread is a work in progress]
There was recently a question posted on the COM forum here at CodeProject and it involved a temporary VARIANT object being created (in a VB client code) to fill an [in, out, optional] VARIANT* parameter of a method call.
The person who posted the original question was one GuimaSun and his COM server code was listed as follows :
<pre>
STDMETHODIMP CDCSClient::CallService( VARIANT *p1 )
{
...
...
VariantClear( p );
VariantInit( p );
p->vt = VT_BSTR | VT_BYREF;
BSTR *pBSTR = new BSTR;
*pBSTR = SysAllocString( L"abc" );
p->pbstrVal = pBSTR;
...
...
...
}
</pre>
His VB client code was :
<pre>
For i = 1 To 1000
Dim myInt As Integer
myInt = 3
ret = client.CallService(myInt)
Next
</pre>
Apparently, his COM server code was changing the Variant Type of the input VARIANT pointer "p" and assigning it to contain a BSTR.
His VB code experienced memory leaks due to the BSTR (created inside his COM CallService() method) not being ::SysFreeString()'ed.
After much discussion with several members of the forum, Vi2 came up with a code suggestion for GuimaSun as follows :
<pre>
STDMETHODIMP CDCSClient::CallService(/*[in,out]*/ VARIANT *p)
{
if (V_VT(p) == (VT_VARIANT | VT_BYREF))
{
// only here you can change the type of passed variable (see VB example below)
VARIANT *p2 = V_VARIANTREF(p);
p2->vt = VT_BSTR;
p2->bstrVal = SysAllocString( L"abc" );
return S_OK;
}
}
Dim v As Variant
v = 1
Debug.Print TypeName(v) & " " & v
Call obj.CallService(v)
Debug.Print TypeName(v) & " " & v
</pre>
Basically, only a VARIANT of Variant Type (VT_VARIANT | VT_BYREF) can be modified to be of a different VT.
modified on Thursday, August 13, 2009 12:13 PM
|
|
|
|
|
1. There are two ways to reference types defined externally in an IDL file :
1.1 Via the [import] MIDL directive.
1.2 Via the [importlib] MIDL directive.
2. An example use of the [import] directive is listed below :
import "msado15.idl";
2.1 This avails all definitions inside the imported IDL file ("msado15.idl" in above example) inside the current IDL file.
3. An example use of the [importlib] directive is listed below :
library MyInterfacesLib
{
importlib("msado15.tlb");
...
};
3.1 This avails all types that have already been compiled in another type library ("msado15.tlb" in the above example) to the current IDL file.
3.2 Note that an [importlib] statement can only appear inside a [library] statement. Furthermore, the imported type library, together with the generated type library of the current IDL file, must be available at runtime for the application.
3.3 Now, unlike the [import] statement, where a #include <header file of the imported IDL> directive is generated inside the .h file the current IDL, an [importlib] statement does not cause the generation of such an #include statement.
3.4 Because of this, the cpp_quote IDL keyword should be used to #include any necessary header files in the generated .h file for the current IDL. For example :
cpp_quote("#include \"msado15.h\"")
|
|
|
|
|
1. The CCmdTarget class implements COM interfaces via the declaration of macros. These include DECLARE_INTERFACE_MAP(), BEGIN_INTERFACE_MAP(), INTERFACE_PART() and END_INTERFACE_MAP().
2. CCmdTarget also implements its own version of QueryInterface() named InternalQueryInterface().
3. In order to be generic, InternalQueryInterface() is coded with the help of the interface map which is accessed via the function GetInterfaceMap(). The interface map itself is an array of AFX_INTERFACEMAP_ENTRY structures. Each AFX_INTERFACEMAP_ENTRY structure indicates a specific COM interface that the CCmdTarget class exposes.
4. InternalQueryInterface() looks up this array of AFX_INTERFACEMAP_ENTRY structures to determine whether an interface is supported.
|
|
|
|
|
1. The API to use for connecting to the event source of an object to a sink in a client application is AfxConnectionAdvise().
2. Assuming an event source interface that is purely IDispatch-based, we can implement an event sink (using MFC) via a CCmdTarget-derived class.
3. Listed below are the basic requirements of a sink :
3.1 It must implement IDispatch.
3.2 It must acknowledge the support of the DIID (dispatch ID) of the outgoing event interface.
3.3 It must implement the methods of the outgoing source interface.
4. Based on the requirements listed above, we can proceed to create an eventsink that is based on code provided by MFC. The following are the salient points of such a class :
4.1 It must derive from CCmdTarget. This is due to the CCmdTarget class' internal support of the IDispatch interface (as per requirement 3.1). The CCmdTarget's IDispatch implementation is achieved via a tiny embedded class named XDispatch. An instance of this emdedded class (m_xDispatch) is declared inside CCmdTarget. m_xDispatch must first be initialized via the CCmdTarget::EnableAutomation() function. This should be called inside the constructor of the sink class.
4.2 It must include the DECLARE_INTERFACE_MAP() macro in its class definition. This indicates to the MFC framework that this class will have a custom interface map. Through the interface map, we declare the various interfaces (including any source Dispatch interface) supported by the sink class (as per requirement 3.2). Via the macros BEGIN_INTERFACE_MAP(), INTERFACE_PART() and END_INTERFACE_MAP(), we declare that the sink class implements the source Dispatch interface.
4.3 It must include the DECLARE_DISPATCH_MAP() macro in its class definition. This macro indicates to the framework that this class will provide a dispatch map to expose IDispatch-based methods and properties. Through the diapatch map, we declare the various methods and properties exposed by the sink class. The methods must of course correspond with the methods of the source IDispatch interface (as per requirement 3.3).
The macros used are : BEGIN_DISPATCH_MAP(), DISP_FUNCTION(), DISP_PROPERTY(), END_DISPATCH_MAP().
5. In a call to AfxConnectionAdvise(), the 3rd parameter must be the IUnknown interface pointer to the event sink. To get this IUnknown interface pointer, the entry point function is the CCmdTarget::GetIDispatch() function. Call this function using the sink object. From the IDispatch interface pointer returned from CCmdTarget::GetIDispatch(), we can QueryInterface() it for an IUnknown interface pointer.
modified on Tuesday, June 16, 2009 7:11 AM
|
|
|
|
|