Can't add *.ltb into C#

Hi all,
I am having a problem about COM programming. Please give idea to fix it.
I created successfully a COM object by C++ (C Win32).
I tried accessing this COM object by C Win32, the result is successful.
However, When accessing it by C#, I could not add *.ltb file or *.Dll file into C# project.

Please help me.
Thanks very much!
Error message?
You know you need to go to the Project Properties and the COM tab and do an Add Reference to the correct COM library so the COM wrappers are generated by .NET?
Thanks for your replies.

To webJose:
. When I add *.dll file, the message content is "A reference to 'D:\Project\Confirm\COM_Dll\COM_Dll\Debug\COM_Dll.dll' could not be added. Please make sure that the file is accessible, and that it is a valid assembly or COM component."

. And *.tlb file, the message content is "A reference to 'D:\Project\Confirm\COM_Dll\COM_Dll\Debug\COM_Dll.tlb' could not be added. Could not load the type library. Error loading type library/DLL. (Exception from HRESULT: 0x80029C4A (TYPE_E_CANTLOADLIBRARY))"

To freddie1: My operation is correct.

PS: I read some documents about COM, it said that COM does not depend on the programming languages. So, I would like to write a COM object in C++(C Win32), and use it in .Net.

Please help me.

The my following *.idl file:
import "oaidl.idl";
import "ocidl.idl";
[
    uuid(B76F37A9-B7CA-477d-B80D-C9E74475A55B),

    version(1.0),

    helpstring("Spaceship 1.0 Type Library")

]
library SpaceshipLIB

{
	importlib("stdole32.tlb");

	[

	  object,

	  uuid(15F825A5-2DF2-4164-AF45-DBB55BD48552),

	  helpstring("IMotion Interface")

	]
	interface IMotion : IUnknown

	{

	  [helpstring("Fly method")]
	  HRESULT Fly();

	   [helpstring("Get position")]
	   HRESULT GetPosition([out] LONG * aa);

	};


	[

	  object,

	  uuid(EEFBA8F6-EB7A-4891-BFC6-E7D8FA0F2AC9),

	  helpstring("IVisual Interface")

	]
	interface IVisual : IUnknown

	{

	  [helpstring("Display method")]
	  HRESULT Display();

	};


	[
	  uuid(AC327B2F-B0DC-4e2a-B0EF-E139265C2BAC),

	  helpstring("CoSpaceship Class")

	] 
	coclass CoSpaceship
	{
		[default] interface IUnknown;
	};


	[
	  uuid(DC4FBC40-D353-41a7-9E26-E2C20FD11281),

	  helpstring("CoSpaceshipFactory Class")

	]
	coclass CoSpaceshipFactory
	{
		[default] interface IClassFactory;
	};
};


Last edited on
Is your library correctly registered? When you ran RegSvr32 on your dll did you check the typelib key under HKEY_CLASSES_ROOT to see if everything is OK?

Are you using a stand alone *.tlb file or embedding it in the dll? Can you navigate to to either the *.tlb or dll using OleView to see if everything looks OK?

These are just some things to check.


PS: I read some documents about COM, it said that COM does not depend on the programming languages. So, I would like to write a COM object in C++(C Win32), and use it in .Net.


That's all correct. Its one of the reasons I use COM a lot. The particular languages I mess around with a lot are C, C++, PowerBASIC, Visual Basic 6, and VB.NET. I'm pretty weak in C# myself.

I believe you said you can load your COM object in C++ and get it to work. That's good. However, with .NET you'll have to get the type library working, because I believe .NET with COM Interop generates wrappers for its use, and it needs that typelib to be set up correct.
Last edited on
To freddie1.
Thanks so much.

1. About registering into system, It is correct in Windows XP (incorrect in Windows 7 64bit). However, My application also runs successfully these environments (if I use C win32).
therefore, I think my library is registered correctly.

2. Are you using a stand alone *.tlb file or embedding it in the dll?
-> I do not understand this answer.

3. I tried using OleView tool to check COM object. But it fail.
Error message when loading *.dll:
LoadTypeLib( D:\Project\Confirm\COM_Dll\COM_Dll\Debug\COM_Dll.dll ) failed.
Error loading type library/DLL.
 TYPE_E_CANTLOADLIBRARY ($80029C4A)


For registering on Win7 64 bit you need to be running as administrator to get it to register. Usually I create a batch file to open a command prompt window in whatever directory I'm working with the code and have the dll, and when I run Regsvr32 I start the shortcut by right clicking on it and choosing 'Run As Administrator'. There are other ways to do it though.

In terms of your *.tlb problem, when you ran midl.exe a *.tlb file would have been created in your working directory. That is your type lib. They can be used as stand alone files, but I personally never do that. I think its neater to use the resource compiler to compile them right into the dll. In fact, I'm not even sure environments like .NET can work with stand alone *.tlb files. I think so, but I wouldn't bet my life on it. Like I said, I always compile them right into the dll. Upon doing that though, your code in DllRegisterServer() has to call LoadTypeLibEx() to place the type lib in the registry under

HKEY_CLASSES_ROOT\typelib\{YOUR KEY}

If this isn't done you'll still be able to load the dll into your process with such low level languages as C, C++, PowerBASIC, etc., but likely not .NET, because I believe .NET is relying on the info in the typelib to generate the interface declares it needs.

I actually have piles of tutorial information on all this here...

http://www.jose.it-berater.org/smfforum/index.php?board=362.0

How have you created your COM dll, low level & by hand, or using ATL?

However, My application also runs successfully these environments (if I use C win32).


Yes, it will. But your Win32 C or C++ code is obtaining interface declarations likely from code auto-generated by midl, i.e., your *.c and *.h files, and .NET doesn't have access to those. That is why they need the compiled *.tlb file as either a stand alone file, or compiled into the dll as a resource.
FYI, there's a difference between defining an interface inside the library definition than outside the library definition. the IDL file you show here defines the interfaces inside the library definition. If I remember correctly, the IDL compiler will not generate proxy code for those interfaces, which could impact .Net's ability to use the COM objects that implement the interfaces, as they need to be marshaled across processes or threads of different apartments.

I also see that you define your class factory in the TLB. This is usually never done. COM's mechanism of obtaining the class factory does not rely in the standard method of retrieving a CoClass of any other type of object. Instead, COM DLL's provide an entry point called DllGetClassObject() (http://msdn.microsoft.com/en-us/library/windows/apps/ms680760(v=vs.85).aspx ) that is in charge of providing pointers to the class factory. Since it is not customary to add the coclass of the class factory, especially because there is no such thing as a class factory for the class factory, this could be messing up .Net's referencing algorithm.

Finally, you define IMotion and IVisual interfaces, but you just give the IUnknown interface to CoSpaceship. Since .Net wrappers usually isolate this interface so the object is automatically handled by .Net's Garbage Collector, this leaves the CoSpaceship class with absolutely no public methods, which could be problematic too.

So yes, the above is important your correct/verify, but I am honest: If OleView cannot load the TLB, there could be something else going on. After all, OleView is just trying to lad the TLB file, not trying to generate .Net wrapper classes. So after correcting/verifying the above, I wouldn't have my hopes too high as it could still fail. See if you can get a more descriptive error message. See if you can get extended information out of OleView, or try to load the TLB yourself and see if you can get extend information from IErrorInfo (http://msdn.microsoft.com/en-us/library/ms723041(VS.85).aspx ).

To load a type library, you can use LoadTypeLib(): http://msdn.microsoft.com/en-us/library/ms221027(VS.85).aspx .
I hadn't looked really closely at your idl code, but webJose is right. I tend to put my library clause at the bottom, with all the interface definitions above it. Here is one for a grid control I just made...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
// fhGrid8.idl
import "unknwn.idl";

[object, uuid(20000000-0000-0000-0000-000000000085), oleautomation] interface IGrid : IUnknown
{
 HRESULT CreateGrid
 (
  [in] int  hParent, 
  [in] BSTR strSetup, 
  [in] int  x, 
  [in] int  y, 
  [in] int  cx, 
  [in] int  cy, 
  [in] int  iRows, 
  [in] int  iCols, 
  [in] int  iRowHt, 
  [in] int  iSelectionBackColor,
  [in] int  iSelectionTextColor,
  [in] BSTR strFontName, 
  [in] int  iFontSize, 
  [in] int  iFontWeight
 );
 HRESULT SetRowCount([in] int iRowCount, [in] int blnForce);
 HRESULT SetData([in] int iRow, [in] int iCol, [in] BSTR strData);
 HRESULT GetData([in] int iRow, [in] int iCol, [out, retval] BSTR* strData);
 HRESULT FlushData();
 HRESULT Refresh();
 HRESULT GetCtrlId([out, retval] int* iCtrlId);
 HRESULT GethGrid([out, retval] int* hWnd);
 HRESULT GethComboBox([in] int iCol, [out, retval] int* hCombo);
 HRESULT SetCellAttributes([in] int iRow, [in] int iCol, [in] int iBackColor, [in] int iTextColor);
 HRESULT DeleteRow([in] int iRow);
}; 

[object, uuid(20000000-0000-0000-0000-000000000086), oleautomation] interface IGridEvents : IUnknown
{
 HRESULT Grid_OnKeyPress([in] int iKeyCode, [in] int iKeyData, [in] int iRow, [in] int iCol, [out] int* blnCancel);
 HRESULT Grid_OnKeyDown([in] int KeyCode, [in] int iKeyData, [in] int iRow, [in] int iCol, [out] int* blnCancel);
 HRESULT Grid_OnLButtonDown([in] int iCellRow, [in] int iGridRow, [in] int iCol);
 HRESULT Grid_OnLButtonDblClk([in] int iCellRow, [in] int iGridRow, [in] int iCol);
 HRESULT Grid_OnPaste([in] int iCellRow, [in] int iGridRow, [in] int iCol);
 HRESULT Grid_OnRowSelection([in] int iRow, [in] int iAction);
 HRESULT Grid_OnDelete([in] int iRow);
};

[uuid(20000000-0000-0000-0000-000000000087), helpstring("FHGrid8 TypeLib"), version(1.0)] library FHGrid8Library
{
 importlib("stdole32.tlb");
 interface IGrid;
 interface IGridEvents;
 [uuid(20000000-0000-0000-0000-000000000084)] coclass FHGrid8
 {
           interface IGrid;
  [source] interface IGridEvents;
 }
};


I might add that COM, though really neat, is one of the hardest things to learn. I don't think you can pick it up by just browsing around the web. You need to get yourself a number of good books, and work through the exercises.
Last edited on
Thanks so much.
I really have more information about COM from your answer. Now, I will re-read COM information.
Because I am a beginner. Therefore I have many problem when studying COM.

I read COM at http://progtutorials.tripod.com/COM.htm (However I write application by C Win32 (Not ATL) ) and Essential COM (Not complete)
If all of you know more good books, Please introduce to me.

Thanks
Last edited on
To webJose
Thanks for your comment.

1. About defining class factory in the TLB.
Because, When COM client call CoGetClassObject(), We must inpput GUI of class factory.
So, I have to define it in TLB.
Please give me more your idea.

2. I defined IUnknown interface to CoSpaceship
Because I design COM object as below
  Class CSpaceShip : public IUnknown
     {
           private: ......
           Class CVisaul : public IVisual
           {
                .......
           } pVisual;

           Class CMotion : public IMotion
           {
                .......
           } pMotion;
            
          friend CVisaul ;
          friend CMotion ;
     }

Last edited on
To webJose, freddie1

May I give you my source code so that you can help to comment it?

Thanks

1. About defining class factory in the TLB.
Because, When COM client call CoGetClassObject(), We must inpput GUI of class factory.
So, I have to define it in TLB.
Please give me more your idea.


CoGetClassObject() takes a CLSID and an IID and returns a pointer to the class factory of the class. What it does is consult the registry for the path to the dll housing the class and it does a LoadLibrary() on the dll. Then it does a GetProcAddress() on DllGetProcAddress() which is an exported function from the dll. With DllGetProcAddress() it can get a pointer to the class factory so that CoCreateInstance() can be called (it returns the class factory pointer to you). None of this requires anything about IClassFactory to be in the type lib. In fact, nothing about any system defined interfaces need be in a type lib because they are defined in the system. When you include Windows.h or objbase.h the system knows about them, can create pointers to them, etc.

The only thing needing to be defined in type libs are custom interfaces, i.e., those which the system knows nothing about, such as spaceships, rocket ships, warp drives, etc.

Glad to hear you are doing COM in the raw. Bast way to learn I think.

The three books I’ve found most helpful are “Inside COM” by Dale Rogerson, “Inside DCOM” by Guy and Henry Eddon, and “Developer’s Workshop To COM And ATL 3.0 by Andrew W. Troelsen. I have Don Box’s “Essential COM”, but only ever read the first chapter or two from it.

I have a board at this site…

http://www.jose.it-berater.org/smfforum/index.php

where I have lots of COM example code in C, C++, and PowerBASIC, as well as interoperating with it through VB and .NET. Its primarily a COM related site oriented more to PowerBASIC, but I post a lot of C++ stuff on my board. There are other C, C++, and asm coders there. Anyway, when I have a chance I’d take a look at your code and try to get it working. It may take me a bit to get to it though – few days. I believe you can send a personal messages at this forum.
Also, your class spaceship should inherit your custom interfaces - not IUnknown. The interfaces inherit from IUnknown, and thats how QueryInterface(), AddRef(), and Release() get attached as the 1st three members of each interface/VTable.
I see now why you have only added IUnknown to the spaceship, but then your spaceship object is pretty much useless. A .Net wrapper will be able to call zero functionality on it. Or is the spaceship meant to be fully automatic??? Also, if the interfaces are used on private objects only, why the need of adding them to the type library? You only add to the type library the interfaces that are public. Otherwise it is just a waste because no pointers to those can be obtained anyway (they are all private).

Also get rid of the class factory from the type library. It is wrong to include it there.
Maybe think about something like this...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import "unknwn.idl";

[object,uuid(15F825A5-2DF2-4164-AF45-DBB55BD48552),helpstring("IMotion Interface")] interface IMotion : IUnknown
{
  [helpstring("Fly method")]   HRESULT Fly();
  [helpstring("Get position")] HRESULT GetPosition([out] LONG * aa);
};

[object,uuid(EEFBA8F6-EB7A-4891-BFC6-E7D8FA0F2AC9),helpstring("IVisual Interface")] interface IVisual : IUnknown
{
  [helpstring("Display method")]HRESULT Display();
};

[uuid(B76F37A9-B7CA-477d-B80D-C9E74475A55B),version(1.0),helpstring("Spaceship 1.0Type Library")] library SpaceshipLIB
{
 importlib("stdole32.tlb");
 interface iMotion;
 interface IVisual;
 [uuid(AC327B2F-B0DC-4e2a-B0EF-E139265C2BAC),helpstring("CoSpaceship Class")] coclass CoSpaceship
 {
  [default] interface IMotion;
            interface IDisplay;
 }
};
Last edited on
Topic archived. No new replies allowed.