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. 7 3774
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]
- mv*@spam.guard.caspershouse.com
"TJ" <mi***************@tjworld.org> wrote in message
news:eS**************@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.
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)
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/archi...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/archi...10/07/167.aspx
Part 3: Handling ComVisible(true), default, reference-type properties in C#: http://blog.chadmyers.com/chad/archi...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/archi...02/10/145.aspx
Hope this helps.
-Chad
"TJ" <mi***************@tjworld.org> wrote in message
news:eX**************@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)
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.
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... 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?
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!
12345678911234567892123456789312345678941234567895 1234567896123456789712 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
"TJ" <mi***************@tjworld.org> wrote in message
news:Oy****************@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.
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\mscor wks.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...ies_of_mscoree This thread has been closed and replies have been disabled. Please start a new discussion. Similar topics
by: Kalle Anke |
last post by:
I'm coming to Python from other programming languages. I like to
hide all attributes of a class and to only provide access to them
via methods. Some of these languages allows me to write something...
|
by: |
last post by:
Hi,
I want to do things this way:
I have a bunch of stuff that I want to keep track of while a user is
connected to the site. Maybe 50 little peices of information.
So I know I can make 50...
|
by: Laszlo Zsolt Nagy |
last post by:
Hughes, Chad O wrote:
> Is there any way to create a class method? I can create a class
> variable like this:
>
Hmm, seeing this post, I have decided to implement a 'classproperty'...
|
by: Andrea Williams |
last post by:
I need to expose the properties of an User Object to all other classes, but
hide the operations (functions) from all but the Business layer of my app?
The goal is to load up this USER object and...
|
by: Venu |
last post by:
Hi Everyone,
I'm very new to C# and I have the following problem - please help.
I have a Windows Forms application called 'FunApp' which has a status
bar. I also have a separate class library...
|
by: Steve |
last post by:
I'm in a weird situation where I'm using ComboBox's in a DataGrid. When the
ComboBox selection changes, I'm currently storing the SelectedValue object
into the DataSource of the DataGrid cell. ...
|
by: Michael Maes |
last post by:
Hello,
I have a BaseClass and many Classes which all inherit (directly) from the BaseClass.
One of the functions in the BaseClass is to (de)serialize the (inherited) Class to/from disk.
...
|
by: alainpoint |
last post by:
Hello,
I have got a problem that i can't readily solve.
I want the following:
I want to create a supertuple that behaves both as a tuple and as a
class.
It should do the following:...
|
by: Len Weltman |
last post by:
I am trying to pass a NumericUpDown object into a class method using Visual
Studio 2005, but the control type is
not found in Intellisense and the type declaration is flagged as an error.
Here...
|
by: John Wright |
last post by:
I want to create a method in my class where I can pass in a property name
and have it reset the property to the previous value. I have the method
that stores the properties last value, now I need...
|
by: Kemmylinns12 |
last post by:
Blockchain technology has emerged as a transformative force in the business world, offering unprecedented opportunities for innovation and efficiency. While initially associated with cryptocurrencies...
|
by: antdb |
last post by:
Ⅰ. Advantage of AntDB: hyper-convergence + streaming processing engine
In the overall architecture, a new "hyper-convergence" concept was proposed, which integrated multiple engines and...
|
by: Matthew3360 |
last post by:
Hi there. I have been struggling to find out how to use a variable as my location in my header redirect function.
Here is my code.
header("Location:".$urlback);
Is this the right layout the...
|
by: WisdomUfot |
last post by:
It's an interesting question you've got about how Gmail hides the HTTP referrer when a link in an email is clicked. While I don't have the specific technical details, Gmail likely implements measures...
|
by: Matthew3360 |
last post by:
Hi,
I have been trying to connect to a local host using php curl. But I am finding it hard to do this. I am doing the curl get request from my web server and have made sure to enable curl. I get a...
|
by: Oralloy |
last post by:
Hello Folks,
I am trying to hook up a CPU which I designed using SystemC to I/O pins on an FPGA.
My problem (spelled failure) is with the synthesis of my design into a bitstream, not the C++...
|
by: Carina712 |
last post by:
Setting background colors for Excel documents can help to improve the visual appeal of the document and make it easier to read and understand. Background colors can be used to highlight important...
|
by: BLUEPANDA |
last post by:
At BluePanda Dev, we're passionate about building high-quality software and sharing our knowledge with the community. That's why we've created a SaaS starter kit that's not only easy to use but also...
|
by: Rahul1995seven |
last post by:
Introduction:
In the realm of programming languages, Python has emerged as a powerhouse. With its simplicity, versatility, and robustness, Python has gained popularity among beginners and experts...
| |