Showing posts with label VB-callable DLL. Show all posts
Showing posts with label VB-callable DLL. Show all posts

Thursday, January 30, 2020

Updated VB.NET Sample Code

We recently had to help a customer get our DLL to work in their VB.Net environment because our sample code was so out of date. In order to spare other customers this pain, we post this code example. Note that this code is for version 37 (v37); change the version to match your particular situation.


Option Explicit On
Imports System.Deployment
Imports System.Runtime.InteropServices

Module Module1
    Private Declare Function mhdllver Lib "vbdrgv37.dll" (ByVal Buf As String,
    ByVal BufLen As Int32) As Integer

    Private Declare Function mhdrg1 Lib "vbdrgv37.dll" (ByRef drg As Int32,
    ByVal DRGVersion As String, ByVal MasksPath As String, ByVal DischStat As String,
    ByVal PtAge As String, ByVal PtGender As String, ByVal DXList As String,
    ByVal ProcList As String, ByVal POAPresent As String, ByVal ExemptFlag As String) As Int32

    Private Declare Sub mhinfo Lib "vbdrgv37.dll" (ByVal drg As Int32,
    ByVal DRGVersion As String, ByVal MasksPath As String, ByRef mdc As Int32,
    ByRef weight As Double, ByRef los As Double, ByVal Desc As String, ByVal DescLen As Int32)

    Private Declare Function mhdrgver Lib "vbdrgv37.dll" (ByVal MPath As String,
    ByVal Buf As String, ByVal BufLen As Int32) As Int32

    Private Declare Sub mherrdesc Lib "vbdrgv37.dll" (ByVal errBuffer As String, ByVal errLength As Int32)


    Sub Main()
        AssignDRG()
    End Sub

    Public Function AssignDRG()
        Dim ReturnCode As Integer
        Dim drg As Int32, mdc As Int32
        Dim Desc As String
        Dim descLen As Int32
        Dim weight As Double, los As Double
        Dim masksdir As String

        Dim myver As String, mydstat As String, myage As String, mysex As String, myexempt As String, mypoa As String
        Dim mydxstring As String
        Dim mypxstring As String
        Dim pos As Int32

        ' Hardcoded in this example
        masksdir = "C:\Program Files (x86)\MandH\MASKS\" & ControlChars.NullChar
' Ideally, got from registry. Old VB-for-Access example:
' masksdir = QueryValue("Software\MandH\", "BaseDir") & "\Masks\"

        myver = "v37e" ' version 37, exempt from HAC rules
        mydstat = "1"
        myage = "77"
        mysex = "1"
        mydxstring = "I2119^" ' ^ terminates the string; any of ,|-+ separate fields; ~ is followed by PoA
        mypxstring = "02703ZZ^"
        mypoa = "N" ' no POA because no HAC rules
        myexempt = "X" ' crazy way this works: X = exempt, Z = not exempt
        descLen = 80 ' left over from dumb ASCII terminal days; use whatever you like

        ' these should come back from the DLL call
        drg = 0
        mdc = 0
        weight = 150
        los = 10
        Desc = Space(descLen)

' confirm which DLL we have loaded
        drg = mhdllver(Desc, descLen)
        ' deal with char ASCIIz (null-terminated string)
        pos = InStr(1, Desc, ControlChars.NullChar, CompareMethod.Binary)
        Console.WriteLine("DLL Version: " & Desc.Substring(0, pos))

' Assign the DRG for this age, sex, discharge status, exemption, diagnoses and procedures
        ReturnCode = mhdrg1(drg, myver, masksdir, mydstat, myage, mysex, mydxstring, mypxstring, mypoa, myexempt)
        Console.WriteLine("DLL.mhdrg1 returned RC=" & ReturnCode.ToString)

        If ReturnCode = 0 Then
            Console.WriteLine("DLL.mhdrg1 returned DRG=" & drg.ToString)
            Call mhinfo(drg, myver, masksdir, mdc, weight, los, Desc, descLen)
            Console.WriteLine("DLL.mhinfo returned ver=" & myver & " " _
                              & "MDC=" & mdc.ToString & " " _
                              & "Weight=" & weight.ToString & " " _
                              & "LOS=" & los.ToString & " ")
            Console.WriteLine("Desc=[" & Desc & "]")
        Else
            Call mherrdesc(Desc, descLen)
            Console.WriteLine("Error: " & Desc)
        End If

' Should produce this output:
' DLL Version: mhdrgv37.dll v2.10 (c) 2015 M+H Consulting, LLC: v37
' DLL.mhdrg1 returned RC=0
' DLL.mhdrg1 returned DRG=251
' DLL.mhinfo returned ver=v37e MDC=5 Weight=1.683 LOS=2.2
' Desc=[PERC CARDIOVASC PROC W/O CORONARY ARTERY STENT W/O MCC                          ]

        Return ReturnCode
    End Function

End Module

Wednesday, April 13, 2016

Using the VB DLL with VB .NET

QUESTION
The .NET framework is throwing an error on your DLL; something about the Cache manager.

ANSWER
This is not a managed DLL and will not work automatically in a .NET environment. Since the DLL does not have header information in it, you have to provide that information with the DllImport directive. Remember to specify the character coding as ANSI in the DllImport statement. Similarly, you will have to explicitly allocate the storage for any string you pass in to the DLL's functions, eg VerStr = Space(80).

Follow the "VB .NET" or ".NET" labels on our site to see other blog posts related to using our software in the .NET framework envionment.

Wednesday, October 21, 2015

Norton Flagging Our Windows Installers

We create installers for our windows products because that makes life easier for our customers.

Over the years, we have found that Norton Anti-Virus has become more and more aggressive in its suspicion of installers which do not come from large companies.

Specifically, Norton is prone to throw a "WS.Reputation.1" error, which basically means "Norton does not specially know about this installer, so Norton cannot say that it is OK." See this Norton link for details: http://community.norton.com/forums/clarification-wsreputation1-detection

We try to maintain a variety of "common" Windows installations in-house. On some of our validation machines we run Norton AV ourselves, so our in-house testers have reported this issue as well. But our in-house testers only had to respond to a dialogue box from Norton AV in order to allow our installers to finish. Customer Service declared this to be irritating, but not a deal-breaker.

Then one of our customers claimed that Norton simply deleted the file and would not allow them to run the installer at all. Since they are a programming shop, they did not really need the installer, so we sent them the component files and they installed them by hand. So in that case the situation was merely annoying, but we realize that other shops might not be so comfortable installing software by themselves.

This issue is not unique to us: here is another frustrated small vendor's rant about this: https://www.codeandweb.com/blog/2012/06/23/how-symantec-ruins-independent-developers

While we are generally big fans of firewall and anti-virus software, and understand why our customers might be unhappy to have our installer quarantined by Norton, we are not happy that Norton is willing to be so aggressive with so little data. And it is not clear what we can do differently: the DRG market is a niche market and we will never have the volume numbers required to get Norton's attention.

So for now we feel that the best we can do is to happily provide the component files and installation instructions as an alternative if you cannot run our installer on your system. Just send us email at our tech support email box.

PS We use Nullsoft to build our installers. For more information on them, visit their wiki here: http://nsis.sourceforge.net/Main_Page

New VB-Callable API (Windows Only)

The new VB DLL API is very similar to the old one, but we will document the entire API so that this is the only reference you need.

If you have a working pre-V33 program, here is all you need to change:

The call to mhdrg()
  • the name of the DLL in your function delcarations to "vbdrgv33.dll"
  • the name of the DRG assigner from mhdrg() to mhdrg1()
  • add the POA flag: "y" means "have POA" and "n" means "no POAs"
  • add the exempt flag: "y" means "this case is exempt from HAC" and "no" means "not exempt"
  • the DX and Proc buffers both need to be bigger: 256, not 80
The call to mhinfo()
  • the declaration should have this as a Sub not a Function
  • the first argument is now ByVal, not ByRef
Below is the Visual Basics for Applications (VBA) code we use to call vbdrgv33.dll from Excel in order to run Excel-DRG:

Option Explicit

Private Declare Function mhdllver Lib "vbdrgv33.dll" (ByVal Buf As String, _
    ByVal BufLen As Integer) As Integer
Private Declare Function mhdrg1 Lib "vbdrgv33.dll" (drg As Integer, _
    ByVal DRGVersion As String, ByVal MasksPath As String, ByVal DischStat As String, _
    ByVal PtAge As String, ByVal PtGender As String, ByVal DXList As String, _
    ByVal ProcList As String, ByVal POAPresent As String, ByVal ExemptFlag As String) As Integer
Private Declare Sub mhinfo Lib "vbdrgv33.dll" (ByVal drg As Integer, _
    ByVal DRGVersion As String, ByVal MasksPath As String, ByRef mdc As Integer, _
    weight As Double, los As Double, ByVal Desc As String, ByVal DescLen As Integer)
Private Declare Function mhdrgver Lib "vbdrgv33.dll" (ByVal MPath As String, _
    ByVal Buf As String, ByVal BufLen As Integer) As Integer
Private Declare Sub mherrdesc Lib "vbdrgv33.dll" (ByVal errBuffer As String, ByVal errLength As Integer)

Public Function AssignDRG()

On Error GoTo Err_Group

    Dim ReturnCode As Integer
    Dim drg As Integer, mdc As Integer
    Dim Desc As String * 80
    Dim weight As Double, los As Double
    Dim masksdir As String
    Dim myver As String, mydstat As String, myage As String, mysex As String, myexempt As String, mypoa As String
    Dim mydxbuf As String * 256
    Dim mypxbuf As String * 256
    Dim tempStr As String
    Dim N As Integer, needcomma As Integer
    Dim Val As String
    Dim NumRecords As Long, NumErrors As Long
    Dim LastRow As Long
    Dim MyID As Long                                    'user's record number
    Dim myWS As Object
        
    Application.ScreenUpdating = False
    Application.Cursor = xlWait
    'start in first row, DRG column
    Range("CE2").Select
    NumRecords = 0
    NumErrors = 0
    
    'get the path to the masks directory out of the registry, if you can
    masksdir = "C:\Program Files\MandH\Masks\"          'default
    Set myWS = CreateObject("WScript.Shell")
    tempStr = myWS.RegRead("HKLM\Software\MandH\BaseDir")
    If Len(tempStr) > 0 Then
        masksdir = tempStr & "\Masks\"
    End If
    
    'loop through records making sure DRG info is blank
    'first, find last row
    Do Until IsEmpty(ActiveCell.Offset(0, -81).Range("A1").Value) = True
        ActiveCell.Offset(1, 0).Range("A1").Select
    Loop
    
    'select DRG columns and all populated rows
    LastRow = ActiveCell.Row
    LastRow = LastRow - 1
    Range("CE2:CJ" & LastRow).Select
    
    'clear selection
    Selection.ClearContents
    
    Range("CE2").Select
    'loop through records assigning drg info
    Do Until IsEmpty(ActiveCell.Offset(0, -82).Range("A1").Value) = True
        myver = ActiveCell.Offset(0, -2).Range("A1").Value
        MyID = ActiveCell.Offset(0, -82).Value
    
        'abort if version is blank
        If myver = "" Then
            MyID = ActiveCell.Offset(0, -82).Value
            MsgBox "Version is empty for record # " & MyID & ". Cannot group.", vbOKOnly, "Missing Version"
            NumErrors = NumErrors + 1
            GoTo NextOne
        End If
        
        myexempt = ActiveCell.Offset(0, -79).Range("A1").Value
        mydstat = ActiveCell.Offset(0, -78).Range("A1").Value
        mysex = ActiveCell.Offset(0, -80).Range("A1").Value
        myage = ActiveCell.Offset(0, -81).Range("A1").Value
        mypoa = ActiveCell.Offset(0, -1).Range("A1").Value
        
        'Loop through controls, getting their current values
    
        'make string out of the diagnosis codes
        tempStr = ""
        needcomma = 0
        For N = -77 To -53
            Val = ActiveCell.Offset(0, N).Range("A1").Value
            If Len(Val) > 0 Then
                'append POA flag, if present
                If ActiveCell.Offset(0, N + 25).Value <> "" Then
                    Val = Val & "~" & ActiveCell.Offset(0, N + 25).Range("A1").Value
                End If
                If needcomma <> 0 Then
                    tempStr = tempStr & ","
                End If
                needcomma = 1
                tempStr = tempStr & Val
            End If
        Next N
        mydxbuf = tempStr & "^" ' explicit end-of-data marker
        
        'make string out of the procedure codes
        tempStr = ""
        needcomma = 0
        For N = -27 To -3
            Val = ActiveCell.Offset(0, N).Range("A1").Value
            If Len(Val) > 0 Then
                If needcomma <> 0 Then
                    tempStr = tempStr & ","
                End If
                needcomma = 1
                tempStr = tempStr & Val
            End If
        Next N
        mypxbuf = tempStr & "^" ' explicit end-of-data marker
        
        'call the M+H grouper with what you got
        ReturnCode = mhdrg1(drg, myver, masksdir, mydstat, myage, mysex, mydxbuf, mypxbuf, mypoa, myexempt)
        ActiveCell.Offset(0, 2).Range("A1").Value = ReturnCode
    
        If ReturnCode <> 0 Then          'drg assignment failed, alas!
            Call mherrdesc(Desc, 80)
            ActiveCell.Offset(0, 0).Range("A1").Value = drg
            ActiveCell.Offset(0, 1).Range("A1").Value = Desc
            NumErrors = NumErrors + 1
        Else                            'drg assignment worked, hurray!
            'get the particulars of this DRG from M+H dll
            Call mhinfo(drg, myver, masksdir, mdc, weight, los, Desc, 80)
            ActiveCell.Offset(0, 0).Range("A1").Value = drg
            ActiveCell.Offset(0, 1).Range("A1").Value = Desc
            ActiveCell.Offset(0, 3).Range("A1").Value = mdc
            ActiveCell.Offset(0, 4).Range("A1").Value = weight
            ActiveCell.Offset(0, 5).Range("A1").Value = los
        End If
NextOne:
        ActiveCell.Offset(1, 0).Range("A1").Select
        NumRecords = NumRecords + 1
    Loop
    Application.ScreenUpdating = True
    Application.Cursor = xlDefault
    Range("CE2").Select
    
    Beep
    MsgBox NumRecords & " records were grouped with " & NumErrors & " error(s)."

Exit_Group:
   Exit Function
   
Err_Group:
    Application.Cursor = xlDefault
    MsgBox Err.Number & "-" & Err.Description
    Range("CE2").Select
    Resume Exit_Group

End Function


Please check this blog for other entries about the VB callable DLL to answer other questions.

Tuesday, October 20, 2015

Present On Admission (POA) Flags

Starting with version 33 and ICD10 support, diagnosis codes are supposed to have Present On Admission (POA) flags.

(Unless the institution or patient encounter is "exempt" which would be indicated by the POA Logic Indicator which we call "the exempt flag." The POA Logic Indicator gets its own post to keep confusion to a minimum.)

Here are the CMS definitions of the legal values for this single character flag:

 Flag Meaning
YYes, present at the time of inpatient admission
NNo, not present at the time of inpatient admission
UInsufficient documentation to determine if present on admission
WClinically unable to determine if present at time of admission
1Code is exempt from POA reporting (Used on 4010 form)
BlankCode is exempt from POA reporting (Used on 5010 form, effective 01/01/2011)

Many of our APIs have a "Have POA" flag which is something different: the "Have POA" flag is a record-level indication of whether or not there are POA flags in the submitted diagnosis data. The "Have POA" flag is either "y" for yes or "n" for no.

The POA flags are single-character fields at the individual diagnosis code level. There are up to 25 diagnosis codes accepted for DRG assignment and if you are using POA flags, every code should have one.

In their test data set,CMS passes the POA flag to its grouper as the last character of a fixed-width field. We support that methodology in our DRGFilt product's fixed-with mode and in our C-callable DLL as these are environments in which appending an indicator is relatively easy.

We also support passing in separate POA flag fields in our DRGFilt product's CSV mode and in our VB-callable DLL as there environments generally have better support for separate fields than for character manipulation.

Friday, October 16, 2015

I10 Progress Report #10: VB Support Validated

We are delighted to announce that our Excel-DRG validation is done, which means that:
  • we have a CSV version of the CMS test data set
  • we have an API to a VB-callable DLL to assign DRGs which supports:
    • age
    • sex
    • discharge disposition
    • ICD10 diagnosis list with optional POA flags
    • ICD10 procedure list
    • POA indicator (are you sending POA flags?)
    • Exempt flag (is this case exempt?)
  • we have VB for Apps code to use the DLL to:
    • call mhdrg1() and get a DRG
    • call mhinfo() and get the description, weight, MDC, LOS
    • call mherrdesc() when there are errors
  • we have updated Excel-DRG to use this code
  • we have run the test data through Excel-DRG and confirmed that it works
We are done building products, the rest is sales and marketing and support.

We are going to start filling standing orders today and we hope to be ready to take new orders on-line by the end of next week.

Tuesday, January 15, 2013

DLL Return Code of 9

QUESTION

What does a return code of 9 from the VB-Callable DLL mean?

ANSWER

This return code comes from the DRG Assignment Engine (DAE) and is common across all of our products.

This return code means "input record has Present-on-Admission (POA) violations and the institution is not exempt from HAC processing."

For more about HAC and POA support, please visit this blog entry: How To Use POA

UPDATE: if you append an 'e' to the version number, the DAE assumes that the hospital(s) are exempt and HAC checking is not done at all.

Wednesday, October 12, 2011

F29 Release: Windows Validation

Win32 DRGFilt...Passed
Win32 C-callable DLL...Passed
Win32 VB-callable DLL...Passed
Win32 VB-DRG / VB-callable DLL compatibility...Passed
Win32 CGI-DRG...Passed
Win32 F29 ICD9 table...Passed
Win32 ICD labeling DLL...Passed

Win32 F29 masks installer...Passed
Win32 C-callable DLL installer...Passed
Win32 VB-callable DLL installer...Passed

Our windows product line is validated and ready for shipping.

Monday, September 13, 2010

How Does Access-DRG Work?

QUESTION

How does the Access front-end work? (ie. Can I link into a SQL Server dataset (as an input) and have the Access-DRG spit out the results into a SQL Server dataset?

ANSWER

Access-DRG is not really a front end, it is a self-contained app. You load data into it, you call its DRG Assignment function (which, in turn, calls our VB-callable DLL) and it does in-place DRG assignment.

If you want to read data from a database server, assign a DRG and put the results back onto the database server, you probably want to get our VB-callable DLL and write your own simple program to read, call and write. Or you can use DRGFilt and the import/assign a DRG/write method described in a DRGFilt posting.