none
How to convert DotNet library to .h files for use as DLL? RRS feed

  • Question

  • Hello Forum,

    despite my 30+ years in pure OOD using Smalltalk I have no own experiences with compiled languages. This is why I am asking a quiestion that is certainly stupid to every deceloper using compilers:

    My Smalltalk environment can perfectly use DLL as offered for use with C or C++. There exists an interface builder tool, which requires the .h header files to generate interface classes and methods.

    I would like to interface a library, which is only offered for .Net (DotNet).

    My question is:

    Is there a way to convert, generate or whatever from .Net to the data that normally comes with C/C++ libraries?

    Would that require access to all sources or is the compiled code sufficient?

    Thank you very much for some enlightment in this respect.

    Best regards

    Frank

    Saturday, February 8, 2020 1:18 PM

All replies

  • Wrong forum Frank. This one is about the Microsoft Access database.

    -Tom. Microsoft Access MVP

    Saturday, February 8, 2020 4:25 PM
  • Tx
    Saturday, February 8, 2020 4:41 PM
  • Programmers are constantly saying DLL without clarifying what type of DLL. The original type of DLL, the kind that the Windows API exists in and that there are headers for, is often called a native DLL. Another kind are COM DLLs and even for those the header files are not so simple. A .Net DLL should be called a Class Library to properly distinguish it from other types of DLLs. I assume that the .Net library is a Class Library.

    No, it is not possible to describe a .Net Class Library using C-style headers. You will need to either obtain an existing interface or write one.



    Sam Hobbs
    SimpleSamples.Info

    Saturday, February 8, 2020 8:28 PM
  • As noted, this forum is for Access developers and the desktop program called MS-Access.

    However, I often have to interface and consume .dll's from MS-access. So, while you would be best to ask in a .net forum (Access uses Visual Basic for Applications  - not .net for writing code).

    I can answer your questions:

    >Is there a way to convert, generate or whatever from .Net to the data that normally comes with C/C++ libraries?

    Yes, you can do this. There are some "add-ins"for Visual Studio. They allow you to simply create a class in .net, and the compiliner will spit out a set of standard .dll "entery" points. Such .dll's thus will work as if you wrote a .dll in say c++, or any other un-managed code system. So, you can write in vb.net, or c#, but have a "header" and a set of entry points that conform to windows standard un-managed code specs.

    >Would that require access to all sources or is the compiled code sufficient?

    Not if you can fire up Visual Studio, make a reference to the .dll's (assemblies) and then freely create instances of the .net classes contained in those entries. If this is allowed, then you would not need the source code. You would thus simply write a small .net application and with the above first tip then you can do this.

    You can "build" this header by simply choosing the nuget library options from Visual Studio. You find several utilities that will export and setup a standard .dll's with standard entry points. I have done this quite a few times and the results is that "managed" .net code can then be called + used + consumed as if it was a windows x32 bit (or x64 bit) standard .dll with standard entry points. I use the free add-in (dll export) called : 

    Unmanaged Exports by Robert Giesecke.  (just search for Unmanaged Exports).

    Using nuget from Visual Stuido will automatic install his package, but for your reading, he has these notes here:

    https://sites.google.com/site/robertgiesecke/Home/uploads/unmanagedexports

    The beauty of this approach is that I am a vb.net person. So, I am actually able to create c++ compatible (windows) .dll's, but am using Visual Basic, and thus I did not have to learn a new language. I even used this trick to build a terminal services add-in. Such add-ins are supposed to be un-managed code, and c++, but I wrote the whole thing in good old fashioned basic language due to the above suggestion.

    This is as you note is common and standard fair, even for us simple basic programmers of which this group is all about. Given your experience, then you have little issues going down this road.  Check out the above link, and as noted try some .net groups, since this is by no means a .net group.

    Regards,

    Albert D. Kallal (Access MVP 2003-2017)
    Edmonton, Alberta Canada


    Sunday, February 9, 2020 1:32 AM
  • They allow you to simply create a class in .net, and the compiliner will spit out a set of standard .dll "entery" points. Such .dll's thus will work as if you wrote a .dll in say c++, or any other un-managed code system.

    A DLL written in C++ can only be used by C++ except it is possible to tell the compiler to use C++ code as if it is C code. The reason is fundamental to OOP, including C++ and .Net. In OOP, we create instances of objects then use the object to access its members. And without getting into the technical details, that explains why a native DLL cannot be object-oriented.

    I use the free add-in (dll export) called : 

    Unmanaged Exports by Robert Giesecke.  (just search for Unmanaged Exports).

    Using nuget from Visual Stuido will automatic install his package, but for your reading, he has these notes here:

    https://sites.google.com/site/robertgiesecke/Home/uploads/unmanagedexports

    That documentation is extremely vague. It is extremely unclear how it manages object instances. You might be correct that it is that easy but I am skeptical.



    Sam Hobbs
    SimpleSamples.Info


    Sunday, February 9, 2020 6:53 AM
  • >A DLL written in C++ can only be used by C++ except it is possible to tell the compiler to use C++ code as if it is C code. 

    You can certainly consume c++ .dll’s from VBA, or VB6, or just about any system that works with un-managed code. And it “depends” on the kind of .dll. (There are 3 different types).

    And keep in mind the TWO types of windows .dlls (unmanaged types).

    ActiveX/Com = a .dll that you have to FIRST register with regsvr32.exe

    (Generally used to consume classes)

    And then we have what are called standard windows .dll. You don’t register these types of dll’s – you don’t use CreateObject(), and it is a simple .dll with standard “entry points” (as the poster asked about and talked about).

    Such types of dll’s go back even to DOS days. The concept of “entry” points is perhaps one of the most basic and fundamental concepts since the dawn of .dll’s and software development. And that is WHY the poster asked about this idea. It is a GREAT idea if it was possible – and as I noted above, it is!!!!

    So, the “instant” we start talking about a .dll, and entry points? Well then this is a standard .dll, and not a registered .dll. And it not a .net .dll (the 3<sup>rd</sup> type).

    With the above .net utility, then the result is the above second type of .dll. (A standard windows .dll).

    We are creating a .net .dll that can be used by c++, VBA, or in fact any language that supports external standard windows .dll’s (not com objects).

    You can create such dll’s with c, or c++. Also, VB6 I believe allowed this.

    So, yes, in fact the resulting .net .dll could be used directly in a c++ project.  Any dev platform that supports external “standard” .dll’s can consume such dll’s. (Assuming we used the dllexport utility noted above).

    We often call such external .dll code from VBA (most often for windows API’s) like this:

    Private Declare Sub sapiSleep Lib "kernel32" _ Alias "Sleep" _ (ByVal dwMilliseconds As Long) In VB.net, we use much the same syntax: Public Declare Sub sapiSleep2 Lib "kernel32" _ Alias "Sleep" _ (ByVal dwMilliseconds As Integer) Or, you can use this format: <DllImport("KERNEL32.DLL", CallingConvention:=CallingConvention.StdCall, SetLastError:=True, EntryPoint:="Sleep")> Public Sub sapiSleep(ByVal dwMilliseconds As Integer) End Sub (Both of the vb.net examples above are the SAME to each other,

    but using DLLImport directive allows more control over the entry points and the .dll interface). And in c#, you get something like this:

    ' in c# we have something like this:

    [DllImport("KERNEL32.DLL", CallingConvention = CallingConvention.StdCall,

    SetLastError = true, EntryPoint = "Sleep")] public void sapiSleep(int dwMilliseconds) { }


    Monday, February 10, 2020 2:29 AM
  • So a .dll with entry points is the simplest kind of .dll.

    There are some great use cases of this concept. So for example, I needed a RDP (remote desktop “custom” channel). This would allow my Access users to click say on an email button, and then launch say outlook with a PDF invoice attached. We all done this a million times from Access. However, when users go on the road and use Remote Desktop, then clicking on that Access email button on a form? Well the instance of outlook will be launched on the terminal server – and not on the local client. However,  I simply built a RDP bridge. So, now if you click on that email button, the LOCAL copy of the outlook is launched on the client side. And thus attachments etc. and body text appear on the CLIENT side version of Outlook! The result is outlook runs local! The specs and how to write a RDP add-in is outlined here:

    https://docs.microsoft.com/en-us/windows/win32/termserv/virtual-channel-client-dll

    You will WELL note that the above requires standard .dll, with entry points. I was able to write my RDP add-in in good old fashion .net “basic code” by using the dllExport utility. (you don’t have to read the above – but take a quick glance at the page – the important takeaway is this entry point concept).

    Normally, such an add-in would have to be written in c++ (unmanaged code). Yet, I wrote my code in VB.net, and simply made sure that my dll entry points conform to the above docs as to how the remote desktop “channel” interface is to work. (RDP channels are VERY cool, since they by-pass all firewall security settings – and thus I can use + automate outlook on the client side from Access running on the terminal server).

    In "most" cases, to consume .net code from say c++, or even Access? Well, in most cases one would build + use a .net COM object based on the great and easy COM interop ability that exists in .net to create a windows compatible  COM object. I also OFTEN do this – in fact this is the most common approach used to share .net code say with VBA/Access or any un-managed coding system.

    As a regular course of action, .net dll's certainly cannot called or used from non .net code. You can ONLY use such .dll from .net – but then we talking about .net .dll’s (assemblies). Those assemblies can be used from .net, but not un-managed code.

    Monday, February 10, 2020 2:30 AM
  • However!

    With the above DLLExpot “add-in”, it just shoves in a un-managed “c.h” like headers that is “standard” .dll code with entry points and then simply jumps + calls the.net code (so the idea is similar to COM interop, but now we using a simple .dll).

    With that add-in, then even c++ can directly use the .net .dll as an external library call.

    Access VBA can consume standard windows .dlls.

    Most VBA examples in regards to consuming external .dll’s are to the windows API.

    However, VBA never was just limited this way. So the use case is certainly not limited to just windows API, and we can use any standard .dll with Access.

    As long as that .dll has the standard .dll entry points then you can use that .dll. (And that’s what the c++ headers also do).

    So, the dll export utility above simply creates a standard .dll with entry points – non managed. It then just calls the .net code. It is similar to the COM interop abilities in .net, but we not creating a com object.

    The documentation is sparse in that link because “entry” points are quite much to software as to what coffee beans are to coffee! – It is a basic concept. You just talking about “entry” points into the .dll. Since the dawn of dll’s, this is one of the most basic building blocks of software. Entry points are really just calls to subs and functions that “exist” in the given .dll.

    >You might be correct that it is that easy but I am skeptical.

    Its amazing easy, and from RDP channels, to making simple .dll’s to use from say Access or whatever? It has lots of uses.

    Monday, February 10, 2020 2:31 AM
  • The vb.net code is thus this

    Imports System.Runtime.InteropServices
    Imports RGiesecke.DllExport
    
    Module Module1
    
        <DllExport("MyHello")>
        Sub MyHello()
    
            MsgBox("hello from .net code!",, "Hi")
    
        End Sub
    
        <DllExport("PlaySound")>
        Sub PlaySound(ByVal strWave As String)
    
            My.Computer.Audio.Play(strWave, AudioPlayMode.Background)
    
       End Sub
    
        <DllExport("Time2")>
        Function Time2(ByRef x As Integer) As Integer
    
            Return x * 2
    
        End Function
    
    End Module

    That’s it! So we have 3 entry points – 3 features. Now each of the entry points could create a class, have it do whatever, and return values. 

    So, now we have a standard .dll. (or better said a un-managed .dll with a header that defines the correct entry points.

    Monday, February 10, 2020 2:32 AM
  • The VBA to use the above looks like this:

    Option Compare Database
    Option Explicit
    
    ' Private Declare Sub MyHello Lib "c:\test3\mysound.dll" ()
    ' Private Declare Function Time2 Lib "c:\test3\mysound.dll" (ByRef x As Long) As Long
    ' Private Declare Sub PlaySound Lib "c:\test3\mysound.dll" (ByVal strSound As String)
    
    Public Declare Sub MyHello Lib "mysound.dll" ()
    
    Public Declare Function Time2 Lib "mysound.dll" (ByRef x As Long) As Long
    
    Public Declare Sub PlaySound Lib "mysound.dll" (ByVal strSound As String)
    
    Private Declare Function LoadLibrary Lib "kernel32" _
    
    Alias "LoadLibraryA" (ByVal lpLibFileName As String) As Long
     
    Public Sub TestDll()
    
       LoadLibrary CurrentProject.Path & "\mysound.dll"
    
       MyHello
    
       MsgBox "Call of times 2 function - 5 x 2 = " & Time2(5)
    
       PlaySound "c:\test3\working2.wav"
    
       MsgBox "Playing the wav sound file in background mode"
    
    End Sub
    

    That’s it.

    The link to the working zip file is here:

    https://1drv.ms/u/s!Avrwal_LV4qxhpdH1dHQ-dNSBC3Qsg?e=40qXep

    You only need un-zip above sample to a folder and launch the accDB file. It will call + consume the .dlls as if the .dll was a standard c++ or so called windows .dll (API). No creating of classes etc. No installing, no registering of the .dll’s are required. The above zip example “just works” like magic.

     And it is great, since no need exists to register the .dll. Today, I am seeing SO MANY machines locked down, especially in corporate enviroments. This means you need elevated rights to register .dll’s. (but not with this approach!).

     So this gets you around these issues – you can thus deploy a simple .dll say with your Access application and it opens up doors to consume .net code with relative ease.

    I also tossed in the use of loadlibary. (really optional here).

    This was used since if the.dll is not in the windows system folder, then it will not normally be found. You could also place the .dll in the same location as the executable – but that location is actually the location of msaccess.exe! (that’s a silly place way down in C:\Program Files (x86)\Microsoft Office\Office14\MSACCESS.exe.

     Again that folder is often locked down in terms of user rights.

     We thus make the assumption the simple .dll will be located in the SAME folder as the accDB file.

    That load lib trick been used for 20 years in Access and VB6 land. (it just means I don’t have to hard code the paths in the above VBA code as the commented out code shows).

    Give the above a try – its just a un-zip, and then launch the accDB. You be running the that .net code as a standard .dll from VBA.

     So, the poster was asking about creating a .dll with standard entry points – the dllExport utility I note does EXACTLY this idea. It is a great trick, since I can now develop standard windows api .dll’s, but use good old fashion basic code in .net.

    This trick allowed me to write a RDP add-in in simple basic code. And looking at the Virtual Channel Client DLL docs, one would normally require to create that .dll in c, or c++.

    So RDP channels requires c++ (or c), and you have to create a standard .dll with entry points. But those languages are beyond my pay grade – but, this trick lets me join this party and I using plane jane basic code (vb.net code).

    So, this ALSO explains the sparse documentation in that link. This basic concept of calling subs and functions that exist in the dll is quite much the whole design and architecture of when we fired up our first computer going all the way back to DOS  days. The first .dll you ever seen in a OS likely worked this way. 

     So if you have some special “controller” or logging device in a lab, or even a postal scale? Such vendors will often provide a API and provide the entry points into their provided .dlls’

     So, being able to create .dll’s with .net can open up some really cool doors.

    Why bother to create some class c++ .dll, when all you need or want is to call a few subs/functions (entry points) in a simple .dll? This also allows you to with great ease use some .net code and routines from .net in say VBA, and do so without having registration of those .dlls, and the required elevated rights on machines today that are so ever more being locked down – especially in a corporate environment.

    Regards,

    Albert D. Kallal (Access MVP 2003-2017)

    Edmonton, Alberta Canada


    Monday, February 10, 2020 2:33 AM
  • You can certainly consume c++ .dll’s from VBA, or VB6, or just about any system that works with un-managed code. And it “depends” on the kind of .dll. (There are 3 different types).

    Do you know what the following are in C++?

    extern "C" tells the C++ compiler to use the same linkage that C uses for the relevant functions and variables. Otherwise the compiler uses name mangling and a DLL with mangled names cannot be used by any code other than C++ and typically the code can be incompatible with code compiled by a differnt compiler. Name Mangling is required to support Function (Method) Overloading.

    ActiveX/Com = a .dll that you have to FIRST register with regsvr32.exe

    It is more accurate to say that registry entries for COM Objects must be added. I have samples of such registry entries in Registry Entries for a Simple COM Object. The registry can be updated in any way the COM developer wants to but the convention is that the DLL have a function called DllRegisterServer to do the update and regsvr32 simply calls that.

    Such types of dll’s go back even to DOS days. The concept of “entry” points is perhaps one of the most basic and fundamental concepts since the dawn of .dll’s and software development

    Actually it is 16-bit Wiindows days. DOS programs are totaly different from Windows programs and DOS programs were extremely unable to use Windows DLLs. DOS programs interface with DOS using software interupts and Windows programs interface with Windows using DLLs.

    Yes, I understand entry points; assembler programs have entry points of course and even IBM Mainframe operating systems going back to the mid-60s and even operating systems before that had entry points.

    I was familiar with the concept of dynamic linking before Microsoft began developing Windows.

    So, the “instant” we start talking about a .dll, and entry points? Well then this is a standard .dll, and not a registered .dll. And it not a .net .dll (the 3<sup>rd</sup> type).

    A .Net DLL has metadata that no other type of DLL has; see Metadata and Self-Describing Components.

    What is important is to help Frank. We do not need to convince either of us about anything. Either your suggestion works or it does not. I am not suggesting that anyone do anything; I just said that I was skeptical. We can and should agree to disagree. Please help Frank and prove me wrong.



    Sam Hobbs
    SimpleSamples.Info

    Monday, February 10, 2020 5:31 AM
  • > Do you know what the following are in C++?

    Yes, I took computing science at the university level. I have written interactive oil spill simulations for Exxon.

    I already pointed out how I used this for years. Don't think you need any more witness and testimony then my own experience of having done this for years.

    >and DOS programs were extremely unable to use Windows DLLs.

    You saying that we can’t run a new version of word on DOS? Ok, I guess we can be thankful for that information, but I not aware of anyone thinking as such, or suggesting, or hinting as such?

     I guess it’s ok to point out that older DOS boxes can’t run and consume windows programs. I just did not think this was a surprise to you or anyone else reading this. I am having difficulty why it is necessary to point out that DOS can't run windows programs? Did anyone suggest, hint, or imply as such? (not to my knowledge).

    I simply said such older dll’s use the SAME concept as entry points. And in fact dll's produced on a DOS system that spit out .dll's can certainly be run and used on a windows box. That includes using windows development tools. The only issue would be "bit size". But no one is suggesting that we can go the other way! (Use windows code on a DOS box!!).

    So, in those DOS days, and dll's? The basic concept and idea of dlls and entry points still stands. If you used any of the many development systems (c, c++, turbo pascal), on DOS before windows? They were all able to create .dll's. And those dll's had entry points. 

    And WHEN windows arrived? Yes, you could in fact consume and use those .dll like any other. So any new tools that appeared in that timeframe (say windows 3.1) could MOST certainly consume such external .dll – even those developed on a DOS box without windows.  So, no one here is suggesting that windows bits and parts can be used on DOS.

     However, the reverse is most certainly the case.

     So, using c, c++, Delphi, turbo pascal etc.? Yes, they all had support for external .dlls, and they could in most cases even static link in such details (if the dev system supported a linker). And when using the windows versions of these developer tools came on the scene? Yes, you could most certainly consume those older .dll's that were NOT developed for windows.

    In fact they could have been developed for and on a DOS box without windows - (3.1 in this context). You could freely and consume these .dlls. No worse of an issue or problem than using such tools to make calls to BIOS code. (Which had not really changed since DOS arrived). And most of those calls to BIOS also working on this simple concept of entry points.

    This is especially the case when dev tools appeared for windows 3.1 (since dos and windows 3.1 were built around 16 bit architecture).

    So, as I stated, yes, such DOS .dll's had entry points, and yes they could most certainly be used and consumed on windows boxes. And yes the windows development tools supported using and consuming those dll's. 

    > It is more accurate to say that registry entries for COM Objects must be added

     But is that relevant? The issue is that such library use requires you to create an instance of the class(s) involved to use. The issues of some registry entries is not really the meat of that problem. I mean if we had to create some registry entries to consume the static .dll, then that’s not the issue, is it?

    With COM/ActiveX, you are consuming those external .dll’s as “instances” of objects – and that is VERY significant here. We can bring up a gazillion issues, but the elephant in the room for those types of .dlls is they are not simple entry points, and consuming software NOW has to create instances of those objects to consume.  We don't even KNOW if the software in question by the poster can create objects from external programs.

    Such an approach is certainly a viable approach here – but the meat is THEN that requiring to create instances of the class(s) as opposed to a simple entry point in a dll – not that some registry entries are required. Let’s try and keep this discussion in a context that answers the posters question about “entry” points in a dll. Not only does such a solution involve one having to create instances of objects (classes), but the consuming side of the software HAS to create those objects. That is a spectacular elephant in a room as opposed to a sub/function call based on the concept of entry points in a .dll. 

     

    >A .Net DLL has metadata that no other type of DLL has;

     Yes, quite much my whole point as to WHY we can and would use the dllExport utility – it gives us and creates those “standard” entry points that the poster was asking about. Quite much the WHOLE point of my post, don't you agree?

     

    >I just said that I was skeptical. We can and should agree to disagree. Please help Frank and prove me wrong.

    No, problem. That's why I am here to help!

    That’s why I am talking about entry points, and not rambling off on a solution that requires one to create instances of the class(s) involved from the consuming software.

     So to solve this question?

    Just look at the post and sample code I provided. And if you have Access installed on your box, then you can download and actually try the zip simple. It is an ACTUAL WORKING proof of concept and code.

    I can’t do anything more to demonstrate and prove my point when I already posted an actual working example of the concepts I outlined!

     We are LONG past the idea of “convincing” when I posted working code. That posted code and example(s) can be used as a working solution to the question posted here. That question was creating a dll with entry points to consume some .net code.

     This is exactly what the poster asked, and this is exactly what my posted solution achieves.

    If you feel there is any need for me to expand on the soluion posted, I am most happy to provide more details. Give the posted code a try - you see it works as advertised. 

    Regards,

    Albert D. Kallal (Access MVP 2003-2017)

    Edmonton, Alberta Canada

    Monday, February 10, 2020 7:55 PM
  • I guess it’s ok to point out that older DOS boxes can’t run and consume windows programs. I just did not think this was a surprise to you or anyone else reading this. I am having difficulty why it is necessary to point out that DOS can't run windows programs? Did anyone suggest, hint, or imply as such? (not to my knowledge).

    You implied that DOS programs can use DLLs.

    And in fact dll's produced on a DOS system that spit out .dll's can certainly be run and used on a windows box.

    No, that is not true.

    If you used any of the many development systems (c, c++, turbo pascal), on DOS before windows? They were all able to create .dll's. And those dll's had entry points.

    C and C++ are languages, not development systems. And yes, I used Microsoft C, C++ and MASM for DOS. I even wrote a printer device driver using C and MASM.

    Yes, you could most certainly consume those older .dll's that were NOT developed for windows.

    No, not possible. In Windows DLLs are more of a function of the operating system. Prior to Windows, IBM did begin to include dynamic linking in their COBOL compiler and their linker but when Microsoft developed Windows it developed the Windows API as DLLs and therefore DLLs have always been a fundamental part of Windows programs. Windows DLLs require Windows to run them. Unix did not have dynamic linking at the time that Windows was first developed.

    And most of those calls to BIOS also working on this simple concept of entry points.

    No. See Software Interrupts:Hooking an Interrupt BIOS and DOS Interrupts Assembly Language Programming Computer Science Programming Languages. The BIOS is not called in the manner that compilers call functions. PC hardware, the BIOS and DOS programs all use interrupts. DOS programs used DOS by executing an "int" machine instruction specifying an interrupt code of hexadecimal 21. DLLs are called using the "call" machine instruction. So a "call" to BIOS involved the processor accessing the interrupt vector, not entry points set up by a linker.

    So, as I stated, yes, such DOS .dll's had entry points

    There is no such thing as DOS DLLs.



    Sam Hobbs
    SimpleSamples.Info

    Monday, February 10, 2020 10:22 PM
  • Sam,

    The OP's C dll is using the hosting API to host the CLR.  I suspect that the functions exported by the dll are using the CLR to invoke managed code.

    I downloaded the example provided by the OP and ran Dumpbin on it.

    As expected, the dll has mscoree as a dependency and imports the _CorDllMain Function

    Running Ildasm on the dll confirmed that it contained managed code.

    //  Microsoft (R) .NET Framework IL Disassembler.  Version 4.6.1055.0
    //  Copyright (c) Microsoft Corporation.  All rights reserved.
    
    
    // VTableFixup Directory:
    //   IMAGE_COR_VTABLEFIXUP[0]:
    //       RVA:               0x00004000
    //       Count:             0x0003
    //       Type:              0x0005
    //         [0x0000]            (0x06000018)
    //         [0x0001]            (0x06000019)
    //         [0x0002]            (0x0600001a)
    
    
    // Metadata version: v4.0.30319
    .assembly extern mscorlib
    {
      .publickeytoken = (B7 7A 5C 56 19 34 E0 89 )                         // .z\V.4..
      .ver 4:0:0:0
    }
    .assembly extern System
    {
      .publickeytoken = (B7 7A 5C 56 19 34 E0 89 )                         // .z\V.4..
      .ver 4:0:0:0
    }
    .assembly extern Microsoft.VisualBasic
    {
      .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A )                         // .?_....:
      .ver 10:0:0:0
    }
    .assembly mysound
    {
      .custom instance void [mscorlib]System.Runtime.InteropServices.GuidAttribute::.ctor(string) = ( 01 00 24 64 39 61 32 31 66 34 65 2D 31 32 36 32   // ..$d9a21f4e-1262
                                                                                                      2D 34 35 38 66 2D 39 35 33 36 2D 32 36 37 62 38   // -458f-9536-267b8
                                                                                                      65 64 34 61 32 34 34 00 00 )                      // ed4a244..
      .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78   // ....T..WrapNonEx
                                                                                                                 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 )       // ceptionThrows.
      .custom instance void [mscorlib]System.Runtime.InteropServices.ComVisibleAttribute::.ctor(bool) = ( 01 00 00 00 00 ) 
      .custom instance void [mscorlib]System.Runtime.Versioning.TargetFrameworkAttribute::.ctor(string) = ( 01 00 1A 2E 4E 45 54 46 72 61 6D 65 77 6F 72 6B   // ....NETFramework
                                                                                                            2C 56 65 72 73 69 6F 6E 3D 76 34 2E 30 01 00 54   // ,Version=v4.0..T
                                                                                                            0E 14 46 72 61 6D 65 77 6F 72 6B 44 69 73 70 6C   // ..FrameworkDispl
                                                                                                            61 79 4E 61 6D 65 10 2E 4E 45 54 20 46 72 61 6D   // ayName..NET Fram
                                                                                                            65 77 6F 72 6B 20 34 )                            // ework 4
      .custom instance void [mscorlib]System.Reflection.AssemblyFileVersionAttribute::.ctor(string) = ( 01 00 07 31 2E 30 2E 30 2E 30 00 00 )             // ...1.0.0.0..
      .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) 
      .custom instance void [mscorlib]System.Reflection.AssemblyTitleAttribute::.ctor(string) = ( 01 00 07 6D 79 73 6F 75 6E 64 00 00 )             // ...mysound..
      .custom instance void [mscorlib]System.Reflection.AssemblyDescriptionAttribute::.ctor(string) = ( 01 00 00 00 00 ) 
      .custom instance void [mscorlib]System.Reflection.AssemblyCompanyAttribute::.ctor(string) = ( 01 00 00 00 00 ) 
      .custom instance void [mscorlib]System.Reflection.AssemblyProductAttribute::.ctor(string) = ( 01 00 07 6D 79 73 6F 75 6E 64 00 00 )             // ...mysound..
      .custom instance void [mscorlib]System.Reflection.AssemblyCopyrightAttribute::.ctor(string) = ( 01 00 12 43 6F 70 79 72 69 67 68 74 20 C2 A9 20   // ...Copyright .. 
                                                                                                      20 32 30 32 30 00 00 )                            //  2020..
      .custom instance void [mscorlib]System.Reflection.AssemblyTrademarkAttribute::.ctor(string) = ( 01 00 00 00 00 ) 
      .hash algorithm 0x00008004
      .ver 1:0:0:0
    }
    .mresource public mysound.Resources.resources
    {
      // Offset: 0x00000000 Length: 0x000000B4
      // WARNING: managed resource file mysound.Resources.resources created
    }
    .module mysound.dll
    // MVID: {F006EBD1-E904-4D2D-936A-C035E1660EFD}
    .imagebase 0x10000000
    .file alignment 0x00000200
    .stackreserve 0x00100000
    .subsystem 0x0003       // WINDOWS_CUI
    .corflags 0x00000002    //  32BITREQUIRED
    // Image base: 0x02210000
    .vtfixup [3] int32 fromunmanaged at D_00004000 // 06000018 06000019 0600001A
    
    
    // =============== CLASS MEMBERS DECLARATION ===================
    
    .class private auto ansi mysound.My.MyApplication
           extends [Microsoft.VisualBasic]Microsoft.VisualBasic.ApplicationServices.ApplicationBase
    {
      .custom instance void [System]System.CodeDom.Compiler.GeneratedCodeAttribute::.ctor(string,
                                                                                          string) = ( 01 00 0A 4D 79 54 65 6D 70 6C 61 74 65 08 31 31   // ...MyTemplate.11
                                                                                                      2E 30 2E 30 2E 30 00 00 )                         // .0.0.0..
      .custom instance void [System]System.ComponentModel.EditorBrowsableAttribute::.ctor(valuetype [System]System.ComponentModel.EditorBrowsableState) = ( 01 00 01 00 00 00 00 00 ) 
      .method public specialname rtspecialname 
              instance void  .ctor() cil managed
      {
        // Code size       7 (0x7)
        .maxstack  8
        IL_0000:  ldarg.0
        IL_0001:  call       instance void [Microsoft.VisualBasic]Microsoft.VisualBasic.ApplicationServices.ApplicationBase::.ctor()
        IL_0006:  ret
      } // end of method MyApplication::.ctor
    
    } // end of class mysound.My.MyApplication
    
    .class private auto ansi mysound.My.MyComputer
           extends [Microsoft.VisualBasic]Microsoft.VisualBasic.Devices.Computer
    {
      .custom instance void [System]System.CodeDom.Compiler.GeneratedCodeAttribute::.ctor(string,
                                                                                          string) = ( 01 00 0A 4D 79 54 65 6D 70 6C 61 74 65 08 31 31   // ...MyTemplate.11
                                                                                                      2E 30 2E 30 2E 30 00 00 )                         // .0.0.0..
      .custom instance void [System]System.ComponentModel.EditorBrowsableAttribute::.ctor(valuetype [System]System.ComponentModel.EditorBrowsableState) = ( 01 00 01 00 00 00 00 00 ) 
      .method public specialname rtspecialname 
              instance void  .ctor() cil managed
      {
        .custom instance void [System]System.ComponentModel.EditorBrowsableAttribute::.ctor(valuetype [System]System.ComponentModel.EditorBrowsableState) = ( 01 00 01 00 00 00 00 00 ) 
        .custom instance void [mscorlib]System.Diagnostics.DebuggerHiddenAttribute::.ctor() = ( 01 00 00 00 ) 
        // Code size       7 (0x7)
        .maxstack  8
        IL_0000:  ldarg.0
        IL_0001:  call       instance void [Microsoft.VisualBasic]Microsoft.VisualBasic.Devices.Computer::.ctor()
        IL_0006:  ret
      } // end of method MyComputer::.ctor
    
    } // end of class mysound.My.MyComputer
    
    .class private auto ansi sealed beforefieldinit mysound.My.MyProject
           extends [mscorlib]System.Object
    {
      .custom instance void [Microsoft.VisualBasic]Microsoft.VisualBasic.HideModuleNameAttribute::.ctor() = ( 01 00 00 00 ) 
      .custom instance void [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices.StandardModuleAttribute::.ctor() = ( 01 00 00 00 ) 
      .custom instance void [System]System.CodeDom.Compiler.GeneratedCodeAttribute::.ctor(string,
                                                                                          string) = ( 01 00 0A 4D 79 54 65 6D 70 6C 61 74 65 08 31 31   // ...MyTemplate.11
                                                                                                      2E 30 2E 30 2E 30 00 00 )                         // .0.0.0..
      .class auto ansi sealed nested assembly MyWebServices
             extends [mscorlib]System.Object
      {
        .custom instance void [Microsoft.VisualBasic]Microsoft.VisualBasic.MyGroupCollectionAttribute::.ctor(string,
                                                                                                             string,
                                                                                                             string,
                                                                                                             string) = ( 01 00 34 53 79 73 74 65 6D 2E 57 65 62 2E 53 65   // ..4System.Web.Se
                                                                                                                         72 76 69 63 65 73 2E 50 72 6F 74 6F 63 6F 6C 73   // rvices.Protocols
                                                                                                                         2E 53 6F 61 70 48 74 74 70 43 6C 69 65 6E 74 50   // .SoapHttpClientP
                                                                                                                         72 6F 74 6F 63 6F 6C 12 43 72 65 61 74 65 5F 5F   // rotocol.Create__
                                                                                                                         49 6E 73 74 61 6E 63 65 5F 5F 13 44 69 73 70 6F   // Instance__.Dispo
                                                                                                                         73 65 5F 5F 49 6E 73 74 61 6E 63 65 5F 5F 00 00   // se__Instance__..
                                                                                                                         00 ) 
        .custom instance void [System]System.ComponentModel.EditorBrowsableAttribute::.ctor(valuetype [System]System.ComponentModel.EditorBrowsableState) = ( 01 00 01 00 00 00 00 00 ) 
        .method public hidebysig strict virtual 
                instance bool  Equals(object o) cil managed
        {
          .custom instance void [mscorlib]System.Diagnostics.DebuggerHiddenAttribute::.ctor() = ( 01 00 00 00 ) 
          .custom instance void [System]System.ComponentModel.EditorBrowsableAttribute::.ctor(valuetype [System]System.ComponentModel.EditorBrowsableState) = ( 01 00 01 00 00 00 00 00 ) 
          // Code size       13 (0xd)
          .maxstack  8
          IL_0000:  ldarg.0
          IL_0001:  ldarg.1
          IL_0002:  call       object [mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::GetObjectValue(object)
          IL_0007:  call       instance bool [mscorlib]System.Object::Equals(object)
          IL_000c:  ret
        } // end of method MyWebServices::Equals
    
        .method public hidebysig strict virtual 
                instance int32  GetHashCode() cil managed
        {
          .custom instance void [mscorlib]System.Diagnostics.DebuggerHiddenAttribute::.ctor() = ( 01 00 00 00 ) 
          .custom instance void [System]System.ComponentModel.EditorBrowsableAttribute::.ctor(valuetype [System]System.ComponentModel.EditorBrowsableState) = ( 01 00 01 00 00 00 00 00 ) 
          // Code size       7 (0x7)
          .maxstack  8
          IL_0000:  ldarg.0
          IL_0001:  call       instance int32 [mscorlib]System.Object::GetHashCode()
          IL_0006:  ret
        } // end of method MyWebServices::GetHashCode
    
        .method assembly hidebysig instance class [mscorlib]System.Type 
                GetType() cil managed
        {
          .custom instance void [System]System.ComponentModel.EditorBrowsableAttribute::.ctor(valuetype [System]System.ComponentModel.EditorBrowsableState) = ( 01 00 01 00 00 00 00 00 ) 
          .custom instance void [mscorlib]System.Diagnostics.DebuggerHiddenAttribute::.ctor() = ( 01 00 00 00 ) 
          // Code size       11 (0xb)
          .maxstack  8
          IL_0000:  ldtoken    mysound.My.MyProject/MyWebServices
          IL_0005:  call       class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
          IL_000a:  ret
        } // end of method MyWebServices::GetType
    
        .method public hidebysig strict virtual 
                instance string  ToString() cil managed
        {
          .custom instance void [System]System.ComponentModel.EditorBrowsableAttribute::.ctor(valuetype [System]System.ComponentModel.EditorBrowsableState) = ( 01 00 01 00 00 00 00 00 ) 
          .custom instance void [mscorlib]System.Diagnostics.DebuggerHiddenAttribute::.ctor() = ( 01 00 00 00 ) 
          // Code size       7 (0x7)
          .maxstack  8
          IL_0000:  ldarg.0
          IL_0001:  call       instance string [mscorlib]System.Object::ToString()
          IL_0006:  ret
        } // end of method MyWebServices::ToString
    
        .method private static !!T  Create__Instance__<.ctor T>(!!T 'instance') cil managed
        {
          .custom instance void [mscorlib]System.Diagnostics.DebuggerHiddenAttribute::.ctor() = ( 01 00 00 00 ) 
          // Code size       20 (0x14)
          .maxstack  1
          .locals init (!!T V_0)
          IL_0000:  ldarg.0
          IL_0001:  box        !!T
          IL_0006:  brtrue.s   IL_0010
    
          IL_0008:  call       !!0 [mscorlib]System.Activator::CreateInstance<!!0>()
          IL_000d:  stloc.0
          IL_000e:  br.s       IL_0012
    
          IL_0010:  ldarg.0
          IL_0011:  stloc.0
          IL_0012:  ldloc.0
          IL_0013:  ret
        } // end of method MyWebServices::Create__Instance__
    
        .method private instance void  Dispose__Instance__<T>(!!T& 'instance') cil managed
        {
          .custom instance void [mscorlib]System.Diagnostics.DebuggerHiddenAttribute::.ctor() = ( 01 00 00 00 ) 
          // Code size       8 (0x8)
          .maxstack  8
          IL_0000:  ldarg.1
          IL_0001:  initobj    !!T
          IL_0007:  ret
        } // end of method MyWebServices::Dispose__Instance__
    
        .method public specialname rtspecialname 
                instance void  .ctor() cil managed
        {
          .custom instance void [mscorlib]System.Diagnostics.DebuggerHiddenAttribute::.ctor() = ( 01 00 00 00 ) 
          .custom instance void [System]System.ComponentModel.EditorBrowsableAttribute::.ctor(valuetype [System]System.ComponentModel.EditorBrowsableState) = ( 01 00 01 00 00 00 00 00 ) 
          // Code size       7 (0x7)
          .maxstack  8
          IL_0000:  ldarg.0
          IL_0001:  call       instance void [mscorlib]System.Object::.ctor()
          IL_0006:  ret
        } // end of method MyWebServices::.ctor
    
      } // end of class MyWebServices
    
      .class auto ansi sealed nested assembly ThreadSafeObjectProvider`1<.ctor T>
             extends [mscorlib]System.Object
      {
        .custom instance void [System]System.ComponentModel.EditorBrowsableAttribute::.ctor(valuetype [System]System.ComponentModel.EditorBrowsableState) = ( 01 00 01 00 00 00 00 00 ) 
        .custom instance void [mscorlib]System.Runtime.InteropServices.ComVisibleAttribute::.ctor(bool) = ( 01 00 00 00 00 ) 
        .field private initonly class [Microsoft.VisualBasic]Microsoft.VisualBasic.MyServices.Internal.ContextValue`1<!T> m_Context
        .method assembly specialname instance !T 
                get_GetInstance() cil managed
        {
          .custom instance void [mscorlib]System.Diagnostics.DebuggerHiddenAttribute::.ctor() = ( 01 00 00 00 ) 
          // Code size       40 (0x28)
          .maxstack  2
          .locals init (!T V_0)
          IL_0000:  ldarg.0
          IL_0001:  ldfld      class [Microsoft.VisualBasic]Microsoft.VisualBasic.MyServices.Internal.ContextValue`1<!0> class mysound.My.MyProject/ThreadSafeObjectProvider`1<!T>::m_Context
          IL_0006:  callvirt   instance !0 class [Microsoft.VisualBasic]Microsoft.VisualBasic.MyServices.Internal.ContextValue`1<!T>::get_Value()
          IL_000b:  stloc.0
          IL_000c:  ldloc.0
          IL_000d:  box        !T
          IL_0012:  brtrue.s   IL_0026
    
          IL_0014:  call       !!0 [mscorlib]System.Activator::CreateInstance<!T>()
          IL_0019:  stloc.0
          IL_001a:  ldarg.0
          IL_001b:  ldfld      class [Microsoft.VisualBasic]Microsoft.VisualBasic.MyServices.Internal.ContextValue`1<!0> class mysound.My.MyProject/ThreadSafeObjectProvider`1<!T>::m_Context
          IL_0020:  ldloc.0
          IL_0021:  callvirt   instance void class [Microsoft.VisualBasic]Microsoft.VisualBasic.MyServices.Internal.ContextValue`1<!T>::set_Value(!0)
          IL_0026:  ldloc.0
          IL_0027:  ret
        } // end of method ThreadSafeObjectProvider`1::get_GetInstance
    
        .method public specialname rtspecialname 
                instance void  .ctor() cil managed
        {
          .custom instance void [mscorlib]System.Diagnostics.DebuggerHiddenAttribute::.ctor() = ( 01 00 00 00 ) 
          .custom instance void [System]System.ComponentModel.EditorBrowsableAttribute::.ctor(valuetype [System]System.ComponentModel.EditorBrowsableState) = ( 01 00 01 00 00 00 00 00 ) 
          // Code size       18 (0x12)
          .maxstack  8
          IL_0000:  ldarg.0
          IL_0001:  call       instance void [mscorlib]System.Object::.ctor()
          IL_0006:  ldarg.0
          IL_0007:  newobj     instance void class [Microsoft.VisualBasic]Microsoft.VisualBasic.MyServices.Internal.ContextValue`1<!T>::.ctor()
          IL_000c:  stfld      class [Microsoft.VisualBasic]Microsoft.VisualBasic.MyServices.Internal.ContextValue`1<!0> class mysound.My.MyProject/ThreadSafeObjectProvider`1<!T>::m_Context
          IL_0011:  ret
        } // end of method ThreadSafeObjectProvider`1::.ctor
    
        .property instance !T GetInstance()
        {
          .get instance !T mysound.My.MyProject/ThreadSafeObjectProvider`1::get_GetInstance()
        } // end of property ThreadSafeObjectProvider`1::GetInstance
      } // end of class ThreadSafeObjectProvider`1
    
      .field private static initonly class mysound.My.MyProject/ThreadSafeObjectProvider`1<class mysound.My.MyComputer> m_ComputerObjectProvider
      .field private static initonly class mysound.My.MyProject/ThreadSafeObjectProvider`1<class mysound.My.MyApplication> m_AppObjectProvider
      .field private static initonly class mysound.My.MyProject/ThreadSafeObjectProvider`1<class [Microsoft.VisualBasic]Microsoft.VisualBasic.ApplicationServices.User> m_UserObjectProvider
      .field private static initonly class mysound.My.MyProject/ThreadSafeObjectProvider`1<class mysound.My.MyProject/MyWebServices> m_MyWebServicesObjectProvider
      .method private specialname rtspecialname static 
              void  .cctor() cil managed
      {
        // Code size       41 (0x29)
        .maxstack  8
        IL_0000:  newobj     instance void class mysound.My.MyProject/ThreadSafeObjectProvider`1<class mysound.My.MyComputer>::.ctor()
        IL_0005:  stsfld     class mysound.My.MyProject/ThreadSafeObjectProvider`1<class mysound.My.MyComputer> mysound.My.MyProject::m_ComputerObjectProvider
        IL_000a:  newobj     instance void class mysound.My.MyProject/ThreadSafeObjectProvider`1<class mysound.My.MyApplication>::.ctor()
        IL_000f:  stsfld     class mysound.My.MyProject/ThreadSafeObjectProvider`1<class mysound.My.MyApplication> mysound.My.MyProject::m_AppObjectProvider
        IL_0014:  newobj     instance void class mysound.My.MyProject/ThreadSafeObjectProvider`1<class [Microsoft.VisualBasic]Microsoft.VisualBasic.ApplicationServices.User>::.ctor()
        IL_0019:  stsfld     class mysound.My.MyProject/ThreadSafeObjectProvider`1<class [Microsoft.VisualBasic]Microsoft.VisualBasic.ApplicationServices.User> mysound.My.MyProject::m_UserObjectProvider
        IL_001e:  newobj     instance void class mysound.My.MyProject/ThreadSafeObjectProvider`1<class mysound.My.MyProject/MyWebServices>::.ctor()
        IL_0023:  stsfld     class mysound.My.MyProject/ThreadSafeObjectProvider`1<class mysound.My.MyProject/MyWebServices> mysound.My.MyProject::m_MyWebServicesObjectProvider
        IL_0028:  ret
      } // end of method MyProject::.cctor
    
      .method assembly specialname static class mysound.My.MyComputer 
              get_Computer() cil managed
      {
        .custom instance void [mscorlib]System.Diagnostics.DebuggerHiddenAttribute::.ctor() = ( 01 00 00 00 ) 
        // Code size       11 (0xb)
        .maxstack  8
        IL_0000:  ldsfld     class mysound.My.MyProject/ThreadSafeObjectProvider`1<class mysound.My.MyComputer> mysound.My.MyProject::m_ComputerObjectProvider
        IL_0005:  callvirt   instance !0 class mysound.My.MyProject/ThreadSafeObjectProvider`1<class mysound.My.MyComputer>::get_GetInstance()
        IL_000a:  ret
      } // end of method MyProject::get_Computer
    
      .method assembly specialname static class mysound.My.MyApplication 
              get_Application() cil managed
      {
        .custom instance void [mscorlib]System.Diagnostics.DebuggerHiddenAttribute::.ctor() = ( 01 00 00 00 ) 
        // Code size       11 (0xb)
        .maxstack  8
        IL_0000:  ldsfld     class mysound.My.MyProject/ThreadSafeObjectProvider`1<class mysound.My.MyApplication> mysound.My.MyProject::m_AppObjectProvider
        IL_0005:  callvirt   instance !0 class mysound.My.MyProject/ThreadSafeObjectProvider`1<class mysound.My.MyApplication>::get_GetInstance()
        IL_000a:  ret
      } // end of method MyProject::get_Application
    
      .method assembly specialname static class [Microsoft.VisualBasic]Microsoft.VisualBasic.ApplicationServices.User 
              get_User() cil managed
      {
        .custom instance void [mscorlib]System.Diagnostics.DebuggerHiddenAttribute::.ctor() = ( 01 00 00 00 ) 
        // Code size       11 (0xb)
        .maxstack  8
        IL_0000:  ldsfld     class mysound.My.MyProject/ThreadSafeObjectProvider`1<class [Microsoft.VisualBasic]Microsoft.VisualBasic.ApplicationServices.User> mysound.My.MyProject::m_UserObjectProvider
        IL_0005:  callvirt   instance !0 class mysound.My.MyProject/ThreadSafeObjectProvider`1<class [Microsoft.VisualBasic]Microsoft.VisualBasic.ApplicationServices.User>::get_GetInstance()
        IL_000a:  ret
      } // end of method MyProject::get_User
    
      .method assembly specialname static class mysound.My.MyProject/MyWebServices 
              get_WebServices() cil managed
      {
        .custom instance void [mscorlib]System.Diagnostics.DebuggerHiddenAttribute::.ctor() = ( 01 00 00 00 ) 
        // Code size       11 (0xb)
        .maxstack  8
        IL_0000:  ldsfld     class mysound.My.MyProject/ThreadSafeObjectProvider`1<class mysound.My.MyProject/MyWebServices> mysound.My.MyProject::m_MyWebServicesObjectProvider
        IL_0005:  callvirt   instance !0 class mysound.My.MyProject/ThreadSafeObjectProvider`1<class mysound.My.MyProject/MyWebServices>::get_GetInstance()
        IL_000a:  ret
      } // end of method MyProject::get_WebServices
    
      .property class mysound.My.MyComputer Computer()
      {
        .custom instance void [System]System.ComponentModel.Design.HelpKeywordAttribute::.ctor(string) = ( 01 00 0B 4D 79 2E 43 6F 6D 70 75 74 65 72 00 00 ) // ...My.Computer..
        .get class mysound.My.MyComputer mysound.My.MyProject::get_Computer()
      } // end of property MyProject::Computer
      .property class mysound.My.MyApplication
              Application()
      {
        .custom instance void [System]System.ComponentModel.Design.HelpKeywordAttribute::.ctor(string) = ( 01 00 0E 4D 79 2E 41 70 70 6C 69 63 61 74 69 6F   // ...My.Applicatio
                                                                                                           6E 00 00 )                                        // n..
        .get class mysound.My.MyApplication mysound.My.MyProject::get_Application()
      } // end of property MyProject::Application
      .property class [Microsoft.VisualBasic]Microsoft.VisualBasic.ApplicationServices.User
              User()
      {
        .custom instance void [System]System.ComponentModel.Design.HelpKeywordAttribute::.ctor(string) = ( 01 00 07 4D 79 2E 55 73 65 72 00 00 )             // ...My.User..
        .get class [Microsoft.VisualBasic]Microsoft.VisualBasic.ApplicationServices.User mysound.My.MyProject::get_User()
      } // end of property MyProject::User
      .property class mysound.My.MyProject/MyWebServices
              WebServices()
      {
        .custom instance void [System]System.ComponentModel.Design.HelpKeywordAttribute::.ctor(string) = ( 01 00 0E 4D 79 2E 57 65 62 53 65 72 76 69 63 65   // ...My.WebService
                                                                                                           73 00 00 )                                        // s..
        .get class mysound.My.MyProject/MyWebServices mysound.My.MyProject::get_WebServices()
      } // end of property MyProject::WebServices
    } // end of class mysound.My.MyProject
    
    .class private auto ansi sealed mysound.My.Resources.Resources
           extends [mscorlib]System.Object
    {
      .custom instance void [System]System.CodeDom.Compiler.GeneratedCodeAttribute::.ctor(string,
                                                                                          string) = ( 01 00 33 53 79 73 74 65 6D 2E 52 65 73 6F 75 72   // ..3System.Resour
                                                                                                      63 65 73 2E 54 6F 6F 6C 73 2E 53 74 72 6F 6E 67   // ces.Tools.Strong
                                                                                                      6C 79 54 79 70 65 64 52 65 73 6F 75 72 63 65 42   // lyTypedResourceB
                                                                                                      75 69 6C 64 65 72 07 34 2E 30 2E 30 2E 30 00 00 ) // uilder.4.0.0.0..
      .custom instance void [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices.StandardModuleAttribute::.ctor() = ( 01 00 00 00 ) 
      .custom instance void [mscorlib]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) 
      .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) 
      .custom instance void [Microsoft.VisualBasic]Microsoft.VisualBasic.HideModuleNameAttribute::.ctor() = ( 01 00 00 00 ) 
      .field private static class [mscorlib]System.Resources.ResourceManager resourceMan
      .field private static class [mscorlib]System.Globalization.CultureInfo resourceCulture
      .method assembly specialname static class [mscorlib]System.Resources.ResourceManager 
              get_ResourceManager() cil managed
      {
        // Code size       49 (0x31)
        .maxstack  8
        IL_0000:  ldsfld     class [mscorlib]System.Resources.ResourceManager mysound.My.Resources.Resources::resourceMan
        IL_0005:  ldnull
        IL_0006:  call       bool [mscorlib]System.Object::ReferenceEquals(object,
                                                                           object)
        IL_000b:  brfalse.s  IL_002b
    
        IL_000d:  ldstr      "mysound.Resources"
        IL_0012:  ldtoken    mysound.My.Resources.Resources
        IL_0017:  call       class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
        IL_001c:  callvirt   instance class [mscorlib]System.Reflection.Assembly [mscorlib]System.Type::get_Assembly()
        IL_0021:  newobj     instance void [mscorlib]System.Resources.ResourceManager::.ctor(string,
                                                                                             class [mscorlib]System.Reflection.Assembly)
        IL_0026:  stsfld     class [mscorlib]System.Resources.ResourceManager mysound.My.Resources.Resources::resourceMan
        IL_002b:  ldsfld     class [mscorlib]System.Resources.ResourceManager mysound.My.Resources.Resources::resourceMan
        IL_0030:  ret
      } // end of method Resources::get_ResourceManager
    
      .method assembly specialname static class [mscorlib]System.Globalization.CultureInfo 
              get_Culture() cil managed
      {
        // Code size       6 (0x6)
        .maxstack  8
        IL_0000:  ldsfld     class [mscorlib]System.Globalization.CultureInfo mysound.My.Resources.Resources::resourceCulture
        IL_0005:  ret
      } // end of method Resources::get_Culture
    
      .method assembly specialname static void 
              set_Culture(class [mscorlib]System.Globalization.CultureInfo 'value') cil managed
      {
        // Code size       7 (0x7)
        .maxstack  8
        IL_0000:  ldarg.0
        IL_0001:  stsfld     class [mscorlib]System.Globalization.CultureInfo mysound.My.Resources.Resources::resourceCulture
        IL_0006:  ret
      } // end of method Resources::set_Culture
    
      .property class [mscorlib]System.Resources.ResourceManager
              ResourceManager()
      {
        .custom instance void [System]System.ComponentModel.EditorBrowsableAttribute::.ctor(valuetype [System]System.ComponentModel.EditorBrowsableState) = ( 01 00 02 00 00 00 00 00 ) 
        .get class [mscorlib]System.Resources.ResourceManager mysound.My.Resources.Resources::get_ResourceManager()
      } // end of property Resources::ResourceManager
      .property class [mscorlib]System.Globalization.CultureInfo
              Culture()
      {
        .custom instance void [System]System.ComponentModel.EditorBrowsableAttribute::.ctor(valuetype [System]System.ComponentModel.EditorBrowsableState) = ( 01 00 02 00 00 00 00 00 ) 
        .set void mysound.My.Resources.Resources::set_Culture(class [mscorlib]System.Globalization.CultureInfo)
        .get class [mscorlib]System.Globalization.CultureInfo mysound.My.Resources.Resources::get_Culture()
      } // end of property Resources::Culture
    } // end of class mysound.My.Resources.Resources
    
    .class private auto ansi sealed beforefieldinit mysound.My.MySettings
           extends [System]System.Configuration.ApplicationSettingsBase
    {
      .custom instance void [System]System.CodeDom.Compiler.GeneratedCodeAttribute::.ctor(string,
                                                                                          string) = ( 01 00 4B 4D 69 63 72 6F 73 6F 66 74 2E 56 69 73   // ..KMicrosoft.Vis
                                                                                                      75 61 6C 53 74 75 64 69 6F 2E 45 64 69 74 6F 72   // ualStudio.Editor
                                                                                                      73 2E 53 65 74 74 69 6E 67 73 44 65 73 69 67 6E   // s.SettingsDesign
                                                                                                      65 72 2E 53 65 74 74 69 6E 67 73 53 69 6E 67 6C   // er.SettingsSingl
                                                                                                      65 46 69 6C 65 47 65 6E 65 72 61 74 6F 72 08 31   // eFileGenerator.1
                                                                                                      31 2E 30 2E 30 2E 30 00 00 )                      // 1.0.0.0..
      .custom instance void [System]System.ComponentModel.EditorBrowsableAttribute::.ctor(valuetype [System]System.ComponentModel.EditorBrowsableState) = ( 01 00 02 00 00 00 00 00 ) 
      .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) 
      .field private static class mysound.My.MySettings defaultInstance
      .method private specialname rtspecialname static 
              void  .cctor() cil managed
      {
        // Code size       21 (0x15)
        .maxstack  8
        IL_0000:  newobj     instance void mysound.My.MySettings::.ctor()
        IL_0005:  call       class [System]System.Configuration.SettingsBase [System]System.Configuration.SettingsBase::Synchronized(class [System]System.Configuration.SettingsBase)
        IL_000a:  castclass  mysound.My.MySettings
        IL_000f:  stsfld     class mysound.My.MySettings mysound.My.MySettings::defaultInstance
        IL_0014:  ret
      } // end of method MySettings::.cctor
    
      .method public specialname rtspecialname 
              instance void  .ctor() cil managed
      {
        // Code size       7 (0x7)
        .maxstack  8
        IL_0000:  ldarg.0
        IL_0001:  call       instance void [System]System.Configuration.ApplicationSettingsBase::.ctor()
        IL_0006:  ret
      } // end of method MySettings::.ctor
    
      .method public specialname static class mysound.My.MySettings 
              get_Default() cil managed
      {
        // Code size       6 (0x6)
        .maxstack  8
        IL_0000:  ldsfld     class mysound.My.MySettings mysound.My.MySettings::defaultInstance
        IL_0005:  ret
      } // end of method MySettings::get_Default
    
      .property class mysound.My.MySettings Default()
      {
        .get class mysound.My.MySettings mysound.My.MySettings::get_Default()
      } // end of property MySettings::Default
    } // end of class mysound.My.MySettings
    
    .class private auto ansi sealed mysound.My.MySettingsProperty
           extends [mscorlib]System.Object
    {
      .custom instance void [mscorlib]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) 
      .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) 
      .custom instance void [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices.StandardModuleAttribute::.ctor() = ( 01 00 00 00 ) 
      .custom instance void [Microsoft.VisualBasic]Microsoft.VisualBasic.HideModuleNameAttribute::.ctor() = ( 01 00 00 00 ) 
      .method assembly specialname static class mysound.My.MySettings 
              get_Settings() cil managed
      {
        // Code size       6 (0x6)
        .maxstack  8
        IL_0000:  call       class mysound.My.MySettings mysound.My.MySettings::get_Default()
        IL_0005:  ret
      } // end of method MySettingsProperty::get_Settings
    
      .property class mysound.My.MySettings Settings()
      {
        .custom instance void [System]System.ComponentModel.Design.HelpKeywordAttribute::.ctor(string) = ( 01 00 0B 4D 79 2E 53 65 74 74 69 6E 67 73 00 00 ) // ...My.Settings..
        .get class mysound.My.MySettings mysound.My.MySettingsProperty::get_Settings()
      } // end of property MySettingsProperty::Settings
    } // end of class mysound.My.MySettingsProperty
    
    .class private auto ansi sealed mysound.Module1
           extends [mscorlib]System.Object
    {
      .custom instance void [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices.StandardModuleAttribute::.ctor() = ( 01 00 00 00 ) 
      .method public static void modopt([mscorlib]System.Runtime.CompilerServices.CallConvStdcall) 
              MyHello() cil managed
      {
        .vtentry 1 : 1
        .export [0] as MyHello
        // Code size       18 (0x12)
        .maxstack  8
        IL_0000:  ldstr      "hello from .net code!"
        IL_0005:  ldc.i4.0
        IL_0006:  ldstr      "Hi"
        IL_000b:  call       valuetype [Microsoft.VisualBasic]Microsoft.VisualBasic.MsgBoxResult [Microsoft.VisualBasic]Microsoft.VisualBasic.Interaction::MsgBox(object,
                                                                                                                                                                  valuetype [Microsoft.VisualBasic]Microsoft.VisualBasic.MsgBoxStyle,
                                                                                                                                                                  object)
        IL_0010:  pop
        IL_0011:  ret
      } // end of method Module1::MyHello
    
      .method public static void modopt([mscorlib]System.Runtime.CompilerServices.CallConvStdcall) 
              PlaySound(string strWave) cil managed
      {
        .vtentry 1 : 2
        .export [1] as PlaySound
        // Code size       18 (0x12)
        .maxstack  8
        IL_0000:  call       class mysound.My.MyComputer mysound.My.MyProject::get_Computer()
        IL_0005:  callvirt   instance class [Microsoft.VisualBasic]Microsoft.VisualBasic.Devices.Audio [Microsoft.VisualBasic]Microsoft.VisualBasic.Devices.Computer::get_Audio()
        IL_000a:  ldarg.0
        IL_000b:  ldc.i4.1
        IL_000c:  callvirt   instance void [Microsoft.VisualBasic]Microsoft.VisualBasic.Devices.Audio::Play(string,
                                                                                                            valuetype [Microsoft.VisualBasic]Microsoft.VisualBasic.AudioPlayMode)
        IL_0011:  ret
      } // end of method Module1::PlaySound
    
      .method public static int32 modopt([mscorlib]System.Runtime.CompilerServices.CallConvStdcall) 
              Time2(int32& x) cil managed
      {
        .vtentry 1 : 3
        .export [2] as Time2
        // Code size       5 (0x5)
        .maxstack  8
        IL_0000:  ldarg.0
        IL_0001:  ldind.i4
        IL_0002:  ldc.i4.2
        IL_0003:  mul.ovf
        IL_0004:  ret
      } // end of method Module1::Time2
    
    } // end of class mysound.Module1
    
    
    // =============================================================
    
    .data D_00004000 = bytearray (
                     18 00 00 06 19 00 00 06 1A 00 00 06) 
    // *********** DISASSEMBLY COMPLETE ***********************
    // WARNING: Created Win32 resource file mysound.res



    • Edited by RLWA32 Tuesday, February 11, 2020 11:45 AM
    Tuesday, February 11, 2020 11:31 AM
  • >You implied that DOS programs can use DLLs.

    Yes, and they can! I did not hint at this, I FLAT out claimed as such. But NOT a windows .dll!!!! Huge huge difference here!

    Even QuickBasic that shipped with most versions of DOS had support for calling external .dll’s. (You had to pass the address). Mind you in most cases you had to provide your own loader. But yes, programs running on DOS most certainly used .dll.

    You could load a .dll into ram with assembler, and the call the entry points.

    Should I point out that windows 3.1 ran on top of DOS!!!

    If you saying that DOS itself did not have direct support for .dll’s? That is correct, but development systems running on DOS most certainly did implement and use .dll’s. And that includes NON windows development programs.

    So, DOS programs, even DOS based FoxPro, QuickBasic, Turbo C, and “many” other programming systems on DOS have support for calling external code libraries. In most cases they were “libs” and a linker, but it certainly was a common approach, and some used .dll’s. Nothing was preventing the loading of such .dll’s into ram and calling them based on their entry points.

    Remember, we not talking about an ActiveX, or COM based .dll. Those were NOT available on DOS machines. (Because they did not have ActiveX support).

    However an external library as a .dll? Yes, that was most certainly the case.

    “COM” is well defined and has protocols for creating objects, and exposing methods.  COM based .dll’s have an “interface”. You in general have to register these types of .dll.

    A non COM .dll is simply a set of entry points into that .dll. (based on a set of calling standards). As noted with these .dll’s you not creating instances of class, but only calling or “jumping” into a set of entry points. There is NO inter process communication here. There is no need to even register such a .dll.

    In that VBA example I posted, there is no “creating” of objects, and there is no need to “register” the .dll.

    When you call such .dll from VBA (as my API examples show), you are actually jumping to that external code. There is NOT some itner-process communication occurring here. You are simply calling external lib or code in a .dll, and when it is done, the code returns to the calling code. It is not really different then calling a sub or function.

    My working sample demonstrated how this is done.

    You can create such .dll’s with c, c++, Assembler, and as I demonstrated with sample code even vb.net!!! (if you use the .dll export utility I noted).

    The result is a .dll that can be used – even from un-managed code. And again, as noted, you are NOT creating instances of objects. You simply making a call to some external code. As I pointed out, this is the MOST basic approach in software. Now of course that .dll is going to require windows and in fact also requires that .net is installed.

    You are doing a “fantastic” job of confusing the two types of .dlls (COM/ActiveX and non COM based .dlls).

    Often, these libraries were named “.lib”, but since they could be loaded at runtime and not static linked, then they are still by nature a “.dll”. And they can/could be in most cases static linked, but they don’t have to be. They can and were often loaded at runtime, and this approach was used by software running DOS machines.

    Not claiming there is a DOS based .dll, I am simply flat out telling you that programs running on DOS could load external .dlls and call the code inside of that .dll. End of story here!

    So, there seems to be HUGE confusing on your part as to the type of .dll am talking about (and that my code demonstrated).

    So, yes, on EVEN on DOS based systems, .dll’s were and could be used. Just not the COM or ActiveX types of .dll’s.

    And as noted, those .dll’s created with DOS only development tools could be both consumed by windows and DOS programs. (assuming you are now running windows on that DOS box).

    As long as such .dll’s did not use any windows features, but stuck to the basic input/output specs (BIOS), then such programs could be consumed by BOTH DOS programs and windows programs running on that platform!!!  I mean, how on earth do you think windows ran on top of DOS for all those years? 

    You mean now software can’t load a .dll into memory and can jump into the entry points of that software? Of course you can!

    Now, of course a windows 10 .dll can’t be used on Windows 95, or say from DOS box.

    But the overall concept:

    .dll’s with entry points?

    Yes, most certainly possible and was used on DOS machines.

    I am VERY perplexed why you are stating that .dll’s can’t be used on DOS systems when they were used as an “every day” matter of fact.

    Keep in mind that the “extension” of .dll really did not matter in most cases. After all it is just a chunk of callable code that you load and call.

    And that’s what my example does!

    >>No, not possible. In Windows DLLs

    I did not say or limit this discussion to “windows only” .dll’s. I said .dlls with entry points could be BOTH used by DOS and windows programs running on that SAME machine. And again what kind of .dll are you talking about? COM/ActiveX or a simple external .dll library file?

    >>So a "call" to BIOS involved the processor accessing the interrupt vector, not entry points set up by a linker.

    Only for interrupt based calls like keyboard etc. There are MANY routines that you could call from code to the BIOS that was not interrupt based. So, most requests to keyboard, output etc. were interrupt based, but not all such calls to the BIOS routines were based on a interrupts – they were called “in-process” from YOUR code.

    You “jumped” into an entry point in the BIOS and this was no different than jumping into some entry point in your OWN code or some external .dll.

    And this is why many a programs failed on non 100% compatible IBM machines. Because the developers(s) went out side of the standard BIOS interface. They just found and used some “handy” routines in the BIOS. They should not have done so, but they DID!!!!

    So, hardware and interrupts was a big part of BIOS, but a lot of your own code calling into the BIOS routines were NOT interrupt based. It was developers simply calling and jumping into “handy dandy" bits of code in the BIOS. Such calls had zero to do with interrupted based operation of BIOS and hardware and such code run as a result of a interrupt. 

    So sure, most of BIOS was based on interrupts because that’s how the hardware works. But there was “many” a handy dandy bits and subroutines in BIOS that were and are called by developers – and such calls were not based on hardware interrupts to call that code.

    Not all .dll’s are COM based.

    Not all calls to the BIOS were the result of an interrupts.

    Not all .dlls were based on windows software. Developers used them on DOS machines. 

    Many a developer tools simply allowed you to load or consume a .dll – and that consumption was not as a COM object, but that of simple external code you load and call by jumping into entry points  – this is EXACTLY what my sample code demonstrates.

    Regards,

    Albert D. Kallal (Access MVP 2003-2017)

    Edmonton, Alberta Canada

    Friday, February 14, 2020 7:51 PM
  • There is much that is incorrect there but I will limit my reply to the most clear.

    Often, these libraries were named “.lib”, but since they could be loaded at runtime and not static linked, then they are still by nature a “.dll”. And they can/could be in most cases static linked, but they don’t have to be. They can and were often loaded at runtime, and this approach was used by software running DOS machines.

    Originally compilers such as C and C++ produced object files as output. Most compilers still do. In the past it was common practice to combine multiple object files into a library. At link time every user of an object file get the object file combined into the executable file. Later dynamic linking was invented and the original linking methodology was called static linking. In Windows (at least) to implement dynamic linking the format of library files were changed such that they pointed to a DLL instead of containing object files. In Windows a lib file is read by the linker but is never used during execution. Think about it; it is never necessary to provide a lib file during execution of a Windows program.

    So sure, most of BIOS was based on interrupts because that’s how the hardware works. But there was “many” a handy dandy bits and subroutines in BIOS that were and are called by developers – and such calls were not based on hardware interrupts to call that code.

    There is an abundance of articles on this. See the following for just two of many.

    The second one shows many examples of calling the BIOS. As for calling any BIOS routines directly as you describe, you cannot find examples of that because it just does not work that way. Don't reply unless you have actual examples, not just theory.



    Sam Hobbs
    SimpleSamples.Info

    Friday, February 14, 2020 11:55 PM
  • >>Later dynamic linking was invented and the original linking methodology was called static linking.

    Sure, but that changes zero of what I claimed here.

    So, sure, but that process was not limited to just windows. Many a tools supported the loading of library code at run time - and as I pointed out often the user wrote some code to load that file into ram and call it. 

    Calls to BIOS is a moot point here and changes nothing. I do recall writing in assembler and calling a BIOS routine to re-boot the computer, and I did not use a interrupt. But, so what? Lets concede for this discussion that BIOS calls were in "general" if not all interrupt based. We could also get into a long discussion of what OEM's included in their ROM chip sets and if you call those parts BIOS or OEM features they included.

    That BIOS issue has ZERO to do with the fact that DOS programs and development tools for DOS dynamic loaded code routines and jumped into that code. End of story. The BIOS tangent is not really relevant here.

    But program tools running on DOS did load library code routines. And we should not confuse the term "library" code with a actual lib file (I been a bit lose on using that term).

    The fact that windows 3.1 run on top of DOS is another "messy" issue, but IS a given.

    But as noted, I not even limiting tools for windows.

    The simple most basic concept here is loading of library code at runtime and calling  it. And the most basic concept is THEN jumping into that code based on a set of entry points.

    This is is the most basic aspect of computer software - and DOS programs did this. (or better said programs running on DOS did this).

    R

    Albert



    Saturday, February 15, 2020 3:41 AM
  • Calls to BIOS is a moot point here and changes nothing.

    I agree. The subject is totally off-topic here. The BIOS should never had been mentioned here.

    I do recall writing in assembler and calling a BIOS routine to re-boot the computer, and I did not use a interrupt.

    Prove it. I provided samples of what I said. You provide samples of what you are saying; or better than that, let us just end this totally off-topic subject (BIOS).



    Sam Hobbs
    SimpleSamples.Info

    Saturday, February 15, 2020 5:10 AM
  • Note what I said:

    Calls into BIOS - NOT  a bios call!!!

    There is a WHOPPER of a difference here! What I recall is jumping to the BIOS routine - it executed a few lines of code and THEN executed the interrupt command! No one is attempting to argue that a BIOS calls don't not involve interrupts. I am pointing out a HUGE difference to jumping to a routine in the computers "ROMS" + BIOS. There is a BIG difference in terms of semantics here. The code I called MOST certainly did (eventually) execute a interrupt command - but my code did not!

    As I noted, this opens up a can of worms as to what parts of the ROMS are BIOS, and those that are OEM provided. However, I used the term "into" BIOS on purpose and my context has meaning as a result. That meaning is not to suggest that a BIOS call don't involve interrupts, but a call "into" BIOS does not necessary involve a interrupt. But, you can bet your bottom dollars that such cases will involve interrupts - it becomes a question then of who's code is doing the interrupt! And then  the issue is then what one means by a call into BIOS, or a BIOS call in the correct context!

    At the end of the day

    tools that ran on DOS produced callable library code.

    Such library code was callable from windows programs (again, context is important here - we talking 16 bit DOS and then windows (and again, that would be 16 bit windows in this context).

    However near any developer tool set from that time frame ranging from c, c++, Turbo pascal or even assembler? The result was a external file with code (.dll's). And that code could be loaded and called at runtime - hence "dynamic".

    And it turns out you can even create such external library routines from .net with the un-managed .dll export utility. The result is a .dll with entry points - EXACLTY what the poster asked about.

    The working example code is .net, but the result is the same idea, same concept, and is a PROOF of working concept code that actually runs. 

    That concept and issue is the most basic concept of a some external code with entry points, and as such is consumable from near any windows programming environment, including VBA which has support for calling external .dlls. As such, we not talking about a .dll with classes, not COM based, and there is no inter-process communication occurring here. It is a most fundamental building block of callable external subs and functions that are able to return values. 

    Not really making any other point then the above simple concept.

    Regards,

    Albert D. Kallal (Access MVP 2003-2017)
    Edmonton, Alberta Canada
    Monday, February 17, 2020 6:51 PM