Convert Excel RTD library to Excel-DNA while retaining =RTD( interface (64 bit Excel) - excel-dna

I have written an IRTDServer in C# and it works fine.
I'd like to convert the code to use Excel DNA (for various reasons, but the most important being that I'd like to have multiple addins loaded each in their own appdomain).
However, in the short term, I'd like to preserve the "old" way of calling, i.e.
=RTD("ProgId",,"MyCoolFunction","Arg1").
I'm using this sample project to get started with:
https://github.com/Excel-DNA/Samples/tree/master/RtdClocks/RtdClock-ExcelRtdServer
I have it working so that I can use =dnaRtdClock_ExcelRtdServer() in Excel and it gives me time updates every few seconds.
Is it going to be possible to call it via =RTD(" ...
If so, am I correct that the function to enter into Excel would be =RTD("RtdClock.ClockServer",,"dummy/ignored").
And then mainly:
What do I need to do to be able to call that same function via =RTD("RtdClock.ClockServer",,"dummy/ignored")
I guess I should also ask a fourth question:
If I am able to successfully access the IRTDServer via the =RTD( function call, will Excel/Excel-DNA be loading the implementing DLL into the FullyTrustedSandbox or the DefaultDomain? [after all, the whole reason I'm doing this conversion to Excel DNA is to get the AppDomain feature].
Things I've tried:
I added a GUID to RTDClockServer project.
I ran %SystemRoot%\Microsoft.Net\Framework64\v4.0.30319\RegAsm.exe RtdClock-ExcelRtdServer.dll /codebase. Initially it complained about ExcelDna.Integration being missing. So I changed the reference to that package to be CopyLocal=True.
I checked the registry. It does have all the COM registration entries that I would expect from registering an RTD server.
I've tried adding =RTD("RtdClock.ClockServerRTD",,) as function call. I see that it does load my DLL and hits a break point, but ONLY the ServerTerminate breakpoint (so, not the ServerStart for example). I also see that the AppDomain in this case is DefaultDomain, not the "FullyTrustedSandBox: ..."
I've tried running c:\windows\system32\regsvr32 against the DLL, against all of the *.xll files in the following directory: [note: I started with the RtdClock-ExcelRtdServer-AddIn64-packed.xll and RtdClock-ExcelRtdServer-AddIn64.xlls as they seemed most logical too me.] These consistently fail silently from the command line, but in the EventViewer I see an error:
Application: regsvr32.exe
Framework Version: v4.0.30319
Description: The process was terminated due to an unhandled exception.
Exception Info: System.InvalidOperationException
at ExcelDna.Integration.RunMacroSynchronization.Register()
at ExcelDna.Integration.SynchronizationWindow..ctor()
at ExcelDna.Integration.SynchronizationManager.Install()
at ExcelDna.Integration.DnaLibrary.Initialize()
at ExcelDna.Integration.DnaLibrary.InitializeRootLibrary(System.String)
Directory of C:\Users\xxxx\source\repos\ExcelDna-Samples\RtdClocks\RtdClock-ExcelRtdServer\bin\Debug
02/01/2020 10:38 <DIR> .
02/01/2020 10:38 <DIR> ..
02/01/2020 10:38 629,248 RtdClock-ExcelRtdServer-AddIn-packed.xll
02/01/2020 08:31 939 RtdClock-ExcelRtdServer-AddIn.dna
09/09/2015 22:49 751,104 RtdClock-ExcelRtdServer-AddIn.xll
02/01/2020 10:38 539,136 RtdClock-ExcelRtdServer-AddIn64-packed.xll
02/01/2020 08:31 939 RtdClock-ExcelRtdServer-AddIn64.dna
09/09/2015 22:49 660,992 RtdClock-ExcelRtdServer-AddIn64.xll
02/01/2020 10:38 6,144 RtdClock-ExcelRtdServer.dll
02/01/2020 10:38 17,920 RtdClock-ExcelRtdServer.pdb
namespace RtdClock_ExcelRtdServer
{
[Guid("2838E6F0-B2CA-4FC9-A9AF-7F834CBC595C")]
[ComVisible(true)] // Required since the default template puts [assembly:ComVisible(false)] in the AssemblyInfo.cs
[ProgId(RtdClockServer.ServerProgId)] // If ProgId is not specified, change the XlCall.RTD call in the wrapper to use namespace + type name (the default ProgId)
public class RtdClockServer : ExcelRtdServer
{
public const string ServerProgId = "RtdClock.ClockServer";
// Using a System.Threading.Time which invokes the callback on a ThreadPool thread
// (normally that would be dangerous for an RTD server, but ExcelRtdServer is thread-safe)
Timer _timer;
List<Topic> _topics;
protected override bool ServerStart()
{
_timer = new Timer(timer_tick, null, 0, 1000);
_topics = new List<Topic>();
return true;
}
Note:
OS Name Microsoft Windows 10 Pro
Version 10.0.18362 Build 18362
Microsoft Excel for Office 365 Version 1911 Build 12228.20364 Click-to-Run Monthly Channel

I think you will need to run RegSvr32.exe RtdClock-ExcelRtdServer-AddIn64-packed.xll or something similar, to register the .xll file as the COM library serving up the RTD server. Doing Regasm.exe on the .dll file makes a mess, causing the COM registration to go against mscorlib which hosts the .dll in the default AppDomain as you see.

Related

MikroORM repository throwing Global Context error when debugging in Visual Studio Code

I am trying to use MikroORM repositories, but I keep getting the Global Context validation error when I use them while debugging in Visual Studio Code. I am using the RequestContext middleware, just as the documentation and demo applications suggest. The error does not occur when I run the application in a terminal window instead of by debugging.
I am currently running Visual Studio Code version 1.68.1 on Windows 10, using Node version 18.2.0.
Error Recreation:
Install the sample application Express + MongoDB + JavaScript from the Mikro-orm Example integrations (https://github.com/mikro-orm/express-js-example-app).
Follow the instructions in the README.md to run the application.
Use Postman (or equivalent) to create an author.
3.1. POST http://localhost:3000/author\
3.2. Body (raw/JSON):
{
"name": "test person",
"email": "test#test.com",
"age": 42
}
Use Postman (or equivalent) to retrieve authors.
4.1. GET http://localhost:3000/author
Copy the id of the author you just created.
Use Postman (or equivalent) to update the author.
6.1. PUT http://localhost:3000/author/<id from step 5.>
6.2. Body (raw/JSON):
{
"name": "test 'success' person"
}
NOTE: This will succeed, proving that everything is configured correctly. I have tried this with both the repository's version of Mikro-orm as well as the current latest version (5.2.3).
Stop the application (Ctrl-C in terminal)
Attach the debugger
8.1. Open the file app\server.js
8.2. Open VS Code's Run and Debug panel.
8.3. Click the "Run and Debug" button.
Use Postman (or equivalent) to update the author.
9.1. PUT http://localhost:3000/author/<id from step 5.>
9.2. Body (raw/JSON):
{
"name": "test 'failure' person"
}
This time the update will fail with the message:
"Using global EntityManager instance methods for context specific
actions is disallowed. If you need to work with the global instance's
identity map, use allowGlobalContext configuration option or
fork() instead."
Within the sample application, the error occurs in author.controller.js, within the router.put('/:id', async (req, res) section, specifically on the line await DI.authorRepository.flush() (line 55). However, experimentation suggests that the error will occur the second time you make any repository call. For example, if you duplicate the findOneOrFail line like this:
const author = await DI.authorRepository.findOneOrFail(req.params.id);
const foo = await DI.authorRepository.findOneOrFail(req.params.id);
the error will occur on the second call to .findOneOrFail.
The error is being thrown in #mikro-orm/core/EntityManager.js, in the getContext method (line 737). When the second call to the repository occurs, getContext is called multiple times. The first time has the following call stack
EntityRepository.flush
MongoEntityRepository.em (get)
EntityManager.getContext
This call is successful.
However, the second call to getContext fails. It has this call stack:
EntityRepository.flush
EntityManager.flush
EntityManager.getUnitOfWork
EntityManager.getContext
The issue seems to be that between the two calls to getContext, em._id goes from not 1 to 1. It looks to me like the EntityManager with an _id of 1 is the global context we shouldn't be using. However, when you run the code without debugging, em._id is not 1 during any of these calls.
I have not been able to figure out why the context is not retrieving the correct entity manager when in debugging mode.

Wifi WPS client start in Windows 10 in script or code

I can not find how to start WPS client in Windows 10 from command prompt or powershell. When I used Linux, everything was really ease with wla_supplicant (wpa_cli wps_pbc). Is there something similar in Windows?
Does anyone know how to set up Wi-Fi network (over WPS) key without human input in Windows?
I also tried WCN (Windows Connect Now) from Microsoft as it implements WPS features. I got also samples from Windows SDK on WCN, but they could not get key by WPS (it faild). But if I use Windows user interface to connect wiothout PIN, everyting seems to be pretty fine.
I am sure that there is possibility to do that, it is very important to perform Wifi Protected Setup by button start from the command prompt or app (C++/C#) without human intrusion or input (once WPS is on air, Windows should automatically get the network key and connect then).
I don't know if it's too late to answer, just put what I know in here and hope it can help.
First, if your system has updated to 16299(Fall Creator Update), you can just simply use new wifi api from UWP.
Install newest Windows SDK, create a C# console project, target C# version to at least 7.1, then add two reference to the project.
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework.NETCore\v4.5\System.Runtime.WindowsRuntime.dll
C:\Program Files (x86)\Windows Kits\10\UnionMetadata\10.0.16299.0\Windows.winmd
After all of that , code in below should work.
using System;
using System.Threading.Tasks;
using Windows.Devices.Enumeration;
using Windows.Devices.WiFi;
class Program
{
static async Task Main(string[] args)
{
var dic = await DeviceInformation.FindAllAsync(WiFiAdapter.GetDeviceSelector());
if (dic.Count > 0)
{
var adapter = await WiFiAdapter.FromIdAsync(dic[0].Id);
foreach (var an in adapter.NetworkReport.AvailableNetworks)
{
if (an.Ssid == "Ssid which you want to connect to.")
{
// Fouth parameter which is ssid can not be set to null even if we provided
// first one, or an exception will be thrown.
await adapter.ConnectAsync(an, WiFiReconnectionKind.Manual, null, "",
WiFiConnectionMethod.WpsPushButton);
}
}
}
}
}
Build and run the exe, then push your router's button, your pc will be connect to the router.
But if you can not update to 16299, WCN will be your only choice. You may already notice that if call IWCNDevic::Connect frist with push-button method, the WSC(Wifi Simple Configuration) session will fail. That's because WNC would not start a push-button session as a enrollee, but only as a registrar. That means you have to ensure that router's button has been pushed before you call IWCNDevic::Connect. The way to do that is using Native Wifi api to scan your router repeatedly, analyse the newest WSC information element from the scan result, confirm that Selected Registrar attribute has been set to true and Device Password Id attribute has been set to 4. After that, query the IWCNDevice and call Connect function will succeed. Then you can call IWCNDevice::GetNetworkProfile to get a profile that can use to connect to the router. Because it's too much of code, I will only list the main wifi api that will be used.
WlanEnuminterfaces: Use to get a available wifi interface.
WlanRegisterNotification: Use to register a callback to handle scan an connect results.
WlanScan: Use to scan a specified wifi BSS.
WlanGetNetworkBsslist: Use to get newest BSS information after scan.
WlanSetProfile: Use to save profile for a BSS.
WlanConnect: Use to connect to a BSS.
And about the WSC information element and it's attributes, you can find all the information from Wi-Fi Simple Configuration Technical Specification v2.0.5.
For Krisz. About timeout.
You can't cast IAsyncOperation to Task directly. The right way to do that is using AsTask method. And also, you should cancel ConnectAsync after timeout.
Sample code:
var t = adapter.ConnectAsync(an, WiFiReconnectionKind.Manual, null, "",
WiFiConnectionMethod.WpsPushButton).AsTask();
if (!t.Wait(10000))
t.AsAsyncOperation().Cancel();

Why is MdiActiveDocument null in the beginning in AutoCAD 2015+?

I am working in AutoCAD 2014 using Visual Studio 2013.
With my code I access the MdiActiveDocument's database from the DocumentManager .
Using the database I start a transaction and use the GetObject method of the transaction to retrieve Entity objects.
Database acCurDb = Application.DocumentManager.MdiActiveDocument.Database;
using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
{
var obj = acTrans.GetObject(id, OpenMode.ForRead);
if (obj is Entity)
{
// do stuff
}
acTrans.Commit();
}
This works fine while I am in development and start AutoCAD from inside of Visual Studio. In development I set the "Start external program" switch in the Debug tab of the application properties so it starts AutoCAD for me and everything works great.
The issue I am having is that in production when the app is loaded by AutoCAD via registry settings (I use demand loading) the MdiActiveDocument is null so the code crashes. I have discovered there is a document in the Application.DocumentManager but when I assign the database from that document to acCurDb the TransactionManager throws an error with I try to use the StartTransaction method.
if (Application.DocumentManager.Count > 0)
{
foreach(Document doc in Application.DocumentManager)
{
acCurDb = doc.Database;
break;
}
}
using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
{
}
Can someone help me understand why the MdiActiveDocument is null and/or direct me to the proper way to get a Transaction object in AutoCAD?
Beginning in 2015, AutoCAD may have a null active document on startup depending on user system variables. It's just another check you have to add before running your routine.
As mentioned by #david-wolfe, AutoCAD 2015 may start with no active document (just a dashboard). In this case the MdiActiveDocument can be null.
Now you're on AutoCAD 2014, so a different scenario may happen: if your app is loading with AutoCAD, you code may run before anything is really ready. How are you running the code? Is it a CommandMethod? If is a command, the user can only run it from a command, so it will be a active document. But if you run it from other method (like direct call from the Ribbon or from a palette), it may be null.

V8 "Platform" is null

I'm calling "Isolate->IdleNotification(100)" within an isolate scope (via "v8::Isolate::Scope ..."), and at some point when "V8::GetCurrentPlatform()->CallOnBackgroundThread(...)" is called within V8, "V8::GetCurrentPlatform()" returns NULL, and the whole thing dies a fatal death. Any ideas why the current platform might be null? Or rather, what should be done to make sure it isn't? Everything else seems to be working just fine.
Addition details: I'm using Visual Studio 2013, and compiled the source, the resulting libraries of which I use with the V8.Net wrapper (on codeplex). I run the following code before triggering the idle notification call:
v8::Locker __lockScope(engine->Isolate());
v8::Isolate::Scope __isolateScope(engine->Isolate());
v8::HandleScope __handleScope(Isolate::GetCurrent());
I tried different combinations, and it seems only v8::Locker __lockScope is required, but the platform is still null;
v8::Platform* V8::GetCurrentPlatform() {
DCHECK(platform_); // <-- 'platform_' is NULL
return platform_;
}
Got this working shortly after, but forgot to post the answer. Here it is: Google changed stuff (surprise! :/), and now you have to initialize a platform first. I did this and it all works again:
v8::V8::InitializePlatform(v8::platform::CreateDefaultPlatform());
// Sets the v8::Platform to use. This should be invoked before V8 is initialized.
v8::V8::InitializeICU();
// Initialize the ICU library bundled with V8. (if using the bundled ICU)
v8::V8::Initialize();
// Initialize V8.

Tracing CSP calls within Windows Crypto API

I am working on Private Key archival with a Windows Server 2008 R2 Certificate Authority. On the client side, I am interested in knowing which Crypto API function calls are made by the windows processes when the client requests for an archival enabled certificate. In particular, my focus is to track the function calls listed here, http://msdn.microsoft.com/en-us/library/aa922849.aspx , present in Advapi32.dll .
I have tried out the windbg/cdb script here ( http://blogs.msdn.com/b/alejacma/archive/2007/10/31/cryptoapi-tracer.aspx ). I attached it to mmc.exe while making a certificate request but I couldn't detect any CSP calls being made during the process. I also tried requesting certificate through certreq.exe but windbg couldn't trace any CSP calls. I have also tried other forms of tracing as mentioned in the stackoverflow thread titled "Monitoring application calls to DLL"
Please tell me which windows process/service I need to attach to, in order to find how these calls are being made. Is it possible to trace whenever a function call is made, irrespective of which process makes it?
Any suggestions on how to trace these CSP calls are most welcome!
advapi32.dll used to contain cryptography implementation some time ago, but now this functionality has been moved to cryptsp.dll. Windows team sometimes moves implementation of public methods from one DLL into another. See more examples on this in The Old New Thing Blog. Advapi32.dll simply calls into corresponding functions in cryptsp.dll.
Apparently certificate APIs in mmc that you were trying to debug are calling directly into cryptsp.dll. Example from a call stack on Windows 8:
0:000> k
Child-SP RetAddr Call Site
00000000`0059c278 000007fd`6c1b7d8b CRYPTSP!CryptAcquireContextA
00000000`0059c280 000007fd`6c1ace66 CRYPT32!I_CryptGetDefaultCryptProv+0xbc
00000000`0059c2d0 000007fd`6c1ae1b3 CRYPT32!FastCreateCtlElement+0x4a6
00000000`0059c4e0 000007fd`6c1a248a CRYPT32!CreateCtlElement+0x23
00000000`0059c530 000007fd`6c1a2297 CRYPT32!CreateStoreElement+0x139
00000000`0059c610 000007fd`6c1abaa4 CRYPT32!LoadStoreElement+0x244
00000000`0059c6f0 000007fd`6c1a2c76 CRYPT32!OpenFromRegistry+0x39e
00000000`0059c950 000007fd`6c1a2e7c CRYPT32!OpenAllFromRegistryEx+0x96
00000000`0059c9d0 000007fd`6c1a394b CRYPT32!I_CertDllOpenRegStoreProv+0xfc
00000000`0059ca20 000007fd`6c196926 CRYPT32!I_CertDllOpenSystemRegistryStoreProvW+0x28b
00000000`0059cb20 000007fd`6c1a3b72 CRYPT32!CertOpenStore+0x296
00000000`0059cba0 000007fd`6c1a3dc2 CRYPT32!OpenPhysicalStoreCallback+0xc2
00000000`0059cc70 000007fd`6c1a4512 CRYPT32!EnumPhysicalStore+0x648
00000000`0059ce00 000007fd`6c196926 CRYPT32!I_CertDllOpenSystemStoreProvW+0x162
00000000`0059cee0 000007fd`6c1a3b72 CRYPT32!CertOpenStore+0x296
00000000`0059cf60 000007fd`6c1a3dc2 CRYPT32!OpenPhysicalStoreCallback+0xc2
00000000`0059d030 000007fd`6c1a4512 CRYPT32!EnumPhysicalStore+0x648
00000000`0059d1c0 000007fd`6c196926 CRYPT32!I_CertDllOpenSystemStoreProvW+0x162
00000000`0059d2a0 000007fd`47371a27 CRYPT32!CertOpenStore+0x296
00000000`0059d320 000007fd`47363611 certmgr!CCertStore::GetStoreHandle+0xc7
Notice, advapi32.dll is not even present in the call stack.
So the solution for you would be to put breakpoints directly on the functions in cryptsp.dll. E.g.:
> bu CRYPTSP!CryptAcquireContextW
> bu CRYPTSP!CryptAcquireContextA
> bu CRYPTSP!CryptDecrypt
> ... and so on ...
> g

Resources