You are on page 1of 122

Managed C++:

From ATL/COM to .NET

Chris Sells
http://staff.develop.com/csells

1
Motivation

• You’ve got a lot of existing code


– Do you leave it and ignore .NET?
– Do you leave it and interop with .NET?
– Do you move it to Managed C++?
– Do you move it to C#?
– Do you start over in C#?
• All of these questions require that you understand .NET,
MC++ and C#
– Luckily, you’re in the right place…

2
Recall: The Component Object Model

• COM is a runtime provided by the OS to support


– Finding and loading classes
– Negotiating types exposed by COM classes
– Marshaling types between contexts
– Security, both RPC and Authenicode
– Multi-language support, e.g. C, C++, VB, script

3
The .NET Common Language Runtime
• The .NET CLR is an OS add-on provided to support:
– Finding and loading classes
– Negotiating types exposing by COM classes
– Marshaling types between contexts
– Sandbox security
– Multi-language support, i.e. MC++, C#, VB, JS, Eiffel, Camel, et al
– Intermediate language representation (IL)
– JIT compilers
– Common type system
– Metadata and reflection
– Versioning
– Garbage collection
– XCOPY deployment
– Win32/COM/COM+ interop

4
Recall: The Active Template Library

• ATL is a framework for building COM classes


– Infrastructure classes, e.g. CComCoClass
– Template classes, e.g. IPersistImpl
– Smart types, e.g. CComBSTR
– Wizards to get us started

5
Exposing a COM Class

• Exposing a COM class in ATL is a multi-step feat


1. Define the class’s default interface
2. Define the COM class
3. Implement a class object via CComCoClass
4. Expose the interface via the COM_MAP
5. Implement the class’s default interface
6. Provide a mapping between the COM and the C++ type via
the OBJECT_MAP
7. Expose the class object via DllGetClassObject
8. Build and register via DllRegisterServer

6
Exposing a COM Class
// (1) Define the class’s default interface
[…] interface IBeachBall : IUnknown {
HRESULT Bounce();
}

[…] library Balls {


// (2) Define the COM class
[…] coclass BeachBall {
[default] interface IBeachBall;
}
}

7
Exposing a COM Class
class ATL_NO_VTABLE CBeachBall :
public CComObjectRootEx<…>,
// (3) Implement a class object
public CComCoClass<…>,
public IBeachBall {
public:

// (4) Expose the interface
BEGIN_COM_MAP(CBeachBall)
COM_INTERFACE_ENTRY(IBeachBall)
END_COM_MAP()

// (5) Implement the class’s default interface


STDMETHODIMP Bounce() {…}
};
8
Exposing a COM Class
// (6) Provide a mapping between the COM
// and the C++ type
BEGIN_OBJECT_MAP(ObjectMap)
OBJECT_ENTRY(CLSID_BeachBall, CBeachBall)
END_OBJECT_MAP()

// (7) Expose the class object


STDAPI DllGetClassObject(…) {
return _Module.GetClassObject(…);
}

// (8) Register
STDAPI DllRegisterServer() {
return _Module.RegisterServer(TRUE);
}
9
ATL Simplifies

• Remember that ATL simplifies COM greatly


– No manual implementation of IClassFactory
– No manual COM to C++ type lookup
– No manual Registry code
– No manual ref-counting on objects or servers
– No manual interface lookup for negotiating types
• Even so, without the wizards, we’d still be scratching
our heads…

10
.NET Simplifies

• To expose a class in .NET using Managed C++


– Declare a class as public and managed

public __gc class BeachBall {


public: void Bounce() {…}
};

11
The Mechanics of Managed C++

• Microsoft have updated C++ with Managed


Extensions (aka MC++)
– Adds support for managed code
– Adds support for managed types

12
Managed Code

• By default, C++ code is unmanaged


• Managed code allows access to .NET (managed) types
– E.g. .NET framework, custom C# components, etc.
• C++ compiler extended to support managed types
– “cl.exe /CLR” from the command line
– “Use Managed Extensions: Yes” from VS.NET
• Allows use of managed types from legacy code
– E.g. MFC EXE or ATL DLL

13
Managed Code

#using <mscorlib.dll>
using namespace System;

void main() {
Console::WriteLine(S"Hello, MC++");
}
C:\> cl /CLR hello.cpp
C:\> hello.exe
C:\> ildasm hello.exe

14
Managed Types

• A managed type is handled by the CLR


– Garbage collected, described via metadata, type safe, etc.
• MC++ makes it easy to expose managed types
– Somewhat strange syntax due to ANSI C++ extension
requirements
– Lots, lots easier than ATL/COM

15
Example: MC++ Component

#using <mscorlib.dll>
using namespace System;

namespace DevelopMentorSample {
public __gc class HelloHolder {
public:
String* Hello;
String* GetHello() {
return Hello;
}
}; C:\> cl /LD /CLR hh.cpp /o hh.dll
} C:\> ildasm hh.dll
16
Example: MC++ Client

#using <mscorlib.dll>
using namespace System;

#using "hh.dll"
using namespace DevelopMentorSample;

void main() {
HelloHolder* hh = new HelloHolder;
hh->Hello = "hi";
Console::WriteLine(hh->GetHello());
}
C:\> cl /CLR hellocli.cpp
C:\> hellocli.exe
17
What Happened?

• We simplified
– No DllXxx entry points
– No extra mapping from COM to/from C++ types
– No “strange” types, e.g. BSTR, VARIANT, SAFEARRAY
– No HRESULTs (and therefore no retval)
– No get_/put_ property simulations
– No need for a formal interface
– No separate header files
• Conceptually, we didn’t do anything different…

18
From ATL/COM to .NET

• COM and .NET share a lot of common ideas


– COM exposed the ideas via language mappings
– .NET exposes the ideas directly in the languages
• As you move from COM to .NET, it’s handy to start from
what you know
– Learning a delta is always easier
– We’ll be seeing the new MC++ features as we go along

19
Component Packaging

20
“Server Type”

• COM servers may be DLLs or EXEs


• COM servers expose
– Zero or more classes
– Metadata that describes what’s exposed (optional)
– Self-registration capability (optional)
• All driven via well-known entry points and per server
code

21
.NET Assemblies

• A .NET assembly is a unit of deployment


– PE format to bootstrap the runtime (mscoree.dll)
– May be DLLs or EXEs
• .NET assemblies expose
– Zero or more types, e.g. classes, interfaces, etc.
– Metadata that describes what’s exposed and what’s
consumed
– Version info to avoid “DLL Hell”
• All driven via metadata used by the runtime

22
Assemblies Manifest Assembly 0

Assembly Declaration

File/Module References (0..M)


Manifest


Module Declaration

External Assembly
References (1..N)
Assembly M

Type Definitions (0..K)

23
Example .assembly paul

Manifest
<empty>
.assembly band
.module paul.dll
<empty>
Manifest

.assembly extern mscorlib


.module band.dll
.class public PP {
.assembly extern mscorlib .field [paul]Bassist m
.assembly extern paul }
.assembly extern john
.assembly john

Manifest
<empty>
.class public Manager { .module john.dll
}
.assembly extern mscorlib

.class public JP {
.field [john]Singer m
}

24
Private vs. Global Assemblies

• Private assemblies are loaded from app’s directory or


sub-directory
• Global assemblies can be used by multiple apps
– Must have a strong name (globally unique public key)
• Global assemblies deployed to global assembly cache
– Allows for side-by-side access to multiple versions
• Both combat DLL Hell

25
COM and DLL Hell

• The problems caused by field replaceable components


is called DLL Hell
– Interface remains the same
– Implementation changes
– Implicit contract violated
• COM servers are as susceptible as DLLs
– Only one mapping between a CLSID and a server

26
.NET and DLL Hell

• Private assemblies are updated with the application


– No DLL Hell
• Global assemblies are versioned and marked by culture
– Client binds to a particular version (and optionally, culture)
– Two versions of the same assembly can coexist on a
machine or even in a single process

27
Example: Different Versions

Monday’s
Child.exe Tuesday’s
Child.exe
Wednesday’s
Character.dll:1.0 Child.exe
(implemented with
“fairness of face”) Character.dll:1.1
(implemented with
“grace”) Character.dll:1.2
(implemented with
“woe”)

28
“Allow merging of proxy/stub code”

• COM interfaces required special handling to work


across boundaries
– Custom proxy/stub code
– Typelib-driven proxy/stub code (duals and oleautomation)
– Proxy/stub code needs registration on both sides of the
boundary to work
• Default marshaling behavior is by-reference
– By-value must be implemented by hand

29
.NET Marshalling

• Objects marshal across AppDomains


– An AppDomain is a group of threads in a process
– One process can have one or more AppDomains
• Objects don’t marshal by default
– Runtime error
• Marshalling is done via metadata
– Marshal by value for Serializable-attributed classes
– Marshal by reference for MarshalByRefObject-derived
classes
– Metadata must exist on both sides for this to work

30
.NET Marshalling

public __gc class A {}; AppDomain X

[Serializable]
public __gc class B {}; A B C

public __gc class C :


public MarshalByRefObject
{};

Clone Proxy
of B to C

AppDomain Y
Process M

31
COM Remoting

• Clients activate remotely via CoCreateInstance


– Client may explicitly indicate a remote client
– Client may get remote client implicitly via Registry settings
• OS provides a universal listener to route incoming
activation requests
– COM components do nothing special
• Does not work across the internet
– Only HTTP and SMTP really does…

32
.NET Remoting

• Clients activate remotely


– Client may explicitly indicate a remote client via
System::Activator or RemotingServices::Connect
– Client may get remote client implicitly via “new” and a
configuration file
• OS provides no universal listener
– Servers must start up and listen for requests
• Works great across the internet
– Using HTTP and SOAP makes it a WebService

33
Example: .NET Remoting Server
void main() {
// Create object and bind to "myuri"
HelloHolder* hh = new HelloHolder;
ObjRef* or = RemotingServices::Marshal(hh,
S"myuri");

// Register HTTP channel:


// http://localhost:3456/myuri
IChannel* ch = new Http::HttpChannel(3456);
ChannelServices::RegisterChannel(ch);

// Wait for keystroke to stop service


Console::ReadLine();
}

34
Example: .NET Remoting Client
void main() {
// Register HTTP channel
IChannel* ch = new Http::HttpChannel;
ChannelServices::RegisterChannel(ch);

// Build a proxy
System::Type* type = (new HelloHolder)->GetType();
HelloHolder* hh =
(HelloHolder*)RemotingServices::Connect(
type, "http://localhost:3456/myuri");

// Do something
hh->Hello = S"hi";
Console::WriteLine(hh->GetHello());
}
35
“Support MFC”

• Every language has its own class library


– C++ has two, which is why folks want to mix them
• Thunking to/from COM types is a giant pain
– ATL’s smart types are wrappers around difficult to use COM
types
– Still a giant pain

36
.NET Type System

Object ValueType Boolean


Int64
Byte
SByte
Enum Char
Single
Currency
•Shared Type TimeSpan
between DateTime
TypedRef.
languages String Decimal
UInt16
Double
UInt32
Array Guid
UInt64
Int16
Exception Void
Int32

Delegate MultiCastDelegate
37
.NET Framework
System Core type system types

System::Collections Collection types

System::Data Database access

System::IO Binary and text I/O

System::Net Network I/O types

System::Xml XML data access

System::Reflection Runtime type info

System::Runtime::InteropServices Native code support

System::Runtime::Remoting SOAP/Binary proxies

System::Runtime::Serialization Object persistence

System::Security Access control

System::Web HTTP support

System::Windows::Forms Desktop UI 38
“Support MTS”

• COM provides a runtime called COM+ (MTS)


• COM+ attributes in catalog designate services to
provide to a component
– Transactions
– Security
– Object Pooling
– Just-in-time Activation
• Services were provided at runtime via interception

39
.NET Enterprise Services

• .NET relies on COM+ runtime for its services


• COM+ attributes are part of .NET component metadata
– Via .NET attributes
– CLR will take care of mapping between the two
• Services provided at runtime via COM+ interception

40
Example: .NET Transaction Services
using namespace System::EnterpriseServices;

[assembly: ApplicationName("MyApp")];
[assembly: AssemblyKeyFileAttribute("MyApp.key")];

[Transaction(TransactionOption::Required)]
public __gc class MyTx : public ServicedComponent {
public: void PerformService() {
if( !DoSomethingCritical() ) {
ContextUtil::MyTransactionVote =
TransactionVote::Abort;
}
}
}; C:\> cl /CLR /LD mytx.cpp /o mytx.dll
C:\> regsvcs mytx.dll
41
Components

42
“Names”

• Names in COM are GUIDs


– CLSID, IID, LIBID, etc
– ProgIDs are mapped to GUIDs are runtime
• C++ symbols are for compile-type only
– CLSID_Foo, IID_IFoo, etc
• GUIDs are unique, language-independent and available
at runtime
– Some languages expose only symbols or ProgIDs, but
GUIDs are used under the covers

43
.NET Names

• .NET type names are scoped first by assembly and then


by namespace
– *Not* based on GUIDs
– Names guaranteed unique from global assemblies only
• Type names are available at runtime
– Need to reference the type’s assembly at compile time

namespace Sample {
public __gc class MyClass {…};
}
#using <sample.dll>
MyClass* obj = new Sample::MyClass;
44
“CoClass”

• In COM, CLSIDs are used to resolve to servers at


runtime
– Registry used for mapping
• CoCreateInstance used as a language-neutral “new”

IUnknown* punk = 0;
CoCreateInstance(CLSID_Foo, …, &punk);

45
“Interface”

• COM explicitly separates interface and implementation


– All functionality was exposed via interfaces
• QueryInterface used to negotiate type with the object
– Language-independent

IFoo* pfoo = 0;
punk->QueryInterface(IID_IFoo, &pfoo);

46
Resolving .NET Names

• .NET names are scoped to an assembly name at


compile-time
– Uses strong names for global assemblies
• Objects are created via language “new”
• Objects may implement interfaces
– Negotiate interfaces using language cast

47
.NET Classes and Interfaces
namespace Pets {
public __gc __interface IDog {
public: void Bark();
};
public __gc class SmartDog : public IDog {
public: void Bark() {…}
void RollOver() {…}
};
} Object* obj = new Pets::Dog;
IDog* dog = __try_cast<IDog*>(obj);
dog->Bark();
SmartDog* fido = __try_cast<SmartDog*>(obj);
fido->RollOver();
48
COM Binding to Member Names

• COM binds to interfaces, not classes


– Each class has a unique, “custom” interface
– Classes may also implement “standard” interfaces
• Compilers bind to member names at compile-time
• Names resolved down to vtbl index
• Interfaces, once published, cannot change
– Leads to IFooEx and IFoo2

49
COM Binding to Member Names

IFoo objref IFoo vptr Foo::QueryInterface

IBar objref IBar vptr Foo::AddRef

m_cRef Foo::Release

Foo::DoFoo
Additional
Data Foo::QueryInterface
Members
Foo::AddRef

Foo::Release

Foo::DoBar
50
.NET Binding to Member Names

• .NET allows binding to a member on any type, not just


interfaces
– Reserves interfaces for explicit polymorphism
• .NET binds to names at load-time
– Does binding once and caches results
• Types can change
– Global types need versioning

51
“Threading Model” and “FTM”

• COM provided several ways of determining thread


affinity and synchronization
– Determined via ThreadingModel key and the FTM

Thread Affinity Synchronization

Single yes yes

STA yes yes

MTA no no
desired
TNA no no

FTM no no

N/A no yes

52
.NET Threading and Synchronization

• .NET classes can be tagged via attributes to set


synchronization settings
– The default is no synchronization
– There’s no attribute to set thread affinity
• Require a context bound object
#using <system.enterpriseServices.dll>
using namespace System::EnterpriseServices;

[Synchronization(Synchronization::Required)]
public __gc class Rental :
public ContextBoundObject {};

53
.NET Synchronization

• .NET objects aren’t likely to want their own context


– Language-level synchronization is sufficient
• .NET objects expect client to synchronize
– Just like C++ or Java objects
• Every object has its own SynchBlock
– Allocated on demand
• Entering and exiting your SynchBlock acts like a Win32
Critical Section
– Block of code only allowed to be executed by one thread

54
Example: .NET Synchronization

using namespace System::Threading;

public __gc class Foo {


public: void ThreadSafeOperation() {
Monitor::Enter(this);

// Do something that requires


// single-threaded access

Monitor::Exit(this);
}
};
55
“Dual” vs. “Custom”

• COM interfaces were automation compatible or not


– Automation compatible interfaces were simple enough to
support typelib marshaling
• Dual = IDispatch + automation compatible
– IDispatch is for use by dynamic binding clients
• Nearly all COM objects implement dynamic binding with
a direct shunt to the typelib

56
Example: COM Dynamic Binding
long RandomNext(IDispatch *pObj, long min, long max) {
// build vector of arguments
VARIANTARGS args[2] = { 0 };
args[1].vt = VT_I4; args[1].lVal = min;
args[0].vt = VT_I4; args[0].lVal = max;
DISPPARAMS params = { args, 0, 2, 0 };
// invoke method
VARIANT result = { 0 };
HRESULT hr = pObj->Invoke(DISPID_NEXT, IID_NULL, 0,
DISPATCH_METHOD, &params, &result, 0, 0);
// coerce result to a long and clean up VARIANTs
if (SUCCEEDED(hr)) {
hr = VariantChangeType(&result, &result, 0, VT_I4);
}
VariantClear(&args[0]); VariantClear(&args[1]);
if (FAILED(hr)) throw hr;
return result.lVal;
} 57
.NET Dynamic Binding

• Statically bound .NET clients still bind using metadata


– Allows types to change (in minor ways) without affecting
existing clients
• Dynamic clients can reflect against any type at runtime
using System::Reflection
– Reflection is the act of pulling in type metadata at runtime
• Objects need do nothing special to support this

58
.NET Reflection
using namespace System::Reflection;
void main() {
Type* t = Type::GetType(S"System.Random");
Object* obj = Activator::CreateInstance(t);

Type* ats[] = new Type*[2];


ats[0] = Type::GetType(S"System.Int32");
ats[1] = Type::GetType(S"System.Int32");
MethodInfo* m = t->GetMethod(S"Next", ats);

Object* args[] = new Object*[2];


args[0] = __box(0);
args[1] = __box(100);
Object* ret = m->Invoke(obj, args);
Console::WriteLine(ret);
}
59
VB.NET Dynamic Binding

• Dynamic binding in VB.NET is easier than MC++ or C#


– Closely mimics VB6 syntax
' Spare the rod…
Option Strict Off

' Load type


Dim t As System.Type = Type.GetType("System.Random")
Dim obj As Object = System.Activator.CreateInstance(t)

' Access members w/o Reflection API


Console.WriteLine(obj.Next(min, max))

60
“Aggregation”

• COM supported a form of binary reuse known as


“aggregation”
– Often considered the “poor man’s inheritance”
• Loophole made possible by the separation of the
language and the type negotiation mechanism
– Almost never used

61
.NET Inheritance

• .NET supports real inheritance across languages


– Although, that doesn’t mean you should use it…

public __gc class Dog {


public: void Bark() {…}
};
Public Class SmartDog
Inherits Dog
Public Sub RollOver()

End Sub SmartDog dog = new SmartDog();
End Class dog.Bark();
dog.RollOver();
62
“Support ISupportErrorInfo”

• COM supports language-neutral exceptions via Error


Information Objects
– Raised via SetErrorInfo
– Caught via GetErrorInfo
– Published (sort of) via ISupportErrorInfo
• Error Info objects implemented IErrorInfo
– Custom error interfaces were mostly ignored

63
.NET Exceptions

• .NET objects can throw typed exceptions

__gc class MyException :


public Exception {};
void Bad()
{ throw new MyException(); }

void BadDog() {
try { Bad(); }
catch( MyException* e ) { throw; }
catch(...) {}
__finally {}
}
64
“Support Connection Points”

• COM events defined in groups via interfaces


– Common usage was to implement only a few
• Client subscribe/unsubscribe via COM interfaces
• Object fires events at will

65
.NET Events

• .NET events defined individually as delegates


• Client subscribe/unsubcribe via event properties
• Object fires events at will
• Syntax pretty ugly in MC++…

66
What Do We Give Up?

• Managed C++ is pretty much a superset of C++


– But we do lose some stuff in the managed world
• Multiple inheritance of implementation
– Can implement multiple interfaces, however
• Templates
– Unmanaged templates still work and interop pretty well
– Coming in v2?
• Deterministic finalization
– This is the one to watch out for…

67
COM Resource Management

• Passing around instances in COM requires care


– AddRef/Release
– CoTaskMemAlloc/CoTaskMemFree
– SysAllocString/SysFreeString
• Very easy to create cycles or leak
• We use ATL “smart” types to deal with the chores
– Nobody can protect us from cycles
– C++ dtors protect us from leaks

68
Example: COM Resource Management

void UseFoo() {
CComPtr<IFoo> spfoo;
spfoo.CoCreateInstance(CLSID_Foo);

CComBSTR bstr;
spfoo->get_Name(&bstr);

} // C++ dtors protect us from leaks


class CFoo : public CComCoClass… {

// Objects get notified at last release
void FinalRelease() {…}
};
69
.NET Resource Management

• All memory in .NET is managed


– Allocated on the stack and freed when method returns or
– Allocated on the heap and managed by the gc
• Garbage collection optimized for dealing with memory
– Never need worry about calling
Release/CoTaskMemFree/SysFreeString/delete
• However, object is not notified when last reference goes
away
– Only when gc reclaims memory

70
Example: .NET Resource Management

void UseFoo() {
Foo* pfoo = new Foo();
String* str = pfoo->Name;

} // GC will clean up for us

public __ gc class Foo {


// Objects get notified whenever…
~Foo() {…}
};

71
.NET Finalization

• The .NET gc will call an object’s finalizer when the


memory is freed
– Finalizers are called 1/2 to 1/10th less frequently then the gc
runs
• This will not happen deterministically
• Objects holding critical resources must be notified when
the client is through
– Client is responsible for providing this notification

72
The Disposer Pattern

• Objects with critical resources should implement


IDisposable
public __gc class MyClass :
public IDisposable {

public: void Dispose() {…}
};

void f() {
MyClass* p;
try { p = new MyClass;… }
__finally { if( p ) p->Dispose(); }
}
73
The Rest

• The rest of what ATL provides is handled in the .NET


framework
– Object Models & Collections
– Persistence
– Desktop UI
– Controls & Control Containment
– Database Access & Providers
– Asynch Download

74
Mixing Managed and Unmanaged Code

• /CLR compiler switch turns on managed code


– Can turn it off via #pragma

// mixed.cpp
…managed code by default…

#pragma unmanaged
…unmanaged code…

#pragma managed
…managed code…

75
Calling Unmanaged Code

• Every time you call a DLL function, that’s unmanaged


• Since the GC can fire at any time, you have to “pin”
managed data you don’t want moved
– The compiler will warn you

76
Example: Pinning Managed Data
HRESULT __stdcall VarI4FromI2(short sIn, long* plOut);

__gc struct ShortLong {


short n;
long l;
};

void main() {
ShortLong* sl = new ShortLong;
sl->n = 10;
VarI4FromI2(sl->n, &sl->l); // Compile-time error

long __pin* pn = &sl->l;


VarI4FromI2(sl->n, pn); // OK
}

77
C# is Cool

• C# is the native language of .NET


– Articles are written in C#
– Books are written in C#
– Talks are written in C#
– Samples are written in C#
– Docs are written in C#
• C# is the new language in the C family
– Mixture of C, C++, Java and VB
• You need to know C#
– Who knows, you may even like it…

78
Hello, C#

using System;

class HelloCSharp {
public static void Main(string[] args) {
Console.WriteLine("Hello, C#");
}
}

C:\> csc hello.cs


C:\> hello.exe

79
C# Component

namespace com.develop.sample {
public class HelloHolder {
public string Hello = "Hello, C#";
public string GetHello() {
return Hello;
}
}
}
C:\> csc /t:library hh.cs
C:\> ildasm hh.dll

80
Component Client
using System;
using com.develop.sample;

class HelloHolderClient {
public static void Main() {
HelloHolder hh = new HelloHolder();
Console.WriteLine("From HH: " +
hh.GetHello());
Console.WriteLine("From HH: {0}",
hh.GetHello());
}
} C:\> csc /r:hh.dll hc.cs
C:\> hc.exe
81
The C# type system
object ValueType bool
long
byte
sbyte
enum char
•All types float
map to CLR Currency
string decimal
types in DateTime
“System” int
Array TimeSpan
namespace ushort
double
Exception uint
•Integral types Guid
fixed-width ulong
short
delegate void

82
Type Safety

• Variables must be initialized before first use


• Bounds checking (no pointer arithmetic)
• All casts are dynamic casts

long n; Console.WriteLine(n); // CT ERR

long[] rg = new long[10];


rg[10] = 4; // RT ERR

Guid guid = new Guid();


object o = guid;
Currency c = (Currency)o; // RT ERR
83
Struct

• Always placed on the stack when created directly


– Constructor call optional (members set manually)
– May not have a “destructor”

struct Number {
public Number(long _n) { n = _n; }
public long n;
}

Number n1; n1.n = 4; // No ctor
Number n2 = new Number(4); // Ctor

84
Struct Are Not Classes

• Structs are not classes


– Created on the stack, not the heap
– unless they’re created as part of an instance of a class
– Can’t have a default ctor
– Can’t have a dtor/Finalize
– Can’t use them as base classes
– Can’t have inline member initialization

85
Interfaces

• Interfaces are contract only, not implementation


– Can contain properties, methods, and events
– An interface can extend zero or more interfaces

public interface ICalculator {


double Add(double x, double y);
}
public interface ITelephone {
void Call(System.String number);
}
public interface ITeleCalc:
ICalculator, ITelephone {
bool IsBatteryDead();
}

86
Class

• Always created on the heap


– Freed by the gc

class Number {
public Number(long _n) { n = _n; }
public long n = 0;
}

Number n1; // Just a reference
n1.n = 4; // ERR

Number n2 = new Number(4); // Object

87
Class Members

• Classes can have fields, methods, properties, events


and nested types
– Fields are named, typed units of storage
– Methods are typed operations
– Properties are operations that access/mutate a named value
– Events are named callbacks
– Nested types are simply classes defined inside the scope of
another class
• Members may be per-instance or per-class (static)

88
Access Modifiers

• public: access not limited


• protected: access limited to the containing class or
types derived from the containing class
• internal: access limited to this project
• private: access limited to the containing type

89
Fields and Properties

• Fields are variable members


• Properties are functions
• Both are invoked the same way (prefer properties)

class Point {
public long x;
public long y {
get { return _y; }
set { _y = value; }
}
private long _y; Point pt = new Point();
} pt.x = 1;
pt.y = 2;
90
Method Arguments

• Args are in if no modified is used


• May be marked as out or ref
– ref means in+out
• Out and ref arguments must be marked when calling
public static void Square(ref long n)
{ n *= n; }

public static void Main() {


long n = 2;
Square(ref n);
System.Console.WriteLine(n);
}
91
Variable Arguments

• You may use the params modifier to designate variable


arguments

static void Echo(params string[] rg) {


for( int i = 0; i != rg.Length; ++i )
{ System.Console.WriteLine(rg[i]); }
}

public static void Main() {


Echo("1", "2", "3");
}

92
Indexers

• An indexer allows a class to implement the [] operator

class Numbers {
public Numbers(int n)
{ _rg = new long[n]; }
public long this[int n] {
get { return _rg[n]; }
set { _rg[n] = value; }
}
private long[] _rg;
}
Numbers rg = new Numbers(10);
rg[0] = 1; Console.WriteLine(rg[0]);
93
Operator overloading

• Can implement unary, binary and conversion operators


– Conversion operators are implicit or explicit

class Number {
public Number(long n) { _n = n; }
public static Number
operator+(Number n1, Number n2)
{ return new Number(n1._n + n2._n); }
public static implicit
operator long(Number n) { return n._n; }
private long _n;
};

94
Delegates

• Delegates are C# “function pointers”


– Initialized with a method name
– Invoked like a function

delegate int BinOp(int x, int y);

public class Adder {


public int Add(int x, int y)
{ return x + y; }
}

Adder a = new Adder();


BinOp pfn = new BinOp(a.Add);
int x = pfn(4, 5); 95
Events

• An event is a pseudo-property of type delegate


– Holds zero or more delegates
– Operators += and -= for adding/removing delegates
– Client may not invoke event

delegate void ClickHandler();


class Button {

public event ClickHandler OnClick;
}

Button b = new Button();


b.OnClick += new ClickHandler(form.OnClickButton);
96
Firing Events

• Invoking the event notifies all delegates


– A null value indicates zero delegates

class Button {
public void Click() {
if( OnClick != null ) OnClick();
}
public event ClickHandler OnClick;
};

97
Inheritance

• Every type extends exactly one type


– Implicitly object if none specified
– May implement zero or more interfaces
• Most-derived constructor called at instantiation
– Base ctor called implicitly (can call explicitly)

class Foo { public Foo(int n) … }


class Foo2 : Foo {
public Foo2(int n) : base(n) {}
}

class FooPhone : Foo, ITelephone {…}

98
Function Overriding

• All functions are non-virtual unless marked “virtual”


• Must indicate intention to override methods
– “override” to override virtual functions
– “new” to override non-virtual functions

class Base {
public virtual void foo() {…}
public void bar() {…}
}
class Derived {
public override void foo() {…}
public new void bar() {…}
}
99
System.Object

• Universal base class

namespace System {
public class Object {
public Object();
public virtual Boolean Equals(Object rhs);
public virtual Int32 GetHashCode();
public Type GetType();
public virtual String ToString();

protected virtual void Finalize();


protected Object MemberwiseClone();
}

100
Example: Overriding ToString
class Number {
public Number(int n) { _n = n; }
private int _n;
public override string ToString()
{ return _n.ToString(); }
}

Number n = new Number(2);


System.Console.WriteLine(n.ToString());
System.Console.WriteLine(n);

101
Overriding Finalize

• Finalize method will be called when the gc frees the


object
– Not going to happen at scope boundaries
– May not happen at all
• Implementing Finalize requires dtor syntax

class MyClass {
// Overrides Finalize()
~MyClass() {…}
}

102
C# Destructors Aren’t

• C# borrows the C++ destructor syntax


– Merely syntactic sugar for calling the base class’s Finalize
– Not called at scope boundaries

class Derived : MyBase {


public ~Derived() {
… // free resources
} // base.Finalize() called implicitly
}

103
The Disposer Pattern

• Objects with critical resources should expose


IDisposable

class MyClass : IDisposable {



void Dispose() {…}
};

void f() {
MyClass p;
try { p = new MyClass(); … }
finally { if( p ) p.Dispose(); }
}
104
C# Supports IDisposable Directly

• C# provides the “using” keyword to define a scope


– IDisposable::Dispose called at end of scope

class MyClass : IDisposable {…}

void f() {
using( MyClass obj = new MyClass() ) {
// Use obj
} // (IDisposable)obj.Dispose() called
}

105
Sealed and Abstract Types

• Types can be marked as abstract to prevent


instantiation without derivation
– All interfaces are abstract
• Types can be marked as sealed to prevent use as a
base type
– All structs are sealed
• Can force derived type to override method by declaring
it abstract
sealed class B {}
abstract class C {}
class D : B {} // ERR
new C(); // ERR
106
Value vs. Reference types

• Value types live on the stack


– Are copied when passed as arguments
– Structs and simple types are value types
• Reference types live on the heap
– Classes are reference types

struct V {…}
class R {…}

V vt = new V; // stack
R rt = new R; // heap

107
Boxing and Unboxing

• Boxing is converting between a value type and a


reference type
– Boxing creates a copy of the value type
• Unboxing is converting between a reference type and a
value type

108
Example: Boxing and Unboxing
a 0
2 0
3
obj vptr 2
5 3
7
foo1
foo2 vptr 2
6 3
8
b 6 8 V a = new V();
a.g(2);
object obj = a; // box
IFoo foo1 = (IFoo)obj;
foo1.g(3);
IFoo foo2 = (IFoo)a; // box
foo2.g(4);
V b = (V)foo1; // unbox
109
Compilation Symbols

• Can define compilation symbols


– Can’t define macros
– Can pass them on the command line
#if DEBUG
#warning Compiling in debug mode
#define DEBUGGING
#endif

C:\> csc /d:DEBUG;OTHER hello.cs

110
Conditional Compilation

• Functions can be marked as conditional


– Calls are dropped if symbol not defined
– Must return void
[conditional("DEBUGGING")]
static void Trace(params string[] rg)…

Trace(“My Trace Statement”);

111
Control Flow

• If, while, do, goto just like C++


• Switch
– Requires break (explicit goto allowed)
– Permits string cases

switch( s ) {
case "one": …; break;
case "two": case "three": …; break;
default: …; goto case "one"; break;
}

112
The foreach statement

• The foreach statement allows easy access to members


of a collection
– Collections implement GetEnumerator, MoveNext and
Current

public static void Main() {


IDictionary coll =
Environment.GetEnvironmentVariables();
foreach( string key in coll.Keys ) {
Console.WriteLine(key + "= " + coll[key]);
}
}

113
Exceptions

• Catch exceptions based on type via try-catch block


• Handle final chores in a finally block
• Throw exceptions using throw statement
• Define custom exceptions by deriving from
System.Exception

114
Example: Exceptions

class MyException : Exception {};

static void Bad()


{ throw new MyException(); }

public static void Main() {


try { Bad(); }
catch( MyException e ) { throw; }
catch {}
finally {}
}
115
Win32/.NET Interop

• Can access Win32 DLLs and COM servers from .NET


– And vice versa

using System.Runtime.InteropServices;

[DllImport("user32.dll")]
public static extern int
MessageBox(int p, string t, string c, ulong f);

public static void Main() {


MessageBox(0, "Hello, .NET", "", 0);
}

116
COM/.NET Interop

• tlbimp tool converts typelibs to assemblies


– tlbexp tool converts assembly metadata to typelib
– regasm tool registers .NET assemblies as COM servers
– aximp converts COM Controls to WinForm Controls
c:\>tlbimp C:\WINNT\System32\scrrun.dll

using Scripting;

Dictionary dict = new Dictionary();


object key = 1;
object value = "one";
dict.Add(ref key, ref value);
Console.WriteLine(dict.get_Item(ref key)); 117
.NET Framework
System Core type system types

System.Collections Collection types

System.Data Database access

System.IO Binary and text I/O

System.Net Network I/O types

System.Xml XML data access

System.Reflection Runtime type info

System.Runtime.InteropServices Native code support

System.Runtime.Remoting SOAP/Binary proxies

System.Runtime.Serialization Object persistence

System.Security Access control

System.Web HTTP support

System.Windows.Forms Desktop UI 118


What C++ Programmers Give Up

• Templates
• Global functions
• Pointer arithmetic (except in unsafe sections)
• Scope resolution and arrow operators
• Deterministic finalization
• Local static variables
• Macros

119
Summary

• You’ve got a lot of existing code


– Do you leave it and ignore .NET?
– Do you leave it and interop with .NET?
– Do you move it to Managed C++?
– Do you move it to C#?
– Do you start over in C#?

120
References

• “Visual Studio .NET: Managed Extensions Bring .NET


CLR Support to C++”
– by Chris Sells, MSDN Magazine, July, 2001.
– Shows things from a more language-centric view
• “COM+ Programming with .NET”
– by Tim Ewald, MSDN Magazine, 2H2001
• .NET mailing list
– http://discuss.develop.com
• .NET SDK
• Essential .NET
– http://www.develop.com

121
Questions?

122

You might also like