@@ -68,6 +68,10 @@ private static bool GenerateCcwFor(MetadataReader reader, StringHandle typeName,
6868 return true ;
6969 }
7070
71+ private static StatementSyntax ThrowOnHRFailure ( ExpressionSyntax hrExpression ) => ExpressionStatement ( InvocationExpression (
72+ MemberAccessExpression ( SyntaxKind . SimpleMemberAccessExpression , hrExpression , HRThrowOnFailureMethodName ) ,
73+ ArgumentList ( ) ) ) ;
74+
7175 /// <summary>
7276 /// Generates a type to represent a COM interface.
7377 /// </summary>
@@ -327,10 +331,6 @@ FunctionPointerParameterSyntax ToFunctionPointerParameter(ParameterSyntax p)
327331 if ( methodDefinition . Generator . TryGetPropertyAccessorInfo ( methodDefinition , originalIfaceName , context , out IdentifierNameSyntax ? propertyName , out SyntaxKind ? accessorKind , out TypeSyntax ? propertyType ) &&
328332 declaredProperties . Contains ( propertyName . Identifier . ValueText ) )
329333 {
330- StatementSyntax ThrowOnHRFailure ( ExpressionSyntax hrExpression ) => ExpressionStatement ( InvocationExpression (
331- MemberAccessExpression ( SyntaxKind . SimpleMemberAccessExpression , hrExpression , HRThrowOnFailureMethodName ) ,
332- ArgumentList ( ) ) ) ;
333-
334334 BlockSyntax ? body ;
335335 switch ( accessorKind )
336336 {
@@ -1307,15 +1307,124 @@ private bool TryDeclareCOMGuidInterfaceIfNecessary()
13071307 /// Creates an empty class that when instantiated, creates a cocreatable Windows object
13081308 /// that may implement a number of interfaces at runtime, discoverable only by documentation.
13091309 /// </summary>
1310- private ClassDeclarationSyntax DeclareCocreatableClass ( TypeDefinition typeDef )
1310+ private ClassDeclarationSyntax DeclareCocreatableClass ( TypeDefinition typeDef , Context context )
13111311 {
1312+ bool canUseComImport = context . AllowMarshaling && ! this . useSourceGenerators ;
1313+
13121314 IdentifierNameSyntax name = IdentifierName ( this . Reader . GetString ( typeDef . Name ) ) ;
13131315 Guid guid = this . FindGuidFromAttribute ( typeDef ) ?? throw new ArgumentException ( "Type does not have a GuidAttribute." ) ;
13141316 SyntaxTokenList classModifiers = TokenList ( TokenWithSpace ( this . Visibility ) ) ;
13151317 classModifiers = classModifiers . Add ( TokenWithSpace ( SyntaxKind . PartialKeyword ) ) ;
13161318 ClassDeclarationSyntax result = ClassDeclaration ( name . Identifier )
13171319 . WithModifiers ( classModifiers )
1318- . AddAttributeLists ( AttributeList ( ) . AddAttributes ( GUID ( guid ) , ComImportAttributeSyntax ) ) ;
1320+ . AddAttributeLists ( AttributeList ( ) . AddAttributes ( GUID ( guid ) ) . AddAttributes ( canUseComImport ? [ ComImportAttributeSyntax ] : [ ] ) ) ;
1321+
1322+ if ( ! canUseComImport && ! this . Options . ComInterop . UseIntPtrForComOutPointers )
1323+ {
1324+ string obsoleteMessage = context . AllowMarshaling
1325+ ? $ "COM source generators do not support direct instantiation of co-creatable classes. Use { name . Identifier } .CreateInstance<T> instead."
1326+ : $ "Marshaling is disabled, so direct instantiation of co-creatable classes is not supported. Use { name . Identifier } .CreateInstance<T> instead.";
1327+
1328+ // Generate a private readonly field for the Guid
1329+ // private static readonly Guid CLSID_Foo = new Guid(...);
1330+ SyntaxToken clsidFieldName = Identifier ( $ "CLSID_{ name . Identifier } ") ;
1331+ FieldDeclarationSyntax clsidField = FieldDeclaration (
1332+ VariableDeclaration ( IdentifierName ( nameof ( Guid ) ) )
1333+ . AddVariables ( VariableDeclarator ( clsidFieldName ) . WithInitializer ( EqualsValueClause ( GuidValue ( guid ) ) ) ) )
1334+ . AddModifiers ( TokenWithSpace ( SyntaxKind . PrivateKeyword ) , TokenWithSpace ( SyntaxKind . StaticKeyword ) , TokenWithSpace ( SyntaxKind . ReadOnlyKeyword ) ) ;
1335+ result = result . AddMembers ( clsidField ) ;
1336+
1337+ // If using source generators or marshalling is disabled, generate a constructor with obsolete attribute like this:
1338+ // [Obsolete("COM source generators do not support direct instantiation of co-creatable classes. Use CreateInstance<T> method instead.")]
1339+ // public Foo() { throw new NotSupportedException("COM source generators do not support direct instantiation of co-creatable classes. Use CreateInstance<T> method instead."); }
1340+ AttributeSyntax obsoleteAttribute =
1341+ Attribute ( IdentifierName ( nameof ( ObsoleteAttribute ) ) )
1342+ . AddArgumentListArguments (
1343+ AttributeArgument ( LiteralExpression ( SyntaxKind . StringLiteralExpression , Literal ( obsoleteMessage ) ) ) ) ;
1344+ ConstructorDeclarationSyntax constructor = ConstructorDeclaration ( name . Identifier )
1345+ . AddModifiers ( TokenWithSpace ( SyntaxKind . PublicKeyword ) )
1346+ . AddAttributeLists ( AttributeList ( ) . AddAttributes ( obsoleteAttribute ) )
1347+ . WithBody (
1348+ Block (
1349+ ThrowStatement (
1350+ ObjectCreationExpression ( IdentifierName ( nameof ( NotSupportedException ) ) )
1351+ . WithArgumentList (
1352+ ArgumentList ( ) . AddArguments (
1353+ Argument (
1354+ LiteralExpression ( SyntaxKind . StringLiteralExpression , Literal ( obsoleteMessage ) ) ) ) ) ) ) ) ;
1355+ result = result . AddMembers ( constructor ) ;
1356+
1357+ this . MainGenerator . TryGenerateExternMethod ( "CoCreateInstance" , out IReadOnlyCollection < string > preciseApi ) ;
1358+ this . MainGenerator . TryGenerateConstant ( "CLSCTX" , out preciseApi ) ;
1359+
1360+ if ( context . AllowMarshaling )
1361+ {
1362+ // Then add the CreateInstance<T> method:
1363+ // public static T CreateInstance<T>() where T : class
1364+ // {
1365+ // PInvoke.CoCreateInstance<T>(CLSID_Foo, null, CLSCTX.CLSCTX_SERVER, out T ret).ThrowOnFailure();
1366+ // return ret;
1367+ // }
1368+ TypeParameterSyntax typeParameter = TypeParameter ( Identifier ( "T" ) ) ;
1369+ GenericNameSyntax genericName = GenericName ( "CreateInstance" ) . AddTypeArgumentListArguments ( IdentifierName ( "T" ) ) ;
1370+ MethodDeclarationSyntax createInstanceMethod = MethodDeclaration ( IdentifierName ( "T" ) , genericName . Identifier )
1371+ . AddModifiers ( TokenWithSpace ( SyntaxKind . PublicKeyword ) , TokenWithSpace ( SyntaxKind . StaticKeyword ) )
1372+ . AddTypeParameterListParameters ( typeParameter )
1373+ . AddConstraintClauses (
1374+ TypeParameterConstraintClause ( IdentifierName ( "T" ) , SingletonSeparatedList < TypeParameterConstraintSyntax > ( ClassOrStructConstraint ( SyntaxKind . ClassConstraint ) ) ) )
1375+ . WithBody (
1376+ Block (
1377+ ThrowOnHRFailure (
1378+ InvocationExpression ( QualifiedName ( ParseName ( $ "{ this . Win32NamespacePrefix } .{ this . options . ClassName } ") , GenericName ( "CoCreateInstance" ) . AddTypeArgumentListArguments ( IdentifierName ( "T" ) ) ) )
1379+ . WithArgumentList (
1380+ ArgumentList ( ) . AddArguments (
1381+ Argument ( IdentifierName ( clsidFieldName ) ) ,
1382+ Argument ( LiteralExpression ( SyntaxKind . NullLiteralExpression ) ) ,
1383+ Argument (
1384+ MemberAccessExpression (
1385+ SyntaxKind . SimpleMemberAccessExpression ,
1386+ QualifiedName ( ParseName ( $ "{ this . Win32NamespacePrefix } .System.Com") , IdentifierName ( "CLSCTX" ) ) ,
1387+ IdentifierName ( "CLSCTX_SERVER" ) ) ) ,
1388+ Argument ( DeclarationExpression ( IdentifierName ( "T" ) . WithTrailingTrivia ( Space ) , SingleVariableDesignation ( Identifier ( "ret" ) ) ) ) . WithRefKindKeyword ( Token ( SyntaxKind . OutKeyword ) ) ) ) ) ,
1389+ ReturnStatement ( IdentifierName ( "ret" ) ) ) ) ;
1390+ result = result . AddMembers ( createInstanceMethod ) ;
1391+ }
1392+ else
1393+ {
1394+ // Then add a CreateInstance<T> method that looks like this:
1395+ // public static HRESULT CreateInstance<T>(out T* instance) where T : unmanaged
1396+ // {
1397+ // return PInvoke.CoCreateInstance<T>(CLSID_Foo, null, CLSCTX.CLSCTX_SERVER, out instance);
1398+ // }
1399+ TypeParameterSyntax typeParameter = TypeParameter ( Identifier ( "T" ) ) ;
1400+ GenericNameSyntax genericName = GenericName ( "CreateInstance" ) . AddTypeArgumentListArguments ( IdentifierName ( "T" ) ) ;
1401+ MethodDeclarationSyntax createInstanceMethod = MethodDeclaration ( IdentifierName ( $ "{ this . Win32NamespacePrefix } .Foundation.HRESULT") , genericName . Identifier )
1402+ . AddModifiers ( TokenWithSpace ( SyntaxKind . PublicKeyword ) , TokenWithSpace ( SyntaxKind . StaticKeyword ) , TokenWithSpace ( SyntaxKind . UnsafeKeyword ) )
1403+ . AddTypeParameterListParameters ( typeParameter )
1404+ . AddConstraintClauses (
1405+ TypeParameterConstraintClause ( IdentifierName ( "T" ) , SingletonSeparatedList < TypeParameterConstraintSyntax > ( TypeConstraint ( IdentifierName ( "unmanaged" ) ) ) ) )
1406+ . WithParameterList (
1407+ ParameterList ( ) . AddParameters (
1408+ Parameter ( Identifier ( "instance" ) )
1409+ . WithType ( PointerType ( IdentifierName ( "T" ) ) )
1410+ . WithModifiers ( TokenList ( Token ( SyntaxKind . OutKeyword ) ) ) ) )
1411+ . WithBody (
1412+ Block (
1413+ ReturnStatement (
1414+ InvocationExpression ( QualifiedName ( ParseName ( $ "{ this . Win32NamespacePrefix } .{ this . options . ClassName } ") , GenericName ( "CoCreateInstance" ) . AddTypeArgumentListArguments ( IdentifierName ( "T" ) ) ) )
1415+ . WithArgumentList (
1416+ ArgumentList ( ) . AddArguments (
1417+ Argument ( IdentifierName ( clsidFieldName ) ) ,
1418+ Argument ( LiteralExpression ( SyntaxKind . NullLiteralExpression ) ) ,
1419+ Argument (
1420+ MemberAccessExpression (
1421+ SyntaxKind . SimpleMemberAccessExpression ,
1422+ QualifiedName ( ParseName ( $ "{ this . Win32NamespacePrefix } .System.Com") , IdentifierName ( "CLSCTX" ) ) ,
1423+ IdentifierName ( "CLSCTX_SERVER" ) ) ) ,
1424+ Argument ( IdentifierName ( "instance" ) ) . WithRefKindKeyword ( Token ( SyntaxKind . OutKeyword ) ) ) ) ) ) ) ;
1425+ result = result . AddMembers ( createInstanceMethod ) ;
1426+ }
1427+ }
13191428
13201429 result = this . AddApiDocumentation ( name . Identifier . ValueText , result ) ;
13211430 return result ;
0 commit comments