Visual C++ DLL for Parsing the Database and Modifying Entities


The function to append single unit fractions text and leaders will be called SUFAppend (for Single Unit Fraction Append).

Using the same Visual C++ project created in Example DLL to Show, Hide, or Toggle Point Display, add the following to VCVCADD.CPP:


  //      Append Single Unit Fraction templates to all selected Text and Leaders
  HRESULT WINAPI SUFAppend(void)
  {

Several variables will be required, and will be explained in the following section as they are used. Add the following to a line that is below and indented (tabbed) from the opening brace:

  	short iRet;
  	short iKind;

  	ENTITYHANDLE hEnt;


 	char strSUF[16];
  	char strText[1024];

In Visual C++, characters can be represented by their byte value using escape codes, which are specified by a backslash. The special character required for the single unit fractions is \x01, using the escape code \x for hexadecimal numbers. The following will put the necessary special codes into the string strSUF.

  	//      setup strSUF
        strcpy( strSUF," \x01\x4E\x6E\x01/\x01\x44\x64\x01\x01" );

The \x4E\x6E are the hexadecimal ASCII codes for 'Nn' and \x44\x64 are the hexadecimal ASCII codes for 'Dd'. Thus, the string used above should be equivalent to \x01Nn\x01/\x01Dd\x01\x01. However, because of the way Visual C++ compiles escape codes, the latter string leads to an error. Caution should always be exercised with escape codes to verify that they have compiled correctly.

This will produce a fraction n/d (for numerator over denominator), which can be edited by the user to insert the appropriate numeric values. The complete code will appear as *Nn*/*Dd**, using * to indicate the unprintable character represented by a hexadecimal byte equal to 1 (one).

The undo level is begun and settings are saved by:

  	 //      begin undo
         VCBeginOperation(pErr);
         VCSaveSettings();

Near the end of this program, we will include VCEndOperation and VCRestoreSettings to end the undo level and restore the settings.

To use the tool, the user must first select the text and leader entities to which he wishes to append the single unit fractions. The user does this in Visual CADD before starting the tool. The application finds the first entity selected by:

  	 //      get first selected entity
         iRet = VCFirstSelected(pErr, &iKind);

When the application calls VCFirstSelected, two values are returned (besides the error code). The integer iRet is a success code. It is 1 (one) if it found a selected entity, and 0 (zero) if no entity was selected. The integer iKind is the number representing the kind of the first entity selected. Note that because iKind is a return value in the parameter list, it is necessary that its address, &iKind, be passed. Both iRet and iKind will be used later.

If VCFirstSelected succeeds, it also makes the first selected entity the current entity. By being current, the Visual CADD API is ready to work on the entity.

The iRet can be tested to see if anything was selected. If so, the application will process it, then move to next selected entity. It will keep doing this until it processes the last selected entity and the request for the next entity fails. This can be done with a while loop.

  	//      while more entities
        while (iRet != 0)
        {

We will add the right brace (}) later to close the while.

As mentioned above, the Visual CADD API can work on entities either by working on the current entity or by referring to an entity by its entity handle. When we get ready to modify the text or leader entity, we will need to duplicate it with VCDuplicate. VCDuplicate requires the entity handle to work, so we can get that handle now and save it. The overview of this example cautioned against storing (or saving) entity handles for later use because they may change. We are safe in this case because the database will not change before we use the saved entity handle. Make sure this next code is on a new line below the left brace:

  		//      get selected entity handle
        	hEnt = VCGetCurrentEntityHandle(pErr);

The application is being designed to add single unit fractions to text and leaders, so we need to check to see if the current entity is one of those kinds.

  		//      check for entity kind text
        	if (iKind == TEXT2D)
        	{

If it is a text entity, then we want to append the single unit fraction to it. We will do this by creating a new text entity just like the original, but with the fraction appended.

The first step is to make all the settings match the current entity (i.e., the text entity).

  			//      match current entity
        		VCMatchCurrentEntity(pErr);

The only setting we must change is the text string. The new string will be the original string with the single unit fraction appended.

  			//      get existing text and append SUF
        		VCGetTextString(pErr, strText);
        		strcat(strText, strSUF);
         		VCSetTextString(pErr, strText);

Now that all the text settings are ready, the new entity with the single unit fraction is created by duplicating the original. VCDuplicate, which duplicates the entity, must reference the entity handle - it does not work on the current entity. Once duplicated, the original is deleted by VCSetCurrentErased, which does work on the current entity.

  			//      duplicate, delete original
        		VCDuplicate(pErr, hEnt);
        		VCSetCurrentErased(pErr);
       		 }

Those steps complete the work on text entities. The steps for leader entities are similar.

  		else if (iKind == LEADER2D)
         	{
                 	//      match current entity
                 	VCMatchCurrentEntity(pErr);

                 	//      get existing text and append SUF
                	VCGetLeaderString(pErr, strText);
                 	strcat(strText, strSUF);
                  	VCSetLeaderString(pErr, strText);

                 	//      duplicate, delete original
                 	VCDuplicate(pErr, hEnt);
                 	VCSetCurrentErased(pErr);
  		}

There are a couple of differences between the code for the leader and the code for the text - VCGetLeaderString and VCSetLeaderString are used instead, which are self explanatory.

At this point, the original entity is deleted but still in the database. Deleting only marks the entity as deleted, because leaving the deleted entities in the database facilitates the undo and redo. Moreover, this deleted entity is still the current entity. VCDuplicate and the other routines which create new entities do not make those new entities current.

Now that we are done processing the current entity (i.e., the original entity), we should deselect it. Deselecting is not always necessary, but in this case, the current entity has been deleted. Leaving deleted entities selected can have unpredictable results, because they will not appear to the user.

  		//      deselect entity
        	 VCSetCurrentDeSelected(pErr);

Now that the entity has been deselected, we are completely done processing it. The next selected entity is found by using VCNextSelected.

  		//      get next entity
         	iRet = VCNextSelected(pErr, &iKind);
  	}

VCNextSelected works much like VCFirstSelected. It returns a success code (iRet), an error code (iError), and an entity kind (iKind). It also makes the next entity the current entity.

The right brace (}) completes the while loop. When the application returns to the while, it will again test iRet. The first time through the while, the iRet is from the VCFirstSelected, and all subsequent times through, the iRet is from the VCNextSelected.

The application will loop through all selected entities until VCNextEntity fails and returns a 0 (zero) to iRet. When it does, the program proceeds past the while loop and continues.

When the program falls through the loop, processing of all selected entities is complete and we are ready to restore the original settings and end the undo level.

  	//      end undo
         VCRestoreSettings();
         VCEndOperation(pErr);

Because entities have been deleted and created, we should redraw the drawing, return a non-error code, and end.

  	//      redraw
         VCInvalidateRect();

         return(NOERROR);
  }

The function declaration also needs to be added to VCVCADD.H:

  HRESULT WINAPI SUFAppend(void);   

Lastly, for the function to be available to external calling applications, such as Visual CADD, the function name needs to be exported by adding it to the EXPORTS section MFCVCADD.DEF:

  SUFAppend

To run your DLL, include the following in your CMDEXT.DEF (recommended) or Assign Script (AS), where the first line is omitted for scripts.

  SUFAppend,SUA, ,Single Unit Fraction Append, Single Unit Fraction Append,
         DllName;C:\VC++4\MFCVCADD\RELEASE\MFCVCADD.DLL;
         DllFunName;SUFAppend;DllRun;

To use SUFAppend, the user first selects the text and leaders to which he wants to append single unit fractions. Then, from Visual CADD, running SUFAppend (SUA) will append the single unit fractions to each selected text and leader. When SUFAppend returns the user to Visual CADD, the user will need to edit all the text and leaders to enter the fractional values and move the fractions to the appropriate places within the text by using cut-and-paste (CTRL+C, CTRL+X, and CTRL+V). The fractional values must be typed in place of the 'n' and 'd', being careful not to change any of the rest of the special codes, including the 'N' and 'D'

Programming Index | VB and VC++ Setup | Example 3 Overview | Tutorial 4