Subscribe to this thread
Home - General / All posts - VS 2017 C# app
dmbrubac


1,620 post(s)
#23-Jan-19 01:39

I can't seem to get off the ground with a C# (.NET Framework 4.6.2) project that needs to use some mapping data.

I have 9.0.168 on my Win 10 system, along with VS 2017. My project has a class library that references ExtNet.dll and a simple bit of code to make sure the API starts ok.

using Manifold

...

            var root = new Root(@"C:\Program Files\Manifold\v9.0\Bin");

var application = root.Application;

var _Tech = application.GetTechnologyForFile(@"C:\Users\...\Test.map"true); 

This code accesses the 32-bit version but I've also made a 64 bit test project and the results are the same. Yes the map exists and can be opened and yes the '...' is there on purpose for this post.

In all cases I get an Invalid API Operation exception on the call to GetTechnologyForFile. I can also replace the _Tech line with

var db = application.CreateDatabase();

which should just give me a new temp file - same exception. I have stepped into the code and both root and application seem to be created correctly (not surprising, since I get no exceptions on the previous lines).

I feel like I'm missing something really obvious.


Don't expect, suggest!

tjhb
10,094 post(s)
#23-Jan-19 04:31

Adam's model here.

Oh, beta thread. Back soon.

This thread is public.

The [STAThread] attribute is required.

tjhb
10,094 post(s)
#23-Jan-19 08:27

I probably got the wrong end of the stick.

dmbrubac


1,620 post(s)
#23-Jan-19 15:40

Such high hopes...

I copy/pasted Adams example from the public thread and changed the path to Ext.dll to match my system. The Invalid API Operation occurs at app.CreateDatabase().

  • Added [STAThread] (thanks tjhb - it's been a while since I've been here!)
  • I specified Ext.dll (thanks rk)
  • Path cannot be left blank - there is no constructor override as documented
  • var or explicit types makes no difference, as expected

Either something has changed, I have an install issue with Manifold, VS or both or who knows...

I have to go out, but will check back later.


Don't expect, suggest!

rk
621 post(s)
#23-Jan-19 09:17

When creating root, we must pass path to ext.dll

var root = new Root(@"c:\Program Files\Manifold\v9.0\Bin\ext.dll");

tjhb
10,094 post(s)
#23-Jan-19 10:18

Or we can leave the path blank, and Manfold will check well-known locations. Including path but not filename seems a hybrid, but David says no errors on creating Root()...

Is it safe to rely on type inference in this situation? How about replacing var with explicit types?

rk
621 post(s)
#23-Jan-19 13:48

Invalid API operation is the first error that happens. Root is not null, Applicator is not null. Any further call raises exception.

Been there. Perhaps we could compose a short troubleshooting list.

  • Error
  • Probable cause
  • Solution

I have handful of cases in mind.

tjhb
10,094 post(s)
#23-Jan-19 20:37

This sounds good. I could learn a lot here.

We could take advantage of C# interactive for this. Step by simple step, with inline testing. Easy to post a command sequence or interactive session for direct comparison. No setup.

Here is a session in Visual Studio 2017 (15.9.5), Windows 10 version 1809 x64.

Interactive checks and responses at each step are included in comments.

C# interactive uses STA threading model (checked inline below).

#r "System.dll"

#r "D:\manifold-9.0.168.7-x64\bin64\extnet.dll"

    // #r "C:\Program Files\Manifold\v9.0\Bin64\extnet.dll"

    // #r "C:\Program Files\Manifold\v9.0\Bin\extnet.dll"

 

using System;

    // using System.Collections.Generic;

    // using System.Linq;

    // using System.Text;

    // using System.Threading;

    // using System.Threading.Tasks;

using Manifold;

 

// check COM threading model for current thread

System.Threading.ApartmentState state = System.Threading.Thread.CurrentThread.GetApartmentState();

    // state ->

    // STA

 

Manifold.Root root = new Manifold.Root(@"D:\manifold-9.0.168.7-x64\bin64\ext.dll");

    // root ->

    // Root { Application=Application { Name="Manifold System" } }

Manifold.Application app = root.Application;

    // app ->

    // Application { Name="Manifold System" }

Manifold.Database db = app.CreateDatabase();

    // db ->

    // Database {

        // CanDelete=true, CanDesign=true, CanInsert=true, 

        // CanMigrate=false, CanRefresh=false, CanRename=true, 

        // CanRun=true, CanSave=true, CanSaveCompact=false, 

        // Connection="", IsMigrateNeeded=false, IsReadOnly=false, 

        // IsSaveAsNeeded=false, IsSaveCompactNeeded=false, IsSaveNeeded=true, 

        // Technology="manifold", TechnologyResolved=null 

        // }

        // line breaks added

 

Manifold.Table table = db.Search("mfd_root");

    // table ->

    // Table { 

        // CanDelete=false, CanDesign=false, CanGetVersions=true, 

        // CanInsert=false, CanRename=false, CanUpdate=false 

        // }

        // line breaks added

Console.WriteLine("mfd_root contains {0} fields", table.GetSchema().Fields.Count);

    // ->

    // mfd_root contains 3 fields

 

string tech = app.GetTechnologyForFile(@"D:\Test.map", true);

    // tech ->

    // "manifold"

 

table.Dispose();

db.Dispose();

root.Dispose();

Attachments:
20190124 C# interactive m9.txt

tjhb
10,094 post(s)
#23-Jan-19 20:57

When something works in C# interactive but not in compiled code, that will be useful for troubleshooting.

dmbrubac


1,620 post(s)
#24-Jan-19 15:28

+1 for reminding me of C# interactive!

Good news / bad news... My interactive session completed with no errors - exactly like yours. Now to figure out what's going on with real the applications.


Don't expect, suggest!

dmbrubac


1,620 post(s)
#24-Jan-19 15:51

Turns out it didn't like living in a class library. The test code in the main app works fine.


Don't expect, suggest!

tjhb
10,094 post(s)
#24-Jan-19 22:51

Great. If you find out why--what to avoid, how to avoid it--can you post back.

By the way, AFAIK I misused Dispose() in the C# interactive example above. It is incorrect (and ineffective) to use it this way.

I should ahev either not worried about proper disposal (this is interactive after all, it is more or less a sandbox), or wrapped the resources in using blocks, which do work in interactive (although it does reduce the interactivity or immediacy).

As far as I can tell, it's not possible to (correctly) use the alternative syntax in interactive: wrapping working code in a try block, then calling Dispose() inside a finally block. I get no execution at all using try blocks in interactive.

dmbrubac


1,620 post(s)
#25-Jan-19 04:54

Yeah I figured the Dispose stuff was unnecessary and have been 'using' all the time with expensive operations.

I'm still not sure why it didn't like being called in a class library but will definitely post what I learn when I learn it. I suspect it's to do with threading. Adam may also pop by and effortlessly enlighten us. Until I figure it out I probably can't do unit testing, so I'm motivated for sure.


Don't expect, suggest!

tjhb
10,094 post(s)
#25-Jan-19 05:38

Thanks.

I'm finally enjoying learning C#, and dialog such as this greatly helps.

Jeffrey Richter's "CLR via C#" turned up in the mail this week, and I finally feel I can explore the wood, not just look at trees.

dmbrubac


1,620 post(s)
#25-Jan-19 14:42

I keep C# in a Nutshell (O'Reilly) close at hand. I've also found that giving myself over completely to test-driven development, dependency injection, inversion of control, MVVM, etc. has made coding much more enjoyable. Sometimes if feels like you have to do a bunch of work to get started, but compared to the time spent later it's not much, especially when you refactor something to meet a new requirement, run your tests and know all the old stuff still works (or at least passes the tests!)


Don't expect, suggest!

tjhb
10,094 post(s)
#25-Jan-19 21:33

I hate the OO paradigm, and am really happy that future C# is embracing functional programming. This is great for anyone SQL.

Lists like "test-driven development, dependency injection, inversion of control, MVVM, etc." are I think just a passing fashion. I know I might be be partly wrong about this.

The functional paradigm needs roughly none of it.

dmbrubac


1,620 post(s)
#26-Jan-19 15:49

I feel exactly the same except opposite. I think in OO terms very easily. The MVVM pattern separates the concerns of 'data' and 'doing something with/to data' very nicely. Also, to suggest you don't need to test functional programs is perhaps a spot where you are partly wrong.

Anyway, it hardly matters. If you can do what you need with a functional paradigm and I can do it in an OO paradigm then it's all good.


Don't expect, suggest!

adamw


10,447 post(s)
#31-Jan-19 09:54

Two ideas:

First, the failure might be related to the code being bit-neutral, thus launching 64-bit, but the path passed to Root being to the 32-bit EXT.DLL ('...Bin...' instead of '...Bin64...').

Second, if the failure indeed seems related to being in a class library, perhaps whoever loads the library already initialized COM into an MTA (whether explicitly or implicitly) before the execution even gets to the library.

dmbrubac


1,620 post(s)
#31-Jan-19 15:42

Hi Adam

It's definitely not a 'bit-neutral' issue. I've been careful to be explicit in VS Config Manager, the project properties and the path to the DLL to ensure I'm only working with x86 or x64 as appropriate. No 'Any CPU' allowed.

I think you are right though about an implicit MTA initialization. That makes a lot of sense. Thanks!

Any idea when the apartment threading requirement might go away?


Don't expect, suggest!

adamw


10,447 post(s)
#01-Feb-19 08:18

We need STA for system services like the clipboard. That said, we might try allowing the script to start in an MTA and disable these services. We will look into it. (No time frame, but we have a couple of big features touching scripts coming up relatively soon, so we'll perhaps look into it then.)

Manifold User Community Use Agreement Copyright (C) 2007-2021 Manifold Software Limited. All rights reserved.