|
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/
|
|
|
|
|
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).
|
|
|
|
|
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
|
|
|
|
|
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).
|
|
|
|
|
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
|
|
|
|
|
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).
|
|
|
|
|
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.
|
|
|
|
|
Note that the order of registration can be important :
1. In Visual C++ 6.0, if an ATL singleton COM object housed within an EXE server creates another COM object which is housed in the same EXE server, then there would be a problem if the contained object is registered after the singleton object.
2. This is because the singleton object will be created inmediately upon registration. If the registration of the contained object occurs after the singleton, then the singleton object creation will not be creatable and will block until the contained object has been registered, thus causing a deadlock.
- Bio.
|
|
|
|
|
TCHAR szModuleFilePath[_MAX_PATH];
GetModuleFileName
(
_Module.GetModuleInstance(),
szModuleFilePath,
sizeof(szModuleFilePath)
);
where _Module is the CComModule of the COM DLL :
CComModule _Module;
|
|
|
|
|
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.
|
|
|
|
|
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;
}
|
|
|
|
|
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
|
|
|
|
|
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)
|
|
|
|
|
* 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.
|
|
|
|
|
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.
|
|
|
|
|
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.
|
|
|
|
|
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").
|
|
|
|
|
Note also the following :
Any entities defined outside the "library" block will cause code to be created by the MIDL compiler that will be used to generate both the proxy stubs and header files for marshaling code.
However, such entities will not be automatically included in a type library generated from the IDL file.
However, if any such entities was referenced inside the "library" block, then it will be included in the type library.
|
|
|
|
|
It seems possible for Visual Basic apps to use MTA COM objects.
|
|
|
|
|
The extern "C" declaration is used to tell the C++ compiler not to mangle the symbol which is being declared.
When the C++ compiler encounters a function name like "DisplayCurrentThreadId()" which has not been declared as extern "C", it will emit a symbol for it which will look like the following :
?DisplayCurrentThreadId@@YAXXZ
The actual symbol produced depends on the compiler used (the above was produced by VC++ 6.0). This symbol will be used in the resulting OBJ file for linking purposes.
C++ has a variety of reasons for symbol name mangling, of course. But the reason for the mangling of function names (both class functions and global ones) is to enable function name overloading (i.e. using the same function name with different parameter types).
If extern "C" was used to declare the function, the symbol produced for it could be:
_DisplayCurrentThreadId
This depends on the compiler used. But the bottom line is that no function name overloading will be allowed (since the C language does not support this, hence extern "C").
|
|
|
|
|
i am glad that i can learn about the real functionality of using exern"C" which i used to use without truly understanding-- just use it like like other people do.
|
|
|
|
|
1. A Proxy/Stub DLL is always required under one of the following situations :
1.1 Your COM object resides in an EXE Server.
Further note the following :
1.1.1 This is where the use of marshalling is most natural.
1.1.2 In order for your client (a separate EXE application) to use your COM object, marshalling is required.
1.2 Your COM object resides in a DLL Server.
Further note the following :
1.2.1 A COM object will always be used by a thread in a client application.
1.2.2 A client thread which uses COM will always reside in an apartment.
1.2.3 A COM object will also always reside in an apartment.
1.2.3 If the apartment of a client thread which uses a COM object is not the same the apartment of the COM object, marshalling will be required.
To put things simply, where a COM object is accessed across threads (be it threads of a single process or threads across processes), marshalling may be involved.
2. Even when you are using OLE-automation-compatible types, marshalling will be involved.
2.1 In this case, the standard COM marshaller is used. However, note carefully :
2.1.1 That this is provided that your interface has been assigned the [dual] or the [oleautomation] attribute.
2.1.2 If not, your interface cannot be marshaled using the standard COM marshaler.
2.2 The standard COM marshaler is oleaut32.dll (the standard Proxy/Stub DLL for Ole Automation).
2.2 In this case, you do not have to build any Proxy/Stub DLL in order for your clients to use your interface.
3. If your interface uses non-OLE-automation-compatible types as parameters, or that it uses OLE-automation-compatible parameter types but that it is not marked with the [dual] or [oleautomation] attributes, you would definitely need a Proxy/Stub DLL.
3.1 Note that your Proxy/Stub DLL is meant to be used for interfaces and not implementations.
3.2 For example, if you have an interface IAnInterface, say. And you have two implementations of IAnInterface in two separate DLLs, the two implementations will use the same Proxy/Stub DLL.
3.3 Every interface that is ever defined in a Windows OS will have an entry in the registry (under the HKEY_CLASSES_ROOT\Interface Key). They are registered under their GUIDs.
3.4 You will notice that many of the interface entries have a "ProxyStubClsid32" subkey.
3.5 This subkey contains a GUID which identifies the Proxy/Stub DLL associated with this Interface.
4. If you use the MIDL compiler, the source codes for building a Proxy/Stub DLL for your interface can be generated for you.
4.1 The followig files, generated by the MIDL compiler, are the raw materials for your Proxy/Stub :
- *_i.c
- *_p.c
- dlldata.c
where * denotes the name of your COM project.
The *_i.c file contains the actual definitions of the IIDs and CLSIDs defined in your COM project IDL file.
The *_p.c file contains proxy/stub data specific to the interfaces defined in your IDL file.
The dlldata.c file contains macros and code that help to generate basic COM DLL requirements (e.g. DllCanUnloadNow(), DllGetClassObject(), etc).
4.2 Start Visual C++ and create a basic Win32 DLL (non-MFC and non-ATL) project.
4.3 Add the *_i.c, *_p.c and dlldata.c files into this project.
4.4 Create your own .DEF file which should contain the following definitions :
LIBRARY <YOUR MODULE NAME>
EXPORTS
DllCanUnloadNow PRIVATE
DllGetClassObject PRIVATE
DllRegisterServer PRIVATE
DllUnregisterServer PRIVATE
GetProxyDllInfo
4.5 You will need to supply the following pre-processor definitions to your project :
- REGISTER_PROXY_DLL
- either _WIN32_WINNT=0x400
- or _WIN32_WINNT=0x500 (if any interfaces has async_uuid attribute)
The REGISTER_PROXY_DLL definition will ensure that two of the basic COM DLL required exported functions DllRegisterServer() and DllUnregisterServer() are generated within dlldata.c.
The "_WIN32_WINNT=0x400" or "_WIN32_WINNT=0x500" is required otherwise you will face several compilation errors.
If any interface has been attributed with "async_uuid" then you will also need to use the correct-versioned "rpcproxy.h" file. If you are not using the correct-versioned one, the compiler will complain of this. The latest MS SDK include directory usually will contain the latest version of "rpcproxy.h". To use this, set $(MSSDK)\include in your "Additional include directories" path (under project settings).
4.6 Your project will also need to link to rpcrt4.lib.
4.7 After building a successful Proxy/Stub DLL project, you must remember to register this DLL.
<div class="ForumMod">modified on Thursday, January 29, 2009 12:31 AM</div>
|
|
|
|
|
2007 May 3rd
-------------
More notes :
1. Note that it is the Proxy/Stub DLL which generates the appropriate information for a non-OLE-automation-compatible (i.e. custom) interface. This includes the GUID and name of the interface, the number of methods it exposes, etc.
2. When the Proxy/Stub DLL is registered, it officially writes these Interface information in the registry (inside the custom interface's entry under the HKEY_CLASSES_ROOT\Interface registry key). The DLL also registers the GUID of the Proxy/Stub DLL (i.e. itself) associated with each custom interface.
3. In order to enable the Proxy/Stub DLL to contain such information, the Interface as defined in the original IDL must be declared outside the "library" block.
4. The best practice is to define the interface outside the library block and then reference that interface from inside the library block, as shown in this example:
//file: AllKnown.idl
[
object, uuid(. . .), <other interface="" attributes="">
]
interface IKnown : IUnknown
{
import "unknwn.idl";
<declarations,>
};
[
<library attributes="">]
library KnownLibrary
{
//reference interface IKnown:
interface IKnown;
//or create a new class:
[
<coclass attributes="">
]
coclass KnowMore
{
interface IKnown;
};
};
[example above taken from MSDN]
5. Note that the original Type Library whose compile-time IDL-file being the one in which the custom interface was originally defined, would not be able to generate the information mentioned in point 1 above for the custom interface. This is because it has no knowledge of the GUID of the Proxy/Stub which will be associated with the custom interface.
6. Note also that for custom interfaces, the type library is not needed. In fact, type libraries are not relevant for them to be used by a client.
modified on Friday, June 20, 2008 6:01 AM
|
|
|
|
|
2008 June 20th
---------------
More Notes :
1. I noticed that if a proxy/stub DLL has been generated for an interface that is oleautomation compatible (i.e. dual or oleautomation attributes have been assigned to the interface), then the proxy/stub DLL will be used even if the Universal Marshaler (i.e. OLEAUT32.DLL) could have been used (assuming of course that the interface has been defined inside a Type Library and that the Type Library has been registered).
2. However, if that proxy/stub DLL is not registered, the Type Library will be used.
3. Note however, that re-registration and/or recompilation of the Type Library and COM Server code may be necessary to effect a switch around.
|
|
|
|
|
// Here is an example of using mem_fun1 together with bind1st()
// to call a member function of a class which has been instantiated.
// The basic objective is to be able to call the for_each() algorithm
// with the use of a member function of an object as the target function
// to be called on each item in the container.
#include <set>
#include <functional>
#include <algorithm>
#include <iostream>
#include <vector>
#include <string>
using namespace std;
class myClass
{
public :
// Note : parameter pString must not be a pointer
// nor a reference.
int DoSomethingWithString( std::string pString )
{
cout << pString.c_str() << endl;
return 0;
}
public:
void Go()
{
std::set<std::string> myStrings;
myStrings.insert("ABC");
myStrings.insert("DEF");
myStrings.insert("GHI");
std::for_each
(
myStrings.begin(),
myStrings.end(),
// Here is the challenging usage of bind1st and mem_fun1.
std::bind1st(std::mem_fun1(&myClass::DoSomethingWithString), this)
);
}
};
int _tmain(int argc, _TCHAR* argv[])
{
myClass mc;
mc.Go();
return 0;
}
|
|
|
|
|