Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • World
  • Users
  • Groups
Skins
  • Light
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Code Project
  1. Home
  2. General Programming
  3. C#
  4. C# COM object to TLB generation problem

C# COM object to TLB generation problem

Scheduled Pinned Locked Moved C#
comcsharpc++helptutorial
7 Posts 2 Posters 0 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • W Offline
    W Offline
    Werdna
    wrote on last edited by
    #1

    I'm trying to access C# object as COM object in C++ and I generate TLB file and use #import in C++, but I'm having a bit of problems with return types. For example if have method: string GetValue() the generated interface (*.tlh file) gets translated to: virtual HRESULT __stdcall GetValue (/*[out,retval]*/ BSTR * pRetVal ) = 0; the return argument always gets put as last parameter. All the example I've seen (in books or MSDN) do not do that. They always return whatever is returned. Is there maybe some attribute I have to apply? I'm using VS2005, so maybe it's new behaviour there? Thanks.

    H 1 Reply Last reply
    0
    • W Werdna

      I'm trying to access C# object as COM object in C++ and I generate TLB file and use #import in C++, but I'm having a bit of problems with return types. For example if have method: string GetValue() the generated interface (*.tlh file) gets translated to: virtual HRESULT __stdcall GetValue (/*[out,retval]*/ BSTR * pRetVal ) = 0; the return argument always gets put as last parameter. All the example I've seen (in books or MSDN) do not do that. They always return whatever is returned. Is there maybe some attribute I have to apply? I'm using VS2005, so maybe it's new behaviour there? Thanks.

      H Offline
      H Offline
      Heath Stewart
      wrote on last edited by
      #2

      This is the documented and expected behavior. All COM methods (and, by extension, properties) are supposed to return HRESULTs (as yours does), and what COM clients like VB and .NET via COM interop see is return values (although in .NET you can attribute your methods with PreserveSigAttribute - among other ways - to make sure an HRESULT is returned as an Int32 since the return result code might be important and may not be an error (like S_FALSE). I recomend that you read Creating a CCW for COM enabled non .NET applications[^] here on CodeProject, as well as Exposing .NET Framework Components to COM[^]. Make sure that you hard-code GUIDs with the GuidAttribute and never change published interfaces (i.e., interfaces you've released unto the world). Always derive new ones. Also never use auto-generated class interfaces. Define your interfaces explicitly and implement your class interface as the first interface in your implementation list. Also, what examples are you looking at? I've read about every article and API doc in MSDN and COM methods are all return HRESULTs or SCODEs (legacy) - it's part of the COM spec to do so. VB (pre-.NET) interprets [retval]s as return values, just like tlbimp.exe and VS.NET do when importing a typelib. Other late-bound clients like Windows Script (VBScript, JScript) do so as well. This posting is provided "AS IS" with no warranties, and confers no rights. Software Design Engineer Developer Division Sustained Engineering Microsoft [My Articles]

      W 1 Reply Last reply
      0
      • H Heath Stewart

        This is the documented and expected behavior. All COM methods (and, by extension, properties) are supposed to return HRESULTs (as yours does), and what COM clients like VB and .NET via COM interop see is return values (although in .NET you can attribute your methods with PreserveSigAttribute - among other ways - to make sure an HRESULT is returned as an Int32 since the return result code might be important and may not be an error (like S_FALSE). I recomend that you read Creating a CCW for COM enabled non .NET applications[^] here on CodeProject, as well as Exposing .NET Framework Components to COM[^]. Make sure that you hard-code GUIDs with the GuidAttribute and never change published interfaces (i.e., interfaces you've released unto the world). Always derive new ones. Also never use auto-generated class interfaces. Define your interfaces explicitly and implement your class interface as the first interface in your implementation list. Also, what examples are you looking at? I've read about every article and API doc in MSDN and COM methods are all return HRESULTs or SCODEs (legacy) - it's part of the COM spec to do so. VB (pre-.NET) interprets [retval]s as return values, just like tlbimp.exe and VS.NET do when importing a typelib. Other late-bound clients like Windows Script (VBScript, JScript) do so as well. This posting is provided "AS IS" with no warranties, and confers no rights. Software Design Engineer Developer Division Sustained Engineering Microsoft [My Articles]

        W Offline
        W Offline
        Werdna
        wrote on last edited by
        #3

        Thanks for response. As usual Heath you provided excelent response. I saw in MSDN example (COM Interop Part 2: C# Server Tutorial) at http://msdn.microsoft.com/library/default.asp?url=/library/en-us/csref/html/vcwlkCOMInteropPart2CServerTutorial.asp?frame=true[^] where they have public int PrintHi(string name) method and call it with only one parameter (string) and use return int value.

        H 1 Reply Last reply
        0
        • W Werdna

          Thanks for response. As usual Heath you provided excelent response. I saw in MSDN example (COM Interop Part 2: C# Server Tutorial) at http://msdn.microsoft.com/library/default.asp?url=/library/en-us/csref/html/vcwlkCOMInteropPart2CServerTutorial.asp?frame=true[^] where they have public int PrintHi(string name) method and call it with only one parameter (string) and use return int value.

          H Offline
          H Offline
          Heath Stewart
          wrote on last edited by
          #4

          You're actually supposed to use the PreserveSigAttribute when you do this. Some times the samples are incorrect. Try compiling the following:

          using System;
          using System.Runtime.InteropServices;
           
          [assembly: ComVisible(true)]
          [assembly: Guid("519a4822-f224-47fa-bdc7-8037829365dc")]
           
          [InterfaceType(ComInterfaceType.InterfaceIsDual)]
          [Guid("f9b77b61-b4f0-4be4-9c53-ded9592e90c6")]
          public interface ITest
          {
          [DispId(0)]
          int Foo(string value);
          }
           
          [ClassInterface(ClassInterfaceType.None)]
          [Guid("ae5e53c8-54d6-4667-9396-582f5c8611bf")]
          public class Test
          {
          public int Foo(string value)
          {
          return value.Length;
          }
          }

          Compile using:

          csc.exe /t:library test.cs

          Next, register the library and generate a typelib:

          regasm.exe /tlb test.dll

          Finally, view the typelib:

          oleview.exe test.tlb

          You'll see the following:

          // Generated .IDL file (by the OLE/COM Object Viewer)
          //
          // typelib filename: test.tlb
           
          [
          uuid(519A4822-F224-47FA-BDC7-8037829365DC),
          version(1.0),
          custom(90883F05-3D28-11D2-8F17-00A0C9A6186D, Test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null)
           
          ]
          library Test
          {
          // TLib : // TLib : mscorlib.dll : {BED7F4EA-1A96-11D2-8F08-00A0C9A6186D}
          importlib("mscorlib.tlb");
          // TLib : OLE Automation : {00020430-0000-0000-C000-000000000046}
          importlib("stdole2.tlb");
           
          // Forward declare all types defined in this typelib
          interface ITest;
           
          [
          odl,
          uuid(F9B77B61-B4F0-4BE4-9C53-DED9592E90C6),
          version(1.0),
          dual,
          oleautomation,
          custom(0F21F359-AB84-41E8-9A78-36D110E6D2F9, ITest)
           
          ]
          interface ITest : IDispatch {
          [id(00000000), propget,
          custom(54FC8F55-38DE-4703-9C4E-250351302B1C, 1)]
          HRESULT Foo(
          [in] BSTR value,
          [out, retval] long* pRetVal);
          };
           
          [
          uuid(AE5E53C8-54D6-4667-9396-582F5C8611BF),
          version(1.0),
          custom(0F21F359-AB84-41E8-9A78-36D110E6D2F9, Test)
          ]
          coclass Test {
          [default] interface _Object;
          };
          };

          If you used PreserveSigAttribute, then it would simply be HRESULT Foo(BSTR value). This posting is provided "AS IS" with no warranties, and confers no rights. Software Design Engineer Developer Division Sustained Engineering Microsoft

          W 1 Reply Last reply
          0
          • H Heath Stewart

            You're actually supposed to use the PreserveSigAttribute when you do this. Some times the samples are incorrect. Try compiling the following:

            using System;
            using System.Runtime.InteropServices;
             
            [assembly: ComVisible(true)]
            [assembly: Guid("519a4822-f224-47fa-bdc7-8037829365dc")]
             
            [InterfaceType(ComInterfaceType.InterfaceIsDual)]
            [Guid("f9b77b61-b4f0-4be4-9c53-ded9592e90c6")]
            public interface ITest
            {
            [DispId(0)]
            int Foo(string value);
            }
             
            [ClassInterface(ClassInterfaceType.None)]
            [Guid("ae5e53c8-54d6-4667-9396-582f5c8611bf")]
            public class Test
            {
            public int Foo(string value)
            {
            return value.Length;
            }
            }

            Compile using:

            csc.exe /t:library test.cs

            Next, register the library and generate a typelib:

            regasm.exe /tlb test.dll

            Finally, view the typelib:

            oleview.exe test.tlb

            You'll see the following:

            // Generated .IDL file (by the OLE/COM Object Viewer)
            //
            // typelib filename: test.tlb
             
            [
            uuid(519A4822-F224-47FA-BDC7-8037829365DC),
            version(1.0),
            custom(90883F05-3D28-11D2-8F17-00A0C9A6186D, Test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null)
             
            ]
            library Test
            {
            // TLib : // TLib : mscorlib.dll : {BED7F4EA-1A96-11D2-8F08-00A0C9A6186D}
            importlib("mscorlib.tlb");
            // TLib : OLE Automation : {00020430-0000-0000-C000-000000000046}
            importlib("stdole2.tlb");
             
            // Forward declare all types defined in this typelib
            interface ITest;
             
            [
            odl,
            uuid(F9B77B61-B4F0-4BE4-9C53-DED9592E90C6),
            version(1.0),
            dual,
            oleautomation,
            custom(0F21F359-AB84-41E8-9A78-36D110E6D2F9, ITest)
             
            ]
            interface ITest : IDispatch {
            [id(00000000), propget,
            custom(54FC8F55-38DE-4703-9C4E-250351302B1C, 1)]
            HRESULT Foo(
            [in] BSTR value,
            [out, retval] long* pRetVal);
            };
             
            [
            uuid(AE5E53C8-54D6-4667-9396-582F5C8611BF),
            version(1.0),
            custom(0F21F359-AB84-41E8-9A78-36D110E6D2F9, Test)
            ]
            coclass Test {
            [default] interface _Object;
            };
            };

            If you used PreserveSigAttribute, then it would simply be HRESULT Foo(BSTR value). This posting is provided "AS IS" with no warranties, and confers no rights. Software Design Engineer Developer Division Sustained Engineering Microsoft

            W Offline
            W Offline
            Werdna
            wrote on last edited by
            #5

            Also, while we're on the subject. How do strings returned from .net work? If I have a method string getSomething() I have to call it as: HRESULT getSomething(BSTR* result); It seems I have to allocate the string for the result. I'm doing: CComBSTR result(20); and call it: getSomething(&result.m_str); what happens if I return string longer than 20 characters? Does CLR allocate more chars? I tried looking in MSDN, but I couldn't find any information. It seems all examples I can find only deal with ints or doubles.

            H 1 Reply Last reply
            0
            • W Werdna

              Also, while we're on the subject. How do strings returned from .net work? If I have a method string getSomething() I have to call it as: HRESULT getSomething(BSTR* result); It seems I have to allocate the string for the result. I'm doing: CComBSTR result(20); and call it: getSomething(&result.m_str); what happens if I return string longer than 20 characters? Does CLR allocate more chars? I tried looking in MSDN, but I couldn't find any information. It seems all examples I can find only deal with ints or doubles.

              H Offline
              H Offline
              Heath Stewart
              wrote on last edited by
              #6

              It's an [out, retval]. Most often, you don't allocate these as the caller. Merely declare a variable and pass the address of the variable. Also, if you read the documentation for the CComBSTR class, you'll see the following remarks:

              Memory Leak Issues

              Passing the address of an initialized CComBSTR to a function as an [out] parameter causes a memory leak. In the example below, the string allocated to hold the string "Initialized" is leaked when the function OutString replaces the string. CComBSTR bstrLeak(L"Initialized"); HRESULT hr = OutString(&bstrLeak); To avoid the leak, call the Empty method on existing CComBSTR objects before passing the address as an [out] parameter. Note that the same code would not cause a leak if the function's parameter was [in, out].

              So, as you see - don't initialize it. You also don't need to mess with m_str. Since the CComBSTR contains only one member - m_str - passing the address of the CComBSTR instance is passing the address of the m_str (because of the way the class/struct is aligned). This posting is provided "AS IS" with no warranties, and confers no rights. Software Design Engineer Developer Division Sustained Engineering Microsoft [My Articles]

              W 1 Reply Last reply
              0
              • H Heath Stewart

                It's an [out, retval]. Most often, you don't allocate these as the caller. Merely declare a variable and pass the address of the variable. Also, if you read the documentation for the CComBSTR class, you'll see the following remarks:

                Memory Leak Issues

                Passing the address of an initialized CComBSTR to a function as an [out] parameter causes a memory leak. In the example below, the string allocated to hold the string "Initialized" is leaked when the function OutString replaces the string. CComBSTR bstrLeak(L"Initialized"); HRESULT hr = OutString(&bstrLeak); To avoid the leak, call the Empty method on existing CComBSTR objects before passing the address as an [out] parameter. Note that the same code would not cause a leak if the function's parameter was [in, out].

                So, as you see - don't initialize it. You also don't need to mess with m_str. Since the CComBSTR contains only one member - m_str - passing the address of the CComBSTR instance is passing the address of the m_str (because of the way the class/struct is aligned). This posting is provided "AS IS" with no warranties, and confers no rights. Software Design Engineer Developer Division Sustained Engineering Microsoft [My Articles]

                W Offline
                W Offline
                Werdna
                wrote on last edited by
                #7

                I see. thanks. Do you know if there are any restrictions on types you can use within .net COM client? I have my C# class do: new SqlConnection(connectionstring) and it throws access violation exception: Error Msg: System.TypeInitializationException: The type initializer for 'System. Data.SqlClient.SqlConnectionFactory' threw an exception. ---> System.TypeInitial izationException: The type initializer for 'System.Data.SqlClient.SqlPerformance Counters' threw an exception. ---> System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory h as been corrupted. at System.Data.ProviderBase.DbConnectionPoolCounters.GetInstanceName() at System.Data.ProviderBase.DbConnectionPoolCounters.InitCounters(String cate goryName, String categoryHelp) at System.Data.SqlClient.SqlPerformanceCounters..ctor() at System.Data.SqlClient.SqlPerformanceCounters..cctor() --- End of inner exception stack trace --- at System.Data.SqlClient.SqlConnectionFactory..ctor() at System.Data.SqlClient.SqlConnectionFactory..cctor() --- End of inner exception stack trace --- at System.Data.SqlClient.SqlConnection..ctor() at System.Data.SqlClient.SqlConnection..ctor(String connectionString) When I use my client thru C#, it's all fine. My C++ COM clinet is very simple for the test case, it just creates COM object and calls one method with no parameters and no return values (I did that to see if it was some memory problem in C++ client).

                1 Reply Last reply
                0
                Reply
                • Reply as topic
                Log in to reply
                • Oldest to Newest
                • Newest to Oldest
                • Most Votes


                • Login

                • Don't have an account? Register

                • Login or register to search.
                • First post
                  Last post
                0
                • Categories
                • Recent
                • Tags
                • Popular
                • World
                • Users
                • Groups