Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
User0015
Nov 24, 2007

Please don't talk about your sexuality unless it serves the ~narrative~!

Nth Doctor posted:

:yeah:
but a Type key FK'd to a Type table because ~*normalization*~

This is likely what I'm doing to do after giving it some more thought. I don't know how good/garbage fire EF is, but I will want to be getting lists of "Type" numbering in the thousands, so here's to hoping EF will properly generate sql for rapid lookup if I do so.


kloa posted:

Do you need a Type field at all? Or can you just use MimeMapping on the FileName to determine it's type when it's being accessed?

Users will be able to upload files, so this would be a very bad idea.


I should have just used Dapper and SQLite and wrote my own queries. Hah.

Adbot
ADBOT LOVES YOU

Munkeymon
Aug 14, 2003

Motherfucker's got an
armor-piercing crowbar! Rigoddamndicu𝜆ous.



User0015 posted:

Users will be able to upload files, so this would be a very bad idea.

Users can lie to you no matter how you ask them what type it is, up to and including crafting a file maliciously to get around a heuristic-using tool that's entirely under your control. It's much easier to whitelist the MIME types you accept and return to exactly the same type, so if someone uploads NOTAVIRUS.txt.exe as text/plain, they just get junk with a MIME type of text/plain back and nobody gets hurt directly by your CMS.

User0015
Nov 24, 2007

Please don't talk about your sexuality unless it serves the ~narrative~!

Munkeymon posted:

Users can lie to you no matter how you ask them what type it is, up to and including crafting a file maliciously to get around a heuristic-using tool that's entirely under your control. It's much easier to whitelist the MIME types you accept and return to exactly the same type, so if someone uploads NOTAVIRUS.txt.exe as text/plain, they just get junk with a MIME type of text/plain back and nobody gets hurt directly by your CMS.

I originally had it set to upload as form data, so this wouldn't be possible. Maybe changing that will make this a little easier to handle, so that's next. For reference, the front end is angular. I'll just offer upload types since there doesn't seem to be a better way to do it, and another user said it's a bad idea to use inheritance on DB objects anyways.

Munkeymon
Aug 14, 2003

Motherfucker's got an
armor-piercing crowbar! Rigoddamndicu𝜆ous.



User0015 posted:

I originally had it set to upload as form data, so this wouldn't be possible. Maybe changing that will make this a little easier to handle, so that's next. For reference, the front end is angular. I'll just offer upload types since there doesn't seem to be a better way to do it, and another user said it's a bad idea to use inheritance on DB objects anyways.

I'm not talking about anything having to do with the database schema, just that trying to police file types users can upload is basically futile.

beuges
Jul 4, 2005
fluffy bunny butterfly broomstick

hackbunny posted:

Initially, yes, but I don't want to get hamstrung by it as the application becomes more complex


It's not the customizations I'm worried about (the longer I can avoid them, the better), it's more the fact that I won't get platform ifdefs with a .NET Standard project, if I understand correctly. Is there an easy way out, should I start with a .NET Standard project but then realize I need platform-specific code?

What are you planning on putting in your platform-specific ifdef though? If you just want to change some behaviour based on whether you're on iOS or Android, e.g. to sort out layout/look-and-feel differences between the two, then you can just do
code:
if (Device.RuntimePlatform == Device.iOS) { } else { }

It'll bloat the binary by a few kb or so, but that's not the end of the world. If you actually want to invoke something platform-specific that doesn't exist in the platform-agnostic netstandard shared library, then you create an IPlatformSpecificStuff interface which you inject into the netstandard shared library.
Also, James Montemagno has probably written a Xamarin plugin for whatever platform-specific behaviour you'd want to invoke, so just browse his GitHub for the solution to most of your problems.

hackbunny
Jul 22, 2007

I haven't been on SA for years but the person who gave me my previous av as a joke felt guilty for doing so and decided to get me a non-shitty av

beuges posted:

What are you planning on putting in your platform-specific ifdef though?

No idea but I've written multiplatform software for a decade and something always comes up

beuges posted:

It'll bloat the binary by a few kb or so, but that's not the end of the world. If you actually want to invoke something platform-specific that doesn't exist in the platform-agnostic netstandard shared library, then you create an IPlatformSpecificStuff interface which you inject into the netstandard shared library.

Where does the implementation come from, though? I haven't written a lot of .NET software and it's always been monolithic, so I don't know a lot about componentization

beuges posted:

Also, James Montemagno has probably written a Xamarin plugin for whatever platform-specific behaviour you'd want to invoke, so just browse his GitHub for the solution to most of your problems.

I will

beuges
Jul 4, 2005
fluffy bunny butterfly broomstick

hackbunny posted:

Where does the implementation come from, though? I haven't written a lot of .NET software and it's always been monolithic, so I don't know a lot about componentization

A netstandard Xamarin Forms application consists of a netstandard (cross platform) common "app" library C# project and platform-specific iOS, Android and UWP C# application projects. So you'd have Hackbunny.App (shared stuff), and HackBunny.iOS and HackBunny.Android projects.
The .iOS and .Android projects are platform-specific, and are the actual application projects (think of them as the .exe for each platform). These tend to do very little, aside from platform-specific initialization, and also contain any platform-specific customizations of whatever that you need. Their main purpose is to instantiate an App object that lives inside the .App dll, which implements the actual app workflow. You define a set of Page-derived classes which contain the screens in your app, and set one of them as the initial page. From there on, you just write C# against the Xamarin Forms libraries.

Now, if you want to do things differently based on what device you're on, it would basically boil down to one of two things - either you just want to override some behaviour based on which platform you're on, e.g. centralize on one and left-align on another, or you want to actually invoke some functionality that does exist on either (or both) devices but isn't in the cross-platform libraries yet.

If its the first, you just go 'if Device.RuntimePlatform == Device.iOS) { alignment = center } else { alignment = left }' or whatever.

If it's the second, you now need to actually talk to iOS and Android directly to do what you want. Xamarin definitely allows you to call the iOS and Android native APIs directly from C# - that's what it was before they introduced Xamarin Forms. But the netstandard shared HackBunny.App.dll doesn't have references to the iOS API or Android API, because it's referenced by both platform-specific projects, so it has to be neutral.
So you create an 'IPlatformSpecific' interface, which you pass to the App() constructor in the .App project. Then, in the iOS project, you create an iOSPlatformSpecific implementation of IPlatformSpecific, and pass that into the App() constructor. Similarly for the Android side. So, the platform-agnostic .App project just has a reference to an interface that has a method called ReadAddressBook() or whatever, which you can then call from your shared code. The actual job of reading the iOS address book is implemented in HackBunny.iOS.iOSPlatformSpecific.ReadAddressBook(), which lives in the iOS platform-specific project and therefore is allowed to talk to the iOS API directly, and when you link against the Android project, the shared code is now talking to HackBunny.Android.AndroidPlatformSpecific.ReadAddressbook() via the same IPlatformSpecific interface.

downout
Jul 6, 2009

User0015 posted:

This is likely what I'm doing to do after giving it some more thought. I don't know how good/garbage fire EF is, but I will want to be getting lists of "Type" numbering in the thousands, so here's to hoping EF will properly generate sql for rapid lookup if I do so.


Users will be able to upload files, so this would be a very bad idea.


I should have just used Dapper and SQLite and wrote my own queries. Hah.

Fortunately, in my experience, EF creates decent queries, especially as this isn't particularly complex. It's a good use of a column and could easily have an index built on the table, although I suspect you won't see issues.

qsvui
Aug 23, 2003
some crazy thing
I ask because I've just been bitten by this, but what is the use case for calling a virtual method from within a constructor?

Red Mike
Jul 11, 2011
If you need to offer some customisation to the constructor, but need to keep most of it the same (and/or private so not accessible to subclasses). Example:


ClassName constructor calls method A, then calls method B. You want method A to be override-able, but method B is sealed and marked as private since you shouldn't be able to mess with it when subclassing.

Mark method A as virtual, method B is sealed by default and private.

If someone implements SubClassName : ClassName, they can't try to make their own constructor that calls their new method C instead of A, then calls method B, because method B is not accessible in the first place. If method B uses private members, they can't even reimplement method B as method D.

Literally the only thing they can do short of reimplementing 90% of the base functionality with new members is have their constructor call base() and override method A. The base class then calls their implementation instead.

hackbunny
Jul 22, 2007

I haven't been on SA for years but the person who gave me my previous av as a joke felt guilty for doing so and decided to get me a non-shitty av

beuges posted:

A netstandard Xamarin Forms application consists of a netstandard (cross platform) common "app" library C# project and platform-specific iOS, Android and UWP C# application projects. So you'd have Hackbunny.App (shared stuff), and HackBunny.iOS and HackBunny.Android projects.

Oooh I thought only "shared project" applications were structured like that. It's fine then, I know how to handle it

beuges posted:

The .iOS and .Android projects are platform-specific, and are the actual application projects (think of them as the .exe for each platform). These tend to do very little, aside from platform-specific initialization, and also contain any platform-specific customizations of whatever that you need. Their main purpose is to instantiate an App object that lives inside the .App dll, which implements the actual app workflow. You define a set of Page-derived classes which contain the screens in your app, and set one of them as the initial page. From there on, you just write C# against the Xamarin Forms libraries.

Thank you for the overview. Sorry I made you explain platform-specific code in that much detail, I was under the mistaken impression that all code (not just most of it) had to target netstandard. I'm quite familiar with cross-platform libraries implementing platform-specific behavior through interfaces implemented by applications... seeing as I've written my fair share of them

Faith For Two
Aug 27, 2015
I currently have a c++ dll that does a bunch of error logging using std::cout. I have a windows.forms application, and I want to display the dll's logging stuff to the user. Right now it displays std::cout's text in the visual studio "Show output from: Debug" window. How do I direct cout text to a System.Windows.Forms textbox.AppendText(/*???*/)? or something similar?

code:
// program.cs
namespace This_Is_A_Test
{
	class Program
	{
        [DllImport("C:\\Stuff\\my_dll.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern void cpp_dll_init();
	

	//I know this example is a console application instead of a windows.forms application but I don't have any code in front of me to work with right now
	static unsafe void Main(string[] args)
        {
		cpp_dll_init();
	}
	
	}
}
code:
// my_dll.cpp

void cpp_dll_init()
{
	if(auto err = weird_api_call();
	    err != API_SUCCESS)
	{
		std::cout << "weird_api_call failed with code: " << err << "\n";
	}
	//... more code sorta like this ^ 
}

Bruegels Fuckbooks
Sep 14, 2004

Now, listen - I know the two of you are very different from each other in a lot of ways, but you have to understand that as far as Grandpa's concerned, you're both pieces of shit! Yeah. I can prove it mathematically.

Faith For Two posted:

I currently have a c++ dll that does a bunch of error logging using std::cout. I have a windows.forms application, and I want to display the dll's logging stuff to the user. Right now it displays std::cout's text in the visual studio "Show output from: Debug" window. How do I direct cout text to a System.Windows.Forms textbox.AppendText(/*???*/)? or something similar?

Do you own the C++ dll (can you make changes)?

Mongolian Queef
May 6, 2004

Faith For Two posted:

I currently have a c++ dll that does a bunch of error logging using std::cout. I have a windows.forms application, and I want to display the dll's logging stuff to the user. Right now it displays std::cout's text in the visual studio "Show output from: Debug" window. How do I direct cout text to a System.Windows.Forms textbox.AppendText(/*???*/)? or something similar?

System.Diagnostics.Process.GetCurrentProcess() has streams you can read from, though I haven't tested anything similar to what you want.

Faith For Two
Aug 27, 2015

Bruegels Fuckbooks posted:

Do you own the C++ dll (can you make changes)?

Yes. I am the author of the dll.

Bruegels Fuckbooks
Sep 14, 2004

Now, listen - I know the two of you are very different from each other in a lot of ways, but you have to understand that as far as Grandpa's concerned, you're both pieces of shit! Yeah. I can prove it mathematically.

Faith For Two posted:

Yes. I am the author of the dll.

the no dll change way of doing this is to wrap the dll in an EXE, and call that from the C# - that will let you read the console output. that might be appropriate for your use case.

if you can change the dll and want to keep the integration a dll call logging to file and having the c# watch the file for changes is probably better.

if you want something more integrated, you could override the behavior of cout. here's a quick lovely example that won't work correctly with unicode:
code:
#pragma once

#include <iostream>
#include <Windows.h>

class debugStreambuf : public std::streambuf {
public:
    virtual int_type overflow(int_type c = EOF) {
        if (c != EOF) {
            TCHAR buf[] = { c, '\0' };
            OutputDebugString(buf);
        }
        return c;
    }
};

class CoutDebugString{

    debugStreambuf dbgstream;
    std::streambuf *default_stream;

public:
    CoutDebugString() {
        default_stream = std::cout.rdbuf(&dbgstream);
    }

    ~CoutDebugString() {
        std::cout.rdbuf(default_stream);
    }
};
if you call that class cout will start logging to the windows outputdebugstring api - you could change it to like, send a message to your window with the text of the cout call, and make the project into a c++/cli project. is it a good idea? i can't say i would recommend it.

hackbunny
Jul 22, 2007

I haven't been on SA for years but the person who gave me my previous av as a joke felt guilty for doing so and decided to get me a non-shitty av

Bruegels Fuckbooks posted:

the no dll change way of doing this is to wrap the dll in an EXE, and call that from the C# - that will let you read the console output. that might be appropriate for your use case.

if you can change the dll and want to keep the integration a dll call logging to file and having the c# watch the file for changes is probably better.

if you want something more integrated, you could override the behavior of cout. here's a quick lovely example that won't work correctly with unicode:
code:
#pragma once

#include <iostream>
#include <Windows.h>

class debugStreambuf : public std::streambuf {
public:
    virtual int_type overflow(int_type c = EOF) {
        if (c != EOF) {
            TCHAR buf[] = { c, '\0' };
            OutputDebugString(buf);
        }
        return c;
    }
};

class CoutDebugString{

    debugStreambuf dbgstream;
    std::streambuf *default_stream;

public:
    CoutDebugString() {
        default_stream = std::cout.rdbuf(&dbgstream);
    }

    ~CoutDebugString() {
        std::cout.rdbuf(default_stream);
    }
};
if you call that class cout will start logging to the windows outputdebugstring api - you could change it to like, send a message to your window with the text of the cout call, and make the project into a c++/cli project. is it a good idea? i can't say i would recommend it.

What the gently caress is wrong with you, these are terrible suggestions, each reliably worse than the one before it. You must be kidding or actively sabotaging the guy

The solution is, simply, to stop using cout for logging. The simplest, lowest overhead solution I can give you knowing absolutely nothing about your code goes like this: on the C++ side, define a global pointer to function, and export a function to set it:

C++ code:
// for simplicity, I'm assuming all log messages are null-terminated
typedef void (__stdcall* log_handler_type)(const char *message);

log_handler_type g_log_handler;

extern "C"
__declspec(dllexport)
void __stdcall set_log_handler(log_handler_type log_handler) {
	g_log_handler = log_handler;
}
Then import it in C# using P/Invoke:

C# code:
// again for simplicity, I'm assuming all log messages are valid UTF-8. woe unto you if that's not true
delegate void LogHandler([MarshalAs(UnmanagedType.LPUTF8Str)] String message);

[DllImport("your.dll", EntryPoint = "set_log_handler", ExactSpelling = true)]
static extern void SetLogHandler(LogHandler logHandler);
As soon as possible, you call SetLogHandler on the C# side to set a method that will receive log messages, and... do whatever it wants with them. On the C++ side, simply call g_log_handler like it was a function to log your messages, just make sure it's never called before SetLogHandler/set_log_handler sets it to a non-null value. Feel free to wrap it in a nicer interface, like Bruegels Fuckbooks has shown you, just don't use OutputDebugString. Never ever loving use OutputDebugString, I reverse engineered the thing in the early 2000s and you really don't want to know what it does

Ensign Expendable
Nov 11, 2008

Lager beer is proof that god loves us
Pillbug
I want to know what it does.

Bruegels Fuckbooks
Sep 14, 2004

Now, listen - I know the two of you are very different from each other in a lot of ways, but you have to understand that as far as Grandpa's concerned, you're both pieces of shit! Yeah. I can prove it mathematically.

hackbunny posted:

What the gently caress is wrong with you, these are terrible suggestions, each reliably worse than the one before it. You must be kidding or actively sabotaging the guy

The solution is, simply, to stop using cout for logging. The simplest, lowest overhead solution I can give you knowing absolutely nothing about your code goes like this: on the C++ side, define a global pointer to function, and export a function to set it:

C++ code:
// for simplicity, I'm assuming all log messages are null-terminated
typedef void (__stdcall* log_handler_type)(const char *message);

log_handler_type g_log_handler;

extern "C"
__declspec(dllexport)
void __stdcall set_log_handler(log_handler_type log_handler) {
	g_log_handler = log_handler;
}
Then import it in C# using P/Invoke:

C# code:
// again for simplicity, I'm assuming all log messages are valid UTF-8. woe unto you if that's not true
delegate void LogHandler([MarshalAs(UnmanagedType.LPUTF8Str)] String message);

[DllImport("your.dll", EntryPoint = "set_log_handler", ExactSpelling = true)]
static extern void SetLogHandler(LogHandler logHandler);
As soon as possible, you call SetLogHandler on the C# side to set a method that will receive log messages, and... do whatever it wants with them. On the C++ side, simply call g_log_handler like it was a function to log your messages, just make sure it's never called before SetLogHandler/set_log_handler sets it to a non-null value. Feel free to wrap it in a nicer interface, like Bruegels Fuckbooks has shown you, just don't use OutputDebugString. Never ever loving use OutputDebugString, I reverse engineered the thing in the early 2000s and you really don't want to know what it does

hahaha yeah
a) I definitely missed the sentence where i was like "get rid of COUT, if you don't want to change anything in the dll, wrap the dll in an EXE, or watch the file path"
b) I wasn't saying to use outputdebugstring for logging, that was more a "hey, if you're not going to stop using cout for logging, you could override it using this." Overriding COUT to use outputdebugstring is like duct taping a boat with a hole in the hull to a broken submarine, I was more amused it would actually work.
c) your way is probably less gross.

Bruegels Fuckbooks fucked around with this message at 13:33 on Nov 15, 2018

hackbunny
Jul 22, 2007

I haven't been on SA for years but the person who gave me my previous av as a joke felt guilty for doing so and decided to get me a non-shitty av

Ensign Expendable posted:

I want to know what it does.

I believe this is the exact code I wrote back then, look at the part inside the _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) block. It's an accurate recreation of the code up to Windows 2000, and I doubt it changed much since then. Summed up very quickly? each call to OutputDebugString might as well be a Sleep(INFINITE) (look at all the global mutexes). Never let a call to OutputDebugString slip into your production code

Faith For Two
Aug 27, 2015

hackbunny posted:

What the gently caress is wrong with you, these are terrible suggestions, each reliably worse than the one before it. You must be kidding or actively sabotaging the guy

The solution is, simply, to stop using cout for logging. The simplest, lowest overhead solution I can give you knowing absolutely nothing about your code goes like this: on the C++ side, define a global pointer to function, and export a function to set it:

C++ code:
// for simplicity, I'm assuming all log messages are null-terminated
typedef void (__stdcall* log_handler_type)(const char *message);

log_handler_type g_log_handler;

extern "C"
__declspec(dllexport)
void __stdcall set_log_handler(log_handler_type log_handler) {
	g_log_handler = log_handler;
}
Then import it in C# using P/Invoke:

C# code:
// again for simplicity, I'm assuming all log messages are valid UTF-8. woe unto you if that's not true
delegate void LogHandler([MarshalAs(UnmanagedType.LPUTF8Str)] String message);

[DllImport("your.dll", EntryPoint = "set_log_handler", ExactSpelling = true)]
static extern void SetLogHandler(LogHandler logHandler);
As soon as possible, you call SetLogHandler on the C# side to set a method that will receive log messages, and... do whatever it wants with them. On the C++ side, simply call g_log_handler like it was a function to log your messages, just make sure it's never called before SetLogHandler/set_log_handler sets it to a non-null value. Feel free to wrap it in a nicer interface, like Bruegels Fuckbooks has shown you, just don't use OutputDebugString. Never ever loving use OutputDebugString, I reverse engineered the thing in the early 2000s and you really don't want to know what it does

This worked. Thank you so much!

EssOEss
Oct 23, 2006
128-bit approved
A package referencing just NETStandard causes invalid binding redirects on .NET 4.7.2 in a web application

The issue is on par with what you can expect from Microsoft these days but check out that list of related issues. drat impressive problem scope to stick on the backlog and not treat as a pants-on-fire emergency.

Munkeymon
Aug 14, 2003

Motherfucker's got an
armor-piercing crowbar! Rigoddamndicu𝜆ous.



EssOEss posted:

A package referencing just NETStandard causes invalid binding redirects on .NET 4.7.2 in a web application

The issue is on par with what you can expect from Microsoft these days but check out that list of related issues. drat impressive problem scope to stick on the backlog and not treat as a pants-on-fire emergency.

I didn't think you could use Standard from the Framework but it was supposed to work the other way around with some compatibility flag?

EssOEss
Oct 23, 2006
128-bit approved
You can use Standard from anything, that's the whole point of Standard. When it works.

Bruegels Fuckbooks
Sep 14, 2004

Now, listen - I know the two of you are very different from each other in a lot of ways, but you have to understand that as far as Grandpa's concerned, you're both pieces of shit! Yeah. I can prove it mathematically.

EssOEss posted:

A package referencing just NETStandard causes invalid binding redirects on .NET 4.7.2 in a web application

The issue is on par with what you can expect from Microsoft these days but check out that list of related issues. drat impressive problem scope to stick on the backlog and not treat as a pants-on-fire emergency.

lol i've spent so much time loving with binding redirects in the past couple of years or so that this wouldn't even register to me as a problem, i'd just gently caress with the binding redirect section until it worked. that's the Faustian bargain of .net, write the code in ten minutes and spend a day figuring out how to include your library in another project so it runs without bitching about version numbers.

amotea
Mar 23, 2008
Grimey Drawer
This issue gives a pretty good insight in why some of this stuff seems so dumb/weird https://github.com/dotnet/standard/issues/481

The MS guy(s) basically have the idea that the latest available version of a package is most likely the correct version, so that is what should be used by the auto version generation process.

The rest of the world, of course, uses the semver convention, which states that any higher major version has breaking changes.

Maybe the difference in perspective is due to how MS employees always have to make everything backwards compatible.

(Discussion starts around https://github.com/dotnet/standard/issues/481#issuecomment-327090269)

Edit, more info: essentially the 'new' MS way moves the burden of getting the version right onto the deployer. You can't specify the version in a redirect anymore. The app will just use whatever DLL it finds on the disk. Technically, this still allows you to get the version right, except that you can't force an error if the wrong version was found.
Explained here: https://github.com/dotnet/standard/issues/481#issuecomment-327410245

Winning quote:

quote:

I would suggest that the most common position is that future versions of useful assemblies will be compatible and incompatibility will be a bug in the library that will be quickly fixed, if found quickly.
:razz:

amotea fucked around with this message at 13:10 on Nov 22, 2018

Bruegels Fuckbooks
Sep 14, 2004

Now, listen - I know the two of you are very different from each other in a lot of ways, but you have to understand that as far as Grandpa's concerned, you're both pieces of shit! Yeah. I can prove it mathematically.

amotea posted:

Edit, more info: essentially the 'new' MS way moves the burden of getting the version right onto the deployer. You can't specify the version in a redirect anymore. The app will just use whatever DLL it finds on the disk. Technically, this still allows you to get the version right, except that you can't force an error if the wrong version was found.
Explained here: https://github.com/dotnet/standard/issues/481#issuecomment-327410245

i feel like whenever I mess with binding redirect, my end goal is making it so it uses the dll it finds on the disk anyway, so it seems like that cuts out a step.

epswing
Nov 4, 2003

Soiled Meat
I'm considering rolling my own software licensing scheme. Among other things, the software, when sold, will be licensed to run on a particular computer. I'm looking for a resilient way to identify "a computer" (where the OS in my case will be one of Windows 7, 10, or Windows Server 2008, 2012, 2016). There's lots of literature recommending different ways to do this. Some common ones I don't like are...
  • Hard drive volume number, but hard drives are essentially "consumable" and get replaced too often.
  • Windows serial number, but again, if the HDD blows up, that'll change if there's no backup and Windows gets reinstalled.
  • MAC address, but this can easily be spoofed, and there can be more than one, and they're not always in the same order.
I'm thinking of using motherboard serial number, which I can get on the console with wmic baseboard get serialnumber, and in C# with
C# code:
var moboSerial = new ManagementObjectSearcher("SELECT SerialNumber FROM Win32_BaseBoard")
    .Get()
    .Cast<ManagementObject>()
    .FirstOrDefault()?
    .GetPropertyValue("SerialNumber")?
    .ToString();
I've run this query on a dozen PCs, and a couple virtual machines on Azure, and so far the values I'm seeing are unique and palatable (20-30 alphanumeric characters). I do understand that nothing is foolproof, so this is on a "best effort" basis. Just something I can put in place that, if (when?) circumvented, would have to be done willfully and not accidentally. I'm at the point where I can say "this seems fine", which usually comes before "oops, this isn't going to work at all" when applied to a larger scale.

Anyone have experience with this?

epswing fucked around with this message at 21:05 on Nov 22, 2018

Potassium Problems
Sep 28, 2001

epalm posted:

I'm considering rolling my own software licensing scheme. Among other things, the software, when sold, will be licensed to run on a particular computer. I'm looking for a resilient way to identify "a computer" (where the OS in my case will be one of Windows 7, 10, or Windows Server 2008, 2012, 2016). There's lots of literature recommending different ways to do this. Some common ones I don't like are...
  • Hard drive volume number, but hard drives are essentially "consumable" and get replaced too often.
  • Windows serial number, but again, if the HDD blows up, that'll change if there's no backup and Windows gets reinstalled.
  • MAC address, but this can easily be spoofed, and there can be more than one, and they're not always in the same order.
I'm thinking of using motherboard serial number, which I can get on the console with wmic baseboard get serialnumber, and in C# with
C# code:
var moboSerial = new ManagementObjectSearcher("SELECT SerialNumber FROM Win32_BaseBoard")
    .Get()
    .Cast<ManagementObject>()
    .FirstOrDefault()?
    .GetPropertyValue("SerialNumber")?
    .ToString();
I've run this query on a dozen PCs, and a couple virtual machines on Azure, and so far the values I'm seeing are unique and palatable (20-30 alphanumeric characters). I do understand that nothing is foolproof, so this is on a "best effort" basis. Just something I can put in place that, if (when?) circumvented, would have to be done willfully and not accidentally. I'm at the point where I can say "this seems fine", which usually comes before "oops, this isn't going to work at all" when applied to a larger scale (hundreds of computers).

Anyone have experience with this?

I've recently had to do this as well, I used this NuGet package to generate a unique machine id, which I believe just uses WMI under the hood anyway. It lets you include different device values to come up with a reasonably unique identifier, or implement your own device component if the built-in ones aren't enough. For my use case, it was acceptable to include only the motherboard serial, OS & MAC address, but these were for machines under our control and any spoofing/tampering with those values would be a larger problem.

Edit: gonna also recommend https://keygen.sh/, software licensing as a service. Not free, so may not be an option.

epswing
Nov 4, 2003

Soiled Meat

Potassium Problems posted:

I've recently had to do this as well, I used this NuGet package to generate a unique machine id, which I believe just uses WMI under the hood anyway. It lets you include different device values to come up with a reasonably unique identifier, or implement your own device component if the built-in ones aren't enough. For my use case, it was acceptable to include only the motherboard serial, OS & MAC address, but these were for machines under our control and any spoofing/tampering with those values would be a larger problem.

Edit: gonna also recommend https://keygen.sh/, software licensing as a service. Not free, so may not be an option.

DeviceId looks like a nice compact package. It is just WMI as you suspected.

"Licensing as a Service" via https://keygen.sh/ is a nice idea, but requires an Internet connection, which doesn't work for me in this case. Thanks for the link though, might use this for other projects.

LongSack
Jan 17, 2003

Question about asynchrony / parallel programming.

I’m an information security engineer and I write tools that help automate things that I do frequently to save time / my sanity. I currently have two tools that read saved firewall configs from a TFTP server and then parse them for various purposes. One tool is a general information tool that allows searches across all firewalls, shows graphs of firewall interconnectivity by location and produces a spreadsheet that we use for reference. The other is more focused on policy (i.e., rules) and produces rule dumps and decommission scripts.

I’m working on combining both tools into one, since there is so much shared functionality when it comes to parsing config files and producing a Firewall object. Since half of the engineers I work with are in India and face heinous network latency, I cache the files locally. So when the applications start, both do something like this:
C# code:
foreach (var remote in StorageManager.RemoteFiles)
{
  if (!StorageManager.LocalFileIsCurrent(remote.Filename))
  {
    StorageManager.Copy(remote.Filename);
  }
  FirewallManager.Add(Firewall.Parse(remote.Filename));
}
Currently, I just throw up a WaitCursor and the user can just deal with it. I’d like to make this all async and maybe even parallel. I’d also like to show progress bars. So the Copy method already is set up to use the asynchronous version of the DownloadFile method, which accepts a delegate for progress reporting, so that is cool.

My question is mainly about my Firewall.Parse method. I can throw an ”async” in front of it, and have it return Task<Firewall>, but I’m not sure about progress reporting. The method already knows how many lines are in the config file, and what the current line number is, so figuring the percent is trivial. But how to report it? Do I add a delegate to the parse method? If so, what thread will that delegate be executing on when invoked?

Also, for parallel execution, I think I’m ok, since each Parse method takes a separate config file and would return a separate Firewall object.

Ideas? Am I on track? Totally off base? TIA

New Yorp New Yorp
Jul 18, 2003

Only in Kenya.
Pillbug

LongSack posted:

Question about asynchrony / parallel programming.

I’m an information security engineer and I write tools that help automate things that I do frequently to save time / my sanity. I currently have two tools that read saved firewall configs from a TFTP server and then parse them for various purposes. One tool is a general information tool that allows searches across all firewalls, shows graphs of firewall interconnectivity by location and produces a spreadsheet that we use for reference. The other is more focused on policy (i.e., rules) and produces rule dumps and decommission scripts.

I’m working on combining both tools into one, since there is so much shared functionality when it comes to parsing config files and producing a Firewall object. Since half of the engineers I work with are in India and face heinous network latency, I cache the files locally. So when the applications start, both do something like this:
C# code:
foreach (var remote in StorageManager.RemoteFiles)
{
  if (!StorageManager.LocalFileIsCurrent(remote.Filename))
  {
    StorageManager.Copy(remote.Filename);
  }
  FirewallManager.Add(Firewall.Parse(remote.Filename));
}
Currently, I just throw up a WaitCursor and the user can just deal with it. I’d like to make this all async and maybe even parallel. I’d also like to show progress bars. So the Copy method already is set up to use the asynchronous version of the DownloadFile method, which accepts a delegate for progress reporting, so that is cool.

My question is mainly about my Firewall.Parse method. I can throw an ”async” in front of it, and have it return Task<Firewall>, but I’m not sure about progress reporting. The method already knows how many lines are in the config file, and what the current line number is, so figuring the percent is trivial. But how to report it? Do I add a delegate to the parse method? If so, what thread will that delegate be executing on when invoked?

Also, for parallel execution, I think I’m ok, since each Parse method takes a separate config file and would return a separate Firewall object.

Ideas? Am I on track? Totally off base? TIA

This is the joy of the async/await pattern. You don't need to do anything to marshal back to the UI thread to report progress. Just report progress through whatever mechanism. If your core "I'm doing stuff" logic is decoupled from your UI logic (as it should be!) in a separate class, you can expose an event in your class that your async method invokes whenever it needs to report progress, then have an async event handler in your UI that handles updating progress.

Roid666
Jul 18, 2010
If you actually want to return updates from inside the awaited function you can use an IProgress.

More info here: http://blog.stephencleary.com/2013/09/taskrun-vs-backgroundworker-round-5.html

New Yorp New Yorp
Jul 18, 2003

Only in Kenya.
Pillbug

Roid666 posted:

If you actually want to return updates from inside the awaited function you can use an IProgress.

More info here: http://blog.stephencleary.com/2013/09/taskrun-vs-backgroundworker-round-5.html

Yeah, I forgot about IProgress. Do that.

LongSack
Jan 17, 2003

Thanks!

Furism
Feb 21, 2006

Live long and headbang
Not sure if this is the right place to ask but here goes.

Is there an application template somewhere I could use with the dotnet tooling that would give me : ASP.NET Core 2.1 + Semantic UI + TypeScript + yarn all ready? I realize I'm asking a lot as this is a very specific stack (although a popular one I reckon). But I'm jut a hobbyist and setting this up takes me way more time than it should.

LongSack
Jan 17, 2003

OK, I probably misunderstand how await works, but I got my new async version of my firewall tool to the point where is could load/parse the firewall configs.

The old version just threw up a WaitCursor and a LoadingWindow which was simply a “windowless window” with the word “Loading” centered on the screen with a fancy drop shadow. The actual code which called the routines to copy (if necessary, the config files are cached locally) and parse the configs was in the MainViewModel’s WindowLoaded command method (invoked with an attached property on the MainWindow).

In the new version, the LoadingWindow has all the work and is a real window with a Stage, current file name, progress bar and a cancel button. The copy and parse routines are run asynchronously, with progress handling, so the UI thread isn’t blocked, and can be updated.

The code in the LoadingViewModel’s WindowLoaded method (stripped down with error handling removed) looks something like this:
C# code:
private async void WindowLoaded()
{
  foreach (var info in FileManager.RemoteFiles)
  {
    if (!FileManager.LocalFileIsCurrent(info.Name))
    {
      await Task.Run(() => FileManager.Copy(info.Name, FTPProgressHandler));
    }
    await Task.Run(() => FirewallManager.Add(info.Name, ParseProgressHandler));
  }
  DialogResult = true; // close the window, we’re done
}
I was getting an exception if the file needed to be copied. The Parse() method eventually called by FirewallManager.Add() uses File.ReadAllText() which was throwing an exception that the file was in use by another process. So after a little troubleshooting, it turns out the the other process was my program, and that at the time of the exception the size of the file was zero bytes.

So it seemed that the the Copy method was returning before the Copy was complete.

I realized that the Copy method eventually called DownloadFileAsync() which, of course, doesn’t wait. Aha! So I stuck an await Task.Run(...) in front of the call and expected the exception to go away.

Nope. It still happened.

I found a fix by changing the single foreach loop into two loops, one for the copy and another for the parsing.

Sorry for all the :words: but am I fundamentally misunderstanding how await works?

FrantzX
Jan 28, 2007
I don't think it is async you are misunderstanding, it is file I/O. To put it simply, many file I/O functions cheat in the interest of performance. Methods that write to a disk will often return before the writing has completed and complete the write asynchronously.

See: https://stackoverflow.com/questions/383324/how-to-ensure-all-data-has-been-physically-written-to-disk

LongSack
Jan 17, 2003

FrantzX posted:

I don't think it is async you are misunderstanding, it is file I/O. To put it simply, many file I/O functions cheat in the interest of performance. Methods that write to a disk will often return before the writing has completed and complete the write asynchronously.

See: https://stackoverflow.com/questions/383324/how-to-ensure-all-data-has-been-physically-written-to-disk

Interesting. Well, I have a workaround that seems to work, and it appears that I do understand the async / await pattern, so I’ll just chalk it up to “things I need to work around” like the splash screen / dialog bug that still isn’t fixed. Thanks!

Adbot
ADBOT LOVES YOU

raminasi
Jan 25, 2005

a last drink with no ice

LongSack posted:

I realized that the Copy method eventually called DownloadFileAsync() which, of course, doesn’t wait. Aha! So I stuck an await Task.Run(...) in front of the call and expected the exception to go away.

Why did you think that would fix it? If it's returning before it's done, it's returning before it's done. Doing that on a different thread won't make a difference.

If there's an async call at the bottom, then that's what you want to be using. Do you control the FileManager? Can you create a CopyAsync that exposes the underlying asynchrony?

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply