Click here to Skip to main content

Lim Bio Liong - Professional Profile

Summary

43,403
Author
1,628
Authority
576
Debator
40
Organiser
916
Participant
0
Editor
0
Enquirer
Lim Bio Liong is a Specialist at a leading Software House in Singapore.
 
Bio has been in software development for over 10 years. He specialises in C/C++ programming and Windows software development.
 
Bio has also done device-driver development and enjoys low-level programming. Bio has recently picked up C# programming and has been researching in this area.
Member since Saturday, December 1, 2001 (11 years, 6 months)
  • 18 Aug 2006: MFC/C++ Jul 2006
  • 30 Jan 2005: MFC/C++ Dec 2004

Contributions

Articles 17 (Legend)
Tech Blogs 0
Messages 612 (Regular)
Q&A Questions 0
Q&A Answers 3
Tips/Tricks 0
Comments 0

Links

Reputation

For more information on Reputation please see the FAQ.

Privileges

Members need to achieve at least one of the given member levels in the given reputation categories in order to perform a given action. For example, to store personal files in your account area you will need to achieve Platinum level in either the Author or Authority category. The "If Owner" column means that owners of an item automatically have the privilege, and the given member types also gain the privilege regardless of their reputation level.

ActionAuthorAuthorityDebatorEditorEnquirerOrganiserParticipantIf OwnerMember Types
Have no restrictions on voting frequencysilversilversilversilverAdmin
Store personal files in your account areaplatinumplatinumSitebuilder, Subeditor, Supporter, Editor, Staff
Have live hyperlinks in your biographybronzebronzebronzebronzebronzebronzesilverSubeditor, Protector, Editor, Staff, Admin
Edit a Question in Q&AsilversilversilversilverYesSubeditor, Protector, Editor, Admin
Edit an Answer in Q&AsilversilversilversilverYesSubeditor, Protector, Editor, Admin
Delete a Question in Q&AYesSubeditor, Protector, Editor, Admin
Delete an Answer in Q&AYesSubeditor, Protector, Editor, Admin
Report an Articlesilversilversilversilver
Approve/Disapprove a pending ArticlegoldgoldgoldgoldSubeditor, Mentor, Protector, Editor, Staff, Admin
Edit other members' articlesSubeditor, Protector, Editor, Admin
Create an article without requiring moderationplatinumSubeditor, Mentor, Protector, Editor, Staff, Admin
Report a forum messagesilversilverbronzeProtector, Editor, Admin
Create a new tagsilversilversilversilverAdmin
Modify a tagsilversilversilversilverAdmin

Actions with a green tick can be performed by this member.


 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   


Advertise | Privacy | Mobile
Web02 | 2.6.130617.1 | Last Updated 18 Jun 2013
Copyright © CodeProject, 1999-2013
All Rights Reserved. Terms of Use
Layout: fixed | fluid

You must Sign In to use this message board.
Search this forum  
GeneralDeclaring an Interop Struct that contains a character array. Pin
Saturday, January 8, 2011 4:04am by Lim Bio Liong
Pls refer to :
 
http://social.msdn.microsoft.com/Forums/en-US/clr/thread/c6f28e84-dcc8-4933-a4f4-54da719db93f[^]
 
The above link discusses the following :
 
1. A C++ struct is declared. This struct contains a character array (commonly used as a string in C++).
 
2. A C# counterpart struct is to be declared.
 
3. The link discusses how the character array can be declared in C#.
 
- Bio.
 
GeneralCOM SAFEARRAYs - How to use it in C# Pin
Saturday, September 25, 2010 1:25pm by Lim Bio Liong
Refer to :
 
"ATL COM Server - Array of Structs"
 
http://social.msdn.microsoft.com/Forums/en-US/vcmfcatl/thread/76c95583-070b-4cfe-b2fd-ff2003c70a9d[^]
 
- Bio.
 
GeneralTroubleshooting Exceptions: System.BadImageFormatException Pin
Friday, September 17, 2010 8:53pm by Lim Bio Liong
Important MSDN support article :
 
"Troubleshooting Exceptions: System.BadImageFormatException"
 
http://msdn.microsoft.com/en-us/library/k7137bfe(v=VS.90).aspx[^]
 
See especially the community comment by Mayank H Parmar.
 
GeneralCreating ActiveX Controls in an MFC Application Pin
Sunday, January 10, 2010 7:26pm by Lim Bio Liong
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.
 
GeneralCareful to allocate BSTRs using ::SysAllocString(). Pin
Thursday, December 31, 2009 7:08pm by Lim Bio Liong
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.
 
GeneralAbout X.509 Certificates Pin
Thursday, December 31, 2009 6:25pm by Lim Bio Liong
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.
 
GeneralSome General Points on Array Initialization Part 1 Pin
Tuesday, December 22, 2009 8:25pm by Lim Bio Liong
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.
 
GeneralSome Comments on Copy Constructors Pin
Sunday, December 20, 2009 11:19pm by Lim Bio Liong
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.
 
GeneralDefault and Customized Constructors Pin
Sunday, December 20, 2009 10:54pm by Lim Bio Liong
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.
GeneralDefault and Customized Constructors (Part 2) PinmemberLim Bio Liong20-Dec-09 23:16 
 
GeneralGeneric Test HTML Code For Testing ActiveX Objects Pin
Saturday, December 19, 2009 2:05pm by Lim Bio Liong
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>
 
GeneralEnsuring a Safe ActiveX Object to be used inside a Web Page Pin
Saturday, December 19, 2009 2:02pm by Lim Bio Liong
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.
 
GeneralCOM Objects that are created via <object> tags</object> Pin
Saturday, December 19, 2009 1:59pm by Lim Bio Liong
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)
 
GeneralInternet Explorer with Tabs : Debugging a COM Object or an ActiveX inside a Web Page [modified] Pin
Saturday, December 19, 2009 1:23pm by Lim Bio Liong
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

 
GeneralThe Significance of the "CLSID:" Symbol for the OBJECT Tag's CLASSID Attribute Pin
Tuesday, December 8, 2009 11:24pm by Lim Bio Liong
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.
 
GeneralCool use of the #import inject_statement() attribute Pin
Friday, December 4, 2009 12:29am by Lim Bio Liong
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 &lt;windows.h&gt;
#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.
 
GeneralFailure of javascript usage of MFC ActiveX Control - Part 1 Pin
Thursday, December 3, 2009 4:31pm by Lim Bio Liong
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>
GeneralFailure of javascript usage of MFC ActiveX Control - Part 2 PinmemberLim Bio Liong3-Dec-09 16:33 
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;
     };
 
}
GeneralFailure of javascript usage of MFC ActiveX Control - Part 3 PinmemberLim Bio Liong3-Dec-09 16:35 
GeneralFailure of javascript usage of MFC ActiveX Control - Part 4 PinmemberLim Bio Liong3-Dec-09 16:36 
 
GeneralInteresting Info On delete and delete [] Pin
Wednesday, November 25, 2009 5:27pm by Lim Bio Liong
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;
 
GeneralInteresting Problem Related to a Temporary VARIANT - Personal Findings - Part 1 Pin
Wednesday, August 12, 2009 10:41pm by Lim Bio Liong
[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

 
GeneralTwo Ways To Reference External Types Into An IDL. Pin
Tuesday, June 16, 2009 1:11am by Lim Bio Liong
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\"")
 
GeneralThe Interface Map of CCmdTarget. Pin
Wednesday, May 6, 2009 8:52pm by Lim Bio Liong
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.
 
GeneralHandling COM Events in an MFC Client. [modified] Pin
Wednesday, May 6, 2009 8:28pm by Lim Bio Liong
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

 
GeneralDetermine Whether ActiveX is in Design Mode or in Runtime Mode. Pin
Sunday, March 29, 2009 11:03pm by Lim Bio Liong
To determine whether an ActiveX is currently in Design Mode or in Runtime Mode, use the CComControl::GetAmbientUserMode() ATL function.
 
GeneralAsynchronous Interfaces Pin
Wednesday, January 28, 2009 11:03pm by Lim Bio Liong
Note the following about asynchronous interfaces :
 
1. An asynchronous interface must only derive from IUnknown directly.
 
2. If an asynchronous interface is derived from another interface that derives from IUnknown, calls to an asynchronous method may not go correctly (e.g. a different method may end up getting called).
 
3. If an asynchronous interface is derived from IDispatch or is derived from another IDispatch-derived interface, it will not be possible to obtain the asynchronous interface from ICallFactory::CreateCall().
 
GeneralObject Implementing 2 Interfaces with same method names. Pin
Monday, January 19, 2009 9:20pm by Lim Bio Liong
If a COM object (written in C++) is to implement 2 interfaces with a common name and common number of parameters and parameter types, then the following are options :
 
1. Use interface shims to supply 2 separate implementations of the 2 methods.
 
2. Supply only one method. This way, both interfaces share one implementation for the common method.
 
If a COM object (written in C++) is to implement 2 interfaces with a common name but different number of parameters or different types of parameters , then the following assertion holds :
 
1. The C++ compiler will properly sort out the appropriate method for each interface's vtbl. In other words, nothing need be done. The C++ compiler will ensure that the correct method will be called according to which interface is used.
 
GeneralAsynchronous Interface Implementations Pin
Sunday, January 18, 2009 11:24pm by Lim Bio Liong
Note the following findings about asynchronous interfaces :
 
1. An interface that is marked to be asynchronous must be derived from IUnknown. It cannot be derived from IDispatch.
 
2. A C++/ATL implementation which has #import'ed the originating IDL may experience compilation errors e.g. "MIDL2320 : conflicting optimization requirements, cannot optimize : [Procedure '...']" :
 
2.1 The cause of the problem has to do with the fact that the asynchronous interface most likely was declared outside of the "library" statement in its original IDL file.
 
2.2 This indicates to the compiler (compiling the current implementation) that the asynchronous interface will not be using Type Library marshaling (i.e. standard marshaling that uses the Type Library for information instead of any proxy/stub DLL).
 
2.3 The marshaling for the asynchronous interface will be done via a proxy/stub DLL.
 
2.4 We currently do not why the compilation error is issued. However, we do have a soluton : place an "importlib" statement inside the "library" statement of the current implementation. It must import the type library of the original IDL file.
 
2.5 This will avert the compilation problem.
 
3. A good news about asynchronous interfaces is that it can be implemented in VB. However, the VB implementation must be ActiveX EXE based.
 
GeneralVB ActiveX Does Not Support Externally Defined Events Pin
Tuesday, January 13, 2009 5:49pm by Lim Bio Liong
* If you define an interface in a Type Library and implement it in Visual Basic, no problem.
 
* For example, if you define an interface ITestInterface in a Type Library and then have your VB ActiveX project reference this type lib, the following statement goes fine :
 
Implements ITestInterface
 
* However, if you define a source (i.e. event) interface in a Type Library and try to have a Visual Basic ActiveX raise its events via RaiseEvent statements, not possible.
 
* For example, if you define a dispinterface _ITestEvents with event Event01() in a Type Library and then have your VB ActiveX Project raise it as follows :
 
RaiseEvent Event01()
 
The VB Compiler will complain of an unidentified event.
 
If you try to raise it as follows :
 
RaiseEvent <typelibraryname>.Event01()
 
The compiler will not complain but it still won't work. The problem is that _ITestEvents will not be recognized as being a source interface of the ActiveX.
 
* If we try to define the events of _ITestEvents in the declaration of the ActiveX itself, e.g. :
 
Public Event Event01()
 
The ActiveX will then declare a source interface for itself and Event01() will be absorbed into this default source event set, i.e. you will find the following declarations when you observe the TLB generated for the ActiveX :
 
coclass clsVBActiveX
{
   ...
   ...
   ...
   [default, source] dispinterface __clsVBActiveX;
};
 
...
...
...
dispinterface __clsVBActiveX
{
   void Event01();
};
 
GeneralVisual Basic COM Objects Shutdown Rules. Pin
Monday, December 29, 2008 8:28pm by Lim Bio Liong
See the MSDN topic "Visual Basic Component Shutdown Rules" for more details.
 
One important point to note is that if a VB-written COM object creates or simply loads any forms, then this form must be unloaded in order that the VB-written COM object be able to shut down properly.
 
GeneralCOM Spy Applications Pin
Monday, December 29, 2008 8:11pm by Lim Bio Liong
1. COM Spy console that allows you to spy applications using COM interfaces:
http://www.nektra.com/products/deviare/comconsole/index.php
 
The COM Spy application is based on Deviare hook library so all you can see in the console can be done by code from any language supporting COM. Source code of this application is available.
 
2. Another one :
 
http://staff.develop.com/jasonw/comspy/default.htm
 
3. Yet another open sourced COM spy :
 
http://jacquelin.potier.free.fr/winapioverride32/
 
GeneralUsing CRITICAL_SECTION objects inside VB app may cause a crash in MSVBVM60.dll Pin
Thursday, November 20, 2008 11:23pm by Lim Bio Liong
Using a CRITICAL_SECTION objects inside VB app may cause a crash in MSVBVM60.dll. The VB app that I created was a COM object.
 
Reasons unknown at this time (circa November 2008).
 
GeneralWhy an ActiveX control written in ATL only fires its default event to a VB client. And how to overcome this. [modified] Pin
Thursday, June 12, 2008 7:18pm by Lim Bio Liong
If you write an ActiveX control in ATL, then if you specify that your ActiveX control will fire a non-default event, a VB client application will not be able to receive this event.
 
The reason for this is that a VB-written client application will only connect with the -default- event set of the ActiveX control.
 
The default event set is that which is specified in the ATL ActiveX control's class declaration, specifically the IProvideClassInfo2Impl template instance declaration :
 
IProvideClassInfo2Impl<[clsid], [iid of default outgoing dispinterface], ...>
 
Here, the second parameter is used to specify the IID of the default outgoing dispinterface.
 
If one needs the VB client app to connect with a different event set, then modify this IID accordingly.
 
modified on Sunday, November 30, 2008 8:45 AM

 
GeneralType Library Marshaling Pin
Friday, June 6, 2008 3:58am by Lim Bio Liong
Q : For marshelling between apartments, are the stub/proxy code generated automatically by COM Runtime ? Is thre a need for us to explicitly write stub/proxy code by ourselves ?
 
A : If your interface methods and properties strictly uses types that are automation-compatible (i.e. the types that can be found in a VARIANT structure), then the standard proxy and stub code as contained in OLEAUT32.DLL (the standard Proxy/Stub DLL for Ole Automation) will be used. Yes, in this case, there is no need to explicitly program proxy/stub code.
 
However, please take note of the following :
 
1. The type library containing your interface definitions must be registered. This type library will be loaded by OLEAUT32.DLL at runtime in order to understand the signatures of your methods (parameter types, return types) for correct marshaling/unmarshaling and method calls across apartments.
 
2. The interface definitions involved in marshaling must be contained in the type library. In order for this to happen, the interface definition must either be defined inside the "library" statement in the source IDL file, or it is at least referenced inside the "library" statement. Otherwise, marshaling will fail. One of the possible failure code is TYPE_E_ELEMENTNOTFOUND (0x8002802BL).
 
3. Most importantly, the interface definition must be attributed with either "oleautomation" or "dual" or that it is by definition a "dispinterface". This will ensure that the Type Library Marshaler (i.e. OLEAUT32.DLL) recognizes the interface as being OLE-AUTOMATION compatible. Failure to comply will result in failure in marshaling. One of the possible failure code is E_FAIL (0x80004005L).
 
GeneralOne possible solution to LNK1169 problem. [modified] Pin
Thursday, May 8, 2008 3:14am by Lim Bio Liong
A few days ago, I created an ATL (with MFC support) DLL project. The project compiled properly with absolutely no problem.
 
Then today, after doing some code modifications (which, believe me, were trivial and innocuous), I was confronted with the link error LNK1169 : Multiple Symbols Defined.
 
The problem was that while linking with mfcs42.lib, the linker noticed that this library contains a DEFINITION (i.e. full code, and not just a reference) of the function _DllMain@12. However, it also noted that the same function _DllMain@12 was already defined inside MSVCRTD.lib.
 
After some research and testing, I discovered the following facts regarding DllMain() when used in the context of a DLL project in which MFC is used :
 
1. Because MFC is used, a CWinApp class will be generated. This is so that the DLL application startup and shutdown processes can be abstracted by the CWinApp class (e.g. the use of InitInstance() and ExitInstance(), etc).
 
2. For this to happen, MFC must have its own version of DllMain() which will initialize the CWinApp class etc. Hence, this (MFC's) DllMain() must be the one eventually used (i.e. linked-to) by the Dll.
 
3. The MSVC runtime libraries also include their own versions of DllMain(). This being the case, potential conflicts can happen.
 
4. Such multiple-defined symbol link errors normally do not cause any problems because the linker will use the function that it finds first in its list of libraries.
 
5. It is still unclear to me why this link error occurred but this is a blessing in disguise because had this error not appeared, the DllMain() defined inside MSVCRTD.LIB would have been used and there will be problems : i.e. my CWinApp class will not be properly initialized.
 
Solution
---------
The solution to the above problem is to make sure that MFC's DllMain() is discovered first by the linker during linkage.
 
The way to do this in VC++ 6.0 is to go to the Project Settings, go to the "Link" tab, go to the "General" category, type in the name of the appropriate MFC library (e.g. mfcs42.lib, in my case) in the "Object/library modules" box. If the competing library (i.e. msvcrtd.lib in my case) is also in the box, make sure that the target MFC library is placed BEFORE the competing library.
 
Note that in my case, simply typing mfcs42.lib in the box ensured that this library is used first. Msvcrtd.lib was not in the box and so I did not have to put it there either.
 
Hope this helps others.
 
- Bio.
 
modified on Friday, May 9, 2008 7:52 AM

 
GeneralRequirements of classes whose objects are stored in containers. Pin
Monday, January 7, 2008 7:57pm by Lim Bio Liong
For C++ classes whose objects are stored inside containers like vectors, lists, deques, the following class items are generally required :
 
1. Constructors - naturally.
 
2. Copy Constructors - for creating objects that are stored inside the containers. These contained objects are actual copies of the originals that are inserted into the containers.
 
3. Destructors - naturally.
 
4. Assignment operator - needed especially when elements in the containers are removed (e.g. via the erase() method).
 
GeneralAffecting the order of Class Factory Registration in a COM Exe Server. Pin
Wednesday, December 26, 2007 5:32pm by Lim Bio Liong
If you have written a COM EXE server in ATL, it is possible to change the order of Class Factory registration in the server. Here's how :
 
1. Look up the ObjectMap declaration. This is encapsulated between the BEGIN_OBJECT_MAP() and END_OBJECT_MAP() macros.
 
2. Between the BEGIN_OBJECT_MAP() and END_OBJECT_MAP() macros are listed lines of OBJECT_ENTRY() statements.
 
3. The order of this list affects the order of Class Factory registration.
 
4. For example, in the following sample code :
 
BEGIN_OBJECT_MAP(ObjectMap)
OBJECT_ENTRY(CLSID_TestCOMExeObject01, CTestCOMExeObject01)
OBJECT_ENTRY(CLSID_TestCOMExeObject02, CTestCOMExeObject02)
END_OBJECT_MAP()
 
The class factory for CTestCOMExeObject01 will be registered first followed by CTestCOMExeObject02.
GeneralRe: Affecting the order of Class Factory Registration in a COM Exe Server. PinmemberLim Bio Liong3-May-10 0:46 
 
GeneralGetting the Full Path to a COM DLL Pin
Monday, December 24, 2007 7:52pm by Lim Bio Liong
TCHAR szModuleFilePath[_MAX_PATH];
 
GetModuleFileName
(
_Module.GetModuleInstance(),
szModuleFilePath,
sizeof(szModuleFilePath)
);
 
where _Module is the CComModule of the COM DLL :
 
CComModule _Module;
 
GeneralThe meaning and use of bind1st() and bind2nd(). Pin
Monday, December 24, 2007 7:06pm by Lim Bio Liong
According to MSDN, bind1st() is a helper template function that creates an adaptor to convert a binary function object into a unary function object by binding the first argument of the binary function to a specified value.
 
What this means is that bind1st() fixes the "1st" argument of a 2 argument functor object to a certain value.
 
GeneralCode snippet : GetCustomAttribute() Pin
Saturday, December 22, 2007 8:51pm by Lim Bio Liong
Here is a short code snippet to extract a custom attribute from a data item in a Type Library :
 
HRESULT GetCustomAttribute
(
LPCTSTR lpszTypeLibraryPath, // Path to type library that contains definition of a User-Defined Type (UDT).
REFGUID refguid_data_item, // GUID of data item of which to obtain the Custom Attribute.
REFGUID refguid_custom_attribute, // GUID of the Custom Attribute.
VARIANT& varCustomAttributeReceiver // Receiver of the Custom Attribute.
)
{
_bstr_t bstTypeLibraryPath = lpszTypeLibraryPath;
ITypeLib* pTypeLib = NULL;
ITypeInfo* pTypeInfo = NULL;
ITypeInfo2* pTypeInfo2 = NULL;
HRESULT hrRet = S_OK;
 
// Initialize receiver.
VariantInit(&varCustomAttributeReceiver);
VariantClear(&varCustomAttributeReceiver);

hrRet = LoadTypeLib((const OLECHAR FAR*)bstTypeLibraryPath, &pTypeLib);
 
if (SUCCEEDED(hrRet))
{
if (pTypeLib)
{
hrRet = pTypeLib -> GetTypeInfoOfGuid(refguid_data_item, &pTypeInfo);
 
pTypeLib->Release();
pTypeLib = NULL;
}
 
if (pTypeInfo)
{
hrRet = pTypeInfo -> QueryInterface(IID_ITypeInfo2, (void**)&pTypeInfo2);

pTypeInfo->Release();
 
pTypeInfo = NULL;
}
 
if (pTypeInfo2)
{
hrRet = pTypeInfo2 -> GetCustData
(
refguid_custom_attribute,
&varCustomAttributeReceiver
);
 
pTypeInfo2 -> Release();
 
pTypeInfo2 = NULL;
}
}
 
return hrRet;
}
 
GeneralCode snippet : GetTypeLibraryPath() [modified] Pin
Saturday, December 22, 2007 8:49pm by Lim Bio Liong
Here is a short code snippet for determining the full path to a Type Library registered in the system :
 
bool GetTypeLibraryPath
(
GUID libid,
std::string& strTypeLibPathReceiver,
int iVerMajor,
int iVerMinor
)
{
std::string strSubKey;
unsigned char* lpszLibId = NULL;
HKEY hKeyTypeLib = NULL;
long lRetTemp = 0;
bool bRet = true;

UuidToString
(
&libid,
&lpszLibId
);

strSubKey.resize(_MAX_PATH, 0);
sprintf (&(strSubKey[0]), "TypeLib\\{%s}\\%d.%d\\0\\win32", lpszLibId, iVerMajor, iVerMinor);
RpcStringFree(&lpszLibId);

lRetTemp = RegOpenKeyEx
(
HKEY_CLASSES_ROOT,
strSubKey.c_str(),
0,
KEY_READ,
&hKeyTypeLib
);

if (lRetTemp != ERROR_SUCCESS)
{
return false;
}

DWORD dwSize = 0;

lRetTemp = RegQueryValueEx
(
hKeyTypeLib,
NULL,
NULL,
NULL,
NULL,
&dwSize
);

if (lRetTemp != ERROR_SUCCESS)
{
return false;
}

strTypeLibPathReceiver.resize(dwSize, 0);
 
lRetTemp = RegQueryValueEx
(
hKeyTypeLib,
NULL,
NULL,
NULL,
(LPBYTE)(&(strTypeLibPathReceiver[0])),
&dwSize
);

if (hKeyTypeLib)
{
RegCloseKey(hKeyTypeLib);
hKeyTypeLib = NULL;
}

return bRet;
}
modified on Sunday, December 23, 2007 11:21:14 PM

 
GeneralThe IDL "custom" attribute Pin
Saturday, December 22, 2007 12:32pm by Lim Bio Liong
Just remembered a potentially useful IDL attribute : custom.
 
The following is taken from MSDN :
 
custom
The [custom] attribute creates a user-defined attribute.
 
[custom(attribute-id, attribute-value),attribute-list] element-type element-name
 
Use the [custom] attribute to define your own attribute. For example, you might create a string-valued attribute that gives the ProgID for a class.
 
To retrieve a custom attribute value call one of the following:
 
ITypeLib2::GetCustData(rguid, pvarVal)
ITypeInfo2::GetCustData(rguid, pvarVal)
ITypeInfo2::GetFuncCustData(index, rguid, pvarVal)
ITypeInfo2::GetVarCustData(index, rguid, pvarval)
ITypeInfo2::GetParamCustData(indexFunc, indexParam, rguid, pvarVal)
 
GeneralMemory Allocation And Release Pin
Friday, December 21, 2007 10:52pm by Lim Bio Liong
* Note that memory allocated inside a DLL must be released within the same DLL and not in the main application program.
 
* This is because the HEAP used for the memory allocated in the DLL will be different from the HEAP used by the main application.
 
GeneralWhat Is a "Programming Model"? Pin
Tuesday, December 18, 2007 9:42pm by Lim Bio Liong
The following is taken from the Technical White Paper "The Basics of Programming Model Design" by Dave Stearns of Microsoft :
 
You may be confused by the term programming model, but it's really just the correct term for what most people call object model. By programming model I mean the set of interfaces, properties, methods, and events exposed from a component that allows a developer to write programs to manipulate it.
 
... a good programming model doesn't just expose internal structures—it exposes its functionality at a higher level of abstraction so that the customer (the developer) can concentrate on what he or she wants to do and not on how to accomplish a simple task.
 
GeneralConcerning making copies of and deletion of objects stored inside VARIANT parameters. Pin
Thursday, December 6, 2007 7:56pm by Lim Bio Liong
For a long time, I had a hard time trying to figure out the correct way to handle VARIANT parameters.
 
It is especially worrisome when VARIANT parameters hold objects like BSTRs, structures (VT_RECORD) and SAFEARRAYs.
 
Do we need to make copies of these before we insert them into VARIANTs ? And why ? Do we need to destroy these after we have inserted them into VARIANTs ?
 
It turned out that the answer is simple (albeit not trivial). It is analogous to the principles of reference counting for COM interface pointers. Basically :
 
1. In a COM method, when you need to pass an object (via a VARIANT) as an [out] parameter, you need to make a copy of it. This is similar to the fact that we AddRef() an interface pointer returned as an [out] parameter.
 
Hence it follows that once a copy of the object is made and inserted into the VARIANT, you do not delete it. It is the responsibility of the receiving client to delete it in its own time.
 
2. In a COM method, when you receive an object (via a VARIANT) as an [in] parameter, you can use it in whatever way you like but you must not delete it. This is similar to the fact that you do not Release() an interface pointer passed into a COM method as an [in] parameter.
 
Visual Basic is a very good tool to test whether your code follows COM standards as outlined above. If you write a COM object in VC++, and you have objects passed as [in] and [out] parameters, write a VB client code to test for robustness.
 
GeneralThe use of the importlib keyword in an IDL file. Pin
Thursday, May 17, 2007 4:12am by Lim Bio Liong

When authoring an IDL file, if you wish to refer to some entity defined inside some type library, you must use the importlib statement in the IDL file.
 
Note the following advise from MSDN :
 
In most cases you should use the MIDL [import] directive to reference definitions from another .IDL file in your .IDL file. This method provides your type library with all the information from the original file, whereas [importlib] only brings in the contents of the type library.
 
Note my comment below :
If the IDL file which you wish to import contains the definition of some entity which is defined OUTSIDE the library statement, e.g. :
 
MyLibrary.IDL :
 
[
object,
uuid(...),
dual,
helpstring("ISomeInterface Interface"),
pointer_default(unique)
]
interface ISomeInterface : IDispatch
{
[id(1), helpstring("method TestMethod01")] HRESULT TestMethod01();
};
 
[
uuid(...),
version(1.0),
helpstring("MyLibrary 1.0 Type Library")
]
library MYLIBRARYLib
{
...
...
...
};
 
Then ISomeInterface will be accessible via an import statement in your current IDL file :
 
import "MyLibrary.IDL";
 
However, if ISomeInterface was defined INSIDE the library statement, e.g. :
 
MyLibrary.IDL :
[
uuid(...),
version(1.0),
helpstring("MyLibrary 1.0 Type Library")
]
library MYLIBRARYLib
{
[
object,
uuid(...),
dual,
helpstring("ISomeInterface Interface"),
pointer_default(unique)
]
interface ISomeInterface : IDispatch
{
[id(1), helpstring("method TestMethod01")] HRESULT TestMethod01();
};
 
...
...
...
};
 
then ISomeInterface can only be accessed via importlib("MyLibrary.tlb").
 

GeneralRe: The use of the importlib keyword in an IDL file. PinmemberLim Bio Liong30-Jun-07 21:46 
 
GeneralPossible For VB to use MTA objects Pin
Monday, August 14, 2006 11:44pm by Lim Bio Liong
It seems possible for Visual Basic apps to use MTA COM objects.