After going through a number of different articles and not finding anything especially conclusive that takes me step-by-step through the process, I've come seeking help.
The Scenario
A client of mine is only proficient in development for ASP Classic. They have recently acquired an account for a site originally written in ASP.NET. They are rolling the site into something they can actively maintain, but the site originally included an image handler that took dynamically changing data regarding water levels and outputs an image containing a graphical representation of that data. The requirement is to develop a COM interop library that can be registered on the server and called with CreateObject to generate the same image's byte array for output using Response.BinaryWrite. The COM interop library must be registered at the remote site on a Windows 2000 Server, and I can't make any assumptions about their having access to regasm/gacutil to accomplish that task.
The Difficulty
I've built the class library by creating a Class Library project in Visual Studio 2010, choosing "COM Class" from the template, and inserting my code to generate a class with a single public method to return a byte array when given an integer (well, enumerator, but all the same). Unfortunately, even on my own development machine after building the library and registering (regasm) and caching the assembly (gacutil), I can't make a call through Classic ASP to instantiate the object, receiving instead an "ActiveX component can't create object" error. And, of course, at the server site, the DLL file can't be registered, the response being "Required module was not found."
Resources I've Used
I've already had a look through the following articles and haven't turned up the answers I need:
(Basic steps) Walkthrough: Creating COM Objects with Visual Basic
Build and Deploy a .NET COM Assembly
.NET COM+ Interop Component with Classic ASP
What I Need
Essentially what I need is a bit of hand-holding on a kind of step by step of what it's going to take to meet the requirements and create a COM+ interop module correctly in Visual Studio 2010. Creating the actual class object itself isn't terribly difficult.
However, none of the articles I've looked through really discuss project options or build procedures with Visual Studio 2010 or the .NET 4.0 Framework, nor have any of them really discussed if there are special considerations for deploying to older systems like Windows Server 2000 and the actual registration of the library on a system with only, say, regsvr32 on hand.
It should be fairly straightforward to get a basic .NET assembly exposed to COM - I've never tried the COM Class project template, so this is the way I've managed it in the past:
Create a new (bog standard) .NET class library using C# or VB. Define a COM interface (replace GUIDs with your own):
[ComVisible(true)]
[Guid("8999F93E-52F6-4E29-BA64-0ADC22A1FB11")]
public interface IComm
{
string GetMyGroups();
}
Now define a class that implements that interface (again, replace GUIDs with your own):
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
[GuidAttribute("C5C5A1A8-9BFB-4CE5-B42C-4E6688F6840B")]
[ProgId("Test.Comm.1")]
public class Comm : IComm
{
public string GetMyGroups()
{
var comm = new CommunicatorAPI.MessengerClass();
var groups = comm.MyGroups as IMessengerGroups;
return string.Join(", ", groups.OfType<IMessengerGroup>().Select(g => g.Name).ToArray());
}
}
The Prog ID attribute on this class is what you will use to instantiate your component from ASP.
Strongly-name the assembly (Project properties -> "Signing" tab -> "Sign the assembly" -> Create a new strong name key file using the dropdown)
Now, build the assembly, and register using Regasm - if you don't wish to register in the GAC (which i'd recommend, as not GACing keeps the deployment simpler), be sure to use the -Codebase parameter (this just adds a reg entry that tells clients where to find the assembly) - e.g:
regasm ClassLibrary2.dll /codebase "S:\Testing\ClassLibrary2\ClassLibrary2\bin\Debug\ClassLibrary2.dll"
Now you should be able to instantiate the component, and call methods on it - for example (in javascript):
var a = new ActiveXObject("Test.Comm.1");
alert(a.GetMyGroups());
When it comes to deployment, the important work that Regasm and Regsvr32 do is to write various settings into the registry, so that clients can find the COM component (based on Prog ID, or COM Class ID). All you need to do is work out what COM settings are being written when you run Regasm on your local machine, and write these to the registry on the server. You can use ProcMon to monitor what gets written to the registry when Regasm is run.
Generally speaking, you can expect to see something like this written to the registry:
[HKEY_CLASSES_ROOT\Test.Comm.1]
#="ClassLibrary2.Comm"
[HKEY_CLASSES_ROOT\Test.Comm.1\CLSID]
#="{00585504-90C8-4760-A359-67CAF08FFED1}"
[HKEY_CLASSES_ROOT\Wow6432Node\CLSID\{00585504-90C8-4760-A359-67CAF08FFED1}]
#="ClassLibrary2.Comm"
[HKEY_CLASSES_ROOT\Wow6432Node\CLSID\{00585504-90C8-4760-A359-67CAF08FFED1}\Implemented Categories]
[HKEY_CLASSES_ROOT\Wow6432Node\CLSID\{00585504-90C8-4760-A359-67CAF08FFED1}\Implemented Categories\{62C8FE65-4EBB-45e7-B440-6E39B2CDBF29}]
[HKEY_CLASSES_ROOT\Wow6432Node\CLSID\{00585504-90C8-4760-A359-67CAF08FFED1}\InprocServer32]
#="mscoree.dll"
"ThreadingModel"="Both"
"Class"="ClassLibrary2.Comm"
"Assembly"="ClassLibrary2, Version=1.0.0.0, Culture=neutral, PublicKeyToken=cf55d4e60653257a"
"RuntimeVersion"="v4.0.30319"
"CodeBase"="file:///S:/Testing/ClassLibrary2/ClassLibrary2/bin/Debug/ClassLibrary2.DLL"
[HKEY_CLASSES_ROOT\Wow6432Node\CLSID\{00585504-90C8-4760-A359-67CAF08FFED1}\InprocServer32\1.0.0.0]
"Class"="ClassLibrary2.Comm"
"Assembly"="ClassLibrary2, Version=1.0.0.0, Culture=neutral, PublicKeyToken=cf55d4e60653257a"
"RuntimeVersion"="v4.0.30319"
"CodeBase"="file:///S:/Testing/ClassLibrary2/ClassLibrary2/bin/Debug/ClassLibrary2.DLL"
[HKEY_CLASSES_ROOT\Wow6432Node\CLSID\{00585504-90C8-4760-A359-67CAF08FFED1}\ProgId]
#="Test.Comm.1"
Hope this helps :)
I have a Classic ASP web site that uses a VB6 COM object. I wanted to create a new version of the COM object using .NET instead of VB6. This is how I did it (hope this helps). I include instructions for both C# and VB.NET.
[01]
Start Visual Studio 2015 (run as admin).
Create a new "Class Library" project.
Name it: "DotNetCom"
[02] C#
Add a new class, name it "HelloCOM".
Use the following code as starting template
( visit https://msdn.microsoft.com/en-us/library/c3fd4a20.aspx for more info )
using System.Runtime.InteropServices;
namespace DotNetCom
{
[Guid("EAA4976A-45C3-4BC5-BC0B-E474F4C3C83F")]
public interface HelloCOMInterface
{
[DispId(1)]
string Hello();
}
[Guid("7BD20046-DF8C-44A6-8F6B-687FAA26FA71"),
InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface HelloCOMEvents
{
}
[Guid("0D53A3E8-E51A-49C7-944E-E72A2064F938"),
ClassInterface(ClassInterfaceType.None),
ComSourceInterfaces(typeof(HelloCOMEvents))]
public class HelloCOM : HelloCOMInterface
{
public string Hello()
{
return "Hello there!";
}
}
}
[02] VB.NET
Add a new "COM class", name it "HelloCOM".
VB.NET creates the starting template.
Add the following function to the "HelloCOM" class.
Public Function Hello() As String
Return "Hello there!"
End Function
[03] C#
Open the project properties.
Go to "Application".
Click "Assembly Information...".
Check "Make assembly COM-Visible"
Go to "Build".
Select "Platform target: x86".
Check "Register COM for interop"
[03] VB.NET
Open "MyProject".
Go to "Compile".
Select "Target CPU: x86".
[04]
Build the "DotNetCom.dll".
[05]
Open a command prompt (run as admin).
Change directory to your dll.
cd DotNetComTest\DotNetComTest\TX7NGN.COM\bin\Debug
Run RegAsm /codebase.
C:\Windows\Microsoft.NET\Framework\v4.0.30319\RegAsm DotNetComTest.dll /codebase "C:\DotNetComTest\DotNetComTest\bin\Debug\DotNetComTest.dll"
[06]
Start Component Services.
Add a new COM+ application.
Name it: "DotNetCom".
Open the "DotNetCom" properties.
Go to the "Security Tab".
UNCHECK "Enforce access checks for this application".
[07]
Add a new component.
Select "DotNetComTest.tlb" (do NOT select "DotNetComTest.dll").
[08]
Use the COM object from the Classic ASP page.
<%
Dim HelloCOM
Set HelloCOM = Server.CreateObject("DotNetCom.HelloCOM")
Response.Write HelloCom.Hello
%>
Related
I have a question about adding AOT objects by X++ Code on D365 FO.
The goal, is to automate creation of security duty, via x++ code, instead of doing it manually
I'm trying actually with the Following code,
public static void main (Args _args)
{
#AOT
str objectName = 'MySpecTable' ;
TreeNode nodePath = TreeNode::findNode(#TablesPath);
TreeNode nodePath1;
nodePath1 = nodePath.AOTfindChild(objectName);
if(nodePath)
{
nodePath.AOTadd(objectName);
//nodePath.AOTsave();
info("Sec privilege well added");
}
else
{
nodePath.AOTadd(objectName);
nodePath.AOTsave();
info("Table well added");
}
}
But i receive the Following error,
is there any way to achieve this goal.to be able adding them via code.
Thanks
This is not possible anymore as, compared with previous AX versions, where code and metadata was dynamically interpreted in runtime, F&O executes only pre-compiled assemblies (like any other .NET application). Same way you can't create a C# class on a running .NET assembly, you can't do it either in Finance and Operations application.
Required Security artifacts must be properly created in Visual Studio, compiled and deployed to the runtime environment (UAT, PROD or whatever), and then the security configuration itself (linking these artifacts between them or with users) is now stored in the database, so it can be done directly in the Security setup forms. It can also be exported and imported with Data entities within the standard form.
What you can do is create a Visual Studio extension to automatically create such objects, so then they can be compiled and deployed correctly.
I'd like to use a piece of Windows 10 specific UWP API (specifically, the Windows.Graphics.Printing3D stuff) in an ASP.NET code-behind DLL. Is there any way to do so?
While looking for a .NET-only resolution to this one, I've found a moderately clean way - a Win32/64 C++ DLL that would consume UWP API and present a COM- or P/Invoke-based interface to .NET.
Create a regular Win32 DLL. Build an interface for .NET to consume - exported functions or objects, depends. In my case, a single exported function will do. In the project's C/C++ settings, make the following changes:
Under General, set Consume Windows Runtime Extensions to Yes.
Under General, set Additional #using Directories to: C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcpackages;C:\Program Files (x86)\Windows Kits\10\UnionMetadata (assuming Visual Studio 2015)
Under Code Generation, set Enable Minimal Rebuild to No (it's only Yes for Debug, not for Release).
Then instantiate and use UWP components in the DLL in the usual C++/CX manner, like you would in a Store app, via using namespace Windows::... and ref new.
In this approach, you lose bitness agnosticism; an unmanaged DLL can't be "Any CPU". You win some, you lose some. Also, the site will not run without the Visual C++ redistributable package on the system. On the other hand, it may run faster than a .NET app; less managed/native boundary crossings.
Inspiration: "Using C++/CX in Desktop apps" by Pavel Y.
Open the project file as XML, and paste the following line under the first <PropertyGroup>:
<TargetPlatformVersion>10.0</TargetPlatformVersion>
Once you do that, the Add reference dialog will include UWP libraries, and the file type options in the "Browse..." dialog there will include .winmd.
Load the project, do Add reference/Browse, locate C:\Program Files (x86)\Windows Kits\10\UnionMetadata\Windows.winmd, add that.
There are some helpful extension methods in the managed assembly System.Runtime.WindowsRuntime (e. g. IBuffer.AsStream()), but for some reason, it's not listed under Assemblies. To reference it, you'd need to edit the project file directly, and under the first <ItemGroup>, add the following:
<Reference Include="System.Runtime.WindowsRuntime" />
Unlike the guide states, you don't need to change the compilation target to x86 or x64; leave AnyCPU be.
For desktop .NET applications, this is sufficient. For ASP.NET, however, there's a catch. The way the ASP.NET runtime sets up its AppDomains not compatible with UWP. It's probably a bug deep down, but I've reported it, and a Microsoft rep said the whole thing was not a supported scenario to begin with.
Anyway, you have to change the LoaderOptimization policy of the AppDomain to SingleDomain. The quickest way to do so is via abusing a private method of AppDomain:
AppDomain ad = AppDomain.CurrentDomain;
MethodInfo mi = ad.GetType().GetMethod("SetupLoaderOptimization", BindingFlags.Instance | BindingFlags.NonPublic);
mi.Invoke(ad, new object[] { LoaderOptimization.SingleDomain });
A good place to do that would be in the app startup code.
A slightly less dangerous approach would involve creating a new AppDomain, which would inherit all setup properties from the current one but LoaderOptimization, which will be set to SingleDomain, and running the UWP dependent code in that domain. Like this:
AppDomain CreateUnsharingDomain()
{
AppDomain cad = AppDomain.CurrentDomain;
AppDomainSetup cads = cad.SetupInformation;
return AppDomain.CreateDomain("Dummy", cad.Evidence,
new AppDomainSetup
{
ApplicationName = cads.ApplicationName,
ApplicationBase = cads.ApplicationBase,
DynamicBase = cads.DynamicBase,
CachePath = cads.CachePath,
PrivateBinPath = cads.PrivateBinPath,
ShadowCopyDirectories = cads.ShadowCopyDirectories,
ShadowCopyFiles = cads.ShadowCopyFiles,
ApplicationTrust = cads.ApplicationTrust,
LoaderOptimization = LoaderOptimization.SingleDomain
});
//Not sure which other properties to copy...
}
CreateUnsharingDomain().DoCallBack(MyUWPDependentMethod);
Again, it would make sense to create the domain once and cache it for the app lifetime.
This approach might be faster than the one with the monkey-patched AppDomain. The MultiDomain optimization exists for a reason; if you leave most of the Web code in a MultiDomain world, the optimization will do its work as intended.
Inspiration: "Walkthrough: Using WinRT libraries from a Windows Desktop application" by David Moore.
My company would like to update legacy visual basic 6/classic asp code. We do not have access to a visual basic 6 compiler, so it seemed like a good idea to use Visual Studio 10 and recompile the visual basic 6 code as a VB.NET class. With some minor modifications everything compiled and we registered it as a com object to be called from classic asp.
Now we are trying to access global variables from the asp server. After an extensive search on Google, the only way mentioned was to use System.Web.HttpContext.Current or System.Web.HttpContext.Current.Application . However, this doesn't appear to work.
Here is the code leading up to error. We have imported System and System.Web.
Public Function DoSomethingUseful() As String
Dim objCurrent As System.Web.HttpContext
DoSomethingUseful = Nothing
objCurrent = System.Web.HttpContext.Current
If objCurrent Is Nothing Then
Throw New Exception("No Context.") '<--- THIS EXCEPTION IS THROWN
End If
The problem is we can't get a current instance of the http context and the exception is thrown. Every example I've seem shows that the line objCurrent = System.Web.HttpContext.Current should work but it doesn't. Perhaps it's because we are not using ASP.NET? If not then why isn't it working?
Yes, it is because you're not using ASP.NET.
Despite the similar sounding names, and to a certain degree similar design choices, there is no relationship whatsoever between ASP.NET anything and "Classic" ASP anything. They are completely independent environments.
To access directly the ASP context objects (not the similarly-named ASP.NET context objects which in your application don't even exist), your object must be a registered COM+ object, so:
Your class must inherit from System.EnterpriseServices.ServicedComponent *
On Windows 7/2008 and up, you must enable the "Allow IIS Intrinsic properties" from Component Services, just like you would have to in VB6 (open \Component Services\My Computer\COM+ Applications\YourComPlusApp\Components; select all the objects that need the ASP objects; right-click; click on Properties; go to the Advanced tab).
You can then retrieve the ASP objects (Application, Server, Request, Response, Session) from the COM+ ObjectContext.
Your code would look something like this:
objCurrent = System.EnterpriseServices.ContextUtil.GetNamedProperty("Application")
There are also attributes that you can add to your class to simplify the deployment of the code, but that's a bit outside the scope of this question.
For your reference:
ASP Built-in Objects
ContextUtil.GetNamedProperty Method
* I don't remember 100% if this is a hard requirement. It might actually depend on what the object is actually doing with COM+.
After going through a number of different articles and not finding anything especially conclusive that takes me step-by-step through the process, I've come seeking help.
The Scenario
A client of mine is only proficient in development for ASP Classic. They have recently acquired an account for a site originally written in ASP.NET. They are rolling the site into something they can actively maintain, but the site originally included an image handler that took dynamically changing data regarding water levels and outputs an image containing a graphical representation of that data. The requirement is to develop a COM interop library that can be registered on the server and called with CreateObject to generate the same image's byte array for output using Response.BinaryWrite. The COM interop library must be registered at the remote site on a Windows 2000 Server, and I can't make any assumptions about their having access to regasm/gacutil to accomplish that task.
The Difficulty
I've built the class library by creating a Class Library project in Visual Studio 2010, choosing "COM Class" from the template, and inserting my code to generate a class with a single public method to return a byte array when given an integer (well, enumerator, but all the same). Unfortunately, even on my own development machine after building the library and registering (regasm) and caching the assembly (gacutil), I can't make a call through Classic ASP to instantiate the object, receiving instead an "ActiveX component can't create object" error. And, of course, at the server site, the DLL file can't be registered, the response being "Required module was not found."
Resources I've Used
I've already had a look through the following articles and haven't turned up the answers I need:
(Basic steps) Walkthrough: Creating COM Objects with Visual Basic
Build and Deploy a .NET COM Assembly
.NET COM+ Interop Component with Classic ASP
What I Need
Essentially what I need is a bit of hand-holding on a kind of step by step of what it's going to take to meet the requirements and create a COM+ interop module correctly in Visual Studio 2010. Creating the actual class object itself isn't terribly difficult.
However, none of the articles I've looked through really discuss project options or build procedures with Visual Studio 2010 or the .NET 4.0 Framework, nor have any of them really discussed if there are special considerations for deploying to older systems like Windows Server 2000 and the actual registration of the library on a system with only, say, regsvr32 on hand.
It should be fairly straightforward to get a basic .NET assembly exposed to COM - I've never tried the COM Class project template, so this is the way I've managed it in the past:
Create a new (bog standard) .NET class library using C# or VB. Define a COM interface (replace GUIDs with your own):
[ComVisible(true)]
[Guid("8999F93E-52F6-4E29-BA64-0ADC22A1FB11")]
public interface IComm
{
string GetMyGroups();
}
Now define a class that implements that interface (again, replace GUIDs with your own):
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
[GuidAttribute("C5C5A1A8-9BFB-4CE5-B42C-4E6688F6840B")]
[ProgId("Test.Comm.1")]
public class Comm : IComm
{
public string GetMyGroups()
{
var comm = new CommunicatorAPI.MessengerClass();
var groups = comm.MyGroups as IMessengerGroups;
return string.Join(", ", groups.OfType<IMessengerGroup>().Select(g => g.Name).ToArray());
}
}
The Prog ID attribute on this class is what you will use to instantiate your component from ASP.
Strongly-name the assembly (Project properties -> "Signing" tab -> "Sign the assembly" -> Create a new strong name key file using the dropdown)
Now, build the assembly, and register using Regasm - if you don't wish to register in the GAC (which i'd recommend, as not GACing keeps the deployment simpler), be sure to use the -Codebase parameter (this just adds a reg entry that tells clients where to find the assembly) - e.g:
regasm ClassLibrary2.dll /codebase "S:\Testing\ClassLibrary2\ClassLibrary2\bin\Debug\ClassLibrary2.dll"
Now you should be able to instantiate the component, and call methods on it - for example (in javascript):
var a = new ActiveXObject("Test.Comm.1");
alert(a.GetMyGroups());
When it comes to deployment, the important work that Regasm and Regsvr32 do is to write various settings into the registry, so that clients can find the COM component (based on Prog ID, or COM Class ID). All you need to do is work out what COM settings are being written when you run Regasm on your local machine, and write these to the registry on the server. You can use ProcMon to monitor what gets written to the registry when Regasm is run.
Generally speaking, you can expect to see something like this written to the registry:
[HKEY_CLASSES_ROOT\Test.Comm.1]
#="ClassLibrary2.Comm"
[HKEY_CLASSES_ROOT\Test.Comm.1\CLSID]
#="{00585504-90C8-4760-A359-67CAF08FFED1}"
[HKEY_CLASSES_ROOT\Wow6432Node\CLSID\{00585504-90C8-4760-A359-67CAF08FFED1}]
#="ClassLibrary2.Comm"
[HKEY_CLASSES_ROOT\Wow6432Node\CLSID\{00585504-90C8-4760-A359-67CAF08FFED1}\Implemented Categories]
[HKEY_CLASSES_ROOT\Wow6432Node\CLSID\{00585504-90C8-4760-A359-67CAF08FFED1}\Implemented Categories\{62C8FE65-4EBB-45e7-B440-6E39B2CDBF29}]
[HKEY_CLASSES_ROOT\Wow6432Node\CLSID\{00585504-90C8-4760-A359-67CAF08FFED1}\InprocServer32]
#="mscoree.dll"
"ThreadingModel"="Both"
"Class"="ClassLibrary2.Comm"
"Assembly"="ClassLibrary2, Version=1.0.0.0, Culture=neutral, PublicKeyToken=cf55d4e60653257a"
"RuntimeVersion"="v4.0.30319"
"CodeBase"="file:///S:/Testing/ClassLibrary2/ClassLibrary2/bin/Debug/ClassLibrary2.DLL"
[HKEY_CLASSES_ROOT\Wow6432Node\CLSID\{00585504-90C8-4760-A359-67CAF08FFED1}\InprocServer32\1.0.0.0]
"Class"="ClassLibrary2.Comm"
"Assembly"="ClassLibrary2, Version=1.0.0.0, Culture=neutral, PublicKeyToken=cf55d4e60653257a"
"RuntimeVersion"="v4.0.30319"
"CodeBase"="file:///S:/Testing/ClassLibrary2/ClassLibrary2/bin/Debug/ClassLibrary2.DLL"
[HKEY_CLASSES_ROOT\Wow6432Node\CLSID\{00585504-90C8-4760-A359-67CAF08FFED1}\ProgId]
#="Test.Comm.1"
Hope this helps :)
I have a Classic ASP web site that uses a VB6 COM object. I wanted to create a new version of the COM object using .NET instead of VB6. This is how I did it (hope this helps). I include instructions for both C# and VB.NET.
[01]
Start Visual Studio 2015 (run as admin).
Create a new "Class Library" project.
Name it: "DotNetCom"
[02] C#
Add a new class, name it "HelloCOM".
Use the following code as starting template
( visit https://msdn.microsoft.com/en-us/library/c3fd4a20.aspx for more info )
using System.Runtime.InteropServices;
namespace DotNetCom
{
[Guid("EAA4976A-45C3-4BC5-BC0B-E474F4C3C83F")]
public interface HelloCOMInterface
{
[DispId(1)]
string Hello();
}
[Guid("7BD20046-DF8C-44A6-8F6B-687FAA26FA71"),
InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface HelloCOMEvents
{
}
[Guid("0D53A3E8-E51A-49C7-944E-E72A2064F938"),
ClassInterface(ClassInterfaceType.None),
ComSourceInterfaces(typeof(HelloCOMEvents))]
public class HelloCOM : HelloCOMInterface
{
public string Hello()
{
return "Hello there!";
}
}
}
[02] VB.NET
Add a new "COM class", name it "HelloCOM".
VB.NET creates the starting template.
Add the following function to the "HelloCOM" class.
Public Function Hello() As String
Return "Hello there!"
End Function
[03] C#
Open the project properties.
Go to "Application".
Click "Assembly Information...".
Check "Make assembly COM-Visible"
Go to "Build".
Select "Platform target: x86".
Check "Register COM for interop"
[03] VB.NET
Open "MyProject".
Go to "Compile".
Select "Target CPU: x86".
[04]
Build the "DotNetCom.dll".
[05]
Open a command prompt (run as admin).
Change directory to your dll.
cd DotNetComTest\DotNetComTest\TX7NGN.COM\bin\Debug
Run RegAsm /codebase.
C:\Windows\Microsoft.NET\Framework\v4.0.30319\RegAsm DotNetComTest.dll /codebase "C:\DotNetComTest\DotNetComTest\bin\Debug\DotNetComTest.dll"
[06]
Start Component Services.
Add a new COM+ application.
Name it: "DotNetCom".
Open the "DotNetCom" properties.
Go to the "Security Tab".
UNCHECK "Enforce access checks for this application".
[07]
Add a new component.
Select "DotNetComTest.tlb" (do NOT select "DotNetComTest.dll").
[08]
Use the COM object from the Classic ASP page.
<%
Dim HelloCOM
Set HelloCOM = Server.CreateObject("DotNetCom.HelloCOM")
Response.Write HelloCom.Hello
%>
We're trying to write a couple of applications. One ASP.NET site uses the .NET Assembly xxx.Elements.dll, which provides utility functions.
The other, a Classic ASP site, should also use the same DLL in order to use the same utility functions. (It will be used for encrypting data between the two sites).
Despite many attemps and Googling, we cannot get this working.
We have:
Applied ComVisibility to the Assembly.info and ensured there is a Guid:
[assembly: ComVisible(true)]
[assembly: Guid("ab96fbc3-aa39-4fb6-8628-13778445e503")]
We made sure our type ticks the boxes on type visibility by ensuring it has a default constructor, is ComVisible and has a Guid. We've also created a simplistic method for testing:
namespace xxx.Elements
{
[GuidAttribute("D3BE2C7D-7550-4da1-8F61-6871E193242F")]
[ComVisible(true)]
public class UrlUtility : IUrlUtility
{
public UrlUtility()
{
}
public string Test()
{
return "HellO";
}
}
}
Using the interface:
[GuidAttribute("D3BE2C7D-7550-4da1-8F61-6871E193242A")]
[ComVisible(true)]
public interface IUrlUtility
{
string Test();
}
We found this really useful (though non-resolving) post here to do this: warning MSB3391: does not contain any types that can be unregistered for COM Interop.
We have also checked the Regsiter for COM-Interop in the Project property pages.
This continues to output the warning:
c:\Windows\Microsoft.NET\Framework\v4.0.30319\Microsoft.Common.targets(3341,9): warning MSB3214: "D:\dev\yyy\xxx.Elements\bin\Release\xxx.Elements.dll" does not contain any types that can be registered for COM Interop.
when compiling. If we run REGASM xxx.Elements.dll /tlb directly, we get:
Microsoft (R) .NET Framework Assembly Registration Utility 4.0.30319.1
Copyright (C) Microsoft Corporation 1998-2004. All rights reserved.
Types registered successfully
Assembly exported to 'D:\dev\yyy\IWW.Elements\bin\release\xxx.eleme
nts.tlb', and the type library was registered successfully
So a little confused about the ambiguity.
When we look in the registry, it does appear to register the .tlb correctly:
Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT\TypeLib\{AB96FBC3-AA39-4FB6-8628-13778445E503}]
[HKEY_CLASSES_ROOT\TypeLib\{AB96FBC3-AA39-4FB6-8628-13778445E503}\1.1]
#="Elementary support library for xxx' products"
[HKEY_CLASSES_ROOT\TypeLib\{AB96FBC3-AA39-4FB6-8628-13778445E503}\1.1\0]
[HKEY_CLASSES_ROOT\TypeLib\{AB96FBC3-AA39-4FB6-8628-13778445E503}\1.1\0\win32]
#="D:\\dev\\yyy\\xxx.Elements\\bin\\Release\\xxx.Elements.tlb"
[HKEY_CLASSES_ROOT\TypeLib\{AB96FBC3-AA39-4FB6-8628-13778445E503}\1.1\FLAGS]
#="0"
[HKEY_CLASSES_ROOT\TypeLib\{AB96FBC3-AA39-4FB6-8628-13778445E503}\1.1\HELPDIR]
#="D:\\dev\\yyy\\xxx.Elements\\bin\\Release"
We have also registered the type in the GAC:
gacutil /i xxx.Elements.dll
and given it a strong name using:
sn -k xxx.Elements.snk
and included the name in the AssemblyInfo.cs file:
[assembly: AssemblyKeyFile(#"D:\dev\yyy\xxx.Elements\xxx.Elements.key")]
We have also applied the IUSR user Read permission to the registry keys:
HKEY_CLASSES_ROOT\TypeLib\{AB96FBC3-AA39-4FB6-8628-13778445E503}
HKEY_USERS\S-1-5-20\Software\Microsoft\Windows\CurrentVersion\Internet Settings\Zones
(the last one was suggested here: Cannot instanciate .Net COM object in classic ASP/VBScript page (Error ASP 0177))
Despite all this, when we activate the object in the ASP page using the code:
dim urlUtility
set urlUtility = Server.CreateObject("xxx.Elements.UrlUtility")
' should return "Hello"
test=urlUtility.Test()
The process stops and when we debug into the W3WP process, we get the error:
Server object: 006~ASP 0177~Server.CreateObject Failed~800401f3
We have tried to check all the boxes, but we must be missing something. Any ideas, please?
I had a similar problem some time ago. I solved it by hosting the .NET component in the COM+ application server. This article suggests and describes the same. In addition to the article it maybe necessary to configure your assembly (It is running stand-alone, if you do as suggested). In this case you need a manifest and a config file in the applications root. Look at this SO answer for details.