c#:
In C# how do you achieve pass-by-reference property declarations in the Type Library? I am writing a COM Class Library that must mimick an existing library for which the only information is the TypeLib. I'm using Visual Studio .NET 2003. The original library provides simple authentication services, from Access and MS-SQL OLEDB providers. The enhancement I'm creating provides support for ODBC and will be a drop-in replacement. The original library was written in Visual Basic 6, and declares an interface with property setter methods that take pointers as arguments, as this extract shows: dispinterface _DBLink { methods: [id(0x60030006)] VARIANT_BOOL CheckUser([in, out] BSTR* User, [in, out] BSTR* Password); [id(0x68030008), propput] void LoginClassField([in, out] BSTR* rhs); I've created an identical COM Class Library in C#, but the arguments to the property setter methods are passed-by-value, and it isn't possible to use the C# "ref" or "out" modifier on Properties. dispinterface _DBLink { methods: [id(0x60020006)] VARIANT_BOOL CheckUser([in, out] BSTR* User, [in, out] BSTR* Password); [id(0x60020008), propput] void LoginClassField([in] BSTR rhs); [id(0x60020009)] void set_LoginClassField([in, out] BSTR* value); code: public interface _DBLink { bool CheckUser(ref string User, ref string Password); string LoginPassField { set; } void set_LoginClassField(ref string value); // ... rest of code } As you can see, although I can define the Property setters as methods, the TypeLib would then no longer declare them as "propput". Any ideas? TJ.
TJ, Why not use the TLBIMP utility and then reference that from your project? I'm not too sure if the propput attribute in IDL is supported, but if it is, TLBIMP should be able to create the correct managed assembly. Hope this helps. -- - Nicholas Paldino [.NET/C# MVP] - mvp@spam.guard.caspershouse.com [quoted text, click to view] "TJ" <microsoft.support@tjworld.org> wrote in message news:eS935JjZFHA.3220@TK2MSFTNGP14.phx.gbl... > In C# how do you achieve pass-by-reference property declarations in the > Type Library? > > I am writing a COM Class Library that must mimick an existing library for > which the only information is the TypeLib. I'm using Visual Studio .NET > 2003. > > The original library provides simple authentication services, from Access > and MS-SQL OLEDB providers. The enhancement I'm creating provides support > for ODBC and will be a drop-in replacement. > > The original library was written in Visual Basic 6, and declares an > interface with property setter methods that take pointers as arguments, as > this extract shows: > > dispinterface _DBLink { > methods: > [id(0x60030006)] VARIANT_BOOL CheckUser([in, out] BSTR* User, [in, out] > BSTR* Password); > [id(0x68030008), propput] void LoginClassField([in, out] BSTR* rhs); > > I've created an identical COM Class Library in C#, but the arguments to > the property setter methods are passed-by-value, and it isn't possible to > use the C# "ref" or "out" modifier on Properties. > > dispinterface _DBLink { > methods: > [id(0x60020006)] VARIANT_BOOL CheckUser([in, out] BSTR* User, [in, out] > BSTR* Password); > [id(0x60020008), propput] void LoginClassField([in] BSTR rhs); > > [id(0x60020009)] void set_LoginClassField([in, out] BSTR* value); > > code: > public interface _DBLink { > bool CheckUser(ref string User, ref string Password); > string LoginPassField { set; } > > void set_LoginClassField(ref string value); > > // ... rest of code > } > > As you can see, although I can define the Property setters as methods, the > TypeLib would then no longer declare them as "propput". > > Any ideas? > > TJ. >
TJ, I think Nicholas misunderstood your problem. It looks like he thought you were trying to IMPORT a COM dll. If I now understand you correctly, you're trying to EXPORT a .NET class as a COM interface to emulate an older VB6 component you had (i.e. you've re-written this component in C#, but you want it to appear to clients as essentially the same interface, right)? I've had to do similar things in my job recently. I've encountered a situation similar to yours, though not exactly the same. What I had to do is manually create my own IDL and my own TLB and, after calling regasm /tlb, swap out the regasm-generated TLB with my homebrew TLB. This is some complicated brain surgery, but in many cases, it's the only thing you can do. I've written several blog posts about doing this kind of stuff. Sorry for the poor grammar and spelling, these were all written after midnight after a long day. I need to re-write them, but I'm too lazy :) Part 1: (poorly) explaining some of the concepts involved in exporting .NET types to COM: http://blog.chadmyers.com/chad/archive/2004/10/06/166.aspx Part 2: Explaining problems with Tlbexp/TypeLibExporter and how to hack together your own homebrew IDL: http://blog.chadmyers.com/chad/archive/2004/10/07/167.aspx Part 3: Handling ComVisible(true), default, reference-type properties in C#: http://blog.chadmyers.com/chad/archive/2004/10/07/168.aspx Part 3 is what my specific deal-breaker situation was. It's similar to your situation. If you can find no other solution, you may have to follow some of the steps in my Part 2 article to hack together your own IDL. I've encountered a few other problems along the way that required IDL manipulation, but I, sadly, haven't had time to blog about them. I'd be happy to walk you through some of them once you've given everything else a good try and studied up on IDL hacking. I *STRONGLY* urge you to purchase, borrow, or otherwise acquire the book ".NET and COM The Complete Interoperability Guide" by Adam Nathan. This book explains almost everything I did in those 3 articles. You may have to dig around a little, but the information is all there. One other thing, if you've had to deal with optional parameters in your COM interface (i.e. the VB6 had some optional arguments, and now you have to simulate that in the C# interface), check out: http://blog.chadmyers.com/chad/archive/2004/02/10/145.aspx Hope this helps. -Chad [quoted text, click to view] "TJ" <microsoft.support@tjworld.org> wrote in message news:eXGEAyvZFHA.2984@TK2MSFTNGP15.phx.gbl... > Nicholas, thanks for that suggestion, it offers some progress but further > confusion. I have further questions if anyone can help? > > I'd been using Object Viewer stand-alone so it wasn't showing me C# > specific code matching the VB6 COM Type Library. As soon as I added the > external COM's TLB to my C# solution and ran Object Viewer on it, it > showed: > > IDL: [id(0x68030008), propput] void LoginClassField([in, out] BSTR* rhs); > C# prototype: public abstract new System.IntPtr LoginClassField [ set ] > > I didn't know about the IntPtr structure. I've now redefined my Interface > this way: > > public interface _DBLink > { > [DispId(0x68030008)] IntPtr LoginClassField{ set; } > // ... > } > public class DBLink : _DBLink { > public IntPtr LoginClassField { > set { > _LoginClassField = Marshal.PtrToStringBSTR(value); > Marshal.FreeBSTR(value); > } > } > // ... > } > > Now I'm concerned that when viewed in Object Viewer the IDL looks > different in other ways: > > public System.IntPtr LoginClassField [ set ] > > and when used to view the resulting Type Library (from the build) shows: > [id(0x68030008), propput] void LoginClassField([in] long rhs); > > So although the C# property declaration seems to match, the IDL looks > further away than it was. I've now got a long instead of a BSTR* > > I tried to use [MarshalAs(UnmanagedType.LPStr)] as per the documentation, > but as for many things Property-related, the compiler won't accept it. > > [DispId(0x68030008), MarshalAs(UnmanagedType.LPStr)] IntPtr > LoginClassField{ set; } > > Is it possible to manually apply the propputref attribute to a C# method > so I could do something along the lines of this ? > > [ BindAs( BindFlags.PutRefDispProperty ) ] public void > set_LoginClassField(ref string value) > > which, if there is a way to control the Bind, would result in something > like this in the IDL: > > [propputref] void LoginClassField( [in, out] BSTR* value) >
Nicholas, thanks for that suggestion, it offers some progress but further confusion. I have further questions if anyone can help? I'd been using Object Viewer stand-alone so it wasn't showing me C# specific code matching the VB6 COM Type Library. As soon as I added the external COM's TLB to my C# solution and ran Object Viewer on it, it showed: IDL: [id(0x68030008), propput] void LoginClassField([in, out] BSTR* rhs); C# prototype: public abstract new System.IntPtr LoginClassField [ set ] I didn't know about the IntPtr structure. I've now redefined my Interface this way: public interface _DBLink { [DispId(0x68030008)] IntPtr LoginClassField{ set; } // ... } public class DBLink : _DBLink { public IntPtr LoginClassField { set { _LoginClassField = Marshal.PtrToStringBSTR(value); Marshal.FreeBSTR(value); } } // ... } Now I'm concerned that when viewed in Object Viewer the IDL looks different in other ways: public System.IntPtr LoginClassField [ set ] and when used to view the resulting Type Library (from the build) shows: [id(0x68030008), propput] void LoginClassField([in] long rhs); So although the C# property declaration seems to match, the IDL looks further away than it was. I've now got a long instead of a BSTR* I tried to use [MarshalAs(UnmanagedType.LPStr)] as per the documentation, but as for many things Property-related, the compiler won't accept it. [DispId(0x68030008), MarshalAs(UnmanagedType.LPStr)] IntPtr LoginClassField{ set; } Is it possible to manually apply the propputref attribute to a C# method so I could do something along the lines of this ? [ BindAs( BindFlags.PutRefDispProperty ) ] public void set_LoginClassField(ref string value) which, if there is a way to control the Bind, would result in something like this in the IDL: [propputref] void LoginClassField( [in, out] BSTR* value)
Re: Minor problems with midl: Oh yeah, forgot to mention all the nitpicky stuff, but it's not that hard to figure out or work through. But then you said something that confused me... [quoted text, click to view] > HOWEVER, having got this far I've now convinced myself I've taken the > wrong route, since the COM object I'm creating needs to be a drop-in > replacement for an existing COM object, and may be deployed on > systems that don't have the .Net framework installed.
How do you plan on deploying, and running, a .NET/C# component on a system without the .NET Framework installed? Isn't the whole point of this excercise to replace a VB6 component with a C# component? [quoted text, click to view] > Thinking of switching to C++, but it seems that Visual Studio .Net 2003 > won't let me create a classic COM library, only a .net assembly!
123456789112345678921234567893123456789412345678951234567896123456789712 [quoted text, click to view] > I'll be glad when I've got this finished and can go back to Java and > Eclipse IDE! I only started on this C# / Microsoft programming 2 days > ago to provide ODBC functionality to the application that uses DBLink, > so it can share a common user database with a J2EE web application.
Ah, I see the problem. You are very confused. C# and VB.NET and Managed Extensions for C++ are all .NET-framework based. Imagine if the JVM supported multiple languages besides Java. That's what .NET essentially is. You can create unmanaged components from VS.NET 2003, but only in C++ without the /clr compiler switch. C# is a .NET-only language, you cannot create unmanaged code. You can create a .NET component which exposes a COM interface, but you cannot create non-.NET components from VS.NET 2003 if you're not using C++ [sans /clr]. Good luck on your efforts and I'm sorry you have to use Java/Eclipse ;) -c [quoted text, click to view] "TJ" <microsoft.support@tjworld.org> wrote in message news:Oy%233a65ZFHA.1148@tk2msftngp13.phx.gbl... > Thanks Chad, your articles have moved me on somewhat. > > Working out what was wrong with the syntax of the Object Viewer IDL was a > pain, but then I had yet more joy solving "cl.exe not found" and > "oaidl.idl not found" errors from MIDL, along with others. > > Solutions were to add to the environmental variables for the location of > midl.exe, cl.exe (wasn't even in the Visual Studio 2003 .Net folder > structure), and the library .tbl files used by MIDL. > > PATH=%PATH%;C:\Program Files\Microsoft\Visual Studio .NET > 2003\Common7\IDE; > PATH=%PATH%;C:\Program Files\Microsoft\Visual Studio .NET 2003\Vc7\bin; > INCLUDE=C:\Program Files\Microsoft\SDK\include\ > > *Note: I always edit my Microsoft applications install folder to be > "\Program Files\Microsoft\<application>" rather than "\Program > Files\Microsoft <application>" so as to stop the multiplication of > "Microsoft..." folders in the "Program Files" folder. > > In the revised class IDL file express the full path for the importlib() > statement: > > // TLib : // TLib : Common Language Runtime Library : > {BED7F4EA-1A96-11D2-8F08-00A0C9A6186D} > importlib("C:\WINDOWS\Microsoft.NET\Framework\v1.1.4322\mscorlib.tlb"); > > HOWEVER, having got this far I've now convinced myself I've taken the > wrong route, since the COM object I'm creating needs to be a drop-in > replacement for an existing COM object, and may be deployed on systems > that don't have the .Net framework installed. > > What a pain, as the code in the library itself is complete! > > I was investigating [ ComRegisterFunctionAttribute ] & [ > ComUnregisterFunctionAttribute ] thinking I could create a component that > would work in a .Net and classic environment, but I think maybe I > misunderstood the documentation and that my component will only work in > the .Net environment, even when called by a classic COM client. > > Thinking of switching to C++, but it seems that Visual Studio .Net 2003 > won't let me create a classic COM library, only a .net assembly! > Investigating Visual Basic (*spits!) it looks like that still allows the > creation of classic COM libraries - grrr!! > > I'll be glad when I've got this finished and can go back to Java and > Eclipse IDE! I only started on this C# / Microsoft programming 2 days ago > to provide ODBC functionality to the application that uses DBLink, so it > can share a common user database with a J2EE web application. > > TJ. >
Thanks Chad, your articles have moved me on somewhat. Working out what was wrong with the syntax of the Object Viewer IDL was a pain, but then I had yet more joy solving "cl.exe not found" and "oaidl.idl not found" errors from MIDL, along with others. Solutions were to add to the environmental variables for the location of midl.exe, cl.exe (wasn't even in the Visual Studio 2003 .Net folder structure), and the library .tbl files used by MIDL. PATH=%PATH%;C:\Program Files\Microsoft\Visual Studio .NET 2003\Common7\IDE; PATH=%PATH%;C:\Program Files\Microsoft\Visual Studio .NET 2003\Vc7\bin; INCLUDE=C:\Program Files\Microsoft\SDK\include\ *Note: I always edit my Microsoft applications install folder to be "\Program Files\Microsoft\<application>" rather than "\Program Files\Microsoft <application>" so as to stop the multiplication of "Microsoft..." folders in the "Program Files" folder. In the revised class IDL file express the full path for the importlib() statement: // TLib : // TLib : Common Language Runtime Library : {BED7F4EA-1A96-11D2-8F08-00A0C9A6186D} importlib("C:\WINDOWS\Microsoft.NET\Framework\v1.1.4322\mscorlib.tlb"); HOWEVER, having got this far I've now convinced myself I've taken the wrong route, since the COM object I'm creating needs to be a drop-in replacement for an existing COM object, and may be deployed on systems that don't have the .Net framework installed. What a pain, as the code in the library itself is complete! I was investigating [ ComRegisterFunctionAttribute ] & [ ComUnregisterFunctionAttribute ] thinking I could create a component that would work in a .Net and classic environment, but I think maybe I misunderstood the documentation and that my component will only work in the ..Net environment, even when called by a classic COM client. Thinking of switching to C++, but it seems that Visual Studio .Net 2003 won't let me create a classic COM library, only a .net assembly! Investigating Visual Basic (*spits!) it looks like that still allows the creation of classic COM libraries - grrr!! I'll be glad when I've got this finished and can go back to Java and Eclipse IDE! I only started on this C# / Microsoft programming 2 days ago to provide ODBC functionality to the application that uses DBLink, so it can share a common user database with a J2EE web application. TJ.
The primary issue is, the COM DLL I'm "extending" is an optional part of a Windows IRC server (IRCXPro), so for operators that want to use my ODBC version of DBLink I can't dictate that they install the .Net Framework if it isn't already there - a severe case of overkill I feel. Assuming for the moment I continue with the current C# (how nice is it is to see microsoft providing something so close to Java - the reason I was able to get up to speed so quick)... As I understand it, my managed-code replacement DLL can simply replace the existing DLL, but am I correct in thinking the TLB would need to be added to the DLL's resources? (I don't want to confuse users by shipping more than the one file). If thats the case, how is one supposed to add a resource to the DLL? I tried to follow the destructions in the help files but I seem to hit a brick wall no matter what approach I take - where the hell has the lovely ole Resource Workshop gone for doing stuff like string-tables, icons, bitmaps, version info, and raw RC_data? Because the original DLL is set to OLESelfRegister does this have implications for me? Do i need to implement the [ ComRegisterFunctionAttribute ] etc static methods and make entries in the registry of the server the component is installed on? Do I need to create an install package that simply makes the component register itself, or is that handled the first time the DLL is called? Because the finished component will use the same GUIDs as the original, in theory it shouldn't need to register itself because all the settings will be there already, but I just want to be clear. Many thanks for your help on this; It's been an intense learning experience and tiring on the eyes 8-) TJ.
I thought I'd complete this thread with my latest experiences. This article began as an appeal for help, but in the course of writing it I appear to have solved the specific issue so check the article on my blog for details. When loading a VB6 application that delay-loads a database-access DLL written in C# I'm seeing the error dialog -------------------------------- Two different copies of MSCOREE.DLL have been loaded. First copy: <Unknown> Second copy: C:\Windows\Microsoft.NET\Framework\v1.1.4322\mscorwks.dll This is typically caused by having a registered MSCOREE.DLL that is different from one that is statically linked with the application. ------------------------ It is a 3rd-party application that recently added support for user authentication via MS Access and MS SQL Server databases using a delay-loaded DLL. The DLL exposes its interfaces via COM and its TLB is in the resource section of the DLL. I used OLE/COM Viewer to extract the IDL and wrote my own C# component that supports ODBC datasources, with an identical type library so it can be used as a drop-in replacement for the application's own database-access DLL. http://blog.tjworld.net/blog/page/TJ/20050622#two_different_copies_of_mscoree
Don't see what you're looking for? Try a search.
|