As I know using pooling layer reduces the dimension. Also, the convolution can reduce the size based on choosing stride, kernel size and padding. Therefore, if you want to keep the size without change, you must choose stride, kernel size and padding in a way that prevent dimension reduction.
Moreover, pooling reduces the dimension of the input to the layer. Hence,  in case you want to keep the dimension the same as input, remove pooling layers and change kernel size, stride and padding in convolution layers.
Also, when you want to feed the output of the last convolution layer to fc layer, you must pay attention to changing the shape of last layer’s output because the output is bs x c x w x h ( batch_size, channel, width, height), but the input to fc layer must be 1x n. So, you must change the dimension to a vector through this command:
x = x.view(-1, c x w x h)
this command says to change the dimension to a vector whose shape is (1, c x w x h). a sample code on MNIST dataset in forward function of defined model is as follow. Please note that in this sample code it is not considered to keep the size of input image without change.
class ConvNet(nn.Module):
def __init__(self):
    super(ConvNet, self).__init__()
    self.conv1 = nn.Conv2d(in_channels=1, out_channels=16, kernel_size=(5,5),  stride=(1,1))
    self.conv2 = nn.Conv2d(in_channels=16, out_channels=64,  kernel_size=(5,5), stride=(1,1))
    self.fc1 = nn.Linear(in_features=4*4*64,  out_features=256)
    self.fc2 = nn.Linear(in_features=256, out_features=10)

def forward(self,x):
    x = self.conv1(x)
    x = F.relu(x)
    x = F.max_pool2d(x, 2, 2)
    x = self.conv2(x)
    x = F.relu(x)
    x = F.max_pool2d(x, 2, 2)
    ########################## feed output to fully connected layer #############
   ## vectorizing the input matrix
    x = x.view(-1, 4*4*64)
    x = F.relu(self.fc1(x))
    x = self.fc2(x)
    return x

I hope this guide can help you